Add unarmed hits improvements (#29)
This commit is contained in:
parent
2859410d4b
commit
87289a34c0
343
src/combat.cc
343
src/combat.cc
|
@ -54,6 +54,19 @@ typedef struct CombatAiInfo {
|
|||
int lastMove;
|
||||
} CombatAiInfo;
|
||||
|
||||
typedef struct UnarmedHitDescription {
|
||||
int requiredLevel;
|
||||
int requiredSkill;
|
||||
int requiredStats[PRIMARY_STAT_COUNT];
|
||||
int minDamage;
|
||||
int maxDamage;
|
||||
int bonusDamage;
|
||||
int bonusCriticalChance;
|
||||
int actionPointCost;
|
||||
bool isPenetrate;
|
||||
bool isSecondary;
|
||||
} UnarmedHitDescription;
|
||||
|
||||
static bool _combat_safety_invalidate_weapon_func(Object* critter, Object* weapon, int hitMode, Object* a4, int* a5, Object* a6);
|
||||
static int aiInfoCopy(int srcIndex, int destIndex);
|
||||
static void _combat_begin(Object* a1);
|
||||
|
@ -96,6 +109,10 @@ static void criticalsReset();
|
|||
static void criticalsExit();
|
||||
static void burstModInit();
|
||||
static int burstModComputeRounds(int totalRounds, int* centerRoundsPtr, int* leftRoundsPtr, int* rightRoundsPtr);
|
||||
static void unarmedInit();
|
||||
static void unarmedInitVanilla();
|
||||
static void unarmedInitCustom();
|
||||
static int unarmedGetHitModeInRange(int firstHitMode, int lastHitMode, bool isSecondary);
|
||||
|
||||
// 0x500B50
|
||||
static char _a_1[] = ".";
|
||||
|
@ -1932,6 +1949,7 @@ static int gBurstModCenterMultiplier = SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_MUL
|
|||
static int gBurstModCenterDivisor = SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR;
|
||||
static int gBurstModTargetMultiplier = SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_MULTIPLIER;
|
||||
static int gBurstModTargetDivisor = SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_DIVISOR;
|
||||
static UnarmedHitDescription gUnarmedHitDescriptions[HIT_MODE_COUNT];
|
||||
|
||||
// combat_init
|
||||
// 0x420CC0
|
||||
|
@ -1974,6 +1992,7 @@ int combatInit()
|
|||
// SFALL
|
||||
criticalsInit();
|
||||
burstModInit();
|
||||
unarmedInit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -3757,13 +3776,12 @@ static int attackCompute(Attack* attack)
|
|||
damageMultiplier = 4;
|
||||
}
|
||||
|
||||
if (((attack->hitMode == HIT_MODE_HAMMER_PUNCH || attack->hitMode == HIT_MODE_POWER_KICK) && randomBetween(1, 100) <= 5)
|
||||
|| ((attack->hitMode == HIT_MODE_JAB || attack->hitMode == HIT_MODE_HOOK_KICK) && randomBetween(1, 100) <= 10)
|
||||
|| (attack->hitMode == HIT_MODE_HAYMAKER && randomBetween(1, 100) <= 15)
|
||||
|| (attack->hitMode == HIT_MODE_PALM_STRIKE && randomBetween(1, 100) <= 20)
|
||||
|| (attack->hitMode == HIT_MODE_PIERCING_STRIKE && randomBetween(1, 100) <= 40)
|
||||
|| (attack->hitMode == HIT_MODE_PIERCING_KICK && randomBetween(1, 100) <= 50)) {
|
||||
roll = ROLL_CRITICAL_SUCCESS;
|
||||
// SFALL
|
||||
int bonusCriticalChance = unarmedGetBonusCriticalChance(attack->hitMode);
|
||||
if (bonusCriticalChance != 0) {
|
||||
if (randomBetween(1, 100) <= bonusCriticalChance) {
|
||||
roll = ROLL_CRITICAL_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4203,7 +4221,7 @@ static int attackDetermineToHit(Object* attacker, int tile, Object* defender, in
|
|||
bool isRangedWeapon = false;
|
||||
|
||||
int accuracy;
|
||||
if (weapon == NULL || hitMode == HIT_MODE_PUNCH || hitMode == HIT_MODE_KICK || (hitMode >= FIRST_ADVANCED_UNARMED_HIT_MODE && hitMode <= LAST_ADVANCED_UNARMED_HIT_MODE)) {
|
||||
if (weapon == NULL || isUnarmedHitMode(hitMode)) {
|
||||
accuracy = skillGetValue(attacker, SKILL_UNARMED);
|
||||
} else {
|
||||
accuracy = _item_w_skill_level(attacker, hitMode);
|
||||
|
@ -4413,11 +4431,9 @@ static void attackComputeDamage(Attack* attack, int ammoQuantity, int bonusDamag
|
|||
damageThreshold = 20 * damageThreshold / 100;
|
||||
damageResistance = 20 * damageResistance / 100;
|
||||
} else {
|
||||
// SFALL
|
||||
if (weaponGetPerk(attack->weapon) == PERK_WEAPON_PENETRATE
|
||||
|| attack->hitMode == HIT_MODE_PALM_STRIKE
|
||||
|| attack->hitMode == HIT_MODE_PIERCING_STRIKE
|
||||
|| attack->hitMode == HIT_MODE_HOOK_KICK
|
||||
|| attack->hitMode == HIT_MODE_PIERCING_KICK) {
|
||||
|| unarmedIsPenetrating(attack->hitMode)) {
|
||||
damageThreshold = 20 * damageThreshold / 100;
|
||||
}
|
||||
|
||||
|
@ -6189,3 +6205,306 @@ static int burstModComputeRounds(int totalRounds, int* centerRoundsPtr, int* lef
|
|||
|
||||
return mainTargetRounds;
|
||||
}
|
||||
|
||||
static void unarmedInit()
|
||||
{
|
||||
unarmedInitVanilla();
|
||||
unarmedInitCustom();
|
||||
}
|
||||
|
||||
static void unarmedInitVanilla()
|
||||
{
|
||||
UnarmedHitDescription* hitDescription;
|
||||
|
||||
// Punch
|
||||
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_PUNCH]);
|
||||
hitDescription->minDamage = 1;
|
||||
hitDescription->maxDamage = 2;
|
||||
hitDescription->actionPointCost = 3;
|
||||
|
||||
// Strong Punch
|
||||
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_STRONG_PUNCH]);
|
||||
hitDescription->requiredSkill = 55;
|
||||
hitDescription->requiredStats[STAT_AGILITY] = 6;
|
||||
hitDescription->minDamage = 1;
|
||||
hitDescription->maxDamage = 2;
|
||||
hitDescription->bonusDamage = 3;
|
||||
hitDescription->actionPointCost = 3;
|
||||
hitDescription->isPenetrate = false;
|
||||
hitDescription->isSecondary = false;
|
||||
|
||||
// Hammer Punch
|
||||
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_HAMMER_PUNCH]);
|
||||
hitDescription->requiredLevel = 6;
|
||||
hitDescription->requiredSkill = 75;
|
||||
hitDescription->requiredStats[STAT_STRENGTH] = 5;
|
||||
hitDescription->requiredStats[STAT_AGILITY] = 6;
|
||||
hitDescription->minDamage = 1;
|
||||
hitDescription->maxDamage = 2;
|
||||
hitDescription->bonusDamage = 5;
|
||||
hitDescription->bonusCriticalChance = 5;
|
||||
hitDescription->actionPointCost = 3;
|
||||
|
||||
// Lightning Punch
|
||||
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_HAYMAKER]);
|
||||
hitDescription->requiredLevel = 9;
|
||||
hitDescription->requiredSkill = 100;
|
||||
hitDescription->requiredStats[STAT_STRENGTH] = 5;
|
||||
hitDescription->requiredStats[STAT_AGILITY] = 7;
|
||||
hitDescription->minDamage = 1;
|
||||
hitDescription->maxDamage = 2;
|
||||
hitDescription->bonusDamage = 7;
|
||||
hitDescription->bonusCriticalChance = 15;
|
||||
hitDescription->actionPointCost = 3;
|
||||
|
||||
// Chop Punch
|
||||
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_JAB]);
|
||||
hitDescription->requiredLevel = 5;
|
||||
hitDescription->requiredSkill = 75;
|
||||
hitDescription->requiredStats[STAT_STRENGTH] = 5;
|
||||
hitDescription->requiredStats[STAT_AGILITY] = 7;
|
||||
hitDescription->minDamage = 1;
|
||||
hitDescription->maxDamage = 2;
|
||||
hitDescription->bonusDamage = 3;
|
||||
hitDescription->bonusCriticalChance = 10;
|
||||
hitDescription->actionPointCost = 3;
|
||||
hitDescription->isSecondary = true;
|
||||
|
||||
// Dragon Punch
|
||||
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_PALM_STRIKE]);
|
||||
hitDescription->requiredLevel = 12;
|
||||
hitDescription->requiredSkill = 115;
|
||||
hitDescription->requiredStats[STAT_STRENGTH] = 5;
|
||||
hitDescription->requiredStats[STAT_AGILITY] = 7;
|
||||
hitDescription->minDamage = 1;
|
||||
hitDescription->maxDamage = 2;
|
||||
hitDescription->bonusDamage = 7;
|
||||
hitDescription->bonusCriticalChance = 20;
|
||||
hitDescription->actionPointCost = 6;
|
||||
hitDescription->isPenetrate = true;
|
||||
hitDescription->isSecondary = true;
|
||||
|
||||
// Force Punch
|
||||
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_PIERCING_STRIKE]);
|
||||
hitDescription->requiredLevel = 16;
|
||||
hitDescription->requiredSkill = 130;
|
||||
hitDescription->requiredStats[STAT_STRENGTH] = 5;
|
||||
hitDescription->requiredStats[STAT_AGILITY] = 7;
|
||||
hitDescription->minDamage = 1;
|
||||
hitDescription->maxDamage = 2;
|
||||
hitDescription->bonusDamage = 10;
|
||||
hitDescription->bonusCriticalChance = 40;
|
||||
hitDescription->actionPointCost = 8;
|
||||
hitDescription->isPenetrate = true;
|
||||
hitDescription->isSecondary = true;
|
||||
|
||||
// Kick
|
||||
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_KICK]);
|
||||
hitDescription->minDamage = 1;
|
||||
hitDescription->maxDamage = 2;
|
||||
hitDescription->actionPointCost = 3;
|
||||
|
||||
// Strong Kick
|
||||
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_STRONG_KICK]);
|
||||
hitDescription->requiredSkill = 40;
|
||||
hitDescription->requiredStats[STAT_AGILITY] = 6;
|
||||
hitDescription->minDamage = 1;
|
||||
hitDescription->maxDamage = 2;
|
||||
hitDescription->bonusDamage = 5;
|
||||
hitDescription->actionPointCost = 4;
|
||||
|
||||
// Snap Kick
|
||||
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_SNAP_KICK]);
|
||||
hitDescription->requiredLevel = 6;
|
||||
hitDescription->requiredSkill = 60;
|
||||
hitDescription->requiredStats[STAT_AGILITY] = 6;
|
||||
hitDescription->minDamage = 1;
|
||||
hitDescription->maxDamage = 2;
|
||||
hitDescription->bonusDamage = 7;
|
||||
hitDescription->actionPointCost = 4;
|
||||
|
||||
// Roundhouse Kick
|
||||
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_POWER_KICK]);
|
||||
hitDescription->requiredLevel = 9;
|
||||
hitDescription->requiredSkill = 80;
|
||||
hitDescription->requiredStats[STAT_STRENGTH] = 6;
|
||||
hitDescription->requiredStats[STAT_AGILITY] = 6;
|
||||
hitDescription->minDamage = 1;
|
||||
hitDescription->maxDamage = 2;
|
||||
hitDescription->bonusDamage = 9;
|
||||
hitDescription->bonusCriticalChance = 5;
|
||||
hitDescription->actionPointCost = 4;
|
||||
|
||||
// Kip Kick
|
||||
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_HIP_KICK]);
|
||||
hitDescription->requiredLevel = 6;
|
||||
hitDescription->requiredSkill = 60;
|
||||
hitDescription->requiredStats[STAT_STRENGTH] = 6;
|
||||
hitDescription->requiredStats[STAT_AGILITY] = 7;
|
||||
hitDescription->minDamage = 1;
|
||||
hitDescription->maxDamage = 2;
|
||||
hitDescription->bonusDamage = 7;
|
||||
hitDescription->actionPointCost = 7;
|
||||
hitDescription->isSecondary = true;
|
||||
|
||||
// Jump Kick
|
||||
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_HOOK_KICK]);
|
||||
hitDescription->requiredLevel = 12;
|
||||
hitDescription->requiredSkill = 100;
|
||||
hitDescription->requiredStats[STAT_STRENGTH] = 6;
|
||||
hitDescription->requiredStats[STAT_AGILITY] = 7;
|
||||
hitDescription->minDamage = 1;
|
||||
hitDescription->maxDamage = 2;
|
||||
hitDescription->bonusDamage = 9;
|
||||
hitDescription->bonusCriticalChance = 10;
|
||||
hitDescription->actionPointCost = 7;
|
||||
hitDescription->isPenetrate = true;
|
||||
hitDescription->isSecondary = true;
|
||||
|
||||
// Death Blossom Kick
|
||||
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_PIERCING_KICK]);
|
||||
hitDescription->requiredLevel = 15;
|
||||
hitDescription->requiredSkill = 125;
|
||||
hitDescription->requiredStats[STAT_STRENGTH] = 6;
|
||||
hitDescription->requiredStats[STAT_AGILITY] = 8;
|
||||
hitDescription->minDamage = 1;
|
||||
hitDescription->maxDamage = 2;
|
||||
hitDescription->bonusDamage = 12;
|
||||
hitDescription->bonusCriticalChance = 50;
|
||||
hitDescription->actionPointCost = 9;
|
||||
hitDescription->isPenetrate = true;
|
||||
hitDescription->isSecondary = true;
|
||||
}
|
||||
|
||||
static void unarmedInitCustom()
|
||||
{
|
||||
char* unarmedFileName = NULL;
|
||||
configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_UNARMED_FILE_KEY, &unarmedFileName);
|
||||
if (unarmedFileName != NULL && *unarmedFileName == '\0') {
|
||||
unarmedFileName = NULL;
|
||||
}
|
||||
|
||||
if (unarmedFileName == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
Config unarmedConfig;
|
||||
if (configInit(&unarmedConfig)) {
|
||||
if (configRead(&unarmedConfig, unarmedFileName, false)) {
|
||||
char section[4];
|
||||
char statKey[6];
|
||||
|
||||
for (int hitMode = 0; hitMode < HIT_MODE_COUNT; hitMode++) {
|
||||
if (!isUnarmedHitMode(hitMode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
UnarmedHitDescription* hitDescription = &(gUnarmedHitDescriptions[hitMode]);
|
||||
sprintf(section, "%d", hitMode);
|
||||
|
||||
configGetInt(&unarmedConfig, section, "ReqLevel", &(hitDescription->requiredLevel));
|
||||
configGetInt(&unarmedConfig, section, "SkillLevel", &(hitDescription->requiredSkill));
|
||||
configGetInt(&unarmedConfig, section, "MinDamage", &(hitDescription->minDamage));
|
||||
configGetInt(&unarmedConfig, section, "MaxDamage", &(hitDescription->maxDamage));
|
||||
configGetInt(&unarmedConfig, section, "BonusDamage", &(hitDescription->bonusDamage));
|
||||
configGetInt(&unarmedConfig, section, "BonusCrit", &(hitDescription->bonusCriticalChance));
|
||||
configGetInt(&unarmedConfig, section, "APCost", &(hitDescription->actionPointCost));
|
||||
configGetBool(&unarmedConfig, section, "BonusDamage", &(hitDescription->isPenetrate));
|
||||
configGetBool(&unarmedConfig, section, "Secondary", &(hitDescription->isSecondary));
|
||||
|
||||
for (int stat = 0; stat < PRIMARY_STAT_COUNT; stat++) {
|
||||
sprintf(statKey, "Stat%d", stat);
|
||||
configGetInt(&unarmedConfig, section, statKey, &(hitDescription->requiredStats[stat]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configFree(&unarmedConfig);
|
||||
}
|
||||
}
|
||||
|
||||
int unarmedGetDamage(int hitMode, int* minDamagePtr, int* maxDamagePtr)
|
||||
{
|
||||
UnarmedHitDescription* hitDescription = &(gUnarmedHitDescriptions[hitMode]);
|
||||
*minDamagePtr = hitDescription->minDamage;
|
||||
*maxDamagePtr = hitDescription->maxDamage;
|
||||
return hitDescription->bonusDamage;
|
||||
}
|
||||
|
||||
int unarmedGetBonusCriticalChance(int hitMode)
|
||||
{
|
||||
UnarmedHitDescription* hitDescription = &(gUnarmedHitDescriptions[hitMode]);
|
||||
return hitDescription->bonusCriticalChance;
|
||||
}
|
||||
|
||||
int unarmedGetActionPointCost(int hitMode)
|
||||
{
|
||||
UnarmedHitDescription* hitDescription = &(gUnarmedHitDescriptions[hitMode]);
|
||||
return hitDescription->actionPointCost;
|
||||
}
|
||||
|
||||
bool unarmedIsPenetrating(int hitMode)
|
||||
{
|
||||
UnarmedHitDescription* hitDescription = &(gUnarmedHitDescriptions[hitMode]);
|
||||
return hitDescription->isPenetrate;
|
||||
}
|
||||
|
||||
int unarmedGetPunchHitMode(bool isSecondary)
|
||||
{
|
||||
int hitMode = unarmedGetHitModeInRange(FIRST_ADVANCED_PUNCH_HIT_MODE, LAST_ADVANCED_PUNCH_HIT_MODE, isSecondary);
|
||||
if (hitMode == -1) {
|
||||
hitMode = HIT_MODE_PUNCH;
|
||||
}
|
||||
return hitMode;
|
||||
}
|
||||
|
||||
int unarmedGetKickHitMode(bool isSecondary)
|
||||
{
|
||||
int hitMode = unarmedGetHitModeInRange(FIRST_ADVANCED_KICK_HIT_MODE, LAST_ADVANCED_KICK_HIT_MODE, isSecondary);
|
||||
if (hitMode == -1) {
|
||||
hitMode = HIT_MODE_KICK;
|
||||
}
|
||||
return hitMode;
|
||||
}
|
||||
|
||||
static int unarmedGetHitModeInRange(int firstHitMode, int lastHitMode, bool isSecondary)
|
||||
{
|
||||
int hitMode = -1;
|
||||
|
||||
int unarmed = skillGetValue(gDude, SKILL_UNARMED);
|
||||
int level = pcGetStat(PC_STAT_LEVEL);
|
||||
int stats[PRIMARY_STAT_COUNT];
|
||||
for (int stat = 0; stat < PRIMARY_STAT_COUNT; stat++) {
|
||||
stats[stat] = critterGetStat(gDude, stat);
|
||||
}
|
||||
|
||||
for (int candidateHitMode = firstHitMode; candidateHitMode <= lastHitMode; candidateHitMode++) {
|
||||
UnarmedHitDescription* hitDescription = &(gUnarmedHitDescriptions[candidateHitMode]);
|
||||
if (isSecondary != hitDescription->isSecondary) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (unarmed < hitDescription->requiredSkill) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (level < hitDescription->requiredLevel) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool missingStats = false;
|
||||
for (int stat = 0; stat < PRIMARY_STAT_COUNT; stat++) {
|
||||
if (stats[stat] < hitDescription->requiredStats[stat]) {
|
||||
missingStats = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (missingStats) {
|
||||
continue;
|
||||
}
|
||||
|
||||
hitMode = candidateHitMode;
|
||||
}
|
||||
|
||||
return hitMode;
|
||||
}
|
||||
|
|
14
src/combat.h
14
src/combat.h
|
@ -59,10 +59,24 @@ void _combatKillCritterOutsideCombat(Object* critter_obj, char* msg);
|
|||
int criticalsGetValue(int killType, int hitLocation, int effect, int dataMember);
|
||||
void criticalsSetValue(int killType, int hitLocation, int effect, int dataMember, int value);
|
||||
void criticalsResetValue(int killType, int hitLocation, int effect, int dataMember);
|
||||
int unarmedGetDamage(int hitMode, int* minDamagePtr, int* maxDamagePtr);
|
||||
int unarmedGetBonusCriticalChance(int hitMode);
|
||||
int unarmedGetActionPointCost(int hitMode);
|
||||
bool unarmedIsPenetrating(int hitMode);
|
||||
int unarmedGetPunchHitMode(bool isSecondary);
|
||||
int unarmedGetKickHitMode(bool isSecondary);
|
||||
bool unarmedIsPenetrating(int hitMode);
|
||||
|
||||
static inline bool isInCombat()
|
||||
{
|
||||
return (gCombatState & COMBAT_STATE_0x01) != 0;
|
||||
}
|
||||
|
||||
static inline bool isUnarmedHitMode(int hitMode)
|
||||
{
|
||||
return hitMode == HIT_MODE_PUNCH
|
||||
|| hitMode == HIT_MODE_KICK
|
||||
|| (hitMode >= FIRST_ADVANCED_UNARMED_HIT_MODE && hitMode <= LAST_ADVANCED_UNARMED_HIT_MODE);
|
||||
}
|
||||
|
||||
#endif /* COMBAT_H */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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]);
|
||||
|
|
64
src/item.cc
64
src/item.cc
|
@ -1261,7 +1261,7 @@ int weaponGetMeleeDamage(Object* critter, int hitMode)
|
|||
int minDamage = 0;
|
||||
int maxDamage = 0;
|
||||
int meleeDamage = 0;
|
||||
int unarmedDamage = 0;
|
||||
int bonusDamage = 0;
|
||||
|
||||
// NOTE: Uninline.
|
||||
Object* weapon = critterGetWeaponForHitMode(critter, hitMode);
|
||||
|
@ -1278,38 +1278,12 @@ int weaponGetMeleeDamage(Object* critter, int hitMode)
|
|||
meleeDamage = critterGetStat(critter, STAT_MELEE_DAMAGE);
|
||||
}
|
||||
} else {
|
||||
minDamage = 1;
|
||||
maxDamage = critterGetStat(critter, STAT_MELEE_DAMAGE) + 2;
|
||||
|
||||
switch (hitMode) {
|
||||
case HIT_MODE_STRONG_PUNCH:
|
||||
case HIT_MODE_JAB:
|
||||
unarmedDamage = 3;
|
||||
break;
|
||||
case HIT_MODE_HAMMER_PUNCH:
|
||||
case HIT_MODE_STRONG_KICK:
|
||||
unarmedDamage = 4;
|
||||
break;
|
||||
case HIT_MODE_HAYMAKER:
|
||||
case HIT_MODE_PALM_STRIKE:
|
||||
case HIT_MODE_SNAP_KICK:
|
||||
case HIT_MODE_HIP_KICK:
|
||||
unarmedDamage = 7;
|
||||
break;
|
||||
case HIT_MODE_POWER_KICK:
|
||||
case HIT_MODE_HOOK_KICK:
|
||||
unarmedDamage = 9;
|
||||
break;
|
||||
case HIT_MODE_PIERCING_STRIKE:
|
||||
unarmedDamage = 10;
|
||||
break;
|
||||
case HIT_MODE_PIERCING_KICK:
|
||||
unarmedDamage = 12;
|
||||
break;
|
||||
}
|
||||
// SFALL
|
||||
bonusDamage = unarmedGetDamage(hitMode, &minDamage, &maxDamage);
|
||||
meleeDamage = critterGetStat(critter, STAT_MELEE_DAMAGE);
|
||||
}
|
||||
|
||||
return randomBetween(unarmedDamage + minDamage, unarmedDamage + meleeDamage + maxDamage);
|
||||
return randomBetween(bonusDamage + minDamage, bonusDamage + meleeDamage + maxDamage);
|
||||
}
|
||||
|
||||
// 0x478570
|
||||
|
@ -1678,28 +1652,11 @@ int _item_w_mp_cost(Object* critter, int hitMode, bool aiming)
|
|||
return 2;
|
||||
}
|
||||
|
||||
switch (hitMode) {
|
||||
case HIT_MODE_PALM_STRIKE:
|
||||
actionPoints = 6;
|
||||
break;
|
||||
case HIT_MODE_PIERCING_STRIKE:
|
||||
actionPoints = 8;
|
||||
break;
|
||||
case HIT_MODE_STRONG_KICK:
|
||||
case HIT_MODE_SNAP_KICK:
|
||||
case HIT_MODE_POWER_KICK:
|
||||
actionPoints = 4;
|
||||
break;
|
||||
case HIT_MODE_HIP_KICK:
|
||||
case HIT_MODE_HOOK_KICK:
|
||||
actionPoints = 7;
|
||||
break;
|
||||
case HIT_MODE_PIERCING_KICK:
|
||||
actionPoints = 9;
|
||||
break;
|
||||
default:
|
||||
// TODO: Inverse conditions.
|
||||
if (weapon != NULL && hitMode != HIT_MODE_PUNCH && hitMode != HIT_MODE_KICK && hitMode != HIT_MODE_STRONG_PUNCH && hitMode != HIT_MODE_HAMMER_PUNCH && hitMode != HIT_MODE_HAYMAKER) {
|
||||
// CE: The entire function is different in Sfall.
|
||||
if (isUnarmedHitMode(hitMode)) {
|
||||
actionPoints = unarmedGetActionPointCost(hitMode);
|
||||
} else {
|
||||
if (weapon != NULL) {
|
||||
if (hitMode == HIT_MODE_LEFT_WEAPON_PRIMARY || hitMode == HIT_MODE_RIGHT_WEAPON_PRIMARY) {
|
||||
// NOTE: Uninline.
|
||||
actionPoints = weaponGetActionPointCost1(weapon);
|
||||
|
@ -1718,7 +1675,6 @@ int _item_w_mp_cost(Object* critter, int hitMode, bool aiming)
|
|||
} else {
|
||||
actionPoints = 3;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (critter == gDude) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue