Add unarmed hits improvements (#29)

This commit is contained in:
Alexander Batalov 2022-08-10 20:46:09 +03:00
parent 2859410d4b
commit 87289a34c0
7 changed files with 400 additions and 118 deletions

View File

@ -54,6 +54,19 @@ typedef struct CombatAiInfo {
int lastMove;
} CombatAiInfo;
typedef struct UnarmedHitDescription {
int requiredLevel;
int requiredSkill;
int requiredStats[PRIMARY_STAT_COUNT];
int minDamage;
int maxDamage;
int bonusDamage;
int bonusCriticalChance;
int actionPointCost;
bool isPenetrate;
bool isSecondary;
} UnarmedHitDescription;
static bool _combat_safety_invalidate_weapon_func(Object* critter, Object* weapon, int hitMode, Object* a4, int* a5, Object* a6);
static int aiInfoCopy(int srcIndex, int destIndex);
static void _combat_begin(Object* a1);
@ -96,6 +109,10 @@ static void criticalsReset();
static void criticalsExit();
static void burstModInit();
static int burstModComputeRounds(int totalRounds, int* centerRoundsPtr, int* leftRoundsPtr, int* rightRoundsPtr);
static void unarmedInit();
static void unarmedInitVanilla();
static void unarmedInitCustom();
static int unarmedGetHitModeInRange(int firstHitMode, int lastHitMode, bool isSecondary);
// 0x500B50
static char _a_1[] = ".";
@ -1932,6 +1949,7 @@ static int gBurstModCenterMultiplier = SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_MUL
static int gBurstModCenterDivisor = SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR;
static int gBurstModTargetMultiplier = SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_MULTIPLIER;
static int gBurstModTargetDivisor = SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_DIVISOR;
static UnarmedHitDescription gUnarmedHitDescriptions[HIT_MODE_COUNT];
// combat_init
// 0x420CC0
@ -1974,6 +1992,7 @@ int combatInit()
// SFALL
criticalsInit();
burstModInit();
unarmedInit();
return 0;
}
@ -3757,16 +3776,15 @@ 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)) {
// SFALL
int bonusCriticalChance = unarmedGetBonusCriticalChance(attack->hitMode);
if (bonusCriticalChance != 0) {
if (randomBetween(1, 100) <= bonusCriticalChance) {
roll = ROLL_CRITICAL_SUCCESS;
}
}
}
}
if (attackType == ATTACK_TYPE_RANGED) {
attack->ammoQuantity = v26;
@ -4203,7 +4221,7 @@ static int attackDetermineToHit(Object* attacker, int tile, Object* defender, in
bool isRangedWeapon = false;
int accuracy;
if (weapon == NULL || hitMode == HIT_MODE_PUNCH || hitMode == HIT_MODE_KICK || (hitMode >= FIRST_ADVANCED_UNARMED_HIT_MODE && hitMode <= LAST_ADVANCED_UNARMED_HIT_MODE)) {
if (weapon == NULL || isUnarmedHitMode(hitMode)) {
accuracy = skillGetValue(attacker, SKILL_UNARMED);
} else {
accuracy = _item_w_skill_level(attacker, hitMode);
@ -4413,11 +4431,9 @@ static void attackComputeDamage(Attack* attack, int ammoQuantity, int bonusDamag
damageThreshold = 20 * damageThreshold / 100;
damageResistance = 20 * damageResistance / 100;
} else {
// SFALL
if (weaponGetPerk(attack->weapon) == PERK_WEAPON_PENETRATE
|| attack->hitMode == HIT_MODE_PALM_STRIKE
|| attack->hitMode == HIT_MODE_PIERCING_STRIKE
|| attack->hitMode == HIT_MODE_HOOK_KICK
|| attack->hitMode == HIT_MODE_PIERCING_KICK) {
|| unarmedIsPenetrating(attack->hitMode)) {
damageThreshold = 20 * damageThreshold / 100;
}
@ -6189,3 +6205,306 @@ static int burstModComputeRounds(int totalRounds, int* centerRoundsPtr, int* lef
return mainTargetRounds;
}
static void unarmedInit()
{
unarmedInitVanilla();
unarmedInitCustom();
}
static void unarmedInitVanilla()
{
UnarmedHitDescription* hitDescription;
// Punch
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_PUNCH]);
hitDescription->minDamage = 1;
hitDescription->maxDamage = 2;
hitDescription->actionPointCost = 3;
// Strong Punch
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_STRONG_PUNCH]);
hitDescription->requiredSkill = 55;
hitDescription->requiredStats[STAT_AGILITY] = 6;
hitDescription->minDamage = 1;
hitDescription->maxDamage = 2;
hitDescription->bonusDamage = 3;
hitDescription->actionPointCost = 3;
hitDescription->isPenetrate = false;
hitDescription->isSecondary = false;
// Hammer Punch
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_HAMMER_PUNCH]);
hitDescription->requiredLevel = 6;
hitDescription->requiredSkill = 75;
hitDescription->requiredStats[STAT_STRENGTH] = 5;
hitDescription->requiredStats[STAT_AGILITY] = 6;
hitDescription->minDamage = 1;
hitDescription->maxDamage = 2;
hitDescription->bonusDamage = 5;
hitDescription->bonusCriticalChance = 5;
hitDescription->actionPointCost = 3;
// Lightning Punch
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_HAYMAKER]);
hitDescription->requiredLevel = 9;
hitDescription->requiredSkill = 100;
hitDescription->requiredStats[STAT_STRENGTH] = 5;
hitDescription->requiredStats[STAT_AGILITY] = 7;
hitDescription->minDamage = 1;
hitDescription->maxDamage = 2;
hitDescription->bonusDamage = 7;
hitDescription->bonusCriticalChance = 15;
hitDescription->actionPointCost = 3;
// Chop Punch
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_JAB]);
hitDescription->requiredLevel = 5;
hitDescription->requiredSkill = 75;
hitDescription->requiredStats[STAT_STRENGTH] = 5;
hitDescription->requiredStats[STAT_AGILITY] = 7;
hitDescription->minDamage = 1;
hitDescription->maxDamage = 2;
hitDescription->bonusDamage = 3;
hitDescription->bonusCriticalChance = 10;
hitDescription->actionPointCost = 3;
hitDescription->isSecondary = true;
// Dragon Punch
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_PALM_STRIKE]);
hitDescription->requiredLevel = 12;
hitDescription->requiredSkill = 115;
hitDescription->requiredStats[STAT_STRENGTH] = 5;
hitDescription->requiredStats[STAT_AGILITY] = 7;
hitDescription->minDamage = 1;
hitDescription->maxDamage = 2;
hitDescription->bonusDamage = 7;
hitDescription->bonusCriticalChance = 20;
hitDescription->actionPointCost = 6;
hitDescription->isPenetrate = true;
hitDescription->isSecondary = true;
// Force Punch
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_PIERCING_STRIKE]);
hitDescription->requiredLevel = 16;
hitDescription->requiredSkill = 130;
hitDescription->requiredStats[STAT_STRENGTH] = 5;
hitDescription->requiredStats[STAT_AGILITY] = 7;
hitDescription->minDamage = 1;
hitDescription->maxDamage = 2;
hitDescription->bonusDamage = 10;
hitDescription->bonusCriticalChance = 40;
hitDescription->actionPointCost = 8;
hitDescription->isPenetrate = true;
hitDescription->isSecondary = true;
// Kick
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_KICK]);
hitDescription->minDamage = 1;
hitDescription->maxDamage = 2;
hitDescription->actionPointCost = 3;
// Strong Kick
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_STRONG_KICK]);
hitDescription->requiredSkill = 40;
hitDescription->requiredStats[STAT_AGILITY] = 6;
hitDescription->minDamage = 1;
hitDescription->maxDamage = 2;
hitDescription->bonusDamage = 5;
hitDescription->actionPointCost = 4;
// Snap Kick
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_SNAP_KICK]);
hitDescription->requiredLevel = 6;
hitDescription->requiredSkill = 60;
hitDescription->requiredStats[STAT_AGILITY] = 6;
hitDescription->minDamage = 1;
hitDescription->maxDamage = 2;
hitDescription->bonusDamage = 7;
hitDescription->actionPointCost = 4;
// Roundhouse Kick
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_POWER_KICK]);
hitDescription->requiredLevel = 9;
hitDescription->requiredSkill = 80;
hitDescription->requiredStats[STAT_STRENGTH] = 6;
hitDescription->requiredStats[STAT_AGILITY] = 6;
hitDescription->minDamage = 1;
hitDescription->maxDamage = 2;
hitDescription->bonusDamage = 9;
hitDescription->bonusCriticalChance = 5;
hitDescription->actionPointCost = 4;
// Kip Kick
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_HIP_KICK]);
hitDescription->requiredLevel = 6;
hitDescription->requiredSkill = 60;
hitDescription->requiredStats[STAT_STRENGTH] = 6;
hitDescription->requiredStats[STAT_AGILITY] = 7;
hitDescription->minDamage = 1;
hitDescription->maxDamage = 2;
hitDescription->bonusDamage = 7;
hitDescription->actionPointCost = 7;
hitDescription->isSecondary = true;
// Jump Kick
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_HOOK_KICK]);
hitDescription->requiredLevel = 12;
hitDescription->requiredSkill = 100;
hitDescription->requiredStats[STAT_STRENGTH] = 6;
hitDescription->requiredStats[STAT_AGILITY] = 7;
hitDescription->minDamage = 1;
hitDescription->maxDamage = 2;
hitDescription->bonusDamage = 9;
hitDescription->bonusCriticalChance = 10;
hitDescription->actionPointCost = 7;
hitDescription->isPenetrate = true;
hitDescription->isSecondary = true;
// Death Blossom Kick
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_PIERCING_KICK]);
hitDescription->requiredLevel = 15;
hitDescription->requiredSkill = 125;
hitDescription->requiredStats[STAT_STRENGTH] = 6;
hitDescription->requiredStats[STAT_AGILITY] = 8;
hitDescription->minDamage = 1;
hitDescription->maxDamage = 2;
hitDescription->bonusDamage = 12;
hitDescription->bonusCriticalChance = 50;
hitDescription->actionPointCost = 9;
hitDescription->isPenetrate = true;
hitDescription->isSecondary = true;
}
static void unarmedInitCustom()
{
char* unarmedFileName = NULL;
configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_UNARMED_FILE_KEY, &unarmedFileName);
if (unarmedFileName != NULL && *unarmedFileName == '\0') {
unarmedFileName = NULL;
}
if (unarmedFileName == NULL) {
return;
}
Config unarmedConfig;
if (configInit(&unarmedConfig)) {
if (configRead(&unarmedConfig, unarmedFileName, false)) {
char section[4];
char statKey[6];
for (int hitMode = 0; hitMode < HIT_MODE_COUNT; hitMode++) {
if (!isUnarmedHitMode(hitMode)) {
continue;
}
UnarmedHitDescription* hitDescription = &(gUnarmedHitDescriptions[hitMode]);
sprintf(section, "%d", hitMode);
configGetInt(&unarmedConfig, section, "ReqLevel", &(hitDescription->requiredLevel));
configGetInt(&unarmedConfig, section, "SkillLevel", &(hitDescription->requiredSkill));
configGetInt(&unarmedConfig, section, "MinDamage", &(hitDescription->minDamage));
configGetInt(&unarmedConfig, section, "MaxDamage", &(hitDescription->maxDamage));
configGetInt(&unarmedConfig, section, "BonusDamage", &(hitDescription->bonusDamage));
configGetInt(&unarmedConfig, section, "BonusCrit", &(hitDescription->bonusCriticalChance));
configGetInt(&unarmedConfig, section, "APCost", &(hitDescription->actionPointCost));
configGetBool(&unarmedConfig, section, "BonusDamage", &(hitDescription->isPenetrate));
configGetBool(&unarmedConfig, section, "Secondary", &(hitDescription->isSecondary));
for (int stat = 0; stat < PRIMARY_STAT_COUNT; stat++) {
sprintf(statKey, "Stat%d", stat);
configGetInt(&unarmedConfig, section, statKey, &(hitDescription->requiredStats[stat]));
}
}
}
configFree(&unarmedConfig);
}
}
int unarmedGetDamage(int hitMode, int* minDamagePtr, int* maxDamagePtr)
{
UnarmedHitDescription* hitDescription = &(gUnarmedHitDescriptions[hitMode]);
*minDamagePtr = hitDescription->minDamage;
*maxDamagePtr = hitDescription->maxDamage;
return hitDescription->bonusDamage;
}
int unarmedGetBonusCriticalChance(int hitMode)
{
UnarmedHitDescription* hitDescription = &(gUnarmedHitDescriptions[hitMode]);
return hitDescription->bonusCriticalChance;
}
int unarmedGetActionPointCost(int hitMode)
{
UnarmedHitDescription* hitDescription = &(gUnarmedHitDescriptions[hitMode]);
return hitDescription->actionPointCost;
}
bool unarmedIsPenetrating(int hitMode)
{
UnarmedHitDescription* hitDescription = &(gUnarmedHitDescriptions[hitMode]);
return hitDescription->isPenetrate;
}
int unarmedGetPunchHitMode(bool isSecondary)
{
int hitMode = unarmedGetHitModeInRange(FIRST_ADVANCED_PUNCH_HIT_MODE, LAST_ADVANCED_PUNCH_HIT_MODE, isSecondary);
if (hitMode == -1) {
hitMode = HIT_MODE_PUNCH;
}
return hitMode;
}
int unarmedGetKickHitMode(bool isSecondary)
{
int hitMode = unarmedGetHitModeInRange(FIRST_ADVANCED_KICK_HIT_MODE, LAST_ADVANCED_KICK_HIT_MODE, isSecondary);
if (hitMode == -1) {
hitMode = HIT_MODE_KICK;
}
return hitMode;
}
static int unarmedGetHitModeInRange(int firstHitMode, int lastHitMode, bool isSecondary)
{
int hitMode = -1;
int unarmed = skillGetValue(gDude, SKILL_UNARMED);
int level = pcGetStat(PC_STAT_LEVEL);
int stats[PRIMARY_STAT_COUNT];
for (int stat = 0; stat < PRIMARY_STAT_COUNT; stat++) {
stats[stat] = critterGetStat(gDude, stat);
}
for (int candidateHitMode = firstHitMode; candidateHitMode <= lastHitMode; candidateHitMode++) {
UnarmedHitDescription* hitDescription = &(gUnarmedHitDescriptions[candidateHitMode]);
if (isSecondary != hitDescription->isSecondary) {
continue;
}
if (unarmed < hitDescription->requiredSkill) {
continue;
}
if (level < hitDescription->requiredLevel) {
continue;
}
bool missingStats = false;
for (int stat = 0; stat < PRIMARY_STAT_COUNT; stat++) {
if (stats[stat] < hitDescription->requiredStats[stat]) {
missingStats = true;
break;
}
}
if (missingStats) {
continue;
}
hitMode = candidateHitMode;
}
return hitMode;
}

View File

@ -59,10 +59,24 @@ void _combatKillCritterOutsideCombat(Object* critter_obj, char* msg);
int criticalsGetValue(int killType, int hitLocation, int effect, int dataMember);
void criticalsSetValue(int killType, int hitLocation, int effect, int dataMember, int value);
void criticalsResetValue(int killType, int hitLocation, int effect, int dataMember);
int unarmedGetDamage(int hitMode, int* minDamagePtr, int* maxDamagePtr);
int unarmedGetBonusCriticalChance(int hitMode);
int unarmedGetActionPointCost(int hitMode);
bool unarmedIsPenetrating(int hitMode);
int unarmedGetPunchHitMode(bool isSecondary);
int unarmedGetKickHitMode(bool isSecondary);
bool unarmedIsPenetrating(int hitMode);
static inline bool isInCombat()
{
return (gCombatState & COMBAT_STATE_0x01) != 0;
}
static inline bool isUnarmedHitMode(int hitMode)
{
return hitMode == HIT_MODE_PUNCH
|| hitMode == HIT_MODE_KICK
|| (hitMode >= FIRST_ADVANCED_UNARMED_HIT_MODE && hitMode <= LAST_ADVANCED_UNARMED_HIT_MODE);
}
#endif /* COMBAT_H */

View File

@ -1264,30 +1264,9 @@ int interfaceUpdateItems(bool animated, int leftItemAction, int rightItemAction)
leftItemState->action = INTERFACE_ITEM_ACTION_PRIMARY;
leftItemState->itemFid = -1;
int unarmed = skillGetValue(gDude, SKILL_UNARMED);
int agility = critterGetStat(gDude, STAT_AGILITY);
int strength = critterGetStat(gDude, STAT_STRENGTH);
int level = pcGetStat(PC_STAT_LEVEL);
if (unarmed > 99 && agility > 6 && strength > 4 && level > 8) {
leftItemState->primaryHitMode = HIT_MODE_HAYMAKER;
} else if (unarmed > 74 && agility > 5 && strength > 4 && level > 5) {
leftItemState->primaryHitMode = HIT_MODE_HAMMER_PUNCH;
} else if (unarmed > 54 && agility > 5) {
leftItemState->primaryHitMode = HIT_MODE_STRONG_PUNCH;
} else {
leftItemState->primaryHitMode = HIT_MODE_PUNCH;
}
if (unarmed > 129 && agility > 6 && strength > 4 && level > 15) {
leftItemState->secondaryHitMode = HIT_MODE_PIERCING_STRIKE;
} else if (unarmed > 114 && agility > 6 && strength > 4 && level > 11) {
leftItemState->secondaryHitMode = HIT_MODE_PALM_STRIKE;
} else if (unarmed > 74 && agility > 6 && strength > 4 && level > 4) {
leftItemState->secondaryHitMode = HIT_MODE_JAB;
} else {
leftItemState->secondaryHitMode = HIT_MODE_PUNCH;
}
// SFALL
leftItemState->primaryHitMode = unarmedGetPunchHitMode(false);
leftItemState->secondaryHitMode = unarmedGetPunchHitMode(true);
}
}
@ -1324,30 +1303,9 @@ int interfaceUpdateItems(bool animated, int leftItemAction, int rightItemAction)
rightItemState->action = INTERFACE_ITEM_ACTION_PRIMARY;
rightItemState->itemFid = -1;
int unarmed = skillGetValue(gDude, SKILL_UNARMED);
int agility = critterGetStat(gDude, STAT_AGILITY);
int strength = critterGetStat(gDude, STAT_STRENGTH);
int level = pcGetStat(PC_STAT_LEVEL);
if (unarmed > 79 && agility > 5 && strength > 5 && level > 8) {
rightItemState->primaryHitMode = HIT_MODE_POWER_KICK;
} else if (unarmed > 59 && agility > 5 && level > 5) {
rightItemState->primaryHitMode = HIT_MODE_SNAP_KICK;
} else if (unarmed > 39 && agility > 5) {
rightItemState->primaryHitMode = HIT_MODE_STRONG_KICK;
} else {
rightItemState->primaryHitMode = HIT_MODE_KICK;
}
if (unarmed > 124 && agility > 7 && strength > 5 && level > 14) {
rightItemState->secondaryHitMode = HIT_MODE_PIERCING_KICK;
} else if (unarmed > 99 && agility > 6 && strength > 5 && level > 11) {
rightItemState->secondaryHitMode = HIT_MODE_HOOK_KICK;
} else if (unarmed > 59 && agility > 6 && strength > 5 && level > 5) {
rightItemState->secondaryHitMode = HIT_MODE_HIP_KICK;
} else {
rightItemState->secondaryHitMode = HIT_MODE_KICK;
}
// SFALL
rightItemState->primaryHitMode = unarmedGetKickHitMode(false);
rightItemState->secondaryHitMode = unarmedGetKickHitMode(true);
}
}

View File

@ -2433,6 +2433,11 @@ static void inventoryRenderSummary()
HIT_MODE_RIGHT_WEAPON_PRIMARY,
};
const int unarmedHitModes[2] = {
HIT_MODE_PUNCH,
HIT_MODE_KICK,
};
offset += 499 * fontGetLineHeight();
for (int index = 0; index < 2; index += 1) {
@ -2451,10 +2456,33 @@ static void inventoryRenderSummary()
// Unarmed dmg:
messageListItem.num = 24;
if (messageListGetItem(&gInventoryMessageList, &messageListItem)) {
// TODO: Figure out why it uses STAT_MELEE_DAMAGE instead of
// STAT_UNARMED_DAMAGE.
int damage = critterGetStat(_stack[0], STAT_MELEE_DAMAGE) + 2;
sprintf(formattedText, "%s 1-%d", messageListItem.text, damage);
// SFALL: Display the actual damage values of unarmed attacks.
// CE: Implementation is different.
int hitMode = unarmedHitModes[index];
if (_stack[0] == gDude) {
int actions[2];
interfaceGetItemActions(&(actions[0]), &(actions[1]));
bool isSecondary = actions[index] == INTERFACE_ITEM_ACTION_SECONDARY ||
actions[index] == INTERFACE_ITEM_ACTION_SECONDARY_AIMING;
if (index == HAND_LEFT) {
hitMode = unarmedGetPunchHitMode(isSecondary);
} else {
hitMode = unarmedGetKickHitMode(isSecondary);
}
}
// Formula is the same as in `weaponGetMeleeDamage`.
int minDamage;
int maxDamage;
int bonusDamage = unarmedGetDamage(hitMode, &minDamage, &maxDamage);
int meleeDamage = critterGetStat(_stack[0], STAT_MELEE_DAMAGE);
// TODO: Localize unarmed attack names.
sprintf(formattedText, "%s %d-%d",
messageListItem.text,
bonusDamage + minDamage,
bonusDamage + meleeDamage + maxDamage);
}
fontDrawText(windowBuffer + offset, formattedText, 120, 499, _colorTable[992]);

View File

@ -1261,7 +1261,7 @@ int weaponGetMeleeDamage(Object* critter, int hitMode)
int minDamage = 0;
int maxDamage = 0;
int meleeDamage = 0;
int unarmedDamage = 0;
int bonusDamage = 0;
// NOTE: Uninline.
Object* weapon = critterGetWeaponForHitMode(critter, hitMode);
@ -1278,38 +1278,12 @@ int weaponGetMeleeDamage(Object* critter, int hitMode)
meleeDamage = critterGetStat(critter, STAT_MELEE_DAMAGE);
}
} else {
minDamage = 1;
maxDamage = critterGetStat(critter, STAT_MELEE_DAMAGE) + 2;
switch (hitMode) {
case HIT_MODE_STRONG_PUNCH:
case HIT_MODE_JAB:
unarmedDamage = 3;
break;
case HIT_MODE_HAMMER_PUNCH:
case HIT_MODE_STRONG_KICK:
unarmedDamage = 4;
break;
case HIT_MODE_HAYMAKER:
case HIT_MODE_PALM_STRIKE:
case HIT_MODE_SNAP_KICK:
case HIT_MODE_HIP_KICK:
unarmedDamage = 7;
break;
case HIT_MODE_POWER_KICK:
case HIT_MODE_HOOK_KICK:
unarmedDamage = 9;
break;
case HIT_MODE_PIERCING_STRIKE:
unarmedDamage = 10;
break;
case HIT_MODE_PIERCING_KICK:
unarmedDamage = 12;
break;
}
// SFALL
bonusDamage = unarmedGetDamage(hitMode, &minDamage, &maxDamage);
meleeDamage = critterGetStat(critter, STAT_MELEE_DAMAGE);
}
return randomBetween(unarmedDamage + minDamage, unarmedDamage + meleeDamage + maxDamage);
return randomBetween(bonusDamage + minDamage, bonusDamage + meleeDamage + maxDamage);
}
// 0x478570
@ -1678,28 +1652,11 @@ int _item_w_mp_cost(Object* critter, int hitMode, bool aiming)
return 2;
}
switch (hitMode) {
case HIT_MODE_PALM_STRIKE:
actionPoints = 6;
break;
case HIT_MODE_PIERCING_STRIKE:
actionPoints = 8;
break;
case HIT_MODE_STRONG_KICK:
case HIT_MODE_SNAP_KICK:
case HIT_MODE_POWER_KICK:
actionPoints = 4;
break;
case HIT_MODE_HIP_KICK:
case HIT_MODE_HOOK_KICK:
actionPoints = 7;
break;
case HIT_MODE_PIERCING_KICK:
actionPoints = 9;
break;
default:
// TODO: Inverse conditions.
if (weapon != NULL && hitMode != HIT_MODE_PUNCH && hitMode != HIT_MODE_KICK && hitMode != HIT_MODE_STRONG_PUNCH && hitMode != HIT_MODE_HAMMER_PUNCH && hitMode != HIT_MODE_HAYMAKER) {
// CE: The entire function is different in Sfall.
if (isUnarmedHitMode(hitMode)) {
actionPoints = unarmedGetActionPointCost(hitMode);
} else {
if (weapon != NULL) {
if (hitMode == HIT_MODE_LEFT_WEAPON_PRIMARY || hitMode == HIT_MODE_RIGHT_WEAPON_PRIMARY) {
// NOTE: Uninline.
actionPoints = weaponGetActionPointCost1(weapon);
@ -1718,7 +1675,6 @@ int _item_w_mp_cost(Object* critter, int hitMode, bool aiming)
} else {
actionPoints = 3;
}
break;
}
if (critter == gDude) {

View File

@ -41,6 +41,7 @@
#define SFALL_CONFIG_PLASTIC_EXPLOSIVE_MAX_DAMAGE_KEY "PlasticExplosive_DmgMax"
#define SFALL_CONFIG_EXPLOSION_EMITS_LIGHT_KEY "ExplosionsEmitLight"
#define SFALL_CONFIG_CITY_REPUTATION_LIST_KEY "CityRepsList"
#define SFALL_CONFIG_UNARMED_FILE_KEY "UnarmedFile"
#define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_MULTIPLIER 1
#define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR 3

View File

@ -772,6 +772,12 @@ int pcAddExperienceWithOptions(int xp, bool a2)
interfaceRenderHitPoints(false);
// SFALL: Update unarmed attack after leveling up.
int leftItemAction;
int rightItemAction;
interfaceGetItemActions(&leftItemAction, &rightItemAction);
interfaceUpdateItems(false, leftItemAction, rightItemAction);
if (a2) {
_partyMemberIncLevels();
}