Skip to content

Commit 87289a3

Browse files
committed
Add unarmed hits improvements (#29)
1 parent 2859410 commit 87289a3

File tree

7 files changed

+400
-118
lines changed

7 files changed

+400
-118
lines changed

src/combat.cc

Lines changed: 331 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,19 @@ typedef struct CombatAiInfo {
5454
int lastMove;
5555
} CombatAiInfo;
5656

57+
typedef struct UnarmedHitDescription {
58+
int requiredLevel;
59+
int requiredSkill;
60+
int requiredStats[PRIMARY_STAT_COUNT];
61+
int minDamage;
62+
int maxDamage;
63+
int bonusDamage;
64+
int bonusCriticalChance;
65+
int actionPointCost;
66+
bool isPenetrate;
67+
bool isSecondary;
68+
} UnarmedHitDescription;
69+
5770
static bool _combat_safety_invalidate_weapon_func(Object* critter, Object* weapon, int hitMode, Object* a4, int* a5, Object* a6);
5871
static int aiInfoCopy(int srcIndex, int destIndex);
5972
static void _combat_begin(Object* a1);
@@ -96,6 +109,10 @@ static void criticalsReset();
96109
static void criticalsExit();
97110
static void burstModInit();
98111
static int burstModComputeRounds(int totalRounds, int* centerRoundsPtr, int* leftRoundsPtr, int* rightRoundsPtr);
112+
static void unarmedInit();
113+
static void unarmedInitVanilla();
114+
static void unarmedInitCustom();
115+
static int unarmedGetHitModeInRange(int firstHitMode, int lastHitMode, bool isSecondary);
99116

100117
// 0x500B50
101118
static char _a_1[] = ".";
@@ -1932,6 +1949,7 @@ static int gBurstModCenterMultiplier = SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_MUL
19321949
static int gBurstModCenterDivisor = SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR;
19331950
static int gBurstModTargetMultiplier = SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_MULTIPLIER;
19341951
static int gBurstModTargetDivisor = SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_DIVISOR;
1952+
static UnarmedHitDescription gUnarmedHitDescriptions[HIT_MODE_COUNT];
19351953

19361954
// combat_init
19371955
// 0x420CC0
@@ -1974,6 +1992,7 @@ int combatInit()
19741992
// SFALL
19751993
criticalsInit();
19761994
burstModInit();
1995+
unarmedInit();
19771996

19781997
return 0;
19791998
}
@@ -3757,13 +3776,12 @@ static int attackCompute(Attack* attack)
37573776
damageMultiplier = 4;
37583777
}
37593778

3760-
if (((attack->hitMode == HIT_MODE_HAMMER_PUNCH || attack->hitMode == HIT_MODE_POWER_KICK) && randomBetween(1, 100) <= 5)
3761-
|| ((attack->hitMode == HIT_MODE_JAB || attack->hitMode == HIT_MODE_HOOK_KICK) && randomBetween(1, 100) <= 10)
3762-
|| (attack->hitMode == HIT_MODE_HAYMAKER && randomBetween(1, 100) <= 15)
3763-
|| (attack->hitMode == HIT_MODE_PALM_STRIKE && randomBetween(1, 100) <= 20)
3764-
|| (attack->hitMode == HIT_MODE_PIERCING_STRIKE && randomBetween(1, 100) <= 40)
3765-
|| (attack->hitMode == HIT_MODE_PIERCING_KICK && randomBetween(1, 100) <= 50)) {
3766-
roll = ROLL_CRITICAL_SUCCESS;
3779+
// SFALL
3780+
int bonusCriticalChance = unarmedGetBonusCriticalChance(attack->hitMode);
3781+
if (bonusCriticalChance != 0) {
3782+
if (randomBetween(1, 100) <= bonusCriticalChance) {
3783+
roll = ROLL_CRITICAL_SUCCESS;
3784+
}
37673785
}
37683786
}
37693787
}
@@ -4203,7 +4221,7 @@ static int attackDetermineToHit(Object* attacker, int tile, Object* defender, in
42034221
bool isRangedWeapon = false;
42044222

42054223
int accuracy;
4206-
if (weapon == NULL || hitMode == HIT_MODE_PUNCH || hitMode == HIT_MODE_KICK || (hitMode >= FIRST_ADVANCED_UNARMED_HIT_MODE && hitMode <= LAST_ADVANCED_UNARMED_HIT_MODE)) {
4224+
if (weapon == NULL || isUnarmedHitMode(hitMode)) {
42074225
accuracy = skillGetValue(attacker, SKILL_UNARMED);
42084226
} else {
42094227
accuracy = _item_w_skill_level(attacker, hitMode);
@@ -4413,11 +4431,9 @@ static void attackComputeDamage(Attack* attack, int ammoQuantity, int bonusDamag
44134431
damageThreshold = 20 * damageThreshold / 100;
44144432
damageResistance = 20 * damageResistance / 100;
44154433
} else {
4434+
// SFALL
44164435
if (weaponGetPerk(attack->weapon) == PERK_WEAPON_PENETRATE
4417-
|| attack->hitMode == HIT_MODE_PALM_STRIKE
4418-
|| attack->hitMode == HIT_MODE_PIERCING_STRIKE
4419-
|| attack->hitMode == HIT_MODE_HOOK_KICK
4420-
|| attack->hitMode == HIT_MODE_PIERCING_KICK) {
4436+
|| unarmedIsPenetrating(attack->hitMode)) {
44214437
damageThreshold = 20 * damageThreshold / 100;
44224438
}
44234439

@@ -6189,3 +6205,306 @@ static int burstModComputeRounds(int totalRounds, int* centerRoundsPtr, int* lef
61896205

61906206
return mainTargetRounds;
61916207
}
6208+
6209+
static void unarmedInit()
6210+
{
6211+
unarmedInitVanilla();
6212+
unarmedInitCustom();
6213+
}
6214+
6215+
static void unarmedInitVanilla()
6216+
{
6217+
UnarmedHitDescription* hitDescription;
6218+
6219+
// Punch
6220+
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_PUNCH]);
6221+
hitDescription->minDamage = 1;
6222+
hitDescription->maxDamage = 2;
6223+
hitDescription->actionPointCost = 3;
6224+
6225+
// Strong Punch
6226+
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_STRONG_PUNCH]);
6227+
hitDescription->requiredSkill = 55;
6228+
hitDescription->requiredStats[STAT_AGILITY] = 6;
6229+
hitDescription->minDamage = 1;
6230+
hitDescription->maxDamage = 2;
6231+
hitDescription->bonusDamage = 3;
6232+
hitDescription->actionPointCost = 3;
6233+
hitDescription->isPenetrate = false;
6234+
hitDescription->isSecondary = false;
6235+
6236+
// Hammer Punch
6237+
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_HAMMER_PUNCH]);
6238+
hitDescription->requiredLevel = 6;
6239+
hitDescription->requiredSkill = 75;
6240+
hitDescription->requiredStats[STAT_STRENGTH] = 5;
6241+
hitDescription->requiredStats[STAT_AGILITY] = 6;
6242+
hitDescription->minDamage = 1;
6243+
hitDescription->maxDamage = 2;
6244+
hitDescription->bonusDamage = 5;
6245+
hitDescription->bonusCriticalChance = 5;
6246+
hitDescription->actionPointCost = 3;
6247+
6248+
// Lightning Punch
6249+
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_HAYMAKER]);
6250+
hitDescription->requiredLevel = 9;
6251+
hitDescription->requiredSkill = 100;
6252+
hitDescription->requiredStats[STAT_STRENGTH] = 5;
6253+
hitDescription->requiredStats[STAT_AGILITY] = 7;
6254+
hitDescription->minDamage = 1;
6255+
hitDescription->maxDamage = 2;
6256+
hitDescription->bonusDamage = 7;
6257+
hitDescription->bonusCriticalChance = 15;
6258+
hitDescription->actionPointCost = 3;
6259+
6260+
// Chop Punch
6261+
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_JAB]);
6262+
hitDescription->requiredLevel = 5;
6263+
hitDescription->requiredSkill = 75;
6264+
hitDescription->requiredStats[STAT_STRENGTH] = 5;
6265+
hitDescription->requiredStats[STAT_AGILITY] = 7;
6266+
hitDescription->minDamage = 1;
6267+
hitDescription->maxDamage = 2;
6268+
hitDescription->bonusDamage = 3;
6269+
hitDescription->bonusCriticalChance = 10;
6270+
hitDescription->actionPointCost = 3;
6271+
hitDescription->isSecondary = true;
6272+
6273+
// Dragon Punch
6274+
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_PALM_STRIKE]);
6275+
hitDescription->requiredLevel = 12;
6276+
hitDescription->requiredSkill = 115;
6277+
hitDescription->requiredStats[STAT_STRENGTH] = 5;
6278+
hitDescription->requiredStats[STAT_AGILITY] = 7;
6279+
hitDescription->minDamage = 1;
6280+
hitDescription->maxDamage = 2;
6281+
hitDescription->bonusDamage = 7;
6282+
hitDescription->bonusCriticalChance = 20;
6283+
hitDescription->actionPointCost = 6;
6284+
hitDescription->isPenetrate = true;
6285+
hitDescription->isSecondary = true;
6286+
6287+
// Force Punch
6288+
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_PIERCING_STRIKE]);
6289+
hitDescription->requiredLevel = 16;
6290+
hitDescription->requiredSkill = 130;
6291+
hitDescription->requiredStats[STAT_STRENGTH] = 5;
6292+
hitDescription->requiredStats[STAT_AGILITY] = 7;
6293+
hitDescription->minDamage = 1;
6294+
hitDescription->maxDamage = 2;
6295+
hitDescription->bonusDamage = 10;
6296+
hitDescription->bonusCriticalChance = 40;
6297+
hitDescription->actionPointCost = 8;
6298+
hitDescription->isPenetrate = true;
6299+
hitDescription->isSecondary = true;
6300+
6301+
// Kick
6302+
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_KICK]);
6303+
hitDescription->minDamage = 1;
6304+
hitDescription->maxDamage = 2;
6305+
hitDescription->actionPointCost = 3;
6306+
6307+
// Strong Kick
6308+
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_STRONG_KICK]);
6309+
hitDescription->requiredSkill = 40;
6310+
hitDescription->requiredStats[STAT_AGILITY] = 6;
6311+
hitDescription->minDamage = 1;
6312+
hitDescription->maxDamage = 2;
6313+
hitDescription->bonusDamage = 5;
6314+
hitDescription->actionPointCost = 4;
6315+
6316+
// Snap Kick
6317+
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_SNAP_KICK]);
6318+
hitDescription->requiredLevel = 6;
6319+
hitDescription->requiredSkill = 60;
6320+
hitDescription->requiredStats[STAT_AGILITY] = 6;
6321+
hitDescription->minDamage = 1;
6322+
hitDescription->maxDamage = 2;
6323+
hitDescription->bonusDamage = 7;
6324+
hitDescription->actionPointCost = 4;
6325+
6326+
// Roundhouse Kick
6327+
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_POWER_KICK]);
6328+
hitDescription->requiredLevel = 9;
6329+
hitDescription->requiredSkill = 80;
6330+
hitDescription->requiredStats[STAT_STRENGTH] = 6;
6331+
hitDescription->requiredStats[STAT_AGILITY] = 6;
6332+
hitDescription->minDamage = 1;
6333+
hitDescription->maxDamage = 2;
6334+
hitDescription->bonusDamage = 9;
6335+
hitDescription->bonusCriticalChance = 5;
6336+
hitDescription->actionPointCost = 4;
6337+
6338+
// Kip Kick
6339+
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_HIP_KICK]);
6340+
hitDescription->requiredLevel = 6;
6341+
hitDescription->requiredSkill = 60;
6342+
hitDescription->requiredStats[STAT_STRENGTH] = 6;
6343+
hitDescription->requiredStats[STAT_AGILITY] = 7;
6344+
hitDescription->minDamage = 1;
6345+
hitDescription->maxDamage = 2;
6346+
hitDescription->bonusDamage = 7;
6347+
hitDescription->actionPointCost = 7;
6348+
hitDescription->isSecondary = true;
6349+
6350+
// Jump Kick
6351+
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_HOOK_KICK]);
6352+
hitDescription->requiredLevel = 12;
6353+
hitDescription->requiredSkill = 100;
6354+
hitDescription->requiredStats[STAT_STRENGTH] = 6;
6355+
hitDescription->requiredStats[STAT_AGILITY] = 7;
6356+
hitDescription->minDamage = 1;
6357+
hitDescription->maxDamage = 2;
6358+
hitDescription->bonusDamage = 9;
6359+
hitDescription->bonusCriticalChance = 10;
6360+
hitDescription->actionPointCost = 7;
6361+
hitDescription->isPenetrate = true;
6362+
hitDescription->isSecondary = true;
6363+
6364+
// Death Blossom Kick
6365+
hitDescription = &(gUnarmedHitDescriptions[HIT_MODE_PIERCING_KICK]);
6366+
hitDescription->requiredLevel = 15;
6367+
hitDescription->requiredSkill = 125;
6368+
hitDescription->requiredStats[STAT_STRENGTH] = 6;
6369+
hitDescription->requiredStats[STAT_AGILITY] = 8;
6370+
hitDescription->minDamage = 1;
6371+
hitDescription->maxDamage = 2;
6372+
hitDescription->bonusDamage = 12;
6373+
hitDescription->bonusCriticalChance = 50;
6374+
hitDescription->actionPointCost = 9;
6375+
hitDescription->isPenetrate = true;
6376+
hitDescription->isSecondary = true;
6377+
}
6378+
6379+
static void unarmedInitCustom()
6380+
{
6381+
char* unarmedFileName = NULL;
6382+
configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_UNARMED_FILE_KEY, &unarmedFileName);
6383+
if (unarmedFileName != NULL && *unarmedFileName == '\0') {
6384+
unarmedFileName = NULL;
6385+
}
6386+
6387+
if (unarmedFileName == NULL) {
6388+
return;
6389+
}
6390+
6391+
Config unarmedConfig;
6392+
if (configInit(&unarmedConfig)) {
6393+
if (configRead(&unarmedConfig, unarmedFileName, false)) {
6394+
char section[4];
6395+
char statKey[6];
6396+
6397+
for (int hitMode = 0; hitMode < HIT_MODE_COUNT; hitMode++) {
6398+
if (!isUnarmedHitMode(hitMode)) {
6399+
continue;
6400+
}
6401+
6402+
UnarmedHitDescription* hitDescription = &(gUnarmedHitDescriptions[hitMode]);
6403+
sprintf(section, "%d", hitMode);
6404+
6405+
configGetInt(&unarmedConfig, section, "ReqLevel", &(hitDescription->requiredLevel));
6406+
configGetInt(&unarmedConfig, section, "SkillLevel", &(hitDescription->requiredSkill));
6407+
configGetInt(&unarmedConfig, section, "MinDamage", &(hitDescription->minDamage));
6408+
configGetInt(&unarmedConfig, section, "MaxDamage", &(hitDescription->maxDamage));
6409+
configGetInt(&unarmedConfig, section, "BonusDamage", &(hitDescription->bonusDamage));
6410+
configGetInt(&unarmedConfig, section, "BonusCrit", &(hitDescription->bonusCriticalChance));
6411+
configGetInt(&unarmedConfig, section, "APCost", &(hitDescription->actionPointCost));
6412+
configGetBool(&unarmedConfig, section, "BonusDamage", &(hitDescription->isPenetrate));
6413+
configGetBool(&unarmedConfig, section, "Secondary", &(hitDescription->isSecondary));
6414+
6415+
for (int stat = 0; stat < PRIMARY_STAT_COUNT; stat++) {
6416+
sprintf(statKey, "Stat%d", stat);
6417+
configGetInt(&unarmedConfig, section, statKey, &(hitDescription->requiredStats[stat]));
6418+
}
6419+
}
6420+
}
6421+
6422+
configFree(&unarmedConfig);
6423+
}
6424+
}
6425+
6426+
int unarmedGetDamage(int hitMode, int* minDamagePtr, int* maxDamagePtr)
6427+
{
6428+
UnarmedHitDescription* hitDescription = &(gUnarmedHitDescriptions[hitMode]);
6429+
*minDamagePtr = hitDescription->minDamage;
6430+
*maxDamagePtr = hitDescription->maxDamage;
6431+
return hitDescription->bonusDamage;
6432+
}
6433+
6434+
int unarmedGetBonusCriticalChance(int hitMode)
6435+
{
6436+
UnarmedHitDescription* hitDescription = &(gUnarmedHitDescriptions[hitMode]);
6437+
return hitDescription->bonusCriticalChance;
6438+
}
6439+
6440+
int unarmedGetActionPointCost(int hitMode)
6441+
{
6442+
UnarmedHitDescription* hitDescription = &(gUnarmedHitDescriptions[hitMode]);
6443+
return hitDescription->actionPointCost;
6444+
}
6445+
6446+
bool unarmedIsPenetrating(int hitMode)
6447+
{
6448+
UnarmedHitDescription* hitDescription = &(gUnarmedHitDescriptions[hitMode]);
6449+
return hitDescription->isPenetrate;
6450+
}
6451+
6452+
int unarmedGetPunchHitMode(bool isSecondary)
6453+
{
6454+
int hitMode = unarmedGetHitModeInRange(FIRST_ADVANCED_PUNCH_HIT_MODE, LAST_ADVANCED_PUNCH_HIT_MODE, isSecondary);
6455+
if (hitMode == -1) {
6456+
hitMode = HIT_MODE_PUNCH;
6457+
}
6458+
return hitMode;
6459+
}
6460+
6461+
int unarmedGetKickHitMode(bool isSecondary)
6462+
{
6463+
int hitMode = unarmedGetHitModeInRange(FIRST_ADVANCED_KICK_HIT_MODE, LAST_ADVANCED_KICK_HIT_MODE, isSecondary);
6464+
if (hitMode == -1) {
6465+
hitMode = HIT_MODE_KICK;
6466+
}
6467+
return hitMode;
6468+
}
6469+
6470+
static int unarmedGetHitModeInRange(int firstHitMode, int lastHitMode, bool isSecondary)
6471+
{
6472+
int hitMode = -1;
6473+
6474+
int unarmed = skillGetValue(gDude, SKILL_UNARMED);
6475+
int level = pcGetStat(PC_STAT_LEVEL);
6476+
int stats[PRIMARY_STAT_COUNT];
6477+
for (int stat = 0; stat < PRIMARY_STAT_COUNT; stat++) {
6478+
stats[stat] = critterGetStat(gDude, stat);
6479+
}
6480+
6481+
for (int candidateHitMode = firstHitMode; candidateHitMode <= lastHitMode; candidateHitMode++) {
6482+
UnarmedHitDescription* hitDescription = &(gUnarmedHitDescriptions[candidateHitMode]);
6483+
if (isSecondary != hitDescription->isSecondary) {
6484+
continue;
6485+
}
6486+
6487+
if (unarmed < hitDescription->requiredSkill) {
6488+
continue;
6489+
}
6490+
6491+
if (level < hitDescription->requiredLevel) {
6492+
continue;
6493+
}
6494+
6495+
bool missingStats = false;
6496+
for (int stat = 0; stat < PRIMARY_STAT_COUNT; stat++) {
6497+
if (stats[stat] < hitDescription->requiredStats[stat]) {
6498+
missingStats = true;
6499+
break;
6500+
}
6501+
}
6502+
if (missingStats) {
6503+
continue;
6504+
}
6505+
6506+
hitMode = candidateHitMode;
6507+
}
6508+
6509+
return hitMode;
6510+
}

0 commit comments

Comments
 (0)