mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
Further implementation of unique ID.
* weapons with knockback modifiers applied will have a special ID (negative value) that work as a temporary UID for the current game session. Added checks on object types to functions in Combat module. Added the second argument to set_unique_id script function.
This commit is contained in:
@@ -473,10 +473,13 @@ Some utility/math functions are available:
|
||||
- clears the cache of FRM image files loaded into memory
|
||||
|
||||
> 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 -------
|
||||
|
||||
+16
-17
@@ -27,15 +27,9 @@
|
||||
|
||||
static std::vector<long> NoBursts; // object id
|
||||
|
||||
struct KnockbackModifier {
|
||||
long id;
|
||||
DWORD type;
|
||||
double value;
|
||||
};
|
||||
|
||||
static std::vector<KnockbackModifier> mTargets;
|
||||
static std::vector<KnockbackModifier> mAttackers;
|
||||
static std::vector<KnockbackModifier> mWeapons;
|
||||
std::vector<KnockbackModifier> Knockback_mWeapons;
|
||||
|
||||
struct ChanceModifier {
|
||||
long id;
|
||||
@@ -73,7 +67,7 @@ static double ApplyModifiers(std::vector<KnockbackModifier>* mods, TGameObj* obj
|
||||
|
||||
static DWORD __fastcall CalcKnockbackMod(int knockValue, int damage, TGameObj* weapon, TGameObj* attacker, TGameObj* target) {
|
||||
double result = (double)damage / (double)knockValue;
|
||||
result = ApplyModifiers(&mWeapons, weapon, result);
|
||||
result = ApplyModifiers(&Knockback_mWeapons, weapon, result);
|
||||
result = ApplyModifiers(&mAttackers, attacker, result);
|
||||
result = ApplyModifiers(&mTargets, target, result);
|
||||
return (DWORD)floor(result);
|
||||
@@ -177,19 +171,25 @@ void _stdcall KnockbackSetMod(TGameObj* object, DWORD type, float val, DWORD on)
|
||||
std::vector<KnockbackModifier>* mods;
|
||||
switch (on) {
|
||||
case 0:
|
||||
mods = &mWeapons;
|
||||
if (object->pid >> 24 != OBJ_TYPE_ITEM) return;
|
||||
mods = &Knockback_mWeapons;
|
||||
break;
|
||||
case 1:
|
||||
if (object->pid >> 24 != OBJ_TYPE_CRITTER) return;
|
||||
mods = &mTargets;
|
||||
break;
|
||||
case 2:
|
||||
if (object->pid >> 24 != OBJ_TYPE_CRITTER) return;
|
||||
mods = &mAttackers;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
long id = SetObjectUniqueID(object);
|
||||
long id = (on == 0)
|
||||
? SetSpecialID(object)
|
||||
: SetObjectUniqueID(object);
|
||||
|
||||
KnockbackModifier mod = { id, type, (double)val };
|
||||
for (DWORD i = 0; i < mods->size(); i++) {
|
||||
if ((*mods)[i].id == id) {
|
||||
@@ -202,11 +202,9 @@ void _stdcall KnockbackSetMod(TGameObj* object, DWORD type, float val, DWORD on)
|
||||
|
||||
void _stdcall KnockbackRemoveMod(TGameObj* object, DWORD on) {
|
||||
std::vector<KnockbackModifier>* mods;
|
||||
int id = object->ID;
|
||||
switch (on) {
|
||||
case 0:
|
||||
object->ID = NewObjId(); // revert to engine range id
|
||||
mods = &mWeapons;
|
||||
mods = &Knockback_mWeapons;
|
||||
break;
|
||||
case 1:
|
||||
mods = &mTargets;
|
||||
@@ -218,8 +216,9 @@ void _stdcall KnockbackRemoveMod(TGameObj* 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) SetNewEngineID(object); // revert to engine range id
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -231,7 +230,7 @@ void _stdcall SetHitChanceMax(TGameObj* critter, DWORD maximum, DWORD mod) {
|
||||
BaseHitChance.mod = mod;
|
||||
return;
|
||||
}
|
||||
|
||||
if (critter->pid >> 24 != OBJ_TYPE_CRITTER) return;
|
||||
long id = SetObjectUniqueID(critter);
|
||||
for (DWORD i = 0; i < HitChanceMods.size(); i++) {
|
||||
if (id == HitChanceMods[i].id) {
|
||||
@@ -270,7 +269,7 @@ void _stdcall SetPickpocketMax(TGameObj* critter, DWORD maximum, DWORD mod) {
|
||||
}
|
||||
|
||||
void _stdcall SetNoBurstMode(TGameObj* critter, DWORD on) {
|
||||
if (critter == *ptr_obj_dude) return;
|
||||
if (critter == *ptr_obj_dude || critter->pid >> 24 != OBJ_TYPE_CRITTER) return;
|
||||
|
||||
long id = SetObjectUniqueID(critter);
|
||||
for (DWORD i = 0; i < NoBursts.size(); i++) {
|
||||
@@ -349,7 +348,7 @@ void Knockback_OnGameLoad() {
|
||||
BasePickpocket.mod = 0;
|
||||
mTargets.clear();
|
||||
mAttackers.clear();
|
||||
mWeapons.clear();
|
||||
Knockback_mWeapons.clear();
|
||||
HitChanceMods.clear();
|
||||
PickpocketMods.clear();
|
||||
NoBursts.clear();
|
||||
|
||||
@@ -18,6 +18,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
struct KnockbackModifier {
|
||||
long id;
|
||||
DWORD type;
|
||||
double value;
|
||||
};
|
||||
|
||||
extern std::vector<KnockbackModifier> Knockback_mWeapons;
|
||||
|
||||
void _stdcall SetPickpocketMax(TGameObj* critter, DWORD maximum, DWORD mod);
|
||||
void _stdcall SetHitChanceMax(TGameObj* critter, DWORD maximum, DWORD mod);
|
||||
void _stdcall KnockbackSetMod(TGameObj* object, DWORD type, float val, DWORD on);
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "FalloutEngine.h"
|
||||
#include "HookScripts.h"
|
||||
#include "input.h"
|
||||
#include "Knockback.h"
|
||||
#include "LoadGameHook.h"
|
||||
#include "Logging.h"
|
||||
#include "ScriptExtender.h"
|
||||
@@ -402,7 +403,7 @@ static const SfallOpcodeMetadata opcodeMetaArray[] = {
|
||||
{sf_set_map_enter_position, "set_map_enter_position", {DATATYPE_MASK_INT, DATATYPE_MASK_INT, DATATYPE_MASK_INT}},
|
||||
{sf_set_object_data, "set_object_data", {DATATYPE_MASK_VALID_OBJ, DATATYPE_MASK_INT, DATATYPE_MASK_INT}},
|
||||
{sf_set_outline, "set_outline", {DATATYPE_MASK_VALID_OBJ, DATATYPE_MASK_INT}},
|
||||
{sf_set_unique_id, "set_unique_id", {DATATYPE_MASK_VALID_OBJ}},
|
||||
{sf_set_unique_id, "set_unique_id", {DATATYPE_MASK_VALID_OBJ, DATATYPE_MASK_INT}},
|
||||
{sf_spatial_radius, "spatial_radius", {DATATYPE_MASK_VALID_OBJ}},
|
||||
{sf_unjam_lock, "unjam_lock", {DATATYPE_MASK_VALID_OBJ}},
|
||||
#ifndef NDEBUG
|
||||
@@ -1269,6 +1270,45 @@ long SetObjectUniqueID(TGameObj* obj) {
|
||||
return objUniqueID;
|
||||
}
|
||||
|
||||
// Assigns a unique ID in the negative range (0x8FFFFFFF - 0xFFFFFFFE)
|
||||
long SetSpecialID(TGameObj* obj) {
|
||||
long id = obj->ID;
|
||||
if (id < -1 || id > UID_START) return id;
|
||||
|
||||
if ((DWORD)objUniqueID >= UID_END) objUniqueID = UID_START;
|
||||
id = ++objUniqueID + UID_END;
|
||||
obj->ID = id;
|
||||
return id;
|
||||
}
|
||||
|
||||
void SetNewEngineID(TGameObj* obj) {
|
||||
if (obj->ID > UID_START) return;
|
||||
obj->ID = NewObjId();
|
||||
}
|
||||
|
||||
static bool __fastcall CheckSpecialID(long id) {
|
||||
if (Knockback_mWeapons.empty()) return false;
|
||||
for (std::vector<KnockbackModifier>::iterator it = Knockback_mWeapons.begin(); it != Knockback_mWeapons.end(); ++it) {
|
||||
if (id == it->id) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void __declspec(naked) item_identical_hack() {
|
||||
__asm {
|
||||
mov ecx, [edi]; // item id
|
||||
cmp ecx, UID_START; // start unique ID
|
||||
jg notIdentical;
|
||||
call CheckSpecialID;
|
||||
test al, al;
|
||||
jnz notIdentical;
|
||||
mov eax, [esi + 0x78]; // 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;
|
||||
@@ -1316,6 +1356,10 @@ void ScriptExtenderSetup() {
|
||||
HookCall(0x4A38A5, new_obj_id_hook);
|
||||
SafeWrite8(0x4A38B3, 0x90); // fix ID increment
|
||||
|
||||
//if (GetPrivateProfileIntA("Misc", "StackableUniqueItems", 0, ini) == 0) {
|
||||
MakeCall(0x477A0E, item_identical_hack); // don't put to item stack
|
||||
//}
|
||||
|
||||
arraysBehavior = GetPrivateProfileIntA("Misc", "arraysBehavior", 1, ini);
|
||||
if (arraysBehavior > 0) {
|
||||
arraysBehavior = 1; // only 1 and 0 allowed at this time
|
||||
|
||||
@@ -98,6 +98,8 @@ extern DWORD AvailableGlobalScriptTypes;
|
||||
// object's unique id
|
||||
extern long objUniqueID;
|
||||
long SetObjectUniqueID(TGameObj* obj);
|
||||
long SetSpecialID(TGameObj* obj);
|
||||
void SetNewEngineID(TGameObj* obj);
|
||||
|
||||
// types for script variables
|
||||
#define VAR_TYPE_INT (0xC001)
|
||||
|
||||
@@ -133,7 +133,7 @@ static const SfallMetarule metaruleArray[] = {
|
||||
{"set_map_enter_position", sf_set_map_enter_position, 3, 3},
|
||||
{"set_object_data", sf_set_object_data, 3, 3},
|
||||
{"set_outline", sf_set_outline, 2, 2},
|
||||
{"set_unique_id", sf_set_unique_id, 1, 1},
|
||||
{"set_unique_id", sf_set_unique_id, 1, 2},
|
||||
{"spatial_radius", sf_spatial_radius, 1, 1},
|
||||
{"tile_refresh_display", sf_tile_refresh_display, 0, 0},
|
||||
{"unjam_lock", sf_unjam_lock, 1, 1},
|
||||
|
||||
@@ -645,5 +645,13 @@ static void sf_set_object_data() {
|
||||
}
|
||||
|
||||
static void sf_set_unique_id() {
|
||||
opHandler.setReturn(SetObjectUniqueID(opHandler.arg(0).asObject()), DATATYPE_INT);
|
||||
TGameObj* obj = opHandler.arg(0).asObject();
|
||||
long id;
|
||||
if (opHandler.numArgs() == 2 && opHandler.arg(1).asInt() == -1) {
|
||||
id = NewObjId();
|
||||
obj->ID = id;
|
||||
} else {
|
||||
id = SetObjectUniqueID(obj);
|
||||
}
|
||||
opHandler.setReturn(id, DATATYPE_INT);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user