diff options
author | Eugene Sandulenko | 2016-09-10 16:55:13 +0200 |
---|---|---|
committer | GitHub | 2016-09-10 16:55:13 +0200 |
commit | a46cc8597cdc2ab2aca146ed79c0fcf3f4df5617 (patch) | |
tree | 8d56828835eb1124d3d268e4e934e7957123ca3a /engines/dm/projexpl.cpp | |
parent | d3d0819b00c0dddcb35d99561c2eeadf04791190 (diff) | |
parent | 4099814fbe6894931b7b393dc2f6ef5736297338 (diff) | |
download | scummvm-rg350-a46cc8597cdc2ab2aca146ed79c0fcf3f4df5617.tar.gz scummvm-rg350-a46cc8597cdc2ab2aca146ed79c0fcf3f4df5617.tar.bz2 scummvm-rg350-a46cc8597cdc2ab2aca146ed79c0fcf3f4df5617.zip |
Merge pull request #817 from WinterGrascph/dm
DM: New Engine
Diffstat (limited to 'engines/dm/projexpl.cpp')
-rw-r--r-- | engines/dm/projexpl.cpp | 562 |
1 files changed, 562 insertions, 0 deletions
diff --git a/engines/dm/projexpl.cpp b/engines/dm/projexpl.cpp new file mode 100644 index 0000000000..c9682a18e1 --- /dev/null +++ b/engines/dm/projexpl.cpp @@ -0,0 +1,562 @@ +/* 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 projectileThing = _vm->_dungeonMan->getUnusedThing(k14_ProjectileThingType); + if (projectileThing == 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; + + projectileThing = thingWithNewCell(projectileThing, cell); + Projectile *projectilePtr = (Projectile *)_vm->_dungeonMan->getThingData(projectileThing); + projectilePtr->_slot = thing; + projectilePtr->_kineticEnergy = MIN((int16)kineticEnergy, (int16)255); + projectilePtr->_attack = attack; + _vm->_dungeonMan->linkThingToList(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 */ + TimelineEvent newEvent; + setMapAndTime(newEvent._mapTime, _vm->_dungeonMan->_currMapIndex, _vm->_gameTime + 1); + if (_createLauncherProjectile) + newEvent._type = k49_TMEventTypeMoveProjectile; /* Launcher projectiles can impact immediately */ + else + newEvent._type = k48_TMEventTypeMoveProjectileIgnoreImpacts; /* Projectiles created by champions or creatures ignore impacts on their first movement */ + + newEvent._priority = 0; + newEvent._B._slot = projectileThing.toUint16(); + newEvent._C._projectile.setMapX(mapX); + newEvent._C._projectile.setMapY(mapY); + newEvent._C._projectile.setStepEnergy(stepEnergy); + newEvent._C._projectile.setDir(dir); + projectilePtr->_eventIndex = _vm->_timeline->addEventGetEventIndex(&newEvent); +} + +bool ProjExpl::hasProjectileImpactOccurred(int16 impactType, int16 mapXCombo, int16 mapYCombo, int16 cell, Thing projectileThing) { + Projectile *projectileThingData = (Projectile *)_vm->_dungeonMan->getThingData(Thing(projectileThing)); + bool removePotion = false; + int16 potionPower = 0; + _creatureDamageOutcome = k0_outcomeKilledNoCreaturesInGroup; + Thing projectileAssociatedThing = projectileThingData->_slot; + int16 projectileAssociatedThingType = projectileAssociatedThing.getType(); + Potion *potion = nullptr; + Thing explosionThing = Thing::_none; + if (projectileAssociatedThingType == k8_PotionThingType) { + Group *projectileAssociatedGroup = (Group *)_vm->_dungeonMan->getThingData(projectileAssociatedThing); + PotionType potionType = ((Potion *)projectileAssociatedGroup)->getType(); + if ((potionType == k3_PotionTypeVen) || (potionType == k19_PotionTypeFulBomb)) { + explosionThing = (potionType == k3_PotionTypeVen) ? Thing::_explPoisonCloud: Thing::_explFireBall; + removePotion = true; + potionPower = ((Potion *)projectileAssociatedGroup)->getPower(); + potion = (Potion *)projectileAssociatedGroup; + } + } + bool createExplosionOnImpact = (projectileAssociatedThingType == k15_ExplosionThingType) && (projectileAssociatedThing != Thing::_explSlime) && (projectileAssociatedThing != Thing::_explPoisonBolt); + Thing *curGroupSlot = nullptr; + int16 projectileMapX; + int16 projectileMapY; + int16 projectileTargetMapX = mapXCombo; + int16 projectileTargetMapY = mapYCombo; + + if (mapXCombo <= 255) { + projectileMapX = mapXCombo; + projectileMapY = mapYCombo; + } else { + projectileMapX = (mapXCombo >> 8) - 1; + projectileMapY = (mapYCombo >> 8); + projectileTargetMapX &= 0x00FF; + projectileTargetMapY &= 0x00FF; + } + + int16 championAttack = 0; + int16 attack = 0; + int16 championIndex = 0; + switch (impactType) { + case k4_DoorElemType: { + byte curSquare = _vm->_dungeonMan->_currMapData[projectileTargetMapX][projectileTargetMapY]; + int16 curDoorState = Square(curSquare).getDoorState(); + Door *curDoor = (Door *)_vm->_dungeonMan->getSquareFirstThingData(projectileTargetMapX, projectileTargetMapY); + if ((curDoorState != k5_doorState_DESTROYED) && (projectileAssociatedThing == Thing::_explOpenDoor)) { + if (curDoor->hasButton()) + _vm->_moveSens->addEvent(k10_TMEventTypeDoor, projectileTargetMapX, projectileTargetMapY, 0, k2_SensorEffToggle, _vm->_gameTime + 1); + break; + } + + if ((curDoorState == k5_doorState_DESTROYED) || (curDoorState <= k1_doorState_FOURTH)) + return false; + + DoorInfo curDoorInfo = _vm->_dungeonMan->_currMapDoorInfo[curDoor->getType()]; + if (getFlag(curDoorInfo._attributes, k0x0002_MaskDoorInfo_ProjectilesCanPassThrough)) { + if (projectileAssociatedThingType == k15_ExplosionThingType) { + if (projectileAssociatedThing.toUint16() >= Thing::_explHarmNonMaterial.toUint16()) + return false; + } else { + int16 associatedThingIndex = _vm->_dungeonMan->getObjectInfoIndex(projectileAssociatedThing); + uint16 associatedAllowedSlots = _vm->_dungeonMan->_objectInfos[associatedThingIndex].getAllowedSlots(); + int16 iconIndex = _vm->_objectMan->getIconIndex(projectileAssociatedThing); + + if ((projectileThingData->_attack > _vm->getRandomNumber(128)) + && getFlag(associatedAllowedSlots, k0x0100_ObjectAllowedSlotPouchPassAndThroughDoors) + && ( (projectileAssociatedThingType != k10_JunkThingType) + || (iconIndex < kDMIconIndiceJunkIronKey) + || (iconIndex > kDMIconIndiceJunkMasterKey) + )) { + return false; + } + } + } + attack = getProjectileImpactAttack(projectileThingData, projectileAssociatedThing) + 1; + _vm->_groupMan->groupIsDoorDestoryedByAttack(projectileTargetMapX, projectileTargetMapY, attack + _vm->getRandomNumber(attack), false, 0); + } + break; + case kM2_ChampionElemType: + championIndex = _vm->_championMan->getIndexInCell(cell); + if (championIndex < 0) + return false; + + championAttack = attack = getProjectileImpactAttack(projectileThingData, projectileAssociatedThing); + break; + case kM1_CreatureElemType: { + Group *curGroup = (Group *)_vm->_dungeonMan->getThingData(_vm->_groupMan->groupGetThing(projectileTargetMapX, projectileTargetMapY)); + uint16 curCreatureIndex = _vm->_groupMan->getCreatureOrdinalInCell(curGroup, cell); + if (!curCreatureIndex) + return false; + + curCreatureIndex--; + uint16 curCreatureType = curGroup->_type; + CreatureInfo *curCreatureInfo = &_vm->_dungeonMan->_creatureInfos[curCreatureType]; + if ((projectileAssociatedThing == Thing::_explFireBall) && (curCreatureType == k11_CreatureTypeBlackFlame)) { + uint16 *curCreatureHealth = &curGroup->_health[curCreatureIndex]; + *curCreatureHealth = MIN(1000, *curCreatureHealth + getProjectileImpactAttack(projectileThingData, projectileAssociatedThing)); + goto T0217044; + } + if (getFlag(curCreatureInfo->_attributes, k0x0040_MaskCreatureInfo_nonMaterial) && (projectileAssociatedThing != Thing::_explHarmNonMaterial)) + return false; + + attack = (uint16)((unsigned long)getProjectileImpactAttack(projectileThingData, projectileAssociatedThing) << 6) / curCreatureInfo->_defense; + if (attack) { + int16 outcome = _vm->_groupMan->groupGetDamageCreatureOutcome(curGroup, curCreatureIndex, projectileTargetMapX, projectileTargetMapY, attack + _vm->_groupMan->groupGetResistanceAdjustedPoisonAttack(curCreatureType, _projectilePoisonAttack), true); + if (outcome != k0_outcomeKilledNoCreaturesInGroup) + _vm->_groupMan->processEvents29to41(projectileTargetMapX, projectileTargetMapY, kM2_TMEventTypeCreateReactionEvent30HitByProjectile, 0); + + _creatureDamageOutcome = outcome; + if (!createExplosionOnImpact && (outcome == k0_outcomeKilledNoCreaturesInGroup) + && (projectileAssociatedThingType == k5_WeaponThingType) + && getFlag(curCreatureInfo->_attributes, k0x0400_MaskCreatureInfo_keepThrownSharpWeapon)) { + Weapon *weapon = (Weapon *)_vm->_dungeonMan->getThingData(projectileAssociatedThing); + WeaponType weaponType = weapon->getType(); + if ((weaponType == k8_WeaponTypeDagger) || (weaponType == k27_WeaponTypeArrow) + || (weaponType == k28_WeaponTypeSlayer) || (weaponType == k31_WeaponTypePoisonDart) + || (weaponType == k32_WeaponTypeThrowingStar)) + curGroupSlot = &curGroup->_slot; + } + } + } + break; + } + if (championAttack && _projectilePoisonAttack && _vm->getRandomNumber(2) + && _vm->_championMan->addPendingDamageAndWounds_getDamage(championIndex, attack, kDMWoundHead | kDMWoundTorso, _projectileAttackType)) + _vm->_championMan->championPoison(championIndex, _projectilePoisonAttack); + + if (createExplosionOnImpact || removePotion) { + uint16 explosionAttack; + if (removePotion) { + projectileAssociatedThing = explosionThing; + explosionAttack = potionPower; + } else { + explosionAttack = projectileThingData->_kineticEnergy; + } + if ((projectileAssociatedThing == Thing::_explLightningBolt) && !(explosionAttack >>= 1)) + goto T0217044; + createExplosion(projectileAssociatedThing, explosionAttack, mapXCombo, mapYCombo, (projectileAssociatedThing == Thing::_explPoisonCloud) ? k255_CreatureTypeSingleCenteredCreature : cell); + } else { + uint16 soundIndex; + if ((projectileAssociatedThing).getType() == k5_WeaponThingType) + soundIndex = k00_soundMETALLIC_THUD; + else if (projectileAssociatedThing == Thing::_explPoisonBolt) + soundIndex = k13_soundSPELL; + else + soundIndex = k04_soundWOODEN_THUD_ATTACK_TROLIN_ANTMAN_STONE_GOLEM; + + _vm->_sound->requestPlay(soundIndex, projectileMapX, projectileMapY, k1_soundModePlayIfPrioritized); + } +T0217044: + if (removePotion) { + potion->_nextThing = Thing::_none; + projectileThingData->_slot = explosionThing; + } + _vm->_dungeonMan->unlinkThingFromList(projectileThing, Thing(0), projectileMapX, projectileMapY); + projectileDelete(projectileThing, curGroupSlot, projectileMapX, projectileMapY); + return true; +} + +uint16 ProjExpl::getProjectileImpactAttack(Projectile *projectile, Thing thing) { + _projectilePoisonAttack = 0; + _projectileAttackType = kDMAttackTypeBlunt; + + uint16 kineticEnergy = projectile->_kineticEnergy; + ThingType thingType = thing.getType(); + uint16 attack; + if (thingType != k15_ExplosionThingType) { + if (thingType == k5_WeaponThingType) { + WeaponInfo *weaponInfo = _vm->_dungeonMan->getWeaponInfo(thing); + attack = weaponInfo->_kineticEnergy; + _projectileAttackType = kDMAttackTypeBlunt; + } else + attack = _vm->getRandomNumber(4); + + attack += _vm->_dungeonMan->getObjectWeight(thing) >> 1; + } else if (thing == Thing::_explSlime) { + attack = _vm->getRandomNumber(16); + _projectilePoisonAttack = attack + 10; + attack += _vm->getRandomNumber(32); + } else { + if (thing.toUint16() >= Thing::_explHarmNonMaterial.toUint16()) { + _projectileAttackType = kDMAttackTypeMagic; + if (thing == Thing::_explPoisonBolt) { + _projectilePoisonAttack = kineticEnergy; + return 1; + } + return 0; + } + _projectileAttackType = kDMAttackTypeFire; + attack = _vm->getRandomNumber(16) + _vm->getRandomNumber(16) + 10; + if (thing == Thing::_explLightningBolt) { + _projectileAttackType = kDMAttackTypeLightning; + attack *= 5; + } + } + attack = ((attack + kineticEnergy) >> 4) + 1; + attack += _vm->getRandomNumber((attack >> 1) + 1) + _vm->getRandomNumber(4); + attack = MAX(attack >> 1, attack - (32 - (projectile->_attack >> 3))); + return attack; +} + +void ProjExpl::createExplosion(Thing explThing, uint16 attack, uint16 mapXCombo, uint16 mapYCombo, uint16 cell) { + Thing unusedThing = _vm->_dungeonMan->getUnusedThing(k15_ExplosionThingType); + if (unusedThing == Thing::_none) + return; + + Explosion *explosion = &((Explosion *)_vm->_dungeonMan->_thingData[k15_ExplosionThingType])[(unusedThing).getIndex()]; + int16 projectileTargetMapX; + int16 projectileTargetMapY; + uint16 projectileMapX = mapXCombo; + uint16 projectileMapY = mapYCombo; + + if (mapXCombo <= 255) { + projectileTargetMapX = mapXCombo; + projectileTargetMapY = mapYCombo; + } else { + projectileTargetMapX = mapXCombo & 0x00FF; + projectileTargetMapY = mapYCombo & 0x00FF; + projectileMapX >>= 8; + projectileMapX--; + projectileMapY >>= 8; + } + + if (cell == k255_CreatureTypeSingleCenteredCreature) + explosion->setCentered(true); + else { + explosion->setCentered(false); + unusedThing = thingWithNewCell(unusedThing, cell); + } + + explosion->setType(explThing.toUint16() - Thing::_firstExplosion.toUint16()); + explosion->setAttack(attack); + if (explThing.toUint16() < Thing::_explHarmNonMaterial.toUint16()) { + uint16 soundIndex = (attack > 80) ? k05_soundSTRONG_EXPLOSION : k20_soundWEAK_EXPLOSION; + _vm->_sound->requestPlay(soundIndex, projectileMapX, projectileMapY, k1_soundModePlayIfPrioritized); + } else if (explThing != Thing::_explSmoke) + _vm->_sound->requestPlay(k13_soundSPELL, projectileMapX, projectileMapY, k1_soundModePlayIfPrioritized); + + _vm->_dungeonMan->linkThingToList(unusedThing, Thing(0), projectileMapX, projectileMapY); + TimelineEvent newEvent; + setMapAndTime(newEvent._mapTime, _vm->_dungeonMan->_currMapIndex, _vm->_gameTime + ((explThing == Thing::_explRebirthStep1) ? 5 : 1)); + newEvent._type = k25_TMEventTypeExplosion; + newEvent._priority = 0; + newEvent._C._slot = unusedThing.toUint16(); + newEvent._B._location._mapX = projectileMapX; + newEvent._B._location._mapY = projectileMapY; + _vm->_timeline->addEventGetEventIndex(&newEvent); + if ((explThing == Thing::_explLightningBolt) || (explThing == Thing::_explFireBall)) { + projectileMapX = projectileTargetMapX; + projectileMapY = projectileTargetMapY; + attack = (attack >> 1) + 1; + attack += _vm->getRandomNumber(attack) + 1; + if ((explThing == Thing::_explFireBall) || (attack >>= 1)) { + if ((_vm->_dungeonMan->_currMapIndex == _vm->_dungeonMan->_partyMapIndex) && (projectileMapX == _vm->_dungeonMan->_partyMapX) && (projectileMapY == _vm->_dungeonMan->_partyMapY)) { + int16 wounds = kDMWoundReadHand | kDMWoundActionHand | kDMWoundHead | kDMWoundTorso | kDMWoundLegs | kDMWoundFeet; + _vm->_championMan->getDamagedChampionCount(attack, wounds, kDMAttackTypeFire); + } else { + unusedThing = _vm->_groupMan->groupGetThing(projectileMapX, projectileMapY); + if (unusedThing != Thing::_endOfList) { + Group *creatureGroup = (Group *)_vm->_dungeonMan->getThingData(unusedThing); + CreatureInfo *creatureInfo = &_vm->_dungeonMan->_creatureInfos[creatureGroup->_type]; + int16 creatureFireResistance = creatureInfo->getFireResistance(); + if (creatureFireResistance != k15_immuneToFire) { + if (getFlag(creatureInfo->_attributes, k0x0040_MaskCreatureInfo_nonMaterial)) + attack >>= 2; + + if ((attack -= _vm->getRandomNumber((creatureFireResistance << 1) + 1)) > 0) + _creatureDamageOutcome = _vm->_groupMan->getDamageAllCreaturesOutcome(creatureGroup, projectileMapX, projectileMapY, attack, true); + + } + } + } + } + } +} + +int16 ProjExpl::projectileGetImpactCount(int16 impactType, int16 mapX, int16 mapY, int16 cell) { + int16 impactCount = 0; + _creatureDamageOutcome = k0_outcomeKilledNoCreaturesInGroup; + + for (Thing curThing = _vm->_dungeonMan->getSquareFirstThing(mapX, mapY); curThing != Thing::_endOfList; ) { + if (((curThing).getType() == k14_ProjectileThingType) && ((curThing).getCell() == cell) && + hasProjectileImpactOccurred(impactType, mapX, mapY, cell, curThing)) { + projectileDeleteEvent(curThing); + impactCount++; + if ((impactType == kM1_CreatureElemType) && (_creatureDamageOutcome == k2_outcomeKilledAllCreaturesInGroup)) + break; + + Thing curThing = _vm->_dungeonMan->getSquareFirstThing(mapX, mapY); + } else + curThing = _vm->_dungeonMan->getNextThing(curThing); + } + return impactCount; +} + +void ProjExpl::projectileDeleteEvent(Thing thing) { + Projectile *projectile = (Projectile *)_vm->_dungeonMan->getThingData(thing); + _vm->_timeline->deleteEvent(projectile->_eventIndex); +} + +void ProjExpl::projectileDelete(Thing projectileThing, Thing *groupSlot, int16 mapX, int16 mapY) { + Projectile *projectile = (Projectile *)_vm->_dungeonMan->getThingData(projectileThing); + Thing projectileSlotThing = projectile->_slot; + if (projectileSlotThing.getType() != k15_ExplosionThingType) { + if (groupSlot != NULL) { + Thing previousThing = *groupSlot; + if (previousThing == Thing::_endOfList) { + Thing *genericThing = (Thing *)_vm->_dungeonMan->getThingData(projectileSlotThing); + *genericThing = Thing::_endOfList; + *groupSlot = projectileSlotThing; + } else + _vm->_dungeonMan->linkThingToList(projectileSlotThing, previousThing, kM1_MapXNotOnASquare, 0); + } else + _vm->_moveSens->getMoveResult(Thing((projectileSlotThing).getTypeAndIndex() | getFlag(projectileThing.toUint16(), 0xC)), -2, 0, mapX, mapY); + } + projectile->_nextThing = Thing::_none; +} + +void ProjExpl::processEvents48To49(TimelineEvent *event) { + int16 sourceMapX = -1; + int16 sourceMapY = -1; + TimelineEvent firstEvent = *event; + TimelineEvent *curEvent = &firstEvent; + Thing projectileThingNewCell = Thing(curEvent->_B._slot); + Thing projectileThing = projectileThingNewCell; + Projectile *projectile = (Projectile *)_vm->_dungeonMan->getThingData(projectileThing); + int16 destinationMapX = curEvent->_C._projectile.getMapX(); + int16 destinationMapY = curEvent->_C._projectile.getMapY(); + + if (curEvent->_type == k48_TMEventTypeMoveProjectileIgnoreImpacts) + curEvent->_type = k49_TMEventTypeMoveProjectile; + else { + uint16 projectileCurCell = projectileThingNewCell.getCell(); + if ((_vm->_dungeonMan->_currMapIndex == _vm->_dungeonMan->_partyMapIndex) && (destinationMapX == _vm->_dungeonMan->_partyMapX) && (destinationMapY == _vm->_dungeonMan->_partyMapY) && hasProjectileImpactOccurred(kM2_ChampionElemType, destinationMapX, destinationMapY, projectileCurCell, projectileThingNewCell)) + return; + + if ((_vm->_groupMan->groupGetThing(destinationMapX, destinationMapY) != Thing::_endOfList) && hasProjectileImpactOccurred(kM1_CreatureElemType, destinationMapX, destinationMapY, projectileCurCell, projectileThing)) + return; + + uint16 stepEnergy = curEvent->_C._projectile.getStepEnergy(); + if (projectile->_kineticEnergy <= stepEnergy) { + _vm->_dungeonMan->unlinkThingFromList(projectileThingNewCell = projectileThing, Thing(0), destinationMapX, destinationMapY); + projectileDelete(projectileThingNewCell, NULL, destinationMapX, destinationMapY); + return; + } + projectile->_kineticEnergy -= stepEnergy; + if (projectile->_attack < stepEnergy) + projectile->_attack = 0; + else + projectile->_attack -= stepEnergy; + } + uint16 projectileDirection = curEvent->_C._projectile.getDir(); + projectileThingNewCell = Thing(curEvent->_B._slot); + uint16 projectileNewCell = projectileThingNewCell.getCell(); + bool projectileMovesToOtherSquare = (projectileDirection == projectileNewCell) || (returnNextVal(projectileDirection) == projectileNewCell); + if (projectileMovesToOtherSquare) { + sourceMapX = destinationMapX; + sourceMapY = destinationMapY; + destinationMapX += _vm->_dirIntoStepCountEast[projectileDirection], destinationMapY += _vm->_dirIntoStepCountNorth[projectileDirection]; + Square destSquare = _vm->_dungeonMan->getSquare(destinationMapX, destinationMapY); + SquareType destSquareType = destSquare.getType(); + if ((destSquareType == k0_WallElemType) || + ((destSquareType == k6_FakeWallElemType) && !getFlag(destSquare.toByte(), (k0x0001_FakeWallImaginary | k0x0004_FakeWallOpen))) || + ((destSquareType == k3_StairsElemType) && (Square(_vm->_dungeonMan->_currMapData[sourceMapX][sourceMapY]).getType() == k3_StairsElemType))) { + if (hasProjectileImpactOccurred(destSquare.getType(), sourceMapX, sourceMapY, projectileNewCell, projectileThingNewCell)) { + return; + } + } + } + + if ((projectileDirection & 0x0001) == (projectileNewCell & 0x0001)) + projectileNewCell--; + else + projectileNewCell++; + + projectileThingNewCell = thingWithNewCell(projectileThingNewCell, projectileNewCell &= 0x0003); + if (projectileMovesToOtherSquare) { + _vm->_moveSens->getMoveResult(projectileThingNewCell, sourceMapX, sourceMapY, destinationMapX, destinationMapY); + curEvent->_C._projectile.setMapX(_vm->_moveSens->_moveResultMapX); + curEvent->_C._projectile.setMapY(_vm->_moveSens->_moveResultMapY); + curEvent->_C._projectile.setDir((Direction)_vm->_moveSens->_moveResultDir); + projectileThingNewCell = thingWithNewCell(projectileThingNewCell, _vm->_moveSens->_moveResultCell); + M31_setMap(curEvent->_mapTime, _vm->_moveSens->_moveResultMapIndex); + } else { + if ((Square(_vm->_dungeonMan->getSquare(destinationMapX, destinationMapY)).getType() == k4_DoorElemType) && hasProjectileImpactOccurred(k4_DoorElemType, destinationMapX, destinationMapY, projectileNewCell, projectileThing)) + return; + + _vm->_dungeonMan->unlinkThingFromList(projectileThingNewCell, Thing(0), destinationMapX, destinationMapY); + _vm->_dungeonMan->linkThingToList(projectileThingNewCell, Thing(0), destinationMapX, destinationMapY); + } + + // This code is from CSB20. The projectiles move at the same speed on all maps instead of moving slower on maps other than the party map */ + curEvent->_mapTime++; + + curEvent->_B._slot = projectileThingNewCell.toUint16(); + projectile->_eventIndex = _vm->_timeline->addEventGetEventIndex(curEvent); +} + +void ProjExpl::processEvent25(TimelineEvent *event) { + uint16 mapX = event->_B._location._mapX; + uint16 mapY = event->_B._location._mapY; + Explosion *explosion = &((Explosion *)_vm->_dungeonMan->_thingData[k15_ExplosionThingType])[Thing((event->_C._slot)).getIndex()]; + int16 curSquareType = Square(_vm->_dungeonMan->_currMapData[mapX][mapY]).getType(); + bool explosionOnPartySquare = (_vm->_dungeonMan->_currMapIndex == _vm->_dungeonMan->_partyMapIndex) && (mapX == _vm->_dungeonMan->_partyMapX) && (mapY == _vm->_dungeonMan->_partyMapY); + Thing groupThing = _vm->_groupMan->groupGetThing(mapX, mapY); + + Group *group = nullptr; + CreatureInfo *creatureInfo = nullptr; + + uint16 creatureType = 0; + if (groupThing != Thing::_endOfList) { + group = (Group *)_vm->_dungeonMan->getThingData(groupThing); + creatureType = group->_type; + creatureInfo = &_vm->_dungeonMan->_creatureInfos[creatureType]; + } + + Thing explosionThing = Thing(Thing::_firstExplosion.toUint16() + explosion->getType()); + int16 attack; + if (explosionThing == Thing::_explPoisonCloud) + attack = MAX(1, MIN(explosion->getAttack() >> 5, 4) + _vm->getRandomNumber(2)); /* Value between 1 and 5 */ + else { + attack = (explosion->getAttack() >> 1) + 1; + attack += _vm->getRandomNumber(attack) + 1; + } + + bool AddEventFl = false; + + switch (explosionThing.toUint16()) { + case 0xFF82: + if (!(attack >>= 1)) + break; + case 0xFF80: + if (curSquareType == k4_DoorElemType) + _vm->_groupMan->groupIsDoorDestoryedByAttack(mapX, mapY, attack, true, 0); + + break; + case 0xFF83: + if ((groupThing != Thing::_endOfList) && getFlag(creatureInfo->_attributes, k0x0040_MaskCreatureInfo_nonMaterial)) { + if ((creatureType == k19_CreatureTypeMaterializerZytaz) && (_vm->_dungeonMan->_currMapIndex == _vm->_dungeonMan->_partyMapIndex)) { + int16 nonMaterialAdditionalAttack = attack >> 3; + attack -= nonMaterialAdditionalAttack; + nonMaterialAdditionalAttack <<= 1; + nonMaterialAdditionalAttack++; + int16 creatureCount = group->getCount(); + do { + if (getFlag(_vm->_groupMan->_activeGroups[group->getActiveGroupIndex()]._aspect[creatureCount], k0x0080_MaskActiveGroupIsAttacking)) /* Materializer / Zytaz can only be damaged while they are attacking */ + _vm->_groupMan->groupGetDamageCreatureOutcome(group, creatureCount, mapX, mapY, attack + _vm->getRandomNumber(nonMaterialAdditionalAttack) + _vm->getRandomNumber(4), true); + } while (--creatureCount >= 0); + } else + _vm->_groupMan->getDamageAllCreaturesOutcome(group, mapX, mapY, attack, true); + } + break; + case 0xFFE4: + explosion->setType(explosion->getType() + 1); + _vm->_sound->requestPlay(k05_soundSTRONG_EXPLOSION, mapX, mapY, k1_soundModePlayIfPrioritized); + AddEventFl = true; + break; + case 0xFFA8: + if (explosion->getAttack() > 55) { + explosion->setAttack(explosion->getAttack() - 40); + AddEventFl = true; + } + break; + case 0xFF87: + if (explosionOnPartySquare) + _vm->_championMan->getDamagedChampionCount(attack, kDMWoundNone, kDMAttackTypeNormal); + else if ((groupThing != Thing::_endOfList) + && (attack = _vm->_groupMan->groupGetResistanceAdjustedPoisonAttack(creatureType, attack)) + && (_vm->_groupMan->getDamageAllCreaturesOutcome(group, mapX, mapY, attack, true) != k2_outcomeKilledAllCreaturesInGroup) + && (attack > 2)) { + _vm->_groupMan->processEvents29to41(mapX, mapY, kM3_TMEventTypeCreateReactionEvent29DangerOnSquare, 0); + } + if (explosion->getAttack() >= 6) { + explosion->setAttack(explosion->getAttack() - 3); + AddEventFl = true; + } + break; + } + if (AddEventFl) { + TimelineEvent newEvent; + newEvent = *event; + newEvent._mapTime++; + _vm->_timeline->addEventGetEventIndex(&newEvent); + } else { + _vm->_dungeonMan->unlinkThingFromList(Thing(event->_C._slot), Thing(0), mapX, mapY); + explosion->setNextThing(Thing::_none); + } +} +} |