diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index 1750e689..49b7dd20 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -579,10 +579,13 @@ Some utility/math functions are available: 1 - changes the duration of the addiction effect for the drug (a value of 1 = one game minute) > int sfall_func1("set_unique_id", object) -- assigns an unique ID number to the object and returns it. If an unique ID number has already been assigned to an object, then ID number is returned without reassignment +> int sfall_func2("set_unique_id", object, int flag) +- assigns a unique ID number to the object and returns it. If a unique ID number has already been assigned to an object, then ID number is returned without reassignment +- items with unique IDs will not stack with other items of the same type in the inventory - to just get the current ID number of an object, use sfall_func2("get_object_data", object, OBJ_DATA_ID) - unique ID numbers are saved in your savegame, and have a range from 0x10000000 to 0x7FFFFFFF -- there is also an unique ID number range for the player and party members from 18000 to 83535 +- there is also a unique ID number range for the player and party members from 18000 to 83535 +- to assign a new ID number generated by the engine to the object (i.e. unassign a unique ID), call the function with two arguments and pass -1 for the flag argument ------------------------ ------ MORE INFO ------- diff --git a/sfall/Modules/Combat.cpp b/sfall/Modules/Combat.cpp index cb2be1b9..de9f1ea7 100644 --- a/sfall/Modules/Combat.cpp +++ b/sfall/Modules/Combat.cpp @@ -32,15 +32,9 @@ namespace sfall static std::vector noBursts; // object id -struct KnockbackModifier { - long id; - DWORD type; - double value; -}; - static std::vector mTargets; static std::vector mAttackers; -static std::vector mWeapons; +std::vector Combat::mWeapons; static std::vector hitChanceMods; static ChanceModifier baseHitChance; @@ -168,7 +162,7 @@ static double ApplyModifiers(std::vector* mods, fo::GameObjec static DWORD __fastcall CalcKnockbackMod(int knockValue, int damage, fo::GameObject* weapon, fo::GameObject* attacker, fo::GameObject* target) { double result = (double)damage / (double)knockValue; - result = ApplyModifiers(&mWeapons, weapon, result); + result = ApplyModifiers(&Combat::mWeapons, weapon, result); result = ApplyModifiers(&mAttackers, attacker, result); result = ApplyModifiers(&mTargets, target, result); return (DWORD)floor(result); @@ -250,19 +244,25 @@ void _stdcall KnockbackSetMod(fo::GameObject* object, DWORD type, float val, DWO std::vector* mods; switch (on) { case 0: - mods = &mWeapons; + if (object->Type() != fo::OBJ_TYPE_ITEM) return; + mods = &Combat::mWeapons; break; case 1: + if (object->Type() != fo::OBJ_TYPE_CRITTER) return; mods = &mTargets; break; case 2: + if (object->Type() != fo::OBJ_TYPE_CRITTER) return; mods = &mAttackers; break; default: return; } - long id = Objects::SetObjectUniqueID(object); + long id = (on == 0) + ? Objects::SetSpecialID(object) + : Objects::SetObjectUniqueID(object); + KnockbackModifier mod = { id, type, (double)val }; for (DWORD i = 0; i < mods->size(); i++) { if ((*mods)[i].id == id) { @@ -275,11 +275,9 @@ void _stdcall KnockbackSetMod(fo::GameObject* object, DWORD type, float val, DWO void _stdcall KnockbackRemoveMod(fo::GameObject* object, DWORD on) { std::vector* mods; - int id = object->id; switch (on) { case 0: - object->id = fo::func::new_obj_id(); // revert to engine range id - mods = &mWeapons; + mods = &Combat::mWeapons; break; case 1: mods = &mTargets; @@ -291,8 +289,9 @@ void _stdcall KnockbackRemoveMod(fo::GameObject* object, DWORD on) { return; } for (DWORD i = 0; i < mods->size(); i++) { - if ((*mods)[i].id == id) { + if ((*mods)[i].id == object->id) { mods->erase(mods->begin() + i); + if (on == 0) Objects::SetNewEngineID(object); // revert to engine range id return; } } @@ -304,7 +303,7 @@ void _stdcall SetHitChanceMax(fo::GameObject* critter, DWORD maximum, DWORD mod) baseHitChance.mod = mod; return; } - + if (critter->Type() != fo::OBJ_TYPE_CRITTER) return; long id = Objects::SetObjectUniqueID(critter); for (DWORD i = 0; i < hitChanceMods.size(); i++) { if (id == hitChanceMods[i].id) { @@ -317,7 +316,7 @@ void _stdcall SetHitChanceMax(fo::GameObject* critter, DWORD maximum, DWORD mod) } void _stdcall SetNoBurstMode(fo::GameObject* critter, bool on) { - if (critter == fo::var::obj_dude) return; + if (critter == fo::var::obj_dude || critter->Type() != fo::OBJ_TYPE_CRITTER) return; long id = Objects::SetObjectUniqueID(critter); for (size_t i = 0; i < noBursts.size(); i++) { @@ -393,7 +392,7 @@ static void Combat_OnGameLoad() { baseHitChance.SetDefault(); mTargets.clear(); mAttackers.clear(); - mWeapons.clear(); + Combat::mWeapons.clear(); hitChanceMods.clear(); noBursts.clear(); disabledAS.clear(); diff --git a/sfall/Modules/Combat.h b/sfall/Modules/Combat.h index 0bd55d48..e430d59b 100644 --- a/sfall/Modules/Combat.h +++ b/sfall/Modules/Combat.h @@ -23,10 +23,18 @@ namespace sfall { +struct KnockbackModifier { + long id; + DWORD type; + double value; +}; + class Combat : public Module { public: const char* name() { return "Combat"; } void init(); + + static std::vector mWeapons; }; struct ChanceModifier { diff --git a/sfall/Modules/Objects.cpp b/sfall/Modules/Objects.cpp index ec28f1c6..9828eff0 100644 --- a/sfall/Modules/Objects.cpp +++ b/sfall/Modules/Objects.cpp @@ -18,6 +18,7 @@ #include "..\main.h" #include "..\FalloutEngine\Fallout2.h" +#include "Combat.h" #include "LoadGameHook.h" #include "Objects.h" @@ -28,7 +29,7 @@ namespace sfall static int unjamTimeState; static int maxCountProto = 512; -long Objects::uniqueID = UniqueID::Start; // saving to sfallgv.sav +long Objects::uniqueID = UniqueID::Start; // current counter id, saving to sfallgv.sav // Assigns a new unique identifier to an object if it has not been previously assigned // the identifier is saved with the object in the saved game and this can used in various script @@ -42,6 +43,46 @@ long Objects::SetObjectUniqueID(fo::GameObject* obj) { return uniqueID; } +// Assigns a unique ID in the negative range (0x8FFFFFFF - 0xFFFFFFFE) +long Objects::SetSpecialID(fo::GameObject* obj) { + long id = obj->id; + if (id < -1 || id > UniqueID::Start) return id; + + if ((DWORD)uniqueID >= UniqueID::End) uniqueID = UniqueID::Start; + id = ++uniqueID + UniqueID::End; + obj->id = id; + return id; +} + +void Objects::SetNewEngineID(fo::GameObject* obj) { + if (obj->id > UniqueID::Start) return; + obj->id = fo::func::new_obj_id(); +} + +static bool __fastcall CheckSpecialID(long id) { + if (Combat::mWeapons.empty()) return false; + for (auto& weapon : Combat::mWeapons) { + if (id == weapon.id) return true; + } + return false; +} + +static void __declspec(naked) item_identical_hack() { + using namespace fo::Fields; + __asm { + mov ecx, [edi]; // item id + cmp ecx, Start; // start unique ID + jg notIdentical; + call CheckSpecialID; + test al, al; + jnz notIdentical; + mov eax, [esi + scriptId]; + cmp eax, ebx; +notIdentical: + retn; // if ZF == 0 then item is not identical + } +} + static void __declspec(naked) new_obj_id_hook() { __asm { mov eax, 83535; @@ -119,6 +160,10 @@ void Objects::init() { HookCall(0x4A38A5, new_obj_id_hook); SafeWrite8(0x4A38B3, 0x90); // fix ID increment + + //if (GetConfigInt("Misc", "StackableUniqueItems", 0) == 0) { + MakeCall(0x477A0E, item_identical_hack); // don't put to item stack + //} } } diff --git a/sfall/Modules/Objects.h b/sfall/Modules/Objects.h index c4e21f37..f93be94c 100644 --- a/sfall/Modules/Objects.h +++ b/sfall/Modules/Objects.h @@ -18,6 +18,9 @@ public: static long uniqueID; static long SetObjectUniqueID(fo::GameObject* obj); + static long SetSpecialID(fo::GameObject* obj); + static void SetNewEngineID(fo::GameObject* obj); + static void SetAutoUnjamLockTime(DWORD time); static void LoadProtoAutoMaxLimit(); }; diff --git a/sfall/Modules/Scripting/Handlers/Metarule.cpp b/sfall/Modules/Scripting/Handlers/Metarule.cpp index 41414249..a30fa78c 100644 --- a/sfall/Modules/Scripting/Handlers/Metarule.cpp +++ b/sfall/Modules/Scripting/Handlers/Metarule.cpp @@ -127,7 +127,7 @@ static const SfallMetarule metarules[] = { {"set_outline", sf_set_outline, 2, 2, {ARG_OBJECT, ARG_INT}}, {"set_rest_heal_time", sf_set_rest_heal_time, 1, 1, {ARG_INT}}, {"set_rest_mode", sf_set_rest_mode, 1, 1, {ARG_INT}}, - {"set_unique_id", sf_set_unique_id, 1, 1, {ARG_OBJECT}}, + {"set_unique_id", sf_set_unique_id, 1, 2, {ARG_OBJECT, ARG_INT}}, {"set_unjam_locks_time", sf_set_unjam_locks_time, 1, 1, {ARG_INT}}, {"spatial_radius", sf_spatial_radius, 1, 1, {ARG_OBJECT}}, {"tile_refresh_display", sf_tile_refresh_display, 0, 0}, diff --git a/sfall/Modules/Scripting/Handlers/Objects.cpp b/sfall/Modules/Scripting/Handlers/Objects.cpp index b07df25d..079016a2 100644 --- a/sfall/Modules/Scripting/Handlers/Objects.cpp +++ b/sfall/Modules/Scripting/Handlers/Objects.cpp @@ -520,7 +520,15 @@ void sf_set_drugs_data(OpcodeContext& ctx) { } void sf_set_unique_id(OpcodeContext& ctx) { - ctx.setReturn(Objects::SetObjectUniqueID(ctx.arg(0).asObject())); + fo::GameObject* obj = ctx.arg(0).asObject(); + long id; + if (ctx.arg(1).asInt() == -1) { + id = fo::func::new_obj_id(); + obj->id = id; + } else { + id = Objects::SetObjectUniqueID(obj); + } + ctx.setReturn(id); } }