Add critical hits improvements (#29)

This commit is contained in:
Alexander Batalov 2022-08-02 19:37:31 +03:00
parent df5bceaf2a
commit 63bcb2d009
7 changed files with 342 additions and 17 deletions

View File

@ -31,6 +31,7 @@
#include "queue.h"
#include "random.h"
#include "scripts.h"
#include "sfall_config.h"
#include "skill.h"
#include "stat.h"
#include "text_font.h"
@ -90,6 +91,10 @@ static void _draw_loc_on_(int a1, int a2);
static void _draw_loc_(int eventCode, int color);
static int calledShotSelectHitLocation(Object* critter, int* hitLocation, int hitMode);
static void criticalsInit();
static void criticalsReset();
static void criticalsExit();
// 0x500B50
static char _a_1[] = ".";
@ -129,7 +134,7 @@ static const int _hit_location_penalty[HIT_LOCATION_COUNT] = {
// Critical hit tables for every kill type.
//
// 0x510978
static CriticalHitDescription gCriticalHitTables[KILL_TYPE_COUNT][HIT_LOCATION_COUNT][CRTICIAL_EFFECT_COUNT] = {
static CriticalHitDescription gCriticalHitTables[SFALL_KILL_TYPE_COUNT][HIT_LOCATION_COUNT][CRTICIAL_EFFECT_COUNT] = {
// KILL_TYPE_MAN
{
// HIT_LOCATION_HEAD
@ -1907,6 +1912,19 @@ static Attack _shoot_ctd;
// 0x56D458
static Attack _explosion_ctd;
static CriticalHitDescription gBaseCriticalHitTables[SFALL_KILL_TYPE_COUNT][HIT_LOCATION_COUNT][CRTICIAL_EFFECT_COUNT];
static CriticalHitDescription gBasePlayerCriticalHitTable[HIT_LOCATION_COUNT][CRTICIAL_EFFECT_COUNT];
static const char* gCritDataMemberKeys[CRIT_DATA_MEMBER_COUNT] = {
"DamageMultiplier",
"EffectFlags",
"StatCheck",
"StatMod",
"FailureEffect",
"Message",
"FailMessage",
};
// combat_init
// 0x420CC0
int combatInit()
@ -1945,6 +1963,9 @@ int combatInit()
return -1;
}
// SFALL
criticalsInit();
return 0;
}
@ -1970,12 +1991,18 @@ void combatReset()
_combat_ending_guy = NULL;
gDude->data.critter.combat.ap = max_action_points;
// SFALL
criticalsReset();
}
// 0x420E14
void combatExit()
{
messageListFree(&gCombatMessageList);
// SFALL
criticalsExit();
}
// 0x420E24
@ -4023,8 +4050,12 @@ static int attackComputeCriticalFailure(Attack* attack)
}
if (attack->attacker == gDude) {
// SFALL: Remove criticals time limits.
bool criticalsTimeLimitsRemoved = false;
configGetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_REMOVE_CRITICALS_TIME_LIMITS_KEY, &criticalsTimeLimitsRemoved);
unsigned int gameTime = gameTimeGetTime();
if (gameTime / GAME_TIME_TICKS_PER_DAY < 6) {
if (!criticalsTimeLimitsRemoved && gameTime / GAME_TIME_TICKS_PER_DAY < 6) {
return 0;
}
}
@ -5826,3 +5857,263 @@ void _combatKillCritterOutsideCombat(Object* critter_obj, char* msg)
critterKill(critter_obj, -1, 1);
}
}
static void criticalsInit()
{
int mode = 2;
configGetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_OVERRIDE_CRITICALS_MODE_KEY, &mode);
if (mode < 0 || mode > 3) {
mode = 0;
}
if (mode == 2 || mode == 3) {
// Men
criticalsSetValue(KILL_TYPE_MAN, HIT_LOCATION_UNCALLED, 2, CRIT_DATA_MEMBER_FLAGS, DAM_KNOCKED_DOWN | DAM_BYPASS);
criticalsSetValue(KILL_TYPE_MAN, HIT_LOCATION_UNCALLED, 2, CRIT_DATA_MEMBER_MESSAGE_ID, 5019);
// Children
criticalsSetValue(KILL_TYPE_CHILD, HIT_LOCATION_RIGHT_LEG, 1, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_FLAGS, 0);
criticalsSetValue(KILL_TYPE_CHILD, HIT_LOCATION_RIGHT_LEG, 1, CRIT_DATA_MEMBER_MESSAGE_ID, 5216);
criticalsSetValue(KILL_TYPE_CHILD, HIT_LOCATION_RIGHT_LEG, 1, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_MESSAGE_ID, 5000);
criticalsSetValue(KILL_TYPE_CHILD, HIT_LOCATION_RIGHT_LEG, 2, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_FLAGS, 0);
criticalsSetValue(KILL_TYPE_CHILD, HIT_LOCATION_RIGHT_LEG, 2, CRIT_DATA_MEMBER_MESSAGE_ID, 5216);
criticalsSetValue(KILL_TYPE_CHILD, HIT_LOCATION_RIGHT_LEG, 2, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_MESSAGE_ID, 5000);
criticalsSetValue(KILL_TYPE_CHILD, HIT_LOCATION_LEFT_LEG, 1, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_FLAGS, 0);
criticalsSetValue(KILL_TYPE_CHILD, HIT_LOCATION_LEFT_LEG, 1, CRIT_DATA_MEMBER_MESSAGE_ID, 5216);
criticalsSetValue(KILL_TYPE_CHILD, HIT_LOCATION_LEFT_LEG, 1, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_MESSAGE_ID, 5000);
criticalsSetValue(KILL_TYPE_CHILD, HIT_LOCATION_LEFT_LEG, 2, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_FLAGS, 0);
criticalsSetValue(KILL_TYPE_CHILD, HIT_LOCATION_LEFT_LEG, 2, CRIT_DATA_MEMBER_MESSAGE_ID, 5216);
criticalsSetValue(KILL_TYPE_CHILD, HIT_LOCATION_LEFT_LEG, 2, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_MESSAGE_ID, 5000);
criticalsSetValue(KILL_TYPE_CHILD, HIT_LOCATION_UNCALLED, 1, CRIT_DATA_MEMBER_DAMAGE_MULTIPLIER, 4);
criticalsSetValue(KILL_TYPE_CHILD, HIT_LOCATION_UNCALLED, 2, CRIT_DATA_MEMBER_FLAGS, DAM_KNOCKED_DOWN | DAM_BYPASS);
criticalsSetValue(KILL_TYPE_CHILD, HIT_LOCATION_UNCALLED, 2, CRIT_DATA_MEMBER_MESSAGE_ID, 5212);
// Super Mutants
criticalsSetValue(KILL_TYPE_SUPER_MUTANT, HIT_LOCATION_LEFT_LEG, 1, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_MESSAGE_ID, 5306);
// Ghouls
criticalsSetValue(KILL_TYPE_GHOUL, HIT_LOCATION_HEAD, 4, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_STAT, -1);
// Brahmin
criticalsSetValue(KILL_TYPE_BRAHMIN, HIT_LOCATION_HEAD, 4, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_STAT, -1);
// Radscorpions
criticalsSetValue(KILL_TYPE_RADSCORPION, HIT_LOCATION_RIGHT_LEG, 1, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_FLAGS, DAM_KNOCKED_DOWN);
criticalsSetValue(KILL_TYPE_RADSCORPION, HIT_LOCATION_LEFT_LEG, 1, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_FLAGS, DAM_KNOCKED_DOWN);
criticalsSetValue(KILL_TYPE_RADSCORPION, HIT_LOCATION_LEFT_LEG, 2, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_MESSAGE_ID, 5608);
// Centaurs
criticalsSetValue(KILL_TYPE_CENTAUR, HIT_LOCATION_TORSO, 3, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_FLAGS, DAM_KNOCKED_DOWN);
criticalsSetValue(KILL_TYPE_CENTAUR, HIT_LOCATION_UNCALLED, 3, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_FLAGS, DAM_KNOCKED_DOWN);
// Deathclaws
criticalsSetValue(KILL_TYPE_DEATH_CLAW, HIT_LOCATION_LEFT_LEG, 1, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_FLAGS, DAM_CRIP_LEG_LEFT);
criticalsSetValue(KILL_TYPE_DEATH_CLAW, HIT_LOCATION_LEFT_LEG, 2, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_FLAGS, DAM_CRIP_LEG_LEFT);
criticalsSetValue(KILL_TYPE_DEATH_CLAW, HIT_LOCATION_LEFT_LEG, 3, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_FLAGS, DAM_CRIP_LEG_LEFT);
criticalsSetValue(KILL_TYPE_DEATH_CLAW, HIT_LOCATION_LEFT_LEG, 4, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_FLAGS, DAM_CRIP_LEG_LEFT);
criticalsSetValue(KILL_TYPE_DEATH_CLAW, HIT_LOCATION_LEFT_LEG, 5, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_FLAGS, DAM_CRIP_LEG_LEFT);
// Geckos
criticalsSetValue(KILL_TYPE_GECKO, HIT_LOCATION_UNCALLED, 0, CRIT_DATA_MEMBER_MESSAGE_ID, 6701);
criticalsSetValue(KILL_TYPE_GECKO, HIT_LOCATION_UNCALLED, 1, CRIT_DATA_MEMBER_MESSAGE_ID, 6701);
criticalsSetValue(KILL_TYPE_GECKO, HIT_LOCATION_UNCALLED, 2, CRIT_DATA_MEMBER_FLAGS, DAM_KNOCKED_DOWN | DAM_BYPASS);
criticalsSetValue(KILL_TYPE_GECKO, HIT_LOCATION_UNCALLED, 2, CRIT_DATA_MEMBER_MESSAGE_ID, 6704);
criticalsSetValue(KILL_TYPE_GECKO, HIT_LOCATION_UNCALLED, 3, CRIT_DATA_MEMBER_MESSAGE_ID, 6704);
criticalsSetValue(KILL_TYPE_GECKO, HIT_LOCATION_UNCALLED, 4, CRIT_DATA_MEMBER_MESSAGE_ID, 6704);
criticalsSetValue(KILL_TYPE_GECKO, HIT_LOCATION_UNCALLED, 5, CRIT_DATA_MEMBER_MESSAGE_ID, 6704);
// Aliens
criticalsSetValue(16, HIT_LOCATION_UNCALLED, 2, CRIT_DATA_MEMBER_FLAGS, DAM_KNOCKED_DOWN | DAM_BYPASS);
// Giant Ants
criticalsSetValue(17, HIT_LOCATION_UNCALLED, 2, CRIT_DATA_MEMBER_FLAGS, DAM_KNOCKED_DOWN | DAM_BYPASS);
// Big Bad Boss
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_HEAD, 0, CRIT_DATA_MEMBER_MESSAGE_ID, 5001);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_HEAD, 1, CRIT_DATA_MEMBER_MESSAGE_ID, 5001);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_HEAD, 2, CRIT_DATA_MEMBER_MESSAGE_ID, 5001);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_HEAD, 3, CRIT_DATA_MEMBER_MESSAGE_ID, 7105);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_HEAD, 4, CRIT_DATA_MEMBER_MESSAGE_ID, 7101);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_HEAD, 4, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_MESSAGE_ID, 7104);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_HEAD, 5, CRIT_DATA_MEMBER_MESSAGE_ID, 7101);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_LEFT_ARM, 0, CRIT_DATA_MEMBER_MESSAGE_ID, 5008);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_LEFT_ARM, 1, CRIT_DATA_MEMBER_MESSAGE_ID, 5008);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_LEFT_ARM, 2, CRIT_DATA_MEMBER_MESSAGE_ID, 5009);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_LEFT_ARM, 3, CRIT_DATA_MEMBER_MESSAGE_ID, 5009);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_LEFT_ARM, 4, CRIT_DATA_MEMBER_MESSAGE_ID, 7102);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_LEFT_ARM, 5, CRIT_DATA_MEMBER_MESSAGE_ID, 7102);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_RIGHT_ARM, 0, CRIT_DATA_MEMBER_MESSAGE_ID, 5008);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_RIGHT_ARM, 1, CRIT_DATA_MEMBER_MESSAGE_ID, 5008);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_RIGHT_ARM, 2, CRIT_DATA_MEMBER_MESSAGE_ID, 5009);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_RIGHT_ARM, 3, CRIT_DATA_MEMBER_MESSAGE_ID, 5009);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_RIGHT_ARM, 4, CRIT_DATA_MEMBER_MESSAGE_ID, 7102);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_RIGHT_ARM, 5, CRIT_DATA_MEMBER_MESSAGE_ID, 7102);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_TORSO, 4, CRIT_DATA_MEMBER_MESSAGE_ID, 7101);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_TORSO, 5, CRIT_DATA_MEMBER_MESSAGE_ID, 7101);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_RIGHT_LEG, 0, CRIT_DATA_MEMBER_MESSAGE_ID, 5023);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_RIGHT_LEG, 1, CRIT_DATA_MEMBER_MESSAGE_ID, 7101);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_RIGHT_LEG, 1, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_MESSAGE_ID, 7103);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_RIGHT_LEG, 2, CRIT_DATA_MEMBER_MESSAGE_ID, 7101);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_RIGHT_LEG, 2, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_MESSAGE_ID, 7103);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_RIGHT_LEG, 3, CRIT_DATA_MEMBER_MESSAGE_ID, 7103);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_RIGHT_LEG, 4, CRIT_DATA_MEMBER_MESSAGE_ID, 7103);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_RIGHT_LEG, 5, CRIT_DATA_MEMBER_MESSAGE_ID, 7103);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_LEFT_LEG, 0, CRIT_DATA_MEMBER_MESSAGE_ID, 5023);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_LEFT_LEG, 1, CRIT_DATA_MEMBER_MESSAGE_ID, 7101);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_LEFT_LEG, 1, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_MESSAGE_ID, 7103);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_LEFT_LEG, 2, CRIT_DATA_MEMBER_MESSAGE_ID, 7101);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_LEFT_LEG, 2, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_MESSAGE_ID, 7103);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_LEFT_LEG, 3, CRIT_DATA_MEMBER_MESSAGE_ID, 7103);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_LEFT_LEG, 4, CRIT_DATA_MEMBER_MESSAGE_ID, 7103);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_LEFT_LEG, 5, CRIT_DATA_MEMBER_MESSAGE_ID, 7103);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_EYES, 0, CRIT_DATA_MEMBER_MESSAGE_ID, 5027);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_EYES, 1, CRIT_DATA_MEMBER_MESSAGE_ID, 5027);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_EYES, 2, CRIT_DATA_MEMBER_MESSAGE_ID, 5027);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_EYES, 3, CRIT_DATA_MEMBER_MESSAGE_ID, 5027);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_EYES, 4, CRIT_DATA_MEMBER_MESSAGE_ID, 7104);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_EYES, 5, CRIT_DATA_MEMBER_MESSAGE_ID, 7104);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_GROIN, 0, CRIT_DATA_MEMBER_MESSAGE_ID, 5033);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_GROIN, 1, CRIT_DATA_MEMBER_MESSAGE_ID, 5027);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_GROIN, 1, CRIT_DATA_MEMBER_MASSIVE_CRITICAL_MESSAGE_ID, 7101);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_GROIN, 2, CRIT_DATA_MEMBER_MESSAGE_ID, 7101);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_GROIN, 3, CRIT_DATA_MEMBER_MESSAGE_ID, 7101);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_GROIN, 4, CRIT_DATA_MEMBER_MESSAGE_ID, 7101);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_GROIN, 5, CRIT_DATA_MEMBER_MESSAGE_ID, 7101);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_UNCALLED, 2, CRIT_DATA_MEMBER_DAMAGE_MULTIPLIER, 3);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_UNCALLED, 4, CRIT_DATA_MEMBER_DAMAGE_MULTIPLIER, 4);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_UNCALLED, 4, CRIT_DATA_MEMBER_MESSAGE_ID, 7101);
criticalsSetValue(KILL_TYPE_BIG_BAD_BOSS, HIT_LOCATION_UNCALLED, 5, CRIT_DATA_MEMBER_MESSAGE_ID, 7101);
}
if (mode == 1 || mode == 3) {
Config criticalsConfig;
if (configInit(&criticalsConfig)) {
char* criticalsConfigFilePath;
configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_OVERRIDE_CRITICALS_FILE_KEY, &criticalsConfigFilePath);
if (criticalsConfigFilePath != NULL && *criticalsConfigFilePath == '\0') {
criticalsConfigFilePath = NULL;
}
if (configRead(&criticalsConfig, criticalsConfigFilePath, false)) {
if (mode == 1) {
char sectionKey[16];
// Read original kill types (19) plus one for the player.
for (int killType = 0; killType < KILL_TYPE_COUNT + 1; killType++) {
for (int hitLocation = 0; hitLocation < HIT_LOCATION_COUNT; hitLocation++) {
for (int effect = 0; effect < CRTICIAL_EFFECT_COUNT; effect++) {
sprintf_s(sectionKey, "c_%02d_%d_%d", killType, hitLocation, effect);
// Update player kill type if needed.
int newKillType = killType == KILL_TYPE_COUNT ? SFALL_KILL_TYPE_COUNT : killType;
for (int dataMember = 0; dataMember < CRIT_DATA_MEMBER_COUNT; dataMember++) {
int value = criticalsGetValue(newKillType, hitLocation, effect, dataMember);
if (configGetInt(&criticalsConfig, sectionKey, gCritDataMemberKeys[dataMember], &value)) {
criticalsSetValue(newKillType, hitLocation, effect, dataMember, value);
}
}
}
}
}
} else if (mode == 3) {
char ktSectionKey[32];
char hitLocationSectionKey[32];
char key[32];
// Read Sfall kill types (38) plus one for the player.
for (int killType = 0; killType < SFALL_KILL_TYPE_COUNT + 1; killType++) {
sprintf_s(ktSectionKey, "c_%02d", killType);
int enabled = 0;
configGetInt(&criticalsConfig, ktSectionKey, "Enabled", &enabled);
if (enabled == 0) {
continue;
}
for (int hitLocation = 0; hitLocation < HIT_LOCATION_COUNT; hitLocation++) {
if (enabled < 2) {
bool hitLocationChanged = false;
sprintf_s(key, "Part_%d", hitLocation);
configGetBool(&criticalsConfig, ktSectionKey, key, &hitLocationChanged);
if (!hitLocationChanged) {
continue;
}
}
sprintf_s(hitLocationSectionKey, "c_%02d_%d", killType, hitLocation);
for (int effect = 0; effect < CRTICIAL_EFFECT_COUNT; effect++) {
for (int dataMember = 0; dataMember < CRIT_DATA_MEMBER_COUNT; dataMember++) {
int value = criticalsGetValue(killType, hitLocation, effect, dataMember);
sprintf_s(key, "e%d_%s", effect, gCritDataMemberKeys[dataMember]);
if (configGetInt(&criticalsConfig, hitLocationSectionKey, key, &value)) {
criticalsSetValue(killType, hitLocation, effect, dataMember, value);
}
}
}
}
}
}
}
configFree(&criticalsConfig);
}
}
memcpy(gBaseCriticalHitTables, gCriticalHitTables, sizeof(gCriticalHitTables));
memcpy(gBasePlayerCriticalHitTable, gPlayerCriticalHitTable, sizeof(gPlayerCriticalHitTable));
}
static void criticalsReset()
{
memcpy(gCriticalHitTables, gBaseCriticalHitTables, sizeof(gBaseCriticalHitTables));
memcpy(gPlayerCriticalHitTable, gBasePlayerCriticalHitTable, sizeof(gBasePlayerCriticalHitTable));
}
static void criticalsExit()
{
criticalsReset();
}
int criticalsGetValue(int killType, int hitLocation, int effect, int dataMember)
{
if (killType == SFALL_KILL_TYPE_COUNT) {
return gPlayerCriticalHitTable[hitLocation][effect].values[dataMember];
} else {
return gCriticalHitTables[killType][hitLocation][effect].values[dataMember];
}
}
void criticalsSetValue(int killType, int hitLocation, int effect, int dataMember, int value)
{
if (killType == SFALL_KILL_TYPE_COUNT) {
gPlayerCriticalHitTable[hitLocation][effect].values[dataMember] = value;
} else {
gCriticalHitTables[killType][hitLocation][effect].values[dataMember] = value;
}
}
void criticalsResetValue(int killType, int hitLocation, int effect, int dataMember)
{
if (killType == SFALL_KILL_TYPE_COUNT) {
gPlayerCriticalHitTable[hitLocation][effect].values[dataMember] = gBasePlayerCriticalHitTable[hitLocation][effect].values[dataMember];
} else {
gCriticalHitTables[killType][hitLocation][effect].values[dataMember] = gBaseCriticalHitTables[killType][hitLocation][effect].values[dataMember];
}
}

View File

@ -56,6 +56,10 @@ int _combat_explode_scenery(Object* a1, Object* a2);
void _combat_delete_critter(Object* obj);
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);
static inline bool isInCombat()
{
return (gCombatState & COMBAT_STATE_0x01) != 0;

View File

@ -121,25 +121,41 @@ typedef struct Attack {
int extrasKnockback[EXPLOSION_TARGET_COUNT];
} Attack;
typedef enum CriticalHitDescriptionDataMember {
CRIT_DATA_MEMBER_DAMAGE_MULTIPLIER,
CRIT_DATA_MEMBER_FLAGS,
CRIT_DATA_MEMBER_MASSIVE_CRITICAL_STAT,
CRIT_DATA_MEMBER_MASSIVE_CRITICAL_STAT_MODIFIER,
CRIT_DATA_MEMBER_MASSIVE_CRITICAL_FLAGS,
CRIT_DATA_MEMBER_MESSAGE_ID,
CRIT_DATA_MEMBER_MASSIVE_CRITICAL_MESSAGE_ID,
CRIT_DATA_MEMBER_COUNT,
} CriticalHitDescriptionDataMember;
// Provides metadata about critical hit effect.
typedef struct CriticalHitDescription {
int damageMultiplier;
typedef union CriticalHitDescription {
struct {
int damageMultiplier;
// Damage flags that will be applied to defender.
int flags;
// Damage flags that will be applied to defender.
int flags;
// Stat to check to upgrade this critical hit to massive critical hit or
// -1 if there is no massive critical hit.
int massiveCriticalStat;
// Stat to check to upgrade this critical hit to massive critical hit or
// -1 if there is no massive critical hit.
int massiveCriticalStat;
// Bonus/penalty to massive critical stat.
int massiveCriticalStatModifier;
// Bonus/penalty to massive critical stat.
int massiveCriticalStatModifier;
// Additional damage flags if this critical hit become massive critical.
int massiveCriticalFlags;
// Additional damage flags if this critical hit become massive critical.
int massiveCriticalFlags;
int messageId;
int massiveCriticalMessageId;
int messageId;
int massiveCriticalMessageId;
};
// SFALL: Allow indexed access to the data above.
int values[CRIT_DATA_MEMBER_COUNT];
} CriticalHitDescription;
#endif /* COMBAT_DEFS_H */

View File

@ -122,6 +122,11 @@ enum {
KILL_TYPE_GIANT_ANT,
KILL_TYPE_BIG_BAD_BOSS,
KILL_TYPE_COUNT,
// Sfall has the option to treat kill type numbers as shorts, thus doubling
// number of kill types it can deal with without breaking backwards
// compatibility.
SFALL_KILL_TYPE_COUNT = KILL_TYPE_COUNT * 2,
};
enum {

View File

@ -3,6 +3,7 @@
#include "debug.h"
#include "platform_compat.h"
#include "scripts.h"
#include "sfall_config.h"
#include <limits.h>
#include <stdlib.h>
@ -97,11 +98,15 @@ static int randomTranslateRoll(int delta, int criticalSuccessModifier)
{
int gameTime = gameTimeGetTime();
// SFALL: Remove criticals time limits.
bool criticalsTimeLimitsRemoved = false;
configGetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_REMOVE_CRITICALS_TIME_LIMITS_KEY, &criticalsTimeLimitsRemoved);
int roll;
if (delta < 0) {
roll = ROLL_FAILURE;
if ((gameTime / GAME_TIME_TICKS_PER_DAY) >= 1) {
if (criticalsTimeLimitsRemoved || (gameTime / GAME_TIME_TICKS_PER_DAY) >= 1) {
// 10% to become critical failure.
if (randomBetween(1, 100) <= -delta / 10) {
roll = ROLL_CRITICAL_FAILURE;
@ -110,7 +115,7 @@ static int randomTranslateRoll(int delta, int criticalSuccessModifier)
} else {
roll = ROLL_SUCCESS;
if ((gameTime / GAME_TIME_TICKS_PER_DAY) >= 1) {
if (criticalsTimeLimitsRemoved || (gameTime / GAME_TIME_TICKS_PER_DAY) >= 1) {
// 10% + modifier to become critical success.
if (randomBetween(1, 100) <= delta / 10 + criticalSuccessModifier) {
roll = ROLL_CRITICAL_SUCCESS;

View File

@ -34,6 +34,7 @@ bool sfallConfigInit(int argc, char** argv)
configSetInt(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_SKIP_OPENING_MOVIES_KEY, 0);
configSetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_STARTING_MAP_KEY, "");
configSetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DISPLAY_KARMA_CHANGES_KEY, false);
configSetBool(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_REMOVE_CRITICALS_TIME_LIMITS_KEY, false);
char path[COMPAT_MAX_PATH];
char* executable = argv[0];

View File

@ -22,6 +22,9 @@
#define SFALL_CONFIG_KARMA_FRMS_KEY "KarmaFRMs"
#define SFALL_CONFIG_KARMA_POINTS_KEY "KarmaPoints"
#define SFALL_CONFIG_DISPLAY_KARMA_CHANGES_KEY "DisplayKarmaChanges"
#define SFALL_CONFIG_OVERRIDE_CRITICALS_MODE_KEY "OverrideCriticalTable"
#define SFALL_CONFIG_OVERRIDE_CRITICALS_FILE_KEY "OverrideCriticalFile"
#define SFALL_CONFIG_REMOVE_CRITICALS_TIME_LIMITS_KEY "RemoveCriticalTimelimits"
extern bool gSfallConfigInitialized;
extern Config gSfallConfig;