aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/dm/TODOs/todo.txt7
-rw-r--r--engines/dm/champion.cpp737
-rw-r--r--engines/dm/champion.h60
-rw-r--r--engines/dm/dm.cpp63
-rw-r--r--engines/dm/dm.h117
-rw-r--r--engines/dm/dmglobals.cpp11
-rw-r--r--engines/dm/dungeonman.cpp342
-rw-r--r--engines/dm/dungeonman.h110
-rw-r--r--engines/dm/eventman.cpp470
-rw-r--r--engines/dm/eventman.h34
-rw-r--r--engines/dm/gfx.cpp134
-rw-r--r--engines/dm/gfx.h25
-rw-r--r--engines/dm/group.cpp1639
-rw-r--r--engines/dm/group.h103
-rw-r--r--engines/dm/inventory.cpp92
-rw-r--r--engines/dm/inventory.h1
-rw-r--r--engines/dm/menus.cpp30
-rw-r--r--engines/dm/menus.h1
-rw-r--r--engines/dm/module.mk1
-rw-r--r--engines/dm/movesens.cpp1094
-rw-r--r--engines/dm/movesens.h33
-rw-r--r--engines/dm/objectman.cpp6
-rw-r--r--engines/dm/objectman.h1
-rw-r--r--engines/dm/projexpl.cpp418
-rw-r--r--engines/dm/projexpl.h101
-rw-r--r--engines/dm/text.cpp82
-rw-r--r--engines/dm/text.h11
-rw-r--r--engines/dm/timeline.cpp153
-rw-r--r--engines/dm/timeline.h6
29 files changed, 5509 insertions, 373 deletions
diff --git a/engines/dm/TODOs/todo.txt b/engines/dm/TODOs/todo.txt
index 5ef225f869..183889e994 100644
--- a/engines/dm/TODOs/todo.txt
+++ b/engines/dm/TODOs/todo.txt
@@ -7,7 +7,7 @@ Bugs:
Command gui is broken
Logic:
Game crashes when reincaranting a fourth champion and trying to copy his portrait
-
+e
Todo:
@@ -18,7 +18,7 @@ Todo:
Attend to sev's notes on github
Double check enums with hex literals, I think I screwed the regex when processing them
Double check strcat, strstr usages, I might have messed them up in many places
- I forgot to add a bunch of warning for show/hide mouse pointer
+ I forgot to add a bunch of warning for show/hide mouse pointer and other mouse functions
Missing functions:
Add missing F0163_DUNGEON_LinkThingToList in MovesensMan::sensorIsTriggeredByClickOnWall (I don't think it's safe yet)
@@ -26,6 +26,3 @@ Todo:
Refactoring
Add constructor to CreatureInfo
-Forgot to add number to name:
- gBoxChampionPortrait, gBoxEye, gBoxMouth, gSlotMasks
- \ No newline at end of file
diff --git a/engines/dm/champion.cpp b/engines/dm/champion.cpp
index b35e0d8f5a..5c1f5466a7 100644
--- a/engines/dm/champion.cpp
+++ b/engines/dm/champion.cpp
@@ -32,10 +32,15 @@
#include "inventory.h"
#include "objectman.h"
#include "text.h"
+#include "timeline.h"
+#include "projexpl.h"
+#include "group.h"
namespace DM {
+const char *g417_baseSkillName[4] = {"FIGHTER", "NINJA", "PRIEST", "WIZARD"};
+
Box gBoxMouth = Box(55, 72, 12, 29); // @ G0048_s_Graphic562_Box_Mouth
Box gBoxEye = Box(11, 28, 12, 29); // @ G0049_s_Graphic562_Box_Eye
Box g54_BoxChampionIcons[4] = {
@@ -98,6 +103,81 @@ ChampionMan::ChampionMan(DMEngine *vm) : _vm(vm) {
_g413_leaderHandObjectIconIndex = kM1_IconIndiceNone;
_g415_leaderEmptyHanded = true;
_g514_magicCasterChampionIndex = kM1_ChampionNone;
+ for (uint16 i = 0; i < 4; ++i) {
+ _g409_championPendingDamage[i] = 0;
+ _g410_championPendingWounds[i] = 0;
+ }
+
+}
+
+bool ChampionMan::f329_isLeaderHandObjectThrown(int16 side) {
+ if (_g411_leaderIndex == kM1_ChampionNone) {
+ return false;
+ }
+ return f328_isObjectThrown(_g411_leaderIndex, kM1_ChampionSlotLeaderHand, side);
+}
+
+bool ChampionMan::f328_isObjectThrown(uint16 champIndex, int16 slotIndex, int16 side) {
+ int16 L0993_i_KineticEnergy;
+ int16 L0994_i_Multiple;
+#define AL0994_i_Experience L0994_i_Multiple
+#define AL0994_i_Attack L0994_i_Multiple
+ int16 L0995_i_Multiple;
+#define AL0995_i_WeaponKineticEnergy L0995_i_Multiple
+#define AL0995_i_SkillLevel L0995_i_Multiple
+#define AL0995_i_StepEnergy L0995_i_Multiple
+ Thing L0996_T_Thing;
+ Champion* L0997_ps_Champion = nullptr;
+ WeaponInfo* L0998_ps_WeaponInfo;
+ Thing L0999_T_ActionHandThing;
+ bool L1000_B_ThrowingLeaderHandObject;
+
+
+ L1000_B_ThrowingLeaderHandObject = false;
+ if (slotIndex < 0) { /* Throw object in leader hand, which is temporarily placed in action hand */
+ if (_g415_leaderEmptyHanded) {
+ return false;
+ }
+ L0996_T_Thing = f298_getObjectRemovedFromLeaderHand();
+ L0997_ps_Champion = &_gK71_champions[champIndex];
+ L0999_T_ActionHandThing = L0997_ps_Champion->getSlot(k1_ChampionSlotActionHand);
+ L0997_ps_Champion->setSlot(k1_ChampionSlotActionHand, L0996_T_Thing);
+ slotIndex = k1_ChampionSlotActionHand;
+ L1000_B_ThrowingLeaderHandObject = true;
+ }
+ L0993_i_KineticEnergy = f312_getStrength(champIndex, slotIndex);
+ if (L1000_B_ThrowingLeaderHandObject) {
+ L0997_ps_Champion->setSlot((ChampionSlot)slotIndex, L0999_T_ActionHandThing);
+ } else {
+ if ((L0996_T_Thing = f300_getObjectRemovedFromSlot(champIndex, slotIndex)) == Thing::_none) {
+ return false;
+ }
+ }
+ warning("MISSING CODE: F0064_SOUND_RequestPlay_CPSD");
+ f325_decrementStamine(champIndex, f305_getThrowingStaminaCost(L0996_T_Thing));
+ f330_disableAction(champIndex, 4);
+ AL0994_i_Experience = 8;
+ AL0995_i_WeaponKineticEnergy = 1;
+ if (L0996_T_Thing.getType() == k5_WeaponThingType) {
+ AL0994_i_Experience += 4;
+ L0998_ps_WeaponInfo = _vm->_dungeonMan->f158_getWeaponInfo(L0996_T_Thing);
+ if (L0998_ps_WeaponInfo->_class <= k12_WeaponClassPoisinDart) {
+ AL0994_i_Experience += (AL0995_i_WeaponKineticEnergy = L0998_ps_WeaponInfo->_kineticEnergy) >> 2;
+ }
+ }
+ f304_addSkillExperience(champIndex, k10_ChampionSkillThrow, AL0994_i_Experience);
+ L0993_i_KineticEnergy += AL0995_i_WeaponKineticEnergy;
+ AL0995_i_SkillLevel = f303_getSkillLevel((ChampionIndex)champIndex, k10_ChampionSkillThrow);
+ L0993_i_KineticEnergy += _vm->_rnd->getRandomNumber(16) + (L0993_i_KineticEnergy >> 1) + AL0995_i_SkillLevel;
+ AL0994_i_Attack = f26_getBoundedValue((uint16)40, (uint16)((AL0995_i_SkillLevel << 3) + _vm->_rnd->getRandomNumber(31)), (uint16)200);
+ AL0995_i_StepEnergy = MAX(5, 11 - AL0995_i_SkillLevel);
+ _vm->_projexpl->f212_projectileCreate(L0996_T_Thing, _vm->_dungeonMan->_g306_partyMapX, _vm->_dungeonMan->_g307_partyMapY,
+ M21_normalizeModulo4(_vm->_dungeonMan->_g308_partyDir + side),
+ _vm->_dungeonMan->_g308_partyDir, L0993_i_KineticEnergy, AL0994_i_Attack, AL0995_i_StepEnergy);
+ _vm->_g311_projectileDisableMovementTicks = 4;
+ _vm->_g312_lastProjectileDisabledMovementDirection = _vm->_dungeonMan->_g308_partyDir;
+ f292_drawChampionState((ChampionIndex)champIndex);
+ return true;
}
uint16 ChampionMan::M27_getChampionPortraitX(uint16 index) {
@@ -138,7 +218,7 @@ Common::String ChampionMan::f288_getStringFromInteger(uint16 val, bool padding,
return result += valToStr;
}
-void ChampionMan::f299_applyModifiersToStatistics(Champion* champ, ChampionSlot slotIndex, IconIndice iconIndex, int16 modifierFactor, Thing thing) {
+void ChampionMan::f299_applyModifiersToStatistics(Champion* champ, int16 slotIndex, int16 iconIndex, int16 modifierFactor, Thing thing) {
int16 statIndex;
int16 modifier = 0;
ThingType thingType = thing.getType();
@@ -266,7 +346,6 @@ T0299044_ApplyModifier:
} else if (statIndex < k6_ChampionStatAntifire + 1) {
for (uint16 statValIndex = k0_ChampionStatMaximum; statValIndex <= k2_ChampionStatMinimum; ++statValIndex) {
champ->getStatistic((ChampionStatisticType)statIndex, (ChampionStatisticValue)statValIndex) += modifier;
- warning("BUG0_38");
}
}
}
@@ -284,7 +363,7 @@ bool ChampionMan::f295_hasObjectIconInSlotBoxChanged(int16 slotBoxIndex, Thing t
if (newIconIndex != currIconIndex) {
if ((slotBoxIndex < k8_SlotBoxInventoryFirstSlot) && !_g420_mousePointerHiddenToDrawChangedObjIconOnScreen) {
_g420_mousePointerHiddenToDrawChangedObjIconOnScreen = true;
- warning("MISSING CODE: F0077_MOUSE_HidePointer_CPSE");
+ _vm->_eventMan->f77_hideMouse();
}
objMan.f38_drawIconInSlotBox(slotBoxIndex, newIconIndex);
return true;
@@ -312,9 +391,9 @@ void ChampionMan::f296_drawChangedObjectIcons() {
IconIndice iconIndex = objMan.f33_getIconIndex(_g414_leaderHandObject);
if (iconIndex != leaderHandObjIconIndex) {
_g420_mousePointerHiddenToDrawChangedObjIconOnScreen = true;
- warning("MISSING CODE: F0077_MOUSE_HidePointer_CPSE");
+ _vm->_eventMan->f77_hideMouse();
objMan.f36_extractIconFromBitmap(iconIndex, objMan._g412_objectIconForMousePointer);
- warning("MISSING CODE: F0068_MOUSE_SetPointerToObject");
+ _vm->_eventMan->f68_setPointerToObject(_vm->_objectMan->_g412_objectIconForMousePointer);
_g413_leaderHandObjectIconIndex = iconIndex;
objMan.f34_drawLeaderObjectName(_g414_leaderHandObject);
}
@@ -359,7 +438,7 @@ void ChampionMan::f296_drawChangedObjectIcons() {
}
if (_g420_mousePointerHiddenToDrawChangedObjIconOnScreen)
- warning("MISSING CODE: F0078_MOUSE_ShowPointer");
+ _vm->_eventMan->f78_showMouse();
}
void ChampionMan::f301_addObjectInSlot(ChampionIndex champIndex, Thing thing, ChampionSlot slotIndex) {
@@ -401,7 +480,7 @@ void ChampionMan::f301_addObjectInSlot(ChampionIndex champIndex, Thing thing, Ch
if (iconIndex = k4_IconIndiceWeaponTorchUnlit) {
((Weapon*)rawObjPtr)->setLit(true);
- warning("MISSING CODE: F0337_INVENTORY_SetDungeonViewPalette");
+ _vm->_inventoryMan->f337_setDungeonViewPalette();
f296_drawChangedObjectIcons();
} else if (isInventoryChampion && (slotIndex == k1_ChampionSlotActionHand) &&
((iconIndex == k144_IconIndiceContainerChestClosed) || ((iconIndex >= k30_IconIndiceScrollOpen) && (iconIndex <= k31_IconIndiceScrollClosed)))) {
@@ -413,7 +492,7 @@ void ChampionMan::f301_addObjectInSlot(ChampionIndex champIndex, Thing thing, Ch
if ((iconIndex >= k12_IconIndiceJunkIllumuletUnequipped) && (iconIndex <= k13_IconIndiceJunkIllumuletEquipped)) {
((Junk*)rawObjPtr)->setChargeCount(1);
_g407_party._magicalLightAmount += g39_LightPowerToLightAmount[2];
- warning("MISSING CODE: F0337_INVENTORY_SetDungeonViewPalette");
+ _vm->_inventoryMan->f337_setDungeonViewPalette();
iconIndex = (IconIndice)(iconIndex + 1);
} else if ((iconIndex >= k10_IconIndiceJunkJewelSymalUnequipped) && (iconIndex <= k11_IconIndiceJunkJewelSymalEquipped)) {
((Junk*)rawObjPtr)->setChargeCount(1);
@@ -449,7 +528,627 @@ int16 ChampionMan::f315_getScentOrdinal(int16 mapX, int16 mapY) {
return 0;
}
-ChampionIndex ChampionMan::f285_getIndexInCell(ViewCell cell) {
+Thing ChampionMan::f298_getObjectRemovedFromLeaderHand() {
+ Thing L0890_T_LeaderHandObject;
+
+
+ _g415_leaderEmptyHanded = true;
+ if ((L0890_T_LeaderHandObject = _g414_leaderHandObject) != Thing::_none) {
+ _g414_leaderHandObject = Thing::_none;
+ _g413_leaderHandObjectIconIndex = kM1_IconIndiceNone;
+ _vm->_eventMan->f78_showMouse();
+ _vm->_objectMan->f35_clearLeaderObjectName();
+ _vm->_eventMan->f69_setMousePointer();
+ _vm->_eventMan->f77_hideMouse();
+ if (_g411_leaderIndex != kM1_ChampionNone) {
+ _gK71_champions[_g411_leaderIndex]._load -= _vm->_dungeonMan->f140_getObjectWeight(L0890_T_LeaderHandObject);
+ setFlag(_gK71_champions[_g411_leaderIndex]._attributes, k0x0200_ChampionAttributeLoad);
+ f292_drawChampionState(_g411_leaderIndex);
+ }
+ }
+ return L0890_T_LeaderHandObject;
+}
+
+uint16 ChampionMan::f312_getStrength(int16 champIndex, int16 slotIndex) {
+ int16 L0935_i_Strength;
+ uint16 L0936_ui_Multiple;
+#define AL0936_ui_ObjectWeight L0936_ui_Multiple
+#define AL0936_ui_SkillLevel L0936_ui_Multiple
+ uint16 L0937_ui_Multiple;
+#define AL0937_ui_OneSixteenthMaximumLoad L0937_ui_Multiple
+#define AL0937_ui_Class L0937_ui_Multiple
+ Thing L0938_T_Thing;
+ Champion* L0939_ps_Champion;
+ WeaponInfo* L0940_ps_WeaponInfo;
+ int16 L0941_i_LoadThreshold;
+
+
+ L0939_ps_Champion = &_gK71_champions[champIndex];
+ L0935_i_Strength = _vm->_rnd->getRandomNumber(15) + L0939_ps_Champion->_statistics[k1_ChampionStatStrength][k1_ChampionStatCurrent];
+ L0938_T_Thing = L0939_ps_Champion->_slots[slotIndex];
+ if ((AL0936_ui_ObjectWeight = _vm->_dungeonMan->f140_getObjectWeight(L0938_T_Thing)) <= (AL0937_ui_OneSixteenthMaximumLoad = f309_getMaximumLoad(L0939_ps_Champion) >> 4)) {
+ L0935_i_Strength += AL0936_ui_ObjectWeight - 12;
+ } else {
+ if (AL0936_ui_ObjectWeight <= (L0941_i_LoadThreshold = AL0937_ui_OneSixteenthMaximumLoad + ((AL0937_ui_OneSixteenthMaximumLoad - 12) >> 1))) {
+ L0935_i_Strength += (AL0936_ui_ObjectWeight - AL0937_ui_OneSixteenthMaximumLoad) >> 1;
+ } else {
+ L0935_i_Strength -= (AL0936_ui_ObjectWeight - L0941_i_LoadThreshold) << 1;
+ }
+ }
+ if (L0938_T_Thing.getType() == k5_WeaponThingType) {
+ L0940_ps_WeaponInfo = _vm->_dungeonMan->f158_getWeaponInfo(L0938_T_Thing);
+ L0935_i_Strength += L0940_ps_WeaponInfo->_strength;
+ AL0936_ui_SkillLevel = 0;
+ AL0937_ui_Class = L0940_ps_WeaponInfo->_class;
+ if ((AL0937_ui_Class == k0_WeaponClassSwingWeapon) || (AL0937_ui_Class == k2_WeaponClassDaggerAndAxes)) {
+ AL0936_ui_SkillLevel = f303_getSkillLevel(champIndex, k4_ChampionSkillSwing);
+ }
+ if ((AL0937_ui_Class != k0_WeaponClassSwingWeapon) && (AL0937_ui_Class < k16_WeaponClassFirstBow)) {
+ AL0936_ui_SkillLevel += f303_getSkillLevel(champIndex, k10_ChampionSkillThrow);
+ }
+ if ((AL0937_ui_Class >= k16_WeaponClassFirstBow) && (AL0937_ui_Class < k112_WeaponClassFirstMagicWeapon)) {
+ AL0936_ui_SkillLevel += f303_getSkillLevel(champIndex, k11_ChampionSkillShoot);
+ }
+ L0935_i_Strength += AL0936_ui_SkillLevel << 1;
+ }
+ L0935_i_Strength = f306_getStaminaAdjustedValue(L0939_ps_Champion, L0935_i_Strength);
+ if (getFlag(L0939_ps_Champion->_wounds, (slotIndex == k0_ChampionSlotReadyHand) ? k0x0001_ChampionWoundReadHand : k0x0002_ChampionWoundActionHand)) {
+ L0935_i_Strength >>= 1;
+ }
+ MAX(1, 2);
+ return f26_getBoundedValue(0, L0935_i_Strength >> 1, 100);
+}
+
+Thing ChampionMan::f300_getObjectRemovedFromSlot(uint16 champIndex, uint16 slotIndex) {
+ Thing L0894_T_Thing;
+ int16 L0895_i_IconIndex;
+ Champion* L0896_ps_Champion;
+ Weapon* L0897_ps_Weapon;
+ bool L0898_B_IsInventoryChampion;
+
+
+ L0896_ps_Champion = &_gK71_champions[champIndex];
+ if (slotIndex >= k30_ChampionSlotChest_1) {
+ L0894_T_Thing = _vm->_inventoryMan->_g425_chestSlots[slotIndex - k30_ChampionSlotChest_1];
+ _vm->_inventoryMan->_g425_chestSlots[slotIndex - k30_ChampionSlotChest_1] = Thing::_none;
+ } else {
+ L0894_T_Thing = L0896_ps_Champion->_slots[slotIndex];
+ L0896_ps_Champion->_slots[slotIndex] = Thing::_none;
+ }
+ if (L0894_T_Thing == Thing::_none) {
+ return Thing::_none;
+ }
+ L0898_B_IsInventoryChampion = (_vm->M0_indexToOrdinal(champIndex) == _vm->_inventoryMan->_g432_inventoryChampionOrdinal);
+ L0895_i_IconIndex = _vm->_objectMan->f33_getIconIndex(L0894_T_Thing);
+ f299_applyModifiersToStatistics(L0896_ps_Champion, slotIndex, L0895_i_IconIndex, -1, L0894_T_Thing); /* Remove objet modifiers */
+ L0897_ps_Weapon = (Weapon*)_vm->_dungeonMan->f156_getThingData(L0894_T_Thing);
+ if (slotIndex == k10_ChampionSlotNeck) {
+ if ((L0895_i_IconIndex >= k12_IconIndiceJunkIllumuletUnequipped) && (L0895_i_IconIndex <= k13_IconIndiceJunkIllumuletEquipped)) {
+ ((Junk*)L0897_ps_Weapon)->setChargeCount(0);
+ _g407_party._magicalLightAmount -= g39_LightPowerToLightAmount[2];
+ _vm->_inventoryMan->f337_setDungeonViewPalette();
+ } else {
+ if ((L0895_i_IconIndex >= k10_IconIndiceJunkJewelSymalUnequipped) && (L0895_i_IconIndex <= k11_IconIndiceJunkJewelSymalEquipped)) {
+ ((Junk*)L0897_ps_Weapon)->setChargeCount(0);
+ }
+ }
+ }
+ f291_drawSlot(champIndex, slotIndex);
+ if (L0898_B_IsInventoryChampion) {
+ setFlag(L0896_ps_Champion->_attributes, k0x4000_ChampionAttributeViewport);
+ }
+ if (slotIndex < k2_ChampionSlotHead) {
+ if (slotIndex == k1_ChampionSlotActionHand) {
+ setFlag(L0896_ps_Champion->_attributes, k0x8000_ChampionAttributeActionHand);
+ if (_g506_actingChampionOrdinal == _vm->M0_indexToOrdinal(champIndex)) {
+ _vm->_menuMan->f388_clearActingChampion();
+ }
+ if ((L0895_i_IconIndex >= k30_IconIndiceScrollOpen) && (L0895_i_IconIndex <= k31_IconIndiceScrollClosed)) {
+ ((Scroll*)L0897_ps_Weapon)->setClosed(true);
+ f296_drawChangedObjectIcons();
+ }
+ }
+ if ((L0895_i_IconIndex >= k4_IconIndiceWeaponTorchUnlit) && (L0895_i_IconIndex <= k7_IconIndiceWeaponTorchLit)) {
+ L0897_ps_Weapon->setLit(false);
+ _vm->_inventoryMan->f337_setDungeonViewPalette();
+ f296_drawChangedObjectIcons();
+ }
+ if (L0898_B_IsInventoryChampion && (slotIndex == k1_ChampionSlotActionHand)) {
+ if (L0895_i_IconIndex == k144_IconIndiceContainerChestClosed) {
+ _vm->_inventoryMan->f334_closeChest();
+ goto T0300011;
+ }
+ if ((L0895_i_IconIndex >= k30_IconIndiceScrollOpen) && (L0895_i_IconIndex <= k31_IconIndiceScrollClosed)) {
+T0300011:
+ setFlag(L0896_ps_Champion->_attributes, k0x0800_ChampionAttributePanel);
+ }
+ }
+ }
+ L0896_ps_Champion->_load -= _vm->_dungeonMan->f140_getObjectWeight(L0894_T_Thing);
+ setFlag(L0896_ps_Champion->_attributes, k0x0200_ChampionAttributeLoad);
+ return L0894_T_Thing;
+}
+
+void ChampionMan::f325_decrementStamine(int16 championIndex, int16 decrement) {
+ int16 L0988_i_Stamina;
+ Champion* L0989_ps_Champion;
+
+
+ if (championIndex == kM1_ChampionNone) {
+ return;
+ }
+ L0989_ps_Champion = &_gK71_champions[championIndex];
+ if ((L0988_i_Stamina = (L0989_ps_Champion->_currStamina -= decrement)) <= 0) {
+ L0989_ps_Champion->_currStamina = 0;
+ f321_addPendingDamageAndWounds_getDamage(championIndex, (-L0988_i_Stamina) >> 1, k0x0000_ChampionWoundNone, k0_attackType_NORMAL);
+ } else {
+ if (L0988_i_Stamina > L0989_ps_Champion->_maxStamina) {
+ L0989_ps_Champion->_currStamina = L0989_ps_Champion->_maxStamina;
+ }
+ }
+ setFlag(L0989_ps_Champion->_attributes, k0x0200_ChampionAttributeLoad | k0x0100_ChampionAttributeStatistics);
+}
+
+int16 ChampionMan::f321_addPendingDamageAndWounds_getDamage(int16 champIndex, int16 attack, int16 allowedWounds, uint16 attackType) {
+ int16 L0976_i_Multiple;
+#define AL0976_i_WoundIndex L0976_i_Multiple
+#define AL0976_i_WisdomFactor L0976_i_Multiple
+#define AL0976_i_AdjustedAttack L0976_i_Multiple
+ uint16 L0977_ui_Defense;
+ uint16 L0978_ui_WoundCount;
+ Champion* L0979_ps_Champion;
+
+ if (attack <= 0)
+ return 0;
+
+ L0979_ps_Champion = &_gK71_champions[champIndex];
+ if (!L0979_ps_Champion->_currHealth) {
+T0321004:
+ return 0;
+ }
+ if (attackType != k0_attackType_NORMAL) {
+ for (L0978_ui_WoundCount = 0, AL0976_i_WoundIndex = k0_ChampionSlotReadyHand, L0977_ui_Defense = 0; AL0976_i_WoundIndex <= k5_ChampionSlotFeet; AL0976_i_WoundIndex++) {
+ if (allowedWounds & (1 << AL0976_i_WoundIndex)) {
+ L0978_ui_WoundCount++;
+ L0977_ui_Defense += f313_getWoundDefense(champIndex, AL0976_i_WoundIndex | ((attackType == k4_attackType_SHARP) ? k0x8000_maskUseSharpDefense : k0x0000_maskDoNotUseSharpDefense));
+ }
+ }
+ if (L0978_ui_WoundCount) {
+ L0977_ui_Defense /= L0978_ui_WoundCount;
+ }
+ switch (attackType) {
+ case k6_attackType_PSYCHIC:
+ if ((AL0976_i_WisdomFactor = 115 - L0979_ps_Champion->_statistics[k3_ChampionStatWisdom][k1_ChampionStatCurrent]) <= 0) {
+ attack = 0;
+ } else {
+ attack = _vm->f30_getScaledProduct(attack, 6, AL0976_i_WisdomFactor);
+ }
+ goto T0321024;
+ case k5_attackType_MAGIC:
+ attack = f307_getStatisticAdjustedAttack(L0979_ps_Champion, k5_ChampionStatAntimagic, attack);
+ attack -= _g407_party._spellShieldDefense;
+ goto T0321024;
+ case k1_attackType_FIRE:
+ attack = f307_getStatisticAdjustedAttack(L0979_ps_Champion, k6_ChampionStatAntifire, attack);
+ attack -= _g407_party._fireShieldDefense;
+ break;
+ case k2_attackType_SELF:
+ L0977_ui_Defense >>= 1;
+ case k3_attackType_BLUNT:
+ case k4_attackType_SHARP:
+ case k7_attackType_LIGHTNING:
+ ;
+ }
+ if (attack <= 0)
+ goto T0321004;
+ attack = _vm->f30_getScaledProduct(attack, 6, 130 - L0977_ui_Defense);
+ /* BUG0_44 A champion may take much more damage than expected after a Black Flame attack or an impact
+ with a Fireball projectile. If the party has a fire shield defense value higher than the fire attack value then the resulting intermediary
+ attack value is negative and damage should be 0. However, the negative value is still used for further computations and the result may be a very
+ high positive attack value which may kill a champion. This can occur only for k1_attackType_FIRE and if attack is negative before calling F0030_MAIN_GetScaledProduct */
+T0321024:
+ if (attack <= 0)
+ goto T0321004;
+ if (attack > (AL0976_i_AdjustedAttack = f307_getStatisticAdjustedAttack(L0979_ps_Champion, k4_ChampionStatVitality, _vm->_rnd->getRandomNumber(127) + 10))) { /* BUG0_45 This bug is not perceptible because of BUG0_41 that ignores Vitality while determining the probability of being wounded. However if it was fixed, the behavior would be the opposite of what it should: the higher the vitality of a champion, the lower the result of F0307_CHAMPION_GetStatisticAdjustedAttack and the more likely the champion could get wounded (because of more iterations in the loop below) */
+ do {
+ setFlag(*(uint16*)&_g410_championPendingWounds[champIndex], (1 << _vm->_rnd->getRandomNumber(7)) & allowedWounds);
+ } while ((attack > (AL0976_i_AdjustedAttack <<= 1)) && AL0976_i_AdjustedAttack);
+ }
+ if (_g300_partyIsSleeping) {
+ f314_wakeUp();
+ }
+ }
+ _g409_championPendingDamage[champIndex] += attack;
+ return attack;
+}
+
+int16 ChampionMan::f313_getWoundDefense(int16 champIndex, uint16 woundIndex) {
+ static byte g50_woundDefenseFactor[6] = {5, 5, 4, 6, 3, 1}; // @ G0050_auc_Graphic562_WoundDefenseFactor
+
+ int16 L0942_i_Multiple;
+#define AL0942_i_SlotIndex L0942_i_Multiple
+#define AL0942_i_WoundDefense L0942_i_Multiple
+ uint16 L0943_ui_ArmourShieldDefense;
+ bool L0944_B_UseSharpDefense;
+ Thing L0945_T_Thing;
+ Champion* L0946_ps_Champion;
+ ArmourInfo* L0947_ps_ArmourInfo;
+
+
+ L0946_ps_Champion = &_gK71_champions[champIndex];
+ if (L0944_B_UseSharpDefense = getFlag(woundIndex, k0x8000_maskUseSharpDefense)) {
+ clearFlag(woundIndex, k0x8000_maskUseSharpDefense);
+ }
+ for (L0943_ui_ArmourShieldDefense = 0, AL0942_i_SlotIndex = k0_ChampionSlotReadyHand; AL0942_i_SlotIndex <= k1_ChampionSlotActionHand; AL0942_i_SlotIndex++) {
+ if ((L0945_T_Thing = L0946_ps_Champion->_slots[AL0942_i_SlotIndex]).getType() == k6_ArmourThingType) {
+ L0947_ps_ArmourInfo = (ArmourInfo*)_vm->_dungeonMan->f156_getThingData(L0945_T_Thing);
+ L0947_ps_ArmourInfo = &g239_ArmourInfo[((Armour*)L0947_ps_ArmourInfo)->getType()];
+ if (getFlag(L0947_ps_ArmourInfo->_attributes, k0x0080_ArmourAttributeIsAShield)) {
+ L0943_ui_ArmourShieldDefense += ((f312_getStrength(champIndex, AL0942_i_SlotIndex) + _vm->_dungeonMan->f143_getArmourDefense(L0947_ps_ArmourInfo, L0944_B_UseSharpDefense)) * g50_woundDefenseFactor[woundIndex]) >> ((AL0942_i_SlotIndex == woundIndex) ? 4 : 5);
+ }
+ }
+ }
+ AL0942_i_WoundDefense = _vm->getRandomNumber((L0946_ps_Champion->_statistics[k4_ChampionStatVitality][k1_ChampionStatCurrent] >> 3) + 1);
+ if (L0944_B_UseSharpDefense) {
+ AL0942_i_WoundDefense >>= 1;
+ }
+ AL0942_i_WoundDefense += L0946_ps_Champion->_actionDefense + L0946_ps_Champion->_shieldDefense + _g407_party._shieldDefense + L0943_ui_ArmourShieldDefense;
+ if ((woundIndex > k1_ChampionSlotActionHand) && ((L0945_T_Thing = L0946_ps_Champion->_slots[woundIndex]).getType() == k6_ArmourThingType)) {
+ L0947_ps_ArmourInfo = (ArmourInfo*)_vm->_dungeonMan->f156_getThingData(L0945_T_Thing);
+ AL0942_i_WoundDefense += _vm->_dungeonMan->f143_getArmourDefense(&g239_ArmourInfo[((Armour*)L0947_ps_ArmourInfo)->getType()], L0944_B_UseSharpDefense);
+ }
+ if (getFlag(L0946_ps_Champion->_wounds, 1 << woundIndex)) {
+ AL0942_i_WoundDefense -= 8 + _vm->getRandomNumber(4);
+ }
+ if (_g300_partyIsSleeping) {
+ AL0942_i_WoundDefense >>= 1;
+ }
+ return f26_getBoundedValue(0, AL0942_i_WoundDefense >> 1, 100);
+}
+
+uint16 ChampionMan::f307_getStatisticAdjustedAttack(Champion* champ, uint16 statIndex, uint16 attack) {
+ int16 L0927_i_Factor;
+
+ if ((L0927_i_Factor = 170 - champ->_statistics[statIndex][k1_ChampionStatCurrent]) < 16) { /* BUG0_41 The Antifire and Antimagic statistics are completely ignored. The Vitality statistic is ignored against poison and to determine the probability of being wounded. Vitality is still used normally to compute the defense against wounds and the speed of health regeneration. A bug in the Megamax C compiler produces wrong machine code for this statement. It always returns 0 for the current statistic value so that L0927_i_Factor = 170 in all cases */
+ return attack >> 3;
+ }
+ return _vm->f30_getScaledProduct(attack, 7, L0927_i_Factor);
+}
+
+void ChampionMan::f314_wakeUp() {
+ _vm->_g321_stopWaitingForPlayerInput = true;
+ _g300_partyIsSleeping = false;
+ _vm->waitMs(10);
+ _vm->_displayMan->f98_drawFloorAndCeiling();
+ _vm->_eventMan->_g441_primaryMouseInput = g447_PrimaryMouseInput_Interface;
+ _vm->_eventMan->_g442_secondaryMouseInput = g448_SecondaryMouseInput_Movement;
+ warning("MISSING CODE: set G0443_ps_PrimaryKeyboardInput");
+ warning("MISSING CODE: G0444_ps_SecondaryKeyboardInput");
+ _vm->_eventMan->f357_discardAllInput();
+ _vm->_menuMan->f457_drawEnabledMenus();
+}
+
+int16 ChampionMan::f305_getThrowingStaminaCost(Thing thing) {
+ int16 L0923_i_Weight;
+ int16 L0924_i_StaminaCost;
+
+
+ L0924_i_StaminaCost = f26_getBoundedValue((int16)1, L0923_i_Weight = _vm->_dungeonMan->f140_getObjectWeight(thing) >> 1, (int16)10);
+ while ((L0923_i_Weight -= 10) > 0) {
+ L0924_i_StaminaCost += L0923_i_Weight >> 1;
+ }
+ return L0924_i_StaminaCost;
+}
+
+void ChampionMan::f330_disableAction(uint16 champIndex, uint16 ticks) {
+ int32 L1001_l_UpdatedEnableActionEventTime;
+ int32 L1002_l_CurrentEnableActionEventTime;
+ int16 L1003_i_EventIndex;
+ Champion* L1004_ps_Champion;
+ TimelineEvent L1005_s_Event;
+
+
+ L1004_ps_Champion = &_gK71_champions[champIndex];
+ L1001_l_UpdatedEnableActionEventTime = _vm->_g313_gameTime + ticks;
+ L1005_s_Event._type = k11_TMEventTypeEnableChampionAction;
+ L1005_s_Event._priority = champIndex;
+ L1005_s_Event._B._slotOrdinal = 0;
+ if ((L1003_i_EventIndex = L1004_ps_Champion->_enableActionEventIndex) >= 0) {
+ L1002_l_CurrentEnableActionEventTime = M30_time(_vm->_timeline->_g370_events[L1003_i_EventIndex]._mapTime);
+ if (L1001_l_UpdatedEnableActionEventTime >= L1002_l_CurrentEnableActionEventTime) {
+ L1001_l_UpdatedEnableActionEventTime += (L1002_l_CurrentEnableActionEventTime - _vm->_g313_gameTime) >> 1;
+ } else {
+ L1001_l_UpdatedEnableActionEventTime = L1002_l_CurrentEnableActionEventTime + (ticks >> 1);
+ }
+ _vm->_timeline->f237_deleteEvent(L1003_i_EventIndex);
+ } else {
+ setFlag(L1004_ps_Champion->_attributes, k0x8000_ChampionAttributeActionHand | k0x0008_ChampionAttributeDisableAction);
+ f292_drawChampionState((ChampionIndex)champIndex);
+ }
+ M33_setMapAndTime(L1005_s_Event._mapTime, _vm->_dungeonMan->_g309_partyMapIndex, L1001_l_UpdatedEnableActionEventTime);
+ L1004_ps_Champion->_enableActionEventIndex = _vm->_timeline->f238_addEventGetEventIndex(&L1005_s_Event);
+}
+
+void ChampionMan::f304_addSkillExperience(uint16 champIndex, uint16 skillIndex, uint16 exp) {
+#define AP0638_ui_SkillLevelAfter exp
+#define AP0638_ui_ChampionColor exp
+ uint16 L0915_ui_Multiple;
+#define AL0915_ui_MapDifficulty L0915_ui_Multiple
+#define AL0915_ui_SkillLevelBefore L0915_ui_Multiple
+#define AL0915_ui_VitalityAmount L0915_ui_Multiple
+#define AL0915_ui_StaminaAmount L0915_ui_Multiple
+ uint16 L0916_ui_BaseSkillIndex;
+ Skill* L0918_ps_Skill;
+ Champion* L0919_ps_Champion;
+ int16 L0920_i_MinorStatisticIncrease;
+ int16 L0921_i_MajorStatisticIncrease;
+ int16 L0922_i_BaseSkillLevel;
+
+
+ warning("potaneitally dangerous cast of uint32 below");
+ if ((skillIndex >= k4_ChampionSkillSwing) && (skillIndex <= k11_ChampionSkillShoot) && ((uint32)_vm->_projexpl->_g361_lastCreatureAttackTime < (_vm->_g313_gameTime - 150))) {
+ exp >>= 1;
+ }
+ if (exp) {
+ if (AL0915_ui_MapDifficulty = _vm->_dungeonMan->_g269_currMap->_difficulty) {
+ exp *= AL0915_ui_MapDifficulty;
+ }
+ L0919_ps_Champion = &_gK71_champions[champIndex];
+ if (skillIndex >= k4_ChampionSkillSwing) {
+ L0916_ui_BaseSkillIndex = (skillIndex - k4_ChampionSkillSwing) >> 2;
+ } else {
+ L0916_ui_BaseSkillIndex = skillIndex;
+ }
+ AL0915_ui_SkillLevelBefore = f303_getSkillLevel(champIndex, L0916_ui_BaseSkillIndex | (k0x4000_IgnoreObjectModifiers | k0x8000_IgnoreTemporaryExperience));
+ warning("potentially dangerous cast of uint32 below");
+ if ((skillIndex >= k4_ChampionSkillSwing) && ((uint32)_vm->_projexpl->_g361_lastCreatureAttackTime > (_vm->_g313_gameTime - 25))) {
+ exp <<= 1;
+ }
+ L0918_ps_Skill = &L0919_ps_Champion->_skills[skillIndex];
+ L0918_ps_Skill->_experience += exp;
+ if (L0918_ps_Skill->_temporaryExperience < 32000) {
+ L0918_ps_Skill->_temporaryExperience += f26_getBoundedValue(1, exp >> 3, 100);
+ }
+ L0918_ps_Skill = &L0919_ps_Champion->_skills[L0916_ui_BaseSkillIndex];
+ if (skillIndex >= k4_ChampionSkillSwing) {
+ L0918_ps_Skill->_experience += exp;
+ }
+ AP0638_ui_SkillLevelAfter = f303_getSkillLevel(champIndex, L0916_ui_BaseSkillIndex | (k0x4000_IgnoreObjectModifiers | k0x8000_IgnoreTemporaryExperience));
+ if (AP0638_ui_SkillLevelAfter > AL0915_ui_SkillLevelBefore) {
+ L0922_i_BaseSkillLevel = AP0638_ui_SkillLevelAfter;
+ L0920_i_MinorStatisticIncrease = _vm->getRandomNumber(2);
+ L0921_i_MajorStatisticIncrease = 1 + _vm->getRandomNumber(2);
+ AL0915_ui_VitalityAmount = _vm->getRandomNumber(2); /* For Priest skill, the amount is 0 or 1 for all skill levels */
+ if (L0916_ui_BaseSkillIndex != k2_ChampionSkillPriest) {
+ AL0915_ui_VitalityAmount &= AP0638_ui_SkillLevelAfter; /* For non Priest skills the amount is 0 for even skill levels. The amount is 0 or 1 for odd skill levels */
+ }
+ L0919_ps_Champion->_statistics[k4_ChampionStatVitality][k0_ChampionStatMaximum] += AL0915_ui_VitalityAmount;
+ AL0915_ui_StaminaAmount = L0919_ps_Champion->_maxStamina;
+ L0919_ps_Champion->_statistics[k6_ChampionStatAntifire][k0_ChampionStatMaximum] += _vm->getRandomNumber(2) & ~AP0638_ui_SkillLevelAfter; /* The amount is 0 for odd skill levels. The amount is 0 or 1 for even skill levels */
+ switch (L0916_ui_BaseSkillIndex) {
+ case k0_ChampionSkillFighter:
+ AL0915_ui_StaminaAmount >>= 4;
+ AP0638_ui_SkillLevelAfter *= 3;
+ L0919_ps_Champion->_statistics[k1_ChampionStatStrength][k0_ChampionStatMaximum] += L0921_i_MajorStatisticIncrease;
+ L0919_ps_Champion->_statistics[k2_ChampionStatDexterity][k0_ChampionStatMaximum] += L0920_i_MinorStatisticIncrease;
+ break;
+ case k1_ChampionSkillNinja:
+ AL0915_ui_StaminaAmount /= 21;
+ AP0638_ui_SkillLevelAfter <<= 1;
+ L0919_ps_Champion->_statistics[k1_ChampionStatStrength][k0_ChampionStatMaximum] += L0920_i_MinorStatisticIncrease;
+ L0919_ps_Champion->_statistics[k2_ChampionStatDexterity][k0_ChampionStatMaximum] += L0921_i_MajorStatisticIncrease;
+ break;
+ case k3_ChampionSkillWizard:
+ AL0915_ui_StaminaAmount >>= 5;
+ L0919_ps_Champion->_maxMana += AP0638_ui_SkillLevelAfter + (AP0638_ui_SkillLevelAfter >> 1);
+ L0919_ps_Champion->_statistics[k3_ChampionStatWisdom][k0_ChampionStatMaximum] += L0921_i_MajorStatisticIncrease;
+ goto T0304016;
+ case k2_ChampionSkillPriest:
+ AL0915_ui_StaminaAmount /= 25;
+ L0919_ps_Champion->_maxMana += AP0638_ui_SkillLevelAfter;
+ AP0638_ui_SkillLevelAfter += (AP0638_ui_SkillLevelAfter + 1) >> 1;
+ L0919_ps_Champion->_statistics[k3_ChampionStatWisdom][k0_ChampionStatMaximum] += L0920_i_MinorStatisticIncrease;
+T0304016:
+ if ((L0919_ps_Champion->_maxMana += MIN(_vm->getRandomNumber(4), (uint16)(L0922_i_BaseSkillLevel - 1))) > 900) {
+ L0919_ps_Champion->_maxMana = 900;
+ }
+ L0919_ps_Champion->_statistics[k5_ChampionStatAntimagic][k0_ChampionStatMaximum] += _vm->getRandomNumber(3);
+ }
+ if ((L0919_ps_Champion->_maxHealth += AP0638_ui_SkillLevelAfter + _vm->getRandomNumber((AP0638_ui_SkillLevelAfter >> 1) + 1)) > 999) {
+ L0919_ps_Champion->_maxHealth = 999;
+ }
+ if ((L0919_ps_Champion->_maxStamina += AL0915_ui_StaminaAmount + _vm->getRandomNumber((AL0915_ui_StaminaAmount >> 1) + 1)) > 9999) {
+ L0919_ps_Champion->_maxStamina = 9999;
+ }
+ setFlag(L0919_ps_Champion->_attributes, k0x0100_ChampionAttributeStatistics);
+ f292_drawChampionState((ChampionIndex)champIndex);
+ _vm->_textMan->f51_messageAreaPrintLineFeed();
+ _vm->_textMan->f47_messageAreaPrintMessage((Color)(AP0638_ui_ChampionColor = g46_ChampionColor[champIndex]), L0919_ps_Champion->_name);
+ // TODO: localization
+ _vm->_textMan->f47_messageAreaPrintMessage((Color)AP0638_ui_ChampionColor, " JUST GAINED A ");
+ _vm->_textMan->f47_messageAreaPrintMessage((Color)AP0638_ui_ChampionColor, g417_baseSkillName[L0916_ui_BaseSkillIndex]);
+ _vm->_textMan->f47_messageAreaPrintMessage((Color)AP0638_ui_ChampionColor, " LEVEL!");
+ }
+ }
+}
+
+int16 ChampionMan::f324_damageAll_getDamagedChampionCount(uint16 attack, int16 wounds, int16 attackType) {
+ int16 L0984_i_ChampionIndex;
+ int16 L0985_i_RandomAttack;
+ int16 L0986_i_DamagedChampionCount;
+
+ attack -= (L0985_i_RandomAttack = (attack >> 3) + 1);
+ L0985_i_RandomAttack <<= 1;
+ for (L0986_i_DamagedChampionCount = 0, L0984_i_ChampionIndex = k0_ChampionFirst; L0984_i_ChampionIndex < _g305_partyChampionCount; L0984_i_ChampionIndex++) {
+ if (f321_addPendingDamageAndWounds_getDamage(L0984_i_ChampionIndex, MAX(1, attack + _vm->getRandomNumber(L0985_i_RandomAttack)), wounds, attackType)) { /* Actual attack is attack +/- (attack / 8) */
+ L0986_i_DamagedChampionCount++;
+ }
+ }
+ return L0986_i_DamagedChampionCount;
+}
+
+int16 ChampionMan::f286_getTargetChampionIndex(int16 mapX, int16 mapY, uint16 cell) {
+ uint16 L0838_ui_Counter;
+ int16 L0839_i_ChampionIndex;
+ signed char L0840_auc_OrderedCellsToAttack[4];
+
+
+ if (_g305_partyChampionCount && (M38_distance(mapX, mapY, _vm->_dungeonMan->_g306_partyMapX, _vm->_dungeonMan->_g307_partyMapY) <= 1)) {
+ _vm->_groupMan->f229_setOrderedCellsToAttack(L0840_auc_OrderedCellsToAttack, _vm->_dungeonMan->_g306_partyMapX, _vm->_dungeonMan->_g307_partyMapY, mapX, mapY, cell);
+ for (L0838_ui_Counter = 0; L0838_ui_Counter < 4; L0838_ui_Counter++) {
+ if ((L0839_i_ChampionIndex = f285_getIndexInCell(L0840_auc_OrderedCellsToAttack[L0838_ui_Counter])) >= 0) {
+ return L0839_i_ChampionIndex;
+ }
+ }
+ }
+ return kM1_ChampionNone;
+}
+
+int16 ChampionMan::f311_getDexterity(Champion* champ) {
+ int16 L0934_i_Dexterity;
+
+
+ L0934_i_Dexterity = _vm->getRandomNumber(8) + champ->_statistics[k2_ChampionStatDexterity][k1_ChampionStatCurrent];
+ L0934_i_Dexterity -= ((int32)(L0934_i_Dexterity >> 1) * (int32)champ->_load) / f309_getMaximumLoad(champ);
+ if (_g300_partyIsSleeping) {
+ L0934_i_Dexterity >>= 1;
+ }
+ return f26_getBoundedValue(1 + _vm->getRandomNumber(8), L0934_i_Dexterity >> 1, 100 - _vm->getRandomNumber(8));
+}
+
+bool ChampionMan::f308_isLucky(Champion* champ, uint16 percentage) {
+#define AP0646_ui_IsLucky percentage
+ register unsigned char* L0928_puc_Statistic;
+
+
+ if (_vm->getRandomNumber(2) && (_vm->getRandomNumber(100) > percentage)) {
+ return true;
+ }
+ L0928_puc_Statistic = champ->_statistics[k0_ChampionStatLuck];
+ AP0646_ui_IsLucky = (_vm->getRandomNumber(L0928_puc_Statistic[k1_ChampionStatCurrent]) > percentage);
+ L0928_puc_Statistic[k1_ChampionStatCurrent] = f26_getBoundedValue((int32)L0928_puc_Statistic[k2_ChampionStatMinimum], (int32)L0928_puc_Statistic[k1_ChampionStatCurrent] + (AP0646_ui_IsLucky ? -2 : 2), (int32)L0928_puc_Statistic[k0_ChampionStatMaximum]);
+ return AP0646_ui_IsLucky;
+}
+
+void ChampionMan::f322_championPoison(int16 champIndex, uint16 attack) {
+ TimelineEvent L0980_s_Event;
+ Champion* L0981_ps_Champion;
+
+
+ if ((champIndex == kM1_ChampionNone) || (_vm->M0_indexToOrdinal(champIndex) == _g299_candidateChampionOrdinal)) {
+ return;
+ }
+ L0981_ps_Champion = &_gK71_champions[champIndex];
+ f321_addPendingDamageAndWounds_getDamage(champIndex, MAX(1, attack >> 6), k0x0000_ChampionWoundNone, k0_attackType_NORMAL);
+ setFlag(L0981_ps_Champion->_attributes, k0x0100_ChampionAttributeStatistics);
+ if ((_vm->M0_indexToOrdinal(champIndex) == _vm->_inventoryMan->_g432_inventoryChampionOrdinal) && (_vm->_inventoryMan->_g424_panelContent == k0_PanelContentFoodWaterPoisoned)) {
+ setFlag(L0981_ps_Champion->_attributes, k0x0800_ChampionAttributePanel);
+ }
+ if (--attack) {
+ L0981_ps_Champion->_poisonEventCount++;
+ L0980_s_Event._type = k75_TMEventTypePoisonChampion;
+ L0980_s_Event._priority = champIndex;
+ M33_setMapAndTime(L0980_s_Event._mapTime, _vm->_dungeonMan->_g309_partyMapIndex, _vm->_g313_gameTime + 36);
+ L0980_s_Event._B._attack = attack;
+ _vm->_timeline->f238_addEventGetEventIndex(&L0980_s_Event);
+ }
+ f292_drawChampionState((ChampionIndex)champIndex);
+}
+
+void ChampionMan::f284_setPartyDirection(int16 dir) {
+ int16 L0833_i_ChampionIndex;
+ int16 L0834_i_Delta;
+ Champion* L0835_ps_Champion;
+
+
+ if (dir == _vm->_dungeonMan->_g308_partyDir) {
+ return;
+ }
+ if ((L0834_i_Delta = dir - _vm->_dungeonMan->_g308_partyDir) < 0) {
+ L0834_i_Delta += 4;
+ }
+ L0835_ps_Champion = _gK71_champions;
+ for (L0833_i_ChampionIndex = k0_ChampionFirst; L0833_i_ChampionIndex < _g305_partyChampionCount; L0833_i_ChampionIndex++) {
+ L0835_ps_Champion->_cell = (ViewCell)M21_normalizeModulo4(L0835_ps_Champion->_cell + L0834_i_Delta);
+ L0835_ps_Champion->_dir = (direction)M21_normalizeModulo4(L0835_ps_Champion->_dir + L0834_i_Delta);
+ L0835_ps_Champion++;
+ }
+ _vm->_dungeonMan->_g308_partyDir = (direction)dir;
+ f296_drawChangedObjectIcons();
+}
+
+void ChampionMan::f316_deleteScent(uint16 scentIndex) {
+ uint16 L0953_ui_Count;
+
+ if (L0953_ui_Count = --_g407_party._scentCount - scentIndex) {
+ for (uint16 i = 0; i < L0953_ui_Count; ++i) {
+ _g407_party._scents[scentIndex + i] = _g407_party._scents[scentIndex + i + 1];
+ _g407_party._scentStrengths[scentIndex + i] = _g407_party._scentStrengths[scentIndex + i + 1];
+ }
+ }
+ if (scentIndex < _g407_party._firstScentIndex) {
+ _g407_party._firstScentIndex--;
+ }
+ if (scentIndex < _g407_party._lastScentIndex) {
+ _g407_party._lastScentIndex--;
+ }
+}
+
+void ChampionMan::f317_addScentStrength(int16 mapX, int16 mapY, int32 cycleCount) {
+ int16 L0954_i_ScentIndex;
+ bool L0955_B_Merge;
+ bool L0956_B_CycleCountDefined;
+ Scent* L0957_ps_Scent; /* BUG0_00 Useless code */
+ Scent L0958_s_Scent; /* BUG0_00 Useless code */
+
+
+ if (L0954_i_ScentIndex = _vm->_championMan->_g407_party._scentCount) {
+ if (L0955_B_Merge = getFlag(cycleCount, k0x8000_mergeCycles)) {
+ clearFlag(cycleCount, k0x8000_mergeCycles);
+ }
+ L0958_s_Scent.setMapX(mapX); /* BUG0_00 Useless code */
+ L0958_s_Scent.setMapY(mapY); /* BUG0_00 Useless code */
+ L0958_s_Scent.setMapIndex(_vm->_dungeonMan->_g272_currMapIndex); /* BUG0_00 Useless code */
+ L0957_ps_Scent = _vm->_championMan->_g407_party._scents; /* BUG0_00 Useless code */
+ L0956_B_CycleCountDefined = false;
+ while (L0954_i_ScentIndex--) {
+ if (&*L0957_ps_Scent++ == &L0958_s_Scent) {
+ if (!L0956_B_CycleCountDefined) {
+ L0956_B_CycleCountDefined = true;
+ if (L0955_B_Merge) {
+ cycleCount = MAX((int32)_vm->_championMan->_g407_party._scentStrengths[L0954_i_ScentIndex], cycleCount);
+ } else {
+ cycleCount = MIN(80, _vm->_championMan->_g407_party._scentStrengths[L0954_i_ScentIndex] + cycleCount);
+ }
+ }
+ _vm->_championMan->_g407_party._scentStrengths[L0954_i_ScentIndex] = cycleCount;
+ }
+ }
+ }
+}
+
+void ChampionMan::f297_putObjectInLeaderHand(Thing thing, bool setMousePointer) {
+ if (thing == Thing::_none) {
+ return;
+ }
+ _vm->_championMan->_g415_leaderEmptyHanded = false;
+ _vm->_objectMan->f36_extractIconFromBitmap(_vm->_championMan->_g413_leaderHandObjectIconIndex = _vm->_objectMan->f33_getIconIndex(_vm->_championMan->_g414_leaderHandObject = thing), _vm->_objectMan->_g412_objectIconForMousePointer);
+ _vm->_eventMan->f78_showMouse();
+ _vm->_objectMan->f34_drawLeaderObjectName(thing);
+ if (setMousePointer) {
+ _vm->_g325_setMousePointerToObjectInMainLoop = true;
+ } else {
+ _vm->_eventMan->f68_setPointerToObject(_vm->_objectMan->_g412_objectIconForMousePointer);
+ }
+ _vm->_eventMan->f77_hideMouse();
+ if (_vm->_championMan->_g411_leaderIndex != kM1_ChampionNone) {
+ _vm->_championMan->_gK71_champions[_vm->_championMan->_g411_leaderIndex]._load += _vm->_dungeonMan->f140_getObjectWeight(thing);
+ setFlag(_vm->_championMan->_gK71_champions[_vm->_championMan->_g411_leaderIndex]._attributes, k0x0200_ChampionAttributeLoad);
+ _vm->_championMan->f292_drawChampionState(_vm->_championMan->_g411_leaderIndex);
+ }
+}
+
+ChampionIndex ChampionMan::f285_getIndexInCell(int16 cell) {
for (uint16 i = 0; i < _g305_partyChampionCount; ++i) {
if ((_gK71_champions[i]._cell == cell) && _gK71_champions[i]._currHealth)
return (ChampionIndex)i;
@@ -685,7 +1384,7 @@ void ChampionMan::f287_drawChampionBarGraphs(ChampionIndex champIndex) {
} else {
barGraphHeightArray[barGraphIndex++] = 0;
}
- warning("MISSING CODE: F0077_MOUSE_HidePointer_CPSE");
+ _vm->_eventMan->f78_showMouse();
Box box;
box._x1 = champIndex * k69_ChampionStatusBoxSpacing + 46;
@@ -708,7 +1407,7 @@ void ChampionMan::f287_drawChampionBarGraphs(ChampionIndex champIndex) {
box._x1 += 7;
box._x2 += 7;
}
- warning("MISSING CODE: F0078_MOUSE_ShowPointer");
+ _vm->_eventMan->f77_hideMouse();
}
@@ -918,7 +1617,7 @@ T0292042_green:
champ->setAttributeFlag((ChampionAttribute)(k0x0080_ChampionAttributeNameTitle | k0x0100_ChampionAttributeStatistics | k0x0200_ChampionAttributeLoad | k0x0400_ChampionAttributeIcon |
k0x0800_ChampionAttributePanel | k0x1000_ChampionAttributeStatusBox | k0x2000_ChampionAttributeWounds | k0x4000_ChampionAttributeViewport |
k0x8000_ChampionAttributeActionHand), false);
- warning("MISSING CODE: F0078_MOUSE_ShowPointer");
+ _vm->_eventMan->f78_showMouse();
}
uint16 ChampionMan::M26_championIconIndex(int16 val, direction dir) {
@@ -931,7 +1630,7 @@ void ChampionMan::f290_drawHealthStaminaManaValues(Champion* champ) {
f289_drawHealthOrStaminaOrManaValue(132, champ->_currMana, champ->_maxMana);
}
-void ChampionMan::f291_drawSlot(uint16 champIndex, ChampionSlot slotIndex) {
+void ChampionMan::f291_drawSlot(uint16 champIndex, int16 slotIndex) {
int16 nativeBitmapIndex = -1;
Champion *champ = &_gK71_champions[champIndex];
bool isInventoryChamp = (_vm->_inventoryMan->_g432_inventoryChampionOrdinal == _vm->M0_indexToOrdinal(champIndex));
@@ -950,7 +1649,7 @@ void ChampionMan::f291_drawSlot(uint16 champIndex, ChampionSlot slotIndex) {
if (slotIndex >= k30_ChampionSlotChest_1) {
thing = _vm->_inventoryMan->_g425_chestSlots[slotIndex - k30_ChampionSlotChest_1];
} else {
- thing = champ->getSlot(slotIndex);
+ thing = champ->getSlot((ChampionSlot)slotIndex);
}
SlotBox *slotBox = &_vm->_objectMan->_g30_slotBoxes[slotBoxIndex];
@@ -962,7 +1661,7 @@ void ChampionMan::f291_drawSlot(uint16 champIndex, ChampionSlot slotIndex) {
if (!isInventoryChamp) {
- warning("MISSING CODE: F0077_MOUSE_HidePointer_CPSE");
+ _vm->_eventMan->f77_hideMouse();
}
int16 iconIndex;
@@ -983,10 +1682,8 @@ void ChampionMan::f291_drawSlot(uint16 champIndex, ChampionSlot slotIndex) {
}
}
} else {
- warning("BUG0_35");
iconIndex = _vm->_objectMan->f33_getIconIndex(thing); // BUG0_35
if (isInventoryChamp && (slotIndex == k1_ChampionSlotActionHand) && ((iconIndex == k144_IconIndiceContainerChestClosed) || (iconIndex == k30_IconIndiceScrollOpen))) {
- warning("BUG2_00");
iconIndex++;
} // BUG2_00
if (slotIndex <= k5_ChampionSlotFeet) {
@@ -1016,7 +1713,7 @@ void ChampionMan::f291_drawSlot(uint16 champIndex, ChampionSlot slotIndex) {
_vm->_objectMan->f38_drawIconInSlotBox(slotBoxIndex, iconIndex);
if (!isInventoryChamp) {
- warning("MISSING CODE: F0078_MOUSE_ShowPointer");
+ _vm->_eventMan->f78_showMouse();
}
}
@@ -1049,7 +1746,7 @@ void ChampionMan::f281_renameChampion(Champion* champ) {
}
}
-uint16 ChampionMan::f303_getSkillLevel(ChampionIndex champIndex, ChampionSkill skillIndex) {
+uint16 ChampionMan::f303_getSkillLevel(int16 champIndex, int16 skillIndex) {
if (_g300_partyIsSleeping)
return 1;
@@ -1057,7 +1754,7 @@ uint16 ChampionMan::f303_getSkillLevel(ChampionIndex champIndex, ChampionSkill s
bool ignoreObjModifiers = skillIndex & k0x4000_IgnoreObjectModifiers;
skillIndex = (ChampionSkill)(skillIndex & ~(ignoreTempExp | ignoreObjModifiers));
Champion *champ = &_gK71_champions[champIndex];
- Skill *skill = &champ->getSkill(skillIndex);
+ Skill *skill = &champ->getSkill((ChampionSkill)skillIndex);
int32 experience = skill->_experience;
if (!ignoreTempExp)
diff --git a/engines/dm/champion.h b/engines/dm/champion.h
index 43e976e543..282889d09e 100644
--- a/engines/dm/champion.h
+++ b/engines/dm/champion.h
@@ -328,6 +328,14 @@ enum ChampionAction {
k255_ChampionActionNone = 255 // @ C255_ACTION_NONE
};
+#define k0_attackType_NORMAL 0 // @ C0_ATTACK_NORMAL
+#define k1_attackType_FIRE 1 // @ C1_ATTACK_FIRE
+#define k2_attackType_SELF 2 // @ C2_ATTACK_SELF
+#define k3_attackType_BLUNT 3 // @ C3_ATTACK_BLUNT
+#define k4_attackType_SHARP 4 // @ C4_ATTACK_SHARP
+#define k5_attackType_MAGIC 5 // @ C5_ATTACK_MAGIC
+#define k6_attackType_PSYCHIC 6 // @ C6_ATTACK_PSYCHIC
+#define k7_attackType_LIGHTNING 7 // @ C7_ATTACK_LIGHTNING
class Skill {
public:
@@ -338,12 +346,12 @@ public:
}; // @ SKILL
class Champion {
- Thing _slots[30];
- Skill _skills[20];
+public:
uint16 _attributes;
- byte _statistics[7][3];
uint16 _wounds;
-public:
+ byte _statistics[7][3];
+ Thing _slots[30];
+ Skill _skills[20];
char _name[8];
char _title[20];
direction _dir;
@@ -425,16 +433,23 @@ public:
}
}; // @ CHAMPION_INCLUDING_PORTRAIT
+#define k0x0000_maskDoNotUseSharpDefense 0x0000 // @ MASK0x0000_DO_NOT_USE_SHARP_DEFENSE
+#define k0x8000_maskUseSharpDefense 0x8000 // @ MASK0x8000_USE_SHARP_DEFENSE
+
+#define k0x8000_mergeCycles 0x8000 // @ MASK0x8000_MERGE_CYCLES
+extern const char *g417_baseSkillName[4];
+
class ChampionMan {
DMEngine *_vm;
uint16 M27_getChampionPortraitX(uint16 index); // @ M27_PORTRAIT_X
uint16 M28_getChampionPortraitY(uint16 index); // @ M28_PORTRAIT_Y
- ChampionIndex f285_getIndexInCell(ViewCell cell); // @ F0285_CHAMPION_GetIndexInCell
int16 f279_getDecodedValue(char *string, uint16 characterCount); // @ F0279_CHAMPION_GetDecodedValue
void f289_drawHealthOrStaminaOrManaValue(int16 posy, int16 currVal, int16 maxVal); // @ F0289_CHAMPION_DrawHealthOrStaminaOrManaValue
uint16 M70_handSlotIndex(uint16 slotBoxIndex);// @ M70_HAND_SLOT_INDEX
+ int16 _g410_championPendingWounds[4]; // @ G0410_ai_ChampionPendingWounds
+ int16 _g409_championPendingDamage[4]; // @ G0409_ai_ChampionPendingDamage
public:
Champion _gK71_champions[4]; // @ K0071_as_Champions
uint16 _g305_partyChampionCount; // @ G0305_ui_PartyChampionCount
@@ -451,6 +466,9 @@ public:
bool _g420_mousePointerHiddenToDrawChangedObjIconOnScreen; // @ G0420_B_MousePointerHiddenToDrawChangedObjectIconOnScreen
explicit ChampionMan(DMEngine *vm);
+ ChampionIndex f285_getIndexInCell(int16 cell); // @ F0285_CHAMPION_GetIndexInCell
+ bool f329_isLeaderHandObjectThrown(int16 side); // @ F0329_CHAMPION_IsLeaderHandObjectThrown
+ bool f328_isObjectThrown(uint16 champIndex, int16 slotIndex, int16 side); // @ F0328_CHAMPION_IsObjectThrown
void f278_resetDataToStartGame(); // @ F0278_CHAMPION_ResetDataToStartGame
void f280_addCandidateChampionToParty(uint16 championPortraitIndex); // @ F0280_CHAMPION_AddCandidateChampionToParty
void f287_drawChampionBarGraphs(ChampionIndex champIndex); // @ F0287_CHAMPION_DrawBarGraphs
@@ -459,16 +477,42 @@ public:
void f292_drawChampionState(ChampionIndex champIndex); // @ F0292_CHAMPION_DrawState
uint16 M26_championIconIndex(int16 val, direction dir); // @ M26_CHAMPION_ICON_INDEX
void f290_drawHealthStaminaManaValues(Champion *champ); // @ F0290_CHAMPION_DrawHealthStaminaManaValues
- void f291_drawSlot(uint16 champIndex, ChampionSlot slotIndex); // @ F0291_CHAMPION_DrawSlot
+ void f291_drawSlot(uint16 champIndex, int16 slotIndex); // @ F0291_CHAMPION_DrawSlot
void f281_renameChampion(Champion* champ); // @ F0281_CHAMPION_Rename
- uint16 f303_getSkillLevel(ChampionIndex champIndex, ChampionSkill skillIndex);// @ F0303_CHAMPION_GetSkillLevel
+ uint16 f303_getSkillLevel(int16 champIndex, int16 skillIndex);// @ F0303_CHAMPION_GetSkillLevel
Common::String f288_getStringFromInteger(uint16 val, bool padding, uint16 paddingCharCount); // @ F0288_CHAMPION_GetStringFromInteger
- void f299_applyModifiersToStatistics(Champion *champ, ChampionSlot slotIndex, IconIndice iconIndex,
+ void f299_applyModifiersToStatistics(Champion *champ, int16 slotIndex, int16 iconIndex,
int16 modifierFactor, Thing thing); // @ F0299_CHAMPION_ApplyObjectModifiersToStatistics
bool f295_hasObjectIconInSlotBoxChanged(int16 slotBoxIndex, Thing thing); // @ F0295_CHAMPION_HasObjectIconInSlotBoxChanged
void f296_drawChangedObjectIcons(); // @ F0296_CHAMPION_DrawChangedObjectIcons
void f301_addObjectInSlot(ChampionIndex champIndex, Thing thing, ChampionSlot slotIndex); // @ F0301_CHAMPION_AddObjectInSlot
int16 f315_getScentOrdinal(int16 mapX, int16 mapY); // @ F0315_CHAMPION_GetScentOrdinal
+ Thing f298_getObjectRemovedFromLeaderHand(); // @ F0298_CHAMPION_GetObjectRemovedFromLeaderHand
+ uint16 f312_getStrength(int16 champIndex, int16 slotIndex); // @ F0312_CHAMPION_GetStrength
+ Thing f300_getObjectRemovedFromSlot(uint16 champIndex, uint16 slotIndex); // @ F0300_CHAMPION_GetObjectRemovedFromSlot
+ void f325_decrementStamine(int16 championIndex, int16 decrement); // @ F0325_CHAMPION_DecrementStamina
+ int16 f321_addPendingDamageAndWounds_getDamage(int16 champIndex, int16 attack, int16 allowedWounds,
+ uint16 attackType); // @ F0321_CHAMPION_AddPendingDamageAndWounds_GetDamage
+ int16 f313_getWoundDefense(int16 champIndex, uint16 woundIndex); // @ F0313_CHAMPION_GetWoundDefense
+ uint16 f307_getStatisticAdjustedAttack(Champion *champ, uint16 statIndex, uint16 attack); // @ F0307_CHAMPION_GetStatisticAdjustedAttack
+ void f314_wakeUp(); // @ F0314_CHAMPION_WakeUp
+ int16 f305_getThrowingStaminaCost(Thing thing);// @ F0305_CHAMPION_GetThrowingStaminaCost
+ void f330_disableAction(uint16 champIndex, uint16 ticks); // @ F0330_CHAMPION_DisableAction
+ void f304_addSkillExperience(uint16 champIndex, uint16 skillIndex, uint16 exp);// @ F0304_CHAMPION_AddSkillExperience
+ int16 f324_damageAll_getDamagedChampionCount(uint16 attack, int16 wounds,
+ int16 attackType); // @ F0324_CHAMPION_DamageAll_GetDamagedChampionCount
+ int16 f286_getTargetChampionIndex(int16 mapX, int16 mapY, uint16 cell); // @ F0286_CHAMPION_GetTargetChampionIndex
+ int16 f311_getDexterity(Champion *champ); // @ F0311_CHAMPION_GetDexterity
+ bool f308_isLucky(Champion *champ, uint16 percentage); // @ F0308_CHAMPION_IsLucky
+ void f322_championPoison(int16 championIndex, uint16 attack); // @ F0322_CHAMPION_Poison
+ void f284_setPartyDirection(int16 dir); // @ F0284_CHAMPION_SetPartyDirection
+ void f316_deleteScent(uint16 scentIndex); // @ F0316_CHAMPION_DeleteScent
+ void f317_addScentStrength(int16 mapX, int16 mapY, int32 cycleCount); // @ F0317_CHAMPION_AddScentStrength
+ void f297_putObjectInLeaderHand(Thing thing, bool setMousePointer); // @ F0297_CHAMPION_PutObjectInLeaderHand
+
+
+
+
};
diff --git a/engines/dm/dm.cpp b/engines/dm/dm.cpp
index d824cf349a..51b33c321d 100644
--- a/engines/dm/dm.cpp
+++ b/engines/dm/dm.cpp
@@ -51,6 +51,7 @@
#include "movesens.h"
#include "group.h"
#include "timeline.h"
+#include "projexpl.h"
namespace DM {
@@ -68,18 +69,11 @@ uint16 returnNextVal(uint16 val) {
bool isOrientedWestEast(direction dir) { return dir & 1; }
-uint16 getFlag(uint16 val, uint16 mask) {
- return val & mask;
-}
uint16 setFlag(uint16 &val, uint16 mask) {
return val |= mask;
}
-uint16 clearFlag(uint16 &val, uint16 mask) {
- return val &= ~mask;
-}
-
uint16 toggleFlag(uint16& val, uint16 mask) {
return val ^= mask;
}
@@ -92,6 +86,26 @@ uint16 M21_normalizeModulo4(uint16 val) {
return val & 3;
}
+int32 M30_time(int32 map_time) {
+ return map_time & 0x00FFFFFF;
+}
+
+int32 M33_setMapAndTime(int32 &map_time, uint32 map, uint32 time) {
+ return (map_time) = ((time) | (((long)(map)) << 24));
+}
+
+uint16 M29_map(int32 map_time) {
+ return ((uint16)((map_time) >> 24));
+}
+
+Thing M15_thingWithNewCell(Thing thing, int16 cell) {
+ return Thing(((thing.toUint16()) & 0x3FFF) | ((cell) << 14));
+}
+
+int16 M38_distance(int16 mapx1, int16 mapy1, int16 mapx2, int16 mapy2) {
+ return ABS(mapx1 - mapx2) + ABS(mapy1 - mapy2);
+}
+
DMEngine::DMEngine(OSystem *syst) : Engine(syst), _console(nullptr) {
// Do not load data files
// Do not initialize graphics here
@@ -118,6 +132,7 @@ DMEngine::DMEngine(OSystem *syst) : Engine(syst), _console(nullptr) {
_movsens = nullptr;
_groupMan = nullptr;
_timeline = nullptr;
+ _projexpl = nullptr;
_g321_stopWaitingForPlayerInput = false;
_g301_gameTimeTicking = false;
_g524_restartGameAllowed = false;
@@ -126,6 +141,9 @@ DMEngine::DMEngine(OSystem *syst) : Engine(syst), _console(nullptr) {
_g332_stopPressingEye = false;
_g334_stopPressingMouth = false;
_g340_highlightBoxInversionRequested = false;
+ _g313_gameTime = 0;
+ _g302_gameWon = false;
+ _g327_newPartyMapIndex = kM1_mapIndexNone;
debug("DMEngine::DMEngine");
}
@@ -148,11 +166,20 @@ DMEngine::~DMEngine() {
delete _movsens;
delete _groupMan;
delete _timeline;
+ delete _projexpl;
// clear debug channels
DebugMan.clearAllDebugChannels();
}
+void DMEngine::waitMs(uint16 ms) {
+ _system->delayMillis(ms * 20);
+}
+
+uint16 DMEngine::f30_getScaledProduct(uint16 val, uint16 scale, uint16 vale2) {
+ return ((uint32)val * vale2) >> scale;
+}
+
void DMEngine::f463_initializeGame() {
_displayMan->f479_loadGraphics();
_displayMan->f460_initializeGraphicData();
@@ -174,8 +201,8 @@ void DMEngine::f463_initializeGame() {
f462_startGame();
warning("MISSING CODE: F0267_MOVE_GetMoveResult_CPSCE (if newGame)");
- _eventMan->showMouse(true);
- warning("MISSING CODE: F0357_COMMAND_DiscardAllInput");
+ _eventMan->f78_showMouse();
+ _eventMan->f357_discardAllInput();
}
void DMEngine::f448_initMemoryManager() {
@@ -220,7 +247,7 @@ void DMEngine::f3_processNewPartyMap(uint16 mapIndex) {
_dungeonMan->f174_setCurrentMapAndPartyMap(mapIndex);
_displayMan->f96_loadCurrentMapGraphics();
warning("MISSING CODE: F0195_GROUP_AddAllActiveGroups");
- warning("MISSING CODE: F0337_INVENTORY_SetDungeonViewPalette");
+ _inventoryMan->f337_setDungeonViewPalette();
}
Common::Error DMEngine::run() {
@@ -241,6 +268,7 @@ Common::Error DMEngine::run() {
_movsens = new MovesensMan(this);
_groupMan = new GroupMan(this);
_timeline = new Timeline(this);
+ _projexpl = new ProjExpl(this);
_displayMan->setUpScreens(320, 200);
f463_initializeGame(); // @ F0463_START_InitializeGame_CPSADEF
@@ -257,16 +285,19 @@ void DMEngine::f2_gameloop() {
_dungeonMan->_g306_partyMapX = 10;
_dungeonMan->_g307_partyMapY = 4;
_dungeonMan->_g308_partyDir = kDirNorth;
-
-
warning("DUMMY CODE: setting InventoryMan::_g432_inventoryChampionOrdinal to zero");
_inventoryMan->_g432_inventoryChampionOrdinal = 0;
warning("DUMMY CODE: clearing screen to black"); // in loop below
+
while (true) {
- _g321_stopWaitingForPlayerInput = false;
+ _g313_gameTime++;
_menuMan->f390_refreshActionAreaAndSetChampDirMaxDamageReceived();
+ if (_g311_projectileDisableMovementTicks)
+ _g311_projectileDisableMovementTicks--;
+
+ _g321_stopWaitingForPlayerInput = false;
//do {
_eventMan->processInput();
_eventMan->f380_processCommandQueue();
@@ -278,12 +309,8 @@ void DMEngine::f2_gameloop() {
_displayMan->f128_drawDungeon(_dungeonMan->_g308_partyDir, _dungeonMan->_g306_partyMapX, _dungeonMan->_g307_partyMapY);
}
- // DUMMY CODE: next 2 lines
- _menuMan->f395_drawMovementArrows();
- _displayMan->f97_drawViewport(k1_viewportDungeonView);
-
_displayMan->updateScreen();
- _system->delayMillis(10);
+ _system->delayMillis(18);
}
}
diff --git a/engines/dm/dm.h b/engines/dm/dm.h
index c4fe3c5ce1..9ace815f51 100644
--- a/engines/dm/dm.h
+++ b/engines/dm/dm.h
@@ -48,6 +48,8 @@ class TextMan;
class MovesensMan;
class GroupMan;
class Timeline;
+class ProjExpl;
+
enum direction {
@@ -57,50 +59,26 @@ enum direction {
kDirWest = 3
};
-void turnDirRight(direction &dir);
-void turnDirLeft(direction &dir);
-direction returnOppositeDir(direction dir); // @ M18_OPPOSITE
-uint16 returnPrevVal(uint16 val); // @ M19_PREVIOUS
-uint16 returnNextVal(uint16 val); // @ M17_NEXT
-bool isOrientedWestEast(direction dir); // @ M16_IS_ORIENTED_WEST_EAST
-
-
-uint16 getFlag(uint16 val, uint16 mask); // @ M07_GET
-uint16 setFlag(uint16 &val, uint16 mask); // @ M08_SET
-uint16 clearFlag(uint16 &val, uint16 mask); // @ M09_CLEAR
-uint16 toggleFlag(uint16 &val, uint16 mask); // @ M10_TOGGLE
-uint16 M75_bitmapByteCount(uint16 pixelWidth, uint16 height); // @ M75_BITMAP_BYTE_COUNT
-uint16 M21_normalizeModulo4(uint16 val); // @ M21_NORMALIZE
enum ThingType {
- kM1_PartyThingType = -1, // @ CM1_THING_TYPE_PARTY, special value
- k0_DoorThingType = 0,
- k1_TeleporterThingType = 1,
- k2_TextstringType = 2,
- k3_SensorThingType = 3,
- k4_GroupThingType = 4,
- k5_WeaponThingType = 5,
- k6_ArmourThingType = 6,
- k7_ScrollThingType = 7,
- k8_PotionThingType = 8,
- k9_ContainerThingType = 9,
- k10_JunkThingType = 10,
- k14_ProjectileThingType = 14,
- k15_ExplosionThingType = 15,
+ kM1_PartyThingType = -1, // @ CM1_THING_TYPE_PARTY
+ k0_DoorThingType = 0, // @ C00_THING_TYPE_DOOR
+ k1_TeleporterThingType = 1, // @ C01_THING_TYPE_TELEPORTER
+ k2_TextstringType = 2, // @ C02_THING_TYPE_TEXTSTRING
+ k3_SensorThingType = 3, // @ C03_THING_TYPE_SENSOR
+ k4_GroupThingType = 4, // @ C04_THING_TYPE_GROUP
+ k5_WeaponThingType = 5, // @ C05_THING_TYPE_WEAPON
+ k6_ArmourThingType = 6, // @ C06_THING_TYPE_ARMOUR
+ k7_ScrollThingType = 7, // @ C07_THING_TYPE_SCROLL
+ k8_PotionThingType = 8, // @ C08_THING_TYPE_POTION
+ k9_ContainerThingType = 9, // @ C09_THING_TYPE_CONTAINER
+ k10_JunkThingType = 10, // @ C10_THING_TYPE_JUNK
+ k14_ProjectileThingType = 14, // @ C14_THING_TYPE_PROJECTILE
+ k15_ExplosionThingType = 15, // @ C15_THING_TYPE_EXPLOSION
k16_ThingTypeTotal = 16 // +1 than the last (explosionThingType)
}; // @ C[00..15]_THING_TYPE_...
-enum Cell {
- kM1_CellAny = -1, // @ CM1_CELL_ANY
- k0_CellNorthWest = 0, // @ C00_CELL_NORTHWEST
- k1_CellNorthEast = 1, // @ C01_CELL_NORTHEAST
- k2_CellSouthEast = 2, // @ C02_CELL_SOUTHEAST
- k3_CellSouthWest = 3 // @ C03_CELL_SOUTHWEST
-};
-
-#define kM1_mapIndexNone -1 // @ CM1_MAP_INDEX_NONE
-#define k255_mapIndexEntrance 255 // @ C255_MAP_INDEX_ENTRANCE
class Thing {
uint16 _data;
@@ -131,12 +109,52 @@ public:
byte getCell() const { return _data >> 14; }
ThingType getType() const { return (ThingType)((_data >> 10) & 0xF); }
uint16 getIndex() const { return _data & 0x3FF; }
+ uint16 getTypeAndIndex() { return _data & 0x3FFF; }
uint16 toUint16() const { return _data; } // I don't like 'em cast operators
bool operator==(const Thing &rhs) const { return _data == rhs._data; }
bool operator!=(const Thing &rhs) const { return _data != rhs._data; }
}; // @ THING
+
+void turnDirRight(direction &dir);
+void turnDirLeft(direction &dir);
+direction returnOppositeDir(direction dir); // @ M18_OPPOSITE
+uint16 returnPrevVal(uint16 val); // @ M19_PREVIOUS
+uint16 returnNextVal(uint16 val); // @ M17_NEXT
+bool isOrientedWestEast(direction dir); // @ M16_IS_ORIENTED_WEST_EAST
+
+
+uint16 setFlag(uint16 &val, uint16 mask); // @ M08_SET
+
+
+#define getFlag(val, mask) ((val) & (mask))
+
+
+#define clearFlag(val, mask) ((val) &= (~(mask))) // @ M09_CLEAR
+
+uint16 toggleFlag(uint16 &val, uint16 mask); // @ M10_TOGGLE
+uint16 M75_bitmapByteCount(uint16 pixelWidth, uint16 height); // @ M75_BITMAP_BYTE_COUNT
+uint16 M21_normalizeModulo4(uint16 val); // @ M21_NORMALIZE
+int32 M30_time(int32 map_time); // @ M30_TIME
+int32 M33_setMapAndTime(int32 &map_time, uint32 map, uint32 time); // @ M33_SET_MAP_AND_TIME
+uint16 M29_map(int32 map_time); // @ M29_MAP
+Thing M15_thingWithNewCell(Thing thing, int16 cell); // @ M15_THING_WITH_NEW_CELL
+int16 M38_distance(int16 mapx1, int16 mapy1, int16 mapx2, int16 mapy2);// @ M38_DISTANCE
+
+
+enum Cell {
+ kM1_CellAny = -1, // @ CM1_CELL_ANY
+ k0_CellNorthWest = 0, // @ C00_CELL_NORTHWEST
+ k1_CellNorthEast = 1, // @ C01_CELL_NORTHEAST
+ k2_CellSouthEast = 2, // @ C02_CELL_SOUTHEAST
+ k3_CellSouthWest = 3 // @ C03_CELL_SOUTHWEST
+};
+
+#define kM1_mapIndexNone -1 // @ CM1_MAP_INDEX_NONE
+#define k255_mapIndexEntrance 255 // @ C255_MAP_INDEX_ENTRANCE
+
+
enum {
// engine debug channels
kDMDebugExample = 1 << 0,
@@ -144,6 +162,17 @@ enum {
kDMDebugOftenCalledWarning = 2 << 2
};
+
+
+
+template<typename T>
+inline T f26_getBoundedValue(T min, T val, T max) {
+ return MIN(MAX(min, val), max);
+} // @ F0026_MAIN_GetBoundedValue
+
+#define CALL_MEMBER_FN(object,ptrToMember) ((object).*(ptrToMember))
+
+
class DMEngine : public Engine {
void f462_startGame(); // @ F0462_START_StartGame_CPSF
void f3_processNewPartyMap(uint16 mapIndex); // @ F0003_MAIN_ProcessNewPartyMap_CPSE
@@ -156,8 +185,12 @@ public:
explicit DMEngine(OSystem *syst);
~DMEngine();
+ void waitMs(uint16 ms);
+ uint16 f30_getScaledProduct(uint16 val, uint16 scale, uint16 vale2); // @ F0030_MAIN_GetScaledProduct
+ uint16 getRandomNumber(uint32 max) { return _rnd->getRandomNumber(max - 1); }
int16 M1_ordinalToIndex(int16 val); // @ M01_ORDINAL_TO_INDEX
int16 M0_indexToOrdinal(int16 val); // @ M00_INDEX_TO_ORDINAL
+ void f19_displayErrorAndStop(int16 errorIndex); // @ F0019_MAIN_DisplayErrorAndStop
virtual Common::Error run(); // @ main
private:
@@ -176,6 +209,7 @@ public:
MovesensMan *_movsens;
GroupMan *_groupMan;
Timeline *_timeline;
+ ProjExpl *_projexpl;
bool _g298_newGame; // @ G0298_B_NewGame
@@ -190,10 +224,17 @@ public:
bool _g333_pressingMouth; // @ G0333_B_PressingMouth
bool _g334_stopPressingMouth; // @ G0334_B_StopPressingMouth
bool _g340_highlightBoxInversionRequested; // @ G0340_B_HighlightBoxInversionRequested
+ int16 _g311_projectileDisableMovementTicks; // @ G0311_i_ProjectileDisabledMovementTicks
+ int16 _g312_lastProjectileDisabledMovementDirection; // @ G0312_i_LastProjectileDisabledMovementDirection
+ bool _g302_gameWon; // @ G0302_B_GameWon
+ int16 _g327_newPartyMapIndex; // @ G0327_i_NewPartyMapIndex
+ bool _g325_setMousePointerToObjectInMainLoop; // @ G0325_B_SetMousePointerToObjectInMainLoop
// TODO: refactor direction into a class
int8 _dirIntoStepCountEast[4]; // @ G0233_ai_Graphic559_DirectionToStepEastCount
int8 _dirIntoStepCountNorth[4]; // @ G0234_ai_Graphic559_DirectionToStepNorthCount
+ uint32 _g313_gameTime; // @ G0313_ul_GameTime
+ char _g353_stringBuildBuffer[128]; // @ G0353_ac_StringBuildBuffer
};
class Console : public GUI::Debugger {
diff --git a/engines/dm/dmglobals.cpp b/engines/dm/dmglobals.cpp
index c138453c31..044659aa2e 100644
--- a/engines/dm/dmglobals.cpp
+++ b/engines/dm/dmglobals.cpp
@@ -25,6 +25,7 @@
* maintainer of the Dungeon Master Encyclopaedia (http://dmweb.free.fr/)
*/
+#include "common/system.h"
#include "dm/dm.h"
#include "gfx.h"
#include "dungeonman.h"
@@ -36,6 +37,8 @@
#include "inventory.h"
#include "text.h"
#include "movesens.h"
+#include "string.h"
+
namespace DM {
@@ -52,4 +55,12 @@ void DMEngine::initArrays() {
_dirIntoStepCountNorth[2] = 1; // West
_dirIntoStepCountNorth[3] = 0; // South
}
+
+void DMEngine::f19_displayErrorAndStop(int16 errorIndex) {
+ debug("Stuff hit the fun: ");
+ debug(Common::String::format("%d", errorIndex).c_str());
+ Common::Event event;
+ while (_system->getEventManager()->pollEvent(event) || true)
+ ;
+}
} // End of namespace DM
diff --git a/engines/dm/dungeonman.cpp b/engines/dm/dungeonman.cpp
index fa0e72c4ba..ca7785652b 100644
--- a/engines/dm/dungeonman.cpp
+++ b/engines/dm/dungeonman.cpp
@@ -31,7 +31,9 @@
#include "dungeonman.h"
#include "timeline.h"
#include "champion.h"
-
+#include "group.h"
+#include "movesens.h"
+#include "projexpl.h"
namespace DM {
@@ -835,11 +837,11 @@ void DungeonMan::f172_setSquareAspect(uint16 *aspectArray, direction dir, int16
#define footprintsAllowed L0307_uc_Multiple
#define scentOrdinal L0307_uc_Multiple
Sensor* sensor;
- bool leftRandWallOrnAllowed;
- int16 L0310_i_Multiple;
+ bool leftRandWallOrnAllowed = false;
+ int16 L0310_i_Multiple = 0;
#define frontRandWallOrnAllowed L0310_i_Multiple
#define sideIndex L0310_i_Multiple
- bool rightRandWallOrnAllowed;
+ bool rightRandWallOrnAllowed = false;
int16 thingTypeRedEagle;
bool squreIsFakeWall;
Thing thing;
@@ -946,12 +948,12 @@ T0172030_Pit:
aspectArray[k2_TeleporterVisibleAspect] = getFlag(square, k0x0008_TeleporterOpen) && getFlag(square, k0x0004_TeleporterVisible);
goto T0172029_Teleporter;
case k3_ElementTypeStairs:
- aspectArray[k0_ElemAspect] = ((getFlag(square, k0x0008_StairsNorthSouthOrient) >> 3) == isOrientedWestEast(dir)) ? k18_ElementTypeStairsSide : k19_ElementTypeStaisFront;
+ aspectArray[k0_ElemAspect] = (((getFlag(square, k0x0008_StairsNorthSouthOrient) >> 3) ? true : false) == isOrientedWestEast(dir)) ? k18_ElementTypeStairsSide : k19_ElementTypeStaisFront;
aspectArray[k2_StairsUpAspect] = getFlag(square, k0x0004_StairsUp);
footprintsAllowed = false;
goto T0172046_Stairs;
case k4_DoorElemType:
- if ((getFlag(square, k0x0008_DoorNorthSouthOrient) >> 3) == isOrientedWestEast(dir)) {
+ if (((getFlag(square, k0x0008_DoorNorthSouthOrient) >> 3) ? true : false) == isOrientedWestEast(dir)) {
aspectArray[k0_ElemAspect] = k16_DoorSideElemType;
} else {
aspectArray[k0_ElemAspect] = k17_DoorFrontElemType;
@@ -1190,6 +1192,45 @@ void DungeonMan::f168_decodeText(char *destString, Thing thing, TextType type) {
*destString = ((type == k0_TextTypeInscription) ? 0x81 : '\0');
}
+Thing DungeonMan::f166_getUnusedThing(uint16 thingType) {
+ int16 L0288_i_ThingIndex;
+ int16 L0289_i_ThingDataByteCount;
+ int16 L0290_i_ThingCount;
+ Thing* L0291_ps_Generic;
+ Thing L0292_T_Thing;
+
+
+ L0290_i_ThingCount = _vm->_dungeonMan->_g278_dungeonFileHeader._thingCounts[getFlag(thingType, k0x7FFF_thingType)];
+ if (thingType == (k0x8000_championBones | k10_JunkThingType)) {
+ thingType = k10_JunkThingType;
+ } else {
+ if (thingType == k10_JunkThingType) {
+ L0290_i_ThingCount -= 3; /* Always keep 3 unused JUNK things for the bones of dead champions */
+ }
+ }
+ L0288_i_ThingIndex = L0290_i_ThingCount;
+ L0289_i_ThingDataByteCount = g235_ThingDataWordCount[thingType] >> 1;
+ L0291_ps_Generic = (Thing*)_vm->_dungeonMan->_g284_thingData[thingType];
+ for (;;) { /*_Infinite loop_*/
+ if (*L0291_ps_Generic == Thing::_none) { /* If thing data is unused */
+ L0292_T_Thing = Thing((thingType << 10) | (L0290_i_ThingCount - L0288_i_ThingIndex));
+ break;
+ }
+ if (--L0288_i_ThingIndex) { /* If there are thing data left to process */
+ L0291_ps_Generic += L0289_i_ThingDataByteCount; /* Proceed to the next thing data */
+ } else {
+ if ((L0292_T_Thing = f165_getDiscardTHing(thingType)) == Thing::_none) {
+ return Thing::_none;
+ }
+ L0291_ps_Generic = (Thing*)_vm->_dungeonMan->f156_getThingData(L0292_T_Thing);
+ break;
+ }
+ }
+ memset(L0291_ps_Generic, 0, L0289_i_ThingDataByteCount * 2);
+
+ *L0291_ps_Generic = Thing::_endOfList;
+ return L0292_T_Thing;
+}
uint16 DungeonMan::f140_getObjectWeight(Thing thing) {
static const uint16 g241_junkInfo[] = { // @ G0241_auc_Graphic559_JunkInfo
@@ -1381,4 +1422,293 @@ int16 DungeonMan::f154_getLocationAfterLevelChange(int16 mapIndex, int16 levelDe
}
return kM1_mapIndexNone;
}
+
+Thing DungeonMan::f162_getSquareFirstObject(int16 mapX, int16 mapY) {
+ Thing thing = f161_getSquareFirstThing(mapX, mapY);
+ while ((thing != Thing::_endOfList) && (thing.getType() < k4_GroupThingType)) {
+ thing = f159_getNextThing(thing);
+ }
+ return thing;
+}
+
+uint16 DungeonMan::f143_getArmourDefense(ArmourInfo* armourInfo, bool useSharpDefense) {
+ uint16 L0244_ui_Defense;
+
+ L0244_ui_Defense = armourInfo->_defense;
+ if (useSharpDefense) {
+ L0244_ui_Defense = _vm->f30_getScaledProduct(L0244_ui_Defense, 3, getFlag(armourInfo->_attributes, k0x0007_ArmourAttributeSharpDefense) + 4);
+ }
+ return L0244_ui_Defense;
+}
+
+Thing DungeonMan::f165_getDiscardTHing(uint16 thingType) {
+ uint16 L0276_ui_MapX;
+ uint16 L0277_ui_MapY;
+ Thing L0278_T_Thing;
+ uint16 L0279_ui_MapIndex;
+ byte* L0280_puc_Square;
+ Thing* L0281_pT_SquareFirstThing;
+ Thing* L0282_ps_Generic;
+ uint16 L0283_ui_DiscardThingMapIndex;
+ int L0284_i_CurrentMapIndex;
+ uint16 L0285_ui_MapWidth;
+ uint16 L0286_ui_MapHeight;
+ int L0287_i_ThingType;
+ static unsigned char G0294_auc_LastDiscardedThingMapIndex[16];
+
+
+ if (thingType == k15_ExplosionThingType) {
+ return Thing::_none;
+ }
+ L0284_i_CurrentMapIndex = _vm->_dungeonMan->_g272_currMapIndex;
+ if (((L0279_ui_MapIndex = G0294_auc_LastDiscardedThingMapIndex[thingType]) == _vm->_dungeonMan->_g309_partyMapIndex) && (++L0279_ui_MapIndex >= _vm->_dungeonMan->_g278_dungeonFileHeader._mapCount)) {
+ L0279_ui_MapIndex = 0;
+ }
+ L0283_ui_DiscardThingMapIndex = L0279_ui_MapIndex;
+ for (;;) { /*_Infinite loop_*/
+ L0285_ui_MapWidth = _vm->_dungeonMan->_g277_dungeonMaps[L0279_ui_MapIndex]._width;
+ L0286_ui_MapHeight = _vm->_dungeonMan->_g277_dungeonMaps[L0279_ui_MapIndex]._height;
+ L0280_puc_Square = _vm->_dungeonMan->_g279_dungeonMapData[L0279_ui_MapIndex][0];
+ L0281_pT_SquareFirstThing = &_vm->_dungeonMan->_g283_squareFirstThings[_vm->_dungeonMan->_g280_dungeonColumnsCumulativeSquareThingCount[_vm->_dungeonMan->_g281_dungeonMapsFirstColumnIndex[L0279_ui_MapIndex]]];
+ for (L0276_ui_MapX = 0; L0276_ui_MapX <= L0285_ui_MapWidth; L0276_ui_MapX++) {
+ for (L0277_ui_MapY = 0; L0277_ui_MapY <= L0286_ui_MapHeight; L0277_ui_MapY++) {
+ if (getFlag(*L0280_puc_Square++, k0x0010_ThingListPresent)) {
+ L0278_T_Thing = *L0281_pT_SquareFirstThing++;
+ if ((L0279_ui_MapIndex == _vm->_dungeonMan->_g309_partyMapIndex) && ((L0276_ui_MapX - _vm->_dungeonMan->_g306_partyMapX + 5) <= 10) && ((L0277_ui_MapY - _vm->_dungeonMan->_g307_partyMapY + 5) <= 10)) /* If square is too close to the party */
+ goto T0165029;
+ do {
+ if ((L0287_i_ThingType = (L0278_T_Thing).getType()) == k3_SensorThingType) {
+ L0282_ps_Generic = (Thing*)_vm->_dungeonMan->f156_getThingData(L0278_T_Thing);
+ if (((Sensor*)L0282_ps_Generic)->getType()) /* If sensor is not disabled */
+ break;
+ } else {
+ if (L0287_i_ThingType == thingType) {
+ L0282_ps_Generic = (Thing*)_vm->_dungeonMan->f156_getThingData(L0278_T_Thing);
+ switch (thingType) {
+ case k4_GroupThingType:
+ if (((Group*)L0282_ps_Generic)->getDoNotDiscard())
+ continue;
+ case k14_ProjectileThingType:
+ _vm->_dungeonMan->f173_setCurrentMap(L0279_ui_MapIndex);
+ if (thingType == k4_GroupThingType) {
+ _vm->_groupMan->f188_dropGroupPossessions(L0276_ui_MapX, L0277_ui_MapY, L0278_T_Thing, kM1_soundModeDoNotPlaySound);
+ _vm->_groupMan->f189_delete(L0276_ui_MapX, L0277_ui_MapY);
+ } else {
+ _vm->_projexpl->f214_projectileDeleteEvent(L0278_T_Thing);
+ f164_unlinkThingFromList(L0278_T_Thing, Thing(0), L0276_ui_MapX, L0277_ui_MapY);
+ _vm->_projexpl->f215_projectileDelete(L0278_T_Thing, 0, L0276_ui_MapX, L0277_ui_MapY);
+ }
+ break;
+ case k6_ArmourThingType:
+ if (((Armour*)L0282_ps_Generic)->getDoNotDiscard())
+ continue;
+ goto T0165026;
+ case k5_WeaponThingType:
+ if (((Weapon*)L0282_ps_Generic)->getDoNotDiscard())
+ continue;
+ goto T0165026;
+ case k10_JunkThingType:
+ if (((Junk*)L0282_ps_Generic)->getDoNotDiscard())
+ continue;
+ goto T0165026;
+ case k8_PotionThingType:
+ if (((Potion*)L0282_ps_Generic)->getDoNotDiscard())
+ continue;
+T0165026:
+ _vm->_dungeonMan->f173_setCurrentMap(L0279_ui_MapIndex);
+ _vm->_movsens->f267_getMoveResult(L0278_T_Thing, L0276_ui_MapX, L0277_ui_MapY, kM1_MapXNotOnASquare, 0);
+ }
+ _vm->_dungeonMan->f173_setCurrentMap(L0284_i_CurrentMapIndex);
+ G0294_auc_LastDiscardedThingMapIndex[thingType] = L0279_ui_MapIndex;
+ return Thing((L0278_T_Thing).getTypeAndIndex());
+ }
+ }
+ } while ((L0278_T_Thing = _vm->_dungeonMan->f159_getNextThing(L0278_T_Thing)) != Thing::_endOfList);
+T0165029:
+ ;
+ }
+ }
+ }
+ if ((L0279_ui_MapIndex == _vm->_dungeonMan->_g309_partyMapIndex) || (_vm->_dungeonMan->_g278_dungeonFileHeader._mapCount <= 1)) {
+ G0294_auc_LastDiscardedThingMapIndex[thingType] = L0279_ui_MapIndex;
+ return Thing::_none;
+ }
+ do {
+ if (++L0279_ui_MapIndex >= _vm->_dungeonMan->_g278_dungeonFileHeader._mapCount) {
+ L0279_ui_MapIndex = 0;
+ }
+ } while (L0279_ui_MapIndex == _vm->_dungeonMan->_g309_partyMapIndex);
+ if (L0279_ui_MapIndex == L0283_ui_DiscardThingMapIndex) {
+ L0279_ui_MapIndex = _vm->_dungeonMan->_g309_partyMapIndex;
+ }
+ }
+}
+
+uint16 DungeonMan::f144_getCreatureAttributes(Thing thing) {
+ Group* L0245_ps_Group;
+
+ L0245_ps_Group = (Group*)_vm->_dungeonMan->f156_getThingData(thing);
+ return g243_CreatureInfo[L0245_ps_Group->_type]._attributes;
+}
+
+void DungeonMan::f146_setGroupCells(Group* group, uint16 cells, uint16 mapIndex) {
+ if (mapIndex == _vm->_dungeonMan->_g309_partyMapIndex) {
+ _vm->_groupMan->_g375_activeGroups[group->getActiveGroupIndex()]._cells = cells;
+ } else {
+ group->_cells = cells;
+ }
+}
+
+void DungeonMan::f148_setGroupDirections(Group* group, int16 dir, uint16 mapIndex) {
+ if (mapIndex == _vm->_dungeonMan->_g309_partyMapIndex) {
+ _vm->_groupMan->_g375_activeGroups[group->getActiveGroupIndex()]._directions = (direction)dir;
+ } else {
+ group->setDir(M21_normalizeModulo4(dir));
+ }
+}
+
+bool DungeonMan::f139_isCreatureAllowedOnMap(Thing thing, uint16 mapIndex) {
+ int16 L0234_i_Counter;
+ int16 L0235_i_CreatureType;
+ byte* L0236_puc_Multiple;
+#define AL0236_puc_Group L0236_puc_Multiple
+#define AL0236_puc_AllowedCreatureType L0236_puc_Multiple
+ Map* L0237_ps_Map;
+
+ L0235_i_CreatureType = ((Group*)_vm->_dungeonMan->f156_getThingData(thing))->_type;
+ L0237_ps_Map = &_vm->_dungeonMan->_g277_dungeonMaps[mapIndex];
+ AL0236_puc_AllowedCreatureType = _vm->_dungeonMan->_g279_dungeonMapData[mapIndex][L0237_ps_Map->_width] + L0237_ps_Map->_height + 1;
+ for (L0234_i_Counter = L0237_ps_Map->_creatureTypeCount; L0234_i_Counter > 0; L0234_i_Counter--) {
+ if (*AL0236_puc_AllowedCreatureType++ == L0235_i_CreatureType) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void DungeonMan::f164_unlinkThingFromList(Thing thingToUnlink, Thing thingInList, int16 mapX, int16 mapY) {
+ uint16 L0271_ui_SquareFirstThingIndex;
+ uint16 L0272_ui_Multiple;
+#define AL0272_ui_SquareFirstThingIndex L0272_ui_Multiple
+#define AL0272_ui_Column L0272_ui_Multiple
+ Thing L0273_T_Thing;
+ Thing* L0274_ps_Generic = nullptr;
+ Thing* L0275_pui_Multiple = nullptr;
+#define AL0275_pT_Thing L0275_pui_Multiple
+#define AL0275_pui_CumulativeFirstThingCount L0275_pui_Multiple
+
+
+ if (thingToUnlink == Thing::_endOfList) {
+ return;
+ }
+
+ {
+ uint16 tmp = thingToUnlink.toUint16();
+ clearFlag(tmp, 0xC000);
+ thingToUnlink = Thing(tmp);
+ }
+
+ if (mapX >= 0) {
+ L0274_ps_Generic = (Thing*)_vm->_dungeonMan->f156_getThingData(thingToUnlink);
+ AL0275_pT_Thing = &_vm->_dungeonMan->_g283_squareFirstThings[L0271_ui_SquareFirstThingIndex = _vm->_dungeonMan->f160_getSquareFirstThingIndex(mapX, mapY)]; /* BUG0_01 Coding error without consequence. The engine does not check that there are things at the specified square coordinates. _vm->_dungeonMan->f160_getSquareFirstThingIndex would return -1 for an empty square. No consequence as the function is never called with the coordinates of an empty square (except in the case of BUG0_59) */
+ if ((*L0274_ps_Generic == Thing::_endOfList) && (((Thing*)AL0275_pT_Thing)->getTypeAndIndex() == thingToUnlink.toUint16())) { /* If the thing to unlink is the last thing on the square */
+ clearFlag(_vm->_dungeonMan->_g271_currMapData[mapX][mapY], k0x0010_ThingListPresent);
+ AL0272_ui_SquareFirstThingIndex = _vm->_dungeonMan->_g278_dungeonFileHeader._squareFirstThingCount - 1;
+ for (uint16 i = 0; i < AL0272_ui_SquareFirstThingIndex - L0271_ui_SquareFirstThingIndex; ++i)
+ AL0275_pT_Thing[i] = AL0275_pT_Thing[i + 1];
+
+ _vm->_dungeonMan->_g283_squareFirstThings[AL0272_ui_SquareFirstThingIndex] = Thing::_none;
+ AL0275_pui_CumulativeFirstThingCount = (Thing*)_vm->_dungeonMan->_g270_currMapColCumulativeSquareFirstThingCount + mapX + 1;
+ AL0272_ui_Column = _vm->_dungeonMan->_g282_dungeonColumCount - (_vm->_dungeonMan->_g281_dungeonMapsFirstColumnIndex[_vm->_dungeonMan->_g272_currMapIndex] + mapX) - 1;
+ while (AL0272_ui_Column--) { /* For each column starting from and after the column containing the square where the thing is unlinked */
+ (*(uint16*)AL0275_pui_CumulativeFirstThingCount++)--; /* Decrement the cumulative first thing count */
+ }
+ goto T0164011;
+ }
+ if (((Thing*)AL0275_pT_Thing)->getTypeAndIndex() == thingToUnlink.toUint16()) {
+ *AL0275_pT_Thing = *L0274_ps_Generic;
+ goto T0164011;
+ }
+ thingInList = *AL0275_pT_Thing;
+ }
+ L0273_T_Thing = _vm->_dungeonMan->f159_getNextThing(thingInList);
+ while (L0273_T_Thing.getTypeAndIndex() != thingToUnlink.toUint16()) {
+ if ((L0273_T_Thing == Thing::_endOfList) || (L0273_T_Thing == Thing::_none)) {
+ goto T0164011;
+ }
+ L0273_T_Thing = _vm->_dungeonMan->f159_getNextThing(thingInList = L0273_T_Thing);
+ }
+ L0274_ps_Generic = (Thing*)_vm->_dungeonMan->f156_getThingData(thingInList);
+ *L0274_ps_Generic = _vm->_dungeonMan->f159_getNextThing(L0273_T_Thing);
+ L0274_ps_Generic = (Thing*)_vm->_dungeonMan->f156_getThingData(thingToUnlink);
+T0164011:
+ *L0274_ps_Generic = Thing::_endOfList;
+}
+
+int16 DungeonMan::f155_getStairsExitDirection(int16 mapX, int16 mapY) {
+ int16 L0256_i_SquareType;
+ bool L0257_B_NorthSouthOrientedStairs;
+
+
+ if (L0257_B_NorthSouthOrientedStairs = !getFlag(_vm->_dungeonMan->f151_getSquare(mapX, mapY).toByte(), k0x0008_StairsNorthSouthOrient)) {
+ mapX = mapX + _vm->_dirIntoStepCountEast[kDirEast];
+ mapY = mapY + _vm->_dirIntoStepCountNorth[kDirEast];
+ } else {
+ mapX = mapX + _vm->_dirIntoStepCountEast[kDirNorth];
+ mapY = mapY + _vm->_dirIntoStepCountNorth[kDirNorth];
+ }
+ return ((((L0256_i_SquareType = Square(_vm->_dungeonMan->f151_getSquare(mapX, mapY)).getType()) == k0_ElementTypeWall) || (L0256_i_SquareType == k3_ElementTypeStairs)) << 1) + L0257_B_NorthSouthOrientedStairs;
+
+}
+
+Thing DungeonMan::f167_getObjForProjectileLaucherOrObjGen(uint16 iconIndex) {
+ int16 L0293_i_Type;
+ int16 L0294_i_ThingType;
+ Thing L0295_T_Thing;
+ Junk* L0296_ps_Junk;
+
+
+ L0294_i_ThingType = k5_WeaponThingType;
+ if ((iconIndex >= k4_IconIndiceWeaponTorchUnlit) && (iconIndex <= k7_IconIndiceWeaponTorchLit)) {
+ iconIndex = k4_IconIndiceWeaponTorchUnlit;
+ }
+ switch (iconIndex) {
+ case k54_IconIndiceWeaponRock:
+ L0293_i_Type = k30_WeaponTypeRock;
+ break;
+ case k128_IconIndiceJunkBoulder:
+ L0293_i_Type = k25_JunkTypeBoulder;
+ L0294_i_ThingType = k10_JunkThingType;
+ break;
+ case k51_IconIndiceWeaponArrow:
+ L0293_i_Type = k27_WeaponTypeArrow;
+ break;
+ case k52_IconIndiceWeaponSlayer:
+ L0293_i_Type = k28_WeaponTypeSlayer;
+ break;
+ case k55_IconIndiceWeaponPoisonDart:
+ L0293_i_Type = k31_WeaponTypePoisonDart;
+ break;
+ case k56_IconIndiceWeaponThrowingStar:
+ L0293_i_Type = k32_WeaponTypeThrowingStar;
+ break;
+ case k32_IconIndiceWeaponDagger:
+ L0293_i_Type = k8_WeaponTypeDagger;
+ break;
+ case k4_IconIndiceWeaponTorchUnlit:
+ L0293_i_Type = k2_WeaponTypeTorch;
+ break;
+ default:
+ return Thing::_none;
+ }
+ if ((L0295_T_Thing = f166_getUnusedThing(L0294_i_ThingType)) == Thing::_none) {
+ return Thing::_none;
+ }
+ L0296_ps_Junk = (Junk*)_vm->_dungeonMan->f156_getThingData(L0295_T_Thing);
+ L0296_ps_Junk->setType(L0293_i_Type); /* Also works for WEAPON in cases other than Boulder */
+ if ((iconIndex == k4_IconIndiceWeaponTorchUnlit) && ((Weapon*)L0296_ps_Junk)->isLit()) { /* BUG0_65 Torches created by object generator or projectile launcher sensors have no charges. Charges are only defined if the Torch is lit which is not possible at the time it is created */
+ ((Weapon*)L0296_ps_Junk)->setChargeCount(15);
+ }
+ return L0295_T_Thing;
+}
}
diff --git a/engines/dm/dungeonman.h b/engines/dm/dungeonman.h
index a32edcf3a4..c591881094 100644
--- a/engines/dm/dungeonman.h
+++ b/engines/dm/dungeonman.h
@@ -107,9 +107,7 @@ class ArmourInfo {
public:
uint16 _weight;
uint16 _defense;
-private:
uint16 _attributes;
-public:
ArmourInfo(uint16 weight, uint16 defense, uint16 attributes)
:_weight(weight), _defense(defense), _attributes(attributes) {}
@@ -178,8 +176,9 @@ enum SquareAspectIndice {
};
-
+#define k15_immuneToFire 15 // @ C15_IMMUNE_TO_FIRE
+#define k15_immuneToPoison 15 // @ C15_IMMUNE_TO_POISON
class CreatureInfo {
public:
@@ -200,6 +199,17 @@ public:
uint16 _animationTicks; /* Bits 15-12 Unreferenced */
uint16 _woundProbabilities; /* Contains 4 probabilities to wound a champion's Head (Bits 15-12), Legs (Bits 11-8), Torso (Bits 7-4) and Feet (Bits 3-0) */
byte _attackType;
+
+ uint16 M57_getFearResistance() { return (_properties >> 4) & 0xF; }
+ uint16 M58_getExperience() { return (_properties >> 8) & 0xF; }
+ uint16 M59_getWariness() { return (_properties >> 12) & 0xF; }
+ uint16 M60_getFireResistance() { return (_resistances >> 4) & 0xF; }
+ uint16 M61_poisonResistance() { return (_resistances >> 8) & 0xF; }
+ uint16 M51_height() { return (_attributes >> 7) & 0x3; }
+
+ uint16 M54_getSightRange() { return (_ranges) & 0xF; }
+ uint16 M55_getSmellRange() { return (_ranges >> 8) & 0xF; }
+ uint16 M56_getAttackRange() { return (_ranges >> 12) & 0xF; }
}; // @ CREATURE_INFO
@@ -232,13 +242,13 @@ class Teleporter {
public:
explicit Teleporter(uint16 *rawDat) : _nextThing(rawDat[0]), _attributes(rawDat[1]), _destMapIndex(rawDat[2]) {}
Thing getNextThing() { return _nextThing; }
- bool makesSound() { return (_attributes >> 15) & 1; }
+ bool isAudible() { return (_attributes >> 15) & 1; }
TeleporterScope getScope() { return (TeleporterScope)((_attributes >> 13) & 1); }
- bool absRotation() { return (_attributes >> 12) & 1; }
- direction getRotationDir() { return (direction)((_attributes >> 10) & 1); }
- byte getDestY() { return (_attributes >> 5) & 0xF; }
- byte getDestX() { return _attributes & 0xF; }
- uint16 getDestMapIndex() { return _destMapIndex >> 8; }
+ bool getAbsoluteRotation() { return (_attributes >> 12) & 1; }
+ direction getRotation() { return (direction)((_attributes >> 10) & 1); }
+ byte getTargetMapY() { return (_attributes >> 5) & 0xF; }
+ byte getTargetMapX() { return _attributes & 0xF; }
+ uint16 getTargetMapIndex() { return _destMapIndex >> 8; }
}; // @ TELEPORTER
@@ -311,26 +321,30 @@ public:
uint16 getDataMask2() { return (_datAndType >> 11) & 0xF; } // @ M43_MASK2
void setData(int16 dat) { _datAndType = (_datAndType & 0x7F) | (dat << 7); } // @ M41_SET_DATA
void setTypeDisabled() { _datAndType &= 0xFF80; } // @ M44_SET_TYPE_DISABLED
- uint16 getOrnOrdinal() { return _attributes >> 12; }
- bool isLocalAction() { return (_attributes >> 11) & 1; }
- uint16 getDelay() { return (_attributes >> 7) & 0xF; }
- bool hasSound() { return (_attributes >> 6) & 1; }
- bool shouldRevert() { return (_attributes >> 5) & 1; }
- SensorActionType getActionType() { return (SensorActionType)((_attributes >> 3) & 3); }
- bool isSingleUse() { return (_attributes >> 2) & 1; }
- uint16 getRemoteMapY() { return (_action >> 11); }
- uint16 getRemoteMapX() { return (_action >> 6) & 0x1F; }
- direction getRemoteDir() { return (direction)((_action >> 4) & 3); }
- uint16 getLocalAction() { return (_action >> 4); }
+
+
+
+ bool getOnlyOnce() { return (_attributes >> 2) & 1; }
uint16 getEffectA() { return (_attributes >> 3) & 0x3; }
bool getRevertEffectA() { return (_attributes >> 5) & 0x1; }
bool getAudibleA() { return (_attributes >> 6) & 0x1; }
+ uint16 getValue() { return (_attributes >> 7) & 0xF; }
+ bool getLocalEffect() { return (_attributes >> 11) & 1; }
+ uint16 getOrnOrdinal() { return _attributes >> 12; }
+
+ uint16 getTargetMapY() { return (_action >> 11); }
+ uint16 getTargetMapX() { return (_action >> 6) & 0x1F; }
+ direction getTargetCell() { return (direction)((_action >> 4) & 3); }
+ uint16 M49_localEffect() { return (_action >> 4); }
// some macros missing, i got bored
}; // @ SENSOR
+#define k0x8000_randomDrop 0x8000 // @ MASK0x8000_RANDOM_DROP
+
+
enum WeaponType {
k2_WeaponTypeTorch = 2, // @ C02_WEAPON_TORCH
k8_WeaponTypeDagger = 8, // @ C08_WEAPON_DAGGER
@@ -351,6 +365,7 @@ public:
explicit Weapon(uint16 *rawDat) : _nextThing(rawDat[0]), _desc(rawDat[1]) {}
WeaponType getType() { return (WeaponType)(_desc & 0x7F); }
+ void setType(uint16 val) { _desc = (_desc & ~0x7F) | (val & 0x7F); }
bool isLit() { return (_desc >> 15) & 1; }
void setLit(bool val) {
if (val)
@@ -359,10 +374,13 @@ public:
_desc &= (~(1 << 15));
}
uint16 getChargeCount() { return (_desc >> 10) & 0xF; }
+ void setChargeCount(uint16 val) { _desc = (_desc & ~(0xF << 10)) | ((val & 0xF) << 10); }
Thing getNextThing() { return _nextThing; }
uint16 getCursed() { return (_desc >> 8) & 1; }
+ void setCursed(uint16 val) { _desc = (_desc & ~(1 << 8)) | ((val & 1) << 8); }
uint16 getPoisoned() { return (_desc >> 9) & 1; }
uint16 getBroken() { return (_desc >> 14) & 1; }
+ uint16 getDoNotDiscard() { return (_desc >> 7) & 1; }
}; // @ WEAPON
enum ArmourType {
@@ -382,6 +400,7 @@ public:
Thing getNextThing() { return _nextThing; }
uint16 getCursed() { return (_attributes >> 8) & 1; }
uint16 getBroken() { return (_attributes >> 13) & 1; }
+ uint16 getDoNotDiscard() { return (_attributes >> 7) & 1; }
}; // @ ARMOUR
class Scroll {
@@ -420,15 +439,16 @@ enum PotionType {
k20_PotionTypeEmptyFlask = 20 // @ C20_POTION_EMPTY_FLASK,
};
class Potion {
+public:
Thing _nextThing;
uint16 _attributes;
-public:
explicit Potion(uint16 *rawDat) : _nextThing(rawDat[0]), _attributes(rawDat[1]) {}
PotionType getType() { return (PotionType)((_attributes >> 8) & 0x7F); }
void setType(PotionType val) { _attributes = (_attributes & ~(0x7F << 8)) | ((val & 0x7F) << 8); }
Thing getNextThing() { return _nextThing; }
uint16 getPower() { return _attributes & 0xFF; }
+ uint16 getDoNotDiscard() { return (_attributes >> 15) & 1; }
}; // @ POTION
class Container {
@@ -463,23 +483,29 @@ public:
explicit Junk(uint16 *rawDat) : _nextThing(rawDat[0]), _attributes(rawDat[1]) {}
JunkType getType() { return (JunkType)(_attributes & 0x7F); }
+ void setType(uint16 val) { _attributes = (_attributes & ~0x7F) | (val & 0x7F); }
uint16 getChargeCount() { return (_attributes >> 14) & 0x3; }
void setChargeCount(uint16 val) { _attributes = (_attributes & ~(0x3 << 14)) | ((val & 0x3) << 14); }
+ uint16 getDoNotDiscard() { return (_attributes >> 7) & 1; }
Thing getNextThing() { return _nextThing; }
}; // @ JUNK
+#define kM1_soundModeDoNotPlaySound -1 // @ CM1_MODE_DO_NOT_PLAY_SOUND
+#define k0_soundModePlayImmediately 0 // @ C00_MODE_PLAY_IMMEDIATELY
+#define k1_soundModePlayIfPrioritized 1 // @ C01_MODE_PLAY_IF_PRIORITIZED
+#define k2_soundModePlayOneTickLater 2 // @ C02_MODE_PLAY_ONE_TICK_LATER
+
class Projectile {
public:
Thing _nextThing;
- Thing _object;
- byte _kineticEnergy;
- byte _damageEnergy;
- uint16 _timerIndex;
- explicit Projectile(uint16 *rawDat) : _nextThing(rawDat[0]), _object(rawDat[1]), _kineticEnergy(rawDat[2]),
- _damageEnergy(rawDat[3]), _timerIndex(rawDat[4]) {}
+ Thing _slot;
+ uint16 _kineticEnergy;
+ uint16 _attack;
+ uint16 _eventIndex;
+ explicit Projectile(uint16 *rawDat) : _nextThing(rawDat[0]), _slot(rawDat[1]), _kineticEnergy(rawDat[2]),
+ _attack(rawDat[3]), _eventIndex(rawDat[4]) {}
- Thing getNextThing() { return _nextThing; }
}; // @ PROJECTILE
#define k0_ExplosionType_Fireball 0 // @ C000_EXPLOSION_FIREBALL
@@ -502,8 +528,11 @@ public:
Thing getNextThing() { return _nextThing; }
uint16 getType() { return _attributes & 0x7F; }
+ uint16 setType(uint16 val) { _attributes = (_attributes & ~0x7F) | (val & 0x7F); return (val & 0x7F); }
uint16 getAttack() { return (_attributes >> 8) & 0xFF; }
- uint16 getCentered() { return (_attributes >> 7) & 0x1; }
+ void setAttack(uint16 val) { _attributes = (_attributes & ~(0xFF << 8)) | ((val & 0xFF) << 8); }
+ uint16 getCentered() { return (_attributes >> 7) & 0x1; }
+ void setCentered(uint16 val) { _attributes = (_attributes & ~(1 << 7)) | ((val & 1) << 7); }
}; // @ EXPLOSION
@@ -544,6 +573,9 @@ enum SquareType {
k19_StairsFrontElemType = 19 // @ C19_ELEMENT_STAIRS_FRONT
}; // @ C[-2..19]_ELEMENT_...
+#define k0x8000_championBones 0x8000 // @ MASK0x8000_CHAMPION_BONES
+#define k0x7FFF_thingType 0x7FFF // @ MASK0x7FFF_THING_TYPE
+
class Square {
byte _data;
public:
@@ -598,10 +630,11 @@ class DoorInfo {
public:
byte _attributes;
byte _defense;
- DoorInfo(byte b1, byte b2): _attributes(b1), _defense(b2){}
+ DoorInfo(byte b1, byte b2) : _attributes(b1), _defense(b2) {}
DoorInfo() {}
}; // @ DOOR_INFO
+class Group;
class DungeonMan {
DMEngine *_vm;
@@ -618,14 +651,14 @@ class DungeonMan {
int16 f170_getRandomOrnOrdinal(bool allowed, int16 count, int16 mapX, int16 mapY, int16 modulo); // @ F0170_DUNGEON_GetRandomOrnamentOrdinal
void f171_setSquareAspectOrnOrdinals(uint16 *aspectArray, bool leftAllowed, bool frontAllowed, bool rightAllowed, direction dir,
- int16 mapX, int16 mapY, bool isFakeWall); // @ F0171_DUNGEON_SetSquareAspectRandomWallOrnamentOrdinals
+ int16 mapX, int16 mapY, bool isFakeWall); // @ F0171_DUNGEON_SetSquareAspectRandomWallOrnamentOrdinals
- void f173_setCurrentMap(uint16 mapIndex); // @ F0173_DUNGEON_SetCurrentMap
public:
explicit DungeonMan(DMEngine *dmEngine);
~DungeonMan();
+ void f173_setCurrentMap(uint16 mapIndex); // @ F0173_DUNGEON_SetCurrentMap
Thing f161_getSquareFirstThing(int16 mapX, int16 mapY); // @ F0161_DUNGEON_GetSquareFirstThing
Thing f159_getNextThing(Thing thing); // @ F0159_DUNGEON_GetNextThing(THING P0280_T_Thing)
uint16 *f156_getThingData(Thing thing); // @ F0156_DUNGEON_GetThingData
@@ -642,6 +675,8 @@ public:
} // @ F0153_DUNGEON_GetRelativeSquareType
void f172_setSquareAspect(uint16 *aspectArray, direction dir, int16 mapX, int16 mapY); // @ F0172_DUNGEON_SetSquareAspect
void f168_decodeText(char *destString, Thing thing, TextType type); // F0168_DUNGEON_DecodeText
+ Thing f166_getUnusedThing(uint16 thingType); // @ F0166_DUNGEON_GetUnusedThing
+
uint16 f140_getObjectWeight(Thing thing); // @ F0140_DUNGEON_GetObjectWeight
int16 f141_getObjectInfoIndex(Thing thing); // @ F0141_DUNGEON_GetObjectInfoIndex
@@ -649,6 +684,17 @@ public:
WeaponInfo *f158_getWeaponInfo(Thing thing); // @ F0158_DUNGEON_GetWeaponInfo
int16 f142_getProjectileAspect(Thing thing); // @ F0142_DUNGEON_GetProjectileAspect
int16 f154_getLocationAfterLevelChange(int16 mapIndex, int16 levelDelta, int16 *mapX, int16 *mapY); // @ F0154_DUNGEON_GetLocationAfterLevelChange
+ Thing f162_getSquareFirstObject(int16 mapX, int16 mapY); // @ F0162_DUNGEON_GetSquareFirstObject
+ uint16 f143_getArmourDefense(ArmourInfo *armourInfo, bool useSharpDefense); // @ F0143_DUNGEON_GetArmourDefense
+ Thing f165_getDiscardTHing(uint16 thingType); // @ F0165_DUNGEON_GetDiscardedThing
+ uint16 f144_getCreatureAttributes(Thing thing); // @ F0144_DUNGEON_GetCreatureAttributes
+ void f146_setGroupCells(Group *group, uint16 cells, uint16 mapIndex); // @ F0146_DUNGEON_SetGroupCells
+ void f148_setGroupDirections(Group *group, int16 dir, uint16 mapIndex); // @ F0148_DUNGEON_SetGroupDirections
+ bool f139_isCreatureAllowedOnMap(Thing thing, uint16 mapIndex); // @ F0139_DUNGEON_IsCreatureAllowedOnMap
+ void f164_unlinkThingFromList(Thing thingToUnlink, Thing thingInList, int16 mapX, int16 mapY); // @ F0164_DUNGEON_UnlinkThingFromList
+ int16 f155_getStairsExitDirection(int16 mapX, int16 mapY); // @ F0155_DUNGEON_GetStairsExitDirection
+ Thing f167_getObjForProjectileLaucherOrObjGen(uint16 iconIndex); // @ F0167_DUNGEON_GetObjectForProjectileLauncherOrObjectGenerator
+
uint32 _rawDunFileDataSize; // @ probably NONE
byte *_rawDunFileData; // @ ???
diff --git a/engines/dm/eventman.cpp b/engines/dm/eventman.cpp
index 490449d61c..0104883e21 100644
--- a/engines/dm/eventman.cpp
+++ b/engines/dm/eventman.cpp
@@ -34,12 +34,53 @@
#include "objectman.h"
#include "inventory.h"
#include "menus.h"
-
-
+#include "timeline.h"
+#include "projexpl.h"
+#include "text.h"
+#include "group.h"
namespace DM {
+byte g42_bitmapArrowPointer[576] = { // @ G0042_auc_Graphic562_Bitmap_ArrowPointer
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x7, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xE, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x7, 0xF, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0xC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x7, 0xE, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xF, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x6, 0xC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x8, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x0, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x8, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x3, 0xC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xA, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xC, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xE, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0xC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0xE, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xF, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF, 0xC, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF, 0xC, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0xF, 0xE, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xE, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x7, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+byte g43_bitmapHanPointer[576] = { // @ G0043_auc_Graphic562_Bitmap_HandPointer
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0xA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x3, 0x5, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xA, 0xA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xD, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xE, 0xA, 0x8, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x7, 0xF, 0x8, 0x0, 0x0, 0x0, 0x0, 0xC, 0x7, 0xF, 0xC, 0x0, 0x0, 0x0, 0x0, 0x6, 0x7, 0xF, 0xC, 0x0, 0x0, 0x0, 0x0, 0x7, 0x7, 0xF, 0xC, 0x0, 0x0, 0x0, 0x0,
+ 0x3, 0xF, 0xF, 0xC, 0x0, 0x0, 0x0, 0x0, 0x3, 0xF, 0xF, 0xC, 0x0, 0x0, 0x0, 0x0, 0x1, 0xF, 0xF, 0xE, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7, 0xF, 0xF, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x1, 0xF, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0xA, 0xA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x5, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x1, 0x2, 0xA, 0x8, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x5, 0x4, 0x0, 0x0, 0x0, 0x0, 0xC, 0x8, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x2, 0x8, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0,
+ 0x9, 0x8, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x8, 0x8, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0,
+ 0x2, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xE, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x7, 0xF, 0xE, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0xF, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xF, 0xF, 0x8, 0x0, 0x0, 0x0, 0x0, 0x1, 0xF, 0xF, 0xC, 0x0, 0x0, 0x0, 0x0,
+ 0xC, 0xF, 0xF, 0xC, 0x0, 0x0, 0x0, 0x0, 0xE, 0xF, 0xF, 0xE, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF, 0xF, 0xE, 0x0, 0x0, 0x0, 0x0, 0xF, 0xF, 0xF, 0xE, 0x0, 0x0, 0x0, 0x0,
+ 0x7, 0xF, 0xF, 0xE, 0x0, 0x0, 0x0, 0x0, 0x7, 0xF, 0xF, 0xE, 0x0, 0x0, 0x0, 0x0, 0x3, 0xF, 0xF, 0xF, 0x0, 0x0, 0x0, 0x0, 0x1, 0xF, 0xF, 0xF, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x7, 0xF, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xF, 0xF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+
Box g462_BoxObjectPiles[4] = { // @ G0462_as_Graphic561_Box_ObjectPiles
/* { X1, X2, Y1, Y2 } */
Box(24, 111, 148, 168), /* Front left */
@@ -260,44 +301,179 @@ EventManager::EventManager(DMEngine *vm) : _vm(vm) {
_dummyMapIndex = 0;
_g439_pendingClickButton = k0_NoneMouseButton;
+ _g615_mousePointerOriginalColorsObject = nullptr;
+ _g613_mousePointerOriginalColorsChampionIcon = nullptr;
+ _gK190_mousePointerTempBuffer = nullptr;
}
+EventManager::~EventManager() {
+ delete[] _g615_mousePointerOriginalColorsObject;
+ delete[] _gK190_mousePointerTempBuffer;
+ delete[] _g613_mousePointerOriginalColorsChampionIcon;
+}
-// dummy data
-static const byte mouseData[] = {
- 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
- 1, 7, 1, 0, 0, 0, 0, 0, 0, 0,
- 1, 7, 7, 1, 0, 0, 0, 0, 0, 0,
- 1, 7, 7, 7, 1, 0, 0, 0, 0, 0,
- 1, 7, 7, 7, 7, 1, 0, 0, 0, 0,
- 1, 7, 7, 7, 7, 7, 1, 0, 0, 0,
- 1, 7, 7, 7, 7, 7, 7, 1, 0, 0,
- 1, 7, 7, 7, 7, 7, 7, 7, 1, 0,
- 1, 7, 7, 7, 7, 7, 1, 1, 1, 1,
- 1, 7, 7, 1, 7, 7, 1, 0, 0, 0,
- 1, 7, 1, 0, 1, 7, 7, 1, 0, 0,
- 1, 1, 0, 0, 1, 7, 7, 1, 0, 0,
- 0, 0, 0, 0, 0, 1, 7, 7, 1, 0,
- 0, 0, 0, 0, 0, 1, 7, 7, 1, 0,
- 0, 0, 0, 0, 0, 0, 1, 1, 0, 0
-};
-#define MOUSE_WIDTH 10
-#define MOUSE_HEIGHT 15
+void EventManager::initMouse() {
+ if (!_g615_mousePointerOriginalColorsObject)
+ _g615_mousePointerOriginalColorsObject = new byte[32 * 18];
+ if (!_gK190_mousePointerTempBuffer)
+ _gK190_mousePointerTempBuffer = new byte[32 * 18];
+ if (!_g613_mousePointerOriginalColorsChampionIcon)
+ _g613_mousePointerOriginalColorsChampionIcon = new byte[32 * 18];
+ _gK104_mousePointerType = k0_pointerArrow;
+ _gK105_previousMousePointerType = k1_pointerHand;
-void EventManager::initMouse() {
_mousePos = Common::Point(0, 0);
- CursorMan.pushCursor(mouseData, MOUSE_WIDTH, MOUSE_HEIGHT, 0, 0, 0);
+ CursorMan.pushCursor(g42_bitmapArrowPointer, 32, 18, 0, 0, 0);
CursorMan.showMouse(false);
+
setMousePos(Common::Point(320 / 2, 200 / 2));
- // TODO: add cursor creatin, set to hidden
}
-void EventManager::showMouse(bool visibility) {
- CursorMan.showMouse(visibility);
+void EventManager::f67_setMousePointerToNormal(int16 mousePointer) {
+ _gK100_preventBuildPointerScreenArea = true;
+ _g600_useObjectAsMousePointerBitmap = false;
+ _g601_useHandAsMousePointerBitmap = (mousePointer == k1_pointerHand);
+ _g598_mousePointerBitmapUpdated = true;
+ _gK100_preventBuildPointerScreenArea = false;
+ f73_buildpointerScreenArea(_mousePos.x, _mousePos.y);
+}
+
+void EventManager::f68_setPointerToObject(byte* bitmap) {
+ static byte gK27_palChangesMousepointerOjbectIconShadow[16] = {120, 120, 120, 120, 120, 120, 120, 120,
+ 120, 120, 120, 120, 0, 120, 120, 120}; // @ K0027_auc_PaletteChanges_MousePointerObjectIconShadow
+ static byte g44_palChangesMousePointerIcon[16] = {120, 10, 20, 30, 40, 50, 60, 70, 80, 90,
+ 100, 110, 0, 130, 140, 150}; // @ G0044_auc_Graphic562_PaletteChanges_MousePointerIcon
+ static Box g619_BoxMousePointerObjectShadow(2, 17, 2, 17); // @ G0619_s_Box_MousePointer_ObjectShadow
+ static Box g620_BoxMousePointerObject(0, 15, 0, 15); // @ G0620_s_Box_MousePointer_Object
+ byte* L0051_puc_Bitmap;
+
+ _gK100_preventBuildPointerScreenArea = true;
+ _g600_useObjectAsMousePointerBitmap = true;
+ _g601_useHandAsMousePointerBitmap = false;
+ _g598_mousePointerBitmapUpdated = true;
+ _vm->_displayMan->_g578_useByteBoxCoordinates = true;
+ L0051_puc_Bitmap = _g615_mousePointerOriginalColorsObject;
+ memset(L0051_puc_Bitmap, 0, 32 * 18);
+ _vm->_displayMan->f129_blitToBitmapShrinkWithPalChange(bitmap, _gK190_mousePointerTempBuffer, 16, 16, 16, 16, gK27_palChangesMousepointerOjbectIconShadow);
+ _vm->_displayMan->f132_blitToBitmap(_gK190_mousePointerTempBuffer, L0051_puc_Bitmap, g619_BoxMousePointerObjectShadow, 0, 0, 8, 16, k255_ColorNoTransparency, 16, 18);
+ _vm->_displayMan->f129_blitToBitmapShrinkWithPalChange(bitmap, _gK190_mousePointerTempBuffer, 16, 16, 16, 16, g44_palChangesMousePointerIcon);
+ _vm->_displayMan->f132_blitToBitmap(_gK190_mousePointerTempBuffer, L0051_puc_Bitmap, g620_BoxMousePointerObject, 0, 0, 8, 16, k0_ColorBlack, 16, 18);
+ _gK100_preventBuildPointerScreenArea = false;
+ f73_buildpointerScreenArea(_mousePos.x, _mousePos.y);
+}
+
+void EventManager::f71_mouseDropChampionIcon() {
+ bool L0057_B_UseByteBoxCoordinatesBackup;
+ uint16 L0058_ui_ChampionIconIndex;
+
+
+ _gK100_preventBuildPointerScreenArea = true;
+ L0058_ui_ChampionIconIndex = _vm->M1_ordinalToIndex(_vm->_eventMan->_g599_useChampionIconOrdinalAsMousePointerBitmap);
+ _vm->_eventMan->_g599_useChampionIconOrdinalAsMousePointerBitmap = _vm->M0_indexToOrdinal(kM1_ChampionNone);
+ _vm->_eventMan->_g598_mousePointerBitmapUpdated = true;
+ L0057_B_UseByteBoxCoordinatesBackup = _vm->_displayMan->_g578_useByteBoxCoordinates;
+ _vm->_displayMan->f21_blitToScreen(_g613_mousePointerOriginalColorsChampionIcon, &g54_BoxChampionIcons[L0058_ui_ChampionIconIndex << 2], 16, k12_ColorDarkestGray, 18);
+ _vm->_displayMan->_g578_useByteBoxCoordinates = L0057_B_UseByteBoxCoordinatesBackup;
+ _gK100_preventBuildPointerScreenArea = false;
+}
+
+void EventManager::f73_buildpointerScreenArea(int16 mousePosX, int16 mousePosY) {
+ uint16 L1577_i_ChampionIndex;
+ int16 L1578_i_XOverChampionStatusBox;
+
+
+ if (_gK100_preventBuildPointerScreenArea)
+ return;
+
+ _gK100_preventBuildPointerScreenArea = true;
+ if (_vm->_eventMan->_g599_useChampionIconOrdinalAsMousePointerBitmap) {
+ if ((mousePosY > 28) || (mousePosX < 274)) {
+ _gK104_mousePointerType = k4_pointerTypeAutoselect;
+ f71_mouseDropChampionIcon();
+ } else {
+ _gK104_mousePointerType = k2_pointerTypeChampionIcon;
+ }
+ } else {
+ if (mousePosY >= 169) {
+ _gK104_mousePointerType = k0_pointerTypeArrow;
+ } else {
+ if (mousePosX >= 274) {
+ _gK104_mousePointerType = k0_pointerTypeArrow;
+ } else {
+ if (mousePosY <= 28) {
+ L1577_i_ChampionIndex = mousePosX / 69;
+ L1578_i_XOverChampionStatusBox = mousePosX % 69;
+ if (L1577_i_ChampionIndex >= _vm->_championMan->_g305_partyChampionCount) {
+ _gK104_mousePointerType = k4_pointerTypeAutoselect;
+ } else {
+ if (L1578_i_XOverChampionStatusBox > 42) {
+ _gK104_mousePointerType = k4_pointerTypeAutoselect;
+ } else {
+ L1577_i_ChampionIndex++;
+ if (L1577_i_ChampionIndex == _vm->_inventoryMan->_g432_inventoryChampionOrdinal) {
+ _gK104_mousePointerType = k0_pointerTypeArrow;
+ } else {
+ if (mousePosY <= 6) {
+ _gK104_mousePointerType = k0_pointerTypeArrow;
+ } else {
+ _gK104_mousePointerType = k4_pointerTypeAutoselect;
+ }
+ }
+ }
+ }
+ } else {
+ if (mousePosX >= 224) {
+ _gK104_mousePointerType = k0_pointerTypeArrow;
+ } else {
+ _gK104_mousePointerType = k4_pointerTypeAutoselect;
+ }
+ }
+ }
+ }
+ }
+ if (_gK104_mousePointerType == k4_pointerTypeAutoselect) {
+ _gK104_mousePointerType = (_g600_useObjectAsMousePointerBitmap) ? k1_pointerTypeObjectIcon : (_g601_useHandAsMousePointerBitmap) ? k3_pointerTypeHand : k0_pointerTypeArrow;
+ }
+ if (_vm->_eventMan->_g598_mousePointerBitmapUpdated || (_gK104_mousePointerType != _gK105_previousMousePointerType)) {
+ _vm->_eventMan->_g598_mousePointerBitmapUpdated = false;
+ switch (_gK104_mousePointerType) {
+ case k0_pointerTypeArrow:
+ CursorMan.pushCursor(g42_bitmapArrowPointer, 32, 18, 0, 0, 0);
+ break;
+ case k1_pointerTypeObjectIcon:
+ CursorMan.pushCursor(_g615_mousePointerOriginalColorsObject, 32, 18, 0, 0, 0);
+ break;
+ case k2_pointerTypeChampionIcon:
+ CursorMan.pushCursor(_g613_mousePointerOriginalColorsChampionIcon, 32, 18, 0, 0, 0);
+ break;
+ case k3_pointerTypeHand:
+ CursorMan.pushCursor(g43_bitmapHanPointer, 32, 18, 0, 0, 0);
+ break;
+ }
+ }
+ _gK105_previousMousePointerType = _gK104_mousePointerType;
+ _gK100_preventBuildPointerScreenArea = false;
+}
+
+void EventManager::f69_setMousePointer() {
+ if (_vm->_championMan->_g415_leaderEmptyHanded) {
+ f67_setMousePointerToNormal((_vm->_championMan->_g411_leaderIndex == kM1_ChampionNone) ? k0_pointerArrow : k1_pointerHand);
+ } else {
+ f68_setPointerToObject(_vm->_objectMan->_g412_objectIconForMousePointer);
+ }
+}
+
+void EventManager::f78_showMouse() {
+ CursorMan.showMouse(true);
+}
+
+void EventManager::f77_hideMouse() {
+ CursorMan.showMouse(false);
}
+
void EventManager::setMousePos(Common::Point pos) {
_vm->_system->warpMouse(pos.x, pos.y);
}
@@ -473,6 +649,48 @@ void EventManager::f366_commandMoveParty(CommandType cmdType) {
// MISSING CODE: Lots of code
}
+bool EventManager::f375_processType80_clickDungeonView_isLeaderHandObjThrown(int16 posX, int16 posY) {
+ bool L1149_B_ObjectThrown;
+
+
+#define k0_sideLeft 0 // @ C0_SIDE_LEFT
+#define k1_sideRight 1 // @ C0_SIDE_LEFT
+
+ if ((posY < 47) || (posY > 102)) {
+ return false;
+ }
+ if (posX <= 111) {
+ if (_vm->_dungeonMan->_g285_squareAheadElement == k17_DoorFrontElemType) {
+ if (posX < 64) {
+ return false;
+ }
+ } else {
+ if (posX < 32) {
+ return false;
+ }
+ }
+ // Strangerke: Only poresent in CSB2.1... But it fixes a bug so we keep it
+ L1149_B_ObjectThrown = _vm->_championMan->f329_isLeaderHandObjectThrown(k0_sideLeft);
+ } else {
+ if (_vm->_dungeonMan->_g285_squareAheadElement == k17_DoorFrontElemType) {
+ if (posX > 163) {
+ return false;
+ }
+ } else {
+ if (posX > 191) {
+ return false;
+ }
+ }
+ L1149_B_ObjectThrown = _vm->_championMan->f329_isLeaderHandObjectThrown(k1_sideRight);
+ }
+ if (L1149_B_ObjectThrown) {
+ _vm->_g321_stopWaitingForPlayerInput = true;
+ }
+ return L1149_B_ObjectThrown;
+}
+
+
+
void EventManager::f368_commandSetLeader(ChampionIndex champIndex) {
ChampionMan &cm = *_vm->_championMan;
ChampionIndex leaderIndex;
@@ -513,79 +731,88 @@ void EventManager::f372_commandProcessType80ClickInDungeonViewTouchFrontWall() {
}
void EventManager::f377_commandProcessType80ClickInDungeonView(int16 posX, int16 posY) {
- DungeonMan &dunMan = *_vm->_dungeonMan;
- ChampionMan &champMan = *_vm->_championMan;
-
- if (dunMan._g285_squareAheadElement == k17_ElementTypeDoorFront) {
- if (champMan._g411_leaderIndex == kM1_ChampionNone)
+ uint16 L1150_ui_ViewCell;
+ Junk* L1151_ps_Junk;
+ Thing L1152_T_Thing;
+ uint16 L1153_ui_IconIndex;
+ uint16 L1154_ui_Weight;
+ int16 L1155_i_MapX;
+ int16 L1156_i_MapY;
+
+ if (_vm->_dungeonMan->_g285_squareAheadElement == k17_DoorFrontElemType) {
+ if (_vm->_championMan->_g411_leaderIndex == kM1_ChampionNone) {
return;
-
- if (champMan._g415_leaderEmptyHanded) {
- int16 mapX = dunMan._g306_partyMapX + _vm->_dirIntoStepCountEast[dunMan._g308_partyDir];
- int16 mapY = dunMan._g307_partyMapY + _vm->_dirIntoStepCountNorth[dunMan._g308_partyDir];
-
- if (Door(dunMan.f157_getSquareFirstThingData(mapX, mapY)).hasButton() &&
- dunMan._g291_dungeonViewClickableBoxes[k5_ViewCellDoorButtonOrWallOrn].isPointInside(Common::Point(posX, posY - 33))) {
+ }
+ L1155_i_MapX = _vm->_dungeonMan->_g306_partyMapX;
+ L1156_i_MapY = _vm->_dungeonMan->_g307_partyMapY;
+ L1155_i_MapX += _vm->_dirIntoStepCountEast[_vm->_dungeonMan->_g308_partyDir], L1156_i_MapY += _vm->_dirIntoStepCountNorth[_vm->_dungeonMan->_g308_partyDir];
+ if (_vm->_championMan->_g415_leaderEmptyHanded) {
+ L1151_ps_Junk = (Junk*)_vm->_dungeonMan->f157_getSquareFirstThingData(L1155_i_MapX, L1156_i_MapY);
+ if ((((Door*)L1151_ps_Junk)->hasButton()) && _vm->_dungeonMan->_g291_dungeonViewClickableBoxes[k5_ViewCellDoorButtonOrWallOrn].isPointInside(posX, posY - 33)) {
_vm->_g321_stopWaitingForPlayerInput = true;
warning("MISSING CODE: F0064_SOUND_RequestPlay_CPSD");
- warning("MISSING CODE: F0268_SENSOR_AddEvent");
+ _vm->_movsens->f268_addEvent(k10_TMEventTypeDoor, L1155_i_MapX, L1156_i_MapY, 0, k2_SensorEffToggle, _vm->_g313_gameTime + 1);
+ return;
+ }
+ } else {
+ if (f375_processType80_clickDungeonView_isLeaderHandObjThrown(posX, posY)) {
return;
}
-
- warning("MISSING CODE: F0375_COMMAND_ProcessType80_ClickInDungeonView_IsLeaderHandObjectThrown in elseif condition");
}
}
-
- if (champMan._g415_leaderEmptyHanded) {
- for (int16 viewCell = k0_ViewCellFronLeft; viewCell <= k5_ViewCellDoorButtonOrWallOrn; viewCell++) {
- if (dunMan._g291_dungeonViewClickableBoxes[viewCell].isPointInside(Common::Point(posX, posY - 33))) {
- if (viewCell == k5_ViewCellDoorButtonOrWallOrn) {
- if (!dunMan._g286_isFacingAlcove) {
+ if (_vm->_championMan->_g415_leaderEmptyHanded) {
+ for (L1150_ui_ViewCell = k0_ViewCellFronLeft; L1150_ui_ViewCell < k5_ViewCellDoorButtonOrWallOrn + 1; L1150_ui_ViewCell++) {
+ if (_vm->_dungeonMan->_g291_dungeonViewClickableBoxes[L1150_ui_ViewCell].isPointInside(posX, posY - 33)) {
+ if (L1150_ui_ViewCell == k5_ViewCellDoorButtonOrWallOrn) {
+ if (!_vm->_dungeonMan->_g286_isFacingAlcove) {
f372_commandProcessType80ClickInDungeonViewTouchFrontWall();
}
} else {
- warning("MISSING CODE: F0373_COMMAND_ProcessType80_ClickInDungeonView_GrabLeaderHandObject");
+ f373_processType80_clickInDungeonView_grabLeaderHandObject(L1150_ui_ViewCell);
}
return;
}
}
} else {
- Thing thing = champMan._g414_leaderHandObject;
- uint16 *rawThingPointer = dunMan.f156_getThingData(thing);
- if (dunMan._g285_squareAheadElement == k0_ElementTypeWall) {
- for (int16 viewCell = k0_ViewCellFronLeft; viewCell <= k1_ViewCellFrontRight; ++viewCell) {
- if (g462_BoxObjectPiles[viewCell].isPointInside(Common::Point(posX, posY))) {
- warning("F0374_COMMAND_ProcessType80_ClickInDungeonView_DropLeaderHandObject");
+ L1152_T_Thing = _vm->_championMan->_g414_leaderHandObject;
+ L1151_ps_Junk = (Junk*)_vm->_dungeonMan->f156_getThingData(L1152_T_Thing);
+ if (_vm->_dungeonMan->_g285_squareAheadElement == k0_ElementTypeWall) {
+ for (L1150_ui_ViewCell = k0_ViewCellFronLeft; L1150_ui_ViewCell < k1_ViewCellFrontRight + 1; L1150_ui_ViewCell++) {
+ if (g462_BoxObjectPiles[L1150_ui_ViewCell].isPointInside(posX, posY)) {
+ f374_processType80_clickInDungeonViewDropLeaderHandObject(L1150_ui_ViewCell);
return;
}
}
-
- if (dunMan._g291_dungeonViewClickableBoxes[k5_ViewCellDoorButtonOrWallOrn].isPointInside(Common::Point(posX, posY - 33))) {
- if (dunMan._g286_isFacingAlcove) {
- warning("MISSING CODE: F0374_COMMAND_ProcessType80_ClickInDungeonView_DropLeaderHandObject");
+ if (_vm->_dungeonMan->_g291_dungeonViewClickableBoxes[k5_ViewCellDoorButtonOrWallOrn].isPointInside(posX, posY - 33)) {
+ if (_vm->_dungeonMan->_g286_isFacingAlcove) {
+ f374_processType80_clickInDungeonViewDropLeaderHandObject(k4_ViewCellAlcove);
} else {
- if (dunMan._g288_isFacingFountain) {
- uint16 iconIndex = _vm->_objectMan->f33_getIconIndex(thing);
- int16 weight = dunMan.f140_getObjectWeight(thing);
- if ((iconIndex >= k8_IconIndiceJunkWater) && (iconIndex <= k9_IconIndiceJunkWaterSkin)) {
- ((Junk*)rawThingPointer)->setChargeCount(3);
- } else if (iconIndex == k195_IconIndicePotionEmptyFlask) {
- ((Potion*)rawThingPointer)->setType(k15_PotionTypeWaterFlask);
+ if (_vm->_dungeonMan->_g288_isFacingFountain) {
+ L1153_ui_IconIndex = _vm->_objectMan->f33_getIconIndex(L1152_T_Thing);
+ L1154_ui_Weight = _vm->_dungeonMan->f140_getObjectWeight(L1152_T_Thing);
+ if ((L1153_ui_IconIndex >= k8_IconIndiceJunkWater) && (L1153_ui_IconIndex <= k9_IconIndiceJunkWaterSkin)) {
+ L1151_ps_Junk->setChargeCount(3); /* Full */
} else {
- goto T0377019;
+ if (L1153_ui_IconIndex == k195_IconIndicePotionEmptyFlask) {
+ ((Potion*)L1151_ps_Junk)->setType(k15_PotionTypeWaterFlask);
+ } else {
+ goto T0377019;
+ }
}
- champMan.f296_drawChangedObjectIcons();
- champMan._gK71_champions[champMan._g411_leaderIndex]._load += dunMan.f140_getObjectWeight(thing) - weight;
+ _vm->_championMan->f296_drawChangedObjectIcons();
+ _vm->_championMan->_gK71_champions[_vm->_championMan->_g411_leaderIndex]._load += _vm->_dungeonMan->f140_getObjectWeight(L1152_T_Thing) - L1154_ui_Weight;
}
T0377019:
f372_commandProcessType80ClickInDungeonViewTouchFrontWall();
}
}
} else {
- warning("MISSING CODE: F0375_COMMAND_ProcessType80_ClickInDungeonView_IsLeaderHandObjectThrown in if branch");
- for (int16 viewCell = k0_ViewCellFronLeft; viewCell <= k3_ViewCellBackLeft; viewCell++) {
- if (g462_BoxObjectPiles[viewCell].isPointInside(Common::Point(posX, posY))) {
- warning("MISSING CODE: F0374_COMMAND_ProcessType80_ClickInDungeonView_DropLeaderHandObject");
+ if (f375_processType80_clickDungeonView_isLeaderHandObjThrown(posX, posY)) {
+ return;
+ }
+ for (L1150_ui_ViewCell = k0_ViewCellFronLeft; L1150_ui_ViewCell < k3_ViewCellBackLeft + 1; L1150_ui_ViewCell++) {
+ if (g462_BoxObjectPiles[L1150_ui_ViewCell].isPointInside(posX, posY)) {
+ f374_processType80_clickInDungeonViewDropLeaderHandObject(L1150_ui_ViewCell);
return;
}
}
@@ -616,8 +843,8 @@ void EventManager::f282_commandProcessCommands160To162ClickInResurrectReincarnat
dispMan._g578_useByteBoxCoordinates = false;
dispMan.D24_fillScreenBox(box, k0_ColorBlack);
dispMan.D24_fillScreenBox(g54_BoxChampionIcons[champMan.M26_championIconIndex(champ->_cell, dunMan._g308_partyDir) * 2], k0_ColorBlack);
- warning("F0457_START_DrawEnabledMenus_CPSF");
- warning("F0078_MOUSE_ShowPointer");
+ _vm->_menuMan->f457_drawEnabledMenus();
+ _vm->_eventMan->f78_showMouse();
return;
}
@@ -628,7 +855,7 @@ void EventManager::f282_commandProcessCommands160To162ClickInResurrectReincarnat
for (uint16 slotIndex = k0_ChampionSlotReadyHand; slotIndex < k30_ChampionSlotChest_1; slotIndex++) {
Thing thing = champ->getSlot((ChampionSlot)slotIndex);
if (thing != Thing::_none) {
- warning("MISSING CODE: F0164_DUNGEON_UnlinkThingFromList");
+ _vm->_dungeonMan->f164_unlinkThingFromList(thing, Thing(0), mapX, mapY);
}
}
Thing thing = dunMan.f161_getSquareFirstThing(mapX, mapY);
@@ -652,21 +879,21 @@ void EventManager::f282_commandProcessCommands160To162ClickInResurrectReincarnat
}
if (champMan._g305_partyChampionCount == 1) {
- warning("MISSING CODE: setting time, G0362_l_LastPartyMovementTime , G0313_ul_GameTime");
+ _vm->_projexpl->_g362_lastPartyMovementTime = _vm->_g313_gameTime;
f368_commandSetLeader(k0_ChampionFirst);
_vm->_menuMan->f394_setMagicCasterAndDrawSpellArea(k0_ChampionFirst);
} else {
_vm->_menuMan->f393_drawSpellAreaControls(champMan._g514_magicCasterChampionIndex);
}
- warning("MISSING CODE: F0051_TEXT_MESSAGEAREA_PrintLineFeed");
- Color champColor = g46_ChampionColor[championIndex]; // unreferenced because of missing code
- warning("MISSING CODE: F0047_TEXT_MESSAGEAREA_PrintMessage");
- warning("MISSING CODE: F0047_TEXT_MESSAGEAREA_PrintMessage");
+ _vm->_textMan->f51_messageAreaPrintLineFeed();
+ Color champColor = g46_ChampionColor[championIndex];
+ _vm->_textMan->f47_messageAreaPrintMessage(champColor, champ->_name);
+ _vm->_textMan->f47_messageAreaPrintMessage(champColor, (commandType == k160_CommandClickInPanelResurrect) ? " RESURRECTED." : " REINCARNATED."); // TODO: localization
invMan.f355_toggleInventory(k4_ChampionCloseInventory);
- warning("MISSING CODE: F0457_START_DrawEnabledMenus_CPSF");
- warning("MISSING CODE: F0067_MOUSE_SetPointerToNormal");
+ _vm->_menuMan->f457_drawEnabledMenus();
+ _vm->_eventMan->f67_setMousePointerToNormal((_vm->_championMan->_g411_leaderIndex == kM1_ChampionNone) ? k0_pointerArrow : k1_pointerHand);
}
void EventManager::f378_commandProcess81ClickInPanel(int16 x, int16 y) {
@@ -694,6 +921,72 @@ void EventManager::f378_commandProcess81ClickInPanel(int16 x, int16 y) {
}
}
+void EventManager::f373_processType80_clickInDungeonView_grabLeaderHandObject(uint16 viewCell) {
+ int16 L1137_i_MapX;
+ int16 L1138_i_MapY;
+ Thing L1139_T_Thing;
+
+
+ if (_vm->_championMan->_g411_leaderIndex == kM1_ChampionNone) {
+ return;
+ }
+ L1137_i_MapX = _vm->_dungeonMan->_g306_partyMapX;
+ L1138_i_MapY = _vm->_dungeonMan->_g307_partyMapY;
+ if (viewCell >= k2_ViewCellBackRight) {
+ L1137_i_MapX += _vm->_dirIntoStepCountEast[_vm->_dungeonMan->_g308_partyDir], L1138_i_MapY += _vm->_dirIntoStepCountNorth[_vm->_dungeonMan->_g308_partyDir];
+ if (((L1139_T_Thing = _vm->_groupMan->f175_groupGetThing(L1137_i_MapX, L1138_i_MapY)) != Thing::_endOfList) &&
+ !_vm->_movsens->f264_isLevitating(L1139_T_Thing) &&
+ _vm->_groupMan->f176_getCreatureOrdinalInCell((Group*)_vm->_dungeonMan->f156_getThingData(L1139_T_Thing), M21_normalizeModulo4(viewCell + _vm->_dungeonMan->_g308_partyDir))) {
+ return; /* It is not possible to grab an object on floor if there is a non levitating creature on its cell */
+ }
+ }
+ L1139_T_Thing = _vm->_dungeonMan->_g292_pileTopObject[viewCell];
+ if (_vm->_objectMan->f33_getIconIndex(L1139_T_Thing) != kM1_IconIndiceNone) {
+ _vm->_movsens->f267_getMoveResult(L1139_T_Thing, L1137_i_MapX, L1138_i_MapY, kM1_MapXNotOnASquare, 0);
+ _vm->_championMan->f297_putObjectInLeaderHand(L1139_T_Thing, true);
+ }
+ _vm->_g321_stopWaitingForPlayerInput = true;
+}
+
+void EventManager::f374_processType80_clickInDungeonViewDropLeaderHandObject(uint16 viewCell) {
+ int16 L1140_i_MapX;
+ int16 L1141_i_MapY;
+ Thing L1142_T_Thing;
+ Junk* L1143_ps_Junk;
+ int16 L1144_i_IconIndex;
+ uint16 L1145_ui_Cell;
+ bool L1146_B_DroppingIntoAnAlcove;
+ TimelineEvent L1147_s_Event;
+
+
+ if (_vm->_championMan->_g411_leaderIndex == kM1_ChampionNone) {
+ return;
+ }
+ L1140_i_MapX = _vm->_dungeonMan->_g306_partyMapX;
+ L1141_i_MapY = _vm->_dungeonMan->_g307_partyMapY;
+ if (L1146_B_DroppingIntoAnAlcove = (viewCell == k4_ViewCellAlcove)) {
+ viewCell = k2_ViewCellBackRight;
+ }
+ if (viewCell > k1_ViewCellFrontRight) {
+ L1140_i_MapX += _vm->_dirIntoStepCountEast[_vm->_dungeonMan->_g308_partyDir], L1141_i_MapY += _vm->_dirIntoStepCountNorth[_vm->_dungeonMan->_g308_partyDir];
+ }
+ L1145_ui_Cell = M21_normalizeModulo4(_vm->_dungeonMan->_g308_partyDir + viewCell);
+ L1142_T_Thing = _vm->_championMan->f298_getObjectRemovedFromLeaderHand();
+ _vm->_movsens->f267_getMoveResult(M15_thingWithNewCell(L1142_T_Thing, L1145_ui_Cell), kM1_MapXNotOnASquare, 0, L1140_i_MapX, L1141_i_MapY);
+ if (L1146_B_DroppingIntoAnAlcove && _vm->_dungeonMan->_g287_isFacingViAltar && ((L1144_i_IconIndex = _vm->_objectMan->f33_getIconIndex(L1142_T_Thing)) == k147_IconIndiceJunkChampionBones)) {
+ L1143_ps_Junk = (Junk*)_vm->_dungeonMan->f156_getThingData(L1142_T_Thing);
+ M33_setMapAndTime(L1147_s_Event._mapTime, _vm->_dungeonMan->_g309_partyMapIndex, _vm->_g313_gameTime + 1);
+ L1147_s_Event._type = k13_TMEventTypeViAltarRebirth;
+ L1147_s_Event._priority = L1143_ps_Junk->getChargeCount();
+ L1147_s_Event._B._location._mapX = L1140_i_MapX;
+ L1147_s_Event._B._location._mapY = L1141_i_MapY;
+ L1147_s_Event._C.A._cell = L1145_ui_Cell;
+ L1147_s_Event._C.A._effect = k2_SensorEffToggle;
+ _vm->_timeline->f238_addEventGetEventIndex(&L1147_s_Event);
+ }
+ _vm->_g321_stopWaitingForPlayerInput = true;
+}
+
bool EventManager::f360_hasPendingClick(Common::Point& point, MouseButton button) {
if (_g439_pendingClickButton && button == _g439_pendingClickButton)
point = _g437_pendingClickPos;
@@ -701,4 +994,15 @@ bool EventManager::f360_hasPendingClick(Common::Point& point, MouseButton button
return _g436_pendingClickPresent;
}
+void EventManager::f379_drawSleepScreen() {
+ _vm->_displayMan->f134_fillBitmap(_vm->_displayMan->_g296_bitmapViewport, k0_ColorBlack, 224, 136); // TODO: localization
+}
+
+void EventManager::f357_discardAllInput() {
+ Common::Event event;
+ while (_vm->_system->getEventManager()->pollEvent(event))
+ ;
+ _commandQueue.clear();
+}
+
} // end of namespace DM
diff --git a/engines/dm/eventman.h b/engines/dm/eventman.h
index c156eedd94..d1ded67f35 100644
--- a/engines/dm/eventman.h
+++ b/engines/dm/eventman.h
@@ -211,6 +211,16 @@ public:
class DMEngine;
+#define k0_pointerArrow 0 // @ C0_POINTER_ARROW
+#define k1_pointerHand 1 // @ C1_POINTER_HAND
+
+#define k0_pointerTypeArrow 0 // @ C0_POINTER_TYPE_ARROW
+#define k1_pointerTypeObjectIcon 1 // @ C1_POINTER_TYPE_OBJECT_ICON
+#define k2_pointerTypeChampionIcon 2 // @ C2_POINTER_TYPE_CHAMPION_ICON
+#define k3_pointerTypeHand 3 // @ C3_POINTER_TYPE_HAND
+#define k4_pointerTypeAutoselect 4 // @ C4_POINTER_TYPE_AUTOSELECT
+
+
class EventManager {
DMEngine *_vm;
@@ -220,6 +230,14 @@ class EventManager {
bool _g436_pendingClickPresent; // G0436_B_PendingClickPresent
Common::Point _g437_pendingClickPos; // @ G0437_i_PendingClickX, G0438_i_PendingClickY
MouseButton _g439_pendingClickButton; // @ G0439_i_PendingClickButtonsStatus
+ bool _g600_useObjectAsMousePointerBitmap; // @ G0600_B_UseObjectAsMousePointerBitmap
+ bool _g601_useHandAsMousePointerBitmap; // @ G0601_B_UseHandAsMousePointerBitmap
+ bool _gK100_preventBuildPointerScreenArea; // @ K0100_B_PreventBuildPointerScreenArea
+ byte *_g615_mousePointerOriginalColorsObject; // @ G0615_puc_Bitmap_MousePointerOriginalColorsObject
+ byte *_g613_mousePointerOriginalColorsChampionIcon; // @ G0613_puc_Bitmap_MousePointerOriginalColorsChampionIcon
+ byte *_gK190_mousePointerTempBuffer; // @ K0190_puc_Bitmap_MousePointerTemporaryBuffer
+ int16 _gK104_mousePointerType; // @ K0104_i_MousePointerType
+ int16 _gK105_previousMousePointerType; // @ K0105_i_PreviousMousePointerType
// this doesn't seem to be used anywhere at all
bool _g435_isCommandQueueLocked; // @ G0435_B_CommandQueueLocked
@@ -227,8 +245,12 @@ class EventManager {
void f365_commandTurnParty(CommandType cmdType); // @ F0365_COMMAND_ProcessTypes1To2_TurnParty
void f366_commandMoveParty(CommandType cmdType); // @ F0366_COMMAND_ProcessTypes3To6_MoveParty
+ bool f375_processType80_clickDungeonView_isLeaderHandObjThrown(int16 posX, int16 posY); // @ F0375_COMMAND_ProcessType80_ClickInDungeonView_IsLeaderHandObjectThrown
+
+
public:
explicit EventManager(DMEngine *vm);
+ ~EventManager();
MouseInput* _g441_primaryMouseInput;// @ G0441_ps_PrimaryMouseInput
MouseInput* _g442_secondaryMouseInput;// @ G0442_ps_SecondaryMouseInput
@@ -238,7 +260,13 @@ public:
uint16 _g599_useChampionIconOrdinalAsMousePointerBitmap; // @ G0599_ui_UseChampionIconOrdinalAsMousePointerBitmap
void initMouse();
- void showMouse(bool visibility);
+ void f67_setMousePointerToNormal(int16 mousePointer); // @ F0067_MOUSE_SetPointerToNormal
+ void f68_setPointerToObject(byte *bitmap); // @ F0068_MOUSE_SetPointerToObject
+ void f71_mouseDropChampionIcon(); // @ F0071_MOUSE_DropChampionIcon
+ void f73_buildpointerScreenArea(int16 mousePosX, int16 mousePosY); // @ F0073_MOUSE_BuildPointerScreenArea
+ void f69_setMousePointer(); // @ F0069_MOUSE_SetPointer
+ void f78_showMouse(); // @ F0077_MOUSE_HidePointer_CPSE
+ void f77_hideMouse(); // @ F0078_MOUSE_ShowPointer
void setMousePos(Common::Point pos);
void processInput(); // acknowledges mouse and keyboard input
@@ -252,8 +280,12 @@ public:
void f377_commandProcessType80ClickInDungeonView(int16 posX, int16 posY); // @ F0377_COMMAND_ProcessType80_ClickInDungeonView
void f282_commandProcessCommands160To162ClickInResurrectReincarnatePanel(CommandType commandType); // @ F0282_CHAMPION_ProcessCommands160To162_ClickInResurrectReincarnatePanel
void f378_commandProcess81ClickInPanel(int16 x, int16 y); // @ F0378_COMMAND_ProcessType81_ClickInPanel
+ void f373_processType80_clickInDungeonView_grabLeaderHandObject(uint16 viewCell); // @ F0373_COMMAND_ProcessType80_ClickInDungeonView_GrabLeaderHandObject
+ void f374_processType80_clickInDungeonViewDropLeaderHandObject(uint16 viewCell); // @ F0374_COMMAND_ProcessType80_ClickInDungeonView_DropLeaderHandObject
bool f360_hasPendingClick(Common::Point &point, MouseButton button); // @ F0360_COMMAND_ProcessPendingClick
+ void f379_drawSleepScreen(); // @ F0379_COMMAND_DrawSleepScreen
+ void f357_discardAllInput(); // @ F0357_COMMAND_DiscardAllInput
};
}
diff --git a/engines/dm/gfx.cpp b/engines/dm/gfx.cpp
index 460e7b54a9..05c8f172cd 100644
--- a/engines/dm/gfx.cpp
+++ b/engines/dm/gfx.cpp
@@ -745,6 +745,7 @@ DisplayMan::DisplayMan(DMEngine *dmEngine) : _vm(dmEngine) {
_screenWidth = _screenHeight = 0;
_g289_championPortraitOrdinal = 0;
_g266_currMapViAltarIndex = 0;
+ _g297_drawFloorAndCeilingRequested = true;
for (int i = 0; i < 4; i++)
@@ -1534,6 +1535,16 @@ void DisplayMan::f112_drawCeilingPit(int16 nativeBitmapIndex, Frame* frame, int1
}
}
+void DisplayMan::f21_blitToScreen(byte *bitmap, int16* box, int16 byteWidth, Color transparent, int16 height) {
+ Box actualBox(box[0], box[1], box[2], box[3]);
+ f21_blitToScreen(bitmap, &actualBox, byteWidth, transparent, height);
+}
+
+void DisplayMan::f21_blitToScreen(byte* bitmap, Box* box, int16 viewDoorOrnIndex, Color transparent, int16 doorOrnOrdinal) {
+ _g578_useByteBoxCoordinates = false;
+ f132_blitToBitmap(bitmap, _g348_bitmapScreen, *box, 0, 0, byteWidth, k112_byteWidthViewport, transparent, height, k136_heightViewport);
+}
+
void DisplayMan::f101_drawWallSetBitmapWithoutTransparency(byte *bitmap, Frame &f) {
if (f._srcByteWidth)
f132_blitToBitmap(bitmap, _g296_bitmapViewport, f._box, f._srcX, f._srcY, f._srcByteWidth, k112_byteWidthViewport, k255_ColorNoTransparency);
@@ -1597,7 +1608,6 @@ T0116017_orangeElk:
}
}
-// NOTE: has been screened for missing code
void DisplayMan::f117_drawSquareD3R(direction dir, int16 posX, int16 posY) {
int16 order;
uint16 squareAspect[5];
@@ -2010,7 +2020,7 @@ void DisplayMan::f124_drawSquareD1C(direction dir, int16 posX, int16 posY) {
}
if (_vm->_championMan->_g407_party._event73Count_ThievesEye) {
f132_blitToBitmap(f492_getDerivedBitmap(k1_DerivedBitmapThievesEyeVisibleArea),
- _g296_bitmapViewport, g106_BoxThievesEye_ViewPortVisibleArea, 0, 0,
+ _g296_bitmapViewport, g106_BoxThievesEye_ViewPortVisibleArea, 0, 0,
48, k112_byteWidthViewport, k9_ColorGold, 95, k136_heightViewport); /* BUG0_74 */
f493_addDerivedBitmap(k1_DerivedBitmapThievesEyeVisibleArea);
warning("MISSING CODE: F0480_CACHE_ReleaseBlock");
@@ -2036,7 +2046,7 @@ void DisplayMan::f124_drawSquareD1C(direction dir, int16 posX, int16 posY) {
T0124017:
order = k0x3421_CellOrder_BackLeft_BackRight_FrontLeft_FrontRight;
/* BUG0_64 Floor ornaments are drawn over open pits. There is no check to prevent drawing floor ornaments over open pits */
- f108_drawFloorOrnament(squareAspect[k4_FloorOrnOrdAspect], k7_viewFloor_D1C);
+ f108_drawFloorOrnament(squareAspect[k4_FloorOrnOrdAspect], k7_viewFloor_D1C);
f112_drawCeilingPit(k66_ceilingPitD1C_GraphicIndice, &g156_FrameFloorPit_D1C, posX, posY, false);
T0124018:
f115_cthulhu(Thing(squareAspect[k1_FirstGroupOrObjectAspect]), dir, posX, posY, k6_ViewSquare_D1C, order);
@@ -2094,13 +2104,13 @@ void DisplayMan::f127_drawSquareD0C(direction dir, int16 posX, int16 posY) {
int16 squareAspect[5];
- _vm->_dungeonMan->f172_setSquareAspect((uint16*) squareAspect, dir, posX, posY);
+ _vm->_dungeonMan->f172_setSquareAspect((uint16*)squareAspect, dir, posX, posY);
switch (squareAspect[k0_ElemAspect]) {
case k16_DoorSideElemType:
if (_vm->_championMan->_g407_party._event73Count_ThievesEye) {
memmove(_g74_tmpBitmap, _g709_bitmapWallSet_DoorFrameFront, 32 * 123);
f132_blitToBitmap(f489_getNativeBitmapOrGraphic(k41_holeInWall_GraphicIndice),
- _g74_tmpBitmap, g108_BoxThievesEyeHoleInDoorFrame, g172_Frame_DoorFrame_D0C._box._x1 - g106_BoxThievesEye_ViewPortVisibleArea._x1,
+ _g74_tmpBitmap, g108_BoxThievesEyeHoleInDoorFrame, g172_Frame_DoorFrame_D0C._box._x1 - g106_BoxThievesEye_ViewPortVisibleArea._x1,
0, 48, 16, k9_ColorGold, 95, 123);
f100_drawWallSetBitmap(_g74_tmpBitmap, g172_Frame_DoorFrame_D0C);
} else {
@@ -2127,16 +2137,17 @@ void DisplayMan::f127_drawSquareD0C(direction dir, int16 posX, int16 posY) {
}
void DisplayMan::f128_drawDungeon(direction dir, int16 posX, int16 posY) {
- loadPalette(g20_PalEntrance);
- // TODO: this is a global variable, set from here
- bool flippedFloorCeiling = true;
+ loadPalette(g20_PalEntrance); // dummy code
+ if (_g297_drawFloorAndCeilingRequested)
+ f98_drawFloorAndCeiling();
+ _g578_useByteBoxCoordinates = true;
for (int16 i = 0; i < 6; ++i)
_vm->_dungeonMan->_g291_dungeonViewClickableBoxes[i].setToZero();
for (uint16 i = 0; i < 6; ++i) {
- _vm->_dungeonMan->_g291_dungeonViewClickableBoxes[i]._x1 = 255 + 1;
+ _vm->_dungeonMan->_g291_dungeonViewClickableBoxes[i]._x1 = 255;
}
if (_g76_useFlippedWallAndFootprintsBitmap = (posX + posY + dir) & 1) {
@@ -2144,68 +2155,96 @@ void DisplayMan::f128_drawDungeon(direction dir, int16 posX, int16 posY) {
f99_copyBitmapAndFlipHorizontal(_g84_bitmapFloor, _g74_tmpBitmap, k112_byteWidthViewport, 70);
f100_drawWallSetBitmap(_g74_tmpBitmap, gK13_FloorFrame);
- if (flippedFloorCeiling) {
- _g698_bitmapWallSet_Wall_D3LCR = _g90_bitmapWall_D3LCR_Flipped;
- _g699_bitmapWallSet_Wall_D2LCR = _g91_bitmapWall_D2LCR_Flipped;
- _g700_bitmapWallSet_Wall_D1LCR = _g92_bitmapWall_D1LCR_Flipped;
- _g701_bitmapWallSet_Wall_D0L = _g93_bitmapWall_D0L_Flipped;
- _g702_bitmapWallSet_Wall_D0R = _g94_bitmapWall_D0R_Flipped;
- }
+ _g698_bitmapWallSet_Wall_D3LCR = _g90_bitmapWall_D3LCR_Flipped;
+ _g699_bitmapWallSet_Wall_D2LCR = _g91_bitmapWall_D2LCR_Flipped;
+ _g700_bitmapWallSet_Wall_D1LCR = _g92_bitmapWall_D1LCR_Flipped;
+ _g701_bitmapWallSet_Wall_D0L = _g93_bitmapWall_D0L_Flipped;
+ _g702_bitmapWallSet_Wall_D0R = _g94_bitmapWall_D0R_Flipped;
} else {
f99_copyBitmapAndFlipHorizontal(_g85_bitmapCeiling, _g74_tmpBitmap, k112_byteWidthViewport, 29);
f100_drawWallSetBitmap(_g74_tmpBitmap, gK12_CeilingFrame);
f100_drawWallSetBitmap(_g84_bitmapFloor, gK13_FloorFrame);
}
- if (_vm->_dungeonMan->f153_getRelSquareType(dir, 3, -2, posX, posY) == k0_WallElemType)
+ if (_vm->_dungeonMan->f153_getRelSquareType(dir, 3, -2, posX, posY) == k0_ElementTypeWall) {
f100_drawWallSetBitmap(_g697_bitmapWallSet_Wall_D3L2, g711_FrameWall_D3L2);
- if (_vm->_dungeonMan->f153_getRelSquareType(dir, 3, 2, posX, posY) == k0_WallElemType)
+ }
+ if (_vm->_dungeonMan->f153_getRelSquareType(dir, 3, 2, posX, posY) == k0_ElementTypeWall) {
f100_drawWallSetBitmap(_g696_bitmapWallSet_Wall_D3R2, g712_FrameWall_D3R2);
+ }
- int16 tmpPosX = posX, tmpPosY = posY;
+ int16 tmpPosX = posX;
+ int16 tmpPosY = posY;
+ _vm->_dungeonMan->f150_mapCoordsAfterRelMovement(dir, 4, -1, tmpPosX, tmpPosY);
+ f115_cthulhu(_vm->_dungeonMan->f162_getSquareFirstObject(tmpPosX, tmpPosY), dir, tmpPosX, tmpPosY, kM2_ViewSquare_D4L, k0x0001_CellOrder_BackLeft);
+ tmpPosX = posX;
+ tmpPosY = posY;
+ _vm->_dungeonMan->f150_mapCoordsAfterRelMovement(dir, 4, 1, tmpPosX, tmpPosY);
+ f115_cthulhu(_vm->_dungeonMan->f162_getSquareFirstObject(tmpPosX, tmpPosY), dir, tmpPosX, tmpPosY, kM1_ViewSquare_D4R, k0x0001_CellOrder_BackLeft);
+ tmpPosX = posX;
+ tmpPosY = posY;
+ _vm->_dungeonMan->f150_mapCoordsAfterRelMovement(dir, 4, 0, tmpPosX, tmpPosY);
+ f115_cthulhu(_vm->_dungeonMan->f162_getSquareFirstObject(tmpPosX, tmpPosY), dir, tmpPosX, tmpPosY, kM3_ViewSquare_D4C, k0x0001_CellOrder_BackLeft);
+ tmpPosX = posX;
+ tmpPosY = posY;
_vm->_dungeonMan->f150_mapCoordsAfterRelMovement(dir, 3, -1, tmpPosX, tmpPosY);
f116_drawSquareD3L(dir, tmpPosX, tmpPosY);
- tmpPosX = posX, tmpPosY = posY;
+ tmpPosX = posX;
+ tmpPosY = posY;
_vm->_dungeonMan->f150_mapCoordsAfterRelMovement(dir, 3, 1, tmpPosX, tmpPosY);
f117_drawSquareD3R(dir, tmpPosX, tmpPosY);
- tmpPosX = posX, tmpPosY = posY;
+ tmpPosX = posX;
+ tmpPosY = posY;
_vm->_dungeonMan->f150_mapCoordsAfterRelMovement(dir, 3, 0, tmpPosX, tmpPosY);
f118_drawSquareD3C(dir, tmpPosX, tmpPosY);
- tmpPosX = posX, tmpPosY = posY;
+ tmpPosX = posX;
+ tmpPosY = posY;
_vm->_dungeonMan->f150_mapCoordsAfterRelMovement(dir, 2, -1, tmpPosX, tmpPosY);
f119_drawSquareD2L(dir, tmpPosX, tmpPosY);
- tmpPosX = posX, tmpPosY = posY;
+ tmpPosX = posX;
+ tmpPosY = posY;
_vm->_dungeonMan->f150_mapCoordsAfterRelMovement(dir, 2, 1, tmpPosX, tmpPosY);
f120_drawSquareD2R(dir, tmpPosX, tmpPosY);
- tmpPosX = posX, tmpPosY = posY;
+ tmpPosX = posX;
+ tmpPosY = posY;
_vm->_dungeonMan->f150_mapCoordsAfterRelMovement(dir, 2, 0, tmpPosX, tmpPosY);
f121_drawSquareD2C(dir, tmpPosX, tmpPosY);
- tmpPosX = posX, tmpPosY = posY;
+ tmpPosX = posX;
+ tmpPosY = posY;
_vm->_dungeonMan->f150_mapCoordsAfterRelMovement(dir, 1, -1, tmpPosX, tmpPosY);
f122_drawSquareD1L(dir, tmpPosX, tmpPosY);
- tmpPosX = posX, tmpPosY = posY;
+ tmpPosX = posX;
+ tmpPosY = posY;
_vm->_dungeonMan->f150_mapCoordsAfterRelMovement(dir, 1, 1, tmpPosX, tmpPosY);
f123_drawSquareD1R(dir, tmpPosX, tmpPosY);
- tmpPosX = posX, tmpPosY = posY;
+ tmpPosX = posX;
+ tmpPosY = posY;
_vm->_dungeonMan->f150_mapCoordsAfterRelMovement(dir, 1, 0, tmpPosX, tmpPosY);
f124_drawSquareD1C(dir, tmpPosX, tmpPosY);
- tmpPosX = posX, tmpPosY = posY;
+ tmpPosX = posX;
+ tmpPosY = posY;
_vm->_dungeonMan->f150_mapCoordsAfterRelMovement(dir, 0, -1, tmpPosX, tmpPosY);
f125_drawSquareD0L(dir, tmpPosX, tmpPosY);
- tmpPosX = posX, tmpPosY = posY;
+ tmpPosX = posX;
+ tmpPosY = posY;
_vm->_dungeonMan->f150_mapCoordsAfterRelMovement(dir, 0, 1, tmpPosX, tmpPosY);
f126_drawSquareD0R(dir, tmpPosX, tmpPosY);
f127_drawSquareD0C(dir, posX, posY);
- if (flippedFloorCeiling) {
- _g698_bitmapWallSet_Wall_D3LCR = _g95_bitmapWall_D3LCR_Native;
- _g699_bitmapWallSet_Wall_D2LCR = _g96_bitmapWall_D2LCR_Native;
- _g700_bitmapWallSet_Wall_D1LCR = _g97_bitmapWall_D1LCR_Native;
- _g701_bitmapWallSet_Wall_D0L = _g98_bitmapWall_D0L_Native;
- _g702_bitmapWallSet_Wall_D0R = _g99_bitmapWall_D0R_Native;
- }
+ _g698_bitmapWallSet_Wall_D3LCR = _g95_bitmapWall_D3LCR_Native;
+ _g699_bitmapWallSet_Wall_D2LCR = _g96_bitmapWall_D2LCR_Native;
+ _g700_bitmapWallSet_Wall_D1LCR = _g97_bitmapWall_D1LCR_Native;
+ _g701_bitmapWallSet_Wall_D0L = _g98_bitmapWall_D0L_Native;
+ _g702_bitmapWallSet_Wall_D0R = _g99_bitmapWall_D0R_Native;
f97_drawViewport((_vm->_dungeonMan->_g309_partyMapIndex != k255_mapIndexEntrance) ? 1 : 0);
+ if (_vm->_dungeonMan->_g309_partyMapIndex != k255_mapIndexEntrance)
+ f98_drawFloorAndCeiling();
+}
+
+void DisplayMan::f98_drawFloorAndCeiling() {
+ warning("f98_drawFloorAndCeiling doesn't do anything");
+ _g297_drawFloorAndCeilingRequested = false;
}
void DisplayMan::fillScreen(Color color) {
@@ -2378,6 +2417,7 @@ void DisplayMan::f96_loadCurrentMapGraphics() {
f93_applyCreatureReplColors(10, _vm->M1_ordinalToIndex(replColorOrdinal));
}
+ _g297_drawFloorAndCeilingRequested = true;
_g342_refreshDungeonViewPaleteRequested = true;
}
@@ -2464,7 +2504,7 @@ bool DisplayMan::f107_isDrawnWallOrnAnAlcove(int16 wallOrnOrd, ViewWall viewWall
wallOrnOrd--;
AL0088_i_NativeBitmapIndex = _g101_currMapWallOrnInfo[AP0116_i_WallOrnamentIndex][k0_NativeBitmapIndex];
- AL0090_puc_CoordinateSet = g205_WallOrnCoordSets[AL0089_i_WallOrnamentCoordinateSetIndex =
+ AL0090_puc_CoordinateSet = g205_WallOrnCoordSets[AL0089_i_WallOrnamentCoordinateSetIndex =
_g101_currMapWallOrnInfo[AP0116_i_WallOrnamentIndex][k1_CoordinateSet]][viewWallIndex];
L0096_B_IsAlcove = _vm->_dungeonMan->f149_isWallOrnAnAlcove(AP0116_i_WallOrnamentIndex);
@@ -2474,7 +2514,7 @@ bool DisplayMan::f107_isDrawnWallOrnAnAlcove(int16 wallOrnOrd, ViewWall viewWall
if (viewWallIndex >= k10_ViewWall_D1L_RIGHT) {
if (viewWallIndex == k12_ViewWall_D1C_FRONT) {
if (L0095_B_IsInscription) {
- f132_blitToBitmap(_g700_bitmapWallSet_Wall_D1LCR,_g296_bitmapViewport,
+ f132_blitToBitmap(_g700_bitmapWallSet_Wall_D1LCR, _g296_bitmapViewport,
g202_BoxWallPatchBehindInscription, 94, 28,
g163_FrameWalls[k6_ViewSquare_D1C]._srcByteWidth,
k112_byteWidthViewport, k255_ColorNoTransparency, g163_FrameWalls[k6_ViewSquare_D1C]._srcHeight, k136_heightViewport);
@@ -2543,7 +2583,7 @@ bool DisplayMan::f107_isDrawnWallOrnAnAlcove(int16 wallOrnOrd, ViewWall viewWall
}
}
AL0089_i_PixelWidth = (AL0090_puc_CoordinateSet + L0093_i_CoordinateSetOffset)[1] - (AL0090_puc_CoordinateSet + L0093_i_CoordinateSetOffset)[0];
- if (!f491_isDerivedBitmapInCache(AP0116_i_WallOrnamentIndex = k4_DerivedBitmapFirstWallOrnament +
+ if (!f491_isDerivedBitmapInCache(AP0116_i_WallOrnamentIndex = k4_DerivedBitmapFirstWallOrnament +
(AP0116_i_WallOrnamentIndex << 2) + g190_WallOrnDerivedBitmapIndexIncrement[viewWallIndex])) {
L0092_puc_Bitmap = f489_getNativeBitmapOrGraphic(AL0088_i_NativeBitmapIndex);
f129_blitToBitmapShrinkWithPalChange(L0092_puc_Bitmap, f492_getDerivedBitmap(AP0116_i_WallOrnamentIndex),
@@ -2583,16 +2623,16 @@ bool DisplayMan::f107_isDrawnWallOrnAnAlcove(int16 wallOrnOrd, ViewWall viewWall
}
}
f132_blitToBitmap(AL0091_puc_Bitmap, _g296_bitmapViewport,
- *(Box*)AL0090_puc_CoordinateSet, AL0089_i_X, 0,
+ *(Box*)AL0090_puc_CoordinateSet, AL0089_i_X, 0,
AL0090_puc_CoordinateSet[4], k112_byteWidthViewport, k10_ColorFlesh, AL0090_puc_CoordinateSet[5], k136_heightViewport);
-/* BUG0_05 A champion portrait sensor on a wall square is visible on all sides of the wall.
+/* BUG0_05 A champion portrait sensor on a wall square is visible on all sides of the wall.
If there is another sensor with a wall ornament on one side of the wall then the champion portrait is drawn over that wall ornament */
- if ((viewWallIndex == k12_ViewWall_D1C_FRONT) && _g289_championPortraitOrdinal--) {
+ if ((viewWallIndex == k12_ViewWall_D1C_FRONT) && _g289_championPortraitOrdinal--) {
/* A portrait is 32x29 pixels */
f132_blitToBitmap(f489_getNativeBitmapOrGraphic(k26_ChampionPortraitsIndice),
- _g296_bitmapViewport, g109_BoxChampionPortraitOnWall,
+ _g296_bitmapViewport, g109_BoxChampionPortraitOnWall,
(_g289_championPortraitOrdinal & 0x0007) << 5,
- (_g289_championPortraitOrdinal >> 3) * 29, 128, k112_byteWidthViewport, k1_ColorDarkGary, 87, k136_heightViewport);
+ (_g289_championPortraitOrdinal >> 3) * 29, 128, k112_byteWidthViewport, k1_ColorDarkGary, 87, k136_heightViewport);
}
T0107031:
return L0096_B_IsAlcove;
@@ -2648,7 +2688,7 @@ void DisplayMan::f113_drawField(FieldAspect* fieldAspect, Box& box) {
}
}
- byte *bitmap = dispMan.f489_getNativeBitmapOrGraphic(k73_FieldTeleporterGraphicIndice + fieldAspect->_nativeBitmapRelativeIndex);
+ // byte *bitmap = dispMan.f489_getNativeBitmapOrGraphic(k73_FieldTeleporterGraphicIndice + fieldAspect->_nativeBitmapRelativeIndex);
warning("MISSING CODE: F0133_VIDEO_BlitBoxFilledWithMaskedBitmap");
warning("IGNORED CODE: F0491_CACHE_IsDerivedBitmapInCache, F0493_CACHE_AddDerivedBitmap, F0480_CACHE_ReleaseBlock");
@@ -3375,7 +3415,7 @@ continue;
do {
if ((thingParam.getType() == k14_ProjectileThingType) && (thingParam.getCell() == cellYellowBear)) {
projectile = (Projectile*)dunMan.f156_getThingData(thingParam);
- if ((AL_4_projectileAspect = dunMan.f142_getProjectileAspect(projectile->_object)) < 0) { /* Negative value: projectile aspect is the ordinal of a PROJECTIL_ASPECT */
+ if ((AL_4_projectileAspect = dunMan.f142_getProjectileAspect(projectile->_slot)) < 0) { /* Negative value: projectile aspect is the ordinal of a PROJECTIL_ASPECT */
objectAspect = (ObjectAspect*)&g210_ProjectileAspect[_vm->M1_ordinalToIndex(-AL_4_projectileAspect)];
AL_4_nativeBitmapIndex = ((ProjectileAspect*)objectAspect)->_firstNativeBitmapRelativeIndex + k316_FirstProjectileGraphicIndice;
projectileAspectType = getFlag(((ProjectileAspect*)objectAspect)->_graphicInfo, k0x0003_ProjectileAspectTypeMask);
@@ -3400,7 +3440,7 @@ continue;
projectileBitmapIndexData = 0;
flipVertical = flipHorizontal = false;
} else {
- if (isOrientedWestEast((direction)(projectileDirection = _vm->_timeline->_g370_events[projectile->_timerIndex]._C._projectile.getDir()))
+ if (isOrientedWestEast((direction)(projectileDirection = _vm->_timeline->_g370_events[projectile->_eventIndex]._C._projectile.getDir()))
!= isOrientedWestEast(directionParam)) {
if (projectileAspectType == k2_ProjectileAspectHasRotation) {
projectileBitmapIndexData = 1;
diff --git a/engines/dm/gfx.h b/engines/dm/gfx.h
index cfe271052d..4cdf6139ff 100644
--- a/engines/dm/gfx.h
+++ b/engines/dm/gfx.h
@@ -286,9 +286,16 @@ public:
Box(uint16 x1, uint16 x2, uint16 y1, uint16 y2) : _x1(x1), _x2(x2), _y1(y1), _y2(y2) {}
Box() {}
+ explicit Box(uint16 *ptr) {
+ _x1 = *ptr++;
+ _x2 = *ptr++;
+ _y1 = *ptr++;
+ _y2 = *ptr++;
+ }
bool isPointInside(Common::Point point) {
return (_x1 <= point.x) && (point.x <= _x2) && (_y1 <= point.y) && (point.y <= _y2); // <= because incluseive boundaries
}
+ bool isPointInside(int16 x, int16 y) { return isPointInside(Common::Point(x, y)); }
void setToZero() { _x1 = _x2 = _y1 = _y2 = 0; }
}; // @ BOX_BYTE, BOX_WORD
@@ -453,6 +460,18 @@ public:
#define k160_byteWidthScreen 160 // @ C160_BYTE_WIDTH_SCREEN
#define k200_heightScreen 200 // @ C200_HEIGHT_SCREEN
+#define k8_byteWidth 8 // @ C008_BYTE_WIDTH
+#define k16_byteWidth 16 // @ C016_BYTE_WIDTH
+#define k24_byteWidth 24 // @ C024_BYTE_WIDTH
+#define k32_byteWidth 32 // @ C032_BYTE_WIDTH
+#define k40_byteWidth 40 // @ C040_BYTE_WIDTH
+#define k48_byteWidth 48 // @ C048_BYTE_WIDTH
+#define k64_byteWidth 64 // @ C064_BYTE_WIDTH
+#define k72_byteWidth 72 // @ C072_BYTE_WIDTH
+#define k128_byteWidth 128 // @ C128_BYTE_WIDTH
+#define k144_byteWidth 144 // @ C144_BYTE_WIDTH
+
+
class DoorFrames {
public:
Frame _closedOrDestroyed;
@@ -626,6 +645,9 @@ public:
void f112_drawCeilingPit(int16 nativeBitmapIndex, Frame *frame, int16 mapX, int16 mapY, bool flipHorizontal); // @ F0112_DUNGEONVIEW_DrawCeilingPit
+ void f21_blitToScreen(byte* bitmap, int16 *box, int16 viewDoorOrnIndex, Color transparent, int16 doorOrnOrdinal); // @ F0021_MAIN_BlitToScreen
+ void f21_blitToScreen(byte* bitmap, Box *box, int16 viewDoorOrnIndex, Color transparent, int16 doorOrnOrdinal); // @ F0021_MAIN_BlitToScreen
+
/* srcHeight and destHeight are not necessary for blitting, only error checking, thus they are defaulted for existing code which
does not pass anything, newly imported calls do pass srcHeght and srcWidth, so this is a ceonvenience change so the the parameters
@@ -651,6 +673,7 @@ public:
/* Expects inclusive boundaries in box */
void f135_fillBoxBitmap(byte *destBitmap, Box &box, Color color, int16 byteWidth, int16 height); // @ F0135_VIDEO_FillBox
void f128_drawDungeon(direction dir, int16 posX, int16 posY); // @ F0128_DUNGEONVIEW_Draw_CPSF
+ void f98_drawFloorAndCeiling(); // @ F0098_DUNGEONVIEW_DrawFloorAndCeiling
void updateScreen();
void f97_drawViewport(int16 palSwitchingRequestedState); // @ F0097_DUNGEONVIEW_DrawViewport
@@ -683,6 +706,8 @@ public:
Thing _g290_inscriptionThing; // @ G0290_T_DungeonView_InscriptionThing
+ bool _g297_drawFloorAndCeilingRequested; // @ G0297_B_DrawFloorAndCeilingRequested
+
// This tells blitting functions wther to assume a BYTE_BOX or a WORD_BOX has been passed to them,
// I only use WORD_BOX, so this will probably deem useless
bool _g578_useByteBoxCoordinates; // @ G0578_B_UseByteBoxCoordinates
diff --git a/engines/dm/group.cpp b/engines/dm/group.cpp
index 3fa8c4a3b1..7f845c6aa1 100644
--- a/engines/dm/group.cpp
+++ b/engines/dm/group.cpp
@@ -28,14 +28,19 @@
#include "group.h"
#include "dungeonman.h"
#include "champion.h"
-
+#include "movesens.h"
+#include "projexpl.h"
+#include "timeline.h"
namespace DM {
-
+int32 M32_setTime(int32 &map_time, int32 time) {
+ return map_time = (map_time & 0xFF000000) | time;
+}
GroupMan::GroupMan(DMEngine* vm) : _vm(vm) {
_g375_activeGroups = nullptr;
+ _g377_currActiveGroupCount = 0;
}
GroupMan::~GroupMan() {
@@ -97,4 +102,1634 @@ int16 GroupMan::f176_getCreatureOrdinalInCell(Group* group, uint16 cell) {
uint16 GroupMan::M50_getCreatureValue(uint16 groupVal, uint16 creatureIndex) {
return (groupVal >> (creatureIndex << 1)) & 0x3;
}
+
+void GroupMan::f188_dropGroupPossessions(int16 mapX, int16 mapY, Thing groupThing, int16 mode) {
+ Thing L0365_T_CurrentThing;
+ Thing L0366_T_NextThing;
+ Group* L0367_ps_Group;
+ uint16 L0368_ui_CreatureType;
+ int16 L0369_i_CreatureIndex;
+ uint16 L0370_ui_GroupCells;
+ bool L0371_B_WeaponDropped;
+
+
+ L0367_ps_Group = (Group*)_vm->_dungeonMan->f156_getThingData(groupThing);
+ if ((mode >= k0_soundModePlayImmediately) && getFlag(g243_CreatureInfo[L0368_ui_CreatureType = L0367_ps_Group->_type]._attributes, k0x0200_MaskCreatureInfo_dropFixedPoss)) {
+ L0369_i_CreatureIndex = L0367_ps_Group->getCount();
+ L0370_ui_GroupCells = _vm->_groupMan->f145_getGroupCells(L0367_ps_Group, _vm->_dungeonMan->_g272_currMapIndex);
+ do {
+ _vm->_groupMan->f186_dropCreatureFixedPossessions(L0368_ui_CreatureType, mapX, mapY, (L0370_ui_GroupCells == k255_CreatureTypeSingleCenteredCreature) ? k255_CreatureTypeSingleCenteredCreature : _vm->_groupMan->M50_getCreatureValue(L0370_ui_GroupCells, L0369_i_CreatureIndex), mode);
+ } while (L0369_i_CreatureIndex--);
+ }
+ if ((L0365_T_CurrentThing = L0367_ps_Group->_slot) != Thing::_endOfList) {
+ L0371_B_WeaponDropped = false;
+ do {
+ L0366_T_NextThing = _vm->_dungeonMan->f159_getNextThing(L0365_T_CurrentThing);
+ L0365_T_CurrentThing = M15_thingWithNewCell(L0365_T_CurrentThing, _vm->getRandomNumber(4));
+ if ((L0365_T_CurrentThing).getType() == k5_WeaponThingType) {
+ L0371_B_WeaponDropped = true;
+ }
+ _vm->_movsens->f267_getMoveResult(L0365_T_CurrentThing, kM1_MapXNotOnASquare, 0, mapX, mapY);
+ } while ((L0365_T_CurrentThing = L0366_T_NextThing) != Thing::_endOfList);
+ if (mode >= k0_soundModePlayImmediately) {
+ warning("MISSING CODE: F0064_SOUND_RequestPlay_CPSD");
+ }
+ }
+}
+
+void GroupMan::f186_dropCreatureFixedPossessions(uint16 creatureType, int16 mapX, int16 mapY, uint16 cell, int16 mode) {
+ static uint16 g245FixedPossessionCreature_12Skeleton[3] = { // @ G0245_aui_Graphic559_FixedPossessionsCreature12Skeleton
+ k23_ObjectInfoIndexFirstWeapon + k9_WeaponTypeFalchion,
+ k69_ObjectInfoIndexFirstArmour + k30_ArmourTypeWoodenShield, 0};
+ static uint16 g246FixedPossessionCreature_9StoneGolem[2] = { // @ G0246_aui_Graphic559_FixedPossessionsCreature09StoneGolem
+ k23_ObjectInfoIndexFirstWeapon + k24_WeaponTypeStoneClub, 0};
+ static uint16 g247FixedPossessionCreature_16TrolinAntman[2] = { // @ G0247_aui_Graphic559_FixedPossessionsCreature16Trolin_Antman
+ k23_ObjectInfoIndexFirstWeapon + k23_WeaponTypeClub, 0};
+ static uint16 g248FixedPossessionCreature_18AnimatedArmourDethKnight[7] = { // @ G0248_aui_Graphic559_FixedPossessionsCreature18AnimatedArmour_DethKnight
+ k69_ObjectInfoIndexFirstArmour + k41_ArmourTypeFootPlate,
+ k69_ObjectInfoIndexFirstArmour + k40_ArmourTypeLegPlate,
+ k69_ObjectInfoIndexFirstArmour + k39_ArmourTypeTorsoPlate,
+ k23_ObjectInfoIndexFirstWeapon + k10_WeaponTypeSword,
+ k69_ObjectInfoIndexFirstArmour + k38_ArmourTypeArmet,
+ k23_ObjectInfoIndexFirstWeapon + k10_WeaponTypeSword, 0};
+ static uint16 g249FixedPossessionCreature_7rockRockPile[5] = { // @ G0249_aui_Graphic559_FixedPossessionsCreature07Rock_RockPile
+ k127_ObjectInfoIndexFirstJunk + k25_JunkTypeBoulder,
+ k127_ObjectInfoIndexFirstJunk + k25_JunkTypeBoulder | k0x8000_randomDrop,
+ k23_ObjectInfoIndexFirstWeapon + k30_WeaponTypeRock | k0x8000_randomDrop,
+ k23_ObjectInfoIndexFirstWeapon + k30_WeaponTypeRock | k0x8000_randomDrop, 0};
+ static uint16 g250FixedPossessionCreature_4PainRatHellHound[3] = { // @ G0250_aui_Graphic559_FixedPossessionsCreature04PainRat_Hellhound
+ k127_ObjectInfoIndexFirstJunk + k35_JunkTypeDrumstickShank,
+ k127_ObjectInfoIndexFirstJunk + k35_JunkTypeDrumstickShank | k0x8000_randomDrop, 0};
+ static uint16 g251FixedPossessionCreature_6screamer[3] = { // @ G0251_aui_Graphic559_FixedPossessionsCreature06Screamer
+ k127_ObjectInfoIndexFirstJunk + k33_JunkTypeScreamerSlice,
+ k127_ObjectInfoIndexFirstJunk + k33_JunkTypeScreamerSlice | k0x8000_randomDrop, 0};
+ static uint16 g252FixedPossessionCreature_15MagnetaWormWorm[4] = { // @ G0252_aui_Graphic559_FixedPossessionsCreature15MagentaWorm_Worm
+ k127_ObjectInfoIndexFirstJunk + k34_JunkTypeWormRound,
+ k127_ObjectInfoIndexFirstJunk + k34_JunkTypeWormRound | k0x8000_randomDrop,
+ k127_ObjectInfoIndexFirstJunk + k34_JunkTypeWormRound | k0x8000_randomDrop, 0};
+ static uint16 g253FixedPossessionCreature_24RedDragon[11] = { // @ G0253_aui_Graphic559_FixedPossessionsCreature24RedDragon
+ k127_ObjectInfoIndexFirstJunk + k36_JunkTypeDragonSteak,
+ k127_ObjectInfoIndexFirstJunk + k36_JunkTypeDragonSteak,
+ k127_ObjectInfoIndexFirstJunk + k36_JunkTypeDragonSteak,
+ k127_ObjectInfoIndexFirstJunk + k36_JunkTypeDragonSteak,
+ k127_ObjectInfoIndexFirstJunk + k36_JunkTypeDragonSteak,
+ k127_ObjectInfoIndexFirstJunk + k36_JunkTypeDragonSteak,
+ k127_ObjectInfoIndexFirstJunk + k36_JunkTypeDragonSteak,
+ k127_ObjectInfoIndexFirstJunk + k36_JunkTypeDragonSteak,
+ k127_ObjectInfoIndexFirstJunk + k36_JunkTypeDragonSteak | k0x8000_randomDrop,
+ k127_ObjectInfoIndexFirstJunk + k36_JunkTypeDragonSteak | k0x8000_randomDrop, 0};
+
+ uint16 L0356_ui_FixedPossession;
+ int16 L0357_i_ThingType;
+ Thing L0358_T_Thing;
+ uint16* L0359_pui_FixedPossessions;
+ Weapon* L0360_ps_Weapon;
+ bool L0361_B_Cursed;
+ bool L0362_B_WeaponDropped;
+
+
+ L0361_B_Cursed = false;
+ L0362_B_WeaponDropped = false;
+ switch (creatureType) {
+ default:
+ return;
+ case k12_CreatureTypeSkeleton:
+ L0359_pui_FixedPossessions = g245FixedPossessionCreature_12Skeleton;
+ break;
+ case k9_CreatureTypeStoneGolem:
+ L0359_pui_FixedPossessions = g246FixedPossessionCreature_9StoneGolem;
+ break;
+ case k16_CreatureTypeTrolinAntman:
+ L0359_pui_FixedPossessions = g247FixedPossessionCreature_16TrolinAntman;
+ break;
+ case k18_CreatureTypeAnimatedArmourDethKnight:
+ L0361_B_Cursed = true;
+ L0359_pui_FixedPossessions = g248FixedPossessionCreature_18AnimatedArmourDethKnight;
+ break;
+ case k7_CreatureTypeRockpile:
+ L0359_pui_FixedPossessions = g249FixedPossessionCreature_7rockRockPile;
+ break;
+ case k4_CreatureTypePainRatHellHound:
+ L0359_pui_FixedPossessions = g250FixedPossessionCreature_4PainRatHellHound;
+ break;
+ case k6_CreatureTypeScreamer:
+ L0359_pui_FixedPossessions = g251FixedPossessionCreature_6screamer;
+ break;
+ case k15_CreatureTypeMagnetaWormWorm:
+ L0359_pui_FixedPossessions = g252FixedPossessionCreature_15MagnetaWormWorm;
+ break;
+ case k24_CreatureTypeRedDragon:
+ L0359_pui_FixedPossessions = g253FixedPossessionCreature_24RedDragon;
+ }
+ while (L0356_ui_FixedPossession = *L0359_pui_FixedPossessions++) {
+ if (getFlag(L0356_ui_FixedPossession, k0x8000_randomDrop) && _vm->getRandomNumber(2))
+ continue;
+ if (clearFlag(L0356_ui_FixedPossession, k0x8000_randomDrop) >= k127_ObjectInfoIndexFirstJunk) {
+ L0357_i_ThingType = k10_JunkThingType;
+ L0356_ui_FixedPossession -= k127_ObjectInfoIndexFirstJunk;
+ } else {
+ if (L0356_ui_FixedPossession >= k69_ObjectInfoIndexFirstArmour) {
+ L0357_i_ThingType = k6_ArmourThingType;
+ L0356_ui_FixedPossession -= k69_ObjectInfoIndexFirstArmour;
+ } else {
+ L0362_B_WeaponDropped = true;
+ L0357_i_ThingType = k5_WeaponThingType;
+ L0356_ui_FixedPossession -= k23_ObjectInfoIndexFirstWeapon;
+ }
+ }
+ if ((L0358_T_Thing = _vm->_dungeonMan->f166_getUnusedThing(L0357_i_ThingType)) == Thing::_none) {
+ continue;
+ }
+ L0360_ps_Weapon = (Weapon*)_vm->_dungeonMan->f156_getThingData(L0358_T_Thing);
+/* The same pointer type is used no matter the actual type k5_WeaponThingType, k6_ArmourThingType or k10_JunkThingType */
+ L0360_ps_Weapon->setType(L0356_ui_FixedPossession);
+ L0360_ps_Weapon->setCursed(L0361_B_Cursed);
+ L0358_T_Thing = M15_thingWithNewCell(L0358_T_Thing, ((cell == k255_CreatureTypeSingleCenteredCreature) || !_vm->getRandomNumber(4)) ? _vm->getRandomNumber(4) : cell);
+ _vm->_movsens->f267_getMoveResult(L0358_T_Thing, kM1_MapXNotOnASquare, 0, mapX, mapY);
+ }
+ warning("MISSING CODE: F0064_SOUND_RequestPlay_CPSD");
+}
+
+int16 GroupMan::f228_getDirsWhereDestIsVisibleFromSource(int16 srcMapX, int16 srcMapY, int16 destMapX, int16 destMapY) {
+#define AP0483_i_PrimaryDirection srcMapX
+ int16 L0556_i_Direction;
+
+
+ if (srcMapX == destMapX) {
+ _vm->_projexpl->_g363_secondaryDirToOrFromParty = (_vm->getRandomNumber(65536) & 0x0002) + 1; /* Resulting direction may be 1 or 3 (East or West) */
+ if (srcMapY > destMapY) {
+ return kDirNorth;
+ }
+ return kDirSouth;
+ }
+ if (srcMapY == destMapY) {
+ _vm->_projexpl->_g363_secondaryDirToOrFromParty = (_vm->getRandomNumber(65536) & 0x0002) + 0; /* Resulting direction may be 0 or 2 (North or South) */
+ if (srcMapX > destMapX) {
+ return kDirWest;
+ }
+ return kDirEast;
+ }
+ L0556_i_Direction = kDirNorth;
+ for (;;) {
+ if (f227_isDestVisibleFromSource(L0556_i_Direction, srcMapX, srcMapY, destMapX, destMapY)) {
+ if (!f227_isDestVisibleFromSource(_vm->_projexpl->_g363_secondaryDirToOrFromParty = returnNextVal(L0556_i_Direction), srcMapX, srcMapY, destMapX, destMapY)) {
+ if ((L0556_i_Direction != kDirNorth) || !f227_isDestVisibleFromSource(_vm->_projexpl->_g363_secondaryDirToOrFromParty = returnPrevVal(L0556_i_Direction), srcMapX, srcMapY, destMapX, destMapY)) {
+ _vm->_projexpl->_g363_secondaryDirToOrFromParty = returnNextVal((_vm->getRandomNumber(65536) & 0x0002) + L0556_i_Direction);
+ return L0556_i_Direction;
+ }
+ }
+ if (_vm->getRandomNumber(2)) {
+ AP0483_i_PrimaryDirection = _vm->_projexpl->_g363_secondaryDirToOrFromParty;
+ _vm->_projexpl->_g363_secondaryDirToOrFromParty = L0556_i_Direction;
+ return AP0483_i_PrimaryDirection;
+ }
+ return L0556_i_Direction;
+ }
+ L0556_i_Direction++;
+ }
+}
+
+bool GroupMan::f227_isDestVisibleFromSource(uint16 dir, int16 srcMapX, int16 srcMapY, int16 destMapX, int16 destMapY) {
+ int L1637_i_Temp;
+
+ switch (dir) { /* If direction is not 'West' then swap variables so that the same test as for west can be applied */
+ case kDirSouth:
+ L1637_i_Temp = srcMapX;
+ srcMapX = destMapY;
+ destMapY = L1637_i_Temp;
+ L1637_i_Temp = destMapX;
+ destMapX = srcMapY;
+ srcMapY = L1637_i_Temp;
+ break;
+ case kDirEast:
+ L1637_i_Temp = srcMapX;
+ srcMapX = destMapX;
+ destMapX = L1637_i_Temp;
+ L1637_i_Temp = destMapY;
+ destMapY = srcMapY;
+ srcMapY = L1637_i_Temp;
+ break;
+ case kDirNorth:
+ L1637_i_Temp = srcMapX;
+ srcMapX = srcMapY;
+ srcMapY = L1637_i_Temp;
+ L1637_i_Temp = destMapX;
+ destMapX = destMapY;
+ destMapY = L1637_i_Temp;
+ }
+ return ((srcMapX -= (destMapX - 1)) > 0) && ((((srcMapY -= destMapY) < 0) ? -srcMapY : srcMapY) <= srcMapX);
+}
+
+bool GroupMan::f232_groupIsDoorDestoryedByAttack(uint16 mapX, uint16 mapY, int16 attack, bool magicAttack, int16 ticks) {
+ Door* L0573_ps_Door;
+ byte* L0574_puc_Square;
+ TimelineEvent L0575_s_Event;
+
+ L0573_ps_Door = (Door*)_vm->_dungeonMan->f157_getSquareFirstThingData(mapX, mapY);
+ if ((magicAttack && !L0573_ps_Door->isMagicDestructible()) || (!magicAttack && !L0573_ps_Door->isMeleeDestructible())) {
+ return false;
+ }
+ if (attack >= _vm->_dungeonMan->_g275_currMapDoorInfo[L0573_ps_Door->getType()]._defense) {
+ L0574_puc_Square = &_vm->_dungeonMan->_g271_currMapData[mapX][mapY];
+ if (Square(*L0574_puc_Square).getDoorState() == k4_doorState_CLOSED) {
+ if (ticks) {
+ M33_setMapAndTime(L0575_s_Event._mapTime, _vm->_dungeonMan->_g272_currMapIndex, _vm->_g313_gameTime + ticks);
+ L0575_s_Event._type = k2_TMEventTypeDoorDestruction;
+ L0575_s_Event._priority = 0;
+ L0575_s_Event._B._location._mapX = mapX;
+ L0575_s_Event._B._location._mapY = mapY;
+ _vm->_timeline->f238_addEventGetEventIndex(&L0575_s_Event);
+ } else {
+ ((Square*)L0574_puc_Square)->setDoorState(k5_doorState_DESTROYED);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+Thing GroupMan::f175_groupGetThing(int16 mapX, int16 mapY) {
+ Thing L0317_T_Thing;
+
+ L0317_T_Thing = _vm->_dungeonMan->f161_getSquareFirstThing(mapX, mapY);
+ while ((L0317_T_Thing != Thing::_endOfList) && ((L0317_T_Thing).getType() != k4_GroupThingType)) {
+ L0317_T_Thing = _vm->_dungeonMan->f159_getNextThing(L0317_T_Thing);
+ }
+ return L0317_T_Thing;
+}
+
+int16 GroupMan::f190_groupGetDamageCreatureOutcome(Group* group, uint16 creatureIndex, int16 mapX, int16 mapY, int16 damage, bool notMoving) {
+ uint16 L0374_ui_Multiple;
+#define AL0374_ui_EventIndex L0374_ui_Multiple
+#define AL0374_ui_CreatureIndex L0374_ui_Multiple
+#define AL0374_ui_CreatureSize L0374_ui_Multiple
+#define AL0374_ui_Attack L0374_ui_Multiple
+ uint16 L0375_ui_Multiple;
+#define AL0375_ui_Outcome L0375_ui_Multiple
+#define AL0375_ui_EventType L0375_ui_Multiple
+#define AL0375_ui_NextCreatureIndex L0375_ui_Multiple
+ CreatureInfo* L0376_ps_CreatureInfo;
+ TimelineEvent* L0377_ps_Event;
+ ActiveGroup* L0378_ps_ActiveGroup = nullptr;
+ uint16 L0379_ui_CreatureCount;
+ uint16 L0380_ui_Multiple = 0;
+#define AL0380_ui_CreatureType L0380_ui_Multiple
+#define AL0380_ui_FearResistance L0380_ui_Multiple
+ uint16 L0381_ui_GroupCells;
+ uint16 L0382_ui_GroupDirections;
+ bool L0383_B_CurrentMapIsPartyMap;
+ uint16 L0384_ui_Cell;
+
+
+ L0376_ps_CreatureInfo = &g243_CreatureInfo[AL0380_ui_CreatureType = group->_type];
+ if (getFlag(L0376_ps_CreatureInfo->_attributes, k0x2000_MaskCreatureInfo_archenemy)) /* Lord Chaos cannot be damaged */
+ goto T0190024;
+ if (group->_health[creatureIndex] <= damage) {
+ L0381_ui_GroupCells = _vm->_groupMan->f145_getGroupCells(group, _vm->_dungeonMan->_g272_currMapIndex);
+ L0384_ui_Cell = (L0381_ui_GroupCells == k255_CreatureTypeSingleCenteredCreature) ? k255_CreatureTypeSingleCenteredCreature : _vm->_groupMan->M50_getCreatureValue(L0381_ui_GroupCells, creatureIndex);
+ if (!(L0379_ui_CreatureCount = group->getCount())) { /* If there is a single creature in the group */
+ if (notMoving) {
+ f188_dropGroupPossessions(mapX, mapY, _vm->_groupMan->f175_groupGetThing(mapX, mapY), k2_soundModePlayOneTickLater);
+ f189_delete(mapX, mapY);
+ }
+ AL0375_ui_Outcome = k2_outcomeKilledAllCreaturesInGroup;
+ } else { /* If there are several creatures in the group */
+ L0382_ui_GroupDirections = _vm->_groupMan->f147_getGroupDirections(group, _vm->_dungeonMan->_g272_currMapIndex);
+ if (getFlag(L0376_ps_CreatureInfo->_attributes, k0x0200_MaskCreatureInfo_dropFixedPoss)) {
+ if (notMoving) {
+ f186_dropCreatureFixedPossessions(AL0380_ui_CreatureType, mapX, mapY, L0384_ui_Cell, k2_soundModePlayOneTickLater);
+ } else {
+ _g392_dropMovingCreatureFixedPossessionsCell[_g391_dropMovingCreatureFixedPossCellCount++] = L0384_ui_Cell;
+ }
+ }
+ if (L0383_B_CurrentMapIsPartyMap = (_vm->_dungeonMan->_g272_currMapIndex == _vm->_dungeonMan->_g309_partyMapIndex)) {
+ L0378_ps_ActiveGroup = &_vm->_groupMan->_g375_activeGroups[group->getActiveGroupIndex()];
+ }
+ if (group->getBehaviour() == k6_behavior_ATTACK) {
+ L0377_ps_Event = _vm->_timeline->_g370_events;
+ for (AL0374_ui_EventIndex = 0; AL0374_ui_EventIndex < _vm->_timeline->_g369_eventMaxCount; AL0374_ui_EventIndex++) {
+ if ((M29_map(L0377_ps_Event->_mapTime) == _vm->_dungeonMan->_g272_currMapIndex) &&
+ (L0377_ps_Event->_B._location._mapX == mapX) &&
+ (L0377_ps_Event->_B._location._mapY == mapY) &&
+ ((AL0375_ui_EventType = L0377_ps_Event->_type) > k32_TMEventTypeUpdateAspectGroup) &&
+ (AL0375_ui_EventType < k41_TMEventTypeUpdateBehaviour_3 + 1)) {
+ if (AL0375_ui_EventType < k37_TMEventTypeUpdateBehaviourGroup) {
+ AL0375_ui_EventType -= k33_TMEventTypeUpdateAspectCreature_0; /* Get creature index for events 33 to 36 */
+ } else {
+ AL0375_ui_EventType -= k38_TMEventTypeUpdateBehaviour_0; /* Get creature index for events 38 to 41 */
+ }
+ if (AL0375_ui_NextCreatureIndex == creatureIndex) {
+ _vm->_timeline->f237_deleteEvent(AL0374_ui_EventIndex);
+ } else {
+ if (AL0375_ui_NextCreatureIndex > creatureIndex) {
+ L0377_ps_Event->_type -= 1;
+ _vm->_timeline->f236_fixChronology(_vm->_timeline->f235_getIndex(AL0374_ui_EventIndex));
+ }
+ }
+ }
+ L0377_ps_Event++;
+ }
+ if (L0383_B_CurrentMapIsPartyMap && ((AL0380_ui_FearResistance = L0376_ps_CreatureInfo->M57_getFearResistance()) != k15_immuneToFear) && ((AL0380_ui_FearResistance += L0379_ui_CreatureCount - 1) < (_vm->getRandomNumber(16)))) { /* Test if the death of a creature frigthens the remaining creatures in the group */
+ L0378_ps_ActiveGroup->_delayFleeingFromTarget = _vm->getRandomNumber(100 - (AL0380_ui_FearResistance << 2)) + 20;
+ group->setBehaviour(k5_behavior_FLEE);
+ }
+ }
+ for (AL0375_ui_NextCreatureIndex = AL0374_ui_CreatureIndex = creatureIndex; AL0374_ui_CreatureIndex < L0379_ui_CreatureCount; AL0374_ui_CreatureIndex++) {
+ AL0375_ui_NextCreatureIndex++;
+ group->_health[AL0374_ui_CreatureIndex] = group->_health[AL0375_ui_NextCreatureIndex];
+ L0382_ui_GroupDirections = f178_getGroupValueUpdatedWithCreatureValue(L0382_ui_GroupDirections, AL0374_ui_CreatureIndex, _vm->_groupMan->M50_getCreatureValue(L0382_ui_GroupDirections, AL0375_ui_NextCreatureIndex));
+ L0381_ui_GroupCells = f178_getGroupValueUpdatedWithCreatureValue(L0381_ui_GroupCells, AL0374_ui_CreatureIndex, _vm->_groupMan->M50_getCreatureValue(L0381_ui_GroupCells, AL0375_ui_NextCreatureIndex));
+ if (L0383_B_CurrentMapIsPartyMap) {
+ L0378_ps_ActiveGroup->_aspect[AL0374_ui_CreatureIndex] = L0378_ps_ActiveGroup->_aspect[AL0375_ui_NextCreatureIndex];
+ }
+ }
+ L0381_ui_GroupCells &= 0x003F;
+ _vm->_dungeonMan->f146_setGroupCells(group, L0381_ui_GroupCells, _vm->_dungeonMan->_g272_currMapIndex);
+ _vm->_dungeonMan->f148_setGroupDirections(group, L0382_ui_GroupDirections, _vm->_dungeonMan->_g272_currMapIndex);
+ group->setCount(group->getCount() - 1);
+ AL0375_ui_Outcome = k1_outcomeKilledSomeCreaturesInGroup;
+ }
+ if ((AL0374_ui_CreatureSize = getFlag(L0376_ps_CreatureInfo->_attributes, k0x0003_MaskCreatureInfo_size)) == k0_MaskCreatureSizeQuarter) {
+ AL0374_ui_Attack = 110;
+ } else {
+ if (AL0374_ui_CreatureSize == k1_MaskCreatureSizeHalf) {
+ AL0374_ui_Attack = 190;
+ } else {
+ AL0374_ui_Attack = 255;
+ }
+ }
+ _vm->_projexpl->f213_explosionCreate(Thing::_explSmoke, AL0374_ui_Attack, mapX, mapY, L0384_ui_Cell); /* BUG0_66 Smoke is placed on the source map instead of the destination map when a creature dies by falling through a pit. The game has a special case to correctly drop the creature possessions on the destination map but there is no such special case for the smoke. Note that the death must be caused by the damage of the fall (there is no smoke if the creature is removed because its type is not allowed on the destination map). However this bug has no visible consequence because of BUG0_26: the smoke explosion falls in the pit right after being placed in the dungeon and before being drawn on screen so it is only visible on the destination square */
+ return AL0375_ui_Outcome;
+ }
+ if (damage > 0) {
+ group->_health[creatureIndex] -= damage;
+ }
+T0190024:
+ return k0_outcomeKilledNoCreaturesInGroup;
+}
+
+void GroupMan::f189_delete(int16 mapX, int16 mapY) {
+ Thing L0372_T_GroupThing;
+ Group* L0373_ps_Group;
+
+
+ if ((L0372_T_GroupThing = _vm->_groupMan->f175_groupGetThing(mapX, mapY)) == Thing::_endOfList) {
+ return;
+ }
+ L0373_ps_Group = (Group*)_vm->_dungeonMan->f156_getThingData(L0372_T_GroupThing);
+ for (uint16 i = 0; i < 4; ++i)
+ L0373_ps_Group->_health[i] = 0;
+ _vm->_movsens->f267_getMoveResult(L0372_T_GroupThing, mapX, mapY, kM1_MapXNotOnASquare, 0);
+ L0373_ps_Group->_nextThing = Thing::_none;
+ if (_vm->_dungeonMan->_g272_currMapIndex == _vm->_dungeonMan->_g309_partyMapIndex) {
+ _vm->_groupMan->_g375_activeGroups[L0373_ps_Group->getActiveGroupIndex()]._groupThingIndex = -1;
+ _g377_currActiveGroupCount--;
+ }
+ f181_groupDeleteEvents(mapX, mapY);
+}
+
+void GroupMan::f181_groupDeleteEvents(int16 mapX, int16 mapY) {
+ int16 L0334_i_EventIndex;
+ uint16 L0335_ui_EventType;
+ TimelineEvent* L0336_ps_Event;
+
+
+ L0336_ps_Event = _vm->_timeline->_g370_events;
+ for (L0334_i_EventIndex = 0; L0334_i_EventIndex < _vm->_timeline->_g369_eventMaxCount; L0334_i_EventIndex++) {
+ if ((M29_map(L0336_ps_Event->_mapTime) == _vm->_dungeonMan->_g272_currMapIndex) &&
+ ((L0335_ui_EventType = L0336_ps_Event->_type) > k29_TMEventTypeGroupReactionDangerOnSquare - 1) && (L0335_ui_EventType < k41_TMEventTypeUpdateBehaviour_3 + 1) &&
+ (L0336_ps_Event->_B._location._mapX == mapX) && (L0336_ps_Event->_B._location._mapY == mapY)) {
+ _vm->_timeline->f237_deleteEvent(L0334_i_EventIndex);
+ }
+ L0336_ps_Event++;
+ }
+}
+
+uint16 GroupMan::f178_getGroupValueUpdatedWithCreatureValue(uint16 groupVal, uint16 creatureIndex, uint16 creatreVal) {
+ creatreVal &= 0x0003;
+ creatreVal <<= (creatureIndex <<= 1);
+ return creatreVal | (groupVal & ~(3 << creatreVal));
+}
+
+int16 GroupMan::f191_getDamageAllCreaturesOutcome(Group* group, int16 mapX, int16 mapY, int16 attack, bool notMoving) {
+ uint16 L0385_ui_RandomAttack;
+ int16 L0386_i_CreatureIndex;
+ int16 L0387_i_Outcome;
+ bool L0388_B_KilledSomeCreatures;
+ bool L0389_B_KilledAllCreatures;
+
+
+ L0388_B_KilledSomeCreatures = false;
+ L0389_B_KilledAllCreatures = true;
+ _g391_dropMovingCreatureFixedPossCellCount = 0;
+ if (attack > 0) {
+ L0386_i_CreatureIndex = group->getCount();
+ attack -= (L0385_ui_RandomAttack = (attack >> 3) + 1);
+ L0385_ui_RandomAttack <<= 1;
+ do {
+ L0389_B_KilledAllCreatures = (L0387_i_Outcome = f190_groupGetDamageCreatureOutcome(group, L0386_i_CreatureIndex, mapX, mapY, attack + _vm->getRandomNumber(L0385_ui_RandomAttack), notMoving)) && L0389_B_KilledAllCreatures;
+ L0388_B_KilledSomeCreatures = L0388_B_KilledSomeCreatures || L0387_i_Outcome;
+ } while (L0386_i_CreatureIndex--);
+ if (L0389_B_KilledAllCreatures) {
+ return k2_outcomeKilledAllCreaturesInGroup;
+ }
+ if (L0388_B_KilledSomeCreatures) {
+ return k1_outcomeKilledSomeCreaturesInGroup;
+ }
+ return k0_outcomeKilledNoCreaturesInGroup;
+ } else {
+ return k0_outcomeKilledNoCreaturesInGroup;
+ }
+}
+
+int16 GroupMan::f192_groupGetResistanceAdjustedPoisonAttack(uint16 creatreType, int16 poisonAttack) {
+ int16 L0390_i_PoisonResistance;
+
+
+ if (!poisonAttack || ((L0390_i_PoisonResistance = g243_CreatureInfo[creatreType].M61_poisonResistance()) == k15_immuneToPoison)) {
+ return 0;
+ }
+ return ((poisonAttack + _vm->getRandomNumber(4)) << 3) / ++L0390_i_PoisonResistance;
+}
+
+void GroupMan::f209_processEvents29to41(int16 eventMapX, int16 eventMapY, int16 eventType, uint16 ticks) {
+ Group* L0444_ps_Group;
+ ActiveGroup* L0445_ps_ActiveGroup;
+ int16 L0446_i_Multiple;
+#define AL0446_i_EventType L0446_i_Multiple
+#define AL0446_i_Direction L0446_i_Multiple
+#define AL0446_i_Ticks L0446_i_Multiple
+#define AL0446_i_Distance L0446_i_Multiple
+#define AL0446_i_Behavior2Or3 L0446_i_Multiple
+#define AL0446_i_CreatureAspectIndex L0446_i_Multiple
+#define AL0446_i_Range L0446_i_Multiple
+#define AL0446_i_CreatureAttributes L0446_i_Multiple
+#define AL0446_i_Cell L0446_i_Multiple
+#define AL0446_i_GroupCellsCriteria L0446_i_Multiple
+ int16 L0447_i_Multiple;
+#define AL0447_i_Behavior L0447_i_Multiple
+#define AL0447_i_CreatureIndex L0447_i_Multiple
+#define AL0447_i_ReferenceDirection L0447_i_Multiple
+#define AL0447_i_Ticks L0447_i_Multiple
+ CreatureInfo L0448_s_CreatureInfo;
+ Thing L0449_T_GroupThing;
+ int16 L0450_i_Multiple;
+#define AL0450_i_DestinationMapX L0450_i_Multiple
+#define AL0450_i_DistanceXToParty L0450_i_Multiple
+#define AL0450_i_TargetMapX L0450_i_Multiple
+ int16 L0451_i_Multiple;
+#define AL0451_i_DestinationMapY L0451_i_Multiple
+#define AL0451_i_DistanceYToParty L0451_i_Multiple
+#define AL0451_i_TargetMapY L0451_i_Multiple
+ int16 L0452_i_DistanceToVisibleParty = 0;
+ bool L0453_B_NewGroupDirectionFound;
+ int16 L0454_i_PrimaryDirectionToOrFromParty;
+ bool L0455_B_CurrentEventTypeIsNotUpdateBehavior;
+ bool L0456_B_AllowMovementOverImaginaryPitsAndFakeWalls;
+ bool L0457_B_MoveToPriorLocation;
+ bool L0458_B_SetBehavior7_ApproachAfterReaction = false;
+ int16 L0459_i_CreatureSize;
+ uint16 L0460_ui_CreatureCount;
+ int16 L0461_i_MovementTicks;
+ int16 L0462_i_TicksSinceLastMove;
+ bool L0463_B_Archenemy;
+ int32 L0464_l_NextAspectUpdateTime;
+ TimelineEvent L0465_s_NextEvent;
+
+
+ /* If the party is not on the map specified in the event and the event type is not one of 32, 33, 37, 38 then the event is ignored */
+ if ((_vm->_dungeonMan->_g272_currMapIndex != _vm->_dungeonMan->_g309_partyMapIndex) && ((AL0446_i_EventType = eventType) != k37_TMEventTypeUpdateBehaviourGroup) && (AL0446_i_EventType != k32_TMEventTypeUpdateAspectGroup) && (AL0446_i_EventType != k38_TMEventTypeUpdateBehaviour_0) && (AL0446_i_EventType != k33_TMEventTypeUpdateAspectCreature_0))
+ goto T0209139_Return;
+ /* If there is no creature at the location specified in the event then the event is ignored */
+ if ((L0449_T_GroupThing = _vm->_groupMan->f175_groupGetThing(eventMapX, eventMapY)) == Thing::_endOfList) {
+ goto T0209139_Return;
+ }
+ L0444_ps_Group = (Group*)_vm->_dungeonMan->f156_getThingData(L0449_T_GroupThing);
+ L0448_s_CreatureInfo = g243_CreatureInfo[L0444_ps_Group->_type];
+ /* Update the event */
+ M33_setMapAndTime(L0465_s_NextEvent._mapTime, _vm->_dungeonMan->_g272_currMapIndex, _vm->_g313_gameTime);
+ L0465_s_NextEvent._priority = 255 - L0448_s_CreatureInfo._movementTicks; /* The fastest creatures (with small MovementTicks value) get higher event priority */
+ L0465_s_NextEvent._B._location._mapX = eventMapX;
+ L0465_s_NextEvent._B._location._mapY = eventMapY;
+ /* If the creature is not on the party map then try and move the creature in a random direction and place a new event 37 in the timeline for the next creature movement */
+ if (_vm->_dungeonMan->_g272_currMapIndex != _vm->_dungeonMan->_g309_partyMapIndex) {
+ if (f202_isMovementPossible(&L0448_s_CreatureInfo, eventMapX, eventMapY, AL0446_i_Direction = _vm->getRandomNumber(4), false)) { /* BUG0_67 A group that is not on the party map may wrongly move or not move into a teleporter. Normally, a creature type with Wariness >= 10 (Vexirk, Materializer / Zytaz, Demon, Lord Chaos, Red Dragon / Dragon) would only move into a teleporter if the creature type is allowed on the destination map. However, the variable G0380_T_CurrentGroupThing identifying the group is not set before being used by F0139_DUNGEON_IsCreatureAllowedOnMap called by f202_isMovementPossible so the check to see if the creature type is allowed may operate on another creature type and thus return an incorrect result, causing the creature to teleport while it should not, or not to teleport while it should */
+ AL0450_i_DestinationMapX = eventMapX;
+ AL0451_i_DestinationMapY = eventMapY;
+ AL0450_i_DestinationMapX += _vm->_dirIntoStepCountEast[AL0446_i_Direction], AL0451_i_DestinationMapY += _vm->_dirIntoStepCountNorth[AL0446_i_Direction];
+ if (_vm->_movsens->f267_getMoveResult(L0449_T_GroupThing, eventMapX, eventMapY, AL0450_i_DestinationMapX, AL0451_i_DestinationMapY))
+ goto T0209139_Return;
+ L0465_s_NextEvent._B._location._mapX = _vm->_movsens->_g397_moveResultMapX;
+ L0465_s_NextEvent._B._location._mapY = _vm->_movsens->_g398_moveResultMapY;
+ }
+ L0465_s_NextEvent._type = k37_TMEventTypeUpdateBehaviourGroup;
+ AL0446_i_Ticks = MAX(ABS(_vm->_dungeonMan->_g272_currMapIndex - _vm->_dungeonMan->_g309_partyMapIndex) << 4, L0448_s_CreatureInfo._movementTicks << 1);
+ /* BUG0_68 A group moves or acts with a wrong timing. Event is added below but L0465_s_NextEvent.C.Ticks has not been initialized. No consequence while the group is not on the party map. When the party enters the group map the first group event may have a wrong timing */
+T0209005_AddEventAndReturn:
+ L0465_s_NextEvent._mapTime += AL0446_i_Ticks;
+ _vm->_timeline->f238_addEventGetEventIndex(&L0465_s_NextEvent);
+ goto T0209139_Return;
+ }
+ /* If the creature is Lord Chaos then ignore the event if the game is won. Initialize data to analyze Fluxcages */
+ if (L0463_B_Archenemy = getFlag(L0448_s_CreatureInfo._attributes, k0x2000_MaskCreatureInfo_archenemy)) {
+ if (_vm->_g302_gameWon) {
+ goto T0209139_Return;
+ }
+ _g386_fluxCageCount = 0;
+ _g385_fluxCages[0] = 0;
+ }
+ L0445_ps_ActiveGroup = &_vm->_groupMan->_g375_activeGroups[L0444_ps_Group->getActiveGroupIndex()];
+ if ((L0462_i_TicksSinceLastMove = (unsigned char)_vm->_g313_gameTime - L0445_ps_ActiveGroup->_lastMoveTime) < 0) {
+ L0462_i_TicksSinceLastMove += 256;
+ }
+ if ((L0461_i_MovementTicks = L0448_s_CreatureInfo._movementTicks) == k255_immobile) {
+ L0461_i_MovementTicks = 100;
+ }
+ if (_vm->_championMan->_g407_party._freezeLifeTicks && !L0463_B_Archenemy) { /* If life is frozen and the creature is not Lord Chaos (Lord Chaos is immune to Freeze Life) then reschedule the event later (except for reactions which are ignored when life if frozen) */
+ if (eventType < 0)
+ goto T0209139_Return;
+ L0465_s_NextEvent._type = eventType;
+ L0465_s_NextEvent._C._ticks = ticks;
+ AL0446_i_Ticks = 4; /* Retry in 4 ticks */
+ goto T0209005_AddEventAndReturn;
+ }
+ /* If the specified event type is a 'reaction' instead of a real event from the timeline then create the corresponding reaction event with a delay:
+ For event kM1_TMEventTypeCreateReactionEvent31ParyIsAdjacent, the reaction time is 1 tick
+ For event kM2_TMEventTypeCreateReactionEvent30HitByProjectile and kM3_TMEventTypeCreateReactionEvent29DangerOnSquare, the reaction time may be 1 tick or slower: slow moving creatures react more slowly. The more recent is the last creature move, the slower the reaction */
+ if (eventType < 0) {
+ L0465_s_NextEvent._type = eventType + k32_TMEventTypeUpdateAspectGroup;
+ if ((eventType == kM1_TMEventTypeCreateReactionEvent31ParyIsAdjacent) || ((AL0446_i_Ticks = ((L0461_i_MovementTicks + 2) >> 2) - L0462_i_TicksSinceLastMove) < 1)) { /* AL0446_i_Ticks is the reaction time */
+ AL0446_i_Ticks = 1; /* Retry in 1 tick */
+ }
+ goto T0209005_AddEventAndReturn; /* BUG0_68 A group moves or acts with a wrong timing. Event is added but L0465_s_NextEvent.C.Ticks has not been initialized */
+ }
+ AL0447_i_Behavior = L0444_ps_Group->getBehaviour();
+ L0460_ui_CreatureCount = L0444_ps_Group->getCount();
+ L0459_i_CreatureSize = getFlag(L0448_s_CreatureInfo._attributes, k0x0003_MaskCreatureInfo_size);
+ AL0450_i_DistanceXToParty = ((AL0446_i_Distance = eventMapX - _vm->_dungeonMan->_g306_partyMapX) < 0) ? -AL0446_i_Distance : AL0446_i_Distance;
+ AL0451_i_DistanceYToParty = ((AL0446_i_Distance = eventMapY - _vm->_dungeonMan->_g307_partyMapY) < 0) ? -AL0446_i_Distance : AL0446_i_Distance;
+ _g378_currentGroupMapX = eventMapX;
+ _g379_currentGroupMapY = eventMapY;
+ _g380_currGroupThing = L0449_T_GroupThing;
+ _g384_groupMovementTestedDirections[0] = 0;
+ _g381_currGroupDistanceToParty = f226_getDistanceBetweenSquares(eventMapX, eventMapY, _vm->_dungeonMan->_g306_partyMapX, _vm->_dungeonMan->_g307_partyMapY);
+ _g382_currGroupPrimaryDirToParty = f228_getDirsWhereDestIsVisibleFromSource(eventMapX, eventMapY, _vm->_dungeonMan->_g306_partyMapX, _vm->_dungeonMan->_g307_partyMapY);
+ _g383_currGroupSecondaryDirToParty = _vm->_projexpl->_g363_secondaryDirToOrFromParty;
+ L0464_l_NextAspectUpdateTime = 0;
+ L0455_B_CurrentEventTypeIsNotUpdateBehavior = true;
+ if (eventType <= k31_TMEventTypeGroupReactionPartyIsAdjecent) { /* Process Reaction events 29 to 31 */
+ switch (eventType = eventType - k32_TMEventTypeUpdateAspectGroup) {
+ case kM1_TMEventTypeCreateReactionEvent31ParyIsAdjacent: /* This event is used when the party bumps into a group or attacks a group physically (not with a spell). It causes the creature behavior to change to attack if it is not already attacking the party or fleeing from target */
+ if ((AL0447_i_Behavior != k6_behavior_ATTACK) && (AL0447_i_Behavior != k5_behavior_FLEE)) {
+ f181_groupDeleteEvents(eventMapX, eventMapY);
+ goto T0209044_SetBehavior6_Attack;
+ }
+ L0445_ps_ActiveGroup->_targetMapX = _vm->_dungeonMan->_g306_partyMapX;
+ L0445_ps_ActiveGroup->_targetMapY = _vm->_dungeonMan->_g307_partyMapY;
+ goto T0209139_Return;
+ case kM2_TMEventTypeCreateReactionEvent30HitByProjectile: /* This event is used for the reaction of a group after a projectile impacted with one creature in the group (some creatures may have been killed) */
+ if ((AL0447_i_Behavior == k6_behavior_ATTACK) || (AL0447_i_Behavior == k5_behavior_FLEE)) /* If the creature is attacking the party or fleeing from the target then there is no reaction */
+ goto T0209139_Return;
+ if ((AL0446_i_Behavior2Or3 = ((AL0447_i_Behavior == k3_behavior_USELESS) || (AL0447_i_Behavior == k2_behavior_USELESS))) || (_vm->getRandomNumber(4))) { /* BUG0_00 Useless code. Behavior cannot be 2 nor 3 because these values are never used. The actual condition is thus: if 3/4 chances */
+ if (!f200_groupGetDistanceToVisibleParty(L0444_ps_Group, kM1_wholeCreatureGroup, eventMapX, eventMapY)) { /* If the group cannot see the party then look in a random direction to try and search for the party */
+ L0458_B_SetBehavior7_ApproachAfterReaction = L0453_B_NewGroupDirectionFound = false;
+ goto T0209073_SetDirectionGroup;
+ }
+ if (AL0446_i_Behavior2Or3 || (_vm->getRandomNumber(4))) /* BUG0_00 Useless code. Behavior cannot be 2 nor 3 because these values are never used. The actual condition is thus: if 3/4 chances then no reaction */
+ goto T0209139_Return;
+ } /* No 'break': proceed to instruction after the next 'case' below. Reaction is to move in a random direction to try and avoid other projectiles */
+ case kM3_TMEventTypeCreateReactionEvent29DangerOnSquare: /* This event is used when some creatures in the group were killed by a Poison Cloud or by a closing door or if Lord Chaos is surrounded by 3 Fluxcages. It causes the creature to move in a random direction to avoid the danger */
+ L0458_B_SetBehavior7_ApproachAfterReaction = (AL0447_i_Behavior == k6_behavior_ATTACK); /* If the creature behavior is 'Attack' and it has to move to avoid danger then it will change its behavior to 'Approach' after the movement */
+ L0453_B_NewGroupDirectionFound = false;
+ goto T0209058_MoveInRandomDirection;
+ }
+ }
+ if (eventType < k37_TMEventTypeUpdateBehaviourGroup) { /* Process Update Aspect events 32 to 36 */
+ L0465_s_NextEvent._type = eventType + 5;
+ if (f200_groupGetDistanceToVisibleParty(L0444_ps_Group, kM1_wholeCreatureGroup, eventMapX, eventMapY)) {
+ if ((AL0447_i_Behavior != k6_behavior_ATTACK) && (AL0447_i_Behavior != k5_behavior_FLEE)) {
+ if (M38_distance(_vm->_dungeonMan->_g306_partyMapX, _vm->_dungeonMan->_g307_partyMapY, eventMapX, eventMapY) <= 1)
+ goto T0209044_SetBehavior6_Attack;
+ if (((AL0447_i_Behavior == k0_behavior_WANDER) || (AL0447_i_Behavior == k3_behavior_USELESS)) && (AL0447_i_Behavior != k7_behavior_APPROACH)) /* BUG0_00 Useless code. Behavior cannot be 3 because this value is never used. Moreover, the second condition in the && is redundant (if the value is 0 or 3, it cannot be 7). The actual condition is: if (AL0447_i_Behavior == k0_behavior_WANDER) */
+ goto T0209054_SetBehavior7_Approach;
+ }
+ L0445_ps_ActiveGroup->_targetMapX = _vm->_dungeonMan->_g306_partyMapX;
+ L0445_ps_ActiveGroup->_targetMapY = _vm->_dungeonMan->_g307_partyMapY;
+ }
+ if (AL0447_i_Behavior == k6_behavior_ATTACK) {
+ AL0446_i_CreatureAspectIndex = eventType - k33_TMEventTypeUpdateAspectCreature_0; /* Value -1 for event 32, meaning aspect will be updated for all creatures in the group */
+ L0464_l_NextAspectUpdateTime = f179_getCreatureAspectUpdateTime(L0445_ps_ActiveGroup, AL0446_i_CreatureAspectIndex, getFlag(L0445_ps_ActiveGroup->_aspect[AL0446_i_CreatureAspectIndex], k0x0080_MaskActiveGroupIsAttacking));
+ goto T0209136;
+ }
+ if ((AL0450_i_DistanceXToParty > 3) || (AL0451_i_DistanceYToParty > 3)) {
+ L0464_l_NextAspectUpdateTime = _vm->_g313_gameTime + ((L0448_s_CreatureInfo._animationTicks >> 4) & 0xF);
+ goto T0209136;
+ }
+ } else { /* Process Update Behavior events 37 to 41 */
+ L0455_B_CurrentEventTypeIsNotUpdateBehavior = false;
+ if (ticks) {
+ L0464_l_NextAspectUpdateTime = _vm->_g313_gameTime;
+ }
+ if (eventType == k37_TMEventTypeUpdateBehaviourGroup) { /* Process event 37, Update Group Behavior */
+ if ((AL0447_i_Behavior == k0_behavior_WANDER) || (AL0447_i_Behavior == k2_behavior_USELESS) || (AL0447_i_Behavior == k3_behavior_USELESS)) { /* BUG0_00 Useless code. Behavior cannot be 2 nor 3 because these values are never used. The actual condition is: if (AL0447_i_Behavior == k0_behavior_WANDER) */
+ if (L0452_i_DistanceToVisibleParty = f200_groupGetDistanceToVisibleParty(L0444_ps_Group, kM1_wholeCreatureGroup, eventMapX, eventMapY)) {
+ if ((L0452_i_DistanceToVisibleParty <= (L0448_s_CreatureInfo.M56_getAttackRange())) && ((!AL0450_i_DistanceXToParty) || (!AL0451_i_DistanceYToParty))) { /* If the creature is in range for attack and on the same row or column as the party on the map */
+T0209044_SetBehavior6_Attack:
+ if (eventType == kM2_TMEventTypeCreateReactionEvent30HitByProjectile) {
+ f181_groupDeleteEvents(eventMapX, eventMapY);
+ }
+ L0445_ps_ActiveGroup->_targetMapX = _vm->_dungeonMan->_g306_partyMapX;
+ L0445_ps_ActiveGroup->_targetMapY = _vm->_dungeonMan->_g307_partyMapY;
+ L0444_ps_Group->setBehaviour(k6_behavior_ATTACK);
+ AL0446_i_Direction = _g382_currGroupPrimaryDirToParty;
+ for (AL0447_i_CreatureIndex = L0460_ui_CreatureCount; AL0447_i_CreatureIndex >= 0; AL0447_i_CreatureIndex--) {
+ if ((_vm->_groupMan->M50_getCreatureValue(L0445_ps_ActiveGroup->_directions, AL0447_i_CreatureIndex) != AL0446_i_Direction) &&
+ ((!AL0447_i_CreatureIndex) || (!_vm->getRandomNumber(2)))) {
+ f205_setDirection(L0445_ps_ActiveGroup, AL0446_i_Direction, AL0447_i_CreatureIndex, L0460_ui_CreatureCount && (L0459_i_CreatureSize == k1_MaskCreatureSizeHalf));
+ M32_setTime(L0465_s_NextEvent._mapTime, _vm->_g313_gameTime + _vm->getRandomNumber(4) + 2); /* Random delay represents the time for the creature to turn */
+ } else {
+ M32_setTime(L0465_s_NextEvent._mapTime, _vm->_g313_gameTime + 1);
+ }
+ if (L0455_B_CurrentEventTypeIsNotUpdateBehavior) {
+ L0465_s_NextEvent._mapTime += MIN((uint16)((L0448_s_CreatureInfo._attackTicks >> 1) + _vm->getRandomNumber(4)), ticks);
+ }
+ L0465_s_NextEvent._type = k38_TMEventTypeUpdateBehaviour_0 + AL0447_i_CreatureIndex;
+ f208_groupAddEvent(&L0465_s_NextEvent, f179_getCreatureAspectUpdateTime(L0445_ps_ActiveGroup, AL0447_i_CreatureIndex, false));
+ }
+ goto T0209139_Return;
+ }
+ if (AL0447_i_Behavior != k2_behavior_USELESS) { /* BUG0_00 Useless code. Behavior cannot be 2 because this value is never used */
+T0209054_SetBehavior7_Approach:
+ L0444_ps_Group->setBehaviour(k7_behavior_APPROACH);
+ L0445_ps_ActiveGroup->_targetMapX = _vm->_dungeonMan->_g306_partyMapX;
+ L0445_ps_ActiveGroup->_targetMapY = _vm->_dungeonMan->_g307_partyMapY;
+ L0465_s_NextEvent._mapTime += 1;
+ goto T0209134_SetEvent37;
+ }
+ } else {
+ if (AL0447_i_Behavior == k0_behavior_WANDER) {
+ if (L0454_i_PrimaryDirectionToOrFromParty = f201_getSmelledPartyPrimaryDirOrdinal(&L0448_s_CreatureInfo, eventMapX, eventMapY)) {
+ L0454_i_PrimaryDirectionToOrFromParty--;
+ L0456_B_AllowMovementOverImaginaryPitsAndFakeWalls = false;
+ goto T0209085_SingleSquareMove;
+ }
+ L0453_B_NewGroupDirectionFound = false;
+ if (_vm->getRandomNumber(2)) {
+T0209058_MoveInRandomDirection:
+ AL0446_i_Direction = _vm->getRandomNumber(4);
+ AL0447_i_ReferenceDirection = AL0446_i_Direction;
+ L0457_B_MoveToPriorLocation = false;
+ do {
+ AL0450_i_DestinationMapX = eventMapX;
+ AL0451_i_DestinationMapY = eventMapY;
+ AL0450_i_DestinationMapX += _vm->_dirIntoStepCountEast[AL0446_i_Direction], AL0451_i_DestinationMapY += _vm->_dirIntoStepCountNorth[AL0446_i_Direction];
+ if (((L0445_ps_ActiveGroup->_priorMapX != AL0450_i_DestinationMapX) ||
+ (L0445_ps_ActiveGroup->_priorMapY != AL0451_i_DestinationMapY) ||
+ (L0457_B_MoveToPriorLocation = !_vm->getRandomNumber(4))) /* 1/4 chance of moving back to the square that the creature comes from */
+ && f202_isMovementPossible(&L0448_s_CreatureInfo, eventMapX, eventMapY, AL0446_i_Direction, false)) {
+T0209061_MoveGroup:
+ if (L0453_B_NewGroupDirectionFound = ((AL0447_i_Ticks = (L0461_i_MovementTicks >> 1) - L0462_i_TicksSinceLastMove) <= 0)) {
+ if (_vm->_movsens->f267_getMoveResult(L0449_T_GroupThing, eventMapX, eventMapY, AL0450_i_DestinationMapX, AL0451_i_DestinationMapY))
+ goto T0209139_Return;
+ L0465_s_NextEvent._B._location._mapX = _vm->_movsens->_g397_moveResultMapX;
+ L0465_s_NextEvent._B._location._mapY = _vm->_movsens->_g398_moveResultMapY;;
+ L0445_ps_ActiveGroup->_priorMapX = eventMapX;
+ L0445_ps_ActiveGroup->_priorMapY = eventMapY;
+ L0445_ps_ActiveGroup->_lastMoveTime = _vm->_g313_gameTime;
+ } else {
+ L0461_i_MovementTicks = AL0447_i_Ticks;
+ L0462_i_TicksSinceLastMove = -1;
+ }
+ break;
+ }
+ if (_g390_groupMovementBlockedByParty) {
+ if ((eventType != kM3_TMEventTypeCreateReactionEvent29DangerOnSquare) &&
+ ((L0444_ps_Group->getBehaviour() != k5_behavior_FLEE) ||
+ !f203_getFirstPossibleMovementDirOrdinal(&L0448_s_CreatureInfo, eventMapX, eventMapY, false) ||
+ _vm->getRandomNumber(2)))
+ goto T0209044_SetBehavior6_Attack;
+ L0445_ps_ActiveGroup->_targetMapX = _vm->_dungeonMan->_g306_partyMapX;
+ L0445_ps_ActiveGroup->_targetMapY = _vm->_dungeonMan->_g307_partyMapY;
+ }
+ } while ((AL0446_i_Direction = returnNextVal(AL0446_i_Direction)) != AL0447_i_ReferenceDirection);
+ }
+ if (!L0453_B_NewGroupDirectionFound &&
+ (L0462_i_TicksSinceLastMove != -1) &&
+ L0463_B_Archenemy &&
+ ((eventType == kM3_TMEventTypeCreateReactionEvent29DangerOnSquare) || !_vm->getRandomNumber(4))) { /* BUG0_15 The game hangs when you close a door on Lord Chaos. A condition is missing in the code to manage creatures and this may create an infinite loop between two parts in the code */
+ _vm->_projexpl->_g363_secondaryDirToOrFromParty = returnNextVal(L0454_i_PrimaryDirectionToOrFromParty = _vm->getRandomNumber(4));
+ goto T0209089_DoubleSquareMove; /* BUG0_69 Memory corruption when you close a door on Lord Chaos. The local variable (L0454_i_PrimaryDirectionToOrFromParty) containing the direction where Lord Chaos tries to move may be used as an array index without being initialized and cause memory corruption */
+ }
+ if (L0453_B_NewGroupDirectionFound || ((!_vm->getRandomNumber(4) || (L0452_i_DistanceToVisibleParty <= L0448_s_CreatureInfo.M55_getSmellRange())) && (eventType != kM3_TMEventTypeCreateReactionEvent29DangerOnSquare))) {
+T0209073_SetDirectionGroup:
+ if (!L0453_B_NewGroupDirectionFound && (L0462_i_TicksSinceLastMove >= 0)) { /* If direction is not found yet then look around in a random direction */
+ AL0446_i_Direction = _vm->getRandomNumber(4);
+ }
+ f206_groupSetDirGroup(L0445_ps_ActiveGroup, AL0446_i_Direction, L0460_ui_CreatureCount, L0459_i_CreatureSize);
+ }
+ /* If event is kM3_TMEventTypeCreateReactionEvent29DangerOnSquare or kM2_TMEventTypeCreateReactionEvent30HitByProjectile */
+ if (eventType < kM1_TMEventTypeCreateReactionEvent31ParyIsAdjacent) {
+ if (!L0453_B_NewGroupDirectionFound)
+ goto T0209139_Return;
+ if (L0458_B_SetBehavior7_ApproachAfterReaction) {
+ L0444_ps_Group->setBehaviour(k7_behavior_APPROACH);
+ }
+ f182_stopAttacking(L0445_ps_ActiveGroup, eventMapX, eventMapY);
+ }
+ }
+ }
+ } else {
+ if (AL0447_i_Behavior == k7_behavior_APPROACH) {
+ if (L0452_i_DistanceToVisibleParty = f200_groupGetDistanceToVisibleParty(L0444_ps_Group, kM1_wholeCreatureGroup, eventMapX, eventMapY)) {
+ if ((L0452_i_DistanceToVisibleParty <= L0448_s_CreatureInfo.M56_getAttackRange()) && ((!AL0450_i_DistanceXToParty) || (!AL0451_i_DistanceYToParty))) /* If the creature is in range for attack and on the same row or column as the party on the map */
+ goto T0209044_SetBehavior6_Attack;
+T0209081_RunTowardParty:
+ L0461_i_MovementTicks++;
+ L0461_i_MovementTicks = L0461_i_MovementTicks >> 1; /* Running speed is half the movement ticks */
+ AL0450_i_TargetMapX = (L0445_ps_ActiveGroup->_targetMapX = _vm->_dungeonMan->_g306_partyMapX);
+ AL0451_i_TargetMapY = (L0445_ps_ActiveGroup->_targetMapY = _vm->_dungeonMan->_g307_partyMapY);
+ } else {
+T0209082_WalkTowardTarget:
+ AL0450_i_TargetMapX = L0445_ps_ActiveGroup->_targetMapX;
+ AL0451_i_TargetMapY = L0445_ps_ActiveGroup->_targetMapY;
+ /* If the creature reached its target but the party is not there anymore */
+ if ((eventMapX == AL0450_i_TargetMapX) && (eventMapY == AL0451_i_TargetMapY)) {
+ L0453_B_NewGroupDirectionFound = false;
+ L0444_ps_Group->setBehaviour(k0_behavior_WANDER);
+ goto T0209073_SetDirectionGroup;
+ }
+ }
+ L0456_B_AllowMovementOverImaginaryPitsAndFakeWalls = true;
+T0209084_SingleSquareMoveTowardParty:
+ L0454_i_PrimaryDirectionToOrFromParty = f228_getDirsWhereDestIsVisibleFromSource(eventMapX, eventMapY, AL0450_i_TargetMapX, AL0451_i_TargetMapY);
+T0209085_SingleSquareMove:
+ if (f202_isMovementPossible(&L0448_s_CreatureInfo, eventMapX, eventMapY, AL0446_i_Direction = L0454_i_PrimaryDirectionToOrFromParty, L0456_B_AllowMovementOverImaginaryPitsAndFakeWalls) ||
+ f202_isMovementPossible(&L0448_s_CreatureInfo, eventMapX, eventMapY, AL0446_i_Direction = _vm->_projexpl->_g363_secondaryDirToOrFromParty, L0456_B_AllowMovementOverImaginaryPitsAndFakeWalls && _vm->getRandomNumber(2)) ||
+ f202_isMovementPossible(&L0448_s_CreatureInfo, eventMapX, eventMapY, AL0446_i_Direction = returnOppositeDir((direction)AL0446_i_Direction), false) ||
+ (!_vm->getRandomNumber(4) && f202_isMovementPossible(&L0448_s_CreatureInfo, eventMapX, eventMapY, AL0446_i_Direction = returnOppositeDir((direction)L0454_i_PrimaryDirectionToOrFromParty), false))) {
+ AL0450_i_DestinationMapX = eventMapX;
+ AL0451_i_DestinationMapY = eventMapY;
+ AL0450_i_DestinationMapX += _vm->_dirIntoStepCountEast[AL0446_i_Direction], AL0451_i_DestinationMapY += _vm->_dirIntoStepCountNorth[AL0446_i_Direction];
+ goto T0209061_MoveGroup;
+ }
+ if (L0463_B_Archenemy) {
+T0209089_DoubleSquareMove:
+ f203_getFirstPossibleMovementDirOrdinal(&L0448_s_CreatureInfo, eventMapX, eventMapY, false); /* BUG0_00 Useless code. Returned value is ignored. When Lord Chaos teleports two squares away the ability to move to the first square is ignored which means Lord Chaos can teleport through walls or any other obstacle */
+ if (f204_isArchenemyDoubleMovementPossible(&L0448_s_CreatureInfo, eventMapX, eventMapY, AL0446_i_Direction = L0454_i_PrimaryDirectionToOrFromParty) ||
+ f204_isArchenemyDoubleMovementPossible(&L0448_s_CreatureInfo, eventMapX, eventMapY, AL0446_i_Direction = _vm->_projexpl->_g363_secondaryDirToOrFromParty) ||
+ (_g386_fluxCageCount && f204_isArchenemyDoubleMovementPossible(&L0448_s_CreatureInfo, eventMapX, eventMapY, AL0446_i_Direction = returnOppositeDir((direction)AL0446_i_Direction))) ||
+ ((_g386_fluxCageCount >= 2) && f204_isArchenemyDoubleMovementPossible(&L0448_s_CreatureInfo, eventMapX, eventMapY, AL0446_i_Direction = returnOppositeDir((direction)L0454_i_PrimaryDirectionToOrFromParty)))) {
+ AL0450_i_DestinationMapX = eventMapX;
+ AL0451_i_DestinationMapY = eventMapY;
+ AL0450_i_DestinationMapX += _vm->_dirIntoStepCountEast[AL0446_i_Direction] * 2, AL0451_i_DestinationMapY += _vm->_dirIntoStepCountNorth[AL0446_i_Direction] * 2;
+ warning("MISSING CODE: F0064_SOUND_RequestPlay_CPSD");
+ goto T0209061_MoveGroup;
+ }
+ }
+ f206_groupSetDirGroup(L0445_ps_ActiveGroup, L0454_i_PrimaryDirectionToOrFromParty, L0460_ui_CreatureCount, L0459_i_CreatureSize);
+ } else {
+ if (AL0447_i_Behavior == k5_behavior_FLEE) {
+T0209094_FleeFromTarget:
+ L0456_B_AllowMovementOverImaginaryPitsAndFakeWalls = true;
+ /* If the creature can see the party then update target coordinates */
+ if (L0452_i_DistanceToVisibleParty = f200_groupGetDistanceToVisibleParty(L0444_ps_Group, kM1_wholeCreatureGroup, eventMapX, eventMapY)) {
+ AL0450_i_TargetMapX = (L0445_ps_ActiveGroup->_targetMapX = _vm->_dungeonMan->_g306_partyMapX);
+ AL0451_i_TargetMapY = (L0445_ps_ActiveGroup->_targetMapY = _vm->_dungeonMan->_g307_partyMapY);
+ } else {
+ if (!(--(L0445_ps_ActiveGroup->_delayFleeingFromTarget))) { /* If the creature is not afraid anymore then stop fleeing from target */
+T0209096_SetBehavior0_Wander:
+ L0453_B_NewGroupDirectionFound = false;
+ L0444_ps_Group->setBehaviour(k0_behavior_WANDER);
+ goto T0209073_SetDirectionGroup;
+ }
+ if (_vm->getRandomNumber(2)) {
+ /* If the creature cannot move and the party is adjacent then stop fleeing */
+ if (!f203_getFirstPossibleMovementDirOrdinal(&L0448_s_CreatureInfo, eventMapX, eventMapY, false)) {
+ if (M38_distance(eventMapX, eventMapY, _vm->_dungeonMan->_g306_partyMapX, _vm->_dungeonMan->_g307_partyMapY) <= 1)
+ goto T0209096_SetBehavior0_Wander;
+ }
+ /* Set creature target to the home square where the creature was located when the party entered the map */
+ AL0450_i_TargetMapX = L0445_ps_ActiveGroup->_homeMapX;
+ AL0451_i_TargetMapY = L0445_ps_ActiveGroup->_homeMapY;
+ goto T0209084_SingleSquareMoveTowardParty;
+ }
+ AL0450_i_TargetMapX = L0445_ps_ActiveGroup->_targetMapX;
+ AL0451_i_TargetMapY = L0445_ps_ActiveGroup->_targetMapY;
+ }
+ /* Try and flee from the party (opposite direction) */
+ L0454_i_PrimaryDirectionToOrFromParty = returnOppositeDir((direction)f228_getDirsWhereDestIsVisibleFromSource(eventMapX, eventMapY, AL0450_i_TargetMapX, AL0451_i_TargetMapY));
+ _vm->_projexpl->_g363_secondaryDirToOrFromParty = returnOppositeDir((direction)_vm->_projexpl->_g363_secondaryDirToOrFromParty);
+ L0461_i_MovementTicks -= (L0461_i_MovementTicks >> 2);
+ goto T0209085_SingleSquareMove;
+ }
+ }
+ }
+ } else { /* Process events 38 to 41, Update Creature Behavior */
+ if (AL0447_i_Behavior == k5_behavior_FLEE) {
+ if (L0460_ui_CreatureCount) {
+ f182_stopAttacking(L0445_ps_ActiveGroup, eventMapX, eventMapY);
+ }
+ goto T0209094_FleeFromTarget;
+ }
+ /* If the creature is attacking, then compute the next aspect update time and the next attack time */
+ if (getFlag(L0445_ps_ActiveGroup->_aspect[AL0447_i_CreatureIndex = eventType - k38_TMEventTypeUpdateBehaviour_0], k0x0080_MaskActiveGroupIsAttacking)) {
+ L0464_l_NextAspectUpdateTime = f179_getCreatureAspectUpdateTime(L0445_ps_ActiveGroup, AL0447_i_CreatureIndex, false);
+ L0465_s_NextEvent._mapTime += ((AL0447_i_Ticks = L0448_s_CreatureInfo._attackTicks) + _vm->getRandomNumber(4) - 1);
+ if (AL0447_i_Ticks > 15) {
+ L0465_s_NextEvent._mapTime += _vm->getRandomNumber(8) - 2;
+ }
+ } else { /* If the creature is not attacking, then try attacking if possible */
+ if (AL0447_i_CreatureIndex > L0460_ui_CreatureCount) { /* Ignore event if it is for a creature that is not in the group */
+ goto T0209139_Return;
+ }
+ L0454_i_PrimaryDirectionToOrFromParty = _g382_currGroupPrimaryDirToParty;
+ /* If the party is visible, update the target coordinates */
+ if (L0452_i_DistanceToVisibleParty = f200_groupGetDistanceToVisibleParty(L0444_ps_Group, AL0447_i_CreatureIndex, eventMapX, eventMapY)) {
+ L0445_ps_ActiveGroup->_targetMapX = _vm->_dungeonMan->_g306_partyMapX;
+ L0445_ps_ActiveGroup->_targetMapY = _vm->_dungeonMan->_g307_partyMapY;
+ }
+ /* If there is a single creature in the group that is not full square sized and 1/4 chance */
+ if (!L0460_ui_CreatureCount && (L0459_i_CreatureSize != k2_MaskCreatureSizeFull) && !((AL0446_i_GroupCellsCriteria = _vm->getRandomNumber(65536)) & 0x00C0)) {
+ if (L0445_ps_ActiveGroup->_cells != k255_CreatureTypeSingleCenteredCreature) {
+ /* If the creature is not already on the center of the square then change its cell */
+ if (AL0446_i_GroupCellsCriteria & 0x0038) { /* 7/8 chances of changing cell to the center of the square */
+ L0445_ps_ActiveGroup->_cells = k255_CreatureTypeSingleCenteredCreature;
+ } else { /* 1/8 chance of changing cell to the next or previous cell on the square */
+ AL0446_i_GroupCellsCriteria = M21_normalizeModulo4(M21_normalizeModulo4(L0445_ps_ActiveGroup->_cells) + ((AL0446_i_GroupCellsCriteria & 0x0001) ? 1 : -1));
+ }
+ }
+ /* If 1/8 chance and the creature is not adjacent to the party and is a quarter square sized creature then process projectile impacts and update the creature cell if still alive. When the creature is not in front of the party, it has 7/8 chances of dodging a projectile by moving to another cell or staying in the center of the square */
+ if (!(AL0446_i_GroupCellsCriteria & 0x0038) && (L0452_i_DistanceToVisibleParty != 1) && (L0459_i_CreatureSize == k0_MaskCreatureSizeQuarter)) {
+ if (_vm->_projexpl->f218_projectileGetImpactCount(kM1_CreatureElemType, eventMapX, eventMapY, L0445_ps_ActiveGroup->_cells) && (_vm->_projexpl->_g364_creatureDamageOutcome == k2_outcomeKilledAllCreaturesInGroup)) /* This call to F0218_PROJECTILE_GetImpactCount works fine because there is a single creature in the group so L0445_ps_ActiveGroup->Cells contains only one cell index */
+ goto T0209139_Return;
+ L0445_ps_ActiveGroup->_cells = M21_normalizeModulo4(AL0446_i_GroupCellsCriteria);
+ }
+ }
+ /* If the creature can see the party and is looking in the party direction or can attack in all direction */
+ if (L0452_i_DistanceToVisibleParty &&
+ (getFlag(L0448_s_CreatureInfo._attributes, k0x0004_MaskCreatureInfo_sideAttack) ||
+ _vm->_groupMan->M50_getCreatureValue(L0445_ps_ActiveGroup->_directions, AL0447_i_CreatureIndex) == L0454_i_PrimaryDirectionToOrFromParty)) {
+ /* If the creature is in range to attack the party and random test succeeds */
+ if ((L0452_i_DistanceToVisibleParty <= (AL0446_i_Range = L0448_s_CreatureInfo.M56_getAttackRange())) &&
+ (!AL0450_i_DistanceXToParty || !AL0451_i_DistanceYToParty) &&
+ (AL0446_i_Range <= (_vm->getRandomNumber(16) + 1))) {
+ if ((AL0446_i_Range == 1) &&
+ (!getFlag(AL0446_i_CreatureAttributes = L0448_s_CreatureInfo._attributes, k0x0008_MaskCreatureInfo_preferBackRow) || !_vm->getRandomNumber(4) || !getFlag(AL0446_i_CreatureAttributes, k0x0010_MaskCreatureInfo_attackAnyChamp)) &&
+ (L0459_i_CreatureSize == k0_MaskCreatureSizeQuarter) &&
+ (L0445_ps_ActiveGroup->_cells != k255_CreatureTypeSingleCenteredCreature) &&
+ ((AL0446_i_Cell = _vm->_groupMan->M50_getCreatureValue(L0445_ps_ActiveGroup->_cells, AL0447_i_CreatureIndex)) != L0454_i_PrimaryDirectionToOrFromParty) &&
+ (AL0446_i_Cell != returnNextVal(L0454_i_PrimaryDirectionToOrFromParty))) { /* If the creature cannot cast spells (range = 1) and is not on a cell where it can attack the party directly and is a quarter square sized creature not in the center of the square then the creature moves to another cell and attack does not occur immediately */
+ if (!L0460_ui_CreatureCount && _vm->getRandomNumber(2)) {
+ L0445_ps_ActiveGroup->_cells = k255_CreatureTypeSingleCenteredCreature;
+ } else {
+ if ((L0454_i_PrimaryDirectionToOrFromParty & 0x0001) == (AL0446_i_Cell & 0x0001)) {
+ AL0446_i_Cell--;
+ } else {
+ AL0446_i_Cell++;
+ }
+ if (!_vm->_groupMan->f176_getCreatureOrdinalInCell(L0444_ps_Group, AL0446_i_Cell = M21_normalizeModulo4(AL0446_i_Cell)) ||
+ (_vm->getRandomNumber(2) && !_vm->_groupMan->f176_getCreatureOrdinalInCell(L0444_ps_Group, AL0446_i_Cell = returnOppositeDir((direction)AL0446_i_Cell)))) { /* If the selected cell (or the opposite cell) is not already occupied by a creature */
+ if (_vm->_projexpl->f218_projectileGetImpactCount(kM1_CreatureElemType, eventMapX, eventMapY, L0445_ps_ActiveGroup->_cells) && (_vm->_projexpl->_g364_creatureDamageOutcome == k2_outcomeKilledAllCreaturesInGroup)) /* BUG0_70 A projectile impact on a creature may be ignored. The function F0218_PROJECTILE_GetImpactCount to detect projectile impacts when a quarter square sized creature moves inside a group (to another cell on the same square) may fail if there are several creatures in the group because the function expects a single cell index for its last parameter. The function should be called once for each cell where there is a creature */
+ goto T0209139_Return;
+ if (_vm->_projexpl->_g364_creatureDamageOutcome != k1_outcomeKilledSomeCreaturesInGroup) {
+ L0445_ps_ActiveGroup->_cells = f178_getGroupValueUpdatedWithCreatureValue(L0445_ps_ActiveGroup->_cells, AL0447_i_CreatureIndex, AL0446_i_Cell);
+ }
+ }
+ }
+ L0465_s_NextEvent._mapTime += MAX(1, (L0448_s_CreatureInfo._movementTicks >> 1) + _vm->getRandomNumber(2)); /* Time for the creature to change cell */
+ L0465_s_NextEvent._type = eventType;
+ goto T0209135;
+ }
+ L0464_l_NextAspectUpdateTime = f179_getCreatureAspectUpdateTime(L0445_ps_ActiveGroup, AL0447_i_CreatureIndex, f207_isCreatureAttacking(L0444_ps_Group, eventMapX, eventMapY, AL0447_i_CreatureIndex));
+ L0465_s_NextEvent._mapTime += (L0448_s_CreatureInfo._animationTicks & 0xF) + _vm->getRandomNumber(2);
+ } else {
+ L0444_ps_Group->setBehaviour(k7_behavior_APPROACH);
+ if (L0460_ui_CreatureCount) {
+ f182_stopAttacking(L0445_ps_ActiveGroup, eventMapX, eventMapY);
+ }
+ goto T0209081_RunTowardParty;
+ }
+ } else {
+ /* If the party is visible, update target coordinates */
+ if (f200_groupGetDistanceToVisibleParty(L0444_ps_Group, kM1_wholeCreatureGroup, eventMapX, eventMapY)) {
+ L0445_ps_ActiveGroup->_targetMapX = _vm->_dungeonMan->_g306_partyMapX;
+ L0445_ps_ActiveGroup->_targetMapY = _vm->_dungeonMan->_g307_partyMapY;
+ f205_setDirection(L0445_ps_ActiveGroup, L0454_i_PrimaryDirectionToOrFromParty, AL0447_i_CreatureIndex, L0460_ui_CreatureCount && (L0459_i_CreatureSize == k1_MaskCreatureSizeHalf));
+ L0465_s_NextEvent._mapTime += 2;
+ L0464_l_NextAspectUpdateTime = M30_time(L0465_s_NextEvent._mapTime);
+ } else { /* If the party is not visible, move to the target (last known party location) */
+ L0444_ps_Group->setBehaviour(k7_behavior_APPROACH);
+ if (L0460_ui_CreatureCount) {
+ f182_stopAttacking(L0445_ps_ActiveGroup, eventMapX, eventMapY);
+ }
+ goto T0209082_WalkTowardTarget;
+ }
+ }
+ }
+ L0465_s_NextEvent._type = eventType;
+ goto T0209136;
+ }
+ L0465_s_NextEvent._mapTime += MAX(1, _vm->getRandomNumber(4) + L0461_i_MovementTicks - 1);
+T0209134_SetEvent37:
+ L0465_s_NextEvent._type = k37_TMEventTypeUpdateBehaviourGroup;
+ }
+T0209135:
+ if (!L0464_l_NextAspectUpdateTime) {
+ L0464_l_NextAspectUpdateTime = f179_getCreatureAspectUpdateTime(L0445_ps_ActiveGroup, kM1_wholeCreatureGroup, false);
+ }
+T0209136:
+ if (L0455_B_CurrentEventTypeIsNotUpdateBehavior) {
+ L0465_s_NextEvent._mapTime += ticks;
+ } else {
+ L0464_l_NextAspectUpdateTime += ticks;
+ }
+ f208_groupAddEvent(&L0465_s_NextEvent, L0464_l_NextAspectUpdateTime);
+T0209139_Return:
+ ;
+}
+
+bool GroupMan::f202_isMovementPossible(CreatureInfo* creatureInfo, int16 mapX, int16 mapY, uint16 dir, bool allowMovementOverImaginaryPitsAndFakeWalls) {
+ int16 L0428_i_MapX;
+ int16 L0429_i_MapY;
+ uint16 L0430_ui_Square = 0;
+ int16 L0431_i_SquareType = 0;
+ Teleporter* L0432_ps_Teleporter;
+ Thing L0433_T_Thing;
+
+
+ _g384_groupMovementTestedDirections[dir] = true;
+ _g388_groupMovementBlockedByGroupThing = Thing::_endOfList;
+ _g389_groupMovementBlockedByDoor = false;
+ _g390_groupMovementBlockedByParty = false;
+ if (creatureInfo->_movementTicks == k255_immobile) {
+ return false;
+ }
+ _vm->_dungeonMan->f150_mapCoordsAfterRelMovement((direction)dir, 1, 0, mapX, mapY);
+ L0428_i_MapX = mapX;
+ L0429_i_MapY = mapY;
+ if (_g387_groupMovBlockedByWallStairsPitFakeWalFluxCageTeleporter =
+ !(((L0428_i_MapX >= 0) && (L0428_i_MapX < _vm->_dungeonMan->_g273_currMapWidth)) &&
+ ((L0429_i_MapY >= 0) && (L0429_i_MapY < _vm->_dungeonMan->_g274_currMapHeight)) &&
+ ((L0431_i_SquareType = Square(L0430_ui_Square = _vm->_dungeonMan->_g271_currMapData[L0428_i_MapX][L0429_i_MapY]).getType()) != k0_ElementTypeWall) &&
+ (L0431_i_SquareType != k3_ElementTypeStairs) &&
+ ((L0431_i_SquareType != k2_ElementTypePit) || (getFlag(L0430_ui_Square, k0x0001_PitImaginary) && allowMovementOverImaginaryPitsAndFakeWalls) || !getFlag(L0430_ui_Square, k0x0008_PitOpen) || getFlag(creatureInfo->_attributes, k0x0020_MaskCreatureInfo_levitation)) &&
+ ((L0431_i_SquareType != k6_ElementTypeFakeWall) || getFlag(L0430_ui_Square, k0x0004_FakeWallOpen) || (getFlag(L0430_ui_Square, k0x0001_FakeWallImaginary) && allowMovementOverImaginaryPitsAndFakeWalls)))) {
+ return false;
+ }
+ if (getFlag(creatureInfo->_attributes, k0x2000_MaskCreatureInfo_archenemy)) {
+ L0433_T_Thing = _vm->_dungeonMan->f161_getSquareFirstThing(L0428_i_MapX, L0429_i_MapY);
+ while (L0433_T_Thing != Thing::_endOfList) {
+ if ((L0433_T_Thing).getType() == k15_ExplosionThingType) {
+ L0432_ps_Teleporter = (Teleporter*)_vm->_dungeonMan->f156_getThingData(L0433_T_Thing);
+ if (((Explosion*)L0432_ps_Teleporter)->setType(k50_ExplosionType_Fluxcage)) {
+ _g385_fluxCages[dir] = true;
+ _g386_fluxCageCount++;
+ _g387_groupMovBlockedByWallStairsPitFakeWalFluxCageTeleporter = true;
+ return false;
+ }
+ }
+ L0433_T_Thing = _vm->_dungeonMan->f159_getNextThing(L0433_T_Thing);
+ }
+ }
+ if ((L0431_i_SquareType == k5_ElementTypeTeleporter) && getFlag(L0430_ui_Square, k0x0008_TeleporterOpen) && (creatureInfo->M59_getWariness() >= 10)) {
+ L0432_ps_Teleporter = (Teleporter*)_vm->_dungeonMan->f157_getSquareFirstThingData(L0428_i_MapX, L0429_i_MapY);
+ if (getFlag(L0432_ps_Teleporter->getScope(), k0x0001_TelepScopeCreatures) && !_vm->_dungeonMan->f139_isCreatureAllowedOnMap(_g380_currGroupThing, L0432_ps_Teleporter->getTargetMapIndex())) {
+ _g387_groupMovBlockedByWallStairsPitFakeWalFluxCageTeleporter = true;
+ return false;
+ }
+ }
+ if (_g390_groupMovementBlockedByParty = (_vm->_dungeonMan->_g272_currMapIndex == _vm->_dungeonMan->_g309_partyMapIndex) && (L0428_i_MapX == _vm->_dungeonMan->_g306_partyMapX) && (L0429_i_MapY == _vm->_dungeonMan->_g307_partyMapY)) {
+ return false;
+ }
+ if (L0431_i_SquareType == k4_DoorElemType) {
+ L0432_ps_Teleporter = (Teleporter*)_vm->_dungeonMan->f157_getSquareFirstThingData(L0428_i_MapX, L0429_i_MapY);
+ if (((Square(L0430_ui_Square).getDoorState()) > (((Door*)L0432_ps_Teleporter)->opensVertically() ? creatureInfo->M51_height() : 1)) && ((Square(L0430_ui_Square).getDoorState()) != k5_doorState_DESTROYED) && !getFlag(creatureInfo->_attributes, k0x0040_MaskCreatureInfo_nonMaterial)) {
+ _g389_groupMovementBlockedByDoor = true;
+ return false;
+ }
+ }
+ return (_g388_groupMovementBlockedByGroupThing = _vm->_groupMan->f175_groupGetThing(L0428_i_MapX, L0429_i_MapY)) == Thing::_endOfList;
+}
+
+int16 GroupMan::f226_getDistanceBetweenSquares(int16 srcMapX, int16 srcMapY, int16 destMapX, int16 destMapY) {
+ return ((((srcMapX -= destMapX) < 0) ? -srcMapX : srcMapX) +
+ (((srcMapY -= destMapY) < 0) ? -srcMapY : srcMapY));
+}
+
+int16 GroupMan::f200_groupGetDistanceToVisibleParty(Group* group, int16 creatureIndex, int16 mapX, int16 mapY) {
+ int16 L0420_i_CreatureDirection;
+ int16 L0421_i_CreatureViewDirectionCount; /* Count of directions to test in L0425_ai_CreatureViewDirections */
+ int16 L0422_i_Multiple;
+#define AL0422_i_Counter L0422_i_Multiple
+#define AL0422_i_SightRange L0422_i_Multiple
+ uint16 L0423_ui_GroupDirections;
+ CreatureInfo* L0424_ps_CreatureInfo;
+ int16 L0425_ai_CreatureViewDirections[4]; /* List of directions to test */
+
+
+ L0424_ps_CreatureInfo = &g243_CreatureInfo[group->_type];
+ if (_vm->_championMan->_g407_party._event71Count_Invisibility && !getFlag(L0424_ps_CreatureInfo->_attributes, k0x0800_MaskCreatureInfo_seeInvisible)) {
+ return 0;
+ }
+ if (getFlag(L0424_ps_CreatureInfo->_attributes, k0x0004_MaskCreatureInfo_sideAttack)) /* If creature can see in all directions */
+ goto T0200011;
+ L0423_ui_GroupDirections = _vm->_groupMan->_g375_activeGroups[group->getActiveGroupIndex()]._directions;
+ if (creatureIndex < 0) { /* Negative index means test if each creature in the group can see the party in their respective direction */
+ L0421_i_CreatureViewDirectionCount = 0;
+ for (creatureIndex = group->getCount(); creatureIndex >= 0; creatureIndex--) {
+ L0420_i_CreatureDirection = M21_normalizeModulo4(L0423_ui_GroupDirections >> (creatureIndex << 1));
+ AL0422_i_Counter = L0421_i_CreatureViewDirectionCount;
+ while (AL0422_i_Counter--) {
+ if (L0425_ai_CreatureViewDirections[AL0422_i_Counter] == L0420_i_CreatureDirection) /* If the creature looks in the same direction as another one in the group */
+ goto T0200006;
+ }
+ L0425_ai_CreatureViewDirections[L0421_i_CreatureViewDirectionCount++] = L0420_i_CreatureDirection;
+T0200006:
+ ;
+ }
+ } else { /* Positive index means test only if the specified creature in the group can see the party in its direction */
+ L0425_ai_CreatureViewDirections[0] = _vm->_groupMan->M50_getCreatureValue(L0423_ui_GroupDirections, creatureIndex);
+ L0421_i_CreatureViewDirectionCount = 1;
+ }
+ while (L0421_i_CreatureViewDirectionCount--) {
+ if (f227_isDestVisibleFromSource(L0425_ai_CreatureViewDirections[L0421_i_CreatureViewDirectionCount], mapX, mapY, _vm->_dungeonMan->_g306_partyMapX, _vm->_dungeonMan->_g307_partyMapY)) {
+T0200011:
+ AL0422_i_SightRange = L0424_ps_CreatureInfo->M54_getSightRange();
+ if (!getFlag(L0424_ps_CreatureInfo->_attributes, k0x1000_MaskCreatureInfo_nightVision)) {
+ AL0422_i_SightRange -= _vm->_displayMan->_g304_dungeonViewPaletteIndex >> 1;
+ }
+ if (_g381_currGroupDistanceToParty > MAX((int16)1, AL0422_i_SightRange)) {
+ return 0;
+ }
+ return f199_getDistanceBetweenUnblockedSquares(mapX, mapY, _vm->_dungeonMan->_g306_partyMapX, _vm->_dungeonMan->_g307_partyMapY, &GroupMan::f197_isViewPartyBlocked);
+ }
+ }
+ return 0;
+}
+
+int16 GroupMan::f199_getDistanceBetweenUnblockedSquares(int16 srcMapX, int16 srcMapY,
+ int16 destMapX, int16 destMapY, bool (GroupMan::*isBlocked)(uint16, uint16)) {
+ int16 L0410_i_XAxisStep;
+ int16 L0411_i_YAxisStep;
+ int16 L0412_i_Multiple;
+#define AL0412_i_DistanceX L0412_i_Multiple
+#define AL0412_i_PathMapX L0412_i_Multiple
+ int16 L0413_i_Multiple;
+#define AL0413_i_DistanceY L0413_i_Multiple
+#define AL0413_i_PathMapY L0413_i_Multiple
+ int16 L0414_i_LargestAxisDistance;
+ bool L0415_B_DistanceXSmallerThanDistanceY;
+ int16 L0416_i_ValueA;
+ int16 L0417_i_ValueB;
+ bool L0418_B_DistanceXEqualsDistanceY;
+ int16 L0419_i_ValueC;
+
+
+ if (M38_distance(srcMapX, srcMapY, destMapX, destMapY) <= 1) {
+ return 1;
+ }
+ L0415_B_DistanceXSmallerThanDistanceY = (AL0412_i_DistanceX = ((AL0412_i_DistanceX = destMapX - srcMapX) < 0) ? -AL0412_i_DistanceX : AL0412_i_DistanceX) < (AL0413_i_DistanceY = ((AL0413_i_DistanceY = destMapY - srcMapY) < 0) ? -AL0413_i_DistanceY : AL0413_i_DistanceY);
+ L0418_B_DistanceXEqualsDistanceY = (AL0412_i_DistanceX == AL0413_i_DistanceY);
+ L0410_i_XAxisStep = (((AL0412_i_PathMapX = destMapX) - srcMapX) > 0) ? -1 : 1;
+ L0411_i_YAxisStep = (((AL0413_i_PathMapY = destMapY) - srcMapY) > 0) ? -1 : 1;
+ L0419_i_ValueC = L0415_B_DistanceXSmallerThanDistanceY ? ((L0414_i_LargestAxisDistance = AL0413_i_PathMapY - srcMapY) ? ((AL0412_i_PathMapX - srcMapX) << 6) / L0414_i_LargestAxisDistance : 128)
+ : ((L0414_i_LargestAxisDistance = AL0412_i_PathMapX - srcMapX) ? ((AL0413_i_PathMapY - srcMapY) << 6) / L0414_i_LargestAxisDistance : 128);
+ /* 128 when the creature is on the same row or column as the party */
+ do {
+ if (L0418_B_DistanceXEqualsDistanceY) {
+ if (((CALL_MEMBER_FN(*_vm->_groupMan, isBlocked))(AL0412_i_PathMapX + L0410_i_XAxisStep, AL0413_i_PathMapY) && (CALL_MEMBER_FN(*_vm->_groupMan, isBlocked))(AL0412_i_PathMapX, AL0413_i_PathMapY + L0411_i_YAxisStep)) || (CALL_MEMBER_FN(*_vm->_groupMan, isBlocked))(AL0412_i_PathMapX = AL0412_i_PathMapX + L0410_i_XAxisStep, AL0413_i_PathMapY = AL0413_i_PathMapY + L0411_i_YAxisStep)) {
+ return 0;
+ }
+ } else {
+ if ((L0416_i_ValueA = ((L0414_i_LargestAxisDistance = (L0415_B_DistanceXSmallerThanDistanceY ? ((L0414_i_LargestAxisDistance = AL0413_i_PathMapY - srcMapY) ? ((AL0412_i_PathMapX + L0410_i_XAxisStep - srcMapX) << 6) / L0414_i_LargestAxisDistance : 128) : ((L0414_i_LargestAxisDistance = AL0412_i_PathMapX + L0410_i_XAxisStep - srcMapX) ? ((AL0413_i_PathMapY - srcMapY) << 6) / L0414_i_LargestAxisDistance : 128)) - L0419_i_ValueC) < 0) ? -L0414_i_LargestAxisDistance : L0414_i_LargestAxisDistance) < (L0417_i_ValueB = ((L0414_i_LargestAxisDistance = (L0415_B_DistanceXSmallerThanDistanceY ? ((L0414_i_LargestAxisDistance = AL0413_i_PathMapY + L0411_i_YAxisStep - srcMapY) ? ((AL0412_i_PathMapX - srcMapX) << 6) / L0414_i_LargestAxisDistance : 128) : ((L0414_i_LargestAxisDistance = AL0412_i_PathMapX - srcMapX) ? ((AL0413_i_PathMapY + L0411_i_YAxisStep - srcMapY) << 6) / L0414_i_LargestAxisDistance : 128)) - L0419_i_ValueC) < 0) ? -L0414_i_LargestAxisDistance : L0414_i_LargestAxisDistance)) {
+ AL0412_i_PathMapX += L0410_i_XAxisStep;
+ } else {
+ AL0413_i_PathMapY += L0411_i_YAxisStep;
+ }
+ if ((CALL_MEMBER_FN(*_vm->_groupMan, isBlocked))(AL0412_i_PathMapX, AL0413_i_PathMapY) && ((L0416_i_ValueA != L0417_i_ValueB) || (CALL_MEMBER_FN(*_vm->_groupMan, isBlocked))(AL0412_i_PathMapX = AL0412_i_PathMapX + L0410_i_XAxisStep, AL0413_i_PathMapY = AL0413_i_PathMapY - L0411_i_YAxisStep))) {
+ return 0;
+ }
+ }
+ } while (M38_distance(AL0412_i_PathMapX, AL0413_i_PathMapY, srcMapX, srcMapY) > 1);
+ return f226_getDistanceBetweenSquares(srcMapX, srcMapY, destMapX, destMapY);
+}
+
+bool GroupMan::f197_isViewPartyBlocked(uint16 mapX, uint16 mapY) {
+ uint16 L0404_ui_Square;
+ int16 L0405_i_SquareType;
+ int16 L0406_i_DoorState;
+ Door* L0407_ps_Door;
+
+ if ((L0405_i_SquareType = Square(L0404_ui_Square = _vm->_dungeonMan->_g271_currMapData[mapX][mapY]).getType()) == k4_DoorElemType) {
+ L0407_ps_Door = (Door*)_vm->_dungeonMan->f157_getSquareFirstThingData(mapX, mapY);
+ return (((L0406_i_DoorState = Square(L0404_ui_Square).getDoorState()) == k3_doorState_FOURTH) || (L0406_i_DoorState == k4_doorState_CLOSED)) && !getFlag(_vm->_dungeonMan->_g275_currMapDoorInfo[L0407_ps_Door->getType()]._attributes, k0x0001_MaskDoorInfo_CraturesCanSeeThrough);
+ }
+ return (L0405_i_SquareType == k0_ElementTypeWall) || ((L0405_i_SquareType == k6_ElementTypeFakeWall) && !getFlag(L0404_ui_Square, k0x0004_FakeWallOpen));
+}
+
+int32 GroupMan::f179_getCreatureAspectUpdateTime(ActiveGroup* activeGroup, int16 creatureIndex, bool isAttacking) {
+ uint16 L0326_ui_Multiple;
+#define AL0326_ui_Aspect L0326_ui_Multiple
+#define AL0326_ui_AnimationTicks L0326_ui_Multiple
+ uint16 L0327_ui_CreatureGraphicInfo;
+ int16 L0328_i_Offset;
+ Group* L0329_ps_Group;
+ bool L0330_B_ProcessGroup;
+ uint16 L0331_ui_CreatureType;
+ uint16 L1635_ui_SoundIndex;
+
+ L0329_ps_Group = &(((Group*)_vm->_dungeonMan->_g284_thingData[k4_GroupThingType])[activeGroup->_groupThingIndex]);
+ L0327_ui_CreatureGraphicInfo = g243_CreatureInfo[L0331_ui_CreatureType = L0329_ps_Group->_type]._graphicInfo;
+ if (L0330_B_ProcessGroup = (creatureIndex < 0)) { /* If the creature index is negative then all creatures in the group are processed */
+ creatureIndex = L0329_ps_Group->getCount();
+ }
+ do {
+ AL0326_ui_Aspect = activeGroup->_aspect[creatureIndex];
+ AL0326_ui_Aspect &= k0x0080_MaskActiveGroupIsAttacking | k0x0040_MaskActiveGroupFlipBitmap;
+ if (L0328_i_Offset = ((L0327_ui_CreatureGraphicInfo >> 12) & 0x3)) {
+ L0328_i_Offset = _vm->getRandomNumber(L0328_i_Offset);
+ if (_vm->getRandomNumber(2)) {
+ L0328_i_Offset = (-L0328_i_Offset) & 0x0007;
+ }
+ AL0326_ui_Aspect |= L0328_i_Offset;
+ }
+ if (L0328_i_Offset = ((L0327_ui_CreatureGraphicInfo >> 14) & 0x3)) {
+ L0328_i_Offset = _vm->getRandomNumber(L0328_i_Offset);
+ if (_vm->getRandomNumber(2)) {
+ L0328_i_Offset = (-L0328_i_Offset) & 0x0007;
+ }
+ AL0326_ui_Aspect |= (L0328_i_Offset << 3);
+ }
+ if (isAttacking) {
+ if (getFlag(L0327_ui_CreatureGraphicInfo, k0x0200_CreatureInfoGraphicMaskFlipAttack)) {
+ if (getFlag(AL0326_ui_Aspect, k0x0080_MaskActiveGroupIsAttacking) && (L0331_ui_CreatureType == k18_CreatureTypeAnimatedArmourDethKnight)) {
+ if (_vm->getRandomNumber(2)) {
+ toggleFlag(AL0326_ui_Aspect, k0x0040_MaskActiveGroupFlipBitmap);
+ warning("MISSING CODE: F0064_SOUND_RequestPlay_CPSD");
+ }
+ } else {
+ if (!getFlag(AL0326_ui_Aspect, k0x0080_MaskActiveGroupIsAttacking) || !getFlag(L0327_ui_CreatureGraphicInfo, k0x0400_CreatureInfoGraphicMaskFlipDuringAttack)) {
+ if (_vm->getRandomNumber(2)) {
+ setFlag(AL0326_ui_Aspect, k0x0040_MaskActiveGroupFlipBitmap);
+ } else {
+ clearFlag(AL0326_ui_Aspect, k0x0040_MaskActiveGroupFlipBitmap);
+ }
+ }
+ }
+ } else {
+ clearFlag(AL0326_ui_Aspect, k0x0040_MaskActiveGroupFlipBitmap);
+ }
+ setFlag(AL0326_ui_Aspect, k0x0080_MaskActiveGroupIsAttacking);
+ } else {
+ if (getFlag(L0327_ui_CreatureGraphicInfo, k0x0004_CreatureInfoGraphicMaskFlipNonAttack)) {
+ if (L0331_ui_CreatureType == k13_CreatureTypeCouatl) {
+ if (_vm->getRandomNumber(2)) {
+ toggleFlag(AL0326_ui_Aspect, k0x0040_MaskActiveGroupFlipBitmap);
+ L1635_ui_SoundIndex = _vm->_movsens->f514_getSound(k13_CreatureTypeCouatl);
+ if (L1635_ui_SoundIndex <= k34_D13_soundCount) {
+ warning("MISSING CODE: F0064_SOUND_RequestPlay_CPSD");
+ }
+ }
+ } else {
+ if (_vm->getRandomNumber(2)) {
+ setFlag(AL0326_ui_Aspect, k0x0040_MaskActiveGroupFlipBitmap);
+ } else {
+ clearFlag(AL0326_ui_Aspect, k0x0040_MaskActiveGroupFlipBitmap);
+ }
+ }
+ } else {
+ clearFlag(AL0326_ui_Aspect, k0x0040_MaskActiveGroupFlipBitmap);
+ }
+ clearFlag(AL0326_ui_Aspect, k0x0080_MaskActiveGroupIsAttacking);
+ }
+ activeGroup->_aspect[creatureIndex] = AL0326_ui_Aspect;
+ } while (L0330_B_ProcessGroup && (creatureIndex--));
+ AL0326_ui_AnimationTicks = g243_CreatureInfo[L0329_ps_Group->_type]._animationTicks;
+ return _vm->_g313_gameTime + (isAttacking ? ((AL0326_ui_AnimationTicks >> 8) & 0xF) : ((AL0326_ui_AnimationTicks >> 4) & 0xF)) + _vm->getRandomNumber(2);
+}
+
+void GroupMan::f205_setDirection(ActiveGroup* activeGroup, int16 dir, int16 creatureIndex, bool twoHalfSquareSizedCreatures) {
+ uint16 L0435_ui_GroupDirections;
+ static long G0395_l_TwoHalfSquareSizedCreaturesGroupLastDirectionSetTime; /* These two variables are used to prevent setting direction of half square sized creatures twice at the same game time */
+ static ActiveGroup* G0396_ps_TwoHalfSquareSizedCreaturesGroupLastDirectionSetActiveGroup;
+
+
+ warning("potentially dangerous cast to uint32 below");
+ if (twoHalfSquareSizedCreatures && (_vm->_g313_gameTime == (uint32)G0395_l_TwoHalfSquareSizedCreaturesGroupLastDirectionSetTime) && (activeGroup == G0396_ps_TwoHalfSquareSizedCreaturesGroupLastDirectionSetActiveGroup)) {
+ return;
+ }
+ if (M21_normalizeModulo4(_vm->_groupMan->M50_getCreatureValue(L0435_ui_GroupDirections = activeGroup->_directions, creatureIndex) - dir) == 2) { /* If current and new direction are opposites then change direction only one step at a time */
+ L0435_ui_GroupDirections = f178_getGroupValueUpdatedWithCreatureValue(L0435_ui_GroupDirections, creatureIndex, dir = returnNextVal((_vm->getRandomNumber(65536) & 0x0002) + dir));
+ } else {
+ L0435_ui_GroupDirections = f178_getGroupValueUpdatedWithCreatureValue(L0435_ui_GroupDirections, creatureIndex, dir);
+ }
+ if (twoHalfSquareSizedCreatures) {
+ L0435_ui_GroupDirections = f178_getGroupValueUpdatedWithCreatureValue(L0435_ui_GroupDirections, creatureIndex ^ 1, dir); /* Set direction of the second half square sized creature */
+ G0395_l_TwoHalfSquareSizedCreaturesGroupLastDirectionSetTime = _vm->_g313_gameTime;
+ G0396_ps_TwoHalfSquareSizedCreaturesGroupLastDirectionSetActiveGroup = activeGroup;
+ }
+ activeGroup->_directions = (direction)L0435_ui_GroupDirections;
+}
+
+void GroupMan::f208_groupAddEvent(TimelineEvent* event, uint32 time) {
+ warning("potentially dangerous cast to uint32 below");
+ if (time < (uint32)M30_time(event->_mapTime)) {
+ event->_type -= 5;
+ event->_C._ticks = M30_time(event->_mapTime) - time;
+ M32_setTime(event->_mapTime, time);
+ } else {
+ event->_C._ticks = time - M30_time(event->_mapTime);
+ }
+ _vm->_timeline->f238_addEventGetEventIndex(event);
+}
+
+int16 GroupMan::f201_getSmelledPartyPrimaryDirOrdinal(CreatureInfo* creatureInfo, int16 mapY, int16 mapX) {
+
+ uint16 L0426_ui_SmellRange;
+ int16 L0427_i_ScentOrdinal;
+
+
+ if (!(L0426_ui_SmellRange = creatureInfo->M55_getSmellRange())) {
+ return 0;
+ }
+ if ((((L0426_ui_SmellRange + 1) >> 1) >= _g381_currGroupDistanceToParty) && f199_getDistanceBetweenUnblockedSquares(mapY, mapX, _vm->_dungeonMan->_g306_partyMapX, _vm->_dungeonMan->_g307_partyMapY, &GroupMan::f198_isSmellPartyBlocked)) {
+ _vm->_projexpl->_g363_secondaryDirToOrFromParty = _g383_currGroupSecondaryDirToParty;
+ return _vm->M0_indexToOrdinal(_g382_currGroupPrimaryDirToParty);
+ }
+ if ((L0427_i_ScentOrdinal = _vm->_championMan->f315_getScentOrdinal(mapY, mapX)) && ((_vm->_championMan->_g407_party._scentStrengths[_vm->M1_ordinalToIndex(L0427_i_ScentOrdinal)] + _vm->getRandomNumber(4)) > (30 - (L0426_ui_SmellRange << 1)))) { /* If there is a fresh enough party scent on the group square */
+ return _vm->M0_indexToOrdinal(f228_getDirsWhereDestIsVisibleFromSource(mapY, mapX, _vm->_championMan->_g407_party._scents[L0427_i_ScentOrdinal].getMapX(), _vm->_championMan->_g407_party._scents[L0427_i_ScentOrdinal].getMapY()));
+ }
+ return 0;
+}
+
+bool GroupMan::f198_isSmellPartyBlocked(uint16 mapX, uint16 mapY) {
+ uint16 L0408_ui_Square;
+ int16 L0409_i_SquareType;
+
+ return ((L0409_i_SquareType = Square(L0408_ui_Square = _vm->_dungeonMan->_g271_currMapData[mapX][mapY]).getType()) == k0_ElementTypeWall) || ((L0409_i_SquareType == k6_ElementTypeFakeWall) && !getFlag(L0408_ui_Square, k0x0004_FakeWallOpen));
+}
+
+int16 GroupMan::f203_getFirstPossibleMovementDirOrdinal(CreatureInfo* info, int16 mapX, int16 mapY, bool allowMovementOverImaginaryPitsAndFakeWalls) {
+ int16 L0434_i_Direction;
+
+
+ for (L0434_i_Direction = kDirNorth; L0434_i_Direction <= kDirWest; L0434_i_Direction++) {
+ if ((!_g384_groupMovementTestedDirections[L0434_i_Direction]) && f202_isMovementPossible(info, mapX, mapY, L0434_i_Direction, allowMovementOverImaginaryPitsAndFakeWalls)) {
+ return _vm->M0_indexToOrdinal(L0434_i_Direction);
+ }
+ }
+ return 0;
+}
+
+void GroupMan::f206_groupSetDirGroup(ActiveGroup* activeGroup, int16 dir, int16 creatureIndex, int16 creatureSize) {
+ bool L0436_B_TwoHalfSquareSizedCreatures;
+
+
+ if (L0436_B_TwoHalfSquareSizedCreatures = creatureIndex && (creatureSize == k1_MaskCreatureSizeHalf)) {
+ creatureIndex--;
+ }
+ do {
+ if (!creatureIndex || _vm->getRandomNumber(2)) {
+ f205_setDirection(activeGroup, dir, creatureIndex, L0436_B_TwoHalfSquareSizedCreatures);
+ }
+ } while (creatureIndex--);
+}
+
+void GroupMan::f182_stopAttacking(ActiveGroup* group, int16 mapX, int16 mapY) {
+ int16 L0337_i_CreatureIndex;
+
+ for (L0337_i_CreatureIndex = 0; L0337_i_CreatureIndex < 4; clearFlag(group->_aspect[L0337_i_CreatureIndex++], k0x0080_MaskActiveGroupIsAttacking));
+ f181_groupDeleteEvents(mapX, mapY);
+
+}
+
+bool GroupMan::f204_isArchenemyDoubleMovementPossible(CreatureInfo* info, int16 mapX, int16 mapY, uint16 dir) {
+ if (_g385_fluxCages[dir]) {
+ return false;
+ }
+ mapX += _vm->_dirIntoStepCountEast[dir], mapY += _vm->_dirIntoStepCountNorth[dir];
+ return f202_isMovementPossible(info, mapX, mapY, dir, false);
+}
+
+bool GroupMan::f207_isCreatureAttacking(Group* group, int16 mapX, int16 mapY, uint16 creatureIndex) {
+ uint16 L0437_ui_Multiple;
+#define AL0437_ui_CreatureType L0437_ui_Multiple
+#define AL0437_T_Thing L0437_ui_Multiple
+ uint16 L0438_ui_PrimaryDirectionToParty;
+ int16 L0439_i_Multiple;
+#define AL0439_i_GroupCells L0439_i_Multiple
+#define AL0439_i_TargetCell L0439_i_Multiple
+#define AL0439_i_ChampionIndex L0439_i_Multiple
+ int16 L0440_i_Multiple;
+#define AL0440_i_KineticEnergy L0440_i_Multiple
+#define AL0440_i_Counter L0440_i_Multiple
+#define AL0440_i_Damage L0440_i_Multiple
+#define AL0440_i_AttackSoundOrdinal L0440_i_Multiple
+ CreatureInfo* L0441_ps_CreatureInfo;
+ Champion* L0442_ps_Champion;
+ ActiveGroup L0443_s_ActiveGroup;
+
+
+ _vm->_projexpl->_g361_lastCreatureAttackTime = _vm->_g313_gameTime;
+ L0443_s_ActiveGroup = _vm->_groupMan->_g375_activeGroups[group->getActiveGroupIndex()];
+ L0441_ps_CreatureInfo = &g243_CreatureInfo[AL0437_ui_CreatureType = group->_type];
+ L0438_ui_PrimaryDirectionToParty = _g382_currGroupPrimaryDirToParty;
+ if ((AL0439_i_GroupCells = L0443_s_ActiveGroup._cells) == k255_CreatureTypeSingleCenteredCreature) {
+ AL0439_i_TargetCell = _vm->getRandomNumber(2);
+ } else {
+ AL0439_i_TargetCell = ((_vm->_groupMan->M50_getCreatureValue(AL0439_i_GroupCells, creatureIndex) + 5 - L0438_ui_PrimaryDirectionToParty) & 0x0002) >> 1;
+ }
+ AL0439_i_TargetCell += L0438_ui_PrimaryDirectionToParty;
+ AL0439_i_TargetCell &= 0x0003;
+ if ((L0441_ps_CreatureInfo->M56_getAttackRange() > 1) && ((_g381_currGroupDistanceToParty > 1) || _vm->getRandomNumber(2))) {
+ switch (AL0437_ui_CreatureType) {
+ case k14_CreatureTypeVexirk:
+ case k23_CreatureTypeLordChaos:
+ if (_vm->getRandomNumber(2)) {
+ AL0437_T_Thing = Thing::_explFireBall.toUint16();
+ } else {
+ switch (_vm->getRandomNumber(4)) {
+ case 0:
+ AL0437_T_Thing = Thing::_explHarmNonMaterial.toUint16();
+ break;
+ case 1:
+ AL0437_T_Thing = Thing::_explLightningBolt.toUint16();
+ break;
+ case 2:
+ AL0437_T_Thing = Thing::_explPoisonCloud.toUint16();
+ break;
+ case 3:
+ AL0437_T_Thing = Thing::_explOpenDoor.toUint16();
+ }
+ }
+ break;
+ case k1_CreatureTypeSwampSlimeSlime:
+ AL0437_T_Thing = Thing::_explSlime.toUint16();
+ break;
+ case k3_CreatureTypeWizardEyeFlyingEye:
+ if (_vm->getRandomNumber(8)) {
+ AL0437_T_Thing = Thing::_explLightningBolt.toUint16();
+ } else {
+ AL0437_T_Thing = Thing::_explOpenDoor.toUint16();
+ }
+ break;
+ case k19_CreatureTypeMaterializerZytaz:
+ if (_vm->getRandomNumber(2)) {
+ AL0437_T_Thing = Thing::_explPoisonCloud.toUint16();
+ break;
+ }
+ case k22_CreatureTypeDemon:
+ case k24_CreatureTypeRedDragon:
+ AL0437_T_Thing = Thing::_explFireBall.toUint16();
+ } /* BUG0_13 The game may crash when 'Lord Order' or 'Grey Lord' cast spells. This cannot happen with the original dungeons as they do not contain any groups of these types. 'Lord Order' and 'Grey Lord' creatures can cast spells (attack range > 1) but no projectile type is defined for them in the code. If these creatures are present in a dungeon they will cast projectiles containing undefined things because the variable is not initialized */
+ AL0440_i_KineticEnergy = (L0441_ps_CreatureInfo->_attack >> 2) + 1;
+ AL0440_i_KineticEnergy += _vm->getRandomNumber(AL0440_i_KineticEnergy);
+ AL0440_i_KineticEnergy += _vm->getRandomNumber(AL0440_i_KineticEnergy);
+ warning("MISSING CODE: F0064_SOUND_RequestPlay_CPSD");
+ _vm->_projexpl->f212_projectileCreate(Thing(AL0437_T_Thing), mapX, mapY, AL0439_i_TargetCell, (direction)_g382_currGroupPrimaryDirToParty, f26_getBoundedValue((int16)20, AL0440_i_KineticEnergy, (int16)255), L0441_ps_CreatureInfo->_dexterity, 8);
+ } else {
+ if (getFlag(L0441_ps_CreatureInfo->_attributes, k0x0010_MaskCreatureInfo_attackAnyChamp)) {
+ AL0439_i_ChampionIndex = _vm->getRandomNumber(4);
+ for (AL0440_i_Counter = 0; (AL0440_i_Counter < 4) && !_vm->_championMan->_gK71_champions[AL0439_i_ChampionIndex]._currHealth; AL0440_i_Counter++) {
+ AL0439_i_ChampionIndex = returnNextVal(AL0439_i_ChampionIndex);
+ }
+ if (AL0440_i_Counter == 4) {
+ return false;
+ }
+ } else {
+ if ((AL0439_i_ChampionIndex = _vm->_championMan->f286_getTargetChampionIndex(mapX, mapY, AL0439_i_TargetCell)) < 0) {
+ return false;
+ }
+ }
+ if (AL0437_ui_CreatureType == k2_CreatureTypeGiggler) {
+ f193_stealFromChampion(group, AL0439_i_ChampionIndex);
+ } else {
+ AL0440_i_Damage = f230_getChampionDamage(group, AL0439_i_ChampionIndex) + 1;
+ L0442_ps_Champion = &_vm->_championMan->_gK71_champions[AL0439_i_ChampionIndex];
+ if (AL0440_i_Damage > L0442_ps_Champion->_maximumDamageReceived) {
+ L0442_ps_Champion->_maximumDamageReceived = AL0440_i_Damage;
+ L0442_ps_Champion->_directionMaximumDamageReceived = returnOppositeDir((direction)L0438_ui_PrimaryDirectionToParty);
+ }
+ }
+ }
+ if (AL0440_i_AttackSoundOrdinal = L0441_ps_CreatureInfo->_attackSoundOrdinal) {
+ warning("MISSING CODE: F0064_SOUND_RequestPlay_CPSD");
+ }
+ return true;
+}
+
+void GroupMan::f229_setOrderedCellsToAttack(signed char* orderedCellsToAttack, int16 targetMapX, int16 targetMapY, int16 attackerMapX, int16 attackerMapY, uint16 cellSource) {
+ static signed char g23_orderedCellsToAttack[8][4] = { // @ G0023_aac_Graphic562_OrderedCellsToAttack
+ {0, 1, 3, 2}, /* Attack South from position Northwest or Southwest */
+ {1, 0, 2, 3}, /* Attack South from position Northeast or Southeast */
+ {1, 2, 0, 3}, /* Attack West from position Northwest or Northeast */
+ {2, 1, 3, 0}, /* Attack West from position Southeast or Southwest */
+ {3, 2, 0, 1}, /* Attack North from position Northwest or Southwest */
+ {2, 3, 1, 0}, /* Attack North from position Southeast or Northeast */
+ {0, 3, 1, 2}, /* Attack East from position Northwest or Northeast */
+ {3, 0, 2, 1}}; /* Attack East from position Southeast or Southwest */
+ uint16 L0557_ui_OrderedCellsToAttackIndex;
+
+
+ if (!((L0557_ui_OrderedCellsToAttackIndex = f228_getDirsWhereDestIsVisibleFromSource(targetMapX, targetMapY, attackerMapX, attackerMapY) << 1) & 0x0002)) {
+ cellSource++;
+ }
+ L0557_ui_OrderedCellsToAttackIndex += (cellSource >> 1) & 0x0001;
+ for (uint16 i = 0; i < 4; ++i)
+ orderedCellsToAttack[i] = g23_orderedCellsToAttack[L0557_ui_OrderedCellsToAttackIndex][i];
+}
+
+void GroupMan::f193_stealFromChampion(Group* group, uint16 championIndex) {
+ int16 L0391_i_Percentage;
+ uint16 L0392_ui_StealFromSlotIndex;
+ uint16 L0393_ui_Counter;
+ Thing L0394_T_Thing;
+ Champion* L0395_ps_Champion;
+ bool L0396_B_ObjectStolen;
+ static unsigned char G0394_auc_StealFromSlotIndices[8]; /* Initialized with 0 bytes by C loader */
+
+
+ L0396_B_ObjectStolen = false;
+ L0391_i_Percentage = 100 - _vm->_championMan->f311_getDexterity(L0395_ps_Champion = &_vm->_championMan->_gK71_champions[championIndex]);
+ L0393_ui_Counter = _vm->getRandomNumber(8);
+ while ((L0391_i_Percentage > 0) && !_vm->_championMan->f308_isLucky(L0395_ps_Champion, L0391_i_Percentage)) {
+ if ((L0392_ui_StealFromSlotIndex = G0394_auc_StealFromSlotIndices[L0393_ui_Counter]) == k13_ChampionSlotBackpackLine_1_1) {
+ L0392_ui_StealFromSlotIndex += _vm->getRandomNumber(17); /* Select a random slot in the backpack */
+ }
+ if (((L0394_T_Thing = L0395_ps_Champion->_slots[L0392_ui_StealFromSlotIndex]) != Thing::_none)) {
+ L0396_B_ObjectStolen = true;
+ L0394_T_Thing = _vm->_championMan->f300_getObjectRemovedFromSlot(championIndex, L0392_ui_StealFromSlotIndex);
+ if (group->_slot == Thing::_endOfList) {
+ group->_slot = L0394_T_Thing; /* BUG0_12 An object is cloned and appears at two different locations in the dungeon and/or inventory. The game may crash when interacting with this object. If a Giggler with no possessions steals an object that was previously in a chest and was not the last object in the chest then the objects that followed it are cloned. In the chest, the object is part of a linked list of objects that is not reset when the object is removed from the chest and placed in the inventory (but not in the dungeon), nor when it is stolen and added as the first Giggler possession. If the Giggler already has a possession before stealing the object then this does not create a cloned object.
+ The following statement is missing: L0394_T_Thing->Next = Thing::_endOfList;
+ This creates cloned things if L0394_T_Thing->Next is not Thing::_endOfList which is the case when the object comes from a chest in which it was not the last object */
+ } else {
+ _vm->_dungeonMan->f163_linkThingToList(L0394_T_Thing, group->_slot, kM1_MapXNotOnASquare, 0);
+ }
+ _vm->_championMan->f292_drawChampionState((ChampionIndex)championIndex);
+ }
+ ++L0393_ui_Counter;
+ L0393_ui_Counter &= 0x0007;
+ L0391_i_Percentage -= 20;
+ }
+ if (!_vm->getRandomNumber(8) || (L0396_B_ObjectStolen && _vm->getRandomNumber(2))) {
+ _vm->_groupMan->_g375_activeGroups[group->getActiveGroupIndex()]._delayFleeingFromTarget = _vm->getRandomNumber(64) + 20;
+ group->setBehaviour(k5_behavior_FLEE);
+ }
+}
+
+int16 GroupMan::f230_getChampionDamage(Group* group, uint16 champIndex) {
+ unsigned char g24_woundProbabilityIndexToWoundMask[4] = {32, 16, 8, 4}; // @ G0024_auc_Graphic562_WoundProbabilityIndexToWoundMask
+
+ Champion* L0562_ps_Champion;
+ int16 L0558_i_Multiple;
+#define AL0558_i_Attack L0558_i_Multiple
+#define AL0558_i_Damage L0558_i_Multiple
+ uint16 L0559_ui_Multiple;
+#define AL0559_ui_WoundTest L0559_ui_Multiple
+#define AL0559_ui_PoisonAttack L0559_ui_Multiple
+#define AL0559_ui_CreatureDifficulty L0559_ui_Multiple
+ uint16 L0560_ui_WoundProbabilities;
+ uint16 L0561_ui_Multiple;
+#define AL0561_ui_WoundProbabilityIndex L0561_ui_Multiple
+#define AL0561_ui_AllowedWound L0561_ui_Multiple
+ int16 L0563_i_DoubledMapDifficulty;
+ CreatureInfo L0564_s_CreatureInfo;
+
+
+ L0562_ps_Champion = &_vm->_championMan->_gK71_champions[champIndex];
+ if (champIndex >= _vm->_championMan->_g305_partyChampionCount) {
+ return 0;
+ }
+ if (!L0562_ps_Champion->_currHealth) {
+ return 0;
+ }
+ if (_vm->_championMan->_g300_partyIsSleeping) {
+ _vm->_championMan->f314_wakeUp();
+ }
+ L0563_i_DoubledMapDifficulty = _vm->_dungeonMan->_g269_currMap->_difficulty << 1;
+ L0564_s_CreatureInfo = g243_CreatureInfo[group->_type];
+ _vm->_championMan->f304_addSkillExperience(champIndex, k7_ChampionSkillParry, L0564_s_CreatureInfo.M58_getExperience());
+ if (_vm->_championMan->_g300_partyIsSleeping || (((_vm->_championMan->f311_getDexterity(L0562_ps_Champion) < (_vm->getRandomNumber(32) + L0564_s_CreatureInfo._dexterity + L0563_i_DoubledMapDifficulty - 16)) || !_vm->getRandomNumber(4)) && !_vm->_championMan->f308_isLucky(L0562_ps_Champion, 60))) {
+ if ((AL0559_ui_WoundTest = _vm->getRandomNumber(65536)) & 0x0070) {
+ AL0559_ui_WoundTest &= 0x000F;
+ L0560_ui_WoundProbabilities = L0564_s_CreatureInfo._woundProbabilities;
+ for (AL0561_ui_WoundProbabilityIndex = 0; AL0559_ui_WoundTest > (L0560_ui_WoundProbabilities & 0x000F); L0560_ui_WoundProbabilities >>= 4) {
+ AL0561_ui_WoundProbabilityIndex++;
+ }
+ AL0561_ui_AllowedWound = g24_woundProbabilityIndexToWoundMask[AL0561_ui_WoundProbabilityIndex];
+ } else {
+ AL0561_ui_AllowedWound = AL0559_ui_WoundTest & 0x0001; /* 0 (Ready hand) or 1 (action hand) */
+ }
+ if ((AL0558_i_Attack = (_vm->getRandomNumber(16) + L0564_s_CreatureInfo._attack + L0563_i_DoubledMapDifficulty) - (_vm->_championMan->f303_getSkillLevel(champIndex, k7_ChampionSkillParry) << 1)) <= 1) {
+ if (_vm->getRandomNumber(2)) {
+ goto T0230014;
+ }
+ AL0558_i_Attack = _vm->getRandomNumber(4) + 2;
+ }
+ AL0558_i_Attack >>= 1;
+ AL0558_i_Attack += _vm->getRandomNumber(AL0558_i_Attack) + _vm->getRandomNumber(4);
+ AL0558_i_Attack += _vm->getRandomNumber(AL0558_i_Attack);
+ AL0558_i_Attack >>= 2;
+ AL0558_i_Attack += _vm->getRandomNumber(4) + 1;
+ if (_vm->getRandomNumber(2)) {
+ AL0558_i_Attack -= _vm->getRandomNumber((AL0558_i_Attack >> 1) + 1) - 1;
+ }
+ if (AL0558_i_Damage = _vm->_championMan->f321_addPendingDamageAndWounds_getDamage(champIndex, AL0558_i_Attack, AL0561_ui_AllowedWound, L0564_s_CreatureInfo._attackType)) {
+ warning("MISSING CODE: F0064_SOUND_RequestPlay_CPSD");
+ if ((AL0559_ui_PoisonAttack = L0564_s_CreatureInfo._poisonAttack) && _vm->getRandomNumber(2) && ((AL0559_ui_PoisonAttack = _vm->_championMan->f307_getStatisticAdjustedAttack(L0562_ps_Champion, k4_ChampionStatVitality, AL0559_ui_PoisonAttack)) >= 0)) {
+ _vm->_championMan->f322_championPoison(champIndex, AL0559_ui_PoisonAttack);
+ }
+ return AL0558_i_Damage;
+ }
+ }
+T0230014:
+ return 0;
+}
+
+void GroupMan::f187_dropMovingCreatureFixedPossession(Thing thing, int16 mapX, int16 mapY) {
+ Group* L0363_ps_Group;
+ int16 L0364_i_CreatureType;
+
+
+ if (_g391_dropMovingCreatureFixedPossCellCount) {
+ L0363_ps_Group = (Group*)_vm->_dungeonMan->f156_getThingData(thing);
+ L0364_i_CreatureType = L0363_ps_Group->_type;
+ while (_g391_dropMovingCreatureFixedPossCellCount) {
+ f186_dropCreatureFixedPossessions(L0364_i_CreatureType, mapX, mapY, _g392_dropMovingCreatureFixedPossessionsCell[--_g391_dropMovingCreatureFixedPossCellCount], k2_soundModePlayOneTickLater);
+ }
+ }
+}
+
+void GroupMan::f180_startWanedring(int16 mapX, int16 mapY) {
+ Group* L0332_ps_Group;
+ TimelineEvent L0333_s_Event;
+
+
+ L0332_ps_Group = (Group*)_vm->_dungeonMan->f156_getThingData(_vm->_groupMan->f175_groupGetThing(mapX, mapY));
+ if (L0332_ps_Group->getBehaviour() >= k4_behavior_USELESS) {
+ L0332_ps_Group->setBehaviour(k0_behavior_WANDER);
+ }
+ M33_setMapAndTime(L0333_s_Event._mapTime, _vm->_dungeonMan->_g272_currMapIndex, (_vm->_g313_gameTime + 1));
+ L0333_s_Event._type = k37_TMEventTypeUpdateBehaviourGroup;
+ L0333_s_Event._priority = 255 - g243_CreatureInfo[L0332_ps_Group->_type]._movementTicks; /* The fastest creatures (with small MovementTicks value) get higher event priority */
+ L0333_s_Event._C._ticks = 0;
+ L0333_s_Event._B._location._mapX = mapX;
+ L0333_s_Event._B._location._mapY = mapY;
+ _vm->_timeline->f238_addEventGetEventIndex(&L0333_s_Event);
+}
+
+void GroupMan::f183_addActiveGroup(Thing thing, int16 mapX, int16 mapY) {
+ uint16 L0339_ui_CreatureIndex;
+ Group* L0340_ps_Group;
+ ActiveGroup* L0341_ps_ActiveGroup;
+ int16 L0344_i_ActiveGroupIndex;
+
+
+ L0341_ps_ActiveGroup = _vm->_groupMan->_g375_activeGroups;
+ L0344_i_ActiveGroupIndex = 0;
+ while (L0341_ps_ActiveGroup->_groupThingIndex >= 0) {
+ if (++L0344_i_ActiveGroupIndex >= _vm->_groupMan->_g376_maxActiveGroupCount) {
+ return; /* BUG0_11 Data corruption in memory. Each group located on the same map as the party has additional associated data but there is only provision for 60 instances (_vm->_groupMan->_g376_maxActiveGroupCount). If there are more groups at the same time then some of them do not get their instance and when the game accesses this information it will corrupt other data in memory (either the instance of another group, parts of the timeline or events). This situation cannot occur in the original Dungeon Master and Chaos Strikes Back dungeons for the following reasons (but it may occur in custom dungeons if they are not designed carefully): there is no map with already more than 60 groups in the original dungeons and none of the following 3 possible ways to move a group into a map can increase the number of instances above the maximum of 60:
+ - A group generator sensor is triggered: the game never generates a group on the party map if there are less than 5 instances available. This limits the actual number of groups on a map to 55 in most cases.
+ - A group falls through a pit from the map above (the creature type must be allowed on the target map): a group will never willingly move to an open pit square. It may move to a closed pit square and fall if the pit is then open (either automatically or triggered by the party on the map below). There are no such pits in the original dungeons.
+ - A group is teleported from another map (the creature type must be allowed on the target map): in the original dungeons, all teleporters whose scope include groups and target another map are either inaccessible to groups or the groups are not allowed on the target map. The only exception is for some Gigglers in the Chaos Strikes Back dungeon but there are not enough to use the 5 reserved instances.
+
+ This code returns immediately if all ACTIVE_GROUP entries are already in use, which avoids an out of bounds access into _vm->_groupMan->_g375_activeGroups below (through L0341_ps_ActiveGroup). However in this case the specified group ends up without an associated ACTIVE_GROUP structure which is assumed everywhere in the code to be present for groups on the same map as the party. If there are more than 60 groups on the party map at any given time then this will corrupt memory (in _vm->_timeline->_g370_events and _vm->_timeline->_g371_timeline allocated in _vm->_timeline->f233_initTimeline before _vm->_groupMan->_g375_activeGroups) because of read and write operations at incorrect memory addresses (the 'Cells' value of the GROUP will be used as an index in _vm->_groupMan->_g375_activeGroups even though that value was not replaced by the index of an ACTIVE_GROUP in this function) */
+ }
+ L0341_ps_ActiveGroup++;
+ }
+ _g377_currActiveGroupCount++;
+ L0340_ps_Group = ((Group*)_vm->_dungeonMan->_g284_thingData[k4_GroupThingType]) + (L0341_ps_ActiveGroup->_groupThingIndex = (thing).getType());
+ L0341_ps_ActiveGroup->_cells = L0340_ps_Group->_cells;
+ L0340_ps_Group->getActiveGroupIndex() = L0344_i_ActiveGroupIndex;
+ L0341_ps_ActiveGroup->_priorMapX = L0341_ps_ActiveGroup->_homeMapX = mapX;
+ L0341_ps_ActiveGroup->_priorMapY = L0341_ps_ActiveGroup->_homeMapY = mapY;
+ L0341_ps_ActiveGroup->_lastMoveTime = _vm->_g313_gameTime - 127;
+ L0339_ui_CreatureIndex = L0340_ps_Group->getCount();
+ do {
+ L0341_ps_ActiveGroup->_directions = (direction)f178_getGroupValueUpdatedWithCreatureValue(L0341_ps_ActiveGroup->_directions, L0339_ui_CreatureIndex, L0340_ps_Group->getDir());
+ L0341_ps_ActiveGroup->_aspect[L0339_ui_CreatureIndex] = 0;
+ } while (L0339_ui_CreatureIndex--);
+ f179_getCreatureAspectUpdateTime(L0341_ps_ActiveGroup, kM1_wholeCreatureGroup, false);
+}
+
+void GroupMan::f184_removeActiveGroup(uint16 activeGroupIndex) {
+ ActiveGroup* L0347_ps_ActiveGroup;
+ Group* L0348_ps_Group;
+
+
+ if ((activeGroupIndex > _vm->_groupMan->_g376_maxActiveGroupCount) || (_vm->_groupMan->_g375_activeGroups[activeGroupIndex]._groupThingIndex < 0)) {
+ return;
+ }
+ L0347_ps_ActiveGroup = &_vm->_groupMan->_g375_activeGroups[activeGroupIndex];
+ L0348_ps_Group = &((Group*)_vm->_dungeonMan->_g284_thingData[k4_GroupThingType])[L0347_ps_ActiveGroup->_groupThingIndex];
+ _g377_currActiveGroupCount--;
+ L0348_ps_Group->_cells = L0347_ps_ActiveGroup->_cells;
+ L0348_ps_Group->setDir(M21_normalizeModulo4(L0347_ps_ActiveGroup->_directions));
+ if (L0348_ps_Group->getBehaviour() >= k4_behavior_USELESS) {
+ L0348_ps_Group->setBehaviour(k0_behavior_WANDER);
+ }
+ L0347_ps_ActiveGroup->_groupThingIndex = -1;
+}
}
diff --git a/engines/dm/group.h b/engines/dm/group.h
index 76cb8b2649..151594a1d3 100644
--- a/engines/dm/group.h
+++ b/engines/dm/group.h
@@ -32,7 +32,10 @@
#include "dm.h"
namespace DM {
-// this doesn't seem to be used anywhere at all
+ class TimelineEvent;
+ class CreatureInfo;
+
+ // this doesn't seem to be used anywhere at all
/* Creature types */
enum CreatureType {
k0_CreatureTypeGiantScorpionScorpion = 0, // @ C00_CREATURE_GIANT_SCORPION_SCORPION
@@ -107,8 +110,8 @@ class Group {
public:
Thing _nextThing;
Thing _slot;
- byte _type;
- byte _cells;
+ uint16 _type;
+ uint16 _cells;
uint16 _health[4];
private:
uint16 _flags;
@@ -121,20 +124,57 @@ public:
_health[3] = rawDat[7];
}
- byte &getActiveGroupIndex() { return _cells; }
+ byte &getActiveGroupIndex() { return *(byte*)&_cells; }
uint16 getBehaviour() { return _flags & 0xF; }
+ uint16 setBehaviour(uint16 val) { _flags = (_flags & ~0xF) | (val & 0xF); return (val & 0xF); }
uint16 getCount() { return (_flags >> 5) & 0x3; }
+ void setCount(uint16 val) { _flags = (_flags & ~(0x3 << 5)) | ((val & 0x3) << 5); }
direction getDir() { return (direction)((_flags >> 8) & 0x3); }
+ void setDir(uint16 val) { _flags = (_flags & ~(0x3 << 8)) | ((val & 0x3) << 8); }
uint16 getDoNotDiscard() { return (_flags >> 10) & 0x1; }
}; // @ GROUP
+#define k0_behavior_WANDER 0 // @ C0_BEHAVIOR_WANDER
+#define k2_behavior_USELESS 2 // @ C2_BEHAVIOR_USELESS
+#define k3_behavior_USELESS 3 // @ C3_BEHAVIOR_USELESS
+#define k4_behavior_USELESS 4 // @ C4_BEHAVIOR_USELESS
+#define k5_behavior_FLEE 5 // @ C5_BEHAVIOR_FLEE
+#define k6_behavior_ATTACK 6 // @ C6_BEHAVIOR_ATTACK
+#define k7_behavior_APPROACH 7 // @ C7_BEHAVIOR_APPROACH
+
+#define k15_immuneToFear 15 // @ C15_IMMUNE_TO_FEAR
+
+#define k255_immobile 255 // @ C255_IMMOBILE
+#define kM1_wholeCreatureGroup -1 // @ CM1_WHOLE_CREATURE_GROUP
+
+#define k34_D13_soundCount 34 // @ D13_SOUND_COUNT
+
+int32 M32_setTime(int32 &map_time, int32 time); // @ M32_SET_TIME
+
class GroupMan {
DMEngine *_vm;
+ byte _g392_dropMovingCreatureFixedPossessionsCell[4]; // @ G0392_auc_DropMovingCreatureFixedPossessionsCells
+ uint16 _g391_dropMovingCreatureFixedPossCellCount; // @ G0391_ui_DropMovingCreatureFixedPossessionsCellCount
+ uint16 _g386_fluxCageCount; // @ G0386_ui_FluxCageCount
+ int16 _g385_fluxCages[4]; // @ G0385_ac_FluxCages
+ int16 _g378_currentGroupMapX; // @ G0378_i_CurrentGroupMapX
+ int16 _g379_currentGroupMapY; // @ G0379_i_CurrentGroupMapY
+ Thing _g380_currGroupThing; // @ G0380_T_CurrentGroupThing
+ int16 _g384_groupMovementTestedDirections[4]; // @ G0384_auc_GroupMovementTestedDirections
+ uint16 _g381_currGroupDistanceToParty; // @ G0381_ui_CurrentGroupDistanceToParty
+ int16 _g382_currGroupPrimaryDirToParty; // @ G0382_i_CurrentGroupPrimaryDirectionToParty
+ int16 _g383_currGroupSecondaryDirToParty; // @ G0383_i_CurrentGroupSecondaryDirectionToParty
+
+ Thing _g388_groupMovementBlockedByGroupThing; // @ G0388_T_GroupMovementBlockedByGroupThing
+ bool _g389_groupMovementBlockedByDoor; // @ G0389_B_GroupMovementBlockedByDoor
+ bool _g390_groupMovementBlockedByParty; // @ G0390_B_GroupMovementBlockedByParty
+ bool _g387_groupMovBlockedByWallStairsPitFakeWalFluxCageTeleporter; // @ G0387_B_GroupMovementBlockedByWallStairsPitFakeWallFluxcageTeleporter
public:
uint16 _g376_maxActiveGroupCount = 60; // @ G0376_ui_MaximumActiveGroupCount
ActiveGroup *_g375_activeGroups; // @ G0375_ps_ActiveGroups
+ uint16 _g377_currActiveGroupCount; // @ G0377_ui_CurrentActiveGroupCount
GroupMan(DMEngine *vm);
~GroupMan();
void f196_initActiveGroups(); // @ F0196_GROUP_InitializeActiveGroups
@@ -142,6 +182,61 @@ public:
uint16 f147_getGroupDirections(Group *group, int16 mapIndex); // @ F0147_DUNGEON_GetGroupDirections
int16 f176_getCreatureOrdinalInCell(Group *group, uint16 cell); // @ F0176_GROUP_GetCreatureOrdinalInCell
uint16 M50_getCreatureValue(uint16 groupVal, uint16 creatureIndex); // @ M50_CREATURE_VALUE
+ void f188_dropGroupPossessions(int16 mapX, int16 mapY, Thing groupThing, int16 mode); // @ F0188_GROUP_DropGroupPossessions
+ void f186_dropCreatureFixedPossessions(uint16 creatureType, int16 mapX, int16 mapY, uint16 cell,
+ int16 mode); // @ F0186_GROUP_DropCreatureFixedPossessions
+ int16 f228_getDirsWhereDestIsVisibleFromSource(int16 srcMapX, int16 srcMapY,
+ int16 destMapX, int16 destMapY); // @ F0228_GROUP_GetDirectionsWhereDestinationIsVisibleFromSource
+ bool f227_isDestVisibleFromSource(uint16 dir, int16 srcMapX, int16 srcMapY, int16 destMapX,
+ int16 destMapY); // @ F0227_GROUP_IsDestinationVisibleFromSource
+ bool f232_groupIsDoorDestoryedByAttack(uint16 mapX, uint16 mapY, int16 attack,
+ bool magicAttack, int16 ticks); // @ F0232_GROUP_IsDoorDestroyedByAttack
+ Thing f175_groupGetThing(int16 mapX, int16 mapY); // @ F0175_GROUP_GetThing
+ int16 f190_groupGetDamageCreatureOutcome(Group *group, uint16 creatureIndex,
+ int16 mapX, int16 mapY, int16 damage, bool notMoving); // @ F0190_GROUP_GetDamageCreatureOutcome
+ void f189_delete(int16 mapX, int16 mapY); // @ F0189_GROUP_Delete
+ void f181_groupDeleteEvents(int16 mapX, int16 mapY); // @ F0181_GROUP_DeleteEvents
+ uint16 f178_getGroupValueUpdatedWithCreatureValue(uint16 groupVal, uint16 creatureIndex, uint16 creatreVal); // @ F0178_GROUP_GetGroupValueUpdatedWithCreatureValue
+ int16 f191_getDamageAllCreaturesOutcome(Group *group, int16 mapX, int16 mapY, int16 attack, bool notMoving); // @ F0191_GROUP_GetDamageAllCreaturesOutcome
+ int16 f192_groupGetResistanceAdjustedPoisonAttack(uint16 creatreType, int16 poisonAttack); // @ F0192_GROUP_GetResistanceAdjustedPoisonAttack
+ void f209_processEvents29to41(int16 eventMapX, int16 eventMapY, int16 eventType, uint16 ticks); // @ F0209_GROUP_ProcessEvents29to41
+ bool f202_isMovementPossible(CreatureInfo *creatureInfo, int16 mapX, int16 mapY,
+ uint16 dir, bool allowMovementOverImaginaryPitsAndFakeWalls); // @ F0202_GROUP_IsMovementPossible
+ int16 f226_getDistanceBetweenSquares(int16 srcMapX, int16 srcMapY, int16 destMapX,
+ int16 destMapY); // @ F0226_GROUP_GetDistanceBetweenSquares
+
+ int16 f200_groupGetDistanceToVisibleParty(Group *group, int16 creatureIndex, int16 mapX, int16 mapY); // @ F0200_GROUP_GetDistanceToVisibleParty
+ int16 f199_getDistanceBetweenUnblockedSquares(int16 srcMapX, int16 srcMapY,
+ int16 destMapX, int16 destMapY, bool (GroupMan::*isBlocked)(uint16, uint16)); // @ F0199_GROUP_GetDistanceBetweenUnblockedSquares
+ bool f197_isViewPartyBlocked(uint16 mapX, uint16 mapY); // @ F0197_GROUP_IsViewPartyBlocked
+ int32 f179_getCreatureAspectUpdateTime(ActiveGroup *activeGroup, int16 creatureIndex,
+ bool isAttacking); // @ F0179_GROUP_GetCreatureAspectUpdateTime
+ void f205_setDirection(ActiveGroup *activeGroup, int16 dir, int16 creatureIndex, bool twoHalfSquareSizedCreatures); // @ F0205_GROUP_SetDirection
+ void f208_groupAddEvent(TimelineEvent *event, uint32 time); // @ F0208_GROUP_AddEvent
+ int16 f201_getSmelledPartyPrimaryDirOrdinal(CreatureInfo *creatureInfo, int16 mapY, int16 mapX); // @ F0201_GROUP_GetSmelledPartyPrimaryDirectionOrdinal
+ bool f198_isSmellPartyBlocked(uint16 mapX, uint16 mapY); // @ F0198_GROUP_IsSmellPartyBlocked
+ int16 f203_getFirstPossibleMovementDirOrdinal(CreatureInfo *info, int16 mapX, int16 mapY,
+ bool allowMovementOverImaginaryPitsAndFakeWalls); // @ F0203_GROUP_GetFirstPossibleMovementDirectionOrdinal
+ void f206_groupSetDirGroup(ActiveGroup *activeGroup, int16 dir, int16 creatureIndex,
+ int16 creatureSize); // @ F0206_GROUP_SetDirectionGroup
+ void f182_stopAttacking(ActiveGroup *group, int16 mapX, int16 mapY);// @ F0182_GROUP_StopAttacking
+ bool f204_isArchenemyDoubleMovementPossible(CreatureInfo *info, int16 mapX, int16 mapY, uint16 dir); // @ F0204_GROUP_IsArchenemyDoubleMovementPossible
+ bool f207_isCreatureAttacking(Group *group, int16 mapX, int16 mapY, uint16 creatureIndex); // @ F0207_GROUP_IsCreatureAttacking
+ void f229_setOrderedCellsToAttack(signed char * orderedCellsToAttack, int16 targetMapX,
+ int16 targetMapY, int16 attackerMapX, int16 attackerMapY, uint16 cellSource); // @ F0229_GROUP_SetOrderedCellsToAttack
+ void f193_stealFromChampion(Group *group, uint16 championIndex); // @ F0193_GROUP_StealFromChampion
+ int16 f230_getChampionDamage(Group *group, uint16 champIndex); // @ F0230_GROUP_GetChampionDamage
+ void f187_dropMovingCreatureFixedPossession(Thing thing, int16 mapX, int16 mapY); // @ F0187_GROUP_DropMovingCreatureFixedPossessions
+ void f180_startWanedring(int16 mapX, int16 mapY); // @ F0180_GROUP_StartWandering
+ void f183_addActiveGroup(Thing thing, int16 mapX, int16 mapY); // @ F0183_GROUP_AddActiveGroup
+ void f184_removeActiveGroup(uint16 activeGroupIndex); // @ F0184_GROUP_RemoveActiveGroup
+
+
+
+
+
+
+
};
diff --git a/engines/dm/inventory.cpp b/engines/dm/inventory.cpp
index 1583c6f994..b8b0ba8a60 100644
--- a/engines/dm/inventory.cpp
+++ b/engines/dm/inventory.cpp
@@ -82,7 +82,7 @@ void InventoryMan::f355_toggleInventory(ChampionIndex championIndex) {
_vm->_menuMan->f395_drawMovementArrows();
em._g442_secondaryMouseInput = g448_SecondaryMouseInput_Movement;
warning("MISSING CODE: set G0444_ps_SecondaryKeyboardInput");
- warning("MISSING CODE: F0357_COMMAND_DiscardAllInput");
+ _vm->_eventMan->f357_discardAllInput();
return;
}
}
@@ -117,7 +117,7 @@ void InventoryMan::f355_toggleInventory(ChampionIndex championIndex) {
em._g598_mousePointerBitmapUpdated = true;
em._g442_secondaryMouseInput = g449_SecondaryMouseInput_ChampionInventory;
warning("MISSING CODE: set G0444_ps_SecondaryKeyboardInput");
- warning("MISSING CODE: F0357_COMMAND_DiscardAllInput");
+ _vm->_eventMan->f357_discardAllInput();
}
void InventoryMan::f354_drawStatusBoxPortrait(ChampionIndex championIndex) {
@@ -555,4 +555,92 @@ void InventoryMan::f342_drawPanelObject(Thing thingToDraw, bool pressingEye) {
f339_drawPanelArrowOrEye(pressingEye);
}
+
+void InventoryMan::f337_setDungeonViewPalette() {
+ int16 L1036_i_TotalLightAmount;
+ uint16 L1037_ui_TorchLightAmountMultiplier;
+ int16 L1038_i_Counter;
+ uint16 L1039_ui_Multiple;
+#define AL1039_ui_SlotIndex L1039_ui_Multiple
+#define AL1039_ui_PaletteIndex L1039_ui_Multiple
+#define AL1039_ui_Counter L1039_ui_Multiple
+ int16* L1040_pi_Multiple;
+#define AL1040_pi_TorchLightPower L1040_pi_Multiple
+#define AL1040_pi_LightAmount L1040_pi_Multiple
+ int16* L1041_pi_TorchLightPower;
+ Weapon* L1042_ps_Weapon;
+ Champion* L1043_ps_Champion;
+ uint16 L1044_ui_Multiple;
+#define AL1044_T_Thing L1044_ui_Multiple
+#define AL1044_ui_TorchLightPower L1044_ui_Multiple
+ int16 L1045_ai_TorchesLightPower[8];
+
+ int16 g40_palIndexToLightAmmount[6] = {99, 75, 50, 25, 1, 0}; // @ G0040_ai_Graphic562_PaletteIndexToLightAmount
+
+ if (_vm->_dungeonMan->_g269_currMap->_difficulty == 0) {
+ _vm->_displayMan->_g304_dungeonViewPaletteIndex = 0; /* Brightest color palette index */
+ } else {
+ /* Get torch light power from both hands of each champion in the party */
+ L1038_i_Counter = 4; /* BUG0_01 Coding error without consequence. The hands of four champions are inspected even if there are less champions in the party. No consequence as the data in unused champions is set to 0 and _vm->_objectMan->f32_getObjectType then returns -1 */
+ L1043_ps_Champion = _vm->_championMan->_gK71_champions;
+ AL1040_pi_TorchLightPower = L1045_ai_TorchesLightPower;
+ while (L1038_i_Counter--) {
+ AL1039_ui_SlotIndex = k1_ChampionSlotActionHand + 1;
+ while (AL1039_ui_SlotIndex--) {
+ if ((_vm->_objectMan->f32_getObjectType(Thing(AL1044_T_Thing = L1043_ps_Champion->_slots[AL1039_ui_SlotIndex].toUint16())) >= k4_IconIndiceWeaponTorchUnlit) &&
+ (_vm->_objectMan->f32_getObjectType(Thing(AL1044_T_Thing = L1043_ps_Champion->_slots[AL1039_ui_SlotIndex].toUint16())) <= k7_IconIndiceWeaponTorchLit)) {
+ L1042_ps_Weapon = (Weapon*)_vm->_dungeonMan->f156_getThingData(Thing(AL1044_T_Thing));
+ *AL1040_pi_TorchLightPower = L1042_ps_Weapon->getChargeCount();
+ } else {
+ *AL1040_pi_TorchLightPower = 0;
+ }
+ AL1040_pi_TorchLightPower++;
+ }
+ L1043_ps_Champion++;
+ }
+ /* Sort torch light power values so that the four highest values are in the first four entries in the array L1045_ai_TorchesLightPower in decreasing order. The last four entries contain the smallest values but they are not sorted */
+ AL1040_pi_TorchLightPower = L1045_ai_TorchesLightPower;
+ AL1039_ui_Counter = 0;
+ while (AL1039_ui_Counter != 4) {
+ L1038_i_Counter = 7 - AL1039_ui_Counter;
+ L1041_pi_TorchLightPower = &L1045_ai_TorchesLightPower[AL1039_ui_Counter + 1];
+ while (L1038_i_Counter--) {
+ if (*L1041_pi_TorchLightPower > *AL1040_pi_TorchLightPower) {
+ AL1044_ui_TorchLightPower = *L1041_pi_TorchLightPower;
+ *L1041_pi_TorchLightPower = *AL1040_pi_TorchLightPower;
+ *AL1040_pi_TorchLightPower = AL1044_ui_TorchLightPower;
+ }
+ L1041_pi_TorchLightPower++;
+ }
+ AL1040_pi_TorchLightPower++;
+ AL1039_ui_Counter++;
+ }
+ /* Get total light amount provided by the four torches with the highest light power values and by the fifth torch in the array which may be any one of the four torches with the smallest ligh power values */
+ L1037_ui_TorchLightAmountMultiplier = 6;
+ AL1039_ui_Counter = 5;
+ L1036_i_TotalLightAmount = 0;
+ AL1040_pi_TorchLightPower = L1045_ai_TorchesLightPower;
+ while (AL1039_ui_Counter--) {
+ if (*AL1040_pi_TorchLightPower) {
+ L1036_i_TotalLightAmount += (g39_LightPowerToLightAmount[*AL1040_pi_TorchLightPower] << L1037_ui_TorchLightAmountMultiplier) >> 6;
+ L1037_ui_TorchLightAmountMultiplier = MAX(0, L1037_ui_TorchLightAmountMultiplier - 1);
+ }
+ AL1040_pi_TorchLightPower++;
+ }
+ L1036_i_TotalLightAmount += _vm->_championMan->_g407_party._magicalLightAmount;
+ /* Select palette corresponding to the total light amount */
+ AL1040_pi_LightAmount = g40_palIndexToLightAmmount;
+ if (L1036_i_TotalLightAmount > 0) {
+ AL1039_ui_PaletteIndex = 0; /* Brightest color palette index */
+ while (*AL1040_pi_LightAmount++ > L1036_i_TotalLightAmount) {
+ AL1039_ui_PaletteIndex++;
+ }
+ } else {
+ AL1039_ui_PaletteIndex = 5; /* Darkest color palette index */
+ }
+ _vm->_displayMan->_g304_dungeonViewPaletteIndex = AL1039_ui_PaletteIndex;
+ }
+
+ _vm->_displayMan->_g342_refreshDungeonViewPaleteRequested = true;
+}
}
diff --git a/engines/dm/inventory.h b/engines/dm/inventory.h
index df429af70b..aa9b2150d4 100644
--- a/engines/dm/inventory.h
+++ b/engines/dm/inventory.h
@@ -78,6 +78,7 @@ public:
void f335_drawPanelObjectDescriptionString(char *descString); // @ F0335_INVENTORY_DrawPanel_ObjectDescriptionString
void f339_drawPanelArrowOrEye(bool pressingEye); // @ F0339_INVENTORY_DrawPanel_ArrowOrEye
void f342_drawPanelObject(Thing thingToDraw, bool pressingEye); // @ F0342_INVENTORY_DrawPanel_Object
+ void f337_setDungeonViewPalette(); // @ F0337_INVENTORY_SetDungeonViewPalette
};
diff --git a/engines/dm/menus.cpp b/engines/dm/menus.cpp
index 376b629d28..66b293ac3e 100644
--- a/engines/dm/menus.cpp
+++ b/engines/dm/menus.cpp
@@ -32,6 +32,7 @@
#include "objectman.h"
#include "inventory.h"
#include "text.h"
+#include "eventman.h"
namespace DM {
@@ -371,4 +372,33 @@ void MenuMan::f394_setMagicCasterAndDrawSpellArea(int16 champIndex) {
dispMan.f132_blitToBitmap(_gK72_bitmapSpellAreaLine, dispMan._g348_bitmapScreen, gK76_BoxSpellAreaLine3, 0, 0, 48, k160_byteWidthScreen, k255_ColorNoTransparency);
warning("MISSING CODE: F0078_MOUSE_ShowPointer");
}
+
+void MenuMan::f457_drawEnabledMenus() {
+ int16 L1462_i_Multiple;
+#define AL1462_i_MagicCasterChampionIndex L1462_i_Multiple
+#define AL1462_i_InventoryChampionOrdinal L1462_i_Multiple
+
+
+ if (_vm->_championMan->_g300_partyIsSleeping) {
+ _vm->_eventMan->f379_drawSleepScreen();
+ _vm->_displayMan->f97_drawViewport(k0_viewportNotDungeonView);
+ } else {
+ AL1462_i_MagicCasterChampionIndex = _vm->_championMan->_g514_magicCasterChampionIndex;
+ _vm->_championMan->_g514_magicCasterChampionIndex = kM1_ChampionNone; /* Force next function to draw the spell area */
+ _vm->_menuMan->f394_setMagicCasterAndDrawSpellArea(AL1462_i_MagicCasterChampionIndex);
+ if (!_vm->_championMan->_g506_actingChampionOrdinal) {
+ _vm->_menuMan->_g509_actionAreaContainsIcons = true;
+ }
+ _vm->_menuMan->f387_drawActionArea();
+ if (AL1462_i_InventoryChampionOrdinal = _vm->_inventoryMan->_g432_inventoryChampionOrdinal) {
+ _vm->_inventoryMan->_g432_inventoryChampionOrdinal = _vm->M0_indexToOrdinal(kM1_ChampionNone);
+ _vm->_inventoryMan->f355_toggleInventory((ChampionIndex)_vm->M1_ordinalToIndex(AL1462_i_InventoryChampionOrdinal));
+ } else {
+ _vm->_displayMan->f98_drawFloorAndCeiling();
+ _vm->_menuMan->f395_drawMovementArrows();
+ }
+ _vm->_eventMan->f69_setMousePointer();
+ }
+}
+
}
diff --git a/engines/dm/menus.h b/engines/dm/menus.h
index ef5f86d153..4e0324205b 100644
--- a/engines/dm/menus.h
+++ b/engines/dm/menus.h
@@ -71,6 +71,7 @@ public:
void f393_drawSpellAreaControls(ChampionIndex champIndex); // @ F0393_MENUS_DrawSpellAreaControls
void f392_buildSpellAreaLine(int16 spellAreaBitmapLine);// @ F0392_MENUS_BuildSpellAreaLine
void f394_setMagicCasterAndDrawSpellArea(int16 champIndex); // @ F0394_MENUS_SetMagicCasterAndDrawSpellArea
+ void f457_drawEnabledMenus(); // @ F0457_START_DrawEnabledMenus_CPSF
};
}
diff --git a/engines/dm/module.mk b/engines/dm/module.mk
index a1460ae944..50a8364cd4 100644
--- a/engines/dm/module.mk
+++ b/engines/dm/module.mk
@@ -42,6 +42,7 @@ MODULE_OBJS := \
menus.o \
movesens.o \
objectman.o \
+ projexpl.o \
text.o \
timeline.o
diff --git a/engines/dm/movesens.cpp b/engines/dm/movesens.cpp
index 9fab510ecf..70f8cc8220 100644
--- a/engines/dm/movesens.cpp
+++ b/engines/dm/movesens.cpp
@@ -31,6 +31,10 @@
#include "inventory.h"
#include "dungeonman.h"
#include "objectman.h"
+#include "timeline.h"
+#include "group.h"
+#include "projexpl.h"
+#include "text.h"
namespace DM {
@@ -38,158 +42,1042 @@ namespace DM {
MovesensMan::MovesensMan(DMEngine* vm) : _vm(vm) {}
bool MovesensMan::f275_sensorIsTriggeredByClickOnWall(int16 mapX, int16 mapY, uint16 cellParam) {
- ChampionMan &champMan = *_vm->_championMan;
- DungeonMan &dunMan = *_vm->_dungeonMan;
- ObjectMan &objMan = *_vm->_objectMan;
-
-
- bool atLeastOneSensorWasTriggered = false;
- Thing leaderHandObject = champMan._g414_leaderHandObject;
- int16 sensorCountToProcessPerCell[4];
- uint16 cell;
- for (cell = k0_CellNorthWest; cell < k3_CellSouthWest; ++cell) {
- sensorCountToProcessPerCell[cell] = 0;
- }
- Thing squareFirstThing;
- Thing thingBeingProcessed = squareFirstThing = dunMan.f161_getSquareFirstThing(mapX, mapY);
- ThingType thingType;
- while (thingBeingProcessed != Thing::_endOfList) {
- thingType = thingBeingProcessed.getType();
- if (thingType == k3_SensorThingType) {
- sensorCountToProcessPerCell[thingBeingProcessed.getCell()]++;
- } else if (thingType >= k4_GroupThingType) {
- break;
+ Thing L0750_T_ThingBeingProcessed;
+ uint16 L0751_ui_ThingType;
+ uint16 L0752_ui_Cell;
+ bool L0753_B_DoNotTriggerSensor;
+ Thing* L0754_ps_Generic;
+ Sensor* L0755_ps_Sensor;
+ int16 L0756_i_SensorEffect;
+ uint16 L0757_ui_SensorType;
+ int16 L0758_i_SensorData;
+ bool L0759_B_AtLeastOneSensorWasTriggered;
+ int16 L0760_ai_SensorCountToProcessPerCell[4];
+ Thing L0761_T_LeaderHandObject;
+ Thing L0762_T_ThingOnSquare;
+ Thing L0763_T_LastProcessedThing;
+ Thing L0764_T_SquareFirstThing;
+ Sensor* L0765_ps_Sensor;
+
+
+ L0759_B_AtLeastOneSensorWasTriggered = false;
+ L0761_T_LeaderHandObject = _vm->_championMan->_g414_leaderHandObject;
+ for (L0752_ui_Cell = k0_CellNorthWest; L0752_ui_Cell < k3_CellSouthWest + 1; L0752_ui_Cell++) {
+ L0760_ai_SensorCountToProcessPerCell[L0752_ui_Cell] = 0;
+ }
+ L0764_T_SquareFirstThing = L0750_T_ThingBeingProcessed = _vm->_dungeonMan->f161_getSquareFirstThing(mapX, mapY);
+ while (L0750_T_ThingBeingProcessed != Thing::_endOfList) {
+ if ((L0751_ui_ThingType = (L0750_T_ThingBeingProcessed).getType()) == k3_SensorThingType) {
+ L0760_ai_SensorCountToProcessPerCell[(L0750_T_ThingBeingProcessed).getCell()]++;
+ } else {
+ if (L0751_ui_ThingType >= k4_GroupThingType)
+ break;
}
- thingBeingProcessed = dunMan.f159_getNextThing(thingBeingProcessed);
+ L0750_T_ThingBeingProcessed = _vm->_dungeonMan->f159_getNextThing(L0750_T_ThingBeingProcessed);
}
- Thing lastProcessedThing = thingBeingProcessed = squareFirstThing;
-
- while (thingBeingProcessed != Thing::_endOfList) {
- thingType = thingBeingProcessed.getType();
- if (thingType == k3_SensorThingType) {
- cell = thingBeingProcessed.getCell();
- sensorCountToProcessPerCell[cell]--;
- Sensor *sensor = (Sensor*)dunMan.f156_getThingData(thingBeingProcessed); // IF YOU CHECK ME, I'LL CALL THE COPS!
- SensorType sensorType = sensor->getType();
- if (sensorType == k0_SensorDisabled)
+ L0763_T_LastProcessedThing = L0750_T_ThingBeingProcessed = L0764_T_SquareFirstThing;
+ while (L0750_T_ThingBeingProcessed != Thing::_endOfList) {
+ if ((L0751_ui_ThingType = (L0750_T_ThingBeingProcessed).getType()) == k3_SensorThingType) {
+ L0760_ai_SensorCountToProcessPerCell[L0752_ui_Cell = (L0750_T_ThingBeingProcessed).getCell()]--;
+ L0755_ps_Sensor = (Sensor*)_vm->_dungeonMan->f156_getThingData(L0750_T_ThingBeingProcessed);
+ if ((L0757_ui_SensorType = (L0755_ps_Sensor)->getType()) == k0_SensorDisabled)
goto T0275058_ProceedToNextThing;
- if ((champMan._g411_leaderIndex == kM1_ChampionNone) && (sensorType != k127_SensorWallChampionPortrait))
+ if ((_vm->_championMan->_g411_leaderIndex == kM1_ChampionNone) && (L0757_ui_SensorType != k127_SensorWallChampionPortrait))
goto T0275058_ProceedToNextThing;
- if (cell != cellParam)
+ if (L0752_ui_Cell != cellParam)
goto T0275058_ProceedToNextThing;
- int16 sensorData = sensor->getData();
- int16 sensorEffect = sensor->getEffectA();
- bool doNotTriggerSensor;
- switch (sensorType) {
+ L0758_i_SensorData = L0755_ps_Sensor->getData();
+ L0756_i_SensorEffect = L0755_ps_Sensor->getEffectA();
+ switch (L0757_ui_SensorType) {
case k1_SensorWallOrnClick:
- doNotTriggerSensor = false;
- if (sensor->getEffectA() == k3_SensorEffHold) {
+ L0753_B_DoNotTriggerSensor = false;
+ if (L0755_ps_Sensor->getEffectA() == k3_SensorEffHold) {
goto T0275058_ProceedToNextThing;
}
break;
case k2_SensorWallOrnClickWithAnyObj:
- doNotTriggerSensor = (champMan._g415_leaderEmptyHanded != sensor->getRevertEffectA());
+ L0753_B_DoNotTriggerSensor = (_vm->_championMan->_g415_leaderEmptyHanded != L0755_ps_Sensor->getRevertEffectA());
break;
case k17_SensorWallOrnClickWithSpecObjRemovedSensor:
case k11_SensorWallOrnClickWithSpecObjRemovedRotateSensors:
- if (sensorCountToProcessPerCell[cell])
+ if (L0760_ai_SensorCountToProcessPerCell[L0752_ui_Cell]) /* If the sensor is not the last one of its type on the cell */
goto T0275058_ProceedToNextThing;
case k3_SensorWallOrnClickWithSpecObj:
case k4_SensorWallOrnClickWithSpecObjRemoved:
- doNotTriggerSensor = ((sensorData == objMan.f32_getObjectType(leaderHandObject)) == sensor->getRevertEffectA());
- if (!doNotTriggerSensor && (sensorType == k17_SensorWallOrnClickWithSpecObjRemovedSensor)) {
- if (lastProcessedThing == thingBeingProcessed)
+ L0753_B_DoNotTriggerSensor = ((L0758_i_SensorData == _vm->_objectMan->f32_getObjectType(L0761_T_LeaderHandObject)) == L0755_ps_Sensor->getRevertEffectA());
+ if (!L0753_B_DoNotTriggerSensor && (L0757_ui_SensorType == k17_SensorWallOrnClickWithSpecObjRemovedSensor)) {
+ if (L0763_T_LastProcessedThing == L0750_T_ThingBeingProcessed) /* If the sensor is the only one of its type on the cell */
break;
- ((Sensor*)dunMan.f156_getThingData(lastProcessedThing))->setNextThing(sensor->getNextThing());
- sensor->setNextThing(Thing::_none);
- thingBeingProcessed = lastProcessedThing;
+ L0765_ps_Sensor = (Sensor*)_vm->_dungeonMan->f156_getThingData(L0763_T_LastProcessedThing);
+ L0765_ps_Sensor->setNextThing(L0755_ps_Sensor->getNextThing());
+ L0755_ps_Sensor->setNextThing(Thing::_none);
+ L0750_T_ThingBeingProcessed = L0763_T_LastProcessedThing;
}
- if (!doNotTriggerSensor && (sensorType == k11_SensorWallOrnClickWithSpecObjRemovedRotateSensors)) {
- warning("MISSING CODE: F0270_SENSOR_TriggerLocalEffect");
+ if (!L0753_B_DoNotTriggerSensor && (L0757_ui_SensorType == k11_SensorWallOrnClickWithSpecObjRemovedRotateSensors)) {
+ f270_sensorTriggetLocalEffect(k2_SensorEffToggle, mapX, mapY, L0752_ui_Cell); /* This will cause a rotation of the sensors at the specified cell on the specified square after all sensors have been processed */
}
break;
case k12_SensorWallObjGeneratorRotateSensors:
- if (sensorCountToProcessPerCell[cell])
+ if (L0760_ai_SensorCountToProcessPerCell[L0752_ui_Cell]) /* If the sensor is not the last one of its type on the cell */
goto T0275058_ProceedToNextThing;
- doNotTriggerSensor = !champMan._g415_leaderEmptyHanded;
- if (!doNotTriggerSensor) {
- warning("MISSING CODE: F0270_SENSOR_TriggerLocalEffect");
+ L0753_B_DoNotTriggerSensor = !_vm->_championMan->_g415_leaderEmptyHanded;
+ if (!L0753_B_DoNotTriggerSensor) {
+ f270_sensorTriggetLocalEffect(k2_SensorEffToggle, mapX, mapY, L0752_ui_Cell); /* This will cause a rotation of the sensors at the specified cell on the specified square after all sensors have been processed */
}
break;
case k13_SensorWallSingleObjStorageRotateSensors:
- if (champMan._g415_leaderEmptyHanded) {
- warning("MISSING CODE: F0273_SENSOR_GetObjectOfTypeInCell");
- warning("MISSING CODE: F0164_DUNGEON_UnlinkThingFromList");
- warning("MISSING CODE: F0297_CHAMPION_PutObjectInLeaderHand");
+ if (_vm->_championMan->_g415_leaderEmptyHanded) {
+ if ((L0761_T_LeaderHandObject = f273_sensorGetObjectOfTypeInCell(mapX, mapY, L0752_ui_Cell, L0758_i_SensorData)) == Thing::_none)
+ goto T0275058_ProceedToNextThing;
+ _vm->_dungeonMan->f164_unlinkThingFromList(L0761_T_LeaderHandObject, Thing(0), mapX, mapY);
+ _vm->_championMan->f297_putObjectInLeaderHand(L0761_T_LeaderHandObject, true);
} else {
- warning("MISSING CODE: F0273_SENSOR_GetObjectOfTypeInCell");
- warning(("MISSING CODE: F0298_CHAMPION_GetObjectRemovedFromLeaderHand"));
- warning("MISSING CODE: F0163_DUNGEON_LinkThingToList");
- leaderHandObject = Thing::_none;
- }
- warning("MISSING CODE: F0270_SENSOR_TriggerLocalEffect");
- if ((sensorEffect == k3_SensorEffHold) && !champMan._g415_leaderEmptyHanded) {
- doNotTriggerSensor = true;
+ if ((_vm->_objectMan->f32_getObjectType(L0761_T_LeaderHandObject) != L0758_i_SensorData) || (f273_sensorGetObjectOfTypeInCell(mapX, mapY, L0752_ui_Cell, L0758_i_SensorData) != Thing::_none))
+ goto T0275058_ProceedToNextThing;
+ _vm->_championMan->f298_getObjectRemovedFromLeaderHand();
+ _vm->_dungeonMan->f163_linkThingToList(M15_thingWithNewCell(L0761_T_LeaderHandObject, L0752_ui_Cell), Thing(0), mapX, mapY);
+ L0761_T_LeaderHandObject = Thing::_none;
+ }
+ f270_sensorTriggetLocalEffect(k2_SensorEffToggle, mapX, mapY, L0752_ui_Cell); /* This will cause a rotation of the sensors at the specified cell on the specified square after all sensors have been processed */
+ if ((L0756_i_SensorEffect == k3_SensorEffHold) && !_vm->_championMan->_g415_leaderEmptyHanded) {
+ L0753_B_DoNotTriggerSensor = true;
} else {
- doNotTriggerSensor = false;
+ L0753_B_DoNotTriggerSensor = false;
}
break;
- case k16_SensorWallObjExchanger: {
- if (sensorCountToProcessPerCell[cell])
+ case k16_SensorWallObjExchanger:
+ if (L0760_ai_SensorCountToProcessPerCell[L0752_ui_Cell]) /* If the sensor is not the last one of its type on the cell */
goto T0275058_ProceedToNextThing;
- Thing thingOnSquare = dunMan.f161_getSquareFirstThing(mapX, mapY);
- if ((objMan.f32_getObjectType(leaderHandObject) != sensorData) || (thingOnSquare == Thing::_none))
+ L0762_T_ThingOnSquare = _vm->_dungeonMan->f162_getSquareFirstObject(mapX, mapY);
+ if ((_vm->_objectMan->f32_getObjectType(L0761_T_LeaderHandObject) != L0758_i_SensorData) || (L0762_T_ThingOnSquare == Thing::_none))
goto T0275058_ProceedToNextThing;
- warning("MISSING CODE: F0164_DUNGEON_UnlinkThingFromList");
- warning("MISSING CODE: F0298_CHAMPION_GetObjectRemovedFromLeaderHand");
- warning("MISSING CODE: F0163_DUNGEON_LinkThingToList");
- warning("MISSING CODE: F0297_CHAMPION_PutObjectInLeaderHand");
- doNotTriggerSensor = false;
+ _vm->_dungeonMan->f164_unlinkThingFromList(L0762_T_ThingOnSquare, Thing(0), mapX, mapY);
+ _vm->_championMan->f298_getObjectRemovedFromLeaderHand();
+ _vm->_dungeonMan->f163_linkThingToList(M15_thingWithNewCell(L0761_T_LeaderHandObject, L0752_ui_Cell), Thing(0), mapX, mapY);
+ _vm->_championMan->f297_putObjectInLeaderHand(L0762_T_ThingOnSquare, true);
+ L0753_B_DoNotTriggerSensor = false;
break;
- }
case k127_SensorWallChampionPortrait:
- champMan.f280_addCandidateChampionToParty(sensorData);
+ _vm->_championMan->f280_addCandidateChampionToParty(L0758_i_SensorData);
goto T0275058_ProceedToNextThing;
default:
goto T0275058_ProceedToNextThing;
}
-
- if (sensorEffect == k3_SensorEffHold) {
- sensorEffect = doNotTriggerSensor ? k1_SensorEffClear : k0_SensorEffSet;
- doNotTriggerSensor = false;
+ if (L0756_i_SensorEffect == k3_SensorEffHold) {
+ L0756_i_SensorEffect = L0753_B_DoNotTriggerSensor ? k1_SensorEffClear : k0_SensorEffSet;
+ L0753_B_DoNotTriggerSensor = false;
}
-
- if (!doNotTriggerSensor) {
- atLeastOneSensorWasTriggered = true;
- if (sensor->getAudibleA()) {
+ if (!L0753_B_DoNotTriggerSensor) {
+ L0759_B_AtLeastOneSensorWasTriggered = true;
+ if (L0755_ps_Sensor->getAudibleA()) {
warning("MISSING CODE: F0064_SOUND_RequestPlay_CPSD");
}
- if (!champMan._g415_leaderEmptyHanded &&
- ((sensorType == k4_SensorWallOrnClickWithSpecObjRemoved) ||
- (sensorType == k11_SensorWallOrnClickWithSpecObjRemovedRotateSensors) ||
- (sensorType == k17_SensorWallOrnClickWithSpecObjRemovedSensor))) {
-
- *((Thing*)dunMan.f156_getThingData(leaderHandObject)) = Thing::_none;
- warning("MISSING CODE: F0298_CHAMPION_GetObjectRemovedFromLeaderHand");
- leaderHandObject = Thing::_none;
+ if (!_vm->_championMan->_g415_leaderEmptyHanded && ((L0757_ui_SensorType == k4_SensorWallOrnClickWithSpecObjRemoved) || (L0757_ui_SensorType == k11_SensorWallOrnClickWithSpecObjRemovedRotateSensors) || (L0757_ui_SensorType == k17_SensorWallOrnClickWithSpecObjRemovedSensor))) {
+ L0754_ps_Generic = (Thing*)_vm->_dungeonMan->f156_getThingData(L0761_T_LeaderHandObject);
+ *L0754_ps_Generic = Thing::_none;
+ _vm->_championMan->f298_getObjectRemovedFromLeaderHand();
+ L0761_T_LeaderHandObject = Thing::_none;
} else {
- warning("MISSING CODE: (leaderHandObject = F0167_DUNGEON_GetObjectForProjectileLauncherOrObjectGenerator(sensorData)");
- if (champMan._g415_leaderEmptyHanded && (sensorType == k12_SensorWallObjGeneratorRotateSensors) && (leaderHandObject != Thing::_none)) {
- warning("MISSING CODE: F0297_CHAMPION_PutObjectInLeaderHand");
+ if (_vm->_championMan->_g415_leaderEmptyHanded &&
+ (L0757_ui_SensorType == k12_SensorWallObjGeneratorRotateSensors) &&
+ ((L0761_T_LeaderHandObject = _vm->_dungeonMan->f167_getObjForProjectileLaucherOrObjGen(L0758_i_SensorData)) != Thing::_none)) {
+ _vm->_championMan->f297_putObjectInLeaderHand(L0761_T_LeaderHandObject, true);
}
}
- warning("MISSING CODE: F0272_SENSOR_TriggerEffect");
+ f272_sensorTriggerEffect(L0755_ps_Sensor, L0756_i_SensorEffect, mapX, mapY, L0752_ui_Cell);
}
goto T0275058_ProceedToNextThing;
}
- if (thingType >= k4_GroupThingType)
+ if (L0751_ui_ThingType >= k4_GroupThingType)
break;
T0275058_ProceedToNextThing:
- lastProcessedThing = thingBeingProcessed;
- thingBeingProcessed = dunMan.f159_getNextThing(thingBeingProcessed);
+ L0763_T_LastProcessedThing = L0750_T_ThingBeingProcessed;
+ L0750_T_ThingBeingProcessed = _vm->_dungeonMan->f159_getNextThing(L0750_T_ThingBeingProcessed);
+ }
+ f271_processRotationEffect();
+ return L0759_B_AtLeastOneSensorWasTriggered;
+}
+
+bool MovesensMan::f267_getMoveResult(Thing thing, int16 mapX, int16 mapY, int16 destMapX, int16 destMapY) {
+ int16 L0708_i_Multiple = 0;
+#define AL0708_i_DestinationSquare L0708_i_Multiple
+#define AL0708_i_ScentIndex L0708_i_Multiple
+#define AL0708_i_ActiveGroupIndex L0708_i_Multiple
+ int16 L0709_i_Multiple;
+#define AL0709_i_DestinationSquareType L0709_i_Multiple
+#define AL0709_i_ChampionIndex L0709_i_Multiple
+ int16 L0710_i_ThingType;
+ Champion* L0711_ps_Champion;
+ Teleporter* L0712_ps_Teleporter;
+ bool L0713_B_ThingLevitates;
+ uint16 L0714_ui_MapIndexSource = 0;
+ uint16 L0715_ui_MapIndexDestination = 0;
+ uint16 L0716_ui_Direction = 0;
+ uint16 L0717_ui_ThingCell = 0;
+ int16 L0718_i_RequiredTeleporterScope;
+ // Strangerke: Only present in v2.1, but it fixes a bug, so I propose to keep it
+ int16 L0719_i_TraversedPitCount;
+ uint16 L0720_ui_MoveGroupResult;
+ bool L0721_B_GroupOnPartyMap;
+ bool L0722_B_FallKilledGroup;
+ bool L0723_B_DrawDungeonViewWhileFalling;
+ bool L0724_B_DestinationIsTeleporterTarget;
+ bool L0725_B_PartySquare;
+ bool L0726_B_Audible;
+ uint16 L0727_ui_Multiple;
+#define AL0727_ui_ThingCell L0727_ui_Multiple
+#define AL0727_ui_Outcome L0727_ui_Multiple
+#define AL0727_ui_Backup L0727_ui_Multiple
+ int16 L0728_i_ChainedMoveCount;
+ uint16 L1638_ui_MovementSoundIndex;
+
+ L0710_i_ThingType = kM1_PartyThingType;
+ L0713_B_ThingLevitates = false;
+ L0719_i_TraversedPitCount = 0;
+ L0720_ui_MoveGroupResult = 0;
+ L0721_B_GroupOnPartyMap = false;
+ L0722_B_FallKilledGroup = false;
+ L0723_B_DrawDungeonViewWhileFalling = false;
+ L0724_B_DestinationIsTeleporterTarget = false;
+ L0725_B_PartySquare = false;
+ L0726_B_Audible = false;
+ if (thing != Thing::_party) {
+ L0710_i_ThingType = (thing).getType();
+ L0717_ui_ThingCell = (thing).getCell();
+ L0713_B_ThingLevitates = f264_isLevitating(thing);
+ }
+ /* If moving the party or a creature on the party map from a dungeon square then check for a projectile impact */
+ if ((mapX >= 0) && ((thing == Thing::_party) || ((L0710_i_ThingType == k4_GroupThingType) && (_vm->_dungeonMan->_g272_currMapIndex == _vm->_dungeonMan->_g309_partyMapIndex)))) {
+ if (f266_moveIsKilledByProjectileImpact(mapX, mapY, destMapX, destMapY, thing)) {
+ return true; /* The specified group thing cannot be moved because it was killed by a projectile impact */
+ }
+ }
+ if (destMapX >= 0) {
+ L0714_ui_MapIndexSource = L0715_ui_MapIndexDestination = _vm->_dungeonMan->_g272_currMapIndex;
+ L0721_B_GroupOnPartyMap = (L0714_ui_MapIndexSource == _vm->_dungeonMan->_g309_partyMapIndex) && (mapX >= 0);
+ if (thing == Thing::_party) {
+ _vm->_dungeonMan->_g306_partyMapX = destMapX;
+ _vm->_dungeonMan->_g307_partyMapY = destMapY;
+ L0718_i_RequiredTeleporterScope = k0x0002_TelepScopeObjOrParty;
+ L0723_B_DrawDungeonViewWhileFalling = !_vm->_inventoryMan->_g432_inventoryChampionOrdinal && !_vm->_championMan->_g300_partyIsSleeping;
+ L0716_ui_Direction = _vm->_dungeonMan->_g308_partyDir;
+ } else {
+ if (L0710_i_ThingType == k4_GroupThingType) {
+ L0718_i_RequiredTeleporterScope = k0x0001_TelepScopeCreatures;
+ } else {
+ L0718_i_RequiredTeleporterScope = (k0x0001_TelepScopeCreatures | k0x0002_TelepScopeObjOrParty);
+ }
+ }
+ if (L0710_i_ThingType == k14_ProjectileThingType) {
+ L0712_ps_Teleporter = (Teleporter*)_vm->_dungeonMan->f156_getThingData(thing);
+ _g400_moveResultDir = (_vm->_timeline->_g370_events[((Projectile*)L0712_ps_Teleporter)->_eventIndex])._C._projectile.getDir();
+ }
+ for (L0728_i_ChainedMoveCount = 1000; --L0728_i_ChainedMoveCount; ) { /* No more than 1000 chained moves at once (in a chain of teleporters and pits for example) */
+ AL0708_i_DestinationSquare = _vm->_dungeonMan->_g271_currMapData[destMapX][destMapY];
+ if ((AL0709_i_DestinationSquareType = Square(AL0708_i_DestinationSquare).getType()) == k5_ElementTypeTeleporter) {
+ if (!getFlag(AL0708_i_DestinationSquare, k0x0008_TeleporterOpen))
+ break;
+ L0712_ps_Teleporter = (Teleporter*)_vm->_dungeonMan->f157_getSquareFirstThingData(destMapX, destMapY);
+ if ((L0712_ps_Teleporter->getScope() == k0x0001_TelepScopeCreatures) && (L0710_i_ThingType != k4_GroupThingType))
+ break;
+ if ((L0718_i_RequiredTeleporterScope != (k0x0001_TelepScopeCreatures | k0x0002_TelepScopeObjOrParty)) && !getFlag(L0712_ps_Teleporter->getScope(), L0718_i_RequiredTeleporterScope))
+ break;
+ L0724_B_DestinationIsTeleporterTarget = (destMapX == L0712_ps_Teleporter->getTargetMapX()) && (destMapY == L0712_ps_Teleporter->getTargetMapY()) && (L0715_ui_MapIndexDestination == L0712_ps_Teleporter->getTargetMapIndex());
+ destMapX = L0712_ps_Teleporter->getTargetMapX();
+ destMapY = L0712_ps_Teleporter->getTargetMapY();
+ L0726_B_Audible = L0712_ps_Teleporter->isAudible();
+ _vm->_dungeonMan->f173_setCurrentMap(L0715_ui_MapIndexDestination = L0712_ps_Teleporter->getTargetMapIndex());
+ if (thing == Thing::_party) {
+ _vm->_dungeonMan->_g306_partyMapX = destMapX;
+ _vm->_dungeonMan->_g307_partyMapY = destMapY;
+ if (L0712_ps_Teleporter->isAudible()) {
+ warning("MISSING CODE: F0064_SOUND_RequestPlay_CPSD");
+ }
+ L0723_B_DrawDungeonViewWhileFalling = true;
+ if (L0712_ps_Teleporter->getAbsoluteRotation()) {
+ _vm->_championMan->f284_setPartyDirection(L0712_ps_Teleporter->getRotation());
+ } else {
+ _vm->_championMan->f284_setPartyDirection(M21_normalizeModulo4(_vm->_dungeonMan->_g308_partyDir + L0712_ps_Teleporter->getRotation()));
+ }
+ } else {
+ if (L0710_i_ThingType == k4_GroupThingType) {
+ if (L0712_ps_Teleporter->isAudible()) {
+ warning("MISSING CODE: F0064_SOUND_RequestPlay_CPSD");
+ }
+ L0720_ui_MoveGroupResult = f262_getTeleporterRotatedGroupResult(L0712_ps_Teleporter, thing, L0714_ui_MapIndexSource);
+ } else {
+ if (L0710_i_ThingType == k14_ProjectileThingType) {
+ thing = f263_getTeleporterRotatedProjectileThing(L0712_ps_Teleporter, thing);
+ } else {
+ if (!(L0712_ps_Teleporter->getAbsoluteRotation()) && (mapX != -2)) {
+ thing = M15_thingWithNewCell(thing, M21_normalizeModulo4((thing).getCell() + L0712_ps_Teleporter->getRotation()));
+ }
+ }
+ }
+ }
+ if (L0724_B_DestinationIsTeleporterTarget)
+ break;
+ } else {
+ if ((AL0709_i_DestinationSquareType == k2_ElementTypePit) && !L0713_B_ThingLevitates && getFlag(AL0708_i_DestinationSquare, k0x0008_PitOpen) && !getFlag(AL0708_i_DestinationSquare, k0x0001_PitImaginary)) {
+ if (L0723_B_DrawDungeonViewWhileFalling && !_g402_useRopeToClimbDownPit) {
+ L0723_B_DrawDungeonViewWhileFalling = true;
+ if (L0719_i_TraversedPitCount) {
+ _vm->_dungeonMan->f174_setCurrentMapAndPartyMap(L0715_ui_MapIndexDestination);
+ _vm->_displayMan->f96_loadCurrentMapGraphics();
+ }
+ L0719_i_TraversedPitCount++;
+ _vm->_displayMan->f128_drawDungeon(_vm->_dungeonMan->_g308_partyDir, destMapX, destMapY); /* BUG0_28 When falling through multiple pits the dungeon view is updated to show each traversed map but the graphics used for creatures, wall and floor ornaments may not be correct. The dungeon view is drawn for each map by using the graphics loaded for the source map. Therefore the graphics for creatures, wall and floor ornaments may not look like what they should */
+ /* BUG0_71 Some timings are too short on fast computers. When the party falls in a series of pits, the dungeon view is refreshed too quickly because the execution speed is not limited */
+ /* BUG0_01 While drawing creatures the engine will read invalid ACTIVE_GROUP data in _vm->_groupMan->_g375_activeGroups because the data is for the creatures on the source map and not the map being drawn. The only consequence is that creatures may be drawn with incorrect bitmaps and/or directions */
+ }
+ L0715_ui_MapIndexDestination = _vm->_dungeonMan->f154_getLocationAfterLevelChange(L0715_ui_MapIndexDestination, 1, &destMapX, &destMapY);
+ _vm->_dungeonMan->f173_setCurrentMap(L0715_ui_MapIndexDestination);
+ if (thing == Thing::_party) {
+ _vm->_dungeonMan->_g306_partyMapX = destMapX;
+ _vm->_dungeonMan->_g307_partyMapY = destMapY;
+ if (_vm->_championMan->_g305_partyChampionCount > 0) {
+ if (_g402_useRopeToClimbDownPit) {
+ for (AL0709_i_ChampionIndex = k0_ChampionFirst, L0711_ps_Champion = _vm->_championMan->_gK71_champions; AL0709_i_ChampionIndex < _vm->_championMan->_g305_partyChampionCount; AL0709_i_ChampionIndex++, L0711_ps_Champion++) {
+ if (L0711_ps_Champion->_currHealth) {
+ _vm->_championMan->f325_decrementStamine(AL0709_i_ChampionIndex, ((L0711_ps_Champion->_load * 25) / _vm->_championMan->f309_getMaximumLoad(L0711_ps_Champion)) + 1);
+ }
+ }
+ } else {
+ if (_vm->_championMan->f324_damageAll_getDamagedChampionCount(20, k0x0010_ChampionWoundLegs | k0x0020_ChampionWoundFeet, k2_attackType_SELF)) {
+ warning("MISSING CODE: F0064_SOUND_RequestPlay_CPSD");
+ }
+ }
+ }
+ _g402_useRopeToClimbDownPit = false;
+ } else {
+ if (L0710_i_ThingType == k4_GroupThingType) {
+ _vm->_dungeonMan->f173_setCurrentMap(L0714_ui_MapIndexSource);
+ AL0727_ui_Outcome = _vm->_groupMan->f191_getDamageAllCreaturesOutcome((Group*)_vm->_dungeonMan->f156_getThingData(thing), mapX, mapY, 20, false);
+ _vm->_dungeonMan->f173_setCurrentMap(L0715_ui_MapIndexDestination);
+ if (L0722_B_FallKilledGroup = (AL0727_ui_Outcome == k2_outcomeKilledAllCreaturesInGroup))
+ break;
+ if (AL0727_ui_Outcome == k1_outcomeKilledSomeCreaturesInGroup) {
+ _vm->_groupMan->f187_dropMovingCreatureFixedPossession(thing, destMapX, destMapY);
+ }
+ }
+ }
+ } else {
+ if ((AL0709_i_DestinationSquareType == k3_ElementTypeStairs) && (thing != Thing::_party) && (L0710_i_ThingType != k14_ProjectileThingType)) {
+ if (!getFlag(AL0708_i_DestinationSquare, k0x0004_StairsUp)) {
+ L0715_ui_MapIndexDestination = _vm->_dungeonMan->f154_getLocationAfterLevelChange(L0715_ui_MapIndexDestination, 1, &destMapX, &destMapY);
+ _vm->_dungeonMan->f173_setCurrentMap(L0715_ui_MapIndexDestination);
+ }
+ L0716_ui_Direction = _vm->_dungeonMan->f155_getStairsExitDirection(destMapX, destMapY);
+ destMapX += _vm->_dirIntoStepCountEast[L0716_ui_Direction], destMapY += _vm->_dirIntoStepCountNorth[L0716_ui_Direction];
+ L0716_ui_Direction = returnOppositeDir((direction)L0716_ui_Direction);
+ AL0727_ui_ThingCell = (thing).getCell();
+ AL0727_ui_ThingCell = M21_normalizeModulo4((((AL0727_ui_ThingCell - L0716_ui_Direction + 1) & 0x0002) >> 1) + L0716_ui_Direction);
+ thing = M15_thingWithNewCell(thing, AL0727_ui_ThingCell);
+ } else
+ break;
+ }
+ }
+ }
+ if ((L0710_i_ThingType == k4_GroupThingType) && (L0722_B_FallKilledGroup || !_vm->_dungeonMan->f139_isCreatureAllowedOnMap(thing, L0715_ui_MapIndexDestination))) {
+ _vm->_groupMan->f187_dropMovingCreatureFixedPossession(thing, destMapX, destMapY);
+ _vm->_groupMan->f188_dropGroupPossessions(destMapX, destMapY, thing, k2_soundModePlayOneTickLater);
+ _vm->_dungeonMan->f173_setCurrentMap(L0714_ui_MapIndexSource);
+ if (mapX >= 0) {
+ _vm->_groupMan->f189_delete(mapX, mapY);
+ }
+ return true; /* The specified group thing cannot be moved because it was killed by a fall or because it is not allowed on the destination map */
+ }
+ _g397_moveResultMapX = destMapX;
+ _g398_moveResultMapY = destMapY;
+ _g399_moveResultMapIndex = L0715_ui_MapIndexDestination;
+ _g401_moveResultCell = (thing).getCell();
+ L0725_B_PartySquare = (L0715_ui_MapIndexDestination == L0714_ui_MapIndexSource) && (destMapX == mapX) && (destMapY == mapY);
+ if (L0725_B_PartySquare) {
+ if (thing == Thing::_party) {
+ if (_vm->_dungeonMan->_g308_partyDir == L0716_ui_Direction) {
+ return false;
+ }
+ } else {
+ if ((_g401_moveResultCell == L0717_ui_ThingCell) && (L0710_i_ThingType != k14_ProjectileThingType)) {
+ return false;
+ }
+ }
+ } else {
+ if ((thing == Thing::_party) && _vm->_championMan->_g305_partyChampionCount) {
+ AL0727_ui_Backup = AL0708_i_DestinationSquare;
+ AL0708_i_ScentIndex = _vm->_championMan->_g407_party._scentCount;
+ while (AL0708_i_ScentIndex >= 24) {
+ _vm->_championMan->f316_deleteScent(0);
+ AL0708_i_ScentIndex--;
+ }
+ if (AL0708_i_ScentIndex) {
+ _vm->_championMan->f317_addScentStrength(mapX, mapY, (int)(_vm->_g313_gameTime - _vm->_projexpl->_g362_lastPartyMovementTime));
+ }
+ _vm->_projexpl->_g362_lastPartyMovementTime = _vm->_g313_gameTime;
+ _vm->_championMan->_g407_party._scentCount++;
+ if (_vm->_championMan->_g407_party._event79Count_Footprints) {
+ _vm->_championMan->_g407_party._lastScentIndex = _vm->_championMan->_g407_party._scentCount;
+ }
+ _vm->_championMan->_g407_party._scents[AL0708_i_ScentIndex].setMapX(destMapX);
+ _vm->_championMan->_g407_party._scents[AL0708_i_ScentIndex].setMapY(destMapY);
+ _vm->_championMan->_g407_party._scents[AL0708_i_ScentIndex].setMapIndex(L0715_ui_MapIndexDestination);
+ _vm->_championMan->_g407_party._scentStrengths[AL0708_i_ScentIndex] = 0;
+ _vm->_championMan->f317_addScentStrength(destMapX, destMapY, k0x8000_mergeCycles | 24);
+ AL0708_i_DestinationSquare = AL0727_ui_Backup;
+ }
+ if (L0715_ui_MapIndexDestination != L0714_ui_MapIndexSource) {
+ _vm->_dungeonMan->f173_setCurrentMap(L0714_ui_MapIndexSource);
+ }
+ }
+ }
+ if (mapX >= 0) {
+ if (thing == Thing::_party) {
+ f276_sensorProcessThingAdditionOrRemoval(mapX, mapY, Thing::_party, L0725_B_PartySquare, false);
+ } else {
+ if (L0713_B_ThingLevitates) {
+ _vm->_dungeonMan->f164_unlinkThingFromList(thing, Thing(0), mapX, mapY);
+ } else {
+ f276_sensorProcessThingAdditionOrRemoval(mapX, mapY, thing, (_vm->_dungeonMan->_g272_currMapIndex == _vm->_dungeonMan->_g309_partyMapIndex) && (mapX == _vm->_dungeonMan->_g306_partyMapX) && (mapY == _vm->_dungeonMan->_g307_partyMapY), false);
+ }
+ }
+ }
+ if (destMapX >= 0) {
+ if (thing == Thing::_party) {
+ _vm->_dungeonMan->f173_setCurrentMap(L0715_ui_MapIndexDestination);
+ if ((thing = _vm->_groupMan->f175_groupGetThing(_vm->_dungeonMan->_g306_partyMapX, _vm->_dungeonMan->_g307_partyMapY)) != Thing::_endOfList) { /* Delete group if party moves onto its square */
+ _vm->_groupMan->f188_dropGroupPossessions(_vm->_dungeonMan->_g306_partyMapX, _vm->_dungeonMan->_g307_partyMapY, thing, k1_soundModePlayIfPrioritized);
+ _vm->_groupMan->f189_delete(_vm->_dungeonMan->_g306_partyMapX, _vm->_dungeonMan->_g307_partyMapY);
+ }
+ if (L0715_ui_MapIndexDestination == L0714_ui_MapIndexSource) {
+ f276_sensorProcessThingAdditionOrRemoval(_vm->_dungeonMan->_g306_partyMapX, _vm->_dungeonMan->_g307_partyMapY, Thing::_party, L0725_B_PartySquare, true);
+ } else {
+ _vm->_dungeonMan->f173_setCurrentMap(L0714_ui_MapIndexSource);
+ _vm->_g327_newPartyMapIndex = L0715_ui_MapIndexDestination;
+ }
+ } else {
+ if (L0710_i_ThingType == k4_GroupThingType) {
+ _vm->_dungeonMan->f173_setCurrentMap(L0715_ui_MapIndexDestination);
+ L0712_ps_Teleporter = (Teleporter*)_vm->_dungeonMan->f156_getThingData(thing);
+ AL0708_i_ActiveGroupIndex = ((Group*)L0712_ps_Teleporter)->getActiveGroupIndex();
+ if (((L0715_ui_MapIndexDestination == _vm->_dungeonMan->_g309_partyMapIndex) && (destMapX == _vm->_dungeonMan->_g306_partyMapX) && (destMapY == _vm->_dungeonMan->_g307_partyMapY)) || (_vm->_groupMan->f175_groupGetThing(destMapX, destMapY) != Thing::_endOfList)) { /* If a group tries to move to the party square or over another group then create an event to move the group later */
+ _vm->_dungeonMan->f173_setCurrentMap(L0714_ui_MapIndexSource);
+ if (mapX >= 0) {
+ _vm->_groupMan->f181_groupDeleteEvents(mapX, mapY);
+ }
+ if (L0721_B_GroupOnPartyMap) {
+ _vm->_groupMan->f184_removeActiveGroup(AL0708_i_ActiveGroupIndex);
+ }
+ f265_createEvent60to61_moveGroup(thing, destMapX, destMapY, L0715_ui_MapIndexDestination, L0726_B_Audible);
+ return true; /* The specified group thing cannot be moved because the party or another group is on the destination square */
+ }
+ L1638_ui_MovementSoundIndex = f514_getSound(((Group*)_vm->_dungeonMan->_g284_thingData[k4_GroupThingType])[(thing).getIndex()]._type);
+ if (L1638_ui_MovementSoundIndex < k34_D13_soundCount) {
+ warning("MISSING CODE: F0064_SOUND_RequestPlay_CPSD");
+ }
+ if (L0721_B_GroupOnPartyMap && (L0715_ui_MapIndexDestination != _vm->_dungeonMan->_g309_partyMapIndex)) { /* If the group leaves the party map */
+ _vm->_groupMan->f184_removeActiveGroup(AL0708_i_ActiveGroupIndex);
+ L0720_ui_MoveGroupResult = true;
+ } else {
+ if ((L0715_ui_MapIndexDestination == _vm->_dungeonMan->_g309_partyMapIndex) && (!L0721_B_GroupOnPartyMap)) { /* If the group arrives on the party map */
+ _vm->_groupMan->f183_addActiveGroup(thing, destMapX, destMapY);
+ L0720_ui_MoveGroupResult = true;
+ }
+ }
+ if (L0713_B_ThingLevitates) {
+ _vm->_dungeonMan->f163_linkThingToList(thing, Thing(0), destMapX, destMapY);
+ } else {
+ f276_sensorProcessThingAdditionOrRemoval(destMapX, destMapY, thing, false, true);
+ }
+ if (L0720_ui_MoveGroupResult || (mapX < 0)) { /* If group moved from one map to another or if it was just placed on a square */
+ _vm->_groupMan->f180_startWanedring(destMapX, destMapY);
+ }
+ _vm->_dungeonMan->f173_setCurrentMap(L0714_ui_MapIndexSource);
+ if (mapX >= 0) {
+ if (L0720_ui_MoveGroupResult > 1) { /* If the group behavior was C6_BEHAVIOR_ATTACK before being teleported from and to the party map */
+ _vm->_groupMan->f182_stopAttacking(&_vm->_groupMan->_g375_activeGroups[L0720_ui_MoveGroupResult - 2], mapX, mapY);
+ } else {
+ if (L0720_ui_MoveGroupResult) { /* If the group was teleported or leaved the party map or entered the party map */
+ _vm->_groupMan->f181_groupDeleteEvents(mapX, mapY);
+ }
+ }
+ }
+ return L0720_ui_MoveGroupResult;
+ }
+ _vm->_dungeonMan->f173_setCurrentMap(L0715_ui_MapIndexDestination);
+ if (L0710_i_ThingType == k14_ProjectileThingType) { /* BUG0_29 An explosion can trigger a floor sensor. Explosions do not trigger floor sensors on the square where they are created. However, if an explosion is moved by a teleporter (or by falling into a pit, see BUG0_26) after it was created, it can trigger floor sensors on the destination square. This is because explosions are not considered as levitating in the code, while projectiles are. The condition here should be (L0713_B_ThingLevitates) so that explosions would not start sensor processing on their destination square as they should be Levitating. This would work if F0264_MOVE_IsLevitating returned true for explosions (see BUG0_26) */
+ _vm->_dungeonMan->f163_linkThingToList(thing, Thing(0), destMapX, destMapY);
+ } else {
+ f276_sensorProcessThingAdditionOrRemoval(destMapX, destMapY, thing, (_vm->_dungeonMan->_g272_currMapIndex == _vm->_dungeonMan->_g309_partyMapIndex) && (destMapX == _vm->_dungeonMan->_g306_partyMapX) && (destMapY == _vm->_dungeonMan->_g307_partyMapY), true);
+ }
+ _vm->_dungeonMan->f173_setCurrentMap(L0714_ui_MapIndexSource);
+ }
+ }
+ return false;
+}
+
+bool MovesensMan::f264_isLevitating(Thing thing) {
+ int16 L0695_i_ThingType;
+
+
+ if ((L0695_i_ThingType = (thing).getType()) == k4_GroupThingType) {
+ return getFlag(_vm->_dungeonMan->f144_getCreatureAttributes(thing), k0x0020_MaskCreatureInfo_levitation);
+ }
+ if (L0695_i_ThingType == k14_ProjectileThingType) { /* BUG0_26 An explosion may fall in a pit. If a pit is opened while there is an explosion above then the explosion falls into the pit in F0267_MOVE_GetMoveResult_CPSCE. Explosions are not considered as levitating so they are moved when the pit is opened. This function should return true for explosions */
+ return true;
+ }
+ return false;
+}
+
+bool MovesensMan::f266_moveIsKilledByProjectileImpact(int16 srcMapX, int16 srcMapY, int16 destMapX, int16 destMapY, Thing thing) {
+ Thing L0697_T_Thing;
+ uint16 L0699_ui_Multiple;
+#define AL0699_ui_Cell L0699_ui_Multiple
+#define AL0699_ui_PrimaryDirection L0699_ui_Multiple
+#define AL0699_ui_ChampionOrCreatureOrdinal L0699_ui_Multiple
+ int16 L0700_i_Multiple;
+#define AL0700_B_CreatureAlive L0700_i_Multiple
+#define AL0700_i_Distance L0700_i_Multiple
+#define AL0700_i_SecondaryDirection L0700_i_Multiple
+ Group* L0701_ps_Group;
+ int16 L0702_i_ImpactType;
+ bool L0703_B_CheckDestinationSquareProjectileImpacts;
+ uint16 L0704_ui_ProjectileMapX;
+ uint16 L0705_ui_ProjectileMapY;
+ byte L0706_auc_IntermediaryChampionOrCreatureOrdinalInCell[4]; /* This array is used only when moving between two adjacent squares and is used to test projectile impacts when the party or group is in the 'intermediary' step between the two squares. Without this test, in the example below no impact would be detected. In this example, the party moves from the source square on the left (which contains a single champion at cell 2) to the destination square on the right (which contains a single projectile at cell 3).
+ Party: Projectiles on target square: Incorrect result without the test for the intermediary step (the champion would have passed through the projectile without impact):
+ 00 -> 00 00
+ 01 P0 P1 */
+ byte L0707_auc_ChampionOrCreatureOrdinalInCell[4]; /* This array has an entry for each cell on the source square, containing the ordinal of the champion or creature (0 if there is no champion or creature at this cell) */
+
+
+ L0703_B_CheckDestinationSquareProjectileImpacts = false;
+ for (int16 i = 0; i < 4; ++i)
+ L0707_auc_ChampionOrCreatureOrdinalInCell[i] = 0;
+ if (thing == Thing::_party) {
+ L0702_i_ImpactType = kM2_ChampionElemType;
+ for (AL0699_ui_Cell = k0_CellNorthWest; AL0699_ui_Cell < k3_CellSouthWest + 1; AL0699_ui_Cell++) {
+ if (_vm->_championMan->f285_getIndexInCell((ViewCell)AL0699_ui_Cell) >= 0) {
+ L0707_auc_ChampionOrCreatureOrdinalInCell[AL0699_ui_Cell] = _vm->M0_indexToOrdinal(AL0699_ui_Cell);
+ }
+ }
+ } else {
+ L0702_i_ImpactType = kM1_CreatureElemType;
+ L0701_ps_Group = (Group*)_vm->_dungeonMan->f156_getThingData(thing);
+ for (AL0699_ui_Cell = k0_CellNorthWest, AL0700_B_CreatureAlive = false; AL0699_ui_Cell < k3_CellSouthWest + 1; AL0699_ui_Cell++) {
+ AL0700_B_CreatureAlive |= L0701_ps_Group->_health[AL0699_ui_Cell];
+ if (_vm->_groupMan->f176_getCreatureOrdinalInCell(L0701_ps_Group, AL0699_ui_Cell)) {
+ L0707_auc_ChampionOrCreatureOrdinalInCell[AL0699_ui_Cell] = _vm->M0_indexToOrdinal(AL0699_ui_Cell);
+ }
+ }
+ if (!AL0700_B_CreatureAlive) {
+ return false;
+ }
+ }
+ if ((destMapX >= 0) && (((((AL0700_i_Distance = srcMapX - destMapX) < 0) ? -AL0700_i_Distance : AL0700_i_Distance) + (((AL0700_i_Distance = srcMapY - destMapY) < 0) ? -AL0700_i_Distance : AL0700_i_Distance)) == 1)) { /* If source and destination squares are adjacent (if party or group is not being teleported) */
+ AL0699_ui_PrimaryDirection = _vm->_groupMan->f228_getDirsWhereDestIsVisibleFromSource(srcMapX, srcMapY, destMapX, destMapY);
+ AL0700_i_SecondaryDirection = returnNextVal(AL0699_ui_PrimaryDirection);
+ for (int16 i = 0; i < 4; ++i)
+ L0706_auc_IntermediaryChampionOrCreatureOrdinalInCell[i] = 0;
+ if (L0706_auc_IntermediaryChampionOrCreatureOrdinalInCell[returnPrevVal(AL0699_ui_PrimaryDirection)] = L0707_auc_ChampionOrCreatureOrdinalInCell[AL0699_ui_PrimaryDirection]) {
+ L0703_B_CheckDestinationSquareProjectileImpacts = true;
+ }
+ if (L0706_auc_IntermediaryChampionOrCreatureOrdinalInCell[returnNextVal(AL0700_i_SecondaryDirection)] = L0707_auc_ChampionOrCreatureOrdinalInCell[AL0700_i_SecondaryDirection]) {
+ L0703_B_CheckDestinationSquareProjectileImpacts = true;
+ }
+ if (!L0707_auc_ChampionOrCreatureOrdinalInCell[AL0699_ui_PrimaryDirection]) {
+ L0707_auc_ChampionOrCreatureOrdinalInCell[AL0699_ui_PrimaryDirection] = L0707_auc_ChampionOrCreatureOrdinalInCell[returnPrevVal(AL0699_ui_PrimaryDirection)];
+ }
+ if (!L0707_auc_ChampionOrCreatureOrdinalInCell[AL0700_i_SecondaryDirection]) {
+ L0707_auc_ChampionOrCreatureOrdinalInCell[AL0700_i_SecondaryDirection] = L0707_auc_ChampionOrCreatureOrdinalInCell[returnNextVal(AL0700_i_SecondaryDirection)];
+ }
+ }
+ L0704_ui_ProjectileMapX = srcMapX; /* Check impacts with projectiles on the source square */
+ L0705_ui_ProjectileMapY = srcMapY;
+T0266017_CheckProjectileImpacts:
+ L0697_T_Thing = _vm->_dungeonMan->f161_getSquareFirstThing(L0704_ui_ProjectileMapX, L0705_ui_ProjectileMapY);
+ while (L0697_T_Thing != Thing::_endOfList) {
+ if (((L0697_T_Thing).getType() == k14_ProjectileThingType) &&
+ (_vm->_timeline->_g370_events[(((Projectile*)_vm->_dungeonMan->_g284_thingData[k14_ProjectileThingType])[(L0697_T_Thing).getIndex()])._eventIndex]._type != k48_TMEventTypeMoveProjectileIgnoreImpacts) && (AL0699_ui_ChampionOrCreatureOrdinal = L0707_auc_ChampionOrCreatureOrdinalInCell[(L0697_T_Thing).getCell()]) &&
+ _vm->_projexpl->f217_projectileHasImpactOccurred(L0702_i_ImpactType, srcMapX, srcMapY, _vm->M1_ordinalToIndex(AL0699_ui_ChampionOrCreatureOrdinal), L0697_T_Thing)) {
+ _vm->_projexpl->f214_projectileDeleteEvent(L0697_T_Thing);
+ if (_vm->_projexpl->_g364_creatureDamageOutcome == k2_outcomeKilledAllCreaturesInGroup) {
+ return true;
+ }
+ goto T0266017_CheckProjectileImpacts;
+ }
+ L0697_T_Thing = _vm->_dungeonMan->f159_getNextThing(L0697_T_Thing);
+ }
+ if (L0703_B_CheckDestinationSquareProjectileImpacts) {
+ srcMapX |= ((L0704_ui_ProjectileMapX = destMapX) + 1) << 8; /* Check impacts with projectiles on the destination square */
+ srcMapY |= (L0705_ui_ProjectileMapY = destMapY) << 8;
+ for (uint16 i = 0; i < 4; ++i)
+ L0707_auc_ChampionOrCreatureOrdinalInCell[i] = L0706_auc_IntermediaryChampionOrCreatureOrdinalInCell[i];
+ L0703_B_CheckDestinationSquareProjectileImpacts = false;
+ goto T0266017_CheckProjectileImpacts;
+ }
+ return false;
+}
+
+void MovesensMan::f268_addEvent(byte type, byte mapX, byte mapY, byte cell, byte effect, int32 time) {
+ TimelineEvent L0729_s_Event;
+
+
+ M33_setMapAndTime(L0729_s_Event._mapTime, _vm->_dungeonMan->_g272_currMapIndex, time);
+ L0729_s_Event._type = type;
+ L0729_s_Event._priority = 0;
+ L0729_s_Event._B._location._mapX = mapX;
+ L0729_s_Event._B._location._mapY = mapY;
+ L0729_s_Event._C.A._cell = cell;
+ L0729_s_Event._C.A._effect = effect;
+ _vm->_timeline->f238_addEventGetEventIndex(&L0729_s_Event);
+}
+
+int16 MovesensMan::f514_getSound(byte creatureType) {
+ if (_vm->_championMan->_g300_partyIsSleeping) {
+ return 35;
+ }
+
+ switch (creatureType) {
+ case k3_CreatureTypeWizardEyeFlyingEye:
+ case k8_CreatureTypeGhostRive:
+ case k11_CreatureTypeBlackFlame:
+ case k19_CreatureTypeMaterializerZytaz:
+ case k23_CreatureTypeLordChaos:
+ case k25_CreatureTypeLordOrder:
+ case k26_CreatureTypeGreyLord:
+ return 35;
+ case k2_CreatureTypeGiggler:
+ case k9_CreatureTypeStoneGolem:
+ case k10_CreatureTypeMummy:
+ case k14_CreatureTypeVexirk:
+ case k16_CreatureTypeTrolinAntman:
+ case k22_CreatureTypeDemon:
+ return k24_soundMOVE_MUMMY_TROLIN_ANTMAN_STONE_GOLEM_GIGGLER_VEXIRK_DEMON;
+ case k0_CreatureTypeGiantScorpionScorpion:
+ case k4_CreatureTypePainRatHellHound:
+ case k5_CreatureTypeRuster:
+ case k6_CreatureTypeScreamer:
+ case k7_CreatureTypeRockpile:
+ case k15_CreatureTypeMagnetaWormWorm:
+ case k21_CreatureTypeOitu:
+ return k26_soundMOVE_SCREAMER_ROCK_ROCKPILE_MAGENTA_WORM_WORM_PAIN_RAT_HELLHOUND_RUSTER_GIANT_SCORPION_SCORPION_OITU;
+ case k24_CreatureTypeRedDragon:
+ return k32_soundMOVE_RED_DRAGON;
+ case k12_CreatureTypeSkeleton:
+ return k33_soundMOVE_SKELETON;
+ case k18_CreatureTypeAnimatedArmourDethKnight:
+ return k22_soundMOVE_ANIMATED_ARMOUR_DETH_KNIGHT;
+ case k1_CreatureTypeSwampSlimeSlime:
+ case k20_CreatureTypeWaterElemental:
+ return k27_soundMOVE_SWAMP_SLIME_SLIME_DEVIL_WATER_ELEMENTAL;
+ case k13_CreatureTypeCouatl:
+ case k17_CreatureTypeGiantWaspMuncher:
+ return k23_soundMOVE_COUATL_GIANT_WASP_MUNCHER;
+ }
+ return -1000; // if this is returned, it's an error, this should break it good
+}
+
+int16 MovesensMan::f262_getTeleporterRotatedGroupResult(Teleporter* teleporter, Thing thing, uint16 mapIndex) {
+ int16 L0683_i_Rotation;
+ uint16 L0684_ui_GroupDirections;
+ uint16 L0685_ui_UpdatedGroupDirections;
+ Group* L0686_ps_Group;
+ uint16 L0687_ui_UpdatedGroupCells;
+ int16 L0688_i_CreatureIndex;
+ bool L0689_B_AbsoluteRotation;
+ uint16 L0690_ui_GroupCells;
+ int16 L0691_i_CreatureSize;
+ int16 L0692_i_RelativeRotation;
+
+ L0686_ps_Group = (Group*)_vm->_dungeonMan->f156_getThingData(thing);
+ L0683_i_Rotation = teleporter->getRotation();
+ L0684_ui_GroupDirections = _vm->_groupMan->f147_getGroupDirections(L0686_ps_Group, mapIndex);
+ if (L0689_B_AbsoluteRotation = teleporter->getAbsoluteRotation()) {
+ L0685_ui_UpdatedGroupDirections = L0683_i_Rotation;
+ } else {
+ L0685_ui_UpdatedGroupDirections = M21_normalizeModulo4(L0684_ui_GroupDirections + L0683_i_Rotation);
+ }
+ if ((L0687_ui_UpdatedGroupCells = _vm->_groupMan->f145_getGroupCells(L0686_ps_Group, mapIndex)) != k255_CreatureTypeSingleCenteredCreature) {
+ L0690_ui_GroupCells = L0687_ui_UpdatedGroupCells;
+ L0691_i_CreatureSize = getFlag(g243_CreatureInfo[L0686_ps_Group->_type]._attributes, k0x0003_MaskCreatureInfo_size);
+ L0692_i_RelativeRotation = M21_normalizeModulo4(4 + L0685_ui_UpdatedGroupDirections - L0684_ui_GroupDirections);
+ for (L0688_i_CreatureIndex = 0; L0688_i_CreatureIndex <= L0686_ps_Group->getCount(); L0688_i_CreatureIndex++) {
+ L0685_ui_UpdatedGroupDirections = _vm->_groupMan->f178_getGroupValueUpdatedWithCreatureValue(L0685_ui_UpdatedGroupDirections, L0688_i_CreatureIndex, L0689_B_AbsoluteRotation ? L0683_i_Rotation : M21_normalizeModulo4(L0684_ui_GroupDirections + L0683_i_Rotation));
+ if ((L0691_i_CreatureSize == k0_MaskCreatureSizeQuarter) && (L0692_i_RelativeRotation = !L0689_B_AbsoluteRotation)) {
+ L0692_i_RelativeRotation = L0683_i_Rotation;
+ }
+ if (L0692_i_RelativeRotation) {
+ L0687_ui_UpdatedGroupCells = _vm->_groupMan->f178_getGroupValueUpdatedWithCreatureValue(L0687_ui_UpdatedGroupCells, L0688_i_CreatureIndex, M21_normalizeModulo4(L0690_ui_GroupCells + L0692_i_RelativeRotation));
+ }
+ L0684_ui_GroupDirections >>= 2;
+ L0690_ui_GroupCells >>= 2;
+ }
+ }
+ _vm->_dungeonMan->f148_setGroupDirections(L0686_ps_Group, L0685_ui_UpdatedGroupDirections, mapIndex);
+ _vm->_dungeonMan->f146_setGroupCells(L0686_ps_Group, L0687_ui_UpdatedGroupCells, mapIndex);
+ if ((mapIndex == _vm->_dungeonMan->_g309_partyMapIndex) && (L0686_ps_Group->setBehaviour(k6_behavior_ATTACK))) {
+ return L0686_ps_Group->getActiveGroupIndex() + 2;
+ }
+ return 1;
+}
+
+Thing MovesensMan::f263_getTeleporterRotatedProjectileThing(Teleporter* teleporter, Thing projectileThing) {
+ int16 L0693_i_UpdatedDirection;
+ int16 L0694_i_Rotation;
+
+
+ L0693_i_UpdatedDirection = _g400_moveResultDir;
+ L0694_i_Rotation = teleporter->getRotation();
+ if (teleporter->getAbsoluteRotation()) {
+ L0693_i_UpdatedDirection = L0694_i_Rotation;
+ } else {
+ L0693_i_UpdatedDirection = M21_normalizeModulo4(L0693_i_UpdatedDirection + L0694_i_Rotation);
+ projectileThing = M15_thingWithNewCell(projectileThing, M21_normalizeModulo4((projectileThing).getCell() + L0694_i_Rotation));
+ }
+ _g400_moveResultDir = L0693_i_UpdatedDirection;
+ return projectileThing;
+}
+
+void MovesensMan::f276_sensorProcessThingAdditionOrRemoval(uint16 mapX, uint16 mapY, Thing thing, bool partySquare, bool addThing) {
+ Thing L0766_T_Thing;
+ int16 L0767_i_ThingType;
+ bool L0768_B_TriggerSensor;
+ Sensor* L0769_ps_Sensor;
+ uint16 L0770_ui_SensorTriggeredCell;
+ uint16 L0771_ui_ThingType;
+ bool L0772_B_SquareContainsObject;
+ bool L0773_B_SquareContainsGroup;
+ int16 L0774_i_ObjectType;
+ bool L0775_B_SquareContainsThingOfSameType;
+ bool L0776_B_SquareContainsThingOfDifferentType;
+ uint16 L0777_ui_Square;
+ int16 L0778_i_Effect;
+ int16 L0779_i_SensorData;
+
+
+ if (thing != Thing::_party) {
+ L0767_i_ThingType = (thing).getType();
+ L0774_i_ObjectType = _vm->_objectMan->f32_getObjectType(thing);
+ } else {
+ L0767_i_ThingType = kM1_PartyThingType;
+ L0774_i_ObjectType = kM1_IconIndiceNone;
+ }
+ if ((!addThing) && (L0767_i_ThingType != kM1_PartyThingType)) {
+ _vm->_dungeonMan->f164_unlinkThingFromList(thing, Thing(0), mapX, mapY);
+ }
+ if (Square(L0777_ui_Square = _vm->_dungeonMan->_g271_currMapData[mapX][mapY]).getType() == k0_ElementTypeWall) {
+ L0770_ui_SensorTriggeredCell = (thing).getCell();
+ } else {
+ L0770_ui_SensorTriggeredCell = (uint16)kM1_CellAny; // this will wrap around
+ }
+ L0772_B_SquareContainsObject = L0773_B_SquareContainsGroup = L0775_B_SquareContainsThingOfSameType = L0776_B_SquareContainsThingOfDifferentType = false;
+ L0766_T_Thing = _vm->_dungeonMan->f161_getSquareFirstThing(mapX, mapY);
+ if (L0770_ui_SensorTriggeredCell == kM1_CellAny) {
+ while (L0766_T_Thing != Thing::_endOfList) {
+ if ((L0771_ui_ThingType = (L0766_T_Thing).getType()) == k4_GroupThingType) {
+ L0773_B_SquareContainsGroup = true;
+ } else {
+ if ((L0771_ui_ThingType == k2_TextstringType) && (L0767_i_ThingType == kM1_PartyThingType) && addThing && !partySquare) {
+ _vm->_dungeonMan->f168_decodeText(_vm->_g353_stringBuildBuffer, L0766_T_Thing, k1_TextTypeMessage);
+ _vm->_textMan->f47_messageAreaPrintMessage(k15_ColorWhite, _vm->_g353_stringBuildBuffer);
+ } else {
+ if ((L0771_ui_ThingType > k4_GroupThingType) && (L0771_ui_ThingType < k14_ProjectileThingType)) {
+ L0772_B_SquareContainsObject = true;
+ L0775_B_SquareContainsThingOfSameType |= (_vm->_objectMan->f32_getObjectType(L0766_T_Thing) == L0774_i_ObjectType);
+ L0776_B_SquareContainsThingOfDifferentType |= (_vm->_objectMan->f32_getObjectType(L0766_T_Thing) != L0774_i_ObjectType);
+ }
+ }
+ }
+ L0766_T_Thing = _vm->_dungeonMan->f159_getNextThing(L0766_T_Thing);
+ }
+ } else {
+ while (L0766_T_Thing != Thing::_endOfList) {
+ if ((L0770_ui_SensorTriggeredCell == (L0766_T_Thing).getCell()) && ((L0766_T_Thing).getType() > k4_GroupThingType)) {
+ L0772_B_SquareContainsObject = true;
+ L0775_B_SquareContainsThingOfSameType |= (_vm->_objectMan->f32_getObjectType(L0766_T_Thing) == L0774_i_ObjectType);
+ L0776_B_SquareContainsThingOfDifferentType |= (_vm->_objectMan->f32_getObjectType(L0766_T_Thing) != L0774_i_ObjectType);
+ }
+ L0766_T_Thing = _vm->_dungeonMan->f159_getNextThing(L0766_T_Thing);
+ }
+ }
+ if (addThing && (L0767_i_ThingType != kM1_PartyThingType)) {
+ _vm->_dungeonMan->f163_linkThingToList(thing, Thing(0), mapX, mapY);
+ }
+ L0766_T_Thing = _vm->_dungeonMan->f161_getSquareFirstThing(mapX, mapY);
+ while (L0766_T_Thing != Thing::_endOfList) {
+ if ((L0771_ui_ThingType = (L0766_T_Thing).getType()) == k3_SensorThingType) {
+ L0769_ps_Sensor = (Sensor*)_vm->_dungeonMan->f156_getThingData(L0766_T_Thing);
+ if ((L0769_ps_Sensor)->getType() == k0_SensorDisabled)
+ goto T0276079;
+ L0779_i_SensorData = L0769_ps_Sensor->getData();
+ L0768_B_TriggerSensor = addThing;
+ if (L0770_ui_SensorTriggeredCell == kM1_CellAny) {
+ switch (L0769_ps_Sensor->getType()) {
+ case k1_SensorFloorTheronPartyCreatureObj:
+ if (partySquare || L0772_B_SquareContainsObject || L0773_B_SquareContainsGroup) /* BUG0_30 A floor sensor is not triggered when you put an object on the floor if a levitating creature is present on the same square. The condition to determine if the sensor should be triggered checks if there is a creature on the square but does not check whether the creature is levitating. While it is normal not to trigger the sensor if there is a non levitating creature on the square (because it was already triggered by the creature itself), a levitating creature should not prevent triggering the sensor with an object. */
+ goto T0276079;
+ break;
+ case k2_SensorFloorTheronPartyCreature:
+ if ((L0767_i_ThingType > k4_GroupThingType) || partySquare || L0773_B_SquareContainsGroup)
+ goto T0276079;
+ break;
+ case k3_SensorFloorParty:
+ if ((L0767_i_ThingType != kM1_PartyThingType) || (_vm->_championMan->_g305_partyChampionCount == 0))
+ goto T0276079;
+ if (L0779_i_SensorData == 0) {
+ if (partySquare)
+ goto T0276079;
+ } else {
+ if (!addThing) {
+ L0768_B_TriggerSensor = false;
+ } else {
+ L0768_B_TriggerSensor = (L0779_i_SensorData == _vm->M0_indexToOrdinal(_vm->_dungeonMan->_g308_partyDir));
+ }
+ }
+ break;
+ case k4_SensorFloorObj:
+ if ((L0779_i_SensorData != _vm->_objectMan->f32_getObjectType(thing)) || L0775_B_SquareContainsThingOfSameType)
+ goto T0276079;
+ break;
+ case k5_SensorFloorPartyOnStairs:
+ // Strangerke: Only present in v2.1, but it fixes a bug so we'll keep it.
+#ifdef COMPILE52_CSB21EN /* CHANGE8_05_FIX The wrong variable is replaced by the correct variable in the condition. The test should not be on L0771_ui_ThingType but on L0767_i_ThingType */
+ if ((L0767_i_ThingType != kM1_PartyThingType) || (M34_SQUARE_TYPE(L0777_ui_Square) != k3_ElementTypeStairs))
+#endif
+ goto T0276079;
+ break;
+ case k6_SensorFloorGroupGenerator:
+ goto T0276079;
+ case k7_SensorFloorCreature:
+ if ((L0767_i_ThingType > k4_GroupThingType) || (L0767_i_ThingType == kM1_PartyThingType) || L0773_B_SquareContainsGroup)
+ goto T0276079;
+ break;
+ case k8_SensorFloorPartyPossession:
+ if (L0767_i_ThingType != kM1_PartyThingType)
+ goto T0276079;
+ L0768_B_TriggerSensor = f274_sensorIsObjcetInPartyPossession(L0779_i_SensorData);
+ break;
+ case k9_SensorFloorVersionChecker:
+ if ((L0767_i_ThingType != kM1_PartyThingType) || !addThing || partySquare)
+ goto T0276079;
+ // Strangerke: 20 is a harcoded version of the game. later version uses 21. Not present in the original dungeons anyway.
+ L0768_B_TriggerSensor = (L0779_i_SensorData <= 20);
+ break;
+ default:
+ goto T0276079;
+ }
+ } else {
+ if (L0770_ui_SensorTriggeredCell != (L0766_T_Thing).getCell())
+ goto T0276079;
+ switch (L0769_ps_Sensor->getType()) {
+ case k1_SensorWallOrnClick:
+ if (L0772_B_SquareContainsObject)
+ goto T0276079;
+ break;
+ case k2_SensorWallOrnClickWithAnyObj:
+ if (L0775_B_SquareContainsThingOfSameType || (L0769_ps_Sensor->getData() != _vm->_objectMan->f32_getObjectType(thing)))
+ goto T0276079;
+ break;
+ case k3_SensorWallOrnClickWithSpecObj:
+ if (L0776_B_SquareContainsThingOfDifferentType || (L0769_ps_Sensor->getData() == _vm->_objectMan->f32_getObjectType(thing)))
+ goto T0276079;
+ break;
+ default:
+ goto T0276079;
+ }
+ }
+ L0768_B_TriggerSensor ^= L0769_ps_Sensor->getRevertEffectA();
+ if ((L0778_i_Effect = L0769_ps_Sensor->getEffectA()) == k3_SensorEffHold) {
+ L0778_i_Effect = L0768_B_TriggerSensor ? k0_SensorEffSet : k1_SensorEffClear;
+ } else {
+ if (!L0768_B_TriggerSensor)
+ goto T0276079;
+ }
+ if (L0769_ps_Sensor->getAudibleA()) {
+ warning("MISSING CODE: F0064_SOUND_RequestPlay_CPSD");
+ }
+ f272_sensorTriggerEffect(L0769_ps_Sensor, L0778_i_Effect, mapX, mapY, (uint16)kM1_CellAny); // this will wrap around
+ goto T0276079;
+ }
+ if (L0771_ui_ThingType >= k4_GroupThingType)
+ break;
+T0276079:
+ L0766_T_Thing = _vm->_dungeonMan->f159_getNextThing(L0766_T_Thing);
+ }
+ f271_processRotationEffect();
+}
+
+bool MovesensMan::f274_sensorIsObjcetInPartyPossession(int16 objectType) {
+ int16 L0742_i_ChampionIndex;
+ uint16 L0743_ui_SlotIndex = 0;
+ Thing L0744_T_Thing = Thing::_none;
+ Champion* L0745_ps_Champion;
+ Thing* L0746_pT_Thing = nullptr;
+ int16 L0747_i_ObjectType;
+ bool L0748_B_LeaderHandObjectProcessed;
+ Container* L0749_ps_Container;
+
+
+ L0748_B_LeaderHandObjectProcessed = false;
+ for (L0742_i_ChampionIndex = k0_ChampionFirst, L0745_ps_Champion = _vm->_championMan->_gK71_champions; L0742_i_ChampionIndex < _vm->_championMan->_g305_partyChampionCount; L0742_i_ChampionIndex++, L0745_ps_Champion++) {
+ if (L0745_ps_Champion->_currHealth) {
+ L0746_pT_Thing = L0745_ps_Champion->_slots;
+ for (L0743_ui_SlotIndex = k0_ChampionSlotReadyHand; (L0743_ui_SlotIndex < k30_ChampionSlotChest_1) && !L0748_B_LeaderHandObjectProcessed; L0743_ui_SlotIndex++) {
+ L0744_T_Thing = *L0746_pT_Thing++;
+T0274003:
+ if ((L0747_i_ObjectType = _vm->_objectMan->f32_getObjectType(L0744_T_Thing)) == objectType) {
+ return true;
+ }
+ if (L0747_i_ObjectType == k144_IconIndiceContainerChestClosed) {
+ L0749_ps_Container = (Container*)_vm->_dungeonMan->f156_getThingData(L0744_T_Thing);
+ L0744_T_Thing = L0749_ps_Container->getSlot();
+ while (L0744_T_Thing != Thing::_endOfList) {
+ if (_vm->_objectMan->f32_getObjectType(L0744_T_Thing) == objectType) {
+ return true;
+ }
+ L0744_T_Thing = _vm->_dungeonMan->f159_getNextThing(L0744_T_Thing);
+ }
+ }
+ }
+ }
+ }
+ if (!L0748_B_LeaderHandObjectProcessed) {
+ L0748_B_LeaderHandObjectProcessed = true;
+ L0744_T_Thing = _vm->_championMan->_g414_leaderHandObject;
+ goto T0274003;
+ }
+ return false;
+}
+
+void MovesensMan::f272_sensorTriggerEffect(Sensor* sensor, int16 effect, int16 mapX, int16 mapY, uint16 cell) {
+ byte g59_squareTypeToEventType[7] = { // @ G0059_auc_Graphic562_SquareTypeToEventType
+ k6_TMEventTypeWall,
+ k5_TMEventTypeCorridor,
+ k9_TMEventTypePit,
+ k0_TMEventTypeNone,
+ k10_TMEventTypeDoor,
+ k8_TMEventTypeTeleporter,
+ k7_TMEventTypeFakeWall}; /* 1 byte of padding inserted by compiler */
+
+ int16 L0736_i_TargetMapX;
+ int16 L0737_i_TargetMapY;
+ register long L0738_l_Time;
+ uint16 L0739_ui_SquareType;
+ uint16 L0740_ui_TargetCell;
+
+
+ if (sensor->getOnlyOnce()) {
+ sensor->setTypeDisabled();
+ }
+ L0738_l_Time = _vm->_g313_gameTime + sensor->getValue();
+ if (sensor->getLocalEffect()) {
+ f270_sensorTriggetLocalEffect(sensor->M49_localEffect(), mapX, mapY, cell);
+ } else {
+ L0736_i_TargetMapX = sensor->getTargetMapX();
+ L0737_i_TargetMapY = sensor->getTargetMapY();
+ L0739_ui_SquareType = Square(_vm->_dungeonMan->_g271_currMapData[L0736_i_TargetMapX][L0737_i_TargetMapY]).getType();
+ if (L0739_ui_SquareType == k0_ElementTypeWall) {
+ L0740_ui_TargetCell = sensor->getTargetCell();
+ } else {
+ L0740_ui_TargetCell = k0_CellNorthWest;
+ }
+ f268_addEvent(g59_squareTypeToEventType[L0739_ui_SquareType], L0736_i_TargetMapX, L0737_i_TargetMapY, L0740_ui_TargetCell, effect, L0738_l_Time);
+ }
+}
+
+void MovesensMan::f270_sensorTriggetLocalEffect(int16 localEffect, int16 effX, int16 effY, int16 effCell) {
+ if (localEffect == k10_SensorEffAddExp) {
+ f269_sensorAddSkillExperience(k8_ChampionSkillSteal, 300, localEffect != kM1_CellAny);
+ return;
}
- warning("MISSING CODE: F0271_SENSOR_ProcessRotationEffect");
- return atLeastOneSensorWasTriggered;
+ _g403_sensorRotationEffect = localEffect;
+ _g404_sensorRotationEffMapX = effX;
+ _g405_sensorRotationEffMapY = effY;
+ _g406_sensorRotationEffCell = effCell;
}
+void MovesensMan::f269_sensorAddSkillExperience(int16 skillIndex, uint16 exp, bool leaderOnly) {
+ int16 L0730_i_ChampionIndex;
+ Champion* L0731_ps_Champion;
+
+ if (leaderOnly) {
+ if (_vm->_championMan->_g411_leaderIndex != kM1_ChampionNone) {
+ _vm->_championMan->f304_addSkillExperience(_vm->_championMan->_g411_leaderIndex, skillIndex, exp);
+ }
+ } else {
+ exp /= _vm->_championMan->_g305_partyChampionCount;
+ for (L0730_i_ChampionIndex = k0_ChampionFirst, L0731_ps_Champion = _vm->_championMan->_gK71_champions; L0730_i_ChampionIndex < _vm->_championMan->_g305_partyChampionCount; L0730_i_ChampionIndex++, L0731_ps_Champion++) {
+ if (L0731_ps_Champion->_currHealth) {
+ _vm->_championMan->f304_addSkillExperience(L0730_i_ChampionIndex, skillIndex, exp);
+ }
+ }
+ }
+}
+
+void MovesensMan::f271_processRotationEffect() {
+ Thing L0732_T_FirstSensorThing;
+ Thing L0733_T_LastSensorThing;
+ Sensor* L0734_ps_FirstSensor;
+ Sensor* L0735_ps_LastSensor;
+
+
+ if (_g403_sensorRotationEffect == kM1_SensorEffNone) {
+ return;
+ }
+ switch (_g403_sensorRotationEffect) {
+ case k1_SensorEffClear:
+ case k2_SensorEffToggle:
+ L0732_T_FirstSensorThing = _vm->_dungeonMan->f161_getSquareFirstThing(_g404_sensorRotationEffMapX, _g405_sensorRotationEffMapY);
+ while (((L0732_T_FirstSensorThing).getType() != k3_SensorThingType) || ((_g406_sensorRotationEffCell != kM1_CellAny) && ((L0732_T_FirstSensorThing).getCell() != _g406_sensorRotationEffCell))) {
+ L0732_T_FirstSensorThing = _vm->_dungeonMan->f159_getNextThing(L0732_T_FirstSensorThing);
+ }
+ L0734_ps_FirstSensor = (Sensor*)_vm->_dungeonMan->f156_getThingData(L0732_T_FirstSensorThing);
+ L0733_T_LastSensorThing = L0734_ps_FirstSensor->getNextThing();
+ while ((L0733_T_LastSensorThing != Thing::_endOfList) && (((L0733_T_LastSensorThing).getType() != k3_SensorThingType) || ((_g406_sensorRotationEffCell != kM1_CellAny) && ((L0733_T_LastSensorThing).getCell() != _g406_sensorRotationEffCell)))) {
+ L0733_T_LastSensorThing = _vm->_dungeonMan->f159_getNextThing(L0733_T_LastSensorThing);
+ }
+ if (L0733_T_LastSensorThing == Thing::_endOfList)
+ break;
+ _vm->_dungeonMan->f164_unlinkThingFromList(L0732_T_FirstSensorThing, Thing(0), _g404_sensorRotationEffMapX, _g405_sensorRotationEffMapY);
+ L0735_ps_LastSensor = (Sensor*)_vm->_dungeonMan->f156_getThingData(L0733_T_LastSensorThing);
+ L0733_T_LastSensorThing = _vm->_dungeonMan->f159_getNextThing(L0733_T_LastSensorThing);
+ while (((L0733_T_LastSensorThing != Thing::_endOfList) && ((L0733_T_LastSensorThing).getType() == k3_SensorThingType))) {
+ if ((_g406_sensorRotationEffCell == kM1_CellAny) || ((L0733_T_LastSensorThing).getCell() == _g406_sensorRotationEffCell)) {
+ L0735_ps_LastSensor = (Sensor*)_vm->_dungeonMan->f156_getThingData(L0733_T_LastSensorThing);
+ }
+ L0733_T_LastSensorThing = _vm->_dungeonMan->f159_getNextThing(L0733_T_LastSensorThing);
+ }
+ L0734_ps_FirstSensor->setNextThing(L0735_ps_LastSensor->getNextThing());
+ L0735_ps_LastSensor->setNextThing(L0732_T_FirstSensorThing);
+ }
+ _g403_sensorRotationEffect = kM1_SensorEffNone;
+}
+
+void MovesensMan::f265_createEvent60to61_moveGroup(Thing groupThing, int16 mapX, int16 mapY, int16 mapIndex, bool audible) {
+ TimelineEvent L0696_s_Event;
+
+ M33_setMapAndTime(L0696_s_Event._mapTime, mapIndex, _vm->_g313_gameTime + 5);
+ L0696_s_Event._type = audible ? k61_TMEventTypeMoveGroupAudible : k60_TMEventTypeMoveGroupSilent;
+ L0696_s_Event._priority = 0;
+ L0696_s_Event._B._location._mapX = mapX;
+ L0696_s_Event._B._location._mapY = mapY;
+ L0696_s_Event._C._slot = groupThing;
+ _vm->_timeline->f238_addEventGetEventIndex(&L0696_s_Event);
+}
+
+Thing MovesensMan::f273_sensorGetObjectOfTypeInCell(int16 mapX, int16 mapY, int16 cell, int16 objectType) {
+ Thing L0741_T_Thing;
+
+
+ L0741_T_Thing = _vm->_dungeonMan->f162_getSquareFirstObject(mapX, mapY);
+ while (L0741_T_Thing != Thing::_endOfList) {
+ if (_vm->_objectMan->f32_getObjectType(L0741_T_Thing) == objectType) {
+ if ((cell == kM1_CellAny) || ((L0741_T_Thing.getCell()) == cell)) {
+ return L0741_T_Thing;
+ }
+ }
+ L0741_T_Thing = _vm->_dungeonMan->f159_getNextThing(L0741_T_Thing);
+ }
+ return Thing::_none;
+}
}
diff --git a/engines/dm/movesens.h b/engines/dm/movesens.h
index 6a8a53ac96..79a1fa9bf1 100644
--- a/engines/dm/movesens.h
+++ b/engines/dm/movesens.h
@@ -32,12 +32,43 @@
#include "dm.h"
namespace DM {
+ class Sensor;
+ class Teleporter;
-class MovesensMan {
+ class MovesensMan {
DMEngine *_vm;
public:
+ int16 _g397_moveResultMapX; // @ G0397_i_MoveResultMapX
+ int16 _g398_moveResultMapY; // @ G0398_i_MoveResultMapY
+ uint16 _g399_moveResultMapIndex; // @ G0399_ui_MoveResultMapIndex
+ int16 _g400_moveResultDir; // @ G0400_i_MoveResultDirection
+ uint16 _g401_moveResultCell; // @ G0401_ui_MoveResultCell
+ bool _g402_useRopeToClimbDownPit; // @ G0402_B_UseRopeToClimbDownPit
+ int16 _g403_sensorRotationEffect; // @ G0403_i_SensorRotationEffect
+ int16 _g404_sensorRotationEffMapX; // @ G0404_i_SensorRotationEffectMapX
+ int16 _g405_sensorRotationEffMapY; // @ G0405_i_SensorRotationEffectMapY
+ int16 _g406_sensorRotationEffCell; // @ G0406_i_SensorRotationEffectCell
explicit MovesensMan(DMEngine *vm);
bool f275_sensorIsTriggeredByClickOnWall(int16 mapX, int16 mapY, uint16 cellParam); // @ F0275_SENSOR_IsTriggeredByClickOnWall
+ bool f267_getMoveResult(Thing thing, int16 mapX, int16 mapY, int16 destMapX, int16 destMapY); // @ F0267_MOVE_GetMoveResult_CPSCE
+ bool f264_isLevitating(Thing thing); // @ F0264_MOVE_IsLevitating
+ bool f266_moveIsKilledByProjectileImpact(int16 srcMapX, int16 srcMapY, int16 destMapX, int16 destMapY, Thing thing); // @ F0266_MOVE_IsKilledByProjectileImpact
+ void f268_addEvent(byte type, byte mapX, byte mapY, byte cell, byte effect, int32 time); // @ F0268_SENSOR_AddEvent
+ int16 f514_getSound(byte creatureType); // @ F0514_MOVE_GetSound
+ int16 f262_getTeleporterRotatedGroupResult(Teleporter *teleporter, Thing thing, uint16 mapIndex);// @ F0262_MOVE_GetTeleporterRotatedGroupResult
+ Thing f263_getTeleporterRotatedProjectileThing(Teleporter *teleporter, Thing projectileThing); // @ F0263_MOVE_GetTeleporterRotatedProjectileThing
+ void f276_sensorProcessThingAdditionOrRemoval(uint16 mapX, uint16 mapY, Thing thing, bool partySquare, bool addThing);// @ F0276_SENSOR_ProcessThingAdditionOrRemoval
+ bool f274_sensorIsObjcetInPartyPossession(int16 objectType); // @ F0274_SENSOR_IsObjectInPartyPossession
+ void f272_sensorTriggerEffect(Sensor *sensor, int16 effect, int16 mapX, int16 mapY, uint16 cell); // @ F0272_SENSOR_TriggerEffect
+ void f270_sensorTriggetLocalEffect(int16 localEffect, int16 effX, int16 effY, int16 effCell); // @ F0270_SENSOR_TriggerLocalEffect
+ void f269_sensorAddSkillExperience(int16 skillIndex, uint16 exp, bool leaderOnly); // @ F0269_SENSOR_AddSkillExperience
+ void f271_processRotationEffect();// @ F0271_SENSOR_ProcessRotationEffect
+ void f265_createEvent60to61_moveGroup(Thing groupThing, int16 mapX, int16 mapY, int16 mapIndex, bool audible); // @ F0265_MOVE_CreateEvent60To61_MoveGroup
+ Thing f273_sensorGetObjectOfTypeInCell(int16 mapX, int16 mapY, int16 cell, int16 objectType); // @ F0273_SENSOR_GetObjectOfTypeInCell
+
+
+
+
};
}
diff --git a/engines/dm/objectman.cpp b/engines/dm/objectman.cpp
index ff7ff4dd9d..157cfd8ad8 100644
--- a/engines/dm/objectman.cpp
+++ b/engines/dm/objectman.cpp
@@ -252,4 +252,10 @@ void ObjectMan::f34_drawLeaderObjectName(Thing thing) {
IconIndice ObjectMan::f39_getIconIndexInSlotBox(uint16 slotBoxIndex) {
return (IconIndice)_g30_slotBoxes[slotBoxIndex]._iconIndex;
}
+
+void ObjectMan::f35_clearLeaderObjectName() {
+ static Box g28_BoxLeaderHandObjectName(233, 319, 33, 38); // @ G0028_s_Graphic562_Box_LeaderHandObjectName
+ _vm->_displayMan->D24_fillScreenBox(g28_BoxLeaderHandObjectName, k0_ColorBlack);
+}
+
}
diff --git a/engines/dm/objectman.h b/engines/dm/objectman.h
index b01957da8a..3f90f805d4 100644
--- a/engines/dm/objectman.h
+++ b/engines/dm/objectman.h
@@ -64,6 +64,7 @@ public:
void f38_drawIconInSlotBox(uint16 slotBoxIndex, int16 iconIndex); // @ F0038_OBJECT_DrawIconInSlotBox
void f34_drawLeaderObjectName(Thing thing); // @ F0034_OBJECT_DrawLeaderHandObjectName
IconIndice f39_getIconIndexInSlotBox(uint16 slotBoxIndex); // @ F0039_OBJECT_GetIconIndexInSlotBox
+ void f35_clearLeaderObjectName(); // @ F0035_OBJECT_ClearLeaderHandObjectName
};
diff --git a/engines/dm/projexpl.cpp b/engines/dm/projexpl.cpp
new file mode 100644
index 0000000000..95d40bbaea
--- /dev/null
+++ b/engines/dm/projexpl.cpp
@@ -0,0 +1,418 @@
+/* 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 "projexpl.h"
+#include "dungeonman.h"
+#include "timeline.h"
+#include "group.h"
+#include "objectman.h"
+#include "movesens.h"
+
+namespace DM {
+
+ProjExpl::ProjExpl(DMEngine* vm) : _vm(vm) {
+ _g361_lastCreatureAttackTime = -200;
+}
+
+void ProjExpl::f212_projectileCreate(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->f166_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 = M15_thingWithNewCell(L0466_T_ProjectileThing, cell);
+ L0467_ps_Projectile = (Projectile*)_vm->_dungeonMan->f156_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->f163_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 */
+ M33_setMapAndTime(L0468_s_Event._mapTime, _vm->_dungeonMan->_g272_currMapIndex, _vm->_g313_gameTime + 1);
+ if (_g365_createLanucherProjectile) {
+ 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;
+ 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->f238_addEventGetEventIndex(&L0468_s_Event);
+}
+
+bool ProjExpl::f217_projectileHasImpactOccurred(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->f156_getThingData(Thing(projectileThing));
+ L0501_i_MapXCombo = mapXCombo;
+ L0502_i_MapYCombo = mapYCombo;
+ L0509_B_RemovePotion = false;
+ _g364_creatureDamageOutcome = k0_outcomeKilledNoCreaturesInGroup;
+ if ((L0510_i_ProjectileAssociatedThingType = (L0486_T_ProjectileAssociatedThing = L0490_ps_Projectile->_slot).getType()) == k8_PotionThingType) {
+ L0491_ps_Group = (Group*)_vm->_dungeonMan->f156_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;
+ }
+ }
+ 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->_g271_currMapData[AP0454_i_ProjectileTargetMapX][AP0455_i_ProjectileTargetMapY]).getDoorState();
+ L0494_ps_Door = (Door*)_vm->_dungeonMan->f157_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->_movsens->f268_addEvent(k10_TMEventTypeDoor, AP0454_i_ProjectileTargetMapX, AP0455_i_ProjectileTargetMapY, 0, k2_SensorEffToggle, _vm->_g313_gameTime + 1);
+ }
+ break;
+ }
+ if ((AL0487_i_DoorState == k5_doorState_DESTROYED) ||
+ (AL0487_i_DoorState <= k1_doorState_FOURTH) ||
+ (getFlag(_vm->_dungeonMan->_g275_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(g237_ObjectInfo[_vm->_dungeonMan->f141_getObjectInfoIndex(L0486_T_ProjectileAssociatedThing)].getAllowedSlots(), k0x0100_ObjectAllowedSlotPouchPassAndThroughDoors)
+ && ((L0510_i_ProjectileAssociatedThingType != k10_JunkThingType) ||
+ ((AL0487_i_IconIndex = _vm->_objectMan->f33_getIconIndex(L0486_T_ProjectileAssociatedThing)) < 0) ||
+ (!((AL0487_i_IconIndex >= k176_IconIndiceJunkIronKey) && (AL0487_i_IconIndex <= k191_IconIndiceJunkMasterKey))))
+ )))) { /* ASSEMBLY_COMPILATION_DIFFERENCE jmp */
+ return false;
+ }
+ L0488_i_Attack = f216_projectileGetImpactAttack(L0490_ps_Projectile, L0486_T_ProjectileAssociatedThing) + 1;
+ _vm->_groupMan->f232_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->f285_getIndexInCell(cell)) < 0) {
+ return false;
+ }
+ L0489_i_ChampionAttack = L0488_i_Attack = f216_projectileGetImpactAttack(L0490_ps_Projectile, L0486_T_ProjectileAssociatedThing);
+ break;
+ case kM1_CreatureElemType:
+ L0491_ps_Group = (Group*)_vm->_dungeonMan->f156_getThingData(_vm->_groupMan->f175_groupGetThing(AP0454_i_ProjectileTargetMapX, AP0455_i_ProjectileTargetMapY));
+ if (!(L0512_ui_CreatureIndex = _vm->_groupMan->f176_getCreatureOrdinalInCell(L0491_ps_Group, cell))) {
+ return false;
+ }
+ L0512_ui_CreatureIndex--;
+ L0493_ps_CreatureInfo = &g243_CreatureInfo[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 + f216_projectileGetImpactAttack(L0490_ps_Projectile, L0486_T_ProjectileAssociatedThing));
+ goto T0217044;
+ }
+ if (getFlag(L0493_ps_CreatureInfo->_attributes, k0x0040_MaskCreatureInfo_nonMaterial) && (L0486_T_ProjectileAssociatedThing != Thing::_explHarmNonMaterial)) {
+ return false;
+ }
+ if (L0488_i_Attack = (uint16)((unsigned long)f216_projectileGetImpactAttack(L0490_ps_Projectile, L0486_T_ProjectileAssociatedThing) << 6) / L0493_ps_CreatureInfo->_defense) {
+ if ((AL0487_i_Outcome = _vm->_groupMan->f190_groupGetDamageCreatureOutcome(L0491_ps_Group, L0512_ui_CreatureIndex, AP0454_i_ProjectileTargetMapX, AP0455_i_ProjectileTargetMapY, L0488_i_Attack + _vm->_groupMan->f192_groupGetResistanceAdjustedPoisonAttack(L0511_ui_CreatureType, _g366_projectilePoisonAttack), true)) != k0_outcomeKilledNoCreaturesInGroup) {
+ _vm->_groupMan->f209_processEvents29to41(AP0454_i_ProjectileTargetMapX, AP0455_i_ProjectileTargetMapY, kM2_TMEventTypeCreateReactionEvent30HitByProjectile, 0);
+ }
+ _g364_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->f156_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->f321_addPendingDamageAndWounds_getDamage(AP0456_i_ChampionIndex, L0488_i_Attack, k0x0004_ChampionWoundHead | k0x0008_ChampionWoundTorso, _g367_projectileAttackType) && _g366_projectilePoisonAttack && _vm->getRandomNumber(2)) {
+ _vm->_championMan->f322_championPoison(AP0456_i_ChampionIndex, _g366_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;
+ f213_explosionCreate(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;
+ }
+ }
+ warning("MISSING CODE: F0064_SOUND_RequestPlay_CPSD");
+ }
+T0217044:
+ if (L0509_B_RemovePotion) {
+ L0492_ps_Potion->_nextThing = Thing::_none;
+ L0490_ps_Projectile->_slot = L0498_T_ExplosionThing;
+ }
+ _vm->_dungeonMan->f164_unlinkThingFromList(projectileThing, Thing(0), L0499_i_ProjectileMapX, L0500_i_ProjectileMapY);
+ f215_projectileDelete(projectileThing, L0497_pT_GroupSlot, L0499_i_ProjectileMapX, L0500_i_ProjectileMapY);
+ return true;
+}
+
+uint16 ProjExpl::f216_projectileGetImpactAttack(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;
+
+
+ _g366_projectilePoisonAttack = 0;
+ _g367_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->f158_getWeaponInfo(thing);
+ AL0483_ui_Attack = L0485_ps_WeaponInfo->_kineticEnergy;
+ _g367_projectileAttackType = k3_attackType_BLUNT;
+ } else {
+ AL0483_ui_Attack = _vm->getRandomNumber(4);
+ }
+ AL0483_ui_Attack += _vm->_dungeonMan->f140_getObjectWeight(thing) >> 1;
+ } else {
+ if (thing == Thing::_explSlime) {
+ AL0483_ui_Attack = _vm->getRandomNumber(16);
+ _g366_projectilePoisonAttack = AL0483_ui_Attack + 10;
+ AL0483_ui_Attack += _vm->getRandomNumber(32);
+ } else {
+ if (thing.toUint16() >= Thing::_explHarmNonMaterial.toUint16()) {
+ _g367_projectileAttackType = k5_attackType_MAGIC;
+ if (thing == Thing::_explPoisonBolt) {
+ _g366_projectilePoisonAttack = L0484_ui_KineticEnergy;
+ return 1;
+ }
+ return 0;
+ }
+ _g367_projectileAttackType = k1_attackType_FIRE;
+ AL0483_ui_Attack = _vm->getRandomNumber(16) + _vm->getRandomNumber(16) + 10;
+ if (thing == Thing::_explLightningBolt) {
+ _g367_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::f213_explosionCreate(Thing explThing, uint16 attack, uint16 mapXCombo, uint16 mapYCombo, uint16 cell) {
+#define AP0443_ui_ProjectileMapX mapXCombo
+#define AP0444_ui_ProjectileMapY mapYCombo
+ Explosion* L0470_ps_Explosion;
+ CreatureInfo* L0471_ps_CreatureInfo;
+ Group* L0472_ps_Group;
+ Thing L0473_T_Thing;
+ int16 L0474_i_ProjectileTargetMapX;
+ int16 L0475_i_ProjectileTargetMapY;
+ int16 L0469_i_CreatureFireResistance;
+ TimelineEvent L0476_s_Event;
+
+
+ if ((L0473_T_Thing = _vm->_dungeonMan->f166_getUnusedThing(k15_ExplosionThingType)) == Thing::_none) {
+ return;
+ }
+ L0470_ps_Explosion = &((Explosion*)_vm->_dungeonMan->_g284_thingData[k15_ExplosionThingType])[(L0473_T_Thing).getIndex()];
+ 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 = M15_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()) {
+ warning("MISING CODE: F0064_SOUND_RequestPlay_CPSD");
+ } else {
+ if (explThing != Thing::_explSmoke) {
+ warning("MISSING CODE: F0064_SOUND_RequestPlay_CPSD");
+ }
+ }
+ _vm->_dungeonMan->f163_linkThingToList(L0473_T_Thing, Thing(0), AP0443_ui_ProjectileMapX, AP0444_ui_ProjectileMapY);
+ M33_setMapAndTime(L0476_s_Event._mapTime, _vm->_dungeonMan->_g272_currMapIndex, _vm->_g313_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;
+ L0476_s_Event._B._location._mapX = AP0443_ui_ProjectileMapX;
+ L0476_s_Event._B._location._mapY = AP0444_ui_ProjectileMapY;
+ _vm->_timeline->f238_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->_g272_currMapIndex == _vm->_dungeonMan->_g309_partyMapIndex) && (AP0443_ui_ProjectileMapX == _vm->_dungeonMan->_g306_partyMapX) && (AP0444_ui_ProjectileMapY == _vm->_dungeonMan->_g307_partyMapY)) {
+ _vm->_championMan->f324_damageAll_getDamagedChampionCount(attack, k0x0001_ChampionWoundReadHand | k0x0002_ChampionWoundActionHand | k0x0004_ChampionWoundHead | k0x0008_ChampionWoundTorso | k0x0010_ChampionWoundLegs | k0x0020_ChampionWoundFeet, k1_attackType_FIRE);
+ } else {
+ if ((L0473_T_Thing = _vm->_groupMan->f175_groupGetThing(AP0443_ui_ProjectileMapX, AP0444_ui_ProjectileMapY)) != Thing::_endOfList) { /* ASSEMBLY_COMPILATION_DIFFERENCE jmp */
+ L0472_ps_Group = (Group*)_vm->_dungeonMan->f156_getThingData(L0473_T_Thing);
+ L0471_ps_CreatureInfo = &g243_CreatureInfo[L0472_ps_Group->_type];
+ if ((L0469_i_CreatureFireResistance = (L0471_ps_CreatureInfo->M60_getFireResistance())) != k15_immuneToFire) {
+ if (getFlag(L0471_ps_CreatureInfo->_attributes, k0x0040_MaskCreatureInfo_nonMaterial)) {
+ attack >>= 2;
+ }
+ if ((attack -= _vm->getRandomNumber((L0469_i_CreatureFireResistance << 1) + 1)) > 0) {
+ _g364_creatureDamageOutcome = _vm->_groupMan->f191_getDamageAllCreaturesOutcome(L0472_ps_Group, AP0443_ui_ProjectileMapX, AP0444_ui_ProjectileMapY, attack, true);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+int16 ProjExpl::f218_projectileGetImpactCount(int16 impactType, int16 mapX, int16 mapY, int16 cell) {
+ Thing L0513_T_Thing;
+ int16 L0514_i_ImpactCount;
+
+
+ L0514_i_ImpactCount = 0;
+ _g364_creatureDamageOutcome = k0_outcomeKilledNoCreaturesInGroup;
+T0218001:
+ L0513_T_Thing = _vm->_dungeonMan->f161_getSquareFirstThing(mapX, mapY);
+ while (L0513_T_Thing != Thing::_endOfList) {
+ if (((L0513_T_Thing).getType() == k14_ProjectileThingType) &&
+ ((L0513_T_Thing).getCell() == cell) &&
+ f217_projectileHasImpactOccurred(impactType, mapX, mapY, cell, L0513_T_Thing)) {
+ f214_projectileDeleteEvent(L0513_T_Thing);
+ L0514_i_ImpactCount++;
+ if ((impactType == kM1_CreatureElemType) && (_g364_creatureDamageOutcome == k2_outcomeKilledAllCreaturesInGroup))
+ break;
+ goto T0218001;
+ }
+ L0513_T_Thing = _vm->_dungeonMan->f159_getNextThing(L0513_T_Thing);
+ }
+ return L0514_i_ImpactCount;
+}
+
+void ProjExpl::f214_projectileDeleteEvent(Thing thing) {
+ Projectile* L0477_ps_Projectile;
+
+
+ L0477_ps_Projectile = (Projectile*)_vm->_dungeonMan->f156_getThingData(thing);
+ _vm->_timeline->f237_deleteEvent(L0477_ps_Projectile->_eventIndex);
+}
+
+void ProjExpl::f215_projectileDelete(Thing projectileThing, Thing* groupSlot, int16 mapX, int16 mapY) {
+ Thing L0478_T_PreviousThing;
+ Thing L0479_T_Thing;
+ Projectile* L0480_ps_Projectile;
+ Thing* L0481_ps_Generic;
+
+ L0480_ps_Projectile = (Projectile*)_vm->_dungeonMan->f156_getThingData(projectileThing);
+ if ((L0479_T_Thing = L0480_ps_Projectile->_slot).getType() != k15_ExplosionThingType) {
+ if (groupSlot != NULL) {
+ if ((L0478_T_PreviousThing = *groupSlot) == Thing::_endOfList) {
+ L0481_ps_Generic = (Thing*)_vm->_dungeonMan->f156_getThingData(L0479_T_Thing);
+ *L0481_ps_Generic = Thing::_endOfList;
+ *groupSlot = L0479_T_Thing;
+ } else {
+ _vm->_dungeonMan->f163_linkThingToList(L0479_T_Thing, L0478_T_PreviousThing, kM1_MapXNotOnASquare, 0);
+ }
+ } else {
+ _vm->_movsens->f267_getMoveResult(Thing((L0479_T_Thing).getTypeAndIndex() | getFlag(projectileThing.toUint16(), 0xC)), -2, 0, mapX, mapY);
+ }
+ }
+ L0480_ps_Projectile->_nextThing = Thing::_none;
+}
+}
diff --git a/engines/dm/projexpl.h b/engines/dm/projexpl.h
new file mode 100644
index 0000000000..15f74543fc
--- /dev/null
+++ b/engines/dm/projexpl.h
@@ -0,0 +1,101 @@
+/* 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/)
+*/
+#ifndef DM_PROJEXPL_H
+#define DM_PROJEXPL_H
+
+#include "dm.h"
+
+
+#define k0_outcomeKilledNoCreaturesInGroup 0 // @ C0_OUTCOME_KILLED_NO_CREATURES_IN_GROUP
+#define k1_outcomeKilledSomeCreaturesInGroup 1 // @ C1_OUTCOME_KILLED_SOME_CREATURES_IN_GROUP
+#define k2_outcomeKilledAllCreaturesInGroup 2 // @ C2_OUTCOME_KILLED_ALL_CREATURES_IN_GROUP
+
+#define k00_soundMETALLIC_THUD 0 // @ C00_SOUND_METALLIC_THUD
+#define k01_soundSWITCH 1 // @ C01_SOUND_SWITCH
+#define k02_soundDOOR_RATTLE 2 // @ C02_SOUND_DOOR_RATTLE
+#define k03_soundATTACK_PAIN_RAT_HELLHOUND_RED_DRAGON 3 // @ C03_SOUND_ATTACK_PAIN_RAT_HELLHOUND_RED_DRAGON
+#define k04_soundWOODEN_THUD_ATTACK_TROLIN_ANTMAN_STONE_GOLEM 4 // @ C04_SOUND_WOODEN_THUD_ATTACK_TROLIN_ANTMAN_STONE_GOLEM
+#define k05_soundSTRONG_EXPLOSION 5 // @ C05_SOUND_STRONG_EXPLOSION
+#define k06_soundSCREAM 6 // @ C06_SOUND_SCREAM
+#define k07_soundATTACK_MUMMY_GHOST_RIVE 7 // @ C07_SOUND_ATTACK_MUMMY_GHOST_RIVE
+#define k08_soundSWALLOW 8 // @ C08_SOUND_SWALLOW
+#define k09_soundCHAMPION_0_DAMAGED 9 // @ C09_SOUND_CHAMPION_0_DAMAGED
+#define k10_soundCHAMPION_1_DAMAGED 10 // @ C10_SOUND_CHAMPION_1_DAMAGED
+#define k11_soundCHAMPION_2_DAMAGED 11 // @ C11_SOUND_CHAMPION_2_DAMAGED
+#define k12_soundCHAMPION_3_DAMAGED 12 // @ C12_SOUND_CHAMPION_3_DAMAGED
+#define k13_soundSPELL 13 // @ C13_SOUND_SPELL
+#define k14_soundATTACK_SCREAMER_OITU 14 // @ C14_SOUND_ATTACK_SCREAMER_OITU
+#define k15_soundATTACK_GIANT_SCORPION_SCORPION 15 // @ C15_SOUND_ATTACK_GIANT_SCORPION_SCORPION
+#define k16_soundCOMBAT_ATTACK_SKELETON_ANIMATED_ARMOUR_DETH_KNIGHT 16 // @ C16_SOUND_COMBAT_ATTACK_SKELETON_ANIMATED_ARMOUR_DETH_KNIGHT
+#define k17_soundBUZZ 17 // @ C17_SOUND_BUZZ
+#define k18_soundPARTY_DAMAGED 18 // @ C18_SOUND_PARTY_DAMAGED
+#define k19_soundATTACK_MAGENTA_WORM_WORM 19 // @ C19_SOUND_ATTACK_MAGENTA_WORM_WORM
+#define k20_soundWEAK_EXPLOSION 20 // @ C20_SOUND_WEAK_EXPLOSION
+#define k21_soundATTACK_GIGGLER 21 // @ C21_SOUND_ATTACK_GIGGLER
+#define k22_soundMOVE_ANIMATED_ARMOUR_DETH_KNIGHT 22 // @ C22_SOUND_MOVE_ANIMATED_ARMOUR_DETH_KNIGHT
+#define k23_soundMOVE_COUATL_GIANT_WASP_MUNCHER 23 // @ C23_SOUND_MOVE_COUATL_GIANT_WASP_MUNCHER
+#define k24_soundMOVE_MUMMY_TROLIN_ANTMAN_STONE_GOLEM_GIGGLER_VEXIRK_DEMON 24 // @ C24_SOUND_MOVE_MUMMY_TROLIN_ANTMAN_STONE_GOLEM_GIGGLER_VEXIRK_DEMON
+#define k25_soundBLOW_HORN 25 // @ C25_SOUND_BLOW_HORN
+#define k26_soundMOVE_SCREAMER_ROCK_ROCKPILE_MAGENTA_WORM_WORM_PAIN_RAT_HELLHOUND_RUSTER_GIANT_SCORPION_SCORPION_OITU 26 // @ C26_SOUND_MOVE_SCREAMER_ROCK_ROCKPILE_MAGENTA_WORM_WORM_PAIN_RAT_HELLHOUND_RUSTER_GIANT_SCORPION_SCORPION_OITU
+#define k27_soundMOVE_SWAMP_SLIME_SLIME_DEVIL_WATER_ELEMENTAL 27 // @ C27_SOUND_MOVE_SWAMP_SLIME_SLIME_DEVIL_WATER_ELEMENTAL
+#define k28_soundWAR_CRY 28 // @ C28_SOUND_WAR_CRY
+#define k29_soundATTACK_ROCK_ROCKPILE 29 // @ C29_SOUND_ATTACK_ROCK_ROCKPILE
+#define k30_soundATTACK_WATER_ELEMENTAL 30 // @ C30_SOUND_ATTACK_WATER_ELEMENTAL
+#define k31_soundATTACK_COUATL 31 // @ C31_SOUND_ATTACK_COUATL
+#define k32_soundMOVE_RED_DRAGON 32 // @ C32_SOUND_MOVE_RED_DRAGON
+#define k33_soundMOVE_SKELETON 33 // @ C33_SOUND_MOVE_SKELETON
+
+
+namespace DM {
+ class Projectile;
+
+ class ProjExpl {
+ DMEngine *_vm;
+public:
+ int16 _g364_creatureDamageOutcome; // @ G0364_i_CreatureDamageOutcome
+ int16 _g363_secondaryDirToOrFromParty; // @ G0363_i_SecondaryDirectionToOrFromParty
+ int32 _g361_lastCreatureAttackTime; // @ G0361_l_LastCreatureAttackTime
+ bool _g365_createLanucherProjectile; // @ G0365_B_CreateLauncherProjectile
+ int16 _g366_projectilePoisonAttack; // @ G0366_i_ProjectilePoisonAttack
+ int16 _g367_projectileAttackType; // @ G0367_i_ProjectileAttackType
+ int32 _g362_lastPartyMovementTime; // @ G0362_l_LastPartyMovementTime
+ ProjExpl(DMEngine *vm);
+ void f212_projectileCreate(Thing thing, int16 mapX, int16 mapY, uint16 cell, direction dir,
+ byte kineticEnergy, byte attack, byte stepEnergy); // @ F0212_PROJECTILE_Create
+ bool f217_projectileHasImpactOccurred(int16 impactType, int16 mapXCombo, int16 mapYCombo,
+ int16 cell, Thing projectileThing); // @ F0217_PROJECTILE_HasImpactOccured
+ uint16 f216_projectileGetImpactAttack(Projectile *projectile, Thing thing); // @ F0216_PROJECTILE_GetImpactAttack
+ void f213_explosionCreate(Thing explThing, uint16 attack, uint16 mapXCombo,
+ uint16 mapYCombo, uint16 cell); // @ F0213_EXPLOSION_Create
+ int16 f218_projectileGetImpactCount(int16 impactType, int16 mapX, int16 mapY, int16 cell); // @ F0218_PROJECTILE_GetImpactCount
+ void f214_projectileDeleteEvent(Thing thing); // @ F0214_PROJECTILE_DeleteEvent
+ void f215_projectileDelete(Thing projectileThing, Thing *groupSlot, int16 mapX, int16 mapY); // @ F0215_PROJECTILE_Delete
+};
+
+}
+
+#endif
diff --git a/engines/dm/text.cpp b/engines/dm/text.cpp
index 5426dd0f61..d9ca4b7fd1 100644
--- a/engines/dm/text.cpp
+++ b/engines/dm/text.cpp
@@ -30,13 +30,19 @@
namespace DM {
-TextMan::TextMan(DMEngine* vm) : _vm(vm) {}
+TextMan::TextMan(DMEngine* vm) : _vm(vm) {
+ _g356_bitmapMessageAreaNewRow = new byte[320 * 7];
+}
+
+TextMan::~TextMan() {
+ delete[] _g356_bitmapMessageAreaNewRow;
+}
#define k5_LetterWidth 5
#define k6_LetterHeight 6
void TextMan::f40_printTextToBitmap(byte* destBitmap, uint16 destByteWidth, uint16 destX, uint16 destY,
- Color textColor, Color bgColor, const char* text, uint16 destHeight) {
+ Color textColor, Color bgColor, const char* text, uint16 destHeight) {
destX -= 1; // fixes missalignment, to be checked
destY -= 4; // fixes missalignment, to be checked
@@ -78,11 +84,81 @@ void TextMan::f52_printToViewport(int16 posX, int16 posY, Color textColor, const
}
void TextMan::f41_printWithTrailingSpaces(byte* destBitmap, int16 destByteWidth, int16 destX, int16 destY, Color textColor,
- Color bgColor, const char* text, int16 requiredTextLength, int16 destHeight) {
+ Color bgColor, const char* text, int16 requiredTextLength, int16 destHeight) {
Common::String str = text;
for (int16 i = str.size(); i < requiredTextLength; ++i)
str += ' ';
f40_printTextToBitmap(destBitmap, destByteWidth, destX, destY, textColor, bgColor, str.c_str(), destHeight);
}
+void TextMan::f51_messageAreaPrintLineFeed() {
+ f47_messageAreaPrintMessage(k0_ColorBlack, "\n");
+}
+
+void TextMan::f47_messageAreaPrintMessage(Color color, const char* string) {
+ uint16 L0031_ui_CharacterIndex;
+ char L0033_ac_String[54];
+
+
+ while (*string) {
+ if (*string == '\n') { /* New line */
+ string++;
+ if ((_g359_messageAreaCursorColumn != 0) || (_g358_messageAreaCursorRow != 0)) {
+ _g359_messageAreaCursorColumn = 0;
+ f45_messageAreaCreateNewRow();
+ }
+ } else {
+ if (*string == ' ') {
+ string++;
+ if (_g359_messageAreaCursorColumn != 53) {
+ f46_messageAreaPrintString(color, " "); // TODO: I'm not sure this is like the original
+ }
+ } else {
+ L0031_ui_CharacterIndex = 0;
+ do {
+ L0033_ac_String[L0031_ui_CharacterIndex++] = *string++;
+ } while (*string && (*string != ' ') && (*string != '\n')); /* End of string, space or New line */
+ L0033_ac_String[L0031_ui_CharacterIndex] = '\0';
+ if (_g359_messageAreaCursorColumn + L0031_ui_CharacterIndex > 53) {
+ _g359_messageAreaCursorColumn = 2;
+ f45_messageAreaCreateNewRow();
+ }
+ f46_messageAreaPrintString(color, L0033_ac_String);
+ }
+ }
+ }
+}
+
+void TextMan::f45_messageAreaCreateNewRow() {
+ uint16 L0029_ui_RowIndex;
+
+ if (_g358_messageAreaCursorRow == 3) {
+ warning("MISSING CODE: F0561_SCROLLER_IsTextScrolling");
+ memset(_g356_bitmapMessageAreaNewRow, k0_ColorBlack, 320 * 7);
+ warning("MISSING CODE: F0560_SCROLLER_SetCommand");
+ for (L0029_ui_RowIndex = 0; L0029_ui_RowIndex < 3; L0029_ui_RowIndex++) {
+ _g360_messageAreaRowExpirationTime[L0029_ui_RowIndex] = _g360_messageAreaRowExpirationTime[L0029_ui_RowIndex + 1];
+ }
+ _g360_messageAreaRowExpirationTime[3] = -1;
+ } else {
+ _g358_messageAreaCursorRow++;
+ }
+}
+
+void TextMan::f46_messageAreaPrintString(Color color, const char* string) {
+ int16 L0030_i_StringLength;
+
+ L0030_i_StringLength = strlen(string);
+ warning("MISSING CODE: F0561_SCROLLER_IsTextScrolling");
+ if (true) { // there is a test here with F0561_SCROLLER_IsTextScrolling
+ _vm->_textMan->f53_printToLogicalScreen(_g359_messageAreaCursorColumn * 6, (_g358_messageAreaCursorRow * 7) + 177, color, k0_ColorBlack, string);
+ } else {
+ f40_printTextToBitmap(_g356_bitmapMessageAreaNewRow, k160_byteWidthScreen, _g359_messageAreaCursorColumn * 6, 5, color, k0_ColorBlack, string, 7);
+ warning("MISSING CODE: F0561_SCROLLER_IsTextScrolling");
+ warning("MISSING CODE: F0560_SCROLLER_SetCommand");
+ }
+ _g359_messageAreaCursorColumn += L0030_i_StringLength;
+ _g360_messageAreaRowExpirationTime[_g358_messageAreaCursorRow] = _vm->_g313_gameTime + 200;
+}
+
}
diff --git a/engines/dm/text.h b/engines/dm/text.h
index 31344e21d9..b0ecb48716 100644
--- a/engines/dm/text.h
+++ b/engines/dm/text.h
@@ -35,14 +35,25 @@ namespace DM {
class TextMan {
DMEngine *_vm;
+ int16 _g359_messageAreaCursorColumn; // @ G0359_i_MessageAreaCursorColumn
+ int16 _g358_messageAreaCursorRow; // @ G0358_i_MessageAreaCursorRow
+ int32 _g360_messageAreaRowExpirationTime[4]; // @ G0360_al_MessageAreaRowExpirationTime
+ byte *_g356_bitmapMessageAreaNewRow; // @ G0356_puc_Bitmap_MessageAreaNewRow
public:
explicit TextMan(DMEngine *vm);
+ ~TextMan();
void f40_printTextToBitmap(byte *destBitmap, uint16 destByteWidth, uint16 destX, uint16 destY,
Color textColor, Color bgColor, const char *text, uint16 destHeight); // @ F0040_TEXT_Print
void f53_printToLogicalScreen(uint16 destX, uint16 destY, Color textColor, Color bgColor, const char *text); // @ F0053_TEXT_PrintToLogicalScreen
void f52_printToViewport(int16 posX, int16 posY, Color textColor, const char *text, Color bgColor = k12_ColorDarkestGray); // @ F0052_TEXT_PrintToViewport
void f41_printWithTrailingSpaces(byte *destBitmap, int16 destByteWidth, int16 destX, int16 destY, Color textColor, Color bgColor,
const char *text, int16 strLenght, int16 destHeight); // @ F0041_TEXT_PrintWithTrailingSpaces
+ void f51_messageAreaPrintLineFeed(); // @ F0051_TEXT_MESSAGEAREA_PrintLineFeed
+ void f47_messageAreaPrintMessage(Color color, const char *string); // @ F0047_TEXT_MESSAGEAREA_PrintMessage
+ void f45_messageAreaCreateNewRow(); // @ F0045_TEXT_MESSAGEAREA_CreateNewRow
+ void f46_messageAreaPrintString(Color color, const char* string);// @ F0046_TEXT_MESSAGEAREA_PrintString
+
+
};
}
diff --git a/engines/dm/timeline.cpp b/engines/dm/timeline.cpp
index 4b5cfd411d..2b58203b35 100644
--- a/engines/dm/timeline.cpp
+++ b/engines/dm/timeline.cpp
@@ -52,4 +52,157 @@ void Timeline::f233_initTimeline() {
}
}
+void Timeline::f237_deleteEvent(uint16 eventIndex) {
+ uint16 L0586_ui_TimelineIndex;
+ uint16 L0587_ui_EventCount;
+
+
+ _vm->_timeline->_g370_events[eventIndex]._type = k0_TMEventTypeNone;
+ if (eventIndex < _vm->_timeline->_g373_firstUnusedEventIndex) {
+ _vm->_timeline->_g373_firstUnusedEventIndex = eventIndex;
+ }
+ _vm->_timeline->_g372_eventCount--;
+ if ((L0587_ui_EventCount = _vm->_timeline->_g372_eventCount) == 0) {
+ return;
+ }
+ L0586_ui_TimelineIndex = f235_getIndex(eventIndex);
+ if (L0586_ui_TimelineIndex == L0587_ui_EventCount) {
+ return;
+ }
+ _vm->_timeline->_g371_timeline[L0586_ui_TimelineIndex] = _vm->_timeline->_g371_timeline[L0587_ui_EventCount];
+ f236_fixChronology(L0586_ui_TimelineIndex);
+}
+
+void Timeline::f236_fixChronology(uint16 timelineIndex) {
+ uint16 L0581_ui_TimelineIndex;
+ uint16 L0582_ui_EventIndex;
+ uint16 L0583_ui_EventCount;
+ TimelineEvent* L0584_ps_Event;
+ bool L0585_B_ChronologyFixed;
+
+
+ if ((L0583_ui_EventCount = _vm->_timeline->_g372_eventCount) == 1) {
+ return;
+ }
+
+ L0584_ps_Event = &_vm->_timeline->_g370_events[L0582_ui_EventIndex = _vm->_timeline->_g371_timeline[timelineIndex]];
+ L0585_B_ChronologyFixed = false;
+ while (timelineIndex > 0) { /* Check if the event should be moved earlier in the timeline */
+ L0581_ui_TimelineIndex = (timelineIndex - 1) >> 1;
+ if (f234_isEventABeforeB(L0584_ps_Event, &_vm->_timeline->_g370_events[_vm->_timeline->_g371_timeline[L0581_ui_TimelineIndex]])) {
+ _vm->_timeline->_g371_timeline[timelineIndex] = _vm->_timeline->_g371_timeline[L0581_ui_TimelineIndex];
+ timelineIndex = L0581_ui_TimelineIndex;
+ L0585_B_ChronologyFixed = true;
+ } else {
+ break;
+ }
+ }
+ if (L0585_B_ChronologyFixed)
+ goto T0236011;
+ L0583_ui_EventCount = ((L0583_ui_EventCount - 1) - 1) >> 1;
+ while (timelineIndex <= L0583_ui_EventCount) { /* Check if the event should be moved later in the timeline */
+ L0581_ui_TimelineIndex = (timelineIndex << 1) + 1;
+ if (((L0581_ui_TimelineIndex + 1) < _vm->_timeline->_g372_eventCount) && (f234_isEventABeforeB(&_vm->_timeline->_g370_events[_vm->_timeline->_g371_timeline[L0581_ui_TimelineIndex + 1]], &_vm->_timeline->_g370_events[_vm->_timeline->_g371_timeline[L0581_ui_TimelineIndex]]))) {
+ L0581_ui_TimelineIndex++;
+ }
+ if (f234_isEventABeforeB(&_vm->_timeline->_g370_events[_vm->_timeline->_g371_timeline[L0581_ui_TimelineIndex]], L0584_ps_Event)) {
+ _vm->_timeline->_g371_timeline[timelineIndex] = _vm->_timeline->_g371_timeline[L0581_ui_TimelineIndex];
+ timelineIndex = L0581_ui_TimelineIndex;
+ } else {
+ break;
+ }
+ }
+T0236011:
+ _vm->_timeline->_g371_timeline[timelineIndex] = L0582_ui_EventIndex;
+}
+
+bool Timeline::f234_isEventABeforeB(TimelineEvent* eventA, TimelineEvent* eventB) {
+ bool L0578_B_Simultaneous;
+
+ return (M30_time(eventA->_mapTime) < M30_time(eventB->_mapTime)) ||
+ ((L0578_B_Simultaneous = (M30_time(eventA->_mapTime) == M30_time(eventB->_mapTime))) && (eventA->getTypePriority() > eventB->getTypePriority())) ||
+ (L0578_B_Simultaneous && (eventA->getTypePriority() == eventB->getTypePriority()) && (eventA <= eventB));
+}
+
+uint16 Timeline::f235_getIndex(uint16 eventIndex) {
+ uint16 L0579_ui_TimelineIndex;
+ uint16* L0580_pui_TimelineEntry;
+
+
+ for (L0579_ui_TimelineIndex = 0, L0580_pui_TimelineEntry = _vm->_timeline->_g371_timeline; L0579_ui_TimelineIndex < _vm->_timeline->_g369_eventMaxCount; L0579_ui_TimelineIndex++) {
+ if (*L0580_pui_TimelineEntry++ == eventIndex)
+ break;
+ }
+ if (L0579_ui_TimelineIndex >= _vm->_timeline->_g369_eventMaxCount) { /* BUG0_00 Useless code. The function is always called with event indices that are in the timeline */
+ L0579_ui_TimelineIndex = 0; /* BUG0_01 Coding error without consequence. Wrong return value. If the specified event index is not found in the timeline the function returns 0 which is the same value that is returned if the event index is found in the first timeline entry. No consequence because this code is never executed */
+ }
+ return L0579_ui_TimelineIndex;
+}
+
+uint16 Timeline::f238_addEventGetEventIndex(TimelineEvent* event) {
+ uint16 L0588_ui_EventIndex;
+ uint16 L0590_ui_NewEventIndex;
+ TimelineEvent* L0591_ps_Event;
+
+
+ if (_vm->_timeline->_g372_eventCount == _vm->_timeline->_g369_eventMaxCount) {
+ _vm->f19_displayErrorAndStop(45);
+ }
+ if ((event->_type >= k5_TMEventTypeCorridor) && (event->_type <= k10_TMEventTypeDoor)) {
+ for (L0588_ui_EventIndex = 0, L0591_ps_Event = _vm->_timeline->_g370_events; L0588_ui_EventIndex < _vm->_timeline->_g369_eventMaxCount; L0588_ui_EventIndex++, L0591_ps_Event++) {
+ if ((L0591_ps_Event->_type >= k5_TMEventTypeCorridor) && (L0591_ps_Event->_type <= k10_TMEventTypeDoor)) {
+ if ((event->_mapTime == L0591_ps_Event->_mapTime) && (event->getMapXY() == L0591_ps_Event->getMapXY()) && ((L0591_ps_Event->_type != k6_TMEventTypeWall) || (L0591_ps_Event->_C.A._cell == event->_C.A._cell))) {
+ L0591_ps_Event->_C.A._effect = event->_C.A._effect;
+ return L0588_ui_EventIndex;
+ }
+ continue;
+ } else {
+ if ((L0591_ps_Event->_type == k1_TMEventTypeDoorAnimation) && (event->_mapTime == L0591_ps_Event->_mapTime) && (event->getMapXY() == L0591_ps_Event->getMapXY())) {
+ if (event->_C.A._effect == k2_SensorEffToggle) {
+ event->_C.A._effect = 1 - L0591_ps_Event->_C.A._effect;
+ }
+ f237_deleteEvent(L0588_ui_EventIndex);
+ break;
+ }
+ }
+ }
+ } else {
+ if (event->_type == k1_TMEventTypeDoorAnimation) {
+ for (L0588_ui_EventIndex = 0, L0591_ps_Event = _vm->_timeline->_g370_events; L0588_ui_EventIndex < _vm->_timeline->_g369_eventMaxCount; L0588_ui_EventIndex++, L0591_ps_Event++) {
+ if ((event->_mapTime == L0591_ps_Event->_mapTime) && (event->getMapXY() == L0591_ps_Event->getMapXY())) {
+ if (L0591_ps_Event->_type == k10_TMEventTypeDoor) {
+ if (L0591_ps_Event->_C.A._effect == k2_SensorEffToggle) {
+ L0591_ps_Event->_C.A._effect = 1 - event->_C.A._effect;
+ }
+ return L0588_ui_EventIndex;
+ }
+ if (L0591_ps_Event->_type == k1_TMEventTypeDoorAnimation) {
+ L0591_ps_Event->_C.A._effect = event->_C.A._effect;
+ return L0588_ui_EventIndex;
+ }
+ }
+ }
+ } else {
+ if (event->_type == k2_TMEventTypeDoorDestruction) {
+ for (L0588_ui_EventIndex = 0, L0591_ps_Event = _vm->_timeline->_g370_events; L0588_ui_EventIndex < _vm->_timeline->_g369_eventMaxCount; L0588_ui_EventIndex++, L0591_ps_Event++) {
+ if ((event->getMapXY() == L0591_ps_Event->getMapXY()) && (M29_map(event->_mapTime) == M29_map(L0591_ps_Event->_mapTime))) {
+ if ((L0591_ps_Event->_type == k1_TMEventTypeDoorAnimation) || (L0591_ps_Event->_type == k10_TMEventTypeDoor)) {
+ f237_deleteEvent(L0588_ui_EventIndex);
+ }
+ }
+ }
+ }
+ }
+ }
+ _vm->_timeline->_g370_events[L0590_ui_NewEventIndex = _vm->_timeline->_g373_firstUnusedEventIndex] = *event; /* Copy the event data (Megamax C can assign structures) */
+ do {
+ if (_vm->_timeline->_g373_firstUnusedEventIndex == _vm->_timeline->_g369_eventMaxCount)
+ break;
+ _vm->_timeline->_g373_firstUnusedEventIndex++;
+ } while ((_vm->_timeline->_g370_events[_vm->_timeline->_g373_firstUnusedEventIndex])._type != k0_TMEventTypeNone);
+ _vm->_timeline->_g371_timeline[_vm->_timeline->_g372_eventCount] = L0590_ui_NewEventIndex;
+ f236_fixChronology(_vm->_timeline->_g372_eventCount++);
+ return L0590_ui_NewEventIndex;
+}
+
}
diff --git a/engines/dm/timeline.h b/engines/dm/timeline.h
index c2e06bcd9c..1c184e3980 100644
--- a/engines/dm/timeline.h
+++ b/engines/dm/timeline.h
@@ -154,6 +154,12 @@ public:
Timeline(DMEngine *vm);
~Timeline();
void f233_initTimeline(); // @ F0233_TIMELINE_Initialize
+ void f237_deleteEvent(uint16 eventIndex);// @ F0237_TIMELINE_DeleteEvent
+ void f236_fixChronology(uint16 timelineIndex); // @ F0236_TIMELINE_FixChronology
+ bool f234_isEventABeforeB(TimelineEvent *eventA, TimelineEvent *eventB); // @ F0234_TIMELINE_IsEventABeforeEventB
+ uint16 f235_getIndex(uint16 eventIndex); // @ F0235_TIMELINE_GetIndex
+ uint16 f238_addEventGetEventIndex(TimelineEvent *event); // @ F0238_TIMELINE_AddEvent_GetEventIndex_CPSE
+
};