Merge branch 'main' into add-loading-dialog
This commit is contained in:
commit
1de1cdddc1
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
65
src/art.cc
65
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);
|
||||
|
@ -698,12 +714,6 @@ static int artReadList(const char* path, char** artListPtr, int* artListSizePtr)
|
|||
fileClose(stream);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
|
||||
fileClose(stream);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 0x419760
|
||||
|
@ -854,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);
|
||||
|
@ -867,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
|
||||
|
@ -937,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);
|
||||
|
@ -973,7 +994,7 @@ static int artCacheGetFileSizeImpl(int fid, int* sizePtr)
|
|||
}
|
||||
|
||||
if (oldDb != -1) {
|
||||
_db_current(oldDb);
|
||||
_db_select(oldDb);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -986,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);
|
||||
|
@ -1021,7 +1042,7 @@ static int artCacheReadDataImpl(int fid, int* sizePtr, unsigned char* data)
|
|||
}
|
||||
|
||||
if (oldDb != -1) {
|
||||
_db_current(oldDb);
|
||||
_db_select(oldDb);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
@ -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);
|
||||
|
|
646
src/combat.cc
646
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;
|
||||
|
@ -54,6 +61,30 @@ 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;
|
||||
|
||||
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);
|
||||
|
@ -96,6 +127,14 @@ 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);
|
||||
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[] = ".";
|
||||
|
@ -1932,6 +1971,10 @@ 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];
|
||||
static int gDamageCalculationType;
|
||||
static bool gBonusHthDamageFix;
|
||||
static bool gDisplayBonusDamage;
|
||||
|
||||
// combat_init
|
||||
// 0x420CC0
|
||||
|
@ -1974,6 +2017,8 @@ int combatInit()
|
|||
// SFALL
|
||||
criticalsInit();
|
||||
burstModInit();
|
||||
unarmedInit();
|
||||
damageModInit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2126,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;
|
||||
}
|
||||
|
||||
|
@ -2135,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;
|
||||
}
|
||||
|
||||
|
@ -2144,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;
|
||||
}
|
||||
|
||||
|
@ -2777,7 +2828,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)) {
|
||||
|
@ -2796,7 +2849,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);
|
||||
}
|
||||
|
||||
|
@ -3757,13 +3810,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3793,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;
|
||||
|
@ -4203,7 +4265,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 +4475,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;
|
||||
}
|
||||
|
||||
|
@ -4448,41 +4508,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6189,3 +6265,483 @@ 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
16
src/combat.h
16
src/combat.h
|
@ -59,10 +59,26 @@ 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);
|
||||
bool damageModGetBonusHthDamageFix();
|
||||
bool damageModGetDisplayBonusDamage();
|
||||
|
||||
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 */
|
||||
|
|
|
@ -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 {
|
||||
|
@ -1716,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) {
|
||||
|
@ -1760,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);
|
||||
|
@ -1768,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) {
|
||||
|
|
87
src/core.cc
87
src/core.cc
|
@ -17,11 +17,13 @@
|
|||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
// NOT USED.
|
||||
void (*_idle_func)() = NULL;
|
||||
static void idleImpl();
|
||||
|
||||
// NOT USED.
|
||||
void (*_focus_func)(int) = NULL;
|
||||
// 0x51E234
|
||||
IdleFunc* _idle_func = NULL;
|
||||
|
||||
// 0x51E238
|
||||
FocusFunc* _focus_func = NULL;
|
||||
|
||||
// 0x51E23C
|
||||
int gKeyboardKeyRepeatRate = 80;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -922,6 +928,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 +1445,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 +1457,7 @@ void _GNW95_lost_focus()
|
|||
}
|
||||
|
||||
if (_focus_func != NULL) {
|
||||
_focus_func(1);
|
||||
_focus_func(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4843,3 +4913,8 @@ void convertMouseWheelToArrowKey(int* keyCodePtr)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void idleImpl()
|
||||
{
|
||||
SDL_Delay(125);
|
||||
}
|
||||
|
|
16
src/core.h
16
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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
16
src/db.cc
16
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
|
||||
|
|
5
src/db.h
5
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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -1264,29 +1268,14 @@ 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);
|
||||
// SFALL
|
||||
leftItemState->primaryHitMode = unarmedGetPunchHitMode(false);
|
||||
leftItemState->secondaryHitMode = unarmedGetPunchHitMode(true);
|
||||
|
||||
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: Keep selected attack mode.
|
||||
// CE: Implementation is different.
|
||||
if (oldItem == NULL) {
|
||||
leftItemState->action = oldAction;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1300,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) {
|
||||
|
@ -1324,29 +1316,14 @@ 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);
|
||||
// SFALL
|
||||
rightItemState->primaryHitMode = unarmedGetKickHitMode(false);
|
||||
rightItemState->secondaryHitMode = unarmedGetKickHitMode(true);
|
||||
|
||||
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: Keep selected attack mode.
|
||||
// CE: Implementation is different.
|
||||
if (oldItem == NULL) {
|
||||
rightItemState->action = oldAction;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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<Object*>(programStackPopPointer(program));
|
||||
|
||||
int color = _colorTable[32747];
|
||||
|
@ -3340,10 +3344,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<Object*>(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;
|
||||
|
@ -3584,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) {
|
||||
|
|
500
src/inventory.cc
500
src/inventory.cc
|
@ -72,20 +72,26 @@
|
|||
#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_LEFT_SCROLLER_X 29
|
||||
#define INVENTORY_TRADE_RIGHT_SCROLLER_X 395
|
||||
|
||||
#define INVENTORY_TRADE_INNER_LEFT_SCROLLER_X 165
|
||||
#define INVENTORY_TRADE_INNER_RIGHT_SCROLLER_X 250
|
||||
|
||||
#define INVENTORY_TRADE_OUTER_SCROLLER_Y 35
|
||||
#define INVENTORY_TRADE_SCROLLER_Y 30
|
||||
#define INVENTORY_TRADE_INNER_SCROLLER_Y 20
|
||||
|
||||
#define INVENTORY_TRADE_LEFT_SCROLLER_TRACKING_X 165
|
||||
#define INVENTORY_TRADE_LEFT_SCROLLER_X 29
|
||||
#define INVENTORY_TRADE_LEFT_SCROLLER_Y INVENTORY_TRADE_SCROLLER_Y
|
||||
|
||||
#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
|
||||
#define INVENTORY_TRADE_INNER_LEFT_SCROLLER_Y INVENTORY_TRADE_INNER_SCROLLER_Y
|
||||
|
||||
#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
|
||||
#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)
|
||||
|
||||
|
@ -97,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)
|
||||
|
||||
|
@ -105,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)
|
||||
|
||||
|
@ -123,6 +129,46 @@
|
|||
#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 (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_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)
|
||||
#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 +321,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 +757,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 +800,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++) {
|
||||
|
@ -1192,9 +1238,8 @@ 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 = 499;
|
||||
pitch = INVENTORY_WINDOW_WIDTH;
|
||||
|
||||
int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 48, 0, 0, 0);
|
||||
|
||||
|
@ -1202,7 +1247,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 +1273,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 +1281,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, 64, gInventorySlotsCount * 48, 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 +1293,15 @@ 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 * 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, 64, 48 * 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 + 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");
|
||||
}
|
||||
|
@ -1286,54 +1330,95 @@ 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 + 39) + 26;
|
||||
width = 59;
|
||||
offset = pitch * (y + INVENTORY_TRADE_LEFT_SCROLLER_Y_PAD) + INVENTORY_TRADE_LEFT_SCROLLER_X_PAD;
|
||||
} 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;
|
||||
}
|
||||
|
||||
InventoryItem* inventoryItem = &(_pud->items[_pud->length - v21]);
|
||||
|
||||
int inventoryFid = itemGetInventoryFid(inventoryItem->item);
|
||||
artRender(inventoryFid, windowBuffer + offset, width, 40, pitch);
|
||||
artRender(inventoryFid, windowBuffer + offset, INVENTORY_SLOT_WIDTH_PAD, 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;
|
||||
} 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 + 39) + 48 + v49;
|
||||
offset = pitch * (y + INVENTORY_SCROLLER_Y_PAD) + INVENTORY_SCROLLER_X_PAD;
|
||||
}
|
||||
|
||||
_display_inventory_info(inventoryItem->item, inventoryItem->quantity, windowBuffer + offset, pitch, v19 == a2);
|
||||
|
||||
y += 48;
|
||||
y += INVENTORY_SLOT_HEIGHT;
|
||||
}
|
||||
|
||||
if (inventoryWindowType == INVENTORY_WINDOW_TYPE_NORMAL) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@ -1349,21 +1434,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, 64, 48 * 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, 64, 48 * 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,19 +1462,19 @@ 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 += 48;
|
||||
y += INVENTORY_SLOT_HEIGHT;
|
||||
}
|
||||
|
||||
if (inventoryWindowType == INVENTORY_WINDOW_TYPE_LOOT) {
|
||||
|
@ -1409,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.
|
||||
|
@ -1584,10 +1718,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 +1925,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 = 48 * v3 + 35;
|
||||
rect.left = INVENTORY_SCROLLER_X;
|
||||
rect.top = INVENTORY_SLOT_HEIGHT * v3 + INVENTORY_SCROLLER_Y;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1813,7 +1947,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 +1958,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);
|
||||
}
|
||||
|
||||
|
@ -1868,7 +2002,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) {
|
||||
|
@ -2365,44 +2499,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 +2549,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,
|
||||
|
@ -2433,7 +2567,17 @@ static void inventoryRenderSummary()
|
|||
HIT_MODE_RIGHT_WEAPON_PRIMARY,
|
||||
};
|
||||
|
||||
offset += 499 * fontGetLineHeight();
|
||||
const int secondaryHitModes[2] = {
|
||||
HIT_MODE_LEFT_WEAPON_SECONDARY,
|
||||
HIT_MODE_RIGHT_WEAPON_SECONDARY,
|
||||
};
|
||||
|
||||
const int unarmedHitModes[2] = {
|
||||
HIT_MODE_PUNCH,
|
||||
HIT_MODE_KICK,
|
||||
};
|
||||
|
||||
offset += INVENTORY_WINDOW_WIDTH * fontGetLineHeight();
|
||||
|
||||
for (int index = 0; index < 2; index += 1) {
|
||||
Object* item = itemsInHands[index];
|
||||
|
@ -2443,30 +2587,53 @@ 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;
|
||||
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]);
|
||||
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) {
|
||||
|
@ -2474,27 +2641,48 @@ 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;
|
||||
}
|
||||
|
||||
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';
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -2502,19 +2690,41 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -2541,10 +2751,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:
|
||||
|
@ -2560,12 +2770,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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2949,7 +3159,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') {
|
||||
|
@ -2971,7 +3181,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;
|
||||
}
|
||||
|
||||
|
@ -3012,7 +3222,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;
|
||||
|
@ -3043,7 +3253,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);
|
||||
|
||||
|
@ -3061,10 +3271,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]);
|
||||
|
||||
|
@ -3253,8 +3463,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,
|
||||
|
@ -3825,15 +4035,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3920,7 +4132,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);
|
||||
}
|
||||
|
||||
|
@ -4121,10 +4333,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) {
|
||||
|
@ -4137,8 +4349,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;
|
||||
|
@ -4168,7 +4380,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) {
|
||||
|
@ -4203,11 +4415,11 @@ 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.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 = 48 * 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) {
|
||||
|
@ -4220,8 +4432,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;
|
||||
|
@ -4251,7 +4463,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) {
|
||||
|
@ -4290,21 +4502,21 @@ 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 + 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 * 48;
|
||||
dest += INVENTORY_TRADE_WINDOW_WIDTH * INVENTORY_SLOT_HEIGHT;
|
||||
}
|
||||
|
||||
if (gGameDialogSpeakerIsPartyMember) {
|
||||
|
@ -4320,29 +4532,30 @@ 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 + 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, 64, 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 * 48;
|
||||
dest += INVENTORY_TRADE_WINDOW_WIDTH * INVENTORY_SLOT_HEIGHT;
|
||||
}
|
||||
|
||||
if (gGameDialogSpeakerIsPartyMember) {
|
||||
|
@ -4358,12 +4571,13 @@ 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 + 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);
|
||||
}
|
||||
|
@ -4605,7 +4819,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);
|
||||
|
@ -4620,7 +4834,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);
|
||||
|
|
142
src/item.cc
142
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;
|
||||
}
|
||||
|
@ -1261,55 +1267,44 @@ 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);
|
||||
|
||||
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 {
|
||||
minDamage = 1;
|
||||
maxDamage = critterGetStat(critter, STAT_MELEE_DAMAGE) + 2;
|
||||
// SFALL
|
||||
bonusDamage = unarmedGetDamage(hitMode, &minDamage, &maxDamage);
|
||||
meleeDamage = critterGetStat(critter, STAT_MELEE_DAMAGE);
|
||||
|
||||
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: 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(unarmedDamage + minDamage, unarmedDamage + meleeDamage + maxDamage);
|
||||
return randomBetween(bonusDamage + minDamage, bonusDamage + meleeDamage + maxDamage);
|
||||
}
|
||||
|
||||
// 0x478570
|
||||
|
@ -1678,28 +1673,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 +1696,6 @@ int _item_w_mp_cost(Object* critter, int hitMode, bool aiming)
|
|||
} else {
|
||||
actionPoints = 3;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (critter == gDude) {
|
||||
|
@ -2829,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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3613,3 +3591,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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 <ctype.h>
|
||||
#include <stdio.h>
|
||||
|
@ -200,6 +202,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;
|
||||
}
|
||||
|
@ -560,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1208,7 +1231,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;
|
||||
|
|
|
@ -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"
|
||||
|
@ -36,6 +37,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 +247,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 +1499,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 +1546,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 +1616,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)) {
|
||||
|
@ -2657,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;
|
||||
|
|
|
@ -41,6 +41,22 @@
|
|||
#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_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_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_TOWN_MAP_HOTKEYS_FIX "TownMapHotkeysFix"
|
||||
|
||||
#define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_MULTIPLIER 1
|
||||
#define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR 3
|
||||
|
|
18
src/stat.cc
18
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;
|
||||
|
@ -772,12 +774,22 @@ 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (xpGained != NULL) {
|
||||
*xpGained = newXp - oldXp;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -3678,21 +3685,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 {
|
||||
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue