From 87289a34c02ec9fa4afa1ccdcdae97409da52b58 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Wed, 10 Aug 2022 20:46:09 +0300 Subject: [PATCH 01/37] Add unarmed hits improvements (#29) --- src/combat.cc | 343 +++++++++++++++++++++++++++++++++++++++++++-- src/combat.h | 14 ++ src/interface.cc | 54 +------ src/inventory.cc | 36 ++++- src/item.cc | 64 ++------- src/sfall_config.h | 1 + src/stat.cc | 6 + 7 files changed, 400 insertions(+), 118 deletions(-) diff --git a/src/combat.cc b/src/combat.cc index f49c5be..9aab99c 100644 --- a/src/combat.cc +++ b/src/combat.cc @@ -54,6 +54,19 @@ typedef struct CombatAiInfo { int lastMove; } CombatAiInfo; +typedef struct UnarmedHitDescription { + int requiredLevel; + int requiredSkill; + int requiredStats[PRIMARY_STAT_COUNT]; + int minDamage; + int maxDamage; + int bonusDamage; + int bonusCriticalChance; + int actionPointCost; + bool isPenetrate; + bool isSecondary; +} UnarmedHitDescription; + static bool _combat_safety_invalidate_weapon_func(Object* critter, Object* weapon, int hitMode, Object* a4, int* a5, Object* a6); static int aiInfoCopy(int srcIndex, int destIndex); static void _combat_begin(Object* a1); @@ -96,6 +109,10 @@ static void criticalsReset(); static void criticalsExit(); static void burstModInit(); static int burstModComputeRounds(int totalRounds, int* centerRoundsPtr, int* leftRoundsPtr, int* rightRoundsPtr); +static void unarmedInit(); +static void unarmedInitVanilla(); +static void unarmedInitCustom(); +static int unarmedGetHitModeInRange(int firstHitMode, int lastHitMode, bool isSecondary); // 0x500B50 static char _a_1[] = "."; @@ -1932,6 +1949,7 @@ static int gBurstModCenterMultiplier = SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_MUL static int gBurstModCenterDivisor = SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR; static int gBurstModTargetMultiplier = SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_MULTIPLIER; static int gBurstModTargetDivisor = SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_DIVISOR; +static UnarmedHitDescription gUnarmedHitDescriptions[HIT_MODE_COUNT]; // combat_init // 0x420CC0 @@ -1974,6 +1992,7 @@ int combatInit() // SFALL criticalsInit(); burstModInit(); + unarmedInit(); return 0; } @@ -3757,13 +3776,12 @@ static int attackCompute(Attack* attack) damageMultiplier = 4; } - if (((attack->hitMode == HIT_MODE_HAMMER_PUNCH || attack->hitMode == HIT_MODE_POWER_KICK) && randomBetween(1, 100) <= 5) - || ((attack->hitMode == HIT_MODE_JAB || attack->hitMode == HIT_MODE_HOOK_KICK) && randomBetween(1, 100) <= 10) - || (attack->hitMode == HIT_MODE_HAYMAKER && randomBetween(1, 100) <= 15) - || (attack->hitMode == HIT_MODE_PALM_STRIKE && randomBetween(1, 100) <= 20) - || (attack->hitMode == HIT_MODE_PIERCING_STRIKE && randomBetween(1, 100) <= 40) - || (attack->hitMode == HIT_MODE_PIERCING_KICK && randomBetween(1, 100) <= 50)) { - roll = ROLL_CRITICAL_SUCCESS; + // SFALL + int bonusCriticalChance = unarmedGetBonusCriticalChance(attack->hitMode); + if (bonusCriticalChance != 0) { + if (randomBetween(1, 100) <= bonusCriticalChance) { + roll = ROLL_CRITICAL_SUCCESS; + } } } } @@ -4203,7 +4221,7 @@ static int attackDetermineToHit(Object* attacker, int tile, Object* defender, in bool isRangedWeapon = false; int accuracy; - if (weapon == NULL || hitMode == HIT_MODE_PUNCH || hitMode == HIT_MODE_KICK || (hitMode >= FIRST_ADVANCED_UNARMED_HIT_MODE && hitMode <= LAST_ADVANCED_UNARMED_HIT_MODE)) { + if (weapon == NULL || isUnarmedHitMode(hitMode)) { accuracy = skillGetValue(attacker, SKILL_UNARMED); } else { accuracy = _item_w_skill_level(attacker, hitMode); @@ -4413,11 +4431,9 @@ static void attackComputeDamage(Attack* attack, int ammoQuantity, int bonusDamag damageThreshold = 20 * damageThreshold / 100; damageResistance = 20 * damageResistance / 100; } else { + // SFALL if (weaponGetPerk(attack->weapon) == PERK_WEAPON_PENETRATE - || attack->hitMode == HIT_MODE_PALM_STRIKE - || attack->hitMode == HIT_MODE_PIERCING_STRIKE - || attack->hitMode == HIT_MODE_HOOK_KICK - || attack->hitMode == HIT_MODE_PIERCING_KICK) { + || unarmedIsPenetrating(attack->hitMode)) { damageThreshold = 20 * damageThreshold / 100; } @@ -6189,3 +6205,306 @@ static int burstModComputeRounds(int totalRounds, int* centerRoundsPtr, int* lef return mainTargetRounds; } + +static void unarmedInit() +{ + unarmedInitVanilla(); + unarmedInitCustom(); +} + +static void unarmedInitVanilla() +{ + UnarmedHitDescription* hitDescription; + + // Punch + hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_PUNCH]); + hitDescription->minDamage = 1; + hitDescription->maxDamage = 2; + hitDescription->actionPointCost = 3; + + // Strong Punch + hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_STRONG_PUNCH]); + hitDescription->requiredSkill = 55; + hitDescription->requiredStats[STAT_AGILITY] = 6; + hitDescription->minDamage = 1; + hitDescription->maxDamage = 2; + hitDescription->bonusDamage = 3; + hitDescription->actionPointCost = 3; + hitDescription->isPenetrate = false; + hitDescription->isSecondary = false; + + // Hammer Punch + hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_HAMMER_PUNCH]); + hitDescription->requiredLevel = 6; + hitDescription->requiredSkill = 75; + hitDescription->requiredStats[STAT_STRENGTH] = 5; + hitDescription->requiredStats[STAT_AGILITY] = 6; + hitDescription->minDamage = 1; + hitDescription->maxDamage = 2; + hitDescription->bonusDamage = 5; + hitDescription->bonusCriticalChance = 5; + hitDescription->actionPointCost = 3; + + // Lightning Punch + hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_HAYMAKER]); + hitDescription->requiredLevel = 9; + hitDescription->requiredSkill = 100; + hitDescription->requiredStats[STAT_STRENGTH] = 5; + hitDescription->requiredStats[STAT_AGILITY] = 7; + hitDescription->minDamage = 1; + hitDescription->maxDamage = 2; + hitDescription->bonusDamage = 7; + hitDescription->bonusCriticalChance = 15; + hitDescription->actionPointCost = 3; + + // Chop Punch + hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_JAB]); + hitDescription->requiredLevel = 5; + hitDescription->requiredSkill = 75; + hitDescription->requiredStats[STAT_STRENGTH] = 5; + hitDescription->requiredStats[STAT_AGILITY] = 7; + hitDescription->minDamage = 1; + hitDescription->maxDamage = 2; + hitDescription->bonusDamage = 3; + hitDescription->bonusCriticalChance = 10; + hitDescription->actionPointCost = 3; + hitDescription->isSecondary = true; + + // Dragon Punch + hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_PALM_STRIKE]); + hitDescription->requiredLevel = 12; + hitDescription->requiredSkill = 115; + hitDescription->requiredStats[STAT_STRENGTH] = 5; + hitDescription->requiredStats[STAT_AGILITY] = 7; + hitDescription->minDamage = 1; + hitDescription->maxDamage = 2; + hitDescription->bonusDamage = 7; + hitDescription->bonusCriticalChance = 20; + hitDescription->actionPointCost = 6; + hitDescription->isPenetrate = true; + hitDescription->isSecondary = true; + + // Force Punch + hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_PIERCING_STRIKE]); + hitDescription->requiredLevel = 16; + hitDescription->requiredSkill = 130; + hitDescription->requiredStats[STAT_STRENGTH] = 5; + hitDescription->requiredStats[STAT_AGILITY] = 7; + hitDescription->minDamage = 1; + hitDescription->maxDamage = 2; + hitDescription->bonusDamage = 10; + hitDescription->bonusCriticalChance = 40; + hitDescription->actionPointCost = 8; + hitDescription->isPenetrate = true; + hitDescription->isSecondary = true; + + // Kick + hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_KICK]); + hitDescription->minDamage = 1; + hitDescription->maxDamage = 2; + hitDescription->actionPointCost = 3; + + // Strong Kick + hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_STRONG_KICK]); + hitDescription->requiredSkill = 40; + hitDescription->requiredStats[STAT_AGILITY] = 6; + hitDescription->minDamage = 1; + hitDescription->maxDamage = 2; + hitDescription->bonusDamage = 5; + hitDescription->actionPointCost = 4; + + // Snap Kick + hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_SNAP_KICK]); + hitDescription->requiredLevel = 6; + hitDescription->requiredSkill = 60; + hitDescription->requiredStats[STAT_AGILITY] = 6; + hitDescription->minDamage = 1; + hitDescription->maxDamage = 2; + hitDescription->bonusDamage = 7; + hitDescription->actionPointCost = 4; + + // Roundhouse Kick + hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_POWER_KICK]); + hitDescription->requiredLevel = 9; + hitDescription->requiredSkill = 80; + hitDescription->requiredStats[STAT_STRENGTH] = 6; + hitDescription->requiredStats[STAT_AGILITY] = 6; + hitDescription->minDamage = 1; + hitDescription->maxDamage = 2; + hitDescription->bonusDamage = 9; + hitDescription->bonusCriticalChance = 5; + hitDescription->actionPointCost = 4; + + // Kip Kick + hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_HIP_KICK]); + hitDescription->requiredLevel = 6; + hitDescription->requiredSkill = 60; + hitDescription->requiredStats[STAT_STRENGTH] = 6; + hitDescription->requiredStats[STAT_AGILITY] = 7; + hitDescription->minDamage = 1; + hitDescription->maxDamage = 2; + hitDescription->bonusDamage = 7; + hitDescription->actionPointCost = 7; + hitDescription->isSecondary = true; + + // Jump Kick + hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_HOOK_KICK]); + hitDescription->requiredLevel = 12; + hitDescription->requiredSkill = 100; + hitDescription->requiredStats[STAT_STRENGTH] = 6; + hitDescription->requiredStats[STAT_AGILITY] = 7; + hitDescription->minDamage = 1; + hitDescription->maxDamage = 2; + hitDescription->bonusDamage = 9; + hitDescription->bonusCriticalChance = 10; + hitDescription->actionPointCost = 7; + hitDescription->isPenetrate = true; + hitDescription->isSecondary = true; + + // Death Blossom Kick + hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_PIERCING_KICK]); + hitDescription->requiredLevel = 15; + hitDescription->requiredSkill = 125; + hitDescription->requiredStats[STAT_STRENGTH] = 6; + hitDescription->requiredStats[STAT_AGILITY] = 8; + hitDescription->minDamage = 1; + hitDescription->maxDamage = 2; + hitDescription->bonusDamage = 12; + hitDescription->bonusCriticalChance = 50; + hitDescription->actionPointCost = 9; + hitDescription->isPenetrate = true; + hitDescription->isSecondary = true; +} + +static void unarmedInitCustom() +{ + char* unarmedFileName = NULL; + configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_UNARMED_FILE_KEY, &unarmedFileName); + if (unarmedFileName != NULL && *unarmedFileName == '\0') { + unarmedFileName = NULL; + } + + if (unarmedFileName == NULL) { + return; + } + + Config unarmedConfig; + if (configInit(&unarmedConfig)) { + if (configRead(&unarmedConfig, unarmedFileName, false)) { + char section[4]; + char statKey[6]; + + for (int hitMode = 0; hitMode < HIT_MODE_COUNT; hitMode++) { + if (!isUnarmedHitMode(hitMode)) { + continue; + } + + UnarmedHitDescription* hitDescription = &(gUnarmedHitDescriptions[hitMode]); + sprintf(section, "%d", hitMode); + + configGetInt(&unarmedConfig, section, "ReqLevel", &(hitDescription->requiredLevel)); + configGetInt(&unarmedConfig, section, "SkillLevel", &(hitDescription->requiredSkill)); + configGetInt(&unarmedConfig, section, "MinDamage", &(hitDescription->minDamage)); + configGetInt(&unarmedConfig, section, "MaxDamage", &(hitDescription->maxDamage)); + configGetInt(&unarmedConfig, section, "BonusDamage", &(hitDescription->bonusDamage)); + configGetInt(&unarmedConfig, section, "BonusCrit", &(hitDescription->bonusCriticalChance)); + configGetInt(&unarmedConfig, section, "APCost", &(hitDescription->actionPointCost)); + configGetBool(&unarmedConfig, section, "BonusDamage", &(hitDescription->isPenetrate)); + configGetBool(&unarmedConfig, section, "Secondary", &(hitDescription->isSecondary)); + + for (int stat = 0; stat < PRIMARY_STAT_COUNT; stat++) { + sprintf(statKey, "Stat%d", stat); + configGetInt(&unarmedConfig, section, statKey, &(hitDescription->requiredStats[stat])); + } + } + } + + configFree(&unarmedConfig); + } +} + +int unarmedGetDamage(int hitMode, int* minDamagePtr, int* maxDamagePtr) +{ + UnarmedHitDescription* hitDescription = &(gUnarmedHitDescriptions[hitMode]); + *minDamagePtr = hitDescription->minDamage; + *maxDamagePtr = hitDescription->maxDamage; + return hitDescription->bonusDamage; +} + +int unarmedGetBonusCriticalChance(int hitMode) +{ + UnarmedHitDescription* hitDescription = &(gUnarmedHitDescriptions[hitMode]); + return hitDescription->bonusCriticalChance; +} + +int unarmedGetActionPointCost(int hitMode) +{ + UnarmedHitDescription* hitDescription = &(gUnarmedHitDescriptions[hitMode]); + return hitDescription->actionPointCost; +} + +bool unarmedIsPenetrating(int hitMode) +{ + UnarmedHitDescription* hitDescription = &(gUnarmedHitDescriptions[hitMode]); + return hitDescription->isPenetrate; +} + +int unarmedGetPunchHitMode(bool isSecondary) +{ + int hitMode = unarmedGetHitModeInRange(FIRST_ADVANCED_PUNCH_HIT_MODE, LAST_ADVANCED_PUNCH_HIT_MODE, isSecondary); + if (hitMode == -1) { + hitMode = HIT_MODE_PUNCH; + } + return hitMode; +} + +int unarmedGetKickHitMode(bool isSecondary) +{ + int hitMode = unarmedGetHitModeInRange(FIRST_ADVANCED_KICK_HIT_MODE, LAST_ADVANCED_KICK_HIT_MODE, isSecondary); + if (hitMode == -1) { + hitMode = HIT_MODE_KICK; + } + return hitMode; +} + +static int unarmedGetHitModeInRange(int firstHitMode, int lastHitMode, bool isSecondary) +{ + int hitMode = -1; + + int unarmed = skillGetValue(gDude, SKILL_UNARMED); + int level = pcGetStat(PC_STAT_LEVEL); + int stats[PRIMARY_STAT_COUNT]; + for (int stat = 0; stat < PRIMARY_STAT_COUNT; stat++) { + stats[stat] = critterGetStat(gDude, stat); + } + + for (int candidateHitMode = firstHitMode; candidateHitMode <= lastHitMode; candidateHitMode++) { + UnarmedHitDescription* hitDescription = &(gUnarmedHitDescriptions[candidateHitMode]); + if (isSecondary != hitDescription->isSecondary) { + continue; + } + + if (unarmed < hitDescription->requiredSkill) { + continue; + } + + if (level < hitDescription->requiredLevel) { + continue; + } + + bool missingStats = false; + for (int stat = 0; stat < PRIMARY_STAT_COUNT; stat++) { + if (stats[stat] < hitDescription->requiredStats[stat]) { + missingStats = true; + break; + } + } + if (missingStats) { + continue; + } + + hitMode = candidateHitMode; + } + + return hitMode; +} diff --git a/src/combat.h b/src/combat.h index 411487d..652c3fb 100644 --- a/src/combat.h +++ b/src/combat.h @@ -59,10 +59,24 @@ void _combatKillCritterOutsideCombat(Object* critter_obj, char* msg); int criticalsGetValue(int killType, int hitLocation, int effect, int dataMember); void criticalsSetValue(int killType, int hitLocation, int effect, int dataMember, int value); void criticalsResetValue(int killType, int hitLocation, int effect, int dataMember); +int unarmedGetDamage(int hitMode, int* minDamagePtr, int* maxDamagePtr); +int unarmedGetBonusCriticalChance(int hitMode); +int unarmedGetActionPointCost(int hitMode); +bool unarmedIsPenetrating(int hitMode); +int unarmedGetPunchHitMode(bool isSecondary); +int unarmedGetKickHitMode(bool isSecondary); +bool unarmedIsPenetrating(int hitMode); static inline bool isInCombat() { return (gCombatState & COMBAT_STATE_0x01) != 0; } +static inline bool isUnarmedHitMode(int hitMode) +{ + return hitMode == HIT_MODE_PUNCH + || hitMode == HIT_MODE_KICK + || (hitMode >= FIRST_ADVANCED_UNARMED_HIT_MODE && hitMode <= LAST_ADVANCED_UNARMED_HIT_MODE); +} + #endif /* COMBAT_H */ diff --git a/src/interface.cc b/src/interface.cc index 954c4da..50e7dc0 100644 --- a/src/interface.cc +++ b/src/interface.cc @@ -1264,30 +1264,9 @@ int interfaceUpdateItems(bool animated, int leftItemAction, int rightItemAction) leftItemState->action = INTERFACE_ITEM_ACTION_PRIMARY; leftItemState->itemFid = -1; - int unarmed = skillGetValue(gDude, SKILL_UNARMED); - int agility = critterGetStat(gDude, STAT_AGILITY); - int strength = critterGetStat(gDude, STAT_STRENGTH); - int level = pcGetStat(PC_STAT_LEVEL); - - if (unarmed > 99 && agility > 6 && strength > 4 && level > 8) { - leftItemState->primaryHitMode = HIT_MODE_HAYMAKER; - } else if (unarmed > 74 && agility > 5 && strength > 4 && level > 5) { - leftItemState->primaryHitMode = HIT_MODE_HAMMER_PUNCH; - } else if (unarmed > 54 && agility > 5) { - leftItemState->primaryHitMode = HIT_MODE_STRONG_PUNCH; - } else { - leftItemState->primaryHitMode = HIT_MODE_PUNCH; - } - - if (unarmed > 129 && agility > 6 && strength > 4 && level > 15) { - leftItemState->secondaryHitMode = HIT_MODE_PIERCING_STRIKE; - } else if (unarmed > 114 && agility > 6 && strength > 4 && level > 11) { - leftItemState->secondaryHitMode = HIT_MODE_PALM_STRIKE; - } else if (unarmed > 74 && agility > 6 && strength > 4 && level > 4) { - leftItemState->secondaryHitMode = HIT_MODE_JAB; - } else { - leftItemState->secondaryHitMode = HIT_MODE_PUNCH; - } + // SFALL + leftItemState->primaryHitMode = unarmedGetPunchHitMode(false); + leftItemState->secondaryHitMode = unarmedGetPunchHitMode(true); } } @@ -1324,30 +1303,9 @@ int interfaceUpdateItems(bool animated, int leftItemAction, int rightItemAction) rightItemState->action = INTERFACE_ITEM_ACTION_PRIMARY; rightItemState->itemFid = -1; - int unarmed = skillGetValue(gDude, SKILL_UNARMED); - int agility = critterGetStat(gDude, STAT_AGILITY); - int strength = critterGetStat(gDude, STAT_STRENGTH); - int level = pcGetStat(PC_STAT_LEVEL); - - if (unarmed > 79 && agility > 5 && strength > 5 && level > 8) { - rightItemState->primaryHitMode = HIT_MODE_POWER_KICK; - } else if (unarmed > 59 && agility > 5 && level > 5) { - rightItemState->primaryHitMode = HIT_MODE_SNAP_KICK; - } else if (unarmed > 39 && agility > 5) { - rightItemState->primaryHitMode = HIT_MODE_STRONG_KICK; - } else { - rightItemState->primaryHitMode = HIT_MODE_KICK; - } - - if (unarmed > 124 && agility > 7 && strength > 5 && level > 14) { - rightItemState->secondaryHitMode = HIT_MODE_PIERCING_KICK; - } else if (unarmed > 99 && agility > 6 && strength > 5 && level > 11) { - rightItemState->secondaryHitMode = HIT_MODE_HOOK_KICK; - } else if (unarmed > 59 && agility > 6 && strength > 5 && level > 5) { - rightItemState->secondaryHitMode = HIT_MODE_HIP_KICK; - } else { - rightItemState->secondaryHitMode = HIT_MODE_KICK; - } + // SFALL + rightItemState->primaryHitMode = unarmedGetKickHitMode(false); + rightItemState->secondaryHitMode = unarmedGetKickHitMode(true); } } diff --git a/src/inventory.cc b/src/inventory.cc index 83bd82e..c962936 100644 --- a/src/inventory.cc +++ b/src/inventory.cc @@ -2433,6 +2433,11 @@ static void inventoryRenderSummary() HIT_MODE_RIGHT_WEAPON_PRIMARY, }; + const int unarmedHitModes[2] = { + HIT_MODE_PUNCH, + HIT_MODE_KICK, + }; + offset += 499 * fontGetLineHeight(); for (int index = 0; index < 2; index += 1) { @@ -2451,10 +2456,33 @@ static void inventoryRenderSummary() // Unarmed dmg: messageListItem.num = 24; if (messageListGetItem(&gInventoryMessageList, &messageListItem)) { - // TODO: Figure out why it uses STAT_MELEE_DAMAGE instead of - // STAT_UNARMED_DAMAGE. - int damage = critterGetStat(_stack[0], STAT_MELEE_DAMAGE) + 2; - sprintf(formattedText, "%s 1-%d", messageListItem.text, damage); + // SFALL: Display the actual damage values of unarmed attacks. + // CE: Implementation is different. + int hitMode = unarmedHitModes[index]; + if (_stack[0] == gDude) { + int actions[2]; + interfaceGetItemActions(&(actions[0]), &(actions[1])); + + bool isSecondary = actions[index] == INTERFACE_ITEM_ACTION_SECONDARY || + actions[index] == INTERFACE_ITEM_ACTION_SECONDARY_AIMING; + + if (index == HAND_LEFT) { + hitMode = unarmedGetPunchHitMode(isSecondary); + } else { + hitMode = unarmedGetKickHitMode(isSecondary); + } + } + + // Formula is the same as in `weaponGetMeleeDamage`. + int minDamage; + int maxDamage; + int bonusDamage = unarmedGetDamage(hitMode, &minDamage, &maxDamage); + int meleeDamage = critterGetStat(_stack[0], STAT_MELEE_DAMAGE); + // TODO: Localize unarmed attack names. + sprintf(formattedText, "%s %d-%d", + messageListItem.text, + bonusDamage + minDamage, + bonusDamage + meleeDamage + maxDamage); } fontDrawText(windowBuffer + offset, formattedText, 120, 499, _colorTable[992]); diff --git a/src/item.cc b/src/item.cc index a25e47e..f4d3489 100644 --- a/src/item.cc +++ b/src/item.cc @@ -1261,7 +1261,7 @@ int weaponGetMeleeDamage(Object* critter, int hitMode) int minDamage = 0; int maxDamage = 0; int meleeDamage = 0; - int unarmedDamage = 0; + int bonusDamage = 0; // NOTE: Uninline. Object* weapon = critterGetWeaponForHitMode(critter, hitMode); @@ -1278,38 +1278,12 @@ int weaponGetMeleeDamage(Object* critter, int hitMode) meleeDamage = critterGetStat(critter, STAT_MELEE_DAMAGE); } } else { - minDamage = 1; - maxDamage = critterGetStat(critter, STAT_MELEE_DAMAGE) + 2; - - switch (hitMode) { - case HIT_MODE_STRONG_PUNCH: - case HIT_MODE_JAB: - unarmedDamage = 3; - break; - case HIT_MODE_HAMMER_PUNCH: - case HIT_MODE_STRONG_KICK: - unarmedDamage = 4; - break; - case HIT_MODE_HAYMAKER: - case HIT_MODE_PALM_STRIKE: - case HIT_MODE_SNAP_KICK: - case HIT_MODE_HIP_KICK: - unarmedDamage = 7; - break; - case HIT_MODE_POWER_KICK: - case HIT_MODE_HOOK_KICK: - unarmedDamage = 9; - break; - case HIT_MODE_PIERCING_STRIKE: - unarmedDamage = 10; - break; - case HIT_MODE_PIERCING_KICK: - unarmedDamage = 12; - break; - } + // SFALL + bonusDamage = unarmedGetDamage(hitMode, &minDamage, &maxDamage); + meleeDamage = critterGetStat(critter, STAT_MELEE_DAMAGE); } - return randomBetween(unarmedDamage + minDamage, unarmedDamage + meleeDamage + maxDamage); + return randomBetween(bonusDamage + minDamage, bonusDamage + meleeDamage + maxDamage); } // 0x478570 @@ -1678,28 +1652,11 @@ int _item_w_mp_cost(Object* critter, int hitMode, bool aiming) return 2; } - switch (hitMode) { - case HIT_MODE_PALM_STRIKE: - actionPoints = 6; - break; - case HIT_MODE_PIERCING_STRIKE: - actionPoints = 8; - break; - case HIT_MODE_STRONG_KICK: - case HIT_MODE_SNAP_KICK: - case HIT_MODE_POWER_KICK: - actionPoints = 4; - break; - case HIT_MODE_HIP_KICK: - case HIT_MODE_HOOK_KICK: - actionPoints = 7; - break; - case HIT_MODE_PIERCING_KICK: - actionPoints = 9; - break; - default: - // TODO: Inverse conditions. - if (weapon != NULL && hitMode != HIT_MODE_PUNCH && hitMode != HIT_MODE_KICK && hitMode != HIT_MODE_STRONG_PUNCH && hitMode != HIT_MODE_HAMMER_PUNCH && hitMode != HIT_MODE_HAYMAKER) { + // CE: The entire function is different in Sfall. + if (isUnarmedHitMode(hitMode)) { + actionPoints = unarmedGetActionPointCost(hitMode); + } else { + if (weapon != NULL) { if (hitMode == HIT_MODE_LEFT_WEAPON_PRIMARY || hitMode == HIT_MODE_RIGHT_WEAPON_PRIMARY) { // NOTE: Uninline. actionPoints = weaponGetActionPointCost1(weapon); @@ -1718,7 +1675,6 @@ int _item_w_mp_cost(Object* critter, int hitMode, bool aiming) } else { actionPoints = 3; } - break; } if (critter == gDude) { diff --git a/src/sfall_config.h b/src/sfall_config.h index 05bb1aa..569cf09 100644 --- a/src/sfall_config.h +++ b/src/sfall_config.h @@ -41,6 +41,7 @@ #define SFALL_CONFIG_PLASTIC_EXPLOSIVE_MAX_DAMAGE_KEY "PlasticExplosive_DmgMax" #define SFALL_CONFIG_EXPLOSION_EMITS_LIGHT_KEY "ExplosionsEmitLight" #define SFALL_CONFIG_CITY_REPUTATION_LIST_KEY "CityRepsList" +#define SFALL_CONFIG_UNARMED_FILE_KEY "UnarmedFile" #define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_MULTIPLIER 1 #define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR 3 diff --git a/src/stat.cc b/src/stat.cc index c756bf2..2be253a 100644 --- a/src/stat.cc +++ b/src/stat.cc @@ -772,6 +772,12 @@ int pcAddExperienceWithOptions(int xp, bool a2) interfaceRenderHitPoints(false); + // SFALL: Update unarmed attack after leveling up. + int leftItemAction; + int rightItemAction; + interfaceGetItemActions(&leftItemAction, &rightItemAction); + interfaceUpdateItems(false, leftItemAction, rightItemAction); + if (a2) { _partyMemberIncLevels(); } From 4ece7d1188f2d0718231997ff36690727cc027a5 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 11 Aug 2022 13:25:43 +0300 Subject: [PATCH 02/37] Workaround for wrong animate_rotation usage Closes #128 --- src/interpreter_extra.cc | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/interpreter_extra.cc b/src/interpreter_extra.cc index 958a9fd..d8997b9 100644 --- a/src/interpreter_extra.cc +++ b/src/interpreter_extra.cc @@ -3340,10 +3340,26 @@ static void opMetarule(Program* program) // 0x4598BC static void opAnim(Program* program) { - int frame = programStackPopInteger(program); + ProgramValue frameValue = programStackPopValue(program); int anim = programStackPopInteger(program); Object* obj = static_cast(programStackPopPointer(program)); + // CE: There is a bug in the `animate_rotation` macro in the user-space + // sсripts - instead of passing direction, it passes object. The direction + // argument is thrown away by preprocessor. Original code ignores this bug + // since there is no distiction between integers and pointers. In addition + // there is a guard in the code path below which simply ignores any value + // greater than 6 (so rotation does not change when pointer is passed). + int frame; + if (frameValue.opcode == VALUE_TYPE_INT) { + frame = frameValue.integerValue; + } else if (anim == 1000 && frameValue.opcode == VALUE_TYPE_PTR) { + // Force code path below to skip setting rotation. + frame = ROTATION_COUNT; + } else { + programFatalError("script error: %s: invalid arg 2 to anim", program->name); + } + if (obj == NULL) { scriptPredefinedError(program, "anim", SCRIPT_ERROR_OBJECT_IS_NULL); return; From dcb53393c12be6e1f8cd9ae91d01ccd0c115817a Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 11 Aug 2022 13:34:37 +0300 Subject: [PATCH 03/37] FIx opFloatMessage string arg handling Closes #129 --- src/interpreter_extra.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/interpreter_extra.cc b/src/interpreter_extra.cc index d8997b9..f86624b 100644 --- a/src/interpreter_extra.cc +++ b/src/interpreter_extra.cc @@ -3103,7 +3103,11 @@ static void _op_inven_cmds(Program* program) static void opFloatMessage(Program* program) { int floatingMessageType = programStackPopInteger(program); - char* string = programStackPopString(program); + ProgramValue stringValue = programStackPopValue(program); + char* string = NULL; + if ((stringValue.opcode & VALUE_TYPE_MASK) == VALUE_TYPE_STRING) { + string = programGetString(program, stringValue.opcode, stringValue.integerValue); + } Object* obj = static_cast(programStackPopPointer(program)); int color = _colorTable[32747]; From ca78b94a7d6b2dc276af1fce9b725a5a1d3c8c5d Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 11 Aug 2022 13:41:06 +0300 Subject: [PATCH 04/37] Fix sprintf warning --- src/proto_instance.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/proto_instance.cc b/src/proto_instance.cc index e3f782d..3bae855 100644 --- a/src/proto_instance.cc +++ b/src/proto_instance.cc @@ -1208,7 +1208,7 @@ static int _protinst_default_use_item(Object* a1, Object* a2, Object* item) messageListItem.num = 582; if (messageListGetItem(&gProtoMessageList, &messageListItem)) { - sprintf(formattedText, messageListItem.text); + sprintf(formattedText, "%s", messageListItem.text); displayMonitorAddMessage(formattedText); } return -1; From 1ca08cb97a71a91ccd23e890db24237855bdc946 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 11 Aug 2022 14:12:29 +0300 Subject: [PATCH 05/37] Fix unarmed attack mode reset when using inventory --- src/interface.cc | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/interface.cc b/src/interface.cc index 50e7dc0..9f4d0d3 100644 --- a/src/interface.cc +++ b/src/interface.cc @@ -1240,7 +1240,11 @@ int interfaceUpdateItems(bool animated, int leftItemAction, int rightItemAction) leftItemState->itemFid = itemGetInventoryFid(item1); } } else { + Object* oldItem = leftItemState->item; + int oldAction = leftItemState->action; + leftItemState->item = item1; + if (item1 != NULL) { leftItemState->isDisabled = _can_use_weapon(item1); leftItemState->primaryHitMode = HIT_MODE_LEFT_WEAPON_PRIMARY; @@ -1267,6 +1271,12 @@ int interfaceUpdateItems(bool animated, int leftItemAction, int rightItemAction) // SFALL leftItemState->primaryHitMode = unarmedGetPunchHitMode(false); leftItemState->secondaryHitMode = unarmedGetPunchHitMode(true); + + // SFALL: Keep selected attack mode. + // CE: Implementation is different. + if (oldItem == NULL) { + leftItemState->action = oldAction; + } } } @@ -1279,6 +1289,9 @@ int interfaceUpdateItems(bool animated, int leftItemAction, int rightItemAction) rightItemState->itemFid = itemGetInventoryFid(rightItemState->item); } } else { + Object* oldItem = rightItemState->item; + int oldAction = rightItemState->action; + rightItemState->item = item2; if (item2 != NULL) { @@ -1306,6 +1319,12 @@ int interfaceUpdateItems(bool animated, int leftItemAction, int rightItemAction) // SFALL rightItemState->primaryHitMode = unarmedGetKickHitMode(false); rightItemState->secondaryHitMode = unarmedGetKickHitMode(true); + + // SFALL: Keep selected attack mode. + // CE: Implementation is different. + if (oldItem == NULL) { + rightItemState->action = oldAction; + } } } From 5b2a1d13a1b7a1f74a398930cecdbb42e85d8020 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Thu, 11 Aug 2022 14:30:06 +0300 Subject: [PATCH 06/37] Fix displaying secondary weapon mode stats --- src/inventory.cc | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/inventory.cc b/src/inventory.cc index c962936..9beaaa5 100644 --- a/src/inventory.cc +++ b/src/inventory.cc @@ -2433,6 +2433,11 @@ static void inventoryRenderSummary() HIT_MODE_RIGHT_WEAPON_PRIMARY, }; + const int secondaryHitModes[2] = { + HIT_MODE_LEFT_WEAPON_SECONDARY, + HIT_MODE_RIGHT_WEAPON_SECONDARY, + }; + const int unarmedHitModes[2] = { HIT_MODE_PUNCH, HIT_MODE_KICK, @@ -2510,13 +2515,29 @@ static void inventoryRenderSummary() continue; } - int range = _item_w_range(_stack[0], hitModes[index]); + // SFALL: Fix displaying secondary mode weapon range. + int hitMode = hitModes[index]; + if (_stack[0] == gDude) { + int actions[2]; + interfaceGetItemActions(&(actions[0]), &(actions[1])); + + bool isSecondary = actions[index] == INTERFACE_ITEM_ACTION_SECONDARY || + actions[index] == INTERFACE_ITEM_ACTION_SECONDARY_AIMING; + + if (isSecondary) { + hitMode = secondaryHitModes[index]; + } + } + + int range = _item_w_range(_stack[0], hitMode); int damageMin; int damageMax; weaponGetDamageMinMax(item, &damageMin, &damageMax); - int attackType = weaponGetAttackTypeForHitMode(item, hitModes[index]); + // CE: Fix displaying secondary mode weapon damage (affects throwable + // melee weapons - knifes, spears, etc.). + int attackType = weaponGetAttackTypeForHitMode(item, hitMode); formattedText[0] = '\0'; From d86a887cf9da043bab09d1c3f836c23451f01411 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Fri, 12 Aug 2022 13:54:18 +0300 Subject: [PATCH 07/37] Add damage mod (#29) --- src/character_editor.cc | 17 ++- src/combat.cc | 275 ++++++++++++++++++++++++++++++++++++---- src/combat.h | 2 + src/inventory.cc | 27 ++++ src/item.cc | 25 +++- src/sfall_config.h | 3 + 6 files changed, 314 insertions(+), 35 deletions(-) diff --git a/src/character_editor.cc b/src/character_editor.cc index f01bc91..fc14d74 100644 --- a/src/character_editor.cc +++ b/src/character_editor.cc @@ -2,6 +2,7 @@ #include "art.h" #include "color.h" +#include "combat.h" #include "core.h" #include "critter.h" #include "cycle.h" @@ -2801,7 +2802,13 @@ static void characterEditorDrawDerivedStats() sprintf(t, "%s", messageListItemText); fontDrawText(gCharacterEditorWindowBuffer + 640 * y + 194, t, 640, 640, color); - compat_itoa(critterGetStat(gDude, STAT_MELEE_DAMAGE), t, 10); + // SFALL: Display melee damage without "Bonus HtH Damage" bonus. + int meleeDamage = critterGetStat(gDude, STAT_MELEE_DAMAGE); + if (!damageModGetDisplayBonusDamage()) { + meleeDamage -= 2 * perkGetRank(gDude, PERK_BONUS_HTH_DAMAGE); + } + + compat_itoa(meleeDamage, t, 10); fontDrawText(gCharacterEditorWindowBuffer + 640 * y + 288, t, 640, 640, color); // Damage Resistance @@ -4404,13 +4411,19 @@ static int characterPrintToFile(const char* fileName) fileWriteString(title1, stream); fileWriteString("\n", stream); + // SFALL: Display melee damage without "Bonus HtH Damage" bonus. + int meleeDamage = critterGetStat(gDude, STAT_MELEE_DAMAGE); + if (!damageModGetDisplayBonusDamage()) { + meleeDamage -= 2 * perkGetRank(gDude, PERK_BONUS_HTH_DAMAGE); + } + // Charisma / Melee Damage / Carry Weight sprintf(title1, "%s %.2d %s %.2d %s %.3d lbs.", getmsg(&gCharacterEditorMessageList, &gCharacterEditorMessageListItem, 633), critterGetStat(gDude, STAT_CHARISMA), getmsg(&gCharacterEditorMessageList, &gCharacterEditorMessageListItem, 634), - critterGetStat(gDude, STAT_MELEE_DAMAGE), + meleeDamage, getmsg(&gCharacterEditorMessageList, &gCharacterEditorMessageListItem, 635), critterGetStat(gDude, STAT_CARRY_WEIGHT)); fileWriteString(title1, stream); diff --git a/src/combat.cc b/src/combat.cc index 9aab99c..cc72f77 100644 --- a/src/combat.cc +++ b/src/combat.cc @@ -47,6 +47,13 @@ #define CALLED_SHOT_WINDOW_WIDTH (504) #define CALLED_SHOT_WINDOW_HEIGHT (309) +typedef enum DamageCalculationType { + DAMAGE_CALCULATION_TYPE_VANILLA = 0, + DAMAGE_CALCULATION_TYPE_GLOVZ = 1, + DAMAGE_CALCULATION_TYPE_GLOVZ_WITH_DAMAGE_MULTIPLIER_TWEAK = 2, + DAMAGE_CALCULATION_TYPE_YAAM = 5, +} DamageCalculationType; + typedef struct CombatAiInfo { Object* friendlyDead; Object* lastTarget; @@ -67,6 +74,17 @@ typedef struct UnarmedHitDescription { bool isSecondary; } UnarmedHitDescription; +typedef struct DamageCalculationContext { + Attack* attack; + int* damagePtr; + int ammoQuantity; + int damageResistance; + int damageThreshold; + int damageBonus; + int bonusDamageMultiplier; + int combatDifficultyDamageModifier; +} DamageCalculationContext; + static bool _combat_safety_invalidate_weapon_func(Object* critter, Object* weapon, int hitMode, Object* a4, int* a5, Object* a6); static int aiInfoCopy(int srcIndex, int destIndex); static void _combat_begin(Object* a1); @@ -113,6 +131,10 @@ static void unarmedInit(); static void unarmedInitVanilla(); static void unarmedInitCustom(); static int unarmedGetHitModeInRange(int firstHitMode, int lastHitMode, bool isSecondary); +static void damageModInit(); +static void damageModCalculateGlovz(DamageCalculationContext* context); +static int damageModGlovzDivRound(int dividend, int divisor); +static void damageModCalculateYaam(DamageCalculationContext* context); // 0x500B50 static char _a_1[] = "."; @@ -1950,6 +1972,9 @@ static int gBurstModCenterDivisor = SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISO static int gBurstModTargetMultiplier = SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_MULTIPLIER; static int gBurstModTargetDivisor = SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_DIVISOR; static UnarmedHitDescription gUnarmedHitDescriptions[HIT_MODE_COUNT]; +static int gDamageCalculationType; +static bool gBonusHthDamageFix; +static bool gDisplayBonusDamage; // combat_init // 0x420CC0 @@ -1993,6 +2018,7 @@ int combatInit() criticalsInit(); burstModInit(); unarmedInit(); + damageModInit(); return 0; } @@ -4464,41 +4490,57 @@ static void attackComputeDamage(Attack* attack, int ammoQuantity, int bonusDamag } } - damageResistance += weaponGetAmmoDamageResistanceModifier(attack->weapon); - if (damageResistance > 100) { - damageResistance = 100; - } else if (damageResistance < 0) { - damageResistance = 0; - } + // SFALL: Damage mod. + DamageCalculationContext context; + context.attack = attack; + context.damagePtr = damagePtr; + context.damageResistance = damageResistance; + context.damageThreshold = damageThreshold; + context.damageBonus = damageBonus; + context.bonusDamageMultiplier = bonusDamageMultiplier; + context.combatDifficultyDamageModifier = combatDifficultyDamageModifier; - int damageMultiplier = bonusDamageMultiplier * weaponGetAmmoDamageMultiplier(attack->weapon); - int damageDivisor = weaponGetAmmoDamageDivisor(attack->weapon); - - for (int index = 0; index < ammoQuantity; index++) { - int damage = weaponGetMeleeDamage(attack->attacker, attack->hitMode); - - damage += damageBonus; - - damage *= damageMultiplier; - - if (damageDivisor != 0) { - damage /= damageDivisor; + if (gDamageCalculationType == DAMAGE_CALCULATION_TYPE_GLOVZ || gDamageCalculationType == DAMAGE_CALCULATION_TYPE_GLOVZ_WITH_DAMAGE_MULTIPLIER_TWEAK) { + damageModCalculateGlovz(&context); + } else if (gDamageCalculationType == DAMAGE_CALCULATION_TYPE_YAAM) { + damageModCalculateYaam(&context); + } else { + damageResistance += weaponGetAmmoDamageResistanceModifier(attack->weapon); + if (damageResistance > 100) { + damageResistance = 100; + } else if (damageResistance < 0) { + damageResistance = 0; } - // TODO: Why we're halving it? - damage /= 2; + int damageMultiplier = bonusDamageMultiplier * weaponGetAmmoDamageMultiplier(attack->weapon); + int damageDivisor = weaponGetAmmoDamageDivisor(attack->weapon); - damage *= combatDifficultyDamageModifier; - damage /= 100; + for (int index = 0; index < ammoQuantity; index++) { + int damage = weaponGetMeleeDamage(attack->attacker, attack->hitMode); - damage -= damageThreshold; + damage += damageBonus; - if (damage > 0) { - damage -= damage * damageResistance / 100; - } + damage *= damageMultiplier; - if (damage > 0) { - *damagePtr += damage; + if (damageDivisor != 0) { + damage /= damageDivisor; + } + + // TODO: Why we're halving it? + damage /= 2; + + damage *= combatDifficultyDamageModifier; + damage /= 100; + + damage -= damageThreshold; + + if (damage > 0) { + damage -= damage * damageResistance / 100; + } + + if (damage > 0) { + *damagePtr += damage; + } } } @@ -6508,3 +6550,180 @@ static int unarmedGetHitModeInRange(int firstHitMode, int lastHitMode, bool isSe return hitMode; } + +static void damageModInit() +{ + gDamageCalculationType = DAMAGE_CALCULATION_TYPE_VANILLA; + configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DAMAGE_MOD_FORMULA_KEY, &gDamageCalculationType); + + gBonusHthDamageFix = true; + configGetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_BONUS_HTH_DAMAGE_FIX_KEY, &gBonusHthDamageFix); + + gDisplayBonusDamage = false; + configGetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DISPLAY_BONUS_DAMAGE_KEY, &gDisplayBonusDamage); +} + +bool damageModGetBonusHthDamageFix() +{ + return gBonusHthDamageFix; +} + +bool damageModGetDisplayBonusDamage() +{ + return gDisplayBonusDamage; +} + +static void damageModCalculateGlovz(DamageCalculationContext* context) +{ + int ammoX = weaponGetAmmoDamageMultiplier(context->attack->weapon); + if (ammoX <= 0) { + ammoX = 1; + } + + int ammoY = weaponGetAmmoDamageDivisor(context->attack->weapon); + if (ammoY <= 0) { + ammoY = 1; + } + + int ammoDamageResistance = weaponGetAmmoDamageResistanceModifier(context->attack->weapon); + if (ammoDamageResistance > 0) { + ammoDamageResistance = -ammoDamageResistance; + } + + int calculatedDamageThreshold = context->damageThreshold; + if (calculatedDamageThreshold > 0) { + calculatedDamageThreshold = damageModGlovzDivRound(calculatedDamageThreshold, ammoY); + } + + int calculatedDamageResistance = context->damageResistance; + if (calculatedDamageResistance > 0) { + if (context->combatDifficultyDamageModifier > 100) { + calculatedDamageResistance -= 20; + } else if (context->combatDifficultyDamageModifier < 100) { + calculatedDamageResistance += 20; + } + + calculatedDamageResistance += ammoDamageResistance; + + calculatedDamageResistance = damageModGlovzDivRound(calculatedDamageResistance, ammoX); + + if (calculatedDamageResistance >= 100) { + return; + } + } + + for (int index = 0; index < context->ammoQuantity; index++) { + int damage = weaponGetMeleeDamage(context->attack->attacker, context->attack->hitMode); + + damage += context->damageBonus; + if (damage <= 0) { + continue; + } + + if (context->damageThreshold > 0) { + damage -= calculatedDamageThreshold; + if (damage <= 0) { + continue; + } + } + + if (context->damageResistance > 0) { + damage -= damageModGlovzDivRound(damage * calculatedDamageResistance, 100); + if (damage <= 0) { + continue; + } + } + + if (context->damageThreshold <= 0 && context->damageResistance <= 0) { + if (ammoX > 1 && ammoY > 1) { + damage += damageModGlovzDivRound(damage * 15, 100); + } else if (ammoX > 1) { + damage += damageModGlovzDivRound(damage * 20, 100); + } else if (ammoY > 1) { + damage += damageModGlovzDivRound(damage * 10, 100); + } + } + + if (gDamageCalculationType == DAMAGE_CALCULATION_TYPE_GLOVZ_WITH_DAMAGE_MULTIPLIER_TWEAK) { + damage += damageModGlovzDivRound(damage * context->bonusDamageMultiplier * 25, 100); + } else { + damage += damage * context->bonusDamageMultiplier / 2; + } + + if (damage > 0) { + *context->damagePtr += damage; + } + } +} + +static int damageModGlovzDivRound(int dividend, int divisor) +{ + if (dividend < divisor) { + return dividend != divisor && dividend * 2 <= divisor ? 0 : 1; + } + + int quotient = dividend / divisor; + dividend %= divisor; + + if (dividend == 0) { + return quotient; + } + + dividend *= 2; + + if (dividend > divisor || (dividend == divisor && (quotient & 1) != 0)) { + quotient += 1; + } + + return quotient; +} + +static void damageModCalculateYaam(DamageCalculationContext* context) +{ + int damageMultiplier = context->bonusDamageMultiplier * weaponGetAmmoDamageMultiplier(context->attack->weapon); + int damageDivisor = weaponGetAmmoDamageDivisor(context->attack->weapon); + + int ammoDamageResistance = weaponGetAmmoDamageResistanceModifier(context->attack->weapon); + + int calculatedDamageThreshold = context->damageThreshold - ammoDamageResistance; + int damageResistance = calculatedDamageThreshold; + + if (calculatedDamageThreshold >= 0) { + damageResistance = 0; + } else { + calculatedDamageThreshold = 0; + damageResistance *= 10; + } + + int calculatedDamageResistance = context->damageResistance + damageResistance; + if (calculatedDamageResistance < 0) { + calculatedDamageResistance = 0; + } else if (calculatedDamageResistance >= 100) { + return; + } + + for (int index = 0; index < context->ammoQuantity; index++) { + int damage = weaponGetMeleeDamage(context->attack->weapon, context->attack->hitMode); + damage += context->damageBonus; + + damage -= calculatedDamageThreshold; + if (damage <= 0) { + continue; + } + + damage *= damageMultiplier; + if (damageDivisor != 0) { + damage /= damageDivisor; + } + + damage /= 2; + damage *= context->combatDifficultyDamageModifier; + damage /= 100; + + damage -= damage * damageResistance / 100; + + if (damage > 0) { + context->damagePtr += damage; + } + } +} diff --git a/src/combat.h b/src/combat.h index 652c3fb..158a76e 100644 --- a/src/combat.h +++ b/src/combat.h @@ -66,6 +66,8 @@ bool unarmedIsPenetrating(int hitMode); int unarmedGetPunchHitMode(bool isSecondary); int unarmedGetKickHitMode(bool isSecondary); bool unarmedIsPenetrating(int hitMode); +bool damageModGetBonusHthDamageFix(); +bool damageModGetDisplayBonusDamage(); static inline bool isInCombat() { diff --git a/src/inventory.cc b/src/inventory.cc index 9beaaa5..b9ffe05 100644 --- a/src/inventory.cc +++ b/src/inventory.cc @@ -2544,6 +2544,11 @@ static void inventoryRenderSummary() int meleeDamage; if (attackType == ATTACK_TYPE_MELEE || attackType == ATTACK_TYPE_UNARMED) { meleeDamage = critterGetStat(_stack[0], STAT_MELEE_DAMAGE); + + // SFALL: Display melee damage without "Bonus HtH Damage" bonus. + if (damageModGetBonusHthDamageFix() && !damageModGetDisplayBonusDamage()) { + meleeDamage -= 2 * perkGetRank(gDude, PERK_BONUS_HTH_DAMAGE); + } } else { meleeDamage = 0; } @@ -2551,11 +2556,33 @@ static void inventoryRenderSummary() messageListItem.num = 15; // Dmg: if (messageListGetItem(&gInventoryMessageList, &messageListItem)) { if (attackType != 4 && range <= 1) { + // SFALL: Display bonus damage. + if (damageModGetBonusHthDamageFix() && damageModGetDisplayBonusDamage()) { + // CE: Just in case check for attack type, however it looks + // like we cannot be here with anything besides melee or + // unarmed. + if (_stack[0] == gDude && (attackType == ATTACK_TYPE_MELEE || attackType == ATTACK_TYPE_UNARMED)) { + // See explanation in `weaponGetMeleeDamage`. + damageMin += 2 * perkGetRank(gDude, PERK_BONUS_HTH_DAMAGE); + } + } sprintf(formattedText, "%s %d-%d", messageListItem.text, damageMin, damageMax + meleeDamage); } else { MessageListItem rangeMessageListItem; rangeMessageListItem.num = 16; // Rng: if (messageListGetItem(&gInventoryMessageList, &rangeMessageListItem)) { + // SFALL: Display bonus damage. + if (damageModGetDisplayBonusDamage()) { + // CE: There is a bug in Sfall diplaying wrong damage + // bonus for melee weapons with range > 1 (spears, + // sledgehammers) and throwables (secondary mode). + if (_stack[0] == gDude && attackType == ATTACK_TYPE_RANGED) { + int damageBonus = 2 * perkGetRank(gDude, PERK_BONUS_RANGED_DAMAGE); + damageMin += damageBonus; + damageMax += damageBonus; + } + } + sprintf(formattedText, "%s %d-%d %s %d", messageListItem.text, damageMin, damageMax + meleeDamage, rangeMessageListItem.text, range); } } diff --git a/src/item.cc b/src/item.cc index f4d3489..c688f2e 100644 --- a/src/item.cc +++ b/src/item.cc @@ -1267,20 +1267,35 @@ int weaponGetMeleeDamage(Object* critter, int hitMode) Object* weapon = critterGetWeaponForHitMode(critter, hitMode); if (weapon != NULL) { - Proto* proto; - protoGetProto(weapon->pid, &proto); - - minDamage = proto->item.data.weapon.minDamage; - maxDamage = proto->item.data.weapon.maxDamage; + // NOTE: Uninline. + weaponGetDamageMinMax(weapon, &minDamage, &maxDamage); int attackType = weaponGetAttackTypeForHitMode(weapon, hitMode); if (attackType == ATTACK_TYPE_MELEE || attackType == ATTACK_TYPE_UNARMED) { meleeDamage = critterGetStat(critter, STAT_MELEE_DAMAGE); + + // SFALL: Bonus HtH Damage fix. + if (damageModGetBonusHthDamageFix()) { + if (critter == gDude) { + // See explanation below. + minDamage += 2 * perkGetRank(gDude, PERK_BONUS_HTH_DAMAGE); + } + } } } else { // SFALL bonusDamage = unarmedGetDamage(hitMode, &minDamage, &maxDamage); meleeDamage = critterGetStat(critter, STAT_MELEE_DAMAGE); + + // SFALL: Bonus HtH Damage fix. + if (damageModGetBonusHthDamageFix()) { + if (critter == gDude) { + // Increase only min damage. Max damage should not be changed. + // It is calculated later by adding `meleeDamage` which already + // includes damage bonus (via `perkAddEffect`). + minDamage += 2 * perkGetRank(gDude, PERK_BONUS_HTH_DAMAGE); + } + } } return randomBetween(bonusDamage + minDamage, bonusDamage + meleeDamage + maxDamage); diff --git a/src/sfall_config.h b/src/sfall_config.h index 569cf09..865a5ad 100644 --- a/src/sfall_config.h +++ b/src/sfall_config.h @@ -42,6 +42,9 @@ #define SFALL_CONFIG_EXPLOSION_EMITS_LIGHT_KEY "ExplosionsEmitLight" #define SFALL_CONFIG_CITY_REPUTATION_LIST_KEY "CityRepsList" #define SFALL_CONFIG_UNARMED_FILE_KEY "UnarmedFile" +#define SFALL_CONFIG_DAMAGE_MOD_FORMULA_KEY "DamageFormula" +#define SFALL_CONFIG_BONUS_HTH_DAMAGE_FIX_KEY "BonusHtHDamageFix" +#define SFALL_CONFIG_DISPLAY_BONUS_DAMAGE_KEY "DisplayBonusDamage" #define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_MULTIPLIER 1 #define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR 3 From a55feb9301a76e9a1d5d73f5b6e14684b85b48fc Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Fri, 12 Aug 2022 14:19:16 +0300 Subject: [PATCH 08/37] Add custom mouse mode frms --- src/game_mouse.cc | 17 +++++++++++++++++ src/sfall_config.h | 7 +++++++ 2 files changed, 24 insertions(+) diff --git a/src/game_mouse.cc b/src/game_mouse.cc index caecddd..aa2ae58 100644 --- a/src/game_mouse.cc +++ b/src/game_mouse.cc @@ -16,6 +16,7 @@ #include "object.h" #include "proto.h" #include "proto_instance.h" +#include "sfall_config.h" #include "skill.h" #include "skilldex.h" #include "text_font.h" @@ -314,6 +315,8 @@ static int gameMouseHandleScrolling(int x, int y, int cursor); static int objectIsDoor(Object* object); static bool gameMouseClickOnInterfaceBar(); +static void customMouseModeFrmsInit(); + // 0x44B2B0 int gameMouseInit() { @@ -330,6 +333,9 @@ int gameMouseInit() gameMouseSetCursor(MOUSE_CURSOR_ARROW); + // SFALL + customMouseModeFrmsInit(); + return 0; } @@ -2409,3 +2415,14 @@ int objectIsDoor(Object* object) return proto->scenery.type == SCENERY_TYPE_DOOR; } + +static void customMouseModeFrmsInit() +{ + configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_USE_FIRST_AID_FRM_KEY, &(gGameMouseModeFrmIds[GAME_MOUSE_MODE_USE_FIRST_AID])); + configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_USE_DOCTOR_FRM_KEY, &(gGameMouseModeFrmIds[GAME_MOUSE_MODE_USE_DOCTOR])); + configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_USE_LOCKPICK_FRM_KEY, &(gGameMouseModeFrmIds[GAME_MOUSE_MODE_USE_LOCKPICK])); + configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_USE_STEAL_FRM_KEY, &(gGameMouseModeFrmIds[GAME_MOUSE_MODE_USE_STEAL])); + configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_USE_TRAPS_FRM_KEY, &(gGameMouseModeFrmIds[GAME_MOUSE_MODE_USE_TRAPS])); + configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_USE_SCIENCE_FRM_KEY, &(gGameMouseModeFrmIds[GAME_MOUSE_MODE_USE_SCIENCE])); + configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_USE_REPAIR_FRM_KEY, &(gGameMouseModeFrmIds[GAME_MOUSE_MODE_USE_REPAIR])); +} diff --git a/src/sfall_config.h b/src/sfall_config.h index 865a5ad..fb974f6 100644 --- a/src/sfall_config.h +++ b/src/sfall_config.h @@ -45,6 +45,13 @@ #define SFALL_CONFIG_DAMAGE_MOD_FORMULA_KEY "DamageFormula" #define SFALL_CONFIG_BONUS_HTH_DAMAGE_FIX_KEY "BonusHtHDamageFix" #define SFALL_CONFIG_DISPLAY_BONUS_DAMAGE_KEY "DisplayBonusDamage" +#define SFALL_CONFIG_USE_LOCKPICK_FRM_KEY "Lockpick" +#define SFALL_CONFIG_USE_STEAL_FRM_KEY "Steal" +#define SFALL_CONFIG_USE_TRAPS_FRM_KEY "Traps" +#define SFALL_CONFIG_USE_FIRST_AID_FRM_KEY "FirstAid" +#define SFALL_CONFIG_USE_DOCTOR_FRM_KEY "Doctor" +#define SFALL_CONFIG_USE_SCIENCE_FRM_KEY "Science" +#define SFALL_CONFIG_USE_REPAIR_FRM_KEY "Repair" #define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_MULTIPLIER 1 #define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR 3 From 13b76287f8b9fc9e36064ec5e1e03a266b0c219c Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Fri, 12 Aug 2022 14:55:36 +0300 Subject: [PATCH 09/37] Add science/repair on critters patch --- src/actions.cc | 20 ++++++++++++++++++++ src/sfall_config.h | 1 + 2 files changed, 21 insertions(+) diff --git a/src/actions.cc b/src/actions.cc index b732324..a570d85 100644 --- a/src/actions.cc +++ b/src/actions.cc @@ -25,6 +25,7 @@ #include "proto_types.h" #include "random.h" #include "scripts.h" +#include "sfall_config.h" #include "skill.h" #include "stat.h" #include "text_object.h" @@ -36,6 +37,12 @@ #define MAX_KNOCKDOWN_DISTANCE 20 +typedef enum ScienceRepairTargetType { + SCIENCE_REPAIR_TARGET_TYPE_DEFAULT, + SCIENCE_REPAIR_TARGET_TYPE_DUDE, + SCIENCE_REPAIR_TARGET_TYPE_ANYONE, +} ScienceRepairTargetType; + // 0x5106D0 static int _action_in_explode = 0; @@ -1382,6 +1389,19 @@ int actionUseSkill(Object* a1, Object* a2, int skill) break; } + // SFALL: Science on critters patch. + if (1) { + int targetType = SCIENCE_REPAIR_TARGET_TYPE_DEFAULT; + configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_SCIENCE_REPAIR_TARGET_TYPE_KEY, &targetType); + if (targetType == SCIENCE_REPAIR_TARGET_TYPE_DUDE) { + if (a2 == gDude) { + break; + } + } else if (targetType == SCIENCE_REPAIR_TARGET_TYPE_ANYONE) { + break; + } + } + return -1; case SKILL_SNEAK: dudeToggleState(0); diff --git a/src/sfall_config.h b/src/sfall_config.h index fb974f6..fab7f92 100644 --- a/src/sfall_config.h +++ b/src/sfall_config.h @@ -52,6 +52,7 @@ #define SFALL_CONFIG_USE_DOCTOR_FRM_KEY "Doctor" #define SFALL_CONFIG_USE_SCIENCE_FRM_KEY "Science" #define SFALL_CONFIG_USE_REPAIR_FRM_KEY "Repair" +#define SFALL_CONFIG_SCIENCE_REPAIR_TARGET_TYPE_KEY "ScienceOnCritters" #define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_MULTIPLIER 1 #define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR 3 From 140234f40e1c1b58b1496e75d92edf8d30924972 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Fri, 12 Aug 2022 15:03:06 +0300 Subject: [PATCH 10/37] Add game dialog fix --- src/game_dialog.cc | 12 ++++++++++++ src/sfall_config.h | 1 + 2 files changed, 13 insertions(+) diff --git a/src/game_dialog.cc b/src/game_dialog.cc index 2ede61c..1e55248 100644 --- a/src/game_dialog.cc +++ b/src/game_dialog.cc @@ -26,6 +26,7 @@ #include "proto.h" #include "random.h" #include "scripts.h" +#include "sfall_config.h" #include "skill.h" #include "stat.h" #include "text_font.h" @@ -670,10 +671,16 @@ static void gameDialogHighlightsExit(); static void gameDialogRedButtonsInit(); static void gameDialogRedButtonsExit(); +static bool gGameDialogFix; + // gdialog_init // 0x444D1C int gameDialogInit() { + // SFALL: Prevents from using 0 to escape from dialogue at any time. + gGameDialogFix = true; + configGetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_GAME_DIALOG_FIX_KEY, &gGameDialogFix); + return 0; } @@ -1946,6 +1953,11 @@ int _gdProcess() } else if (keyCode >= 1300 && keyCode <= 1330) { gameDialogOptionOnMouseExit(keyCode - 1300); } else if (keyCode >= 48 && keyCode <= 57) { + // SFALL: Prevents from using 0 to escape from dialogue at any time. + if (keyCode == KEY_0 && gGameDialogFix) { + continue; + } + int v11 = keyCode - 49; if (v11 < gGameDialogOptionEntriesLength) { pageCount = 0; diff --git a/src/sfall_config.h b/src/sfall_config.h index fab7f92..c852932 100644 --- a/src/sfall_config.h +++ b/src/sfall_config.h @@ -53,6 +53,7 @@ #define SFALL_CONFIG_USE_SCIENCE_FRM_KEY "Science" #define SFALL_CONFIG_USE_REPAIR_FRM_KEY "Repair" #define SFALL_CONFIG_SCIENCE_REPAIR_TARGET_TYPE_KEY "ScienceOnCritters" +#define SFALL_CONFIG_GAME_DIALOG_FIX_KEY "DialogueFix" #define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_MULTIPLIER 1 #define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR 3 From c3bffa67771f98b1a570025b761f3576802c7709 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Fri, 12 Aug 2022 15:49:43 +0300 Subject: [PATCH 11/37] Increase script message lists capacity --- src/scripts.cc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/scripts.cc b/src/scripts.cc index 6b6f30b..fcbff25 100644 --- a/src/scripts.cc +++ b/src/scripts.cc @@ -36,6 +36,10 @@ #define SCRIPT_LIST_EXTENT_SIZE 16 +// SFALL: Increase number of message lists for scripted dialogs. +// CE: In Sfall this increase is configurable with `BoostScriptDialogLimit`. +#define SCRIPT_DIALOG_MESSAGE_LIST_CAPACITY 10000 + typedef struct ScriptsListEntry { char name[16]; int local_vars_num; @@ -242,7 +246,7 @@ static Object* gScriptsRequestedStealingBy; static Object* gScriptsRequestedStealingFrom; // 0x6649D4 -static MessageList _script_dialog_msgs[1450]; +static MessageList _script_dialog_msgs[SCRIPT_DIALOG_MESSAGE_LIST_CAPACITY]; // scr.msg // @@ -1494,7 +1498,7 @@ int scriptsInit() return -1; } - for (int index = 0; index < 1450; index++) { + for (int index = 0; index < SCRIPT_DIALOG_MESSAGE_LIST_CAPACITY; index++) { if (!messageListInit(&(_script_dialog_msgs[index]))) { return -1; } @@ -1541,7 +1545,7 @@ int _scr_game_init() return -1; } - for (i = 0; i < 1450; i++) { + for (i = 0; i < SCRIPT_DIALOG_MESSAGE_LIST_CAPACITY; i++) { if (!messageListInit(&(_script_dialog_msgs[i]))) { debugPrint("\nERROR IN SCRIPT_DIALOG_MSGS!"); return -1; @@ -1611,7 +1615,7 @@ int scriptsExit() // 0x4A52F4 int _scr_message_free() { - for (int index = 0; index < 1450; index++) { + for (int index = 0; index < SCRIPT_DIALOG_MESSAGE_LIST_CAPACITY; index++) { MessageList* messageList = &(_script_dialog_msgs[index]); if (messageList->entries_num != 0) { if (!messageListFree(messageList)) { From d482f0e610215cac1e97dca655c88c24f2180396 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 14 Aug 2022 12:42:18 +0300 Subject: [PATCH 12/37] Add healing items improvements (#29) --- src/combat_ai.cc | 6 ++---- src/item.cc | 54 ++++++++++++++++++++++++++++++++++++++++++++++ src/item.h | 8 +++++++ src/sfall_config.h | 1 + 4 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/combat_ai.cc b/src/combat_ai.cc index d76a1ab..da67e41 100644 --- a/src/combat_ai.cc +++ b/src/combat_ai.cc @@ -988,8 +988,7 @@ static int _ai_check_drugs(Object* critter) } int drugPid = drug->pid; - if ((drugPid == PROTO_ID_STIMPACK || drugPid == PROTO_ID_SUPER_STIMPACK || drugPid == PROTO_ID_HEALING_POWDER) - && itemRemove(critter, drug, 1) == 0) { + if (itemIsHealing(drugPid) && itemRemove(critter, drug, 1) == 0) { if (_item_d_take_drug(critter, drug) == -1) { itemAdd(critter, drug, 1); } else { @@ -1027,8 +1026,7 @@ static int _ai_check_drugs(Object* critter) } if (index < AI_PACKET_CHEM_PRIMARY_DESIRE_COUNT) { - if (drugPid != PROTO_ID_STIMPACK && drugPid != PROTO_ID_SUPER_STIMPACK && drugPid != 273 - && itemRemove(critter, drug, 1) == 0) { + if (!itemIsHealing(drugPid) && itemRemove(critter, drug, 1) == 0) { if (_item_d_take_drug(critter, drug) == -1) { itemAdd(critter, drug, 1); } else { diff --git a/src/item.cc b/src/item.cc index c688f2e..2415b95 100644 --- a/src/item.cc +++ b/src/item.cc @@ -67,6 +67,10 @@ static void explosionsInit(); static void explosionsReset(); static void explosionsExit(); +static void healingItemsInit(); +static void healingItemsInitVanilla(); +static void healingItemsInitCustom(); + typedef struct DrugDescription { int drugPid; int gvar; @@ -179,6 +183,7 @@ static int gExplosionFrm; static int gExplosionRadius; static int gExplosionDamageType; static int gExplosionMaxTargets; +static int gHealingItemPids[HEALING_ITEM_COUNT]; // 0x4770E0 int itemsInit() @@ -197,6 +202,7 @@ int itemsInit() // SFALL booksInit(); explosionsInit(); + healingItemsInit(); return 0; } @@ -3584,3 +3590,51 @@ void explosionSetMaxTargets(int maxTargets) { gExplosionMaxTargets = maxTargets; } + +static void healingItemsInit() +{ + healingItemsInitVanilla(); + healingItemsInitCustom(); +} + +static void healingItemsInitVanilla() +{ + gHealingItemPids[HEALING_ITEM_STIMPACK] = PROTO_ID_STIMPACK; + gHealingItemPids[HEALING_ITEM_SUPER_STIMPACK] = PROTO_ID_SUPER_STIMPACK; + gHealingItemPids[HEALING_ITEM_HEALING_POWDER] = PROTO_ID_HEALING_POWDER; +} + +static void healingItemsInitCustom() +{ + char* tweaksFilePath = NULL; + configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_TWEAKS_FILE_KEY, &tweaksFilePath); + if (tweaksFilePath != NULL && *tweaksFilePath == '\0') { + tweaksFilePath = NULL; + } + + if (tweaksFilePath == NULL) { + return; + } + + Config tweaksConfig; + if (configInit(&tweaksConfig)) { + if (configRead(&tweaksConfig, tweaksFilePath, false)) { + configGetInt(&gSfallConfig, "Items", "STIMPAK", &(gHealingItemPids[HEALING_ITEM_STIMPACK])); + configGetInt(&gSfallConfig, "Items", "SUPER_STIMPAK", &(gHealingItemPids[HEALING_ITEM_SUPER_STIMPACK])); + configGetInt(&gSfallConfig, "Items", "HEALING_POWDER", &(gHealingItemPids[HEALING_ITEM_HEALING_POWDER])); + } + + configFree(&tweaksConfig); + } +} + +bool itemIsHealing(int pid) +{ + for (int index = 0; index < HEALING_ITEM_COUNT; index++) { + if (gHealingItemPids[index] == pid) { + return true; + } + } + + return false; +} diff --git a/src/item.h b/src/item.h index 2bd2189..012590d 100644 --- a/src/item.h +++ b/src/item.h @@ -13,6 +13,13 @@ typedef enum _WeaponClass { ATTACK_TYPE_COUNT, } WeaponClass; +typedef enum HealingItem { + HEALING_ITEM_STIMPACK, + HEALING_ITEM_SUPER_STIMPACK, + HEALING_ITEM_HEALING_POWDER, + HEALING_ITEM_COUNT, +} HealingItem; + int itemsInit(); void itemsReset(); void itemsExit(); @@ -143,5 +150,6 @@ int explosionGetDamageType(); void explosionSetDamageType(int damageType); int explosionGetMaxTargets(); void explosionSetMaxTargets(int maxTargets); +bool itemIsHealing(int pid); #endif /* ITEM_H */ diff --git a/src/sfall_config.h b/src/sfall_config.h index c852932..0956e23 100644 --- a/src/sfall_config.h +++ b/src/sfall_config.h @@ -54,6 +54,7 @@ #define SFALL_CONFIG_USE_REPAIR_FRM_KEY "Repair" #define SFALL_CONFIG_SCIENCE_REPAIR_TARGET_TYPE_KEY "ScienceOnCritters" #define SFALL_CONFIG_GAME_DIALOG_FIX_KEY "DialogueFix" +#define SFALL_CONFIG_TWEAKS_FILE_KEY "TweaksFile" #define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_MULTIPLIER 1 #define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR 3 From 31edb19379908f39ec026dc9f946b7740e6b9a0e Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 14 Aug 2022 12:52:13 +0300 Subject: [PATCH 13/37] Use English as fallback language --- src/message.cc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/message.cc b/src/message.cc index 96280c5..4491b29 100644 --- a/src/message.cc +++ b/src/message.cc @@ -200,6 +200,15 @@ bool messageListLoad(MessageList* messageList, const char* path) sprintf(localized_path, "%s\\%s\\%s", "text", language, path); file_ptr = fileOpen(localized_path, "rt"); + + // SFALL: Fallback to english if requested localization does not exist. + if (file_ptr == NULL) { + if (compat_stricmp(language, ENGLISH) != 0) { + sprintf(localized_path, "%s\\%s\\%s", "text", ENGLISH, path); + file_ptr = fileOpen(localized_path, "rt"); + } + } + if (file_ptr == NULL) { return false; } From cac96bfc133671a65cf585cec71a84da15c0a826 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 14 Aug 2022 13:06:51 +0300 Subject: [PATCH 14/37] Add gender-specific text improvements (#130) --- src/message.cc | 45 +++++++++++++++++++++++++++++++++++++++++++++ src/message.h | 2 ++ src/scripts.cc | 5 +++++ src/sfall_config.h | 1 + 4 files changed, 53 insertions(+) diff --git a/src/message.cc b/src/message.cc index 4491b29..dadfd8a 100644 --- a/src/message.cc +++ b/src/message.cc @@ -4,7 +4,9 @@ #include "game_config.h" #include "memory.h" #include "platform_compat.h" +#include "proto_types.h" #include "random.h" +#include "sfall_config.h" #include #include @@ -569,3 +571,46 @@ bool messageListFilterBadwords(MessageList* messageList) return true; } + +void messageListFilterGenderWords(MessageList* messageList, int gender) +{ + if (messageList == NULL) { + return; + } + + bool enabled = false; + configGetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_GAME_DIALOG_GENDER_WORDS_KEY, &enabled); + if (!enabled) { + return; + } + + for (int index = 0; index < messageList->entries_num; index++) { + MessageListItem* item = &(messageList->entries[index]); + char* text = item->text; + char* sep; + + while ((sep = strchr(text, '^')) != NULL) { + *sep = '\0'; + char* start = strrchr(text, '<'); + char* end = strchr(sep + 1, '>'); + *sep = '^'; + + if (start != NULL && end != NULL) { + char* src; + size_t length; + if (gender == GENDER_FEMALE) { + src = sep + 1; + length = end - sep - 1; + } else { + src = start + 1; + length = sep - start - 1; + } + + strncpy(start, src, length); + strcpy(start + length, end + 1); + } else { + text = sep + 1; + } + } + } +} diff --git a/src/message.h b/src/message.h index a1fab3a..ff70d76 100644 --- a/src/message.h +++ b/src/message.h @@ -27,4 +27,6 @@ bool _message_make_path(char* dest, const char* path); char* getmsg(MessageList* msg, MessageListItem* entry, int num); bool messageListFilterBadwords(MessageList* messageList); +void messageListFilterGenderWords(MessageList* messageList, int gender); + #endif /* MESSAGE_H */ diff --git a/src/scripts.cc b/src/scripts.cc index fcbff25..dd31748 100644 --- a/src/scripts.cc +++ b/src/scripts.cc @@ -24,6 +24,7 @@ #include "proto.h" #include "proto_instance.h" #include "queue.h" +#include "stat.h" #include "tile.h" #include "window_manager.h" #include "window_manager_private.h" @@ -2661,6 +2662,10 @@ static int scriptsGetMessageList(int a1, MessageList** messageListPtr) debugPrint("\nError filtering script dialog message file!"); return -1; } + + // SFALL: Gender-specific words. + int gender = critterGetStat(gDude, STAT_GENDER); + messageListFilterGenderWords(messageList, gender); } *messageListPtr = messageList; diff --git a/src/sfall_config.h b/src/sfall_config.h index 0956e23..73c7845 100644 --- a/src/sfall_config.h +++ b/src/sfall_config.h @@ -55,6 +55,7 @@ #define SFALL_CONFIG_SCIENCE_REPAIR_TARGET_TYPE_KEY "ScienceOnCritters" #define SFALL_CONFIG_GAME_DIALOG_FIX_KEY "DialogueFix" #define SFALL_CONFIG_TWEAKS_FILE_KEY "TweaksFile" +#define SFALL_CONFIG_GAME_DIALOG_GENDER_WORDS_KEY "DialogGenderWords" #define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_MULTIPLIER 1 #define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR 3 From 330edde0036bca0e0dc19956e727e2f66688a036 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 14 Aug 2022 17:30:45 +0300 Subject: [PATCH 15/37] Fix radiation bugs --- src/critter.cc | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/critter.cc b/src/critter.cc index abdbb25..9cb9c1b 100644 --- a/src/critter.cc +++ b/src/critter.cc @@ -568,6 +568,13 @@ void _process_rads(Object* obj, int radiationLevel, bool isHealing) if (obj == gDude) { // Radiation level message, higher is worse. messageListItem.num = 1000 + radiationLevelIndex; + + // SFALL: Fix radiation message when removing radiation effects. + if (isHealing) { + // You feel better. + messageListItem.num = 3003; + } + if (messageListGetItem(&gMiscMessageList, &messageListItem)) { displayMonitorAddMessage(messageListItem.text); } @@ -579,15 +586,18 @@ void _process_rads(Object* obj, int radiationLevel, bool isHealing) critterSetBonusStat(obj, gRadiationEffectStats[effect], value); } - if ((obj->data.critter.combat.results & DAM_DEAD) == 0) { - // Loop thru effects affecting primary stats. If any of the primary stat - // dropped below minimal value, kill it. - for (int effect = 0; effect < RADIATION_EFFECT_PRIMARY_STAT_COUNT; effect++) { - int base = critterGetBaseStatWithTraitModifier(obj, gRadiationEffectStats[effect]); - int bonus = critterGetBonusStat(obj, gRadiationEffectStats[effect]); - if (base + bonus < PRIMARY_STAT_MIN) { - critterKill(obj, -1, 1); - break; + // SFALL: Prevent death when removing radiation effects. + if (!isHealing) { + if ((obj->data.critter.combat.results & DAM_DEAD) == 0) { + // Loop thru effects affecting primary stats. If any of the primary stat + // dropped below minimal value, kill it. + for (int effect = 0; effect < RADIATION_EFFECT_PRIMARY_STAT_COUNT; effect++) { + int base = critterGetBaseStatWithTraitModifier(obj, gRadiationEffectStats[effect]); + int bonus = critterGetBonusStat(obj, gRadiationEffectStats[effect]); + if (base + bonus < PRIMARY_STAT_MIN) { + critterKill(obj, -1, 1); + break; + } } } } From 7096116296d08d98de6702a9d7153be7fb35df26 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 14 Aug 2022 17:48:59 +0300 Subject: [PATCH 16/37] Fix best weapon calculations --- src/combat_ai.cc | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/combat_ai.cc b/src/combat_ai.cc index da67e41..9a42bd8 100644 --- a/src/combat_ai.cc +++ b/src/combat_ai.cc @@ -1714,17 +1714,18 @@ static Object* _ai_best_weapon(Object* attacker, Object* weapon1, Object* weapon return NULL; } - avgDamage1 = (maxDamage - minDamage) / 2; + // SFALL: Fix avg damage calculation. + avgDamage1 = (maxDamage + minDamage) / 2; if (_item_w_area_damage_radius(weapon1, HIT_MODE_RIGHT_WEAPON_PRIMARY) > 0 && defender != NULL) { attack.weapon = weapon1; _compute_explosion_on_extras(&attack, 0, weaponIsGrenade(weapon1), 1); avgDamage1 *= attack.extrasLength + 1; } - // TODO: Probably an error, why it takes [weapon2], should likely use - // [weapon1]. - if (weaponGetPerk(weapon2) != -1) { - avgDamage1 *= 5; + // SFALL: Fix for the incorrect item being checked. + if (weaponGetPerk(weapon1) != -1) { + // SFALL: Lower weapon score multiplier for having perk. + avgDamage1 *= 2; } if (defender != NULL) { @@ -1758,7 +1759,8 @@ static Object* _ai_best_weapon(Object* attacker, Object* weapon1, Object* weapon return NULL; } - avgDamage2 = (maxDamage - minDamage) / 2; + // SFALL: Fix avg damage calculation. + avgDamage2 = (maxDamage + minDamage) / 2; if (_item_w_area_damage_radius(weapon2, HIT_MODE_RIGHT_WEAPON_PRIMARY) > 0 && defender != NULL) { attack.weapon = weapon2; _compute_explosion_on_extras(&attack, 0, weaponIsGrenade(weapon2), 1); @@ -1766,7 +1768,8 @@ static Object* _ai_best_weapon(Object* attacker, Object* weapon1, Object* weapon } if (weaponGetPerk(weapon2) != -1) { - avgDamage2 *= 5; + // SFALL: Lower weapon score multiplier for having perk. + avgDamage2 *= 2; } if (defender != NULL) { From b10c580d6f4518deebc1759e34e2e9b949bb5517 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 14 Aug 2022 18:46:02 +0300 Subject: [PATCH 17/37] Fix displaying xp gained with Swift Learner bonus --- src/combat.cc | 6 ++++-- src/inventory.cc | 8 +++++--- src/stat.cc | 12 +++++++++--- src/stat.h | 4 ++-- src/world_map.cc | 14 +++++++------- 5 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/combat.cc b/src/combat.cc index cc72f77..0f3599c 100644 --- a/src/combat.cc +++ b/src/combat.cc @@ -2822,7 +2822,9 @@ void _combat_give_exps(int exp_points) return; } - pcAddExperience(exp_points); + // SFALL: Display actual xp received. + int xpGained; + pcAddExperience(exp_points, &xpGained); v7.num = 621; // %s you earn %d exp. points. if (!messageListGetItem(&gProtoMessageList, &v7)) { @@ -2841,7 +2843,7 @@ void _combat_give_exps(int exp_points) return; } - sprintf(text, v7.text, v9.text, exp_points); + sprintf(text, v7.text, v9.text, xpGained); displayMonitorAddMessage(text); } diff --git a/src/inventory.cc b/src/inventory.cc index b9ffe05..bd1891d 100644 --- a/src/inventory.cc +++ b/src/inventory.cc @@ -3901,15 +3901,17 @@ int inventoryOpenLooting(Object* a1, Object* a2) stealingXp = std::min(300 - skillGetValue(a1, SKILL_STEAL), stealingXp); debugPrint("\n[[[%d]]]", 300 - skillGetValue(a1, SKILL_STEAL)); + // SFALL: Display actual xp received. + int xpGained; + pcAddExperience(stealingXp, &xpGained); + // You gain %d experience points for successfully using your Steal skill. messageListItem.num = 29; if (messageListGetItem(&gInventoryMessageList, &messageListItem)) { char formattedText[200]; - sprintf(formattedText, messageListItem.text, stealingXp); + sprintf(formattedText, messageListItem.text, xpGained); displayMonitorAddMessage(formattedText); } - - pcAddExperience(stealingXp); } } } diff --git a/src/stat.cc b/src/stat.cc index 2be253a..9f33b44 100644 --- a/src/stat.cc +++ b/src/stat.cc @@ -717,14 +717,16 @@ int statRoll(Object* critter, int stat, int modifier, int* howMuch) } // 0x4AFAA8 -int pcAddExperience(int xp) +int pcAddExperience(int xp, int* xpGained) { - return pcAddExperienceWithOptions(xp, true); + return pcAddExperienceWithOptions(xp, true, xpGained); } // 0x4AFAB8 -int pcAddExperienceWithOptions(int xp, bool a2) +int pcAddExperienceWithOptions(int xp, bool a2, int* xpGained) { + int oldXp = gPcStatValues[PC_STAT_EXPERIENCE]; + int newXp = gPcStatValues[PC_STAT_EXPERIENCE]; newXp += xp; newXp += perkGetRank(gDude, PERK_SWIFT_LEARNER) * 5 * xp / 100; @@ -784,6 +786,10 @@ int pcAddExperienceWithOptions(int xp, bool a2) } } + if (xpGained != NULL) { + *xpGained = newXp - oldXp; + } + return 0; } diff --git a/src/stat.h b/src/stat.h index d5132b9..afe5444 100644 --- a/src/stat.h +++ b/src/stat.h @@ -35,8 +35,8 @@ char* pcStatGetName(int pcStat); char* pcStatGetDescription(int pcStat); int statGetFrmId(int stat); int statRoll(Object* critter, int stat, int modifier, int* howMuch); -int pcAddExperience(int xp); -int pcAddExperienceWithOptions(int xp, bool a2); +int pcAddExperience(int xp, int* xpGained = NULL); +int pcAddExperienceWithOptions(int xp, bool a2, int* xpGained = NULL); int pcSetExperience(int a1); static inline bool statIsValid(int stat) diff --git a/src/world_map.cc b/src/world_map.cc index 21e1d3a..e33bbe8 100644 --- a/src/world_map.cc +++ b/src/world_map.cc @@ -3678,21 +3678,21 @@ int _wmRndEncounterOccurred() int xp = 100 - outdoorsman; if (xp > 0) { + // SFALL: Display actual xp received. + debugPrint("WorldMap: Giving Player [%d] Experience For Catching Rnd Encounter!", xp); + + int xpGained; + pcAddExperience(xp, &xpGained); + MessageListItem messageListItem; char* text = getmsg(&gMiscMessageList, &messageListItem, 8500); if (strlen(text) < 110) { char formattedText[120]; - sprintf(formattedText, text, xp); + sprintf(formattedText, text, xpGained); displayMonitorAddMessage(formattedText); } else { debugPrint("WorldMap: Error: Rnd Encounter string too long!"); } - - debugPrint("WorldMap: Giving Player [%d] Experience For Catching Rnd Encounter!", xp); - - if (xp < 100) { - pcAddExperience(xp); - } } } } else { From c35ea77c59ea18dfbaa9d66e45d108d5b2905bfe Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 14 Aug 2022 20:45:26 +0300 Subject: [PATCH 18/37] Fix ammo details when examining in barter screen --- src/proto_instance.cc | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/proto_instance.cc b/src/proto_instance.cc index 3bae855..72be2e6 100644 --- a/src/proto_instance.cc +++ b/src/proto_instance.cc @@ -8,6 +8,7 @@ #include "debug.h" #include "display_monitor.h" #include "game.h" +#include "game_dialog.h" #include "game_sound.h" #include "geometry.h" #include "interface.h" @@ -499,6 +500,12 @@ int _obj_examine_func(Object* critter, Object* target, void (*fn)(char* string)) fn(formattedText); } } else if (itemType == ITEM_TYPE_AMMO) { + // SFALL: Fix ammo details when examining in barter screen. + // CE: Underlying `gameDialogRenderSupplementaryMessage` cannot + // accumulate strings like `inventoryRenderItemDescription` does. + char ammoFormattedText[260 * 3]; + ammoFormattedText[0] = '\0'; + MessageListItem ammoMessageListItem; ammoMessageListItem.num = 510; @@ -510,7 +517,11 @@ int _obj_examine_func(Object* critter, Object* target, void (*fn)(char* string)) sprintf(formattedText, ammoMessageListItem.text, ammoGetArmorClassModifier(target)); - fn(formattedText); + if (fn == gameDialogRenderSupplementaryMessage) { + strcat(ammoFormattedText, formattedText); + } else { + fn(formattedText); + } ammoMessageListItem.num++; if (!messageListGetItem(&gProtoMessageList, &ammoMessageListItem)) { @@ -521,7 +532,12 @@ int _obj_examine_func(Object* critter, Object* target, void (*fn)(char* string)) sprintf(formattedText, ammoMessageListItem.text, ammoGetDamageResistanceModifier(target)); - fn(formattedText); + if (fn == gameDialogRenderSupplementaryMessage) { + strcat(ammoFormattedText, ", "); + strcat(ammoFormattedText, formattedText); + } else { + fn(formattedText); + } ammoMessageListItem.num++; if (!messageListGetItem(&gProtoMessageList, &ammoMessageListItem)) { @@ -533,7 +549,14 @@ int _obj_examine_func(Object* critter, Object* target, void (*fn)(char* string)) ammoMessageListItem.text, ammoGetDamageMultiplier(target), ammoGetDamageDivisor(target)); - fn(formattedText); + if (fn == gameDialogRenderSupplementaryMessage) { + strcat(ammoFormattedText, ", "); + strcat(ammoFormattedText, formattedText); + strcat(ammoFormattedText, "."); + fn(ammoFormattedText); + } else { + fn(formattedText); + } } } From 5a47f7402378683b0310ce3f3a57d5e146ac1a9c Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 14 Aug 2022 21:45:58 +0300 Subject: [PATCH 19/37] Fix base EMP damage resistance --- src/proto.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/proto.cc b/src/proto.cc index 0b7427f..0445b26 100644 --- a/src/proto.cc +++ b/src/proto.cc @@ -1863,6 +1863,10 @@ int _ResetPlayer() pcStatsReset(); protoCritterDataResetStats(&(proto->critter.data)); + + // SFALL: Fix base EMP DR not being properly initialized. + proto->critter.data.baseStats[STAT_DAMAGE_RESISTANCE_EMP] = 100; + critterReset(); characterEditorReset(); protoCritterDataResetSkills(&(proto->critter.data)); From 52af5cfc1f001e1727c13b709fdb850eaf632829 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 14 Aug 2022 22:07:22 +0300 Subject: [PATCH 20/37] Fix object search when loading game in combat mode --- src/combat.cc | 12 +++++++++--- src/object.cc | 13 +++++++++++++ src/object.h | 2 ++ 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/combat.cc b/src/combat.cc index 0f3599c..42e2188 100644 --- a/src/combat.cc +++ b/src/combat.cc @@ -2171,7 +2171,9 @@ int combatLoad(File* stream) if (a2 == -1) { aiInfo->friendlyDead = NULL; } else { - aiInfo->friendlyDead = objectFindById(a2); + // SFALL: Fix incorrect object type search when loading a game in + // combat mode. + aiInfo->friendlyDead = objectTypedFindById(a2, OBJ_TYPE_CRITTER); if (aiInfo->friendlyDead == NULL) return -1; } @@ -2180,7 +2182,9 @@ int combatLoad(File* stream) if (a2 == -1) { aiInfo->lastTarget = NULL; } else { - aiInfo->lastTarget = objectFindById(a2); + // SFALL: Fix incorrect object type search when loading a game in + // combat mode. + aiInfo->lastTarget = objectTypedFindById(a2, OBJ_TYPE_CRITTER); if (aiInfo->lastTarget == NULL) return -1; } @@ -2189,7 +2193,9 @@ int combatLoad(File* stream) if (a2 == -1) { aiInfo->lastItem = NULL; } else { - aiInfo->lastItem = objectFindById(a2); + // SFALL: Fix incorrect object type search when loading a game in + // combat mode. + aiInfo->lastItem = objectTypedFindById(a2, OBJ_TYPE_ITEM); if (aiInfo->lastItem == NULL) return -1; } diff --git a/src/object.cc b/src/object.cc index 6d1e6f5..5a1f908 100644 --- a/src/object.cc +++ b/src/object.cc @@ -5222,3 +5222,16 @@ static int _obj_preload_sort(const void* a1, const void* a2) cmp = ((v1 & 0xFF0000) >> 16) - (((v2 & 0xFF0000) >> 16)); return cmp; } + +Object* objectTypedFindById(int id, int type) +{ + Object* obj = objectFindFirst(); + while (obj != NULL) { + if (obj->id == id && PID_TYPE(obj->pid) == type) { + return obj; + } + obj = objectFindNext(); + } + + return NULL; +} diff --git a/src/object.h b/src/object.h index 93c62b3..18ae033 100644 --- a/src/object.h +++ b/src/object.h @@ -96,4 +96,6 @@ int _obj_save_dude(File* stream); int _obj_load_dude(File* stream); void _obj_fix_violence_settings(int* fid); +Object* objectTypedFindById(int id, int type); + #endif /* OBJECT_H */ From 27c5beea014fa7bf873c250d601c0421e5bf868f Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 14 Aug 2022 22:15:13 +0300 Subject: [PATCH 21/37] Fix max items quantity added via scripting --- src/interpreter_extra.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/interpreter_extra.cc b/src/interpreter_extra.cc index f86624b..4decf4c 100644 --- a/src/interpreter_extra.cc +++ b/src/interpreter_extra.cc @@ -3604,7 +3604,8 @@ static void opAddMultipleObjectsToInventory(Program* program) if (quantity < 0) { quantity = 1; } else if (quantity > 99999) { - quantity = 500; + // SFALL + quantity = 99999; } if (itemAdd(object, item, quantity) == 0) { From 8700d0c6012c881f3e42ff9c8b43f751bdd56305 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 14 Aug 2022 22:16:48 +0300 Subject: [PATCH 22/37] Remove unreferenced label --- src/art.cc | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/art.cc b/src/art.cc index 80b5420..f9d85cf 100644 --- a/src/art.cc +++ b/src/art.cc @@ -698,12 +698,6 @@ static int artReadList(const char* path, char** artListPtr, int* artListSizePtr) fileClose(stream); return 0; - -err: - - fileClose(stream); - - return -1; } // 0x419760 From df4382f2e01c3857b156fa82ba03d7a0994a4618 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 14 Aug 2022 22:46:28 +0300 Subject: [PATCH 23/37] Fix Silent Death bonus for critical hits --- src/combat.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/combat.cc b/src/combat.cc index 42e2188..3e48336 100644 --- a/src/combat.cc +++ b/src/combat.cc @@ -3845,6 +3845,16 @@ static int attackCompute(Attack* attack) switch (roll) { case ROLL_CRITICAL_SUCCESS: damageMultiplier = attackComputeCriticalHit(attack); + + // SFALL: Fix Silent Death bonus not being applied to critical hits. + if ((attackType == ATTACK_TYPE_MELEE || attackType == ATTACK_TYPE_UNARMED) && attack->attacker == gDude) { + if (perkHasRank(gDude, PERK_SILENT_DEATH) + && !_is_hit_from_front(gDude, attack->defender) + && dudeHasState(DUDE_STATE_SNEAKING) + && gDude != attack->defender->data.critter.combat.whoHitMe) { + damageMultiplier *= 2; + } + } // FALLTHROUGH case ROLL_SUCCESS: attack->attackerFlags |= DAM_HIT; From c5c5ecd499481792a519fcf3887d6df79e490d45 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 14 Aug 2022 23:06:11 +0300 Subject: [PATCH 24/37] Fix for Jet antidote not being removed --- src/item.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/item.cc b/src/item.cc index 2415b95..405a645 100644 --- a/src/item.cc +++ b/src/item.cc @@ -2806,7 +2806,8 @@ int _item_d_take_drug(Object* critter, Object* item) dudeClearAddiction(PROTO_ID_JET); } - return 0; + // SFALL: Fix for Jet antidote not being removed. + return 1; } } From 957c5af66b3e7481f1fd5326661f581ec0ed4193 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Sun, 14 Aug 2022 23:22:37 +0300 Subject: [PATCH 25/37] Fix pcx extension check --- src/datafile.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/datafile.cc b/src/datafile.cc index bccd470..00e9824 100644 --- a/src/datafile.cc +++ b/src/datafile.cc @@ -89,7 +89,7 @@ unsigned char* datafileReadRaw(char* path, int* widthPtr, int* heightPtr) char* mangledPath = gDatafileNameMangler(path); char* dot = strrchr(mangledPath, '.'); if (dot != NULL) { - if (compat_stricmp(dot + 1, "pcx")) { + if (compat_stricmp(dot + 1, "pcx") == 0) { return pcxRead(mangledPath, widthPtr, heightPtr, gDatafilePalette); } } From 79f5e00f6e9682d39bcd5ddf427ce8d78efc0e79 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Mon, 15 Aug 2022 00:16:36 +0300 Subject: [PATCH 26/37] Fix town map hotkeys --- src/sfall_config.h | 1 + src/world_map.cc | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/src/sfall_config.h b/src/sfall_config.h index 73c7845..c6a64fd 100644 --- a/src/sfall_config.h +++ b/src/sfall_config.h @@ -56,6 +56,7 @@ #define SFALL_CONFIG_GAME_DIALOG_FIX_KEY "DialogueFix" #define SFALL_CONFIG_TWEAKS_FILE_KEY "TweaksFile" #define SFALL_CONFIG_GAME_DIALOG_GENDER_WORDS_KEY "DialogGenderWords" +#define SFALL_CONFIG_TOWN_MAP_HOTKEYS_FIX "TownMapHotkeysFix" #define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_MULTIPLIER 1 #define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR 3 diff --git a/src/world_map.cc b/src/world_map.cc index e33bbe8..3922895 100644 --- a/src/world_map.cc +++ b/src/world_map.cc @@ -28,6 +28,7 @@ #include "queue.h" #include "random.h" #include "scripts.h" +#include "sfall_config.h" #include "skill.h" #include "stat.h" #include "string_parsers.h" @@ -902,6 +903,8 @@ static int _wmMaxEncBaseTypes; // 0x67303C static int gEncounterTablesLength; +static bool gTownMapHotkeysFix; + static int _wmGenDataInit(); static int _wmGenDataReset(); static int _wmWorldMapSaveTempData(); @@ -1026,6 +1029,10 @@ int worldmapInit() _wmMarkSubTileRadiusVisited(_world_xpos, _world_ypos); _wmWorldMapSaveTempData(); + // SFALL + gTownMapHotkeysFix = true; + configGetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_TOWN_MAP_HOTKEYS_FIX, &gTownMapHotkeysFix); + return 0; } @@ -6104,6 +6111,14 @@ int worldmapCityMapViewSelect(int* mapIndexPtr) if (keyCode >= KEY_1 && keyCode < KEY_1 + city->entrancesLength) { EntranceInfo* entrance = &(city->entrances[keyCode - KEY_1]); + // SFALL: Prevent using number keys to enter unvisited areas on + // a town map. + if (gTownMapHotkeysFix) { + if (entrance->state == 0 || entrance->x == -1 || entrance->y == -1) { + continue; + } + } + *mapIndexPtr = entrance->map; mapSetEnteringLocation(entrance->elevation, entrance->tile, entrance->rotation); From 1f6339b3b300c14660e8a78f764de44ce93b1736 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 16 Aug 2022 11:25:56 +0300 Subject: [PATCH 27/37] Uncollapse _db_select --- src/art.cc | 59 +++++++++++++++++++++++++++++++------------ src/db.cc | 16 +++++++----- src/db.h | 5 ++-- src/game.cc | 3 +++ src/window_manager.cc | 2 +- 5 files changed, 59 insertions(+), 26 deletions(-) diff --git a/src/art.cc b/src/art.cc index f9d85cf..b81422b 100644 --- a/src/art.cc +++ b/src/art.cc @@ -146,15 +146,31 @@ int artInit() gArtLanguageInitialized = true; } + bool critterDbSelected = false; for (int objectType = 0; objectType < OBJ_TYPE_COUNT; objectType++) { gArtListDescriptions[objectType].flags = 0; sprintf(path, "%s%s%s\\%s.lst", _cd_path_base, "art\\", gArtListDescriptions[objectType].name, gArtListDescriptions[objectType].name); + int oldDb; + if (objectType == OBJ_TYPE_CRITTER) { + oldDb = _db_current(); + critterDbSelected = true; + _db_select(_critter_db_handle); + } + if (artReadList(path, &(gArtListDescriptions[objectType].fileNames), &(gArtListDescriptions[objectType].fileNamesLength)) != 0) { debugPrint("art_read_lst failed in art_init\n"); + if (critterDbSelected) { + _db_select(oldDb); + } cacheFree(&gArtCache); return -1; } + + if (objectType == OBJ_TYPE_CRITTER) { + critterDbSelected = false; + _db_select(oldDb); + } } _anon_alias = (int*)internal_malloc(sizeof(*_anon_alias) * gArtListDescriptions[OBJ_TYPE_CRITTER].fileNamesLength); @@ -848,8 +864,8 @@ bool artExists(int fid) int oldDb = -1; if (FID_TYPE(fid) == OBJ_TYPE_CRITTER) { - oldDb = _db_current(1); - _db_current(_critter_db_handle); + oldDb = _db_current(); + _db_select(_critter_db_handle); } char* filePath = artBuildFilePath(fid); @@ -861,27 +877,38 @@ bool artExists(int fid) } if (oldDb != -1) { - _db_current(oldDb); + _db_select(oldDb); } return result; } +// NOTE: Exactly the same implementation as `artExists`. +// // 0x419930 bool _art_fid_valid(int fid) { - // NOTE: Original Code involves calling some unknown function. Check in debugger in mapper. + bool result = false; + int oldDb = -1; + + if (FID_TYPE(fid) == OBJ_TYPE_CRITTER) { + oldDb = _db_current(); + _db_select(_critter_db_handle); + } + char* filePath = artBuildFilePath(fid); - if (filePath == NULL) { - return false; + if (filePath != NULL) { + int fileSize; + if (dbGetFileSize(filePath, &fileSize) != -1) { + result = true; + } } - int fileSize; - if (dbGetFileSize(filePath, &fileSize) == -1) { - return false; + if (oldDb != -1) { + _db_select(oldDb); } - return true; + return result; } // 0x419998 @@ -931,8 +958,8 @@ static int artCacheGetFileSizeImpl(int fid, int* sizePtr) int result = -1; if (FID_TYPE(fid) == OBJ_TYPE_CRITTER) { - oldDb = _db_current(1); - _db_current(_critter_db_handle); + oldDb = _db_current(); + _db_select(_critter_db_handle); } char* artFilePath = artBuildFilePath(fid); @@ -967,7 +994,7 @@ static int artCacheGetFileSizeImpl(int fid, int* sizePtr) } if (oldDb != -1) { - _db_current(oldDb); + _db_select(oldDb); } return result; @@ -980,8 +1007,8 @@ static int artCacheReadDataImpl(int fid, int* sizePtr, unsigned char* data) int result = -1; if (FID_TYPE(fid) == OBJ_TYPE_CRITTER) { - oldDb = _db_current(1); - _db_current(_critter_db_handle); + oldDb = _db_current(); + _db_select(_critter_db_handle); } char* artFileName = artBuildFilePath(fid); @@ -1015,7 +1042,7 @@ static int artCacheReadDataImpl(int fid, int* sizePtr, unsigned char* data) } if (oldDb != -1) { - _db_current(oldDb); + _db_select(oldDb); } return result; diff --git a/src/db.cc b/src/db.cc index b0399b0..647a3fb 100644 --- a/src/db.cc +++ b/src/db.cc @@ -61,20 +61,22 @@ int dbOpen(const char* filePath1, int a2, const char* filePath2, int a4) return 0; } -// NOTE: This function simply returns 0, but it definitely accept one parameter -// via eax, as seen at every call site. This value is ignored. It's impossible -// to guess it's name. -// // 0x4C5D54 -int _db_current(int a1) +int _db_select(int dbHandle) +{ + return 0; +} + +// NOTE: Uncollapsed 0x4C5D54. +int _db_current() { return 0; } // 0x4C5D58 -bool _db_total() +int _db_total() { - return true; + return 0; } // 0x4C5D60 diff --git a/src/db.h b/src/db.h index f6206a6..986feb2 100644 --- a/src/db.h +++ b/src/db.h @@ -11,8 +11,9 @@ typedef void FileReadProgressHandler(); typedef char* StrdupProc(const char* string); int dbOpen(const char* filePath1, int a2, const char* filePath2, int a4); -int _db_current(int a1); -bool _db_total(); +int _db_select(int dbHandle); +int _db_current(); +int _db_total(); void dbExit(); int dbGetFileSize(const char* filePath, int* sizePtr); int dbGetFileContents(const char* filePath, void* ptr); diff --git a/src/game.cc b/src/game.cc index e77acd8..38ae806 100644 --- a/src/game.cc +++ b/src/game.cc @@ -1251,6 +1251,7 @@ static int gameDbInit() _critter_db_handle = dbOpen(main_file_name, 0, patch_file_name, 1); if (_critter_db_handle == -1) { + _db_select(_master_db_handle); showMesageBox("Could not find the critter datafile. Please make sure the FALLOUT CD is in the drive and that you are running FALLOUT from the directory you installed it to."); return -1; } @@ -1263,6 +1264,8 @@ static int gameDbInit() } } + _db_select(_master_db_handle); + return 0; } diff --git a/src/window_manager.cc b/src/window_manager.cc index 9840c71..5411ab1 100644 --- a/src/window_manager.cc +++ b/src/window_manager.cc @@ -132,7 +132,7 @@ int windowManagerInit(VideoSystemInitProc* videoSystemInitProc, VideoSystemExitP gOrderedWindowIds[index] = -1; } - if (!_db_total()) { + if (_db_total() == 0) { if (dbOpen(NULL, 0, _path_patches, 1) == -1) { return WINDOW_MANAGER_ERR_INITIALIZING_DEFAULT_DATABASE; } From 1128813bf0c59c42144bc1aeb9f4561b4596c403 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 16 Aug 2022 11:35:21 +0300 Subject: [PATCH 28/37] Fix testing pointers for nulls in Sonora --- src/interpreter.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/interpreter.cc b/src/interpreter.cc index 805f375..7e96d74 100644 --- a/src/interpreter.cc +++ b/src/interpreter.cc @@ -1339,6 +1339,16 @@ static void opConditionalOperatorGreaterThan(Program* program) assert(false && "Should be unreachable"); } break; + // Sonora folks tend to use "object > 0" to test objects for nulls. + case VALUE_TYPE_PTR: + switch (value[0].opcode) { + case VALUE_TYPE_INT: + result = (intptr_t)value[1].pointerValue > (intptr_t)value[0].integerValue; + break; + default: + assert(false && "Should be unreachable"); + } + break; default: assert(false && "Should be unreachable"); } From a1521049b53d0d65b128abd320d4bac8dd679abb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20=C4=90urinec?= Date: Tue, 16 Aug 2022 10:52:37 +0200 Subject: [PATCH 29/37] Fix interface button positions (#131) --- src/interface.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/interface.cc b/src/interface.cc index 9f4d0d3..f0f9e35 100644 --- a/src/interface.cc +++ b/src/interface.cc @@ -459,7 +459,7 @@ int interfaceInit() goto err; } - gInventoryButton = buttonCreate(gInterfaceBarWindow, 211, 41, 32, 21, -1, -1, -1, KEY_LOWERCASE_I, gInventoryButtonUpFrmData, gInventoryButtonDownFrmData, NULL, 0); + gInventoryButton = buttonCreate(gInterfaceBarWindow, 211, 40, 32, 21, -1, -1, -1, KEY_LOWERCASE_I, gInventoryButtonUpFrmData, gInventoryButtonDownFrmData, NULL, 0); if (gInventoryButton == -1) { goto err; } @@ -478,7 +478,7 @@ int interfaceInit() goto err; } - gOptionsButton = buttonCreate(gInterfaceBarWindow, 210, 62, 34, 34, -1, -1, -1, KEY_LOWERCASE_O, gOptionsButtonUpFrmData, gOptionsButtonDownFrmData, NULL, 0); + gOptionsButton = buttonCreate(gInterfaceBarWindow, 210, 61, 34, 34, -1, -1, -1, KEY_LOWERCASE_O, gOptionsButtonUpFrmData, gOptionsButtonDownFrmData, NULL, 0); if (gOptionsButton == -1) { goto err; } @@ -529,7 +529,7 @@ int interfaceInit() goto err; } - gMapButton = buttonCreate(gInterfaceBarWindow, 526, 40, 41, 19, -1, -1, -1, KEY_TAB, gMapButtonUpFrmData, gMapButtonDownFrmData, NULL, BUTTON_FLAG_TRANSPARENT); + gMapButton = buttonCreate(gInterfaceBarWindow, 526, 39, 41, 19, -1, -1, -1, KEY_TAB, gMapButtonUpFrmData, gMapButtonDownFrmData, NULL, BUTTON_FLAG_TRANSPARENT); if (gMapButton == -1) { goto err; } @@ -549,7 +549,7 @@ int interfaceInit() goto err; } - gPipboyButton = buttonCreate(gInterfaceBarWindow, 526, 78, 41, 19, -1, -1, -1, KEY_LOWERCASE_P, gPipboyButtonUpFrmData, gPipboyButtonDownFrmData, NULL, 0); + gPipboyButton = buttonCreate(gInterfaceBarWindow, 526, 77, 41, 19, -1, -1, -1, KEY_LOWERCASE_P, gPipboyButtonUpFrmData, gPipboyButtonDownFrmData, NULL, 0); if (gPipboyButton == -1) { goto err; } @@ -569,7 +569,7 @@ int interfaceInit() goto err; } - gCharacterButton = buttonCreate(gInterfaceBarWindow, 526, 59, 41, 19, -1, -1, -1, KEY_LOWERCASE_C, gCharacterButtonUpFrmData, gCharacterButtonDownFrmData, NULL, 0); + gCharacterButton = buttonCreate(gInterfaceBarWindow, 526, 58, 41, 19, -1, -1, -1, KEY_LOWERCASE_C, gCharacterButtonUpFrmData, gCharacterButtonDownFrmData, NULL, 0); if (gCharacterButton == -1) { goto err; } From 0f60556b73b7896086b3a2682dc9a1f060cc57fc Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 16 Aug 2022 14:14:53 +0300 Subject: [PATCH 30/37] Decompile some input functions --- src/core.cc | 76 ++++++++++++++++++++++++++++++++++++++++++++++++----- src/core.h | 16 ++++++++--- 2 files changed, 83 insertions(+), 9 deletions(-) diff --git a/src/core.cc b/src/core.cc index d22b221..19d02da 100644 --- a/src/core.cc +++ b/src/core.cc @@ -17,11 +17,11 @@ #include #include -// NOT USED. -void (*_idle_func)() = NULL; +// 0x51E234 +IdleFunc* _idle_func = NULL; -// NOT USED. -void (*_focus_func)(int) = NULL; +// 0x51E238 +FocusFunc* _focus_func = NULL; // 0x51E23C int gKeyboardKeyRepeatRate = 80; @@ -922,6 +922,70 @@ unsigned int _get_bk_time() return gTickerLastTimestamp; } +// NOTE: Unused. +// +// 0x4C9418 +void inputSetKeyboardKeyRepeatRate(int value) +{ + gKeyboardKeyRepeatRate = value; +} + +// NOTE: Unused. +// +// 0x4C9420 +int inputGetKeyboardKeyRepeatRate() +{ + return gKeyboardKeyRepeatRate; +} + +// NOTE: Unused. +// +// 0x4C9428 +void inputSetKeyboardKeyRepeatDelay(int value) +{ + gKeyboardKeyRepeatDelay = value; +} + +// NOTE: Unused. +// +// 0x4C9430 +int inputGetKeyboardKeyRepeatDelay() +{ + return gKeyboardKeyRepeatDelay; +} + +// NOTE: Unused. +// +// 0x4C9438 +void inputSetFocusFunc(FocusFunc* func) +{ + _focus_func = func; +} + +// NOTE: Unused. +// +// 0x4C9440 +FocusFunc* inputGetFocusFunc() +{ + return _focus_func; +} + +// NOTE: Unused. +// +// 0x4C9448 +void inputSetIdleFunc(IdleFunc* func) +{ + _idle_func = func; +} + +// NOTE: Unused. +// +// 0x4C9450 +IdleFunc* inputGetIdleFunc() +{ + return _idle_func; +} + // 0x4C9490 void buildNormalizedQwertyKeys() { @@ -1375,7 +1439,7 @@ void _GNW95_process_key(KeyboardData* data) void _GNW95_lost_focus() { if (_focus_func != NULL) { - _focus_func(0); + _focus_func(false); } while (!gProgramIsActive) { @@ -1387,7 +1451,7 @@ void _GNW95_lost_focus() } if (_focus_func != NULL) { - _focus_func(1); + _focus_func(true); } } diff --git a/src/core.h b/src/core.h index 575a1c9..9210f52 100644 --- a/src/core.h +++ b/src/core.h @@ -414,7 +414,9 @@ typedef struct InputEvent { int mouseY; } InputEvent; -typedef void TickerProc(); +typedef void(IdleFunc)(); +typedef void(FocusFunc)(bool focus); +typedef void(TickerProc)(); typedef struct TickerListNode { int flags; @@ -461,8 +463,8 @@ typedef int(PauseHandler)(); typedef int(ScreenshotHandler)(int width, int height, unsigned char* buffer, unsigned char* palette); typedef void(VcrPlaybackCompletionCallback)(int reason); -extern void (*_idle_func)(); -extern void (*_focus_func)(int); +extern IdleFunc* _idle_func; +extern FocusFunc* _focus_func; extern int gKeyboardKeyRepeatRate; extern int gKeyboardKeyRepeatDelay; extern bool _keyboard_hooked; @@ -596,6 +598,14 @@ void coreDelay(unsigned int ms); unsigned int getTicksSince(unsigned int a1); unsigned int getTicksBetween(unsigned int a1, unsigned int a2); unsigned int _get_bk_time(); +void inputSetKeyboardKeyRepeatRate(int value); +int inputGetKeyboardKeyRepeatRate(); +void inputSetKeyboardKeyRepeatDelay(int value); +int inputGetKeyboardKeyRepeatDelay(); +void inputSetFocusFunc(FocusFunc* func); +FocusFunc* inputGetFocusFunc(); +void inputSetIdleFunc(IdleFunc* func); +IdleFunc* inputGetIdleFunc(); void buildNormalizedQwertyKeys(); int _GNW95_input_init(); void _GNW95_process_message(); From cfca07f7f2f2d0586f06a7ecccd6236c355073dc Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 16 Aug 2022 14:24:57 +0300 Subject: [PATCH 31/37] Add idle func --- src/core.cc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/core.cc b/src/core.cc index 19d02da..6778d7c 100644 --- a/src/core.cc +++ b/src/core.cc @@ -17,6 +17,8 @@ #include #include +static void idleImpl(); + // 0x51E234 IdleFunc* _idle_func = NULL; @@ -411,6 +413,10 @@ int coreInit(int a1) gTickerListHead = NULL; gScreenshotKeyCode = KEY_ALT_C; + // SFALL: Set idle function. + // CE: Prevents frying CPU when window is not focused. + inputSetIdleFunc(idleImpl); + return 0; } @@ -4907,3 +4913,8 @@ void convertMouseWheelToArrowKey(int* keyCodePtr) } } } + +static void idleImpl() +{ + SDL_Delay(125); +} From c9fa9a4765a89fd5021f38da2dede6b5c2d9cabd Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 16 Aug 2022 16:09:13 +0300 Subject: [PATCH 32/37] Fix inventory defines misuse --- src/inventory.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/inventory.cc b/src/inventory.cc index bd1891d..ceb37b0 100644 --- a/src/inventory.cc +++ b/src/inventory.cc @@ -81,11 +81,11 @@ #define INVENTORY_TRADE_OUTER_SCROLLER_Y 35 #define INVENTORY_TRADE_INNER_SCROLLER_Y 20 -#define INVENTORY_TRADE_LEFT_SCROLLER_TRACKING_X 165 +#define INVENTORY_TRADE_LEFT_SCROLLER_TRACKING_X 0 #define INVENTORY_TRADE_LEFT_SCROLLER_TRACKING_Y 10 #define INVENTORY_TRADE_LEFT_SCROLLER_TRACKING_MAX_X (INVENTORY_TRADE_LEFT_SCROLLER_TRACKING_X + INVENTORY_SLOT_WIDTH) -#define INVENTORY_TRADE_INNER_LEFT_SCROLLER_TRACKING_X 0 +#define INVENTORY_TRADE_INNER_LEFT_SCROLLER_TRACKING_X 165 #define INVENTORY_TRADE_INNER_LEFT_SCROLLER_TRACKING_Y 10 #define INVENTORY_TRADE_INNER_LEFT_SCROLLER_TRACKING_MAX_X (INVENTORY_TRADE_INNER_LEFT_SCROLLER_TRACKING_X + INVENTORY_SLOT_WIDTH) @@ -4246,7 +4246,7 @@ static void _barter_move_inventory(Object* a1, int quantity, int a3, int a4, Obj MessageListItem messageListItem; if (a7) { - if (mouseHitTestInWindow(gInventoryWindow, INVENTORY_TRADE_LEFT_SCROLLER_TRACKING_X, INVENTORY_TRADE_LEFT_SCROLLER_TRACKING_Y, INVENTORY_TRADE_LEFT_SCROLLER_TRACKING_MAX_X, INVENTORY_SLOT_HEIGHT * gInventorySlotsCount + INVENTORY_TRADE_LEFT_SCROLLER_TRACKING_Y)) { + if (mouseHitTestInWindow(gInventoryWindow, INVENTORY_TRADE_INNER_LEFT_SCROLLER_TRACKING_X, INVENTORY_TRADE_INNER_LEFT_SCROLLER_TRACKING_Y, INVENTORY_TRADE_INNER_LEFT_SCROLLER_TRACKING_MAX_X, INVENTORY_SLOT_HEIGHT * gInventorySlotsCount + INVENTORY_TRADE_INNER_LEFT_SCROLLER_TRACKING_Y)) { int quantityToMove = quantity > 1 ? inventoryQuantitySelect(INVENTORY_WINDOW_TYPE_MOVE_ITEMS, a1, quantity) : 1; if (quantityToMove != -1) { if (_item_move_force(_inven_dude, a6, a1, quantityToMove) == -1) { @@ -4329,7 +4329,7 @@ static void _barter_move_from_table_inventory(Object* a1, int quantity, int a3, MessageListItem messageListItem; if (a6) { - if (mouseHitTestInWindow(gInventoryWindow, INVENTORY_TRADE_INNER_LEFT_SCROLLER_TRACKING_X, INVENTORY_TRADE_INNER_LEFT_SCROLLER_TRACKING_Y, INVENTORY_TRADE_INNER_LEFT_SCROLLER_TRACKING_MAX_X, INVENTORY_SLOT_HEIGHT * gInventorySlotsCount + INVENTORY_TRADE_INNER_LEFT_SCROLLER_TRACKING_Y)) { + if (mouseHitTestInWindow(gInventoryWindow, INVENTORY_TRADE_LEFT_SCROLLER_TRACKING_X, INVENTORY_TRADE_LEFT_SCROLLER_TRACKING_Y, INVENTORY_TRADE_LEFT_SCROLLER_TRACKING_MAX_X, INVENTORY_SLOT_HEIGHT * gInventorySlotsCount + INVENTORY_TRADE_LEFT_SCROLLER_TRACKING_Y)) { int quantityToMove = quantity > 1 ? inventoryQuantitySelect(INVENTORY_WINDOW_TYPE_MOVE_ITEMS, a1, quantity) : 1; if (quantityToMove != -1) { if (_item_move_force(a5, _inven_dude, a1, quantityToMove) == -1) { @@ -4683,7 +4683,7 @@ void inventoryOpenTrade(int win, Object* a2, Object* a3, Object* a4, int a5) keyCode = -1; } } else if ((mouseGetEvent() & MOUSE_EVENT_WHEEL) != 0) { - if (mouseHitTestInWindow(gInventoryWindow, INVENTORY_TRADE_INNER_LEFT_SCROLLER_TRACKING_X, INVENTORY_TRADE_INNER_LEFT_SCROLLER_TRACKING_Y, INVENTORY_TRADE_INNER_LEFT_SCROLLER_TRACKING_MAX_X, INVENTORY_SLOT_HEIGHT * gInventorySlotsCount + INVENTORY_TRADE_INNER_LEFT_SCROLLER_TRACKING_Y)) { + if (mouseHitTestInWindow(gInventoryWindow, INVENTORY_TRADE_LEFT_SCROLLER_TRACKING_X, INVENTORY_TRADE_LEFT_SCROLLER_TRACKING_Y, INVENTORY_TRADE_LEFT_SCROLLER_TRACKING_MAX_X, INVENTORY_SLOT_HEIGHT * gInventorySlotsCount + INVENTORY_TRADE_LEFT_SCROLLER_TRACKING_Y)) { int wheelX; int wheelY; mouseGetWheel(&wheelX, &wheelY); @@ -4698,7 +4698,7 @@ void inventoryOpenTrade(int win, Object* a2, Object* a3, Object* a4, int a5) _display_inventory(_stack_offset[_curr_stack], -1, INVENTORY_WINDOW_TYPE_TRADE); } } - } else if (mouseHitTestInWindow(gInventoryWindow, INVENTORY_TRADE_LEFT_SCROLLER_TRACKING_X, INVENTORY_TRADE_LEFT_SCROLLER_TRACKING_Y, INVENTORY_TRADE_LEFT_SCROLLER_TRACKING_MAX_X, INVENTORY_SLOT_HEIGHT * gInventorySlotsCount + INVENTORY_TRADE_LEFT_SCROLLER_TRACKING_Y)) { + } else if (mouseHitTestInWindow(gInventoryWindow, INVENTORY_TRADE_INNER_LEFT_SCROLLER_TRACKING_X, INVENTORY_TRADE_INNER_LEFT_SCROLLER_TRACKING_Y, INVENTORY_TRADE_INNER_LEFT_SCROLLER_TRACKING_MAX_X, INVENTORY_SLOT_HEIGHT * gInventorySlotsCount + INVENTORY_TRADE_INNER_LEFT_SCROLLER_TRACKING_Y)) { int wheelX; int wheelY; mouseGetWheel(&wheelX, &wheelY); From ebba5482068f11cbf5b1f83b3fa227c3d885bc23 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 16 Aug 2022 16:26:21 +0300 Subject: [PATCH 33/37] Use inventory slot size constants --- src/inventory.cc | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/inventory.cc b/src/inventory.cc index ceb37b0..5c46305 100644 --- a/src/inventory.cc +++ b/src/inventory.cc @@ -1236,7 +1236,7 @@ static void _display_inventory(int a1, int a2, int inventoryWindowType) unsigned char* backgroundFrmData = artLockFrameData(backgroundFid, 0, 0, &backgroundFrmHandle); if (backgroundFrmData != NULL) { // Clear scroll view background. - blitBufferToBuffer(backgroundFrmData + pitch * 35 + 44, 64, gInventorySlotsCount * 48, pitch, windowBuffer + pitch * 35 + 44, pitch); + blitBufferToBuffer(backgroundFrmData + pitch * 35 + 44, INVENTORY_SLOT_WIDTH, gInventorySlotsCount * INVENTORY_SLOT_HEIGHT, pitch, windowBuffer + pitch * 35 + 44, pitch); artUnlock(backgroundFrmHandle); } } else if (inventoryWindowType == INVENTORY_WINDOW_TYPE_LOOT) { @@ -1248,7 +1248,7 @@ static void _display_inventory(int a1, int a2, int inventoryWindowType) unsigned char* backgroundFrmData = artLockFrameData(backgroundFid, 0, 0, &backgroundFrmHandle); if (backgroundFrmData != NULL) { // Clear scroll view background. - blitBufferToBuffer(backgroundFrmData + pitch * 37 + 176, 64, gInventorySlotsCount * 48, pitch, windowBuffer + pitch * 37 + 176, pitch); + blitBufferToBuffer(backgroundFrmData + pitch * 37 + 176, INVENTORY_SLOT_WIDTH, gInventorySlotsCount * INVENTORY_SLOT_HEIGHT, pitch, windowBuffer + pitch * 37 + 176, pitch); artUnlock(backgroundFrmHandle); } } else if (inventoryWindowType == INVENTORY_WINDOW_TYPE_TRADE) { @@ -1256,7 +1256,7 @@ static void _display_inventory(int a1, int a2, int inventoryWindowType) windowBuffer = windowGetBuffer(gInventoryWindow); - blitBufferToBuffer(windowGetBuffer(_barter_back_win) + 35 * 640 + 100, 64, 48 * gInventorySlotsCount, 640, windowBuffer + pitch * 35 + 20, pitch); + blitBufferToBuffer(windowGetBuffer(_barter_back_win) + 35 * 640 + 100, INVENTORY_SLOT_WIDTH, INVENTORY_SLOT_HEIGHT * gInventorySlotsCount, 640, windowBuffer + pitch * 35 + 20, pitch); v49 = -20; } else { assert(false && "Should be unreachable"); @@ -1313,7 +1313,7 @@ static void _display_inventory(int a1, int a2, int inventoryWindowType) _display_inventory_info(inventoryItem->item, inventoryItem->quantity, windowBuffer + offset, pitch, v19 == a2); - y += 48; + y += INVENTORY_SLOT_HEIGHT; } if (inventoryWindowType == INVENTORY_WINDOW_TYPE_NORMAL) { @@ -1356,14 +1356,14 @@ static void _display_target_inventory(int a1, int a2, Inventory* inventory, int CacheEntry* handle; unsigned char* data = artLockFrameData(fid, 0, 0, &handle); if (data != NULL) { - blitBufferToBuffer(data + 537 * 37 + 297, 64, 48 * gInventorySlotsCount, 537, windowBuffer + 537 * 37 + 297, 537); + blitBufferToBuffer(data + 537 * 37 + 297, INVENTORY_SLOT_WIDTH, INVENTORY_SLOT_HEIGHT * gInventorySlotsCount, 537, windowBuffer + 537 * 37 + 297, 537); artUnlock(handle); } } else if (inventoryWindowType == INVENTORY_WINDOW_TYPE_TRADE) { pitch = 480; unsigned char* src = windowGetBuffer(_barter_back_win); - blitBufferToBuffer(src + 640 * 35 + 475, 64, 48 * gInventorySlotsCount, 640, windowBuffer + 480 * 35 + 395, 480); + blitBufferToBuffer(src + 640 * 35 + 475, INVENTORY_SLOT_WIDTH, INVENTORY_SLOT_HEIGHT * gInventorySlotsCount, 640, windowBuffer + 480 * 35 + 395, 480); } else { assert(false && "Should be unreachable"); } @@ -1389,7 +1389,7 @@ static void _display_target_inventory(int a1, int a2, Inventory* inventory, int artRender(inventoryFid, windowBuffer + offset, 56, 40, pitch); _display_inventory_info(inventoryItem->item, inventoryItem->quantity, windowBuffer + offset, pitch, index == a2); - y += 48; + y += INVENTORY_SLOT_HEIGHT; } if (inventoryWindowType == INVENTORY_WINDOW_TYPE_LOOT) { @@ -1792,7 +1792,7 @@ static void _inven_pickup(int keyCode, int a2) // is only for key codes below 1006. v3 = keyCode - 1000; rect.left = 44; - rect.top = 48 * v3 + 35; + rect.top = INVENTORY_SLOT_HEIGHT * v3 + 35; break; } @@ -1868,7 +1868,7 @@ static void _inven_pickup(int keyCode, int a2) int y; mouseGetPositionInWindow(gInventoryWindow, &x, &y); - int v18 = (y - 39) / 48 + a2; + int v18 = (y - 39) / INVENTORY_SLOT_HEIGHT + a2; if (v18 < _pud->length) { Object* v19 = _pud->items[v18].item; if (v19 != a1a) { @@ -4199,10 +4199,10 @@ static void _barter_move_inventory(Object* a1, int quantity, int a3, int a4, Obj Rect rect; if (a7) { rect.left = 23; - rect.top = 48 * a3 + 34; + rect.top = INVENTORY_SLOT_HEIGHT * a3 + 34; } else { rect.left = 395; - rect.top = 48 * a3 + 31; + rect.top = INVENTORY_SLOT_HEIGHT * a3 + 31; } if (quantity > 1) { @@ -4282,10 +4282,10 @@ static void _barter_move_from_table_inventory(Object* a1, int quantity, int a3, Rect rect; if (a6) { rect.left = 169; - rect.top = 48 * a3 + 24; + rect.top = INVENTORY_SLOT_HEIGHT * a3 + 24; } else { rect.left = 254; - rect.top = 48 * a3 + 24; + rect.top = INVENTORY_SLOT_HEIGHT * a3 + 24; } if (quantity > 1) { @@ -4368,11 +4368,11 @@ static void inventoryWindowRenderInnerInventories(int win, Object* a2, Object* a fontSetCurrent(101); char formattedText[80]; - int v45 = fontGetLineHeight() + 48 * gInventorySlotsCount; + int v45 = fontGetLineHeight() + INVENTORY_SLOT_HEIGHT * gInventorySlotsCount; if (a2 != NULL) { unsigned char* src = windowGetBuffer(win); - blitBufferToBuffer(src + 640 * 20 + 249, 64, v45 + 1, 640, windowBuffer + 480 * 20 + 169, 480); + blitBufferToBuffer(src + 640 * 20 + 249, INVENTORY_SLOT_WIDTH, v45 + 1, 640, windowBuffer + 480 * 20 + 169, 480); unsigned char* dest = windowBuffer + 480 * 24 + 169; Inventory* inventory = &(a2->data.inventory); @@ -4382,7 +4382,7 @@ static void inventoryWindowRenderInnerInventories(int win, Object* a2, Object* a artRender(inventoryFid, dest, 56, 40, 480); _display_inventory_info(inventoryItem->item, inventoryItem->quantity, dest, 480, index == a4); - dest += 480 * 48; + dest += 480 * INVENTORY_SLOT_HEIGHT; } if (gGameDialogSpeakerIsPartyMember) { @@ -4398,7 +4398,7 @@ static void inventoryWindowRenderInnerInventories(int win, Object* a2, Object* a sprintf(formattedText, "$%d", cost); } - fontDrawText(windowBuffer + 480 * (48 * gInventorySlotsCount + 24) + 169, formattedText, 80, 480, _colorTable[32767]); + fontDrawText(windowBuffer + 480 * (INVENTORY_SLOT_HEIGHT * gInventorySlotsCount + 24) + 169, formattedText, 80, 480, _colorTable[32767]); Rect rect; rect.left = 169; @@ -4410,7 +4410,7 @@ static void inventoryWindowRenderInnerInventories(int win, Object* a2, Object* a if (a3 != NULL) { unsigned char* src = windowGetBuffer(win); - blitBufferToBuffer(src + 640 * 20 + 334, 64, v45 + 1, 640, windowBuffer + 480 * 20 + 254, 480); + blitBufferToBuffer(src + 640 * 20 + 334, INVENTORY_SLOT_WIDTH, v45 + 1, 640, windowBuffer + 480 * 20 + 254, 480); unsigned char* dest = windowBuffer + 480 * 24 + 254; Inventory* inventory = &(a3->data.inventory); @@ -4420,7 +4420,7 @@ static void inventoryWindowRenderInnerInventories(int win, Object* a2, Object* a artRender(inventoryFid, dest, 56, 40, 480); _display_inventory_info(inventoryItem->item, inventoryItem->quantity, dest, 480, index == a4); - dest += 480 * 48; + dest += 480 * INVENTORY_SLOT_HEIGHT; } if (gGameDialogSpeakerIsPartyMember) { @@ -4436,7 +4436,7 @@ static void inventoryWindowRenderInnerInventories(int win, Object* a2, Object* a sprintf(formattedText, "$%d", cost); } - fontDrawText(windowBuffer + 480 * (48 * gInventorySlotsCount + 24) + 254, formattedText, 80, 480, _colorTable[32767]); + fontDrawText(windowBuffer + 480 * (INVENTORY_SLOT_HEIGHT * gInventorySlotsCount + 24) + 254, formattedText, 80, 480, _colorTable[32767]); Rect rect; rect.left = 254; From 16f0be1a4553826fb6cb35d1a1bbefee485e375f Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 16 Aug 2022 21:08:04 +0300 Subject: [PATCH 34/37] Add more inventory UI constants --- src/inventory.cc | 276 ++++++++++++++++++++++++++++------------------- 1 file changed, 163 insertions(+), 113 deletions(-) diff --git a/src/inventory.cc b/src/inventory.cc index 5c46305..efa47ef 100644 --- a/src/inventory.cc +++ b/src/inventory.cc @@ -72,14 +72,20 @@ #define INVENTORY_ARMOR_SLOT_MAX_X (INVENTORY_ARMOR_SLOT_X + INVENTORY_LARGE_SLOT_WIDTH) #define INVENTORY_ARMOR_SLOT_MAX_Y (INVENTORY_ARMOR_SLOT_Y + INVENTORY_LARGE_SLOT_HEIGHT) +#define INVENTORY_TRADE_SCROLLER_Y 35 +#define INVENTORY_TRADE_INNER_SCROLLER_Y 20 + #define INVENTORY_TRADE_LEFT_SCROLLER_X 29 +#define INVENTORY_TRADE_LEFT_SCROLLER_Y INVENTORY_TRADE_SCROLLER_Y + #define INVENTORY_TRADE_RIGHT_SCROLLER_X 395 +#define INVENTORY_TRADE_RIGHT_SCROLLER_Y INVENTORY_TRADE_SCROLLER_Y #define INVENTORY_TRADE_INNER_LEFT_SCROLLER_X 165 -#define INVENTORY_TRADE_INNER_RIGHT_SCROLLER_X 250 +#define INVENTORY_TRADE_INNER_LEFT_SCROLLER_Y INVENTORY_TRADE_INNER_SCROLLER_Y -#define INVENTORY_TRADE_OUTER_SCROLLER_Y 35 -#define INVENTORY_TRADE_INNER_SCROLLER_Y 20 +#define INVENTORY_TRADE_INNER_RIGHT_SCROLLER_X 250 +#define INVENTORY_TRADE_INNER_RIGHT_SCROLLER_Y INVENTORY_TRADE_INNER_SCROLLER_Y #define INVENTORY_TRADE_LEFT_SCROLLER_TRACKING_X 0 #define INVENTORY_TRADE_LEFT_SCROLLER_TRACKING_Y 10 @@ -123,6 +129,48 @@ #define INVENTORY_LOOT_LEFT_BODY_VIEW_X 44 #define INVENTORY_LOOT_LEFT_BODY_VIEW_Y 35 +#define INVENTORY_SUMMARY_X 297 +#define INVENTORY_SUMMARY_Y 44 +#define INVENTORY_SUMMARY_MAX_X 440 + +#define INVENTORY_WINDOW_WIDTH 499 +#define INVENTORY_USE_ON_WINDOW_WIDTH 292 +#define INVENTORY_LOOT_WINDOW_WIDTH 537 +#define INVENTORY_TRADE_WINDOW_WIDTH 480 +#define INVENTORY_TIMER_WINDOW_WIDTH 259 + +#define INVENTORY_TRADE_BACKGROUND_WINDOW_WIDTH 640 +#define INVENTORY_TRADE_BACKGROUND_WINDOW_HEIGHT 480 +#define INVENTORY_TRADE_WINDOW_OFFSET ((INVENTORY_TRADE_BACKGROUND_WINDOW_WIDTH - INVENTORY_TRADE_WINDOW_WIDTH) / 2) + +#define INVENTORY_SLOT_PADDING 4 + +#define INVENTORY_SCROLLER_X_PAD (INVENTORY_SCROLLER_X + INVENTORY_SLOT_PADDING) +#define INVENTORY_SCROLLER_Y_PAD (INVENTORY_SCROLLER_Y + INVENTORY_SLOT_PADDING) + +#define INVENTORY_LOOT_LEFT_SCROLLER_X_PAD (INVENTORY_LOOT_LEFT_SCROLLER_X + INVENTORY_SLOT_PADDING) +#define INVENTORY_LOOT_LEFT_SCROLLER_Y_PAD (INVENTORY_LOOT_LEFT_SCROLLER_Y + INVENTORY_SLOT_PADDING) + +#define INVENTORY_LOOT_RIGHT_SCROLLER_X_PAD (INVENTORY_LOOT_RIGHT_SCROLLER_X + INVENTORY_SLOT_PADDING) +#define INVENTORY_LOOT_RIGHT_SCROLLER_Y_PAD (INVENTORY_LOOT_RIGHT_SCROLLER_Y + INVENTORY_SLOT_PADDING) + +#define INVENTORY_TRADE_LEFT_SCROLLER_X_PAD 26 +#define INVENTORY_TRADE_LEFT_SCROLLER_Y_PAD (INVENTORY_TRADE_LEFT_SCROLLER_Y + INVENTORY_SLOT_PADDING) + +#define INVENTORY_TRADE_LEFT_SCROLLER_X_AUX 20 + +#define INVENTORY_TRADE_RIGHT_SCROLLER_X_PAD (INVENTORY_TRADE_RIGHT_SCROLLER_X + 2) +#define INVENTORY_TRADE_RIGHT_SCROLLER_Y_PAD (INVENTORY_TRADE_RIGHT_SCROLLER_Y + INVENTORY_SLOT_PADDING) + +#define INVENTORY_TRADE_INNER_LEFT_SCROLLER_X_PAD (INVENTORY_TRADE_INNER_LEFT_SCROLLER_X + INVENTORY_SLOT_PADDING) +#define INVENTORY_TRADE_INNER_LEFT_SCROLLER_Y_PAD (INVENTORY_TRADE_INNER_LEFT_SCROLLER_Y + INVENTORY_SLOT_PADDING) + +#define INVENTORY_TRADE_INNER_RIGHT_SCROLLER_X_PAD (INVENTORY_TRADE_INNER_RIGHT_SCROLLER_X + INVENTORY_SLOT_PADDING) +#define INVENTORY_TRADE_INNER_RIGHT_SCROLLER_Y_PAD (INVENTORY_TRADE_INNER_RIGHT_SCROLLER_Y + INVENTORY_SLOT_PADDING) + +#define INVENTORY_SLOT_WIDTH_PAD (INVENTORY_SLOT_WIDTH - INVENTORY_SLOT_PADDING * 2) +#define INVENTORY_SLOT_HEIGHT_PAD (INVENTORY_SLOT_HEIGHT - INVENTORY_SLOT_PADDING * 2) + #define INVENTORY_NORMAL_WINDOW_PC_ROTATION_DELAY (1000U / ROTATION_COUNT) #define OFF_59E7BC_COUNT 12 @@ -275,12 +323,12 @@ static int _inven_display_msg_line = 1; // 0x519068 static const InventoryWindowDescription gInventoryWindowDescriptions[INVENTORY_WINDOW_TYPE_COUNT] = { - { 48, 499, 377, 80, 0 }, - { 113, 292, 376, 80, 0 }, - { 114, 537, 376, 80, 0 }, - { 111, 480, 180, 80, 290 }, - { 305, 259, 162, 140, 80 }, - { 305, 259, 162, 140, 80 }, + { 48, INVENTORY_WINDOW_WIDTH, 377, 80, 0 }, + { 113, INVENTORY_USE_ON_WINDOW_WIDTH, 376, 80, 0 }, + { 114, INVENTORY_LOOT_WINDOW_WIDTH, 376, 80, 0 }, + { 111, INVENTORY_TRADE_WINDOW_WIDTH, 180, 80, 290 }, + { 305, INVENTORY_TIMER_WINDOW_WIDTH, 162, 140, 80 }, + { 305, INVENTORY_TIMER_WINDOW_WIDTH, 162, 140, 80 }, }; // 0x5190E0 @@ -711,15 +759,15 @@ static bool _setup_inventory(int inventoryWindowType) gInventorySlotsCount = 3; // Trade inventory window is a part of game dialog, which is 640x480. - int tradeWindowX = (screenGetWidth() - 640) / 2 + INVENTORY_TRADE_WINDOW_X; - int tradeWindowY = (screenGetHeight() - 480) / 2 + INVENTORY_TRADE_WINDOW_Y; + int tradeWindowX = (screenGetWidth() - INVENTORY_TRADE_BACKGROUND_WINDOW_WIDTH) / 2 + INVENTORY_TRADE_WINDOW_X; + int tradeWindowY = (screenGetHeight() - INVENTORY_TRADE_BACKGROUND_WINDOW_HEIGHT) / 2 + INVENTORY_TRADE_WINDOW_Y; gInventoryWindow = windowCreate(tradeWindowX, tradeWindowY, INVENTORY_TRADE_WINDOW_WIDTH, INVENTORY_TRADE_WINDOW_HEIGHT, 257, 0); gInventoryWindowMaxX = tradeWindowX + INVENTORY_TRADE_WINDOW_WIDTH; gInventoryWindowMaxY = tradeWindowY + INVENTORY_TRADE_WINDOW_HEIGHT; unsigned char* dest = windowGetBuffer(gInventoryWindow); unsigned char* src = windowGetBuffer(_barter_back_win); - blitBufferToBuffer(src + INVENTORY_TRADE_WINDOW_X, INVENTORY_TRADE_WINDOW_WIDTH, INVENTORY_TRADE_WINDOW_HEIGHT, 640, dest, INVENTORY_TRADE_WINDOW_WIDTH); + blitBufferToBuffer(src + INVENTORY_TRADE_WINDOW_X, INVENTORY_TRADE_WINDOW_WIDTH, INVENTORY_TRADE_WINDOW_HEIGHT, INVENTORY_TRADE_BACKGROUND_WINDOW_WIDTH, dest, INVENTORY_TRADE_WINDOW_WIDTH); gInventoryPrintItemDescriptionHandler = gameDialogRenderSupplementaryMessage; } @@ -754,7 +802,7 @@ static bool _setup_inventory(int inventoryWindowType) y -= INVENTORY_SLOT_HEIGHT; } } else if (inventoryWindowType == INVENTORY_WINDOW_TYPE_TRADE) { - int y1 = INVENTORY_TRADE_OUTER_SCROLLER_Y; + int y1 = INVENTORY_TRADE_SCROLLER_Y; int y2 = INVENTORY_TRADE_INNER_SCROLLER_Y; for (int index = 0; index < gInventorySlotsCount; index++) { @@ -1194,7 +1242,7 @@ static void _display_inventory(int a1, int a2, int inventoryWindowType) int v49 = 0; if (inventoryWindowType == INVENTORY_WINDOW_TYPE_NORMAL) { - pitch = 499; + pitch = INVENTORY_WINDOW_WIDTH; int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 48, 0, 0, 0); @@ -1202,7 +1250,7 @@ static void _display_inventory(int a1, int a2, int inventoryWindowType) unsigned char* backgroundFrmData = artLockFrameData(backgroundFid, 0, 0, &backgroundFrmHandle); if (backgroundFrmData != NULL) { // Clear scroll view background. - blitBufferToBuffer(backgroundFrmData + pitch * 35 + 44, INVENTORY_SLOT_WIDTH, gInventorySlotsCount * INVENTORY_SLOT_HEIGHT, pitch, windowBuffer + pitch * 35 + 44, pitch); + blitBufferToBuffer(backgroundFrmData + pitch * INVENTORY_SCROLLER_Y + INVENTORY_SCROLLER_X, INVENTORY_SLOT_WIDTH, gInventorySlotsCount * INVENTORY_SLOT_HEIGHT, pitch, windowBuffer + pitch * INVENTORY_SCROLLER_Y + INVENTORY_SCROLLER_X, pitch); // Clear armor button background. blitBufferToBuffer(backgroundFrmData + pitch * INVENTORY_ARMOR_SLOT_Y + INVENTORY_ARMOR_SLOT_X, INVENTORY_LARGE_SLOT_WIDTH, INVENTORY_LARGE_SLOT_HEIGHT, pitch, windowBuffer + pitch * INVENTORY_ARMOR_SLOT_Y + INVENTORY_ARMOR_SLOT_X, pitch); @@ -1228,7 +1276,7 @@ static void _display_inventory(int a1, int a2, int inventoryWindowType) artUnlock(backgroundFrmHandle); } } else if (inventoryWindowType == INVENTORY_WINDOW_TYPE_USE_ITEM_ON) { - pitch = 292; + pitch = INVENTORY_USE_ON_WINDOW_WIDTH; int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 113, 0, 0, 0); @@ -1236,11 +1284,11 @@ static void _display_inventory(int a1, int a2, int inventoryWindowType) unsigned char* backgroundFrmData = artLockFrameData(backgroundFid, 0, 0, &backgroundFrmHandle); if (backgroundFrmData != NULL) { // Clear scroll view background. - blitBufferToBuffer(backgroundFrmData + pitch * 35 + 44, INVENTORY_SLOT_WIDTH, gInventorySlotsCount * INVENTORY_SLOT_HEIGHT, pitch, windowBuffer + pitch * 35 + 44, pitch); + blitBufferToBuffer(backgroundFrmData + pitch * INVENTORY_SCROLLER_Y + INVENTORY_SCROLLER_X, INVENTORY_SLOT_WIDTH, gInventorySlotsCount * INVENTORY_SLOT_HEIGHT, pitch, windowBuffer + pitch * INVENTORY_SCROLLER_Y + INVENTORY_SCROLLER_X, pitch); artUnlock(backgroundFrmHandle); } } else if (inventoryWindowType == INVENTORY_WINDOW_TYPE_LOOT) { - pitch = 537; + pitch = INVENTORY_LOOT_WINDOW_WIDTH; int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 114, 0, 0, 0); @@ -1248,16 +1296,16 @@ static void _display_inventory(int a1, int a2, int inventoryWindowType) unsigned char* backgroundFrmData = artLockFrameData(backgroundFid, 0, 0, &backgroundFrmHandle); if (backgroundFrmData != NULL) { // Clear scroll view background. - blitBufferToBuffer(backgroundFrmData + pitch * 37 + 176, INVENTORY_SLOT_WIDTH, gInventorySlotsCount * INVENTORY_SLOT_HEIGHT, pitch, windowBuffer + pitch * 37 + 176, pitch); + blitBufferToBuffer(backgroundFrmData + pitch * INVENTORY_LOOT_LEFT_SCROLLER_Y + INVENTORY_LOOT_LEFT_SCROLLER_X, INVENTORY_SLOT_WIDTH, gInventorySlotsCount * INVENTORY_SLOT_HEIGHT, pitch, windowBuffer + pitch * INVENTORY_LOOT_LEFT_SCROLLER_Y + INVENTORY_LOOT_LEFT_SCROLLER_X, pitch); artUnlock(backgroundFrmHandle); } } else if (inventoryWindowType == INVENTORY_WINDOW_TYPE_TRADE) { - pitch = 480; + pitch = INVENTORY_TRADE_WINDOW_WIDTH; windowBuffer = windowGetBuffer(gInventoryWindow); - blitBufferToBuffer(windowGetBuffer(_barter_back_win) + 35 * 640 + 100, INVENTORY_SLOT_WIDTH, INVENTORY_SLOT_HEIGHT * gInventorySlotsCount, 640, windowBuffer + pitch * 35 + 20, pitch); - v49 = -20; + blitBufferToBuffer(windowGetBuffer(_barter_back_win) + INVENTORY_TRADE_LEFT_SCROLLER_Y * INVENTORY_TRADE_BACKGROUND_WINDOW_WIDTH + INVENTORY_TRADE_LEFT_SCROLLER_X_AUX + INVENTORY_TRADE_WINDOW_OFFSET, INVENTORY_SLOT_WIDTH, INVENTORY_SLOT_HEIGHT * gInventorySlotsCount, INVENTORY_TRADE_BACKGROUND_WINDOW_WIDTH, windowBuffer + pitch * INVENTORY_TRADE_LEFT_SCROLLER_Y + INVENTORY_TRADE_LEFT_SCROLLER_X_AUX, pitch); + v49 = -INVENTORY_TRADE_LEFT_SCROLLER_X_AUX; } else { assert(false && "Should be unreachable"); } @@ -1289,26 +1337,26 @@ static void _display_inventory(int a1, int a2, int inventoryWindowType) int width; int offset; if (inventoryWindowType == INVENTORY_WINDOW_TYPE_TRADE) { - offset = pitch * (y + 39) + 26; + offset = pitch * (y + INVENTORY_TRADE_LEFT_SCROLLER_Y_PAD) + INVENTORY_TRADE_LEFT_SCROLLER_X_PAD; width = 59; } else { if (inventoryWindowType == INVENTORY_WINDOW_TYPE_LOOT) { - offset = pitch * (y + 41) + 180; + offset = pitch * (y + INVENTORY_LOOT_LEFT_SCROLLER_Y_PAD) + INVENTORY_LOOT_LEFT_SCROLLER_X_PAD; } else { - offset = pitch * (y + 39) + 48; + offset = pitch * (y + INVENTORY_SCROLLER_Y_PAD) + INVENTORY_SCROLLER_X_PAD; } - width = 56; + width = INVENTORY_SLOT_WIDTH_PAD; } InventoryItem* inventoryItem = &(_pud->items[_pud->length - v21]); int inventoryFid = itemGetInventoryFid(inventoryItem->item); - artRender(inventoryFid, windowBuffer + offset, width, 40, pitch); + artRender(inventoryFid, windowBuffer + offset, width, INVENTORY_SLOT_HEIGHT_PAD, pitch); if (inventoryWindowType == INVENTORY_WINDOW_TYPE_LOOT) { - offset = pitch * (y + 41) + 180 + v49; + offset = pitch * (y + INVENTORY_LOOT_LEFT_SCROLLER_Y_PAD) + INVENTORY_LOOT_LEFT_SCROLLER_X_PAD + v49; } else { - offset = pitch * (y + 39) + 48 + v49; + offset = pitch * (y + INVENTORY_SCROLLER_Y_PAD) + INVENTORY_SCROLLER_X_PAD + v49; } _display_inventory_info(inventoryItem->item, inventoryItem->quantity, windowBuffer + offset, pitch, v19 == a2); @@ -1320,17 +1368,17 @@ static void _display_inventory(int a1, int a2, int inventoryWindowType) if (gInventoryRightHandItem != NULL) { int width = gInventoryRightHandItem == gInventoryLeftHandItem ? INVENTORY_LARGE_SLOT_WIDTH * 2 : INVENTORY_LARGE_SLOT_WIDTH; int inventoryFid = itemGetInventoryFid(gInventoryRightHandItem); - artRender(inventoryFid, windowBuffer + 499 * INVENTORY_RIGHT_HAND_SLOT_Y + INVENTORY_RIGHT_HAND_SLOT_X, width, INVENTORY_LARGE_SLOT_HEIGHT, 499); + artRender(inventoryFid, windowBuffer + INVENTORY_WINDOW_WIDTH * INVENTORY_RIGHT_HAND_SLOT_Y + INVENTORY_RIGHT_HAND_SLOT_X, width, INVENTORY_LARGE_SLOT_HEIGHT, INVENTORY_WINDOW_WIDTH); } if (gInventoryLeftHandItem != NULL && gInventoryLeftHandItem != gInventoryRightHandItem) { int inventoryFid = itemGetInventoryFid(gInventoryLeftHandItem); - artRender(inventoryFid, windowBuffer + 499 * INVENTORY_LEFT_HAND_SLOT_Y + INVENTORY_LEFT_HAND_SLOT_X, INVENTORY_LARGE_SLOT_WIDTH, INVENTORY_LARGE_SLOT_HEIGHT, 499); + artRender(inventoryFid, windowBuffer + INVENTORY_WINDOW_WIDTH * INVENTORY_LEFT_HAND_SLOT_Y + INVENTORY_LEFT_HAND_SLOT_X, INVENTORY_LARGE_SLOT_WIDTH, INVENTORY_LARGE_SLOT_HEIGHT, INVENTORY_WINDOW_WIDTH); } if (gInventoryArmor != NULL) { int inventoryFid = itemGetInventoryFid(gInventoryArmor); - artRender(inventoryFid, windowBuffer + 499 * INVENTORY_ARMOR_SLOT_Y + INVENTORY_ARMOR_SLOT_X, INVENTORY_LARGE_SLOT_WIDTH, INVENTORY_LARGE_SLOT_HEIGHT, 499); + artRender(inventoryFid, windowBuffer + INVENTORY_WINDOW_WIDTH * INVENTORY_ARMOR_SLOT_Y + INVENTORY_ARMOR_SLOT_X, INVENTORY_LARGE_SLOT_WIDTH, INVENTORY_LARGE_SLOT_HEIGHT, INVENTORY_WINDOW_WIDTH); } } @@ -1349,21 +1397,21 @@ static void _display_target_inventory(int a1, int a2, Inventory* inventory, int int pitch; if (inventoryWindowType == INVENTORY_WINDOW_TYPE_LOOT) { - pitch = 537; + pitch = INVENTORY_LOOT_WINDOW_WIDTH; int fid = buildFid(OBJ_TYPE_INTERFACE, 114, 0, 0, 0); CacheEntry* handle; unsigned char* data = artLockFrameData(fid, 0, 0, &handle); if (data != NULL) { - blitBufferToBuffer(data + 537 * 37 + 297, INVENTORY_SLOT_WIDTH, INVENTORY_SLOT_HEIGHT * gInventorySlotsCount, 537, windowBuffer + 537 * 37 + 297, 537); + blitBufferToBuffer(data + pitch * INVENTORY_LOOT_RIGHT_SCROLLER_Y + INVENTORY_LOOT_RIGHT_SCROLLER_X, INVENTORY_SLOT_WIDTH, INVENTORY_SLOT_HEIGHT * gInventorySlotsCount, pitch, windowBuffer + pitch * INVENTORY_LOOT_RIGHT_SCROLLER_Y + INVENTORY_LOOT_RIGHT_SCROLLER_X, pitch); artUnlock(handle); } } else if (inventoryWindowType == INVENTORY_WINDOW_TYPE_TRADE) { - pitch = 480; + pitch = INVENTORY_TRADE_WINDOW_WIDTH; unsigned char* src = windowGetBuffer(_barter_back_win); - blitBufferToBuffer(src + 640 * 35 + 475, INVENTORY_SLOT_WIDTH, INVENTORY_SLOT_HEIGHT * gInventorySlotsCount, 640, windowBuffer + 480 * 35 + 395, 480); + blitBufferToBuffer(src + INVENTORY_TRADE_BACKGROUND_WINDOW_WIDTH * INVENTORY_TRADE_RIGHT_SCROLLER_Y + INVENTORY_TRADE_RIGHT_SCROLLER_X + INVENTORY_TRADE_WINDOW_OFFSET, INVENTORY_SLOT_WIDTH, INVENTORY_SLOT_HEIGHT * gInventorySlotsCount, INVENTORY_TRADE_BACKGROUND_WINDOW_WIDTH, windowBuffer + INVENTORY_TRADE_WINDOW_WIDTH * INVENTORY_TRADE_RIGHT_SCROLLER_Y + INVENTORY_TRADE_RIGHT_SCROLLER_X, INVENTORY_TRADE_WINDOW_WIDTH); } else { assert(false && "Should be unreachable"); } @@ -1377,16 +1425,16 @@ static void _display_target_inventory(int a1, int a2, Inventory* inventory, int int offset; if (inventoryWindowType == INVENTORY_WINDOW_TYPE_LOOT) { - offset = pitch * (y + 41) + 301; + offset = pitch * (y + INVENTORY_LOOT_RIGHT_SCROLLER_Y_PAD) + INVENTORY_LOOT_RIGHT_SCROLLER_X_PAD; } else if (inventoryWindowType == INVENTORY_WINDOW_TYPE_TRADE) { - offset = pitch * (y + 39) + 397; + offset = pitch * (y + INVENTORY_TRADE_RIGHT_SCROLLER_Y_PAD) + INVENTORY_TRADE_RIGHT_SCROLLER_X_PAD; } else { assert(false && "Should be unreachable"); } InventoryItem* inventoryItem = &(inventory->items[inventory->length - (v27 + 1)]); int inventoryFid = itemGetInventoryFid(inventoryItem->item); - artRender(inventoryFid, windowBuffer + offset, 56, 40, pitch); + artRender(inventoryFid, windowBuffer + offset, INVENTORY_SLOT_WIDTH_PAD, INVENTORY_SLOT_HEIGHT_PAD, pitch); _display_inventory_info(inventoryItem->item, inventoryItem->quantity, windowBuffer + offset, pitch, index == a2); y += INVENTORY_SLOT_HEIGHT; @@ -1584,10 +1632,10 @@ static void _display_body(int fid, int inventoryWindowType) int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 114, 0, 0, 0); unsigned char* src = artLockFrameData(backgroundFid, 0, 0, &backrgroundFrmHandle); if (src != NULL) { - blitBufferToBuffer(src + 537 * rect.top + rect.left, + blitBufferToBuffer(src + INVENTORY_LOOT_WINDOW_WIDTH * rect.top + rect.left, INVENTORY_BODY_VIEW_WIDTH, INVENTORY_BODY_VIEW_HEIGHT, - 537, + INVENTORY_LOOT_WINDOW_WIDTH, windowBuffer + windowPitch * rect.top + rect.left, windowPitch); } @@ -1791,8 +1839,8 @@ static void _inven_pickup(int keyCode, int a2) // NOTE: Original code a little bit different, this code path // is only for key codes below 1006. v3 = keyCode - 1000; - rect.left = 44; - rect.top = INVENTORY_SLOT_HEIGHT * v3 + 35; + rect.left = INVENTORY_SCROLLER_X; + rect.top = INVENTORY_SLOT_HEIGHT * v3 + INVENTORY_SCROLLER_Y; break; } @@ -1813,7 +1861,7 @@ static void _inven_pickup(int keyCode, int a2) int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 48, 0, 0, 0); unsigned char* backgroundFrmData = artLockFrameData(backgroundFid, 0, 0, &backgroundFrmHandle); if (backgroundFrmData != NULL) { - blitBufferToBuffer(backgroundFrmData + 499 * rect.top + rect.left, width, height, 499, windowBuffer + 499 * rect.top + rect.left, 499); + blitBufferToBuffer(backgroundFrmData + INVENTORY_WINDOW_WIDTH * rect.top + rect.left, width, height, INVENTORY_WINDOW_WIDTH, windowBuffer + INVENTORY_WINDOW_WIDTH * rect.top + rect.left, INVENTORY_WINDOW_WIDTH); artUnlock(backgroundFrmHandle); } @@ -1824,7 +1872,7 @@ static void _inven_pickup(int keyCode, int a2) int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 48, 0, 0, 0); unsigned char* backgroundFrmData = artLockFrameData(backgroundFid, 0, 0, &backgroundFrmHandle); if (backgroundFrmData != NULL) { - blitBufferToBuffer(backgroundFrmData + 499 * 286 + 154, 180, 61, 499, windowBuffer + 499 * 286 + 154, 499); + blitBufferToBuffer(backgroundFrmData + INVENTORY_WINDOW_WIDTH * 286 + 154, 180, 61, INVENTORY_WINDOW_WIDTH, windowBuffer + INVENTORY_WINDOW_WIDTH * 286 + 154, INVENTORY_WINDOW_WIDTH); artUnlock(backgroundFrmHandle); } @@ -2365,44 +2413,44 @@ static void inventoryRenderSummary() CacheEntry* backgroundHandle; unsigned char* backgroundData = artLockFrameData(fid, 0, 0, &backgroundHandle); if (backgroundData != NULL) { - blitBufferToBuffer(backgroundData + 499 * 44 + 297, 152, 188, 499, windowBuffer + 499 * 44 + 297, 499); + blitBufferToBuffer(backgroundData + INVENTORY_WINDOW_WIDTH * INVENTORY_SUMMARY_Y + INVENTORY_SUMMARY_X, 152, 188, INVENTORY_WINDOW_WIDTH, windowBuffer + INVENTORY_WINDOW_WIDTH * INVENTORY_SUMMARY_Y + INVENTORY_SUMMARY_X, INVENTORY_WINDOW_WIDTH); } artUnlock(backgroundHandle); // Render character name. const char* critterName = critterGetName(_stack[0]); - fontDrawText(windowBuffer + 499 * 44 + 297, critterName, 80, 499, _colorTable[992]); + fontDrawText(windowBuffer + INVENTORY_WINDOW_WIDTH * INVENTORY_SUMMARY_Y + INVENTORY_SUMMARY_X, critterName, 80, INVENTORY_WINDOW_WIDTH, _colorTable[992]); bufferDrawLine(windowBuffer, - 499, - 297, - 3 * fontGetLineHeight() / 2 + 44, - 440, - 3 * fontGetLineHeight() / 2 + 44, + INVENTORY_WINDOW_WIDTH, + INVENTORY_SUMMARY_X, + 3 * fontGetLineHeight() / 2 + INVENTORY_SUMMARY_Y, + INVENTORY_SUMMARY_MAX_X, + 3 * fontGetLineHeight() / 2 + INVENTORY_SUMMARY_Y, _colorTable[992]); MessageListItem messageListItem; - int offset = 499 * 2 * fontGetLineHeight() + 499 * 44 + 297; + int offset = INVENTORY_WINDOW_WIDTH * 2 * fontGetLineHeight() + INVENTORY_WINDOW_WIDTH * INVENTORY_SUMMARY_Y + INVENTORY_SUMMARY_X; for (int stat = 0; stat < 7; stat++) { messageListItem.num = stat; if (messageListGetItem(&gInventoryMessageList, &messageListItem)) { - fontDrawText(windowBuffer + offset, messageListItem.text, 80, 499, _colorTable[992]); + fontDrawText(windowBuffer + offset, messageListItem.text, 80, INVENTORY_WINDOW_WIDTH, _colorTable[992]); } int value = critterGetStat(_stack[0], stat); sprintf(formattedText, "%d", value); - fontDrawText(windowBuffer + offset + 24, formattedText, 80, 499, _colorTable[992]); + fontDrawText(windowBuffer + offset + 24, formattedText, 80, INVENTORY_WINDOW_WIDTH, _colorTable[992]); - offset += 499 * fontGetLineHeight(); + offset += INVENTORY_WINDOW_WIDTH * fontGetLineHeight(); } - offset -= 499 * 7 * fontGetLineHeight(); + offset -= INVENTORY_WINDOW_WIDTH * 7 * fontGetLineHeight(); for (int index = 0; index < 7; index += 1) { messageListItem.num = 7 + index; if (messageListGetItem(&gInventoryMessageList, &messageListItem)) { - fontDrawText(windowBuffer + offset + 40, messageListItem.text, 80, 499, _colorTable[992]); + fontDrawText(windowBuffer + offset + 40, messageListItem.text, 80, INVENTORY_WINDOW_WIDTH, _colorTable[992]); } if (v57[index] == -1) { @@ -2415,13 +2463,13 @@ static void inventoryRenderSummary() sprintf(formattedText, format, value1, value2); } - fontDrawText(windowBuffer + offset + 104, formattedText, 80, 499, _colorTable[992]); + fontDrawText(windowBuffer + offset + 104, formattedText, 80, INVENTORY_WINDOW_WIDTH, _colorTable[992]); - offset += 499 * fontGetLineHeight(); + offset += INVENTORY_WINDOW_WIDTH * fontGetLineHeight(); } - bufferDrawLine(windowBuffer, 499, 297, 18 * fontGetLineHeight() / 2 + 48, 440, 18 * fontGetLineHeight() / 2 + 48, _colorTable[992]); - bufferDrawLine(windowBuffer, 499, 297, 26 * fontGetLineHeight() / 2 + 48, 440, 26 * fontGetLineHeight() / 2 + 48, _colorTable[992]); + bufferDrawLine(windowBuffer, INVENTORY_WINDOW_WIDTH, INVENTORY_SUMMARY_X, 18 * fontGetLineHeight() / 2 + 48, INVENTORY_SUMMARY_MAX_X, 18 * fontGetLineHeight() / 2 + 48, _colorTable[992]); + bufferDrawLine(windowBuffer, INVENTORY_WINDOW_WIDTH, INVENTORY_SUMMARY_X, 26 * fontGetLineHeight() / 2 + 48, INVENTORY_SUMMARY_MAX_X, 26 * fontGetLineHeight() / 2 + 48, _colorTable[992]); Object* itemsInHands[2] = { gInventoryLeftHandItem, @@ -2443,7 +2491,7 @@ static void inventoryRenderSummary() HIT_MODE_KICK, }; - offset += 499 * fontGetLineHeight(); + offset += INVENTORY_WINDOW_WIDTH * fontGetLineHeight(); for (int index = 0; index < 2; index += 1) { Object* item = itemsInHands[index]; @@ -2453,10 +2501,10 @@ static void inventoryRenderSummary() // No item messageListItem.num = 14; if (messageListGetItem(&gInventoryMessageList, &messageListItem)) { - fontDrawText(windowBuffer + offset, messageListItem.text, 120, 499, _colorTable[992]); + fontDrawText(windowBuffer + offset, messageListItem.text, 120, INVENTORY_WINDOW_WIDTH, _colorTable[992]); } - offset += 499 * fontGetLineHeight(); + offset += INVENTORY_WINDOW_WIDTH * fontGetLineHeight(); // Unarmed dmg: messageListItem.num = 24; @@ -2490,16 +2538,16 @@ static void inventoryRenderSummary() bonusDamage + meleeDamage + maxDamage); } - fontDrawText(windowBuffer + offset, formattedText, 120, 499, _colorTable[992]); + fontDrawText(windowBuffer + offset, formattedText, 120, INVENTORY_WINDOW_WIDTH, _colorTable[992]); - offset += 3 * 499 * fontGetLineHeight(); + offset += 3 * INVENTORY_WINDOW_WIDTH * fontGetLineHeight(); continue; } const char* itemName = itemGetName(item); - fontDrawText(windowBuffer + offset, itemName, 140, 499, _colorTable[992]); + fontDrawText(windowBuffer + offset, itemName, 140, INVENTORY_WINDOW_WIDTH, _colorTable[992]); - offset += 499 * fontGetLineHeight(); + offset += INVENTORY_WINDOW_WIDTH * fontGetLineHeight(); int itemType = itemGetType(item); if (itemType != ITEM_TYPE_WEAPON) { @@ -2507,11 +2555,11 @@ static void inventoryRenderSummary() // (Not worn) messageListItem.num = 18; if (messageListGetItem(&gInventoryMessageList, &messageListItem)) { - fontDrawText(windowBuffer + offset, messageListItem.text, 120, 499, _colorTable[992]); + fontDrawText(windowBuffer + offset, messageListItem.text, 120, INVENTORY_WINDOW_WIDTH, _colorTable[992]); } } - offset += 3 * 499 * fontGetLineHeight(); + offset += 3 * INVENTORY_WINDOW_WIDTH * fontGetLineHeight(); continue; } @@ -2587,10 +2635,10 @@ static void inventoryRenderSummary() } } - fontDrawText(windowBuffer + offset, formattedText, 140, 499, _colorTable[992]); + fontDrawText(windowBuffer + offset, formattedText, 140, INVENTORY_WINDOW_WIDTH, _colorTable[992]); } - offset += 499 * fontGetLineHeight(); + offset += INVENTORY_WINDOW_WIDTH * fontGetLineHeight(); if (ammoGetCapacity(item) > 0) { int ammoTypePid = weaponGetAmmoTypePid(item); @@ -2617,10 +2665,10 @@ static void inventoryRenderSummary() sprintf(formattedText, "%s %d/%d", messageListItem.text, quantity, capacity); } - fontDrawText(windowBuffer + offset, formattedText, 140, 499, _colorTable[992]); + fontDrawText(windowBuffer + offset, formattedText, 140, INVENTORY_WINDOW_WIDTH, _colorTable[992]); } - offset += 2 * 499 * fontGetLineHeight(); + offset += 2 * INVENTORY_WINDOW_WIDTH * fontGetLineHeight(); } // Total wt: @@ -2636,12 +2684,12 @@ static void inventoryRenderSummary() color = _colorTable[31744]; } - fontDrawText(windowBuffer + offset + 15, formattedText, 120, 499, color); + fontDrawText(windowBuffer + offset + 15, formattedText, 120, INVENTORY_WINDOW_WIDTH, color); } else { int inventoryWeight = objectGetInventoryWeight(_stack[0]); sprintf(formattedText, "%s %d", messageListItem.text, inventoryWeight); - fontDrawText(windowBuffer + offset + 30, formattedText, 80, 499, _colorTable[992]); + fontDrawText(windowBuffer + offset + 30, formattedText, 80, INVENTORY_WINDOW_WIDTH, _colorTable[992]); } } @@ -3025,7 +3073,7 @@ static void inventoryRenderItemDescription(char* string) fontSetCurrent(101); unsigned char* windowBuffer = windowGetBuffer(gInventoryWindow); - windowBuffer += 499 * 44 + 297; + windowBuffer += INVENTORY_WINDOW_WIDTH * INVENTORY_SUMMARY_Y + INVENTORY_SUMMARY_X; char* c = string; while (c != NULL && *c != '\0') { @@ -3047,7 +3095,7 @@ static void inventoryRenderItemDescription(char* string) // This was the last line containing very long word. Text // drawing routine will silently truncate it after reaching // desired length. - fontDrawText(windowBuffer + 499 * _inven_display_msg_line * fontGetLineHeight(), c, 152, 499, _colorTable[992]); + fontDrawText(windowBuffer + INVENTORY_WINDOW_WIDTH * _inven_display_msg_line * fontGetLineHeight(), c, 152, INVENTORY_WINDOW_WIDTH, _colorTable[992]); return; } @@ -3088,7 +3136,7 @@ static void inventoryRenderItemDescription(char* string) return; } - fontDrawText(windowBuffer + 499 * _inven_display_msg_line * fontGetLineHeight(), c, 152, 499, _colorTable[992]); + fontDrawText(windowBuffer + INVENTORY_WINDOW_WIDTH * _inven_display_msg_line * fontGetLineHeight(), c, 152, INVENTORY_WINDOW_WIDTH, _colorTable[992]); if (space != NULL) { c = space + 1; @@ -3119,7 +3167,7 @@ static void inventoryExamineItem(Object* critter, Object* item) CacheEntry* handle; unsigned char* backgroundData = artLockFrameData(backgroundFid, 0, 0, &handle); if (backgroundData != NULL) { - blitBufferToBuffer(backgroundData + 499 * 44 + 297, 152, 188, 499, windowBuffer + 499 * 44 + 297, 499); + blitBufferToBuffer(backgroundData + INVENTORY_WINDOW_WIDTH * INVENTORY_SUMMARY_Y + INVENTORY_SUMMARY_X, 152, 188, INVENTORY_WINDOW_WIDTH, windowBuffer + INVENTORY_WINDOW_WIDTH * INVENTORY_SUMMARY_Y + INVENTORY_SUMMARY_X, INVENTORY_WINDOW_WIDTH); } artUnlock(handle); @@ -3137,10 +3185,10 @@ static void inventoryExamineItem(Object* critter, Object* item) // Draw separator. bufferDrawLine(windowBuffer, - 499, - 297, + INVENTORY_WINDOW_WIDTH, + INVENTORY_SUMMARY_X, 3 * lineHeight / 2 + 49, - 440, + INVENTORY_SUMMARY_MAX_X, 3 * lineHeight / 2 + 49, _colorTable[992]); @@ -3329,8 +3377,8 @@ static void inventoryWindowOpenContextMenu(int keyCode, int inventoryWindowType) if (inventoryWindowType == INVENTORY_WINDOW_TYPE_TRADE) { unsigned char* src = windowGetBuffer(_barter_back_win); - int pitch = 640; - blitBufferToBuffer(src + pitch * rect.top + rect.left + 80, + int pitch = INVENTORY_TRADE_BACKGROUND_WINDOW_WIDTH; + blitBufferToBuffer(src + pitch * rect.top + rect.left + INVENTORY_TRADE_WINDOW_OFFSET, cursorData->width, menuButtonHeight, pitch, @@ -3998,7 +4046,7 @@ static int _move_inventory(Object* a1, int a2, Object* a3, bool a4) int fid = buildFid(OBJ_TYPE_INTERFACE, 114, 0, 0, 0); unsigned char* data = artLockFrameData(fid, 0, 0, &handle); if (data != NULL) { - blitBufferToBuffer(data + 537 * rect.top + rect.left, INVENTORY_SLOT_WIDTH, INVENTORY_SLOT_HEIGHT, 537, windowBuffer + 537 * rect.top + rect.left, 537); + blitBufferToBuffer(data + INVENTORY_LOOT_WINDOW_WIDTH * rect.top + rect.left, INVENTORY_SLOT_WIDTH, INVENTORY_SLOT_HEIGHT, INVENTORY_LOOT_WINDOW_WIDTH, windowBuffer + INVENTORY_LOOT_WINDOW_WIDTH * rect.top + rect.left, INVENTORY_LOOT_WINDOW_WIDTH); artUnlock(handle); } @@ -4215,8 +4263,8 @@ static void _barter_move_inventory(Object* a1, int quantity, int a3, int a4, Obj unsigned char* dest = windowGetBuffer(gInventoryWindow); unsigned char* src = windowGetBuffer(_barter_back_win); - int pitch = 640; - blitBufferToBuffer(src + pitch * rect.top + rect.left + 80, INVENTORY_SLOT_WIDTH, INVENTORY_SLOT_HEIGHT, pitch, dest + 480 * rect.top + rect.left, 480); + int pitch = INVENTORY_TRADE_BACKGROUND_WINDOW_WIDTH; + blitBufferToBuffer(src + pitch * rect.top + rect.left + INVENTORY_TRADE_WINDOW_OFFSET, INVENTORY_SLOT_WIDTH, INVENTORY_SLOT_HEIGHT, pitch, dest + INVENTORY_TRADE_WINDOW_WIDTH * rect.top + rect.left, INVENTORY_TRADE_WINDOW_WIDTH); rect.right = rect.left + INVENTORY_SLOT_WIDTH - 1; rect.bottom = rect.top + INVENTORY_SLOT_HEIGHT - 1; @@ -4281,11 +4329,11 @@ static void _barter_move_from_table_inventory(Object* a1, int quantity, int a3, { Rect rect; if (a6) { - rect.left = 169; - rect.top = INVENTORY_SLOT_HEIGHT * a3 + 24; + rect.left = INVENTORY_TRADE_INNER_LEFT_SCROLLER_X_PAD; + rect.top = INVENTORY_SLOT_HEIGHT * a3 + INVENTORY_TRADE_INNER_LEFT_SCROLLER_Y_PAD; } else { - rect.left = 254; - rect.top = INVENTORY_SLOT_HEIGHT * a3 + 24; + rect.left = INVENTORY_TRADE_INNER_RIGHT_SCROLLER_X_PAD; + rect.top = INVENTORY_SLOT_HEIGHT * a3 + INVENTORY_TRADE_INNER_RIGHT_SCROLLER_Y_PAD; } if (quantity > 1) { @@ -4298,8 +4346,8 @@ static void _barter_move_from_table_inventory(Object* a1, int quantity, int a3, unsigned char* dest = windowGetBuffer(gInventoryWindow); unsigned char* src = windowGetBuffer(_barter_back_win); - int pitch = 640; - blitBufferToBuffer(src + pitch * rect.top + rect.left + 80, INVENTORY_SLOT_WIDTH, INVENTORY_SLOT_HEIGHT, pitch, dest + 480 * rect.top + rect.left, 480); + int pitch = INVENTORY_TRADE_BACKGROUND_WINDOW_WIDTH; + blitBufferToBuffer(src + pitch * rect.top + rect.left + INVENTORY_TRADE_WINDOW_OFFSET, INVENTORY_SLOT_WIDTH, INVENTORY_SLOT_HEIGHT, pitch, dest + INVENTORY_TRADE_WINDOW_WIDTH * rect.top + rect.left, INVENTORY_TRADE_WINDOW_WIDTH); rect.right = rect.left + INVENTORY_SLOT_WIDTH - 1; rect.bottom = rect.top + INVENTORY_SLOT_HEIGHT - 1; @@ -4372,17 +4420,17 @@ static void inventoryWindowRenderInnerInventories(int win, Object* a2, Object* a if (a2 != NULL) { unsigned char* src = windowGetBuffer(win); - blitBufferToBuffer(src + 640 * 20 + 249, INVENTORY_SLOT_WIDTH, v45 + 1, 640, windowBuffer + 480 * 20 + 169, 480); + blitBufferToBuffer(src + INVENTORY_TRADE_BACKGROUND_WINDOW_WIDTH * INVENTORY_TRADE_INNER_LEFT_SCROLLER_Y + INVENTORY_TRADE_INNER_LEFT_SCROLLER_X_PAD + INVENTORY_TRADE_WINDOW_OFFSET, INVENTORY_SLOT_WIDTH, v45 + 1, INVENTORY_TRADE_BACKGROUND_WINDOW_WIDTH, windowBuffer + INVENTORY_TRADE_WINDOW_WIDTH * INVENTORY_TRADE_INNER_LEFT_SCROLLER_Y + INVENTORY_TRADE_INNER_LEFT_SCROLLER_X_PAD, INVENTORY_TRADE_WINDOW_WIDTH); - unsigned char* dest = windowBuffer + 480 * 24 + 169; + unsigned char* dest = windowBuffer + INVENTORY_TRADE_WINDOW_WIDTH * INVENTORY_TRADE_INNER_LEFT_SCROLLER_Y_PAD + INVENTORY_TRADE_INNER_LEFT_SCROLLER_X_PAD; Inventory* inventory = &(a2->data.inventory); for (int index = 0; index < gInventorySlotsCount && index + _ptable_offset < inventory->length; index++) { InventoryItem* inventoryItem = &(inventory->items[inventory->length - (index + _ptable_offset + 1)]); int inventoryFid = itemGetInventoryFid(inventoryItem->item); - artRender(inventoryFid, dest, 56, 40, 480); - _display_inventory_info(inventoryItem->item, inventoryItem->quantity, dest, 480, index == a4); + artRender(inventoryFid, dest, INVENTORY_SLOT_WIDTH_PAD, INVENTORY_SLOT_HEIGHT_PAD, INVENTORY_TRADE_WINDOW_WIDTH); + _display_inventory_info(inventoryItem->item, inventoryItem->quantity, dest, INVENTORY_TRADE_WINDOW_WIDTH, index == a4); - dest += 480 * INVENTORY_SLOT_HEIGHT; + dest += INVENTORY_TRADE_WINDOW_WIDTH * INVENTORY_SLOT_HEIGHT; } if (gGameDialogSpeakerIsPartyMember) { @@ -4398,29 +4446,30 @@ static void inventoryWindowRenderInnerInventories(int win, Object* a2, Object* a sprintf(formattedText, "$%d", cost); } - fontDrawText(windowBuffer + 480 * (INVENTORY_SLOT_HEIGHT * gInventorySlotsCount + 24) + 169, formattedText, 80, 480, _colorTable[32767]); + fontDrawText(windowBuffer + INVENTORY_TRADE_WINDOW_WIDTH * (INVENTORY_SLOT_HEIGHT * gInventorySlotsCount + INVENTORY_TRADE_INNER_LEFT_SCROLLER_Y_PAD) + INVENTORY_TRADE_INNER_LEFT_SCROLLER_X_PAD, formattedText, 80, INVENTORY_TRADE_WINDOW_WIDTH, _colorTable[32767]); Rect rect; - rect.left = 169; - rect.top = 24; - rect.right = 223; + rect.left = INVENTORY_TRADE_INNER_LEFT_SCROLLER_X_PAD; + rect.top = INVENTORY_TRADE_INNER_LEFT_SCROLLER_Y_PAD; + // NOTE: Odd math, the only way to get 223 is to subtract 2. + rect.right = INVENTORY_TRADE_INNER_LEFT_SCROLLER_X_PAD + INVENTORY_SLOT_WIDTH_PAD - 2; rect.bottom = rect.top + v45; windowRefreshRect(gInventoryWindow, &rect); } if (a3 != NULL) { unsigned char* src = windowGetBuffer(win); - blitBufferToBuffer(src + 640 * 20 + 334, INVENTORY_SLOT_WIDTH, v45 + 1, 640, windowBuffer + 480 * 20 + 254, 480); + blitBufferToBuffer(src + INVENTORY_TRADE_BACKGROUND_WINDOW_WIDTH * INVENTORY_TRADE_INNER_RIGHT_SCROLLER_Y + INVENTORY_TRADE_INNER_RIGHT_SCROLLER_X_PAD + INVENTORY_TRADE_WINDOW_OFFSET, INVENTORY_SLOT_WIDTH, v45 + 1, INVENTORY_TRADE_BACKGROUND_WINDOW_WIDTH, windowBuffer + INVENTORY_TRADE_WINDOW_WIDTH * INVENTORY_TRADE_INNER_RIGHT_SCROLLER_Y + INVENTORY_TRADE_INNER_RIGHT_SCROLLER_X_PAD, INVENTORY_TRADE_WINDOW_WIDTH); - unsigned char* dest = windowBuffer + 480 * 24 + 254; + unsigned char* dest = windowBuffer + INVENTORY_TRADE_WINDOW_WIDTH * INVENTORY_TRADE_INNER_RIGHT_SCROLLER_Y_PAD + INVENTORY_TRADE_INNER_RIGHT_SCROLLER_X_PAD; Inventory* inventory = &(a3->data.inventory); for (int index = 0; index < gInventorySlotsCount && index + _btable_offset < inventory->length; index++) { InventoryItem* inventoryItem = &(inventory->items[inventory->length - (index + _btable_offset + 1)]); int inventoryFid = itemGetInventoryFid(inventoryItem->item); - artRender(inventoryFid, dest, 56, 40, 480); - _display_inventory_info(inventoryItem->item, inventoryItem->quantity, dest, 480, index == a4); + artRender(inventoryFid, dest, INVENTORY_SLOT_WIDTH_PAD, INVENTORY_SLOT_HEIGHT_PAD, INVENTORY_TRADE_WINDOW_WIDTH); + _display_inventory_info(inventoryItem->item, inventoryItem->quantity, dest, INVENTORY_TRADE_WINDOW_WIDTH, index == a4); - dest += 480 * INVENTORY_SLOT_HEIGHT; + dest += INVENTORY_TRADE_WINDOW_WIDTH * INVENTORY_SLOT_HEIGHT; } if (gGameDialogSpeakerIsPartyMember) { @@ -4436,12 +4485,13 @@ static void inventoryWindowRenderInnerInventories(int win, Object* a2, Object* a sprintf(formattedText, "$%d", cost); } - fontDrawText(windowBuffer + 480 * (INVENTORY_SLOT_HEIGHT * gInventorySlotsCount + 24) + 254, formattedText, 80, 480, _colorTable[32767]); + fontDrawText(windowBuffer + INVENTORY_TRADE_WINDOW_WIDTH * (INVENTORY_SLOT_HEIGHT * gInventorySlotsCount + INVENTORY_TRADE_INNER_RIGHT_SCROLLER_Y_PAD) + INVENTORY_TRADE_INNER_RIGHT_SCROLLER_X_PAD, formattedText, 80, INVENTORY_TRADE_WINDOW_WIDTH, _colorTable[32767]); Rect rect; - rect.left = 254; - rect.top = 24; - rect.right = 318; + rect.left = INVENTORY_TRADE_INNER_RIGHT_SCROLLER_X_PAD; + rect.top = INVENTORY_TRADE_INNER_RIGHT_SCROLLER_Y_PAD; + // NOTE: Odd math, likely should be `INVENTORY_SLOT_WIDTH_PAD`. + rect.right = INVENTORY_TRADE_INNER_RIGHT_SCROLLER_X_PAD + INVENTORY_SLOT_WIDTH; rect.bottom = rect.top + v45; windowRefreshRect(gInventoryWindow, &rect); } From d31e3678701498c325e082e2e60b06389606909a Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 16 Aug 2022 22:19:23 +0300 Subject: [PATCH 35/37] Tweak inventory scroller positions --- src/inventory.cc | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/inventory.cc b/src/inventory.cc index efa47ef..886df5e 100644 --- a/src/inventory.cc +++ b/src/inventory.cc @@ -72,13 +72,13 @@ #define INVENTORY_ARMOR_SLOT_MAX_X (INVENTORY_ARMOR_SLOT_X + INVENTORY_LARGE_SLOT_WIDTH) #define INVENTORY_ARMOR_SLOT_MAX_Y (INVENTORY_ARMOR_SLOT_Y + INVENTORY_LARGE_SLOT_HEIGHT) -#define INVENTORY_TRADE_SCROLLER_Y 35 +#define INVENTORY_TRADE_SCROLLER_Y 30 #define INVENTORY_TRADE_INNER_SCROLLER_Y 20 #define INVENTORY_TRADE_LEFT_SCROLLER_X 29 #define INVENTORY_TRADE_LEFT_SCROLLER_Y INVENTORY_TRADE_SCROLLER_Y -#define INVENTORY_TRADE_RIGHT_SCROLLER_X 395 +#define INVENTORY_TRADE_RIGHT_SCROLLER_X 388 #define INVENTORY_TRADE_RIGHT_SCROLLER_Y INVENTORY_TRADE_SCROLLER_Y #define INVENTORY_TRADE_INNER_LEFT_SCROLLER_X 165 @@ -103,7 +103,7 @@ #define INVENTORY_TRADE_RIGHT_SCROLLER_TRACKING_Y 10 #define INVENTORY_TRADE_RIGHT_SCROLLER_TRACKING_MAX_X (INVENTORY_TRADE_RIGHT_SCROLLER_TRACKING_X + INVENTORY_SLOT_WIDTH) -#define INVENTORY_LOOT_LEFT_SCROLLER_X 176 +#define INVENTORY_LOOT_LEFT_SCROLLER_X 180 #define INVENTORY_LOOT_LEFT_SCROLLER_Y 37 #define INVENTORY_LOOT_LEFT_SCROLLER_MAX_X (INVENTORY_LOOT_LEFT_SCROLLER_X + INVENTORY_SLOT_WIDTH) @@ -111,7 +111,7 @@ #define INVENTORY_LOOT_RIGHT_SCROLLER_Y 37 #define INVENTORY_LOOT_RIGHT_SCROLLER_MAX_X (INVENTORY_LOOT_RIGHT_SCROLLER_X + INVENTORY_SLOT_WIDTH) -#define INVENTORY_SCROLLER_X 44 +#define INVENTORY_SCROLLER_X 46 #define INVENTORY_SCROLLER_Y 35 #define INVENTORY_SCROLLER_MAX_X (INVENTORY_SCROLLER_X + INVENTORY_SLOT_WIDTH) @@ -154,12 +154,10 @@ #define INVENTORY_LOOT_RIGHT_SCROLLER_X_PAD (INVENTORY_LOOT_RIGHT_SCROLLER_X + INVENTORY_SLOT_PADDING) #define INVENTORY_LOOT_RIGHT_SCROLLER_Y_PAD (INVENTORY_LOOT_RIGHT_SCROLLER_Y + INVENTORY_SLOT_PADDING) -#define INVENTORY_TRADE_LEFT_SCROLLER_X_PAD 26 +#define INVENTORY_TRADE_LEFT_SCROLLER_X_PAD (INVENTORY_TRADE_LEFT_SCROLLER_Y + INVENTORY_SLOT_PADDING) #define INVENTORY_TRADE_LEFT_SCROLLER_Y_PAD (INVENTORY_TRADE_LEFT_SCROLLER_Y + INVENTORY_SLOT_PADDING) -#define INVENTORY_TRADE_LEFT_SCROLLER_X_AUX 20 - -#define INVENTORY_TRADE_RIGHT_SCROLLER_X_PAD (INVENTORY_TRADE_RIGHT_SCROLLER_X + 2) +#define INVENTORY_TRADE_RIGHT_SCROLLER_X_PAD (INVENTORY_TRADE_RIGHT_SCROLLER_X + INVENTORY_SLOT_PADDING) #define INVENTORY_TRADE_RIGHT_SCROLLER_Y_PAD (INVENTORY_TRADE_RIGHT_SCROLLER_Y + INVENTORY_SLOT_PADDING) #define INVENTORY_TRADE_INNER_LEFT_SCROLLER_X_PAD (INVENTORY_TRADE_INNER_LEFT_SCROLLER_X + INVENTORY_SLOT_PADDING) @@ -1240,7 +1238,6 @@ static void _display_inventory(int a1, int a2, int inventoryWindowType) unsigned char* windowBuffer = windowGetBuffer(gInventoryWindow); int pitch; - int v49 = 0; if (inventoryWindowType == INVENTORY_WINDOW_TYPE_NORMAL) { pitch = INVENTORY_WINDOW_WIDTH; @@ -1304,8 +1301,7 @@ static void _display_inventory(int a1, int a2, int inventoryWindowType) windowBuffer = windowGetBuffer(gInventoryWindow); - blitBufferToBuffer(windowGetBuffer(_barter_back_win) + INVENTORY_TRADE_LEFT_SCROLLER_Y * INVENTORY_TRADE_BACKGROUND_WINDOW_WIDTH + INVENTORY_TRADE_LEFT_SCROLLER_X_AUX + INVENTORY_TRADE_WINDOW_OFFSET, INVENTORY_SLOT_WIDTH, INVENTORY_SLOT_HEIGHT * gInventorySlotsCount, INVENTORY_TRADE_BACKGROUND_WINDOW_WIDTH, windowBuffer + pitch * INVENTORY_TRADE_LEFT_SCROLLER_Y + INVENTORY_TRADE_LEFT_SCROLLER_X_AUX, pitch); - v49 = -INVENTORY_TRADE_LEFT_SCROLLER_X_AUX; + blitBufferToBuffer(windowGetBuffer(_barter_back_win) + INVENTORY_TRADE_LEFT_SCROLLER_Y * INVENTORY_TRADE_BACKGROUND_WINDOW_WIDTH + INVENTORY_TRADE_LEFT_SCROLLER_X + INVENTORY_TRADE_WINDOW_OFFSET, INVENTORY_SLOT_WIDTH, INVENTORY_SLOT_HEIGHT * gInventorySlotsCount, INVENTORY_TRADE_BACKGROUND_WINDOW_WIDTH, windowBuffer + pitch * INVENTORY_TRADE_LEFT_SCROLLER_Y + INVENTORY_TRADE_LEFT_SCROLLER_X, pitch); } else { assert(false && "Should be unreachable"); } @@ -1334,29 +1330,28 @@ static void _display_inventory(int a1, int a2, int inventoryWindowType) for (int v19 = 0; v19 + a1 < _pud->length && v19 < gInventorySlotsCount; v19 += 1) { int v21 = v19 + a1 + 1; - int width; int offset; if (inventoryWindowType == INVENTORY_WINDOW_TYPE_TRADE) { offset = pitch * (y + INVENTORY_TRADE_LEFT_SCROLLER_Y_PAD) + INVENTORY_TRADE_LEFT_SCROLLER_X_PAD; - width = 59; } else { if (inventoryWindowType == INVENTORY_WINDOW_TYPE_LOOT) { offset = pitch * (y + INVENTORY_LOOT_LEFT_SCROLLER_Y_PAD) + INVENTORY_LOOT_LEFT_SCROLLER_X_PAD; } else { offset = pitch * (y + INVENTORY_SCROLLER_Y_PAD) + INVENTORY_SCROLLER_X_PAD; } - width = INVENTORY_SLOT_WIDTH_PAD; } InventoryItem* inventoryItem = &(_pud->items[_pud->length - v21]); int inventoryFid = itemGetInventoryFid(inventoryItem->item); - artRender(inventoryFid, windowBuffer + offset, width, INVENTORY_SLOT_HEIGHT_PAD, pitch); + artRender(inventoryFid, windowBuffer + offset, INVENTORY_SLOT_WIDTH_PAD, INVENTORY_SLOT_HEIGHT_PAD, pitch); if (inventoryWindowType == INVENTORY_WINDOW_TYPE_LOOT) { - offset = pitch * (y + INVENTORY_LOOT_LEFT_SCROLLER_Y_PAD) + INVENTORY_LOOT_LEFT_SCROLLER_X_PAD + v49; + offset = pitch * (y + INVENTORY_LOOT_LEFT_SCROLLER_Y_PAD) + INVENTORY_LOOT_LEFT_SCROLLER_X_PAD; + } else if (inventoryWindowType == INVENTORY_WINDOW_TYPE_TRADE) { + offset = pitch * (y + INVENTORY_TRADE_LEFT_SCROLLER_Y_PAD) + INVENTORY_TRADE_LEFT_SCROLLER_X_PAD; } else { - offset = pitch * (y + INVENTORY_SCROLLER_Y_PAD) + INVENTORY_SCROLLER_X_PAD + v49; + offset = pitch * (y + INVENTORY_SCROLLER_Y_PAD) + INVENTORY_SCROLLER_X_PAD; } _display_inventory_info(inventoryItem->item, inventoryItem->quantity, windowBuffer + offset, pitch, v19 == a2); From bee34bfdf47e0f343d52bb643c34b733a6d97cd1 Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Tue, 16 Aug 2022 23:06:40 +0300 Subject: [PATCH 36/37] Add items weight summary --- src/inventory.cc | 91 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/src/inventory.cc b/src/inventory.cc index 886df5e..e4dbabc 100644 --- a/src/inventory.cc +++ b/src/inventory.cc @@ -1377,6 +1377,48 @@ static void _display_inventory(int a1, int a2, int inventoryWindowType) } } + // CE: Show items weight. + if (inventoryWindowType == INVENTORY_WINDOW_TYPE_LOOT) { + char formattedText[20]; + + int oldFont = fontGetCurrent(); + fontSetCurrent(101); + + int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 114, 0, 0, 0); + + CacheEntry* backgroundFrmHandle; + unsigned char* backgroundFrmData = artLockFrameData(backgroundFid, 0, 0, &backgroundFrmHandle); + if (backgroundFrmData != NULL) { + int x = INVENTORY_LOOT_LEFT_SCROLLER_X; + int y = INVENTORY_LOOT_LEFT_SCROLLER_Y + gInventorySlotsCount * INVENTORY_SLOT_HEIGHT + 2; + blitBufferToBuffer(backgroundFrmData + pitch * y + x, INVENTORY_SLOT_WIDTH, fontGetLineHeight(), pitch, windowBuffer + pitch * y + x, pitch); + artUnlock(backgroundFrmHandle); + } + + Object* object = _stack[0]; + + int color = _colorTable[992]; + if (PID_TYPE(object->pid) == OBJ_TYPE_CRITTER) { + int carryWeight = critterGetStat(object, STAT_CARRY_WEIGHT); + int inventoryWeight = objectGetInventoryWeight(object); + sprintf(formattedText, "%d/%d", inventoryWeight, carryWeight); + + if (critterIsEncumbered(object)) { + color = _colorTable[31744]; + } + } else { + int inventoryWeight = objectGetInventoryWeight(object); + sprintf(formattedText, "%d", inventoryWeight); + } + + int width = fontGetStringWidth(formattedText); + int x = INVENTORY_LOOT_LEFT_SCROLLER_X + INVENTORY_SLOT_WIDTH / 2 - width / 2; + int y = INVENTORY_LOOT_LEFT_SCROLLER_Y + INVENTORY_SLOT_HEIGHT * gInventorySlotsCount + 2; + fontDrawText(windowBuffer + pitch * y + x, formattedText, width, pitch, color); + + fontSetCurrent(oldFont); + } + windowRefresh(gInventoryWindow); } @@ -1452,6 +1494,55 @@ static void _display_target_inventory(int a1, int a2, Inventory* inventory, int } } } + + // CE: Show items weight. + if (inventoryWindowType == INVENTORY_WINDOW_TYPE_LOOT) { + char formattedText[20]; + formattedText[0] = '\0'; + + int oldFont = fontGetCurrent(); + fontSetCurrent(101); + + int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 114, 0, 0, 0); + + CacheEntry* backgroundFrmHandle; + unsigned char* backgroundFrmData = artLockFrameData(backgroundFid, 0, 0, &backgroundFrmHandle); + if (backgroundFrmData != NULL) { + int x = INVENTORY_LOOT_RIGHT_SCROLLER_X; + int y = INVENTORY_LOOT_RIGHT_SCROLLER_Y + INVENTORY_SLOT_HEIGHT * gInventorySlotsCount + 2; + blitBufferToBuffer(backgroundFrmData + pitch * y + x, INVENTORY_SLOT_WIDTH, fontGetLineHeight(), pitch, windowBuffer + pitch * y + x, pitch); + artUnlock(backgroundFrmHandle); + } + + Object* object = _target_stack[_target_curr_stack]; + + int color = _colorTable[992]; + if (PID_TYPE(object->pid) == OBJ_TYPE_CRITTER) { + int currentWeight = objectGetInventoryWeight(object); + int maxWeight = critterGetStat(object, STAT_CARRY_WEIGHT); + sprintf(formattedText, "%d/%d", currentWeight, maxWeight); + + if (critterIsEncumbered(object)) { + color = _colorTable[31744]; + } + } else if (PID_TYPE(object->pid) == OBJ_TYPE_ITEM) { + if (itemGetType(object) == ITEM_TYPE_CONTAINER) { + int currentSize = containerGetTotalSize(object); + int maxSize = containerGetMaxSize(object); + sprintf(formattedText, "%d/%d", currentSize, maxSize); + } + } else { + int inventoryWeight = objectGetInventoryWeight(object); + sprintf(formattedText, "%d", inventoryWeight); + } + + int width = fontGetStringWidth(formattedText); + int x = INVENTORY_LOOT_RIGHT_SCROLLER_X + INVENTORY_SLOT_WIDTH / 2 - width / 2; + int y = INVENTORY_LOOT_RIGHT_SCROLLER_Y + INVENTORY_SLOT_HEIGHT * gInventorySlotsCount + 2; + fontDrawText(windowBuffer + pitch * y + x, formattedText, width, pitch, color); + + fontSetCurrent(oldFont); + } } // Renders inventory item quantity. From 354b0812c9d339d4b323db81acedf83416d9252c Mon Sep 17 00:00:00 2001 From: Alexander Batalov Date: Wed, 17 Aug 2022 00:01:20 +0300 Subject: [PATCH 37/37] Fix missing Android signing configs in forks/PRs --- .github/workflows/ci-build.yml | 1 + .github/workflows/release.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index ff3e0f0..f2a2f8f 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -54,6 +54,7 @@ jobs: key: android-cmake-v1 - name: Setup signing config + if: env.KEYSTORE_FILE_BASE64 != '' && env.KEYSTORE_PROPERTIES_FILE_BASE64 != '' run: | cd os/android echo "$KEYSTORE_FILE_BASE64" | base64 --decode > debug.keystore diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6e28054..980fbc5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,6 +33,7 @@ jobs: key: android-cmake-v1 - name: Setup signing config + if: env.KEYSTORE_FILE_BASE64 != '' && env.KEYSTORE_PROPERTIES_FILE_BASE64 != '' run: | cd os/android echo "$KEYSTORE_FILE_BASE64" | base64 --decode > release.keystore