mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
Added an option about max HP calculation to the stats ini file
Added an update to the HP stats when the map is loaded first time. Code edits to Animations.cpp. Minor code edits.
This commit is contained in:
@@ -6,6 +6,10 @@
|
||||
;multi[x] - where x is from 0 to 6, the multiplier of st/pe/en/etc.
|
||||
;shift[x] - shifts the base stat before the multiplication
|
||||
|
||||
[Main]
|
||||
;Set to 1 to take the bonus values of SPECIAL stats into account when calculating maximum hit points
|
||||
;Set to 0 for the original behavior (bonus stats do not affect maximum hit points)
|
||||
HPDependOnBonusStats=0
|
||||
|
||||
;max hp
|
||||
[7]
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ static std::tr1::unordered_map<TGameObj*, TGameObj*> sources;
|
||||
|
||||
// Returns the friendly critter or any blocking object in the line of fire
|
||||
TGameObj* AI_CheckShootAndFriendlyInLineOfFire(TGameObj* object, long targetTile, long team) {
|
||||
if (object && object->Type() == OBJ_TYPE_CRITTER && object->critter.teamNum != team) { // is not friendly fire
|
||||
if (object && object->IsCritter() && object->critter.teamNum != team) { // is not friendly fire
|
||||
long objTile = object->tile;
|
||||
if (objTile == targetTile) return nullptr;
|
||||
|
||||
|
||||
+7
-10
@@ -22,14 +22,14 @@
|
||||
#include "FalloutEngine.h"
|
||||
#include "LoadGameHook.h"
|
||||
|
||||
static const int animRecordSize = 2656;
|
||||
static const int animRecordSize = sizeof(AnimationSet);
|
||||
static const int sadSize = 3240;
|
||||
|
||||
static int animationLimit = 32;
|
||||
|
||||
//pointers to new animation struct arrays
|
||||
static BYTE *anim_set;
|
||||
static BYTE *sad;
|
||||
static std::vector<AnimationSet> new_anim_set;
|
||||
static std::vector<BYTE> new_sad;
|
||||
|
||||
static DWORD animSetAddr = FO_VAR_anim_set;
|
||||
static DWORD sadAddr = FO_VAR_sad;
|
||||
@@ -241,11 +241,11 @@ void ApplyAnimationsAtOncePatches(signed char aniMax) {
|
||||
if (aniMax <= 32) return;
|
||||
|
||||
//allocate memory to store larger animation struct arrays
|
||||
anim_set = new BYTE[animRecordSize * (aniMax + 1)];
|
||||
sad = new BYTE[sadSize * (aniMax + 1)];
|
||||
new_anim_set.resize(aniMax + 1);
|
||||
new_sad.resize(sadSize * (aniMax + 1));
|
||||
|
||||
animSetAddr = reinterpret_cast<DWORD>(anim_set);
|
||||
sadAddr = reinterpret_cast<DWORD>(sad);
|
||||
animSetAddr = reinterpret_cast<DWORD>(new_anim_set.data());
|
||||
sadAddr = reinterpret_cast<DWORD>(new_sad.data());
|
||||
|
||||
//set general animation limit check (old 20) aniMax-12 -- +12 reserved for PC movement(4) + other critical animations(8)?
|
||||
SafeWrite8(0x413C07, aniMax - 12);
|
||||
@@ -362,7 +362,4 @@ void Animations_Init() {
|
||||
}
|
||||
|
||||
void Animations_Exit() {
|
||||
if (animationLimit <= 32) return;
|
||||
delete[] anim_set;
|
||||
delete[] sad;
|
||||
}
|
||||
|
||||
+2
-2
@@ -1387,7 +1387,7 @@ end:
|
||||
static int __stdcall ItemCountFix(TGameObj* who, TGameObj* item) {
|
||||
int count = 0;
|
||||
for (int i = 0; i < who->invenSize; i++) {
|
||||
TGameObj::TInvenRec* tableItem = &who->invenTable[i];
|
||||
TGameObj::InvenItem* tableItem = &who->invenTable[i];
|
||||
if (tableItem->object == item) {
|
||||
count += tableItem->count;
|
||||
} else if (ItemGetType(tableItem->object) == item_type_container) {
|
||||
@@ -2058,7 +2058,7 @@ static void __stdcall combat_attack_gcsd() {
|
||||
if ((*ptr_main_ctd).targetDamage > (*ptr_gcsd)->maxDamage) {
|
||||
(*ptr_main_ctd).targetDamage = (*ptr_gcsd)->maxDamage;
|
||||
}
|
||||
if (damage > (*ptr_main_ctd).targetDamage && (*ptr_main_ctd).target->Type() == OBJ_TYPE_CRITTER) {
|
||||
if (damage > (*ptr_main_ctd).targetDamage && (*ptr_main_ctd).target->IsCritter()) {
|
||||
long cHP = (*ptr_main_ctd).target->critter.health;
|
||||
if (cHP > (*ptr_gcsd)->maxDamage && cHP <= damage) {
|
||||
(*ptr_main_ctd).targetFlags &= ~DAM_DEAD; // unset
|
||||
|
||||
@@ -104,7 +104,7 @@ static void RunEditorInternal(SOCKET &s) {
|
||||
for (int tile = 0; tile < 40000; tile++) {
|
||||
TGameObj* obj = ObjFindFirstAtTile(elv, tile);
|
||||
while (obj) {
|
||||
if (obj->Type() == OBJ_TYPE_CRITTER) {
|
||||
if (obj->IsCritter()) {
|
||||
vec.push_back(reinterpret_cast<DWORD*>(obj));
|
||||
}
|
||||
obj = ObjFindNextAtTile();
|
||||
|
||||
@@ -187,6 +187,19 @@ enum GlobalVar : long
|
||||
GVAR_CAR_PLACED_TILE = 633,
|
||||
};
|
||||
|
||||
// Physical material type, used for items and tiles.
|
||||
enum Material : long
|
||||
{
|
||||
MAT_Glass = 0x0,
|
||||
MAT_Metal = 0x1,
|
||||
MAT_Plastic = 0x2,
|
||||
MAT_Wood = 0x3,
|
||||
MAT_Dirt = 0x4,
|
||||
MAT_Stone = 0x5,
|
||||
MAT_Cement = 0x6,
|
||||
MAT_Leather = 0x7
|
||||
};
|
||||
|
||||
namespace ObjectFlag {
|
||||
enum ObjectFlag : DWORD {
|
||||
Mouse_3d = 0x1,
|
||||
@@ -411,6 +424,16 @@ enum Trait : long
|
||||
TRAIT_count = 16,
|
||||
};
|
||||
|
||||
enum ScenerySubType : long
|
||||
{
|
||||
SCENTYPE_DOOR = 0,
|
||||
SCENTYPE_STAIRS = 1,
|
||||
SCENTYPE_ELEVATOR = 2,
|
||||
SCENTYPE_LADDER_BOTTOM = 3,
|
||||
SCENTYPE_LADDER_TOP = 4,
|
||||
SCENTYPE_GENERIC = 5
|
||||
};
|
||||
|
||||
// proto.h: stats //
|
||||
|
||||
enum Stat : long
|
||||
|
||||
@@ -182,6 +182,8 @@ WRAP_WATCOM_FUNC2(long, PerkLevel, perk_level_, TGameObj*, critter, long, perkId
|
||||
WRAP_WATCOM_FUNC6(long, PickDeath, pick_death_, TGameObj*, attacker, TGameObj*, target, TGameObj*, weapon, long, amount, long, anim, long, hitFromBack)
|
||||
WRAP_WATCOM_FUNC0(void, ProcessBk, process_bk_)
|
||||
WRAP_WATCOM_FUNC0(void, ProtoDudeUpdateGender, proto_dude_update_gender_)
|
||||
// Places pointer to a prototype structure into ptrPtr and returns 0 on success or -1 on failure
|
||||
WRAP_WATCOM_FUNC2(long, ProtoPtr, proto_ptr_, long, pid, sProto**, ptrPtr)
|
||||
WRAP_WATCOM_FUNC2(void, QueueClearType, queue_clear_type_, long, qType, void*, func) // removes all events of the given type and performs func before removal
|
||||
WRAP_WATCOM_FUNC2(void*, QueueFindFirst, queue_find_first_, TGameObj*, object, long, qType)
|
||||
WRAP_WATCOM_FUNC2(void*, QueueFindNext, queue_find_next_, TGameObj*, object, long, qType)
|
||||
@@ -204,7 +206,10 @@ WRAP_WATCOM_FUNC2(long, ScrPtr, scr_ptr_, long, scriptId, TScript**, scriptPtr)
|
||||
WRAP_WATCOM_FUNC1(long, ScrRemove, scr_remove_, long, scriptID)
|
||||
WRAP_WATCOM_FUNC1(void, SetFocusFunc, set_focus_func_, void*, func)
|
||||
WRAP_WATCOM_FUNC1(long, SkillIsTagged, skill_is_tagged_, long, skill)
|
||||
WRAP_WATCOM_FUNC2(long, StatGetBase, stat_get_base_, TGameObj*, critter, long, statID)
|
||||
WRAP_WATCOM_FUNC2(long, StatGetBaseDirect, stat_get_base_direct_, TGameObj*, critter, long, statID)
|
||||
//WRAP_WATCOM_FUNC2(void, SkillGetTags, skill_get_tags_, long*, tags, long, num)
|
||||
//WRAP_WATCOM_FUNC2(void, SkillSetTags, skill_set_tags_, long*, tags, long, num)
|
||||
WRAP_WATCOM_FUNC2(long, StatLevel, stat_level_, TGameObj*, critter, long, statId)
|
||||
WRAP_WATCOM_FUNC1(long, TextFont, text_font_, long, fontNum)
|
||||
WRAP_WATCOM_FUNC2(long, TileDist, tile_dist_, long, scrTile, long, dstTile)
|
||||
|
||||
+236
-1
@@ -46,6 +46,29 @@ struct sArt {
|
||||
long total;
|
||||
};
|
||||
|
||||
struct AnimationSet {
|
||||
long currentAnim;
|
||||
long counter;
|
||||
long animCounter;
|
||||
long flags;
|
||||
struct Animation {
|
||||
long number;
|
||||
long source;
|
||||
long target;
|
||||
long data1;
|
||||
long elevation;
|
||||
long animCode;
|
||||
long delay;
|
||||
long(__fastcall *callFunc)(DWORD, DWORD);
|
||||
long(__fastcall *callFunc3)(DWORD, DWORD);
|
||||
long flags;
|
||||
long data2;
|
||||
long frmPtr;
|
||||
} animations[55];
|
||||
};
|
||||
|
||||
static_assert(sizeof(AnimationSet) == 2656, "Incorrect AnimationSet definition.");
|
||||
|
||||
// Bounding rectangle, used by tile_refresh_rect and related functions.
|
||||
struct BoundRect {
|
||||
long x;
|
||||
@@ -77,7 +100,7 @@ struct TGameObj {
|
||||
long elevation;
|
||||
long invenSize;
|
||||
long invenMax;
|
||||
struct TInvenRec {
|
||||
struct InvenItem {
|
||||
TGameObj *object;
|
||||
long count;
|
||||
} *invenTable;
|
||||
@@ -509,6 +532,218 @@ struct PremadeChar {
|
||||
char unknown[20];
|
||||
};
|
||||
|
||||
// In-memory PROTO structure, not the same as PRO file format.
|
||||
struct sProto {
|
||||
struct Tile {
|
||||
long scriptId;
|
||||
Material material;
|
||||
};
|
||||
|
||||
struct Item {
|
||||
struct Weapon {
|
||||
long animationCode;
|
||||
long minDamage;
|
||||
long maxDamage;
|
||||
long damageType;
|
||||
long maxRange[2];
|
||||
long projectilePid;
|
||||
long minStrength;
|
||||
long movePointCost[2];
|
||||
long critFailTable;
|
||||
long perk;
|
||||
long burstRounds;
|
||||
long caliber;
|
||||
long ammoPid;
|
||||
long maxAmmo;
|
||||
// shot sound ID
|
||||
long soundId;
|
||||
long gap_68;
|
||||
};
|
||||
|
||||
struct Ammo {
|
||||
long caliber;
|
||||
long packSize;
|
||||
long acAdjust;
|
||||
long drAdjust;
|
||||
long damageMult;
|
||||
long damageDiv;
|
||||
char gap_3c[48];
|
||||
};
|
||||
|
||||
struct Armor {
|
||||
long armorClass;
|
||||
// for each DamageType
|
||||
long damageResistance[7];
|
||||
// for each DamageType
|
||||
long damageThreshold[7];
|
||||
long perk;
|
||||
long maleFID;
|
||||
long femaleFID;
|
||||
};
|
||||
|
||||
struct Container {
|
||||
// container size capacity (not weight)
|
||||
long maxSize;
|
||||
// 1 - has use animation, 0 - no animation
|
||||
long openFlags;
|
||||
};
|
||||
|
||||
struct Drug {
|
||||
long stats[3];
|
||||
long immediateEffect[3];
|
||||
struct DelayedEffect {
|
||||
// delay for the effect
|
||||
long duration;
|
||||
// effect amount for each stat
|
||||
long effect[3];
|
||||
} delayed[2];
|
||||
long addictionRate;
|
||||
long addictionEffect;
|
||||
long addictionOnset;
|
||||
char gap_68[4];
|
||||
};
|
||||
|
||||
struct Misc {
|
||||
long powerPid;
|
||||
long powerType;
|
||||
long maxCharges;
|
||||
};
|
||||
|
||||
struct Key {
|
||||
long keyCode;
|
||||
};
|
||||
|
||||
long flags;
|
||||
long flagsExt;
|
||||
long scriptId; // SID 0x0Y00XXXX: Y - type: 0=s_system, 1=s_spatial, 2=s_time, 3=s_item, 4=s_critter; XXXX - index in scripts.lst; 0xFFFFFFFF no attached script
|
||||
ItemType type;
|
||||
|
||||
union {
|
||||
Weapon weapon;
|
||||
Ammo ammo;
|
||||
Armor armor;
|
||||
Container container;
|
||||
Drug drug;
|
||||
Misc misc;
|
||||
Key key;
|
||||
};
|
||||
Material material; // should be at 0x6C
|
||||
long size;
|
||||
long weight;
|
||||
long cost;
|
||||
long inventoryFid;
|
||||
BYTE soundId;
|
||||
};
|
||||
|
||||
struct Critter {
|
||||
struct Stats {
|
||||
long strength;
|
||||
long perception;
|
||||
long endurance;
|
||||
long charisma;
|
||||
long intelligence;
|
||||
long agility;
|
||||
long luck;
|
||||
long health;
|
||||
// max move points (action points)
|
||||
long movePoints;
|
||||
long armorClass;
|
||||
// not used by engine
|
||||
long unarmedDamage;
|
||||
long meleeDamage;
|
||||
long carryWeight;
|
||||
long sequence;
|
||||
long healingRate;
|
||||
long criticalChance;
|
||||
long betterCriticals;
|
||||
// for each DamageType
|
||||
long damageThreshold[7];
|
||||
// for each DamageType
|
||||
long damageResistance[7];
|
||||
long radiationResistance;
|
||||
long poisonResistance;
|
||||
long age;
|
||||
long gender;
|
||||
};
|
||||
|
||||
long flags;
|
||||
long flagsExt;
|
||||
long scriptId;
|
||||
long critterFlags;
|
||||
|
||||
Stats base;
|
||||
Stats bonus;
|
||||
|
||||
long skills[SKILL_count];
|
||||
|
||||
long bodyType;
|
||||
long experience;
|
||||
long killType;
|
||||
long damageType;
|
||||
long headFid;
|
||||
long aiPacket;
|
||||
long teamNum;
|
||||
};
|
||||
|
||||
struct Scenery {
|
||||
struct Door {
|
||||
long openFlags;
|
||||
long keyCode;
|
||||
};
|
||||
struct Stairs {
|
||||
long elevationAndTile;
|
||||
long mapId;
|
||||
};
|
||||
struct Elevator {
|
||||
long id;
|
||||
long level;
|
||||
};
|
||||
|
||||
long flags;
|
||||
long flagsExt;
|
||||
long scriptId;
|
||||
ScenerySubType type;
|
||||
union {
|
||||
Door door;
|
||||
Stairs stairs;
|
||||
Elevator elevator;
|
||||
};
|
||||
Material material;
|
||||
char gap_30[4];
|
||||
BYTE soundId;
|
||||
};
|
||||
|
||||
struct Wall {
|
||||
long flags;
|
||||
long flagsExt;
|
||||
long scriptId;
|
||||
Material material;
|
||||
};
|
||||
|
||||
struct Misc {
|
||||
long flags;
|
||||
long flagsExt;
|
||||
};
|
||||
|
||||
long pid;
|
||||
long messageNum;
|
||||
long fid;
|
||||
// range 0-8 in hexes
|
||||
long lightDistance;
|
||||
// range 0 - 65536
|
||||
long lightIntensity;
|
||||
union {
|
||||
Tile tile;
|
||||
Item item;
|
||||
Critter critter;
|
||||
Scenery scenery;
|
||||
Wall wall;
|
||||
Misc misc;
|
||||
};
|
||||
};
|
||||
|
||||
static_assert(offsetof(sProto, item) + offsetof(sProto::Item, material) == 0x6C, "Incorrect sProto definition.");
|
||||
|
||||
struct ScriptListInfoItem {
|
||||
char fileName[16];
|
||||
long numLocalVars;
|
||||
|
||||
+6
-2
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "main.h"
|
||||
#include "FalloutEngine.h"
|
||||
#include "Stats.h"
|
||||
|
||||
#include "Objects.h"
|
||||
|
||||
@@ -89,15 +90,18 @@ pickNewID: // skip PM range (18000 - 83535)
|
||||
}
|
||||
}
|
||||
|
||||
// Reassigns object IDs to all critters upon first loading a map
|
||||
// Reassigns object IDs to all critters upon first loading a map and updates their max HP stat
|
||||
static void map_fix_critter_id() {
|
||||
long npcStartID = 4096;
|
||||
TGameObj* obj = ObjFindFirst();
|
||||
while (obj) {
|
||||
if (obj->Type() == OBJ_TYPE_CRITTER && obj->id < PLAYER_ID) {
|
||||
if (obj->IsCritter()) {
|
||||
if (obj->id < PLAYER_ID) {
|
||||
obj->id = npcStartID++;
|
||||
SetScriptObjectID(obj);
|
||||
}
|
||||
Stats_UpdateHPStat(obj);
|
||||
}
|
||||
obj = ObjFindNext();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ static void __stdcall op_set_script2() {
|
||||
ScrRemove(object->scriptId);
|
||||
object->scriptId = 0xFFFFFFFF;
|
||||
}
|
||||
if (object->Type() == OBJ_TYPE_CRITTER) {
|
||||
if (object->IsCritter()) {
|
||||
scriptType = Scripts::SCRIPT_CRITTER;
|
||||
} else {
|
||||
scriptType = Scripts::SCRIPT_ITEM;
|
||||
@@ -283,7 +283,7 @@ static void __stdcall op_make_path2() {
|
||||
func = getBlockingFunc(type);
|
||||
|
||||
// if the object is not a critter, then there is no need to check tile (tileTo) for blocking
|
||||
long checkFlag = (objFrom->Type() == OBJ_TYPE_CRITTER);
|
||||
long checkFlag = (objFrom->IsCritter());
|
||||
|
||||
char pathData[800];
|
||||
long pathLength = MakePathFunc(objFrom, objFrom->tile, tileTo, pathData, checkFlag, (void*)func);
|
||||
@@ -362,7 +362,7 @@ static void __stdcall op_get_party_members2() {
|
||||
DWORD* partyMemberList = *ptr_partyMemberList;
|
||||
for (int i = 0; i < actualCount; i++) {
|
||||
TGameObj* obj = reinterpret_cast<TGameObj*>(partyMemberList[i * 4]);
|
||||
if (includeHidden || (obj->Type() == OBJ_TYPE_CRITTER && !CritterIsDead(obj) && !(obj->flags & ObjectFlag::Mouse_3d))) {
|
||||
if (includeHidden || (obj->IsCritter() && !CritterIsDead(obj) && !(obj->flags & ObjectFlag::Mouse_3d))) {
|
||||
arrays[arrayId].push_back((long)obj);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ static void __stdcall op_set_critter_base_stat2() {
|
||||
&valArg = opHandler.arg(2);
|
||||
|
||||
if (obj && statArg.isInt() && valArg.isInt()) {
|
||||
if (obj->Type() == OBJ_TYPE_CRITTER) {
|
||||
if (obj->IsCritter()) {
|
||||
int stat = statArg.rawValue();
|
||||
if (stat >= 0 && stat < STAT_max_stat) {
|
||||
char* proto = GetProtoPtr(obj->protoId);
|
||||
@@ -145,7 +145,7 @@ static void __stdcall op_set_critter_extra_stat2() {
|
||||
&valArg = opHandler.arg(2);
|
||||
|
||||
if (obj && statArg.isInt() && valArg.isInt()) {
|
||||
if (obj->Type() == OBJ_TYPE_CRITTER) {
|
||||
if (obj->IsCritter()) {
|
||||
int stat = statArg.rawValue();
|
||||
if (stat >= 0 && stat < STAT_max_stat) {
|
||||
char* proto = GetProtoPtr(obj->protoId);
|
||||
@@ -171,7 +171,7 @@ static void __stdcall op_get_critter_base_stat2() {
|
||||
const ScriptValue &statArg = opHandler.arg(1);
|
||||
|
||||
if (obj && statArg.isInt()) {
|
||||
if (obj->Type() == OBJ_TYPE_CRITTER) {
|
||||
if (obj->IsCritter()) {
|
||||
int stat = statArg.rawValue();
|
||||
if (stat >= 0 && stat < STAT_max_stat) {
|
||||
char* proto = GetProtoPtr(obj->protoId);
|
||||
@@ -198,7 +198,7 @@ static void __stdcall op_get_critter_extra_stat2() {
|
||||
const ScriptValue &statArg = opHandler.arg(1);
|
||||
|
||||
if (obj && statArg.isInt()) {
|
||||
if (obj->Type() == OBJ_TYPE_CRITTER) {
|
||||
if (obj->IsCritter()) {
|
||||
int stat = statArg.rawValue();
|
||||
if (stat >= 0 && stat < STAT_max_stat) {
|
||||
char* proto = GetProtoPtr(obj->protoId);
|
||||
@@ -383,7 +383,7 @@ static void __stdcall op_set_critter_current_ap2() {
|
||||
const ScriptValue &apArg = opHandler.arg(1);
|
||||
|
||||
if (obj && apArg.isInt()) {
|
||||
if (obj->Type() == OBJ_TYPE_CRITTER) {
|
||||
if (obj->IsCritter()) {
|
||||
long ap = apArg.rawValue();
|
||||
if (ap < 0) ap = 0;
|
||||
obj->critter.movePoints = ap;
|
||||
@@ -447,7 +447,7 @@ static void __stdcall op_set_critter_hit_chance_mod2() {
|
||||
&modArg = opHandler.arg(2);
|
||||
|
||||
if (obj && maxArg.isInt() && modArg.isInt()) {
|
||||
if (obj->Type() == OBJ_TYPE_CRITTER) {
|
||||
if (obj->IsCritter()) {
|
||||
SetHitChanceMax(obj, maxArg.rawValue(), modArg.rawValue());
|
||||
} else {
|
||||
opHandler.printOpcodeError(objNotCritter, "set_critter_hit_chance_mod");
|
||||
@@ -485,7 +485,7 @@ static void __stdcall op_set_critter_pickpocket_mod2() {
|
||||
&modArg = opHandler.arg(2);
|
||||
|
||||
if (obj && maxArg.isInt() && modArg.isInt()) {
|
||||
if (obj->Type() == OBJ_TYPE_CRITTER) {
|
||||
if (obj->IsCritter()) {
|
||||
SetPickpocketMax(obj, maxArg.rawValue(), modArg.rawValue());
|
||||
} else {
|
||||
opHandler.printOpcodeError(objNotCritter, "set_critter_pickpocket_mod");
|
||||
@@ -522,7 +522,7 @@ static void __stdcall op_set_critter_skill_mod2() {
|
||||
const ScriptValue &maxArg = opHandler.arg(1);
|
||||
|
||||
if (obj && maxArg.isInt()) {
|
||||
if (obj->Type() == OBJ_TYPE_CRITTER) {
|
||||
if (obj->IsCritter()) {
|
||||
SetSkillMax(obj, maxArg.rawValue());
|
||||
} else {
|
||||
opHandler.printOpcodeError(objNotCritter, "set_critter_skill_mod");
|
||||
|
||||
+50
-21
@@ -22,6 +22,9 @@
|
||||
|
||||
#include "Stats.h"
|
||||
|
||||
static bool engineDerivedStats = true;
|
||||
static bool derivedHPwBonus = false; // recalculate the hit points with bonus stat values
|
||||
|
||||
static DWORD statMaximumsPC[STAT_max_stat];
|
||||
static DWORD statMinimumsPC[STAT_max_stat];
|
||||
static DWORD statMaximumsNPC[STAT_max_stat];
|
||||
@@ -139,32 +142,31 @@ end:
|
||||
}
|
||||
}
|
||||
|
||||
static void __declspec(naked) __stdcall ProtoPtr(DWORD pid, int** proto) {
|
||||
__asm {
|
||||
mov eax, [esp + 4];
|
||||
mov edx, [esp + 8];
|
||||
call proto_ptr_;
|
||||
retn 8;
|
||||
static long __stdcall RecalcStat(int stat, int statsValue[]) {
|
||||
double sum = 0;
|
||||
for (int i = STAT_st; i <= STAT_lu; i++) {
|
||||
sum += (statsValue[i] + statFormulas[stat].shift[i]) * statFormulas[stat].multi[i];
|
||||
}
|
||||
long calcStatValue = statFormulas[stat].base + (int)floor(sum);
|
||||
return (calcStatValue < statFormulas[stat].min) ? statFormulas[stat].min : calcStatValue;
|
||||
}
|
||||
|
||||
static void __stdcall StatRecalcDerived(TGameObj* critter) {
|
||||
int baseStats[7];
|
||||
for (int stat = STAT_st; stat <= STAT_lu; stat++) baseStats[stat] = StatLevel(critter, stat);
|
||||
long* proto;
|
||||
if (ProtoPtr(critter->protoId, (sProto**)&proto) == -1) return;
|
||||
|
||||
int* proto;
|
||||
ProtoPtr(critter->protoId, &proto);
|
||||
|
||||
for (int i = STAT_max_hit_points; i <= STAT_poison_resist; i++) {
|
||||
if (i >= STAT_dmg_thresh && i <= STAT_dmg_resist_explosion) continue;
|
||||
|
||||
double sum = 0;
|
||||
int baseStats[7], levelStats[7];
|
||||
for (int stat = STAT_st; stat <= STAT_lu; stat++) {
|
||||
sum += (baseStats[stat] + statFormulas[i].shift[stat]) * statFormulas[i].multi[stat];
|
||||
levelStats[stat] = StatLevel(critter, stat);
|
||||
if (!derivedHPwBonus) baseStats[stat] = StatGetBase(critter, stat);
|
||||
}
|
||||
long calcStat = statFormulas[i].base + (int)floor(sum);
|
||||
if (calcStat < statFormulas[i].min) calcStat = statFormulas[i].min;
|
||||
proto[9 + i] = calcStat; // offset from base_stat_srength
|
||||
|
||||
((sProto*)proto)->critter.base.health = RecalcStat(STAT_max_hit_points, (derivedHPwBonus) ? levelStats : baseStats);
|
||||
|
||||
for (int stat = STAT_max_move_points; stat <= STAT_poison_resist; stat++) {
|
||||
if (stat >= STAT_dmg_thresh && stat <= STAT_dmg_resist_explosion) continue;
|
||||
// offset from base_stat_srength
|
||||
proto[9 + stat] = RecalcStat(stat, levelStats);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,6 +182,29 @@ static void __declspec(naked) stat_recalc_derived_hack() {
|
||||
}
|
||||
}
|
||||
|
||||
void Stats_UpdateHPStat(TGameObj* critter) {
|
||||
if (engineDerivedStats) return;
|
||||
|
||||
sProto* proto;
|
||||
if (ProtoPtr(critter->protoId, &proto) == -1) return;
|
||||
|
||||
auto getStatFunc = (derivedHPwBonus) ? StatLevel : StatGetBase;
|
||||
|
||||
double sum = 0;
|
||||
for (int stat = STAT_st; stat <= STAT_lu; stat++) {
|
||||
sum += (getStatFunc(critter, stat) + statFormulas[STAT_max_hit_points].shift[stat]) * statFormulas[STAT_max_hit_points].multi[stat];
|
||||
}
|
||||
long calcStatValue = statFormulas[STAT_max_hit_points].base + (int)floor(sum);
|
||||
if (calcStatValue < statFormulas[STAT_max_hit_points].min) calcStatValue = statFormulas[STAT_max_hit_points].min;
|
||||
|
||||
if (proto->critter.base.health != calcStatValue) {
|
||||
DebugPrintf("\nWarning: critter PID: %d, ID: %d, has an incorrect base value %d (must be %d) of the max HP stat.",
|
||||
critter->protoId, critter->id, proto->critter.base.health, calcStatValue);
|
||||
}
|
||||
proto->critter.base.health = calcStatValue;
|
||||
critter->critter.health = calcStatValue + proto->critter.bonus.health;
|
||||
}
|
||||
|
||||
static void __declspec(naked) stat_set_base_hack_allow() {
|
||||
static const DWORD StatSetBaseRet = 0x4AF559;
|
||||
__asm {
|
||||
@@ -301,7 +326,7 @@ void Stats_Init() {
|
||||
|
||||
MakeCall(0x4AF09C, CalcApToAcBonus, 3); // stat_level_
|
||||
|
||||
// Allow set_critter_stat function to change STAT_unused and STAT_dmg_* stats for the player
|
||||
// Allow set_critter_stat function to change the base stats of STAT_unused and STAT_dmg_* for the player
|
||||
MakeCall(0x4AF54E, stat_set_base_hack_allow);
|
||||
MakeCall(0x455D65, op_set_critter_stat_hack); // STAT_unused for other critters
|
||||
|
||||
@@ -334,6 +359,7 @@ void Stats_Init() {
|
||||
|
||||
std::string statsFile = GetConfigString("Misc", "DerivedStats", "", MAX_PATH);
|
||||
if (!statsFile.empty()) {
|
||||
engineDerivedStats = false;
|
||||
MakeJump(0x4AF6FC, stat_recalc_derived_hack); // overrides function
|
||||
|
||||
// STAT_st + STAT_en * 2 + 15
|
||||
@@ -364,10 +390,13 @@ void Stats_Init() {
|
||||
// STAT_en * 5
|
||||
statFormulas[STAT_poison_resist].multi[STAT_en] = 5; // poison resist
|
||||
|
||||
char key[6], buf2[256], buf3[256];
|
||||
const char* statFile = statsFile.insert(0, ".\\").c_str();
|
||||
if (GetFileAttributes(statFile) == INVALID_FILE_ATTRIBUTES) return;
|
||||
|
||||
derivedHPwBonus = (iniGetInt("Main", "HPDependOnBonusStats", 0, statFile) != 0);
|
||||
|
||||
char key[6], buf2[256], buf3[256];
|
||||
|
||||
for (int i = STAT_max_hit_points; i <= STAT_poison_resist; i++) {
|
||||
if (i >= STAT_dmg_thresh && i <= STAT_dmg_resist_explosion) continue;
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
void Stats_Init();
|
||||
void Stats_OnGameLoad();
|
||||
void Stats_UpdateHPStat(TGameObj* critter);
|
||||
|
||||
long __stdcall GetStatMax(int stat, int isNPC);
|
||||
long __stdcall GetStatMin(int stat, int isNPC);
|
||||
|
||||
Reference in New Issue
Block a user