Add unarmed hits improvements (#29)
This commit is contained in:
parent
2859410d4b
commit
87289a34c0
341
src/combat.cc
341
src/combat.cc
|
@ -54,6 +54,19 @@ typedef struct CombatAiInfo {
|
||||||
int lastMove;
|
int lastMove;
|
||||||
} CombatAiInfo;
|
} 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 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 int aiInfoCopy(int srcIndex, int destIndex);
|
||||||
static void _combat_begin(Object* a1);
|
static void _combat_begin(Object* a1);
|
||||||
|
@ -96,6 +109,10 @@ static void criticalsReset();
|
||||||
static void criticalsExit();
|
static void criticalsExit();
|
||||||
static void burstModInit();
|
static void burstModInit();
|
||||||
static int burstModComputeRounds(int totalRounds, int* centerRoundsPtr, int* leftRoundsPtr, int* rightRoundsPtr);
|
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
|
// 0x500B50
|
||||||
static char _a_1[] = ".";
|
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 gBurstModCenterDivisor = SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR;
|
||||||
static int gBurstModTargetMultiplier = SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_MULTIPLIER;
|
static int gBurstModTargetMultiplier = SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_MULTIPLIER;
|
||||||
static int gBurstModTargetDivisor = SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_DIVISOR;
|
static int gBurstModTargetDivisor = SFALL_CONFIG_BURST_MOD_DEFAULT_TARGET_DIVISOR;
|
||||||
|
static UnarmedHitDescription gUnarmedHitDescriptions[HIT_MODE_COUNT];
|
||||||
|
|
||||||
// combat_init
|
// combat_init
|
||||||
// 0x420CC0
|
// 0x420CC0
|
||||||
|
@ -1974,6 +1992,7 @@ int combatInit()
|
||||||
// SFALL
|
// SFALL
|
||||||
criticalsInit();
|
criticalsInit();
|
||||||
burstModInit();
|
burstModInit();
|
||||||
|
unarmedInit();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -3757,16 +3776,15 @@ static int attackCompute(Attack* attack)
|
||||||
damageMultiplier = 4;
|
damageMultiplier = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (((attack->hitMode == HIT_MODE_HAMMER_PUNCH || attack->hitMode == HIT_MODE_POWER_KICK) && randomBetween(1, 100) <= 5)
|
// SFALL
|
||||||
|| ((attack->hitMode == HIT_MODE_JAB || attack->hitMode == HIT_MODE_HOOK_KICK) && randomBetween(1, 100) <= 10)
|
int bonusCriticalChance = unarmedGetBonusCriticalChance(attack->hitMode);
|
||||||
|| (attack->hitMode == HIT_MODE_HAYMAKER && randomBetween(1, 100) <= 15)
|
if (bonusCriticalChance != 0) {
|
||||||
|| (attack->hitMode == HIT_MODE_PALM_STRIKE && randomBetween(1, 100) <= 20)
|
if (randomBetween(1, 100) <= bonusCriticalChance) {
|
||||||
|| (attack->hitMode == HIT_MODE_PIERCING_STRIKE && randomBetween(1, 100) <= 40)
|
|
||||||
|| (attack->hitMode == HIT_MODE_PIERCING_KICK && randomBetween(1, 100) <= 50)) {
|
|
||||||
roll = ROLL_CRITICAL_SUCCESS;
|
roll = ROLL_CRITICAL_SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (attackType == ATTACK_TYPE_RANGED) {
|
if (attackType == ATTACK_TYPE_RANGED) {
|
||||||
attack->ammoQuantity = v26;
|
attack->ammoQuantity = v26;
|
||||||
|
@ -4203,7 +4221,7 @@ static int attackDetermineToHit(Object* attacker, int tile, Object* defender, in
|
||||||
bool isRangedWeapon = false;
|
bool isRangedWeapon = false;
|
||||||
|
|
||||||
int accuracy;
|
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);
|
accuracy = skillGetValue(attacker, SKILL_UNARMED);
|
||||||
} else {
|
} else {
|
||||||
accuracy = _item_w_skill_level(attacker, hitMode);
|
accuracy = _item_w_skill_level(attacker, hitMode);
|
||||||
|
@ -4413,11 +4431,9 @@ static void attackComputeDamage(Attack* attack, int ammoQuantity, int bonusDamag
|
||||||
damageThreshold = 20 * damageThreshold / 100;
|
damageThreshold = 20 * damageThreshold / 100;
|
||||||
damageResistance = 20 * damageResistance / 100;
|
damageResistance = 20 * damageResistance / 100;
|
||||||
} else {
|
} else {
|
||||||
|
// SFALL
|
||||||
if (weaponGetPerk(attack->weapon) == PERK_WEAPON_PENETRATE
|
if (weaponGetPerk(attack->weapon) == PERK_WEAPON_PENETRATE
|
||||||
|| attack->hitMode == HIT_MODE_PALM_STRIKE
|
|| unarmedIsPenetrating(attack->hitMode)) {
|
||||||
|| attack->hitMode == HIT_MODE_PIERCING_STRIKE
|
|
||||||
|| attack->hitMode == HIT_MODE_HOOK_KICK
|
|
||||||
|| attack->hitMode == HIT_MODE_PIERCING_KICK) {
|
|
||||||
damageThreshold = 20 * damageThreshold / 100;
|
damageThreshold = 20 * damageThreshold / 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6189,3 +6205,306 @@ static int burstModComputeRounds(int totalRounds, int* centerRoundsPtr, int* lef
|
||||||
|
|
||||||
return mainTargetRounds;
|
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);
|
int criticalsGetValue(int killType, int hitLocation, int effect, int dataMember);
|
||||||
void criticalsSetValue(int killType, int hitLocation, int effect, int dataMember, int value);
|
void criticalsSetValue(int killType, int hitLocation, int effect, int dataMember, int value);
|
||||||
void criticalsResetValue(int killType, int hitLocation, int effect, int dataMember);
|
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()
|
static inline bool isInCombat()
|
||||||
{
|
{
|
||||||
return (gCombatState & COMBAT_STATE_0x01) != 0;
|
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 */
|
#endif /* COMBAT_H */
|
||||||
|
|
|
@ -1264,30 +1264,9 @@ int interfaceUpdateItems(bool animated, int leftItemAction, int rightItemAction)
|
||||||
leftItemState->action = INTERFACE_ITEM_ACTION_PRIMARY;
|
leftItemState->action = INTERFACE_ITEM_ACTION_PRIMARY;
|
||||||
leftItemState->itemFid = -1;
|
leftItemState->itemFid = -1;
|
||||||
|
|
||||||
int unarmed = skillGetValue(gDude, SKILL_UNARMED);
|
// SFALL
|
||||||
int agility = critterGetStat(gDude, STAT_AGILITY);
|
leftItemState->primaryHitMode = unarmedGetPunchHitMode(false);
|
||||||
int strength = critterGetStat(gDude, STAT_STRENGTH);
|
leftItemState->secondaryHitMode = unarmedGetPunchHitMode(true);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1324,30 +1303,9 @@ int interfaceUpdateItems(bool animated, int leftItemAction, int rightItemAction)
|
||||||
rightItemState->action = INTERFACE_ITEM_ACTION_PRIMARY;
|
rightItemState->action = INTERFACE_ITEM_ACTION_PRIMARY;
|
||||||
rightItemState->itemFid = -1;
|
rightItemState->itemFid = -1;
|
||||||
|
|
||||||
int unarmed = skillGetValue(gDude, SKILL_UNARMED);
|
// SFALL
|
||||||
int agility = critterGetStat(gDude, STAT_AGILITY);
|
rightItemState->primaryHitMode = unarmedGetKickHitMode(false);
|
||||||
int strength = critterGetStat(gDude, STAT_STRENGTH);
|
rightItemState->secondaryHitMode = unarmedGetKickHitMode(true);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2433,6 +2433,11 @@ static void inventoryRenderSummary()
|
||||||
HIT_MODE_RIGHT_WEAPON_PRIMARY,
|
HIT_MODE_RIGHT_WEAPON_PRIMARY,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const int unarmedHitModes[2] = {
|
||||||
|
HIT_MODE_PUNCH,
|
||||||
|
HIT_MODE_KICK,
|
||||||
|
};
|
||||||
|
|
||||||
offset += 499 * fontGetLineHeight();
|
offset += 499 * fontGetLineHeight();
|
||||||
|
|
||||||
for (int index = 0; index < 2; index += 1) {
|
for (int index = 0; index < 2; index += 1) {
|
||||||
|
@ -2451,10 +2456,33 @@ static void inventoryRenderSummary()
|
||||||
// Unarmed dmg:
|
// Unarmed dmg:
|
||||||
messageListItem.num = 24;
|
messageListItem.num = 24;
|
||||||
if (messageListGetItem(&gInventoryMessageList, &messageListItem)) {
|
if (messageListGetItem(&gInventoryMessageList, &messageListItem)) {
|
||||||
// TODO: Figure out why it uses STAT_MELEE_DAMAGE instead of
|
// SFALL: Display the actual damage values of unarmed attacks.
|
||||||
// STAT_UNARMED_DAMAGE.
|
// CE: Implementation is different.
|
||||||
int damage = critterGetStat(_stack[0], STAT_MELEE_DAMAGE) + 2;
|
int hitMode = unarmedHitModes[index];
|
||||||
sprintf(formattedText, "%s 1-%d", messageListItem.text, damage);
|
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, 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 minDamage = 0;
|
||||||
int maxDamage = 0;
|
int maxDamage = 0;
|
||||||
int meleeDamage = 0;
|
int meleeDamage = 0;
|
||||||
int unarmedDamage = 0;
|
int bonusDamage = 0;
|
||||||
|
|
||||||
// NOTE: Uninline.
|
// NOTE: Uninline.
|
||||||
Object* weapon = critterGetWeaponForHitMode(critter, hitMode);
|
Object* weapon = critterGetWeaponForHitMode(critter, hitMode);
|
||||||
|
@ -1278,38 +1278,12 @@ int weaponGetMeleeDamage(Object* critter, int hitMode)
|
||||||
meleeDamage = critterGetStat(critter, STAT_MELEE_DAMAGE);
|
meleeDamage = critterGetStat(critter, STAT_MELEE_DAMAGE);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
minDamage = 1;
|
// SFALL
|
||||||
maxDamage = critterGetStat(critter, STAT_MELEE_DAMAGE) + 2;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return randomBetween(unarmedDamage + minDamage, unarmedDamage + meleeDamage + maxDamage);
|
return randomBetween(bonusDamage + minDamage, bonusDamage + meleeDamage + maxDamage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0x478570
|
// 0x478570
|
||||||
|
@ -1678,28 +1652,11 @@ int _item_w_mp_cost(Object* critter, int hitMode, bool aiming)
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (hitMode) {
|
// CE: The entire function is different in Sfall.
|
||||||
case HIT_MODE_PALM_STRIKE:
|
if (isUnarmedHitMode(hitMode)) {
|
||||||
actionPoints = 6;
|
actionPoints = unarmedGetActionPointCost(hitMode);
|
||||||
break;
|
} else {
|
||||||
case HIT_MODE_PIERCING_STRIKE:
|
if (weapon != NULL) {
|
||||||
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) {
|
|
||||||
if (hitMode == HIT_MODE_LEFT_WEAPON_PRIMARY || hitMode == HIT_MODE_RIGHT_WEAPON_PRIMARY) {
|
if (hitMode == HIT_MODE_LEFT_WEAPON_PRIMARY || hitMode == HIT_MODE_RIGHT_WEAPON_PRIMARY) {
|
||||||
// NOTE: Uninline.
|
// NOTE: Uninline.
|
||||||
actionPoints = weaponGetActionPointCost1(weapon);
|
actionPoints = weaponGetActionPointCost1(weapon);
|
||||||
|
@ -1718,7 +1675,6 @@ int _item_w_mp_cost(Object* critter, int hitMode, bool aiming)
|
||||||
} else {
|
} else {
|
||||||
actionPoints = 3;
|
actionPoints = 3;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (critter == gDude) {
|
if (critter == gDude) {
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#define SFALL_CONFIG_PLASTIC_EXPLOSIVE_MAX_DAMAGE_KEY "PlasticExplosive_DmgMax"
|
#define SFALL_CONFIG_PLASTIC_EXPLOSIVE_MAX_DAMAGE_KEY "PlasticExplosive_DmgMax"
|
||||||
#define SFALL_CONFIG_EXPLOSION_EMITS_LIGHT_KEY "ExplosionsEmitLight"
|
#define SFALL_CONFIG_EXPLOSION_EMITS_LIGHT_KEY "ExplosionsEmitLight"
|
||||||
#define SFALL_CONFIG_CITY_REPUTATION_LIST_KEY "CityRepsList"
|
#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_MULTIPLIER 1
|
||||||
#define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR 3
|
#define SFALL_CONFIG_BURST_MOD_DEFAULT_CENTER_DIVISOR 3
|
||||||
|
|
|
@ -772,6 +772,12 @@ int pcAddExperienceWithOptions(int xp, bool a2)
|
||||||
|
|
||||||
interfaceRenderHitPoints(false);
|
interfaceRenderHitPoints(false);
|
||||||
|
|
||||||
|
// SFALL: Update unarmed attack after leveling up.
|
||||||
|
int leftItemAction;
|
||||||
|
int rightItemAction;
|
||||||
|
interfaceGetItemActions(&leftItemAction, &rightItemAction);
|
||||||
|
interfaceUpdateItems(false, leftItemAction, rightItemAction);
|
||||||
|
|
||||||
if (a2) {
|
if (a2) {
|
||||||
_partyMemberIncLevels();
|
_partyMemberIncLevels();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue