/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ /* * Based on the Reverse Engineering work of Christophe Fontanel, * maintainer of the Dungeon Master Encyclopaedia (http://dmweb.free.fr/) */ #include "dm/projexpl.h" #include "dm/dungeonman.h" #include "dm/timeline.h" #include "dm/group.h" #include "dm/objectman.h" #include "dm/movesens.h" #include "dm/sounds.h" namespace DM { ProjExpl::ProjExpl(DMEngine* vm) : _vm(vm) { _creatureDamageOutcome = 0; _secondaryDirToOrFromParty = 0; _lastCreatureAttackTime = -200; _createLauncherProjectile = false; _projectilePoisonAttack = 0; _projectileAttackType = 0; _lastPartyMovementTime = 0; } void ProjExpl::createProjectile(Thing thing, int16 mapX, int16 mapY, uint16 cell, Direction dir, byte kineticEnergy, byte attack, byte stepEnergy) { Thing L0466_T_ProjectileThing; Projectile* L0467_ps_Projectile; TimelineEvent L0468_s_Event; if ((L0466_T_ProjectileThing = _vm->_dungeonMan->getUnusedThing(k14_ProjectileThingType)) == Thing::_none) { /* BUG0_16 If the game cannot create a projectile thing because it has run out of such things (60 maximum) then the object being thrown/shot/launched is orphaned. If the game has run out of projectile things it will try to remove a projectile from elsewhere in the dungeon, except in an area of 11x11 squares centered around the party (to make sure the player cannot actually see the thing disappear on screen) */ return; } L0466_T_ProjectileThing = thingWithNewCell(L0466_T_ProjectileThing, cell); L0467_ps_Projectile = (Projectile *)_vm->_dungeonMan->getThingData(L0466_T_ProjectileThing); L0467_ps_Projectile->_slot = thing; L0467_ps_Projectile->_kineticEnergy = MIN((int16)kineticEnergy, (int16)255); L0467_ps_Projectile->_attack = attack; _vm->_dungeonMan->linkThingToList(L0466_T_ProjectileThing, Thing(0), mapX, mapY); /* Projectiles are added on the square and not 'moved' onto the square. In the case of a projectile launcher sensor, this means that the new projectile traverses the square in front of the launcher without any trouble: there is no impact if it is a wall, the projectile direction is not changed if it is a teleporter. Impacts with creatures and champions are still processed */ setMapAndTime(L0468_s_Event._mapTime, _vm->_dungeonMan->_currMapIndex, _vm->_gameTime + 1); if (_createLauncherProjectile) { L0468_s_Event._type = k49_TMEventTypeMoveProjectile; /* Launcher projectiles can impact immediately */ } else { L0468_s_Event._type = k48_TMEventTypeMoveProjectileIgnoreImpacts; /* Projectiles created by champions or creatures ignore impacts on their first movement */ } L0468_s_Event._priority = 0; L0468_s_Event._B._slot = L0466_T_ProjectileThing.toUint16(); L0468_s_Event._C._projectile.setMapX(mapX); L0468_s_Event._C._projectile.setMapY(mapY); L0468_s_Event._C._projectile.setStepEnergy(stepEnergy); L0468_s_Event._C._projectile.setDir(dir); L0467_ps_Projectile->_eventIndex = _vm->_timeline->addEventGetEventIndex(&L0468_s_Event); } bool ProjExpl::hasProjectileImpactOccurred(int16 impactType, int16 mapXCombo, int16 mapYCombo, int16 cell, Thing projectileThing) { #define AP0454_i_ProjectileTargetMapX mapXCombo #define AP0455_i_ProjectileTargetMapY mapYCombo #define AP0456_i_ChampionIndex cell Projectile* L0490_ps_Projectile; Group* L0491_ps_Group; Thing L0486_T_ProjectileAssociatedThing; int16 L0487_i_Multiple; #define AL0487_i_DoorState L0487_i_Multiple #define AL0487_i_IconIndex L0487_i_Multiple #define AL0487_i_Outcome L0487_i_Multiple #define AL0487_i_WeaponType L0487_i_Multiple int16 L0488_i_Attack = 0; Potion* L0492_ps_Potion = nullptr; CreatureInfo* L0493_ps_CreatureInfo; Door* L0494_ps_Door; Weapon* L0495_ps_Weapon; uint16* L0496_pui_CreatureHealth; Thing* L0497_pT_GroupSlot; Thing L0498_T_ExplosionThing; int16 L0499_i_ProjectileMapX; int16 L0500_i_ProjectileMapY; int16 L0501_i_MapXCombo; int16 L0502_i_MapYCombo; byte L0503_uc_Square; bool L0505_B_CreateExplosionOnImpact; int16 L0489_i_ChampionAttack; uint16 L0507_ui_Multiple; #define AL0507_ui_ExplosionAttack L0507_ui_Multiple #define AL0507_ui_SoundIndex L0507_ui_Multiple int16 L0508_i_PotionPower = 0; bool L0509_B_RemovePotion; int16 L0510_i_ProjectileAssociatedThingType; uint16 L0511_ui_CreatureType; uint16 L0512_ui_CreatureIndex; L0490_ps_Projectile = (Projectile *)_vm->_dungeonMan->getThingData(Thing(projectileThing)); L0501_i_MapXCombo = mapXCombo; L0502_i_MapYCombo = mapYCombo; L0509_B_RemovePotion = false; _creatureDamageOutcome = k0_outcomeKilledNoCreaturesInGroup; if ((L0510_i_ProjectileAssociatedThingType = (L0486_T_ProjectileAssociatedThing = L0490_ps_Projectile->_slot).getType()) == k8_PotionThingType) { L0491_ps_Group = (Group *)_vm->_dungeonMan->getThingData(L0486_T_ProjectileAssociatedThing); switch (((Potion *)L0491_ps_Group)->getType()) { case k3_PotionTypeVen: L0498_T_ExplosionThing = Thing::_explPoisonCloud; goto T0217004; case k19_PotionTypeFulBomb: L0498_T_ExplosionThing = Thing::_explFireBall; T0217004: L0509_B_RemovePotion = true; L0508_i_PotionPower = ((Potion *)L0491_ps_Group)->getPower(); L0492_ps_Potion = (Potion *)L0491_ps_Group; break; default: break; } } L0505_B_CreateExplosionOnImpact = (L0510_i_ProjectileAssociatedThingType == k15_ExplosionThingType) && (L0486_T_ProjectileAssociatedThing != Thing::_explSlime) && (L0486_T_ProjectileAssociatedThing != Thing::_explPoisonBolt); L0497_pT_GroupSlot = NULL; L0489_i_ChampionAttack = 0; if (mapXCombo <= 255) { L0499_i_ProjectileMapX = mapXCombo; L0500_i_ProjectileMapY = mapYCombo; } else { L0499_i_ProjectileMapX = (mapXCombo >> 8) - 1; L0500_i_ProjectileMapY = (mapYCombo >> 8); AP0454_i_ProjectileTargetMapX &= 0x00FF; AP0455_i_ProjectileTargetMapY &= 0x00FF; } switch (impactType) { case k4_DoorElemType: AL0487_i_DoorState = Square(L0503_uc_Square = _vm->_dungeonMan->_currMapData[AP0454_i_ProjectileTargetMapX][AP0455_i_ProjectileTargetMapY]).getDoorState(); L0494_ps_Door = (Door *)_vm->_dungeonMan->getSquareFirstThingData(AP0454_i_ProjectileTargetMapX, AP0455_i_ProjectileTargetMapY); if ((AL0487_i_DoorState != k5_doorState_DESTROYED) && (L0486_T_ProjectileAssociatedThing == Thing::_explOpenDoor)) { if (L0494_ps_Door->hasButton()) { _vm->_moveSens->addEvent(k10_TMEventTypeDoor, AP0454_i_ProjectileTargetMapX, AP0455_i_ProjectileTargetMapY, 0, k2_SensorEffToggle, _vm->_gameTime + 1); } break; } if ((AL0487_i_DoorState == k5_doorState_DESTROYED) || (AL0487_i_DoorState <= k1_doorState_FOURTH) || (getFlag(_vm->_dungeonMan->_currMapDoorInfo[L0494_ps_Door->getType()]._attributes, k0x0002_MaskDoorInfo_ProjectilesCanPassThrough) && ((L0510_i_ProjectileAssociatedThingType == k15_ExplosionThingType) ? (L0486_T_ProjectileAssociatedThing.toUint16() >= Thing::_explHarmNonMaterial.toUint16()) : ((L0490_ps_Projectile->_attack > _vm->getRandomNumber(128)) && getFlag(_vm->_dungeonMan->_objectInfos[_vm->_dungeonMan->getObjectInfoIndex(L0486_T_ProjectileAssociatedThing)].getAllowedSlots(), k0x0100_ObjectAllowedSlotPouchPassAndThroughDoors) && ((L0510_i_ProjectileAssociatedThingType != k10_JunkThingType) || ((AL0487_i_IconIndex = _vm->_objectMan->getIconIndex(L0486_T_ProjectileAssociatedThing)) < 0) || (!((AL0487_i_IconIndex >= k176_IconIndiceJunkIronKey) && (AL0487_i_IconIndex <= k191_IconIndiceJunkMasterKey)))) )))) { /* ASSEMBLY_COMPILATION_DIFFERENCE jmp */ return false; } L0488_i_Attack = getProjectileImpactAttack(L0490_ps_Projectile, L0486_T_ProjectileAssociatedThing) + 1; _vm->_groupMan->groupIsDoorDestoryedByAttack(AP0454_i_ProjectileTargetMapX, AP0455_i_ProjectileTargetMapY, L0488_i_Attack + _vm->getRandomNumber(L0488_i_Attack), false, 0); break; case kM2_ChampionElemType: if ((AP0456_i_ChampionIndex = _vm->_championMan->getIndexInCell(cell)) < 0) { return false; } L0489_i_ChampionAttack = L0488_i_Attack = getProjectileImpactAttack(L0490_ps_Projectile, L0486_T_ProjectileAssociatedThing); break; case kM1_CreatureElemType: L0491_ps_Group = (Group *)_vm->_dungeonMan->getThingData(_vm->_groupMan->groupGetThing(AP0454_i_ProjectileTargetMapX, AP0455_i_ProjectileTargetMapY)); if (!(L0512_ui_CreatureIndex = _vm->_groupMan->getCreatureOrdinalInCell(L0491_ps_Group, cell))) { return false; } L0512_ui_CreatureIndex--; L0493_ps_CreatureInfo = &_vm->_dungeonMan->_creatureInfos[L0511_ui_CreatureType = L0491_ps_Group->_type]; if ((L0486_T_ProjectileAssociatedThing == Thing::_explFireBall) && (L0511_ui_CreatureType == k11_CreatureTypeBlackFlame)) { L0496_pui_CreatureHealth = &L0491_ps_Group->_health[L0512_ui_CreatureIndex]; *L0496_pui_CreatureHealth = MIN(1000, *L0496_pui_CreatureHealth + getProjectileImpactAttack(L0490_ps_Projectile, L0486_T_ProjectileAssociatedThing)); goto T0217044; } if (getFlag(L0493_ps_CreatureInfo->_attributes, k0x0040_MaskCreatureInfo_nonMaterial) && (L0486_T_ProjectileAssociatedThing != Thing::_explHarmNonMaterial)) { return false; } L0488_i_Attack = (uint16)((unsigned long)getProjectileImpactAttack(L0490_ps_Projectile, L0486_T_ProjectileAssociatedThing) << 6) / L0493_ps_CreatureInfo->_defense; if (L0488_i_Attack) { if ((AL0487_i_Outcome = _vm->_groupMan->groupGetDamageCreatureOutcome(L0491_ps_Group, L0512_ui_CreatureIndex, AP0454_i_ProjectileTargetMapX, AP0455_i_ProjectileTargetMapY, L0488_i_Attack + _vm->_groupMan->groupGetResistanceAdjustedPoisonAttack(L0511_ui_CreatureType, _projectilePoisonAttack), true)) != k0_outcomeKilledNoCreaturesInGroup) { _vm->_groupMan->processEvents29to41(AP0454_i_ProjectileTargetMapX, AP0455_i_ProjectileTargetMapY, kM2_TMEventTypeCreateReactionEvent30HitByProjectile, 0); } _creatureDamageOutcome = AL0487_i_Outcome; if (!L0505_B_CreateExplosionOnImpact && (AL0487_i_Outcome == k0_outcomeKilledNoCreaturesInGroup) && (L0510_i_ProjectileAssociatedThingType == k5_WeaponThingType) && getFlag(L0493_ps_CreatureInfo->_attributes, k0x0400_MaskCreatureInfo_keepThrownSharpWeapon)) { L0495_ps_Weapon = (Weapon *)_vm->_dungeonMan->getThingData(L0486_T_ProjectileAssociatedThing); AL0487_i_WeaponType = L0495_ps_Weapon->getType(); if ((AL0487_i_WeaponType == k8_WeaponTypeDagger) || (AL0487_i_WeaponType == k27_WeaponTypeArrow) || (AL0487_i_WeaponType == k28_WeaponTypeSlayer) || (AL0487_i_WeaponType == k31_WeaponTypePoisonDart) || (AL0487_i_WeaponType == k32_WeaponTypeThrowingStar)) { L0497_pT_GroupSlot = &L0491_ps_Group->_slot; } } } } if (L0489_i_ChampionAttack && _vm->_championMan->addPendingDamageAndWounds_getDamage(AP0456_i_ChampionIndex, L0488_i_Attack, k0x0004_ChampionWoundHead | k0x0008_ChampionWoundTorso, _projectileAttackType) && _projectilePoisonAttack && _vm->getRandomNumber(2)) { _vm->_championMan->championPoison(AP0456_i_ChampionIndex, _projectilePoisonAttack); } if (L0505_B_CreateExplosionOnImpact || L0509_B_RemovePotion ) { if (L0509_B_RemovePotion) { L0486_T_ProjectileAssociatedThing = L0498_T_ExplosionThing; AL0507_ui_ExplosionAttack = L0508_i_PotionPower; } else { AL0507_ui_ExplosionAttack = L0490_ps_Projectile->_kineticEnergy; } if ((L0486_T_ProjectileAssociatedThing == Thing::_explLightningBolt) && !(AL0507_ui_ExplosionAttack >>= 1)) goto T0217044; createExplosion(L0486_T_ProjectileAssociatedThing, AL0507_ui_ExplosionAttack, L0501_i_MapXCombo, L0502_i_MapYCombo, (L0486_T_ProjectileAssociatedThing == Thing::_explPoisonCloud) ? k255_CreatureTypeSingleCenteredCreature : cell); } else { if ((L0486_T_ProjectileAssociatedThing).getType() == k5_WeaponThingType) { AL0507_ui_SoundIndex = k00_soundMETALLIC_THUD; } else { if (L0486_T_ProjectileAssociatedThing == Thing::_explPoisonBolt) { AL0507_ui_SoundIndex = k13_soundSPELL; } else { AL0507_ui_SoundIndex = k04_soundWOODEN_THUD_ATTACK_TROLIN_ANTMAN_STONE_GOLEM; } } _vm->_sound->requestPlay(AL0507_ui_SoundIndex, L0499_i_ProjectileMapX, L0500_i_ProjectileMapY, k1_soundModePlayIfPrioritized); } T0217044: if (L0509_B_RemovePotion) { L0492_ps_Potion->_nextThing = Thing::_none; L0490_ps_Projectile->_slot = L0498_T_ExplosionThing; } _vm->_dungeonMan->unlinkThingFromList(projectileThing, Thing(0), L0499_i_ProjectileMapX, L0500_i_ProjectileMapY); projectileDelete(projectileThing, L0497_pT_GroupSlot, L0499_i_ProjectileMapX, L0500_i_ProjectileMapY); return true; } uint16 ProjExpl::getProjectileImpactAttack(Projectile* projectile, Thing thing) { WeaponInfo* L0485_ps_WeaponInfo; uint16 L0483_ui_Multiple; #define AL0483_ui_ThingType L0483_ui_Multiple #define AL0483_ui_Attack L0483_ui_Multiple uint16 L0484_ui_KineticEnergy; _projectilePoisonAttack = 0; _projectileAttackType = k3_attackType_BLUNT; L0484_ui_KineticEnergy = projectile->_kineticEnergy; if ((AL0483_ui_ThingType = thing.getType()) != k15_ExplosionThingType) { if (AL0483_ui_ThingType == k5_WeaponThingType) { L0485_ps_WeaponInfo = _vm->_dungeonMan->getWeaponInfo(thing); AL0483_ui_Attack = L0485_ps_WeaponInfo->_kineticEnergy; _projectileAttackType = k3_attackType_BLUNT; } else { AL0483_ui_Attack = _vm->getRandomNumber(4); } AL0483_ui_Attack += _vm->_dungeonMan->getObjectWeight(thing) >> 1; } else { if (thing == Thing::_explSlime) { AL0483_ui_Attack = _vm->getRandomNumber(16); _projectilePoisonAttack = AL0483_ui_Attack + 10; AL0483_ui_Attack += _vm->getRandomNumber(32); } else { if (thing.toUint16() >= Thing::_explHarmNonMaterial.toUint16()) { _projectileAttackType = k5_attackType_MAGIC; if (thing == Thing::_explPoisonBolt) { _projectilePoisonAttack = L0484_ui_KineticEnergy; return 1; } return 0; } _projectileAttackType = k1_attackType_FIRE; AL0483_ui_Attack = _vm->getRandomNumber(16) + _vm->getRandomNumber(16) + 10; if (thing == Thing::_explLightningBolt) { _projectileAttackType = k7_attackType_LIGHTNING; AL0483_ui_Attack *= 5; } } } AL0483_ui_Attack = ((AL0483_ui_Attack + L0484_ui_KineticEnergy) >> 4) + 1; AL0483_ui_Attack += _vm->getRandomNumber((AL0483_ui_Attack >> 1) + 1) + _vm->getRandomNumber(4); AL0483_ui_Attack = MAX(AL0483_ui_Attack >> 1, AL0483_ui_Attack - (32 - (projectile->_attack >> 3))); return AL0483_ui_Attack; } void ProjExpl::createExplosion(Thing explThing, uint16 attack, uint16 mapXCombo, uint16 mapYCombo, uint16 cell) { #define AP0443_ui_ProjectileMapX mapXCombo #define AP0444_ui_ProjectileMapY mapYCombo Thing L0473_T_Thing = _vm->_dungeonMan->getUnusedThing(k15_ExplosionThingType); if (L0473_T_Thing == Thing::_none) { return; } Explosion *L0470_ps_Explosion = &((Explosion *)_vm->_dungeonMan->_thingData[k15_ExplosionThingType])[(L0473_T_Thing).getIndex()]; int16 L0474_i_ProjectileTargetMapX; int16 L0475_i_ProjectileTargetMapY; if (mapXCombo <= 255) { L0474_i_ProjectileTargetMapX = mapXCombo; L0475_i_ProjectileTargetMapY = mapYCombo; } else { L0474_i_ProjectileTargetMapX = mapXCombo & 0x00FF; L0475_i_ProjectileTargetMapY = mapYCombo & 0x00FF; AP0443_ui_ProjectileMapX >>= 8; AP0443_ui_ProjectileMapX--; AP0444_ui_ProjectileMapY >>= 8; } if (cell == k255_CreatureTypeSingleCenteredCreature) { L0470_ps_Explosion->setCentered(true); } else { L0470_ps_Explosion->setCentered(false); L0473_T_Thing = thingWithNewCell(L0473_T_Thing, cell); } L0470_ps_Explosion->setType(explThing.toUint16() - Thing::_firstExplosion.toUint16()); L0470_ps_Explosion->setAttack(attack); if (explThing.toUint16() < Thing::_explHarmNonMaterial.toUint16()) { _vm->_sound->requestPlay((attack > 80) ? k05_soundSTRONG_EXPLOSION : k20_soundWEAK_EXPLOSION, AP0443_ui_ProjectileMapX, AP0444_ui_ProjectileMapY, k1_soundModePlayIfPrioritized); } else { if (explThing != Thing::_explSmoke) { _vm->_sound->requestPlay(k13_soundSPELL, AP0443_ui_ProjectileMapX, AP0444_ui_ProjectileMapY, k1_soundModePlayIfPrioritized); } } _vm->_dungeonMan->linkThingToList(L0473_T_Thing, Thing(0), AP0443_ui_ProjectileMapX, AP0444_ui_ProjectileMapY); TimelineEvent L0476_s_Event; setMapAndTime(L0476_s_Event._mapTime, _vm->_dungeonMan->_currMapIndex, _vm->_gameTime + ((explThing == Thing::_explRebirthStep1) ? 5 : 1)); L0476_s_Event._type = k25_TMEventTypeExplosion; L0476_s_Event._priority = 0; L0476_s_Event._C._slot = L0473_T_Thing.toUint16(); L0476_s_Event._B._location._mapX = AP0443_ui_ProjectileMapX; L0476_s_Event._B._location._mapY = AP0444_ui_ProjectileMapY; _vm->_timeline->addEventGetEventIndex(&L0476_s_Event); if ((explThing == Thing::_explLightningBolt) || (explThing == Thing::_explFireBall)) { AP0443_ui_ProjectileMapX = L0474_i_ProjectileTargetMapX; AP0444_ui_ProjectileMapY = L0475_i_ProjectileTargetMapY; attack = (attack >> 1) + 1; attack += _vm->getRandomNumber(attack) + 1; if ((explThing == Thing::_explFireBall) || (attack >>= 1)) { if ((_vm->_dungeonMan->_currMapIndex == _vm->_dungeonMan->_partyMapIndex) && (AP0443_ui_ProjectileMapX == _vm->_dungeonMan->_partyMapX) && (AP0444_ui_ProjectileMapY == _vm->_dungeonMan->_partyMapY)) { _vm->_championMan->getDamagedChampionCount(attack, k0x0001_ChampionWoundReadHand | k0x0002_ChampionWoundActionHand | k0x0004_ChampionWoundHead | k0x0008_ChampionWoundTorso | k0x0010_ChampionWoundLegs | k0x0020_ChampionWoundFeet, k1_attackType_FIRE); } else { if ((L0473_T_Thing = _vm->_groupMan->groupGetThing(AP0443_ui_ProjectileMapX, AP0444_ui_ProjectileMapY)) != Thing::_endOfList) { /* ASSEMBLY_COMPILATION_DIFFERENCE jmp */ Group *L0472_ps_Group = (Group *)_vm->_dungeonMan->getThingData(L0473_T_Thing); CreatureInfo *L0471_ps_CreatureInfo = &_vm->_dungeonMan->_creatureInfos[L0472_ps_Group->_type]; int16 L0469_i_CreatureFireResistance = L0471_ps_CreatureInfo->getFireResistance(); if (L0469_i_CreatureFireResistance != k15_immuneToFire) { if (getFlag(L0471_ps_CreatureInfo->_attributes, k0x0040_MaskCreatureInfo_nonMaterial)) { attack >>= 2; } if ((attack -= _vm->getRandomNumber((L0469_i_CreatureFireResistance << 1) + 1)) > 0) { _creatureDamageOutcome = _vm->_groupMan->getDamageAllCreaturesOutcome(L0472_ps_Group, AP0443_ui_ProjectileMapX, AP0444_ui_ProjectileMapY, attack, true); } } } } } } } int16 ProjExpl::projectileGetImpactCount(int16 impactType, int16 mapX, int16 mapY, int16 cell) { Thing L0513_T_Thing; int16 L0514_i_ImpactCount; L0514_i_ImpactCount = 0; _creatureDamageOutcome = k0_outcomeKilledNoCreaturesInGroup; T0218001: L0513_T_Thing = _vm->_dungeonMan->getSquareFirstThing(mapX, mapY); while (L0513_T_Thing != Thing::_endOfList) { if (((L0513_T_Thing).getType() == k14_ProjectileThingType) && ((L0513_T_Thing).getCell() == cell) && hasProjectileImpactOccurred(impactType, mapX, mapY, cell, L0513_T_Thing)) { projectileDeleteEvent(L0513_T_Thing); L0514_i_ImpactCount++; if ((impactType == kM1_CreatureElemType) && (_creatureDamageOutcome == k2_outcomeKilledAllCreaturesInGroup)) break; goto T0218001; } L0513_T_Thing = _vm->_dungeonMan->getNextThing(L0513_T_Thing); } return L0514_i_ImpactCount; } void ProjExpl::projectileDeleteEvent(Thing thing) { Projectile* L0477_ps_Projectile; L0477_ps_Projectile = (Projectile *)_vm->_dungeonMan->getThingData(thing); _vm->_timeline->deleteEvent(L0477_ps_Projectile->_eventIndex); } void ProjExpl::projectileDelete(Thing projectileThing, Thing* groupSlot, int16 mapX, int16 mapY) { Projectile *L0480_ps_Projectile = (Projectile *)_vm->_dungeonMan->getThingData(projectileThing); Thing L0479_T_Thing = L0480_ps_Projectile->_slot; if (L0479_T_Thing.getType() != k15_ExplosionThingType) { if (groupSlot != NULL) { Thing L0478_T_PreviousThing = *groupSlot; if (L0478_T_PreviousThing == Thing::_endOfList) { Thing *L0481_ps_Generic = (Thing *)_vm->_dungeonMan->getThingData(L0479_T_Thing); *L0481_ps_Generic = Thing::_endOfList; *groupSlot = L0479_T_Thing; } else { _vm->_dungeonMan->linkThingToList(L0479_T_Thing, L0478_T_PreviousThing, kM1_MapXNotOnASquare, 0); } } else { _vm->_moveSens->getMoveResult(Thing((L0479_T_Thing).getTypeAndIndex() | getFlag(projectileThing.toUint16(), 0xC)), -2, 0, mapX, mapY); } } L0480_ps_Projectile->_nextThing = Thing::_none; } void ProjExpl::processEvents48To49(TimelineEvent* event) { TimelineEvent* L0519_ps_Event; Projectile* L0520_ps_Projectile; Thing L0515_T_ProjectileThingNewCell; uint16 L0516_ui_Multiple; #define AL0516_ui_StepEnergy L0516_ui_Multiple #define AL0516_ui_Square L0516_ui_Multiple Thing L0521_T_ProjectileThing; uint16 L0517_ui_ProjectileDirection; bool L0522_B_ProjectileMovesToOtherSquare; int16 L0523_i_DestinationMapX; int16 L0524_i_DestinationMapY; uint16 L0518_ui_Cell; int16 L0525_i_SourceMapX = -1; int16 L0526_i_SourceMapY = -1; TimelineEvent L0527_s_Event; L0527_s_Event = *event; L0519_ps_Event = &L0527_s_Event; L0520_ps_Projectile = (Projectile*)_vm->_dungeonMan->getThingData(L0521_T_ProjectileThing = L0515_T_ProjectileThingNewCell = Thing(L0519_ps_Event->_B._slot)); L0523_i_DestinationMapX = L0519_ps_Event->_C._projectile.getMapX(); L0524_i_DestinationMapY = L0519_ps_Event->_C._projectile.getMapY(); if (L0519_ps_Event->_type == k48_TMEventTypeMoveProjectileIgnoreImpacts) { L0519_ps_Event->_type = k49_TMEventTypeMoveProjectile; } else { L0518_ui_Cell = (L0515_T_ProjectileThingNewCell).getCell(); if ((_vm->_dungeonMan->_currMapIndex == _vm->_dungeonMan->_partyMapIndex) && (L0523_i_DestinationMapX == _vm->_dungeonMan->_partyMapX) && (L0524_i_DestinationMapY == _vm->_dungeonMan->_partyMapY) && hasProjectileImpactOccurred(kM2_ChampionElemType, L0523_i_DestinationMapX, L0524_i_DestinationMapY, L0518_ui_Cell, L0515_T_ProjectileThingNewCell)) { return; } if ((_vm->_groupMan->groupGetThing(L0523_i_DestinationMapX, L0524_i_DestinationMapY) != Thing::_endOfList) && hasProjectileImpactOccurred(kM1_CreatureElemType, L0523_i_DestinationMapX, L0524_i_DestinationMapY, L0518_ui_Cell, L0521_T_ProjectileThing)) { return; } if (L0520_ps_Projectile->_kineticEnergy <= (AL0516_ui_StepEnergy = L0519_ps_Event->_C._projectile.getStepEnergy())) { _vm->_dungeonMan->unlinkThingFromList(L0515_T_ProjectileThingNewCell = L0521_T_ProjectileThing, Thing(0), L0523_i_DestinationMapX, L0524_i_DestinationMapY); projectileDelete(L0515_T_ProjectileThingNewCell, NULL, L0523_i_DestinationMapX, L0524_i_DestinationMapY); return; } L0520_ps_Projectile->_kineticEnergy -= AL0516_ui_StepEnergy; if (L0520_ps_Projectile->_attack < AL0516_ui_StepEnergy) { L0520_ps_Projectile->_attack = 0; } else { L0520_ps_Projectile->_attack -= AL0516_ui_StepEnergy; } } L0517_ui_ProjectileDirection = L0519_ps_Event->_C._projectile.getDir(); L0515_T_ProjectileThingNewCell = Thing(L0519_ps_Event->_B._slot); L0518_ui_Cell = L0515_T_ProjectileThingNewCell.getCell(); L0522_B_ProjectileMovesToOtherSquare = (L0517_ui_ProjectileDirection == L0518_ui_Cell) || (returnNextVal(L0517_ui_ProjectileDirection) == L0518_ui_Cell); if (L0522_B_ProjectileMovesToOtherSquare) { L0525_i_SourceMapX = L0523_i_DestinationMapX; L0526_i_SourceMapY = L0524_i_DestinationMapY; L0523_i_DestinationMapX += _vm->_dirIntoStepCountEast[L0517_ui_ProjectileDirection], L0524_i_DestinationMapY += _vm->_dirIntoStepCountNorth[L0517_ui_ProjectileDirection]; AL0516_ui_Square = _vm->_dungeonMan->getSquare(L0523_i_DestinationMapX, L0524_i_DestinationMapY).toByte(); if ((Square(AL0516_ui_Square).getType() == k0_WallElemType) || ((Square(AL0516_ui_Square).getType() == k6_FakeWallElemType) && !getFlag(AL0516_ui_Square, (k0x0001_FakeWallImaginary | k0x0004_FakeWallOpen))) || ((Square(AL0516_ui_Square).getType() == k3_StairsElemType) && (Square(_vm->_dungeonMan->_currMapData[L0525_i_SourceMapX][L0526_i_SourceMapY]).getType() == k3_StairsElemType))) { if (hasProjectileImpactOccurred(Square(AL0516_ui_Square).getType(), L0525_i_SourceMapX, L0526_i_SourceMapY, L0518_ui_Cell, L0515_T_ProjectileThingNewCell)) { return; } } } if ((L0517_ui_ProjectileDirection & 0x0001) == (L0518_ui_Cell & 0x0001)) { L0518_ui_Cell--; } else { L0518_ui_Cell++; } L0515_T_ProjectileThingNewCell = thingWithNewCell(L0515_T_ProjectileThingNewCell, L0518_ui_Cell &= 0x0003); if (L0522_B_ProjectileMovesToOtherSquare) { _vm->_moveSens->getMoveResult(L0515_T_ProjectileThingNewCell, L0525_i_SourceMapX, L0526_i_SourceMapY, L0523_i_DestinationMapX, L0524_i_DestinationMapY); L0519_ps_Event->_C._projectile.setMapX(_vm->_moveSens->_moveResultMapX); L0519_ps_Event->_C._projectile.setMapY(_vm->_moveSens->_moveResultMapY); L0519_ps_Event->_C._projectile.setDir((Direction)_vm->_moveSens->_moveResultDir); L0515_T_ProjectileThingNewCell = thingWithNewCell(L0515_T_ProjectileThingNewCell, _vm->_moveSens->_moveResultCell); M31_setMap(L0519_ps_Event->_mapTime, _vm->_moveSens->_moveResultMapIndex); } else { if ((Square(_vm->_dungeonMan->getSquare(L0523_i_DestinationMapX, L0524_i_DestinationMapY)).getType() == k4_DoorElemType) && hasProjectileImpactOccurred(k4_DoorElemType, L0523_i_DestinationMapX, L0524_i_DestinationMapY, L0518_ui_Cell, L0521_T_ProjectileThing)) { return; } _vm->_dungeonMan->unlinkThingFromList(L0515_T_ProjectileThingNewCell, Thing(0), L0523_i_DestinationMapX, L0524_i_DestinationMapY); _vm->_dungeonMan->linkThingToList(L0515_T_ProjectileThingNewCell, Thing(0), L0523_i_DestinationMapX, L0524_i_DestinationMapY); } L0519_ps_Event->_mapTime += (_vm->_dungeonMan->_currMapIndex == _vm->_dungeonMan->_partyMapIndex) ? 1 : 3; //Strangerke: CHECKME: Maybe we should keep that piece of code too as it sounds like it's fixing a weird behavior of projectiles on different maps #ifdef COMPILE42_CSB20EN_CSB21EN /* CHANGE7_20_IMPROVEMENT Projectiles now move at the same speed on all maps instead of moving slower on maps other than the party map */ L0519_ps_Event->Map_Time++; #endif L0519_ps_Event->_B._slot = L0515_T_ProjectileThingNewCell.toUint16(); L0520_ps_Projectile->_eventIndex = _vm->_timeline->addEventGetEventIndex(L0519_ps_Event); } void ProjExpl::processEvent25(TimelineEvent* event) { Explosion* L0532_ps_Explosion; Group* L0533_ps_Group = nullptr; CreatureInfo* L0534_ps_CreatureInfo = nullptr; uint16 L0528_ui_MapX; uint16 L0529_ui_MapY; int16 L0530_i_Attack; int16 L0531_i_Multiple; #define AL0531_i_SquareType L0531_i_Multiple #define AL0531_i_CreatureCount L0531_i_Multiple Thing L0535_T_GroupThing; Thing L0536_T_ExplosionThing; uint16 L0537_ui_Multiple = 0; #define AL0537_ui_CreatureType L0537_ui_Multiple #define AL0537_ui_NonMaterialAdditionalAttack L0537_ui_Multiple bool L0538_B_ExplosionOnPartySquare; TimelineEvent L0539_s_Event; L0528_ui_MapX = event->_B._location._mapX; L0529_ui_MapY = event->_B._location._mapY; L0532_ps_Explosion = &((Explosion*)_vm->_dungeonMan->_thingData[k15_ExplosionThingType])[Thing((event->_C._slot)).getIndex()]; AL0531_i_SquareType = Square(_vm->_dungeonMan->_currMapData[L0528_ui_MapX][L0529_ui_MapY]).getType(); L0538_B_ExplosionOnPartySquare = (_vm->_dungeonMan->_currMapIndex == _vm->_dungeonMan->_partyMapIndex) && (L0528_ui_MapX == _vm->_dungeonMan->_partyMapX) && (L0529_ui_MapY == _vm->_dungeonMan->_partyMapY); if ((L0535_T_GroupThing = _vm->_groupMan->groupGetThing(L0528_ui_MapX, L0529_ui_MapY)) != Thing::_endOfList) { L0533_ps_Group = (Group*)_vm->_dungeonMan->getThingData(L0535_T_GroupThing); L0534_ps_CreatureInfo = &_vm->_dungeonMan->_creatureInfos[AL0537_ui_CreatureType = L0533_ps_Group->_type]; } if ((L0536_T_ExplosionThing = Thing(Thing::_firstExplosion.toUint16() + L0532_ps_Explosion->getType())) == Thing::_explPoisonCloud) { L0530_i_Attack = MAX(1, MIN(L0532_ps_Explosion->getAttack() >> 5, 4) + _vm->getRandomNumber(2)); /* Value between 1 and 5 */ } else { L0530_i_Attack = (L0532_ps_Explosion->getAttack() >> 1) + 1; L0530_i_Attack += _vm->getRandomNumber(L0530_i_Attack) + 1; } switch (L0536_T_ExplosionThing.toUint16()) { case 0xFF82: if (!(L0530_i_Attack >>= 1)) break; case 0xFF80: if (AL0531_i_SquareType == k4_DoorElemType) { _vm->_groupMan->groupIsDoorDestoryedByAttack(L0528_ui_MapX, L0529_ui_MapY, L0530_i_Attack, true, 0); } break; case 0xFF83: if ((L0535_T_GroupThing != Thing::_endOfList) && getFlag(L0534_ps_CreatureInfo->_attributes, k0x0040_MaskCreatureInfo_nonMaterial)) { if ((AL0537_ui_CreatureType == k19_CreatureTypeMaterializerZytaz) && (_vm->_dungeonMan->_currMapIndex == _vm->_dungeonMan->_partyMapIndex)) { /* ASSEMBLY_COMPILATION_DIFFERENCE jmp */ L0530_i_Attack -= (AL0537_ui_NonMaterialAdditionalAttack = L0530_i_Attack >> 3); AL0537_ui_NonMaterialAdditionalAttack <<= 1; AL0537_ui_NonMaterialAdditionalAttack++; AL0531_i_CreatureCount = L0533_ps_Group->getCount(); do { if (getFlag(_vm->_groupMan->_activeGroups[L0533_ps_Group->getActiveGroupIndex()]._aspect[AL0531_i_CreatureCount], k0x0080_MaskActiveGroupIsAttacking)) { /* Materializer / Zytaz can only be damaged while they are attacking */ _vm->_groupMan->groupGetDamageCreatureOutcome(L0533_ps_Group, AL0531_i_CreatureCount, L0528_ui_MapX, L0529_ui_MapY, L0530_i_Attack + _vm->getRandomNumber(AL0537_ui_NonMaterialAdditionalAttack) + _vm->getRandomNumber(4), true); } } while (--AL0531_i_CreatureCount >= 0); } else { _vm->_groupMan->getDamageAllCreaturesOutcome(L0533_ps_Group, L0528_ui_MapX, L0529_ui_MapY, L0530_i_Attack, true); } } break; case 0xFFE4: L0532_ps_Explosion->setType(L0532_ps_Explosion->getType() + 1); _vm->_sound->requestPlay(k05_soundSTRONG_EXPLOSION, L0528_ui_MapX, L0529_ui_MapY, k1_soundModePlayIfPrioritized); goto T0220026; case 0xFFA8: if (L0532_ps_Explosion->getAttack() > 55) { L0532_ps_Explosion->setAttack(L0532_ps_Explosion->getAttack() - 40); goto T0220026; } break; case 0xFF87: if (L0538_B_ExplosionOnPartySquare) { _vm->_championMan->getDamagedChampionCount(L0530_i_Attack, k0x0000_ChampionWoundNone, k0_attackType_NORMAL); } else { if ((L0535_T_GroupThing != Thing::_endOfList) && (L0530_i_Attack = _vm->_groupMan->groupGetResistanceAdjustedPoisonAttack(AL0537_ui_CreatureType, L0530_i_Attack)) && (_vm->_groupMan->getDamageAllCreaturesOutcome(L0533_ps_Group, L0528_ui_MapX, L0529_ui_MapY, L0530_i_Attack, true) != k2_outcomeKilledAllCreaturesInGroup) && (L0530_i_Attack > 2)) { _vm->_groupMan->processEvents29to41(L0528_ui_MapX, L0529_ui_MapY, kM3_TMEventTypeCreateReactionEvent29DangerOnSquare, 0); } } if (L0532_ps_Explosion->getAttack() >= 6) { L0532_ps_Explosion->setAttack(L0532_ps_Explosion->getAttack() - 3); T0220026: L0539_s_Event = *event; L0539_s_Event._mapTime++; _vm->_timeline->addEventGetEventIndex(&L0539_s_Event); return; } } _vm->_dungeonMan->unlinkThingFromList(Thing(event->_C._slot), Thing(0), L0528_ui_MapX, L0529_ui_MapY); L0532_ps_Explosion->setNextThing(Thing::_none); } }