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
|
- clears the cache of FRM image files loaded into memory
|
||||||
|
|
||||||
> int sfall_func1("set_unique_id", object)
|
> 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)
|
- 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
|
- 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 -------
|
------ MORE INFO -------
|
||||||
|
|||||||
+16
-17
@@ -27,15 +27,9 @@
|
|||||||
|
|
||||||
static std::vector<long> NoBursts; // object id
|
static std::vector<long> NoBursts; // object id
|
||||||
|
|
||||||
struct KnockbackModifier {
|
|
||||||
long id;
|
|
||||||
DWORD type;
|
|
||||||
double value;
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::vector<KnockbackModifier> mTargets;
|
static std::vector<KnockbackModifier> mTargets;
|
||||||
static std::vector<KnockbackModifier> mAttackers;
|
static std::vector<KnockbackModifier> mAttackers;
|
||||||
static std::vector<KnockbackModifier> mWeapons;
|
std::vector<KnockbackModifier> Knockback_mWeapons;
|
||||||
|
|
||||||
struct ChanceModifier {
|
struct ChanceModifier {
|
||||||
long id;
|
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) {
|
static DWORD __fastcall CalcKnockbackMod(int knockValue, int damage, TGameObj* weapon, TGameObj* attacker, TGameObj* target) {
|
||||||
double result = (double)damage / (double)knockValue;
|
double result = (double)damage / (double)knockValue;
|
||||||
result = ApplyModifiers(&mWeapons, weapon, result);
|
result = ApplyModifiers(&Knockback_mWeapons, weapon, result);
|
||||||
result = ApplyModifiers(&mAttackers, attacker, result);
|
result = ApplyModifiers(&mAttackers, attacker, result);
|
||||||
result = ApplyModifiers(&mTargets, target, result);
|
result = ApplyModifiers(&mTargets, target, result);
|
||||||
return (DWORD)floor(result);
|
return (DWORD)floor(result);
|
||||||
@@ -177,19 +171,25 @@ void _stdcall KnockbackSetMod(TGameObj* object, DWORD type, float val, DWORD on)
|
|||||||
std::vector<KnockbackModifier>* mods;
|
std::vector<KnockbackModifier>* mods;
|
||||||
switch (on) {
|
switch (on) {
|
||||||
case 0:
|
case 0:
|
||||||
mods = &mWeapons;
|
if (object->pid >> 24 != OBJ_TYPE_ITEM) return;
|
||||||
|
mods = &Knockback_mWeapons;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
|
if (object->pid >> 24 != OBJ_TYPE_CRITTER) return;
|
||||||
mods = &mTargets;
|
mods = &mTargets;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
|
if (object->pid >> 24 != OBJ_TYPE_CRITTER) return;
|
||||||
mods = &mAttackers;
|
mods = &mAttackers;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long id = SetObjectUniqueID(object);
|
long id = (on == 0)
|
||||||
|
? SetSpecialID(object)
|
||||||
|
: SetObjectUniqueID(object);
|
||||||
|
|
||||||
KnockbackModifier mod = { id, type, (double)val };
|
KnockbackModifier mod = { id, type, (double)val };
|
||||||
for (DWORD i = 0; i < mods->size(); i++) {
|
for (DWORD i = 0; i < mods->size(); i++) {
|
||||||
if ((*mods)[i].id == id) {
|
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) {
|
void _stdcall KnockbackRemoveMod(TGameObj* object, DWORD on) {
|
||||||
std::vector<KnockbackModifier>* mods;
|
std::vector<KnockbackModifier>* mods;
|
||||||
int id = object->ID;
|
|
||||||
switch (on) {
|
switch (on) {
|
||||||
case 0:
|
case 0:
|
||||||
object->ID = NewObjId(); // revert to engine range id
|
mods = &Knockback_mWeapons;
|
||||||
mods = &mWeapons;
|
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
mods = &mTargets;
|
mods = &mTargets;
|
||||||
@@ -218,8 +216,9 @@ void _stdcall KnockbackRemoveMod(TGameObj* object, DWORD on) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (DWORD i = 0; i < mods->size(); i++) {
|
for (DWORD i = 0; i < mods->size(); i++) {
|
||||||
if ((*mods)[i].id == id) {
|
if ((*mods)[i].id == object->ID) {
|
||||||
mods->erase(mods->begin() + i);
|
mods->erase(mods->begin() + i);
|
||||||
|
if (on == 0) SetNewEngineID(object); // revert to engine range id
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -231,7 +230,7 @@ void _stdcall SetHitChanceMax(TGameObj* critter, DWORD maximum, DWORD mod) {
|
|||||||
BaseHitChance.mod = mod;
|
BaseHitChance.mod = mod;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (critter->pid >> 24 != OBJ_TYPE_CRITTER) return;
|
||||||
long id = SetObjectUniqueID(critter);
|
long id = SetObjectUniqueID(critter);
|
||||||
for (DWORD i = 0; i < HitChanceMods.size(); i++) {
|
for (DWORD i = 0; i < HitChanceMods.size(); i++) {
|
||||||
if (id == HitChanceMods[i].id) {
|
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) {
|
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);
|
long id = SetObjectUniqueID(critter);
|
||||||
for (DWORD i = 0; i < NoBursts.size(); i++) {
|
for (DWORD i = 0; i < NoBursts.size(); i++) {
|
||||||
@@ -349,7 +348,7 @@ void Knockback_OnGameLoad() {
|
|||||||
BasePickpocket.mod = 0;
|
BasePickpocket.mod = 0;
|
||||||
mTargets.clear();
|
mTargets.clear();
|
||||||
mAttackers.clear();
|
mAttackers.clear();
|
||||||
mWeapons.clear();
|
Knockback_mWeapons.clear();
|
||||||
HitChanceMods.clear();
|
HitChanceMods.clear();
|
||||||
PickpocketMods.clear();
|
PickpocketMods.clear();
|
||||||
NoBursts.clear();
|
NoBursts.clear();
|
||||||
|
|||||||
@@ -18,6 +18,14 @@
|
|||||||
|
|
||||||
#pragma once
|
#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 SetPickpocketMax(TGameObj* critter, DWORD maximum, DWORD mod);
|
||||||
void _stdcall SetHitChanceMax(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);
|
void _stdcall KnockbackSetMod(TGameObj* object, DWORD type, float val, DWORD on);
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include "FalloutEngine.h"
|
#include "FalloutEngine.h"
|
||||||
#include "HookScripts.h"
|
#include "HookScripts.h"
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
|
#include "Knockback.h"
|
||||||
#include "LoadGameHook.h"
|
#include "LoadGameHook.h"
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
#include "ScriptExtender.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_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_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_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_spatial_radius, "spatial_radius", {DATATYPE_MASK_VALID_OBJ}},
|
||||||
{sf_unjam_lock, "unjam_lock", {DATATYPE_MASK_VALID_OBJ}},
|
{sf_unjam_lock, "unjam_lock", {DATATYPE_MASK_VALID_OBJ}},
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
@@ -1269,6 +1270,45 @@ long SetObjectUniqueID(TGameObj* obj) {
|
|||||||
return objUniqueID;
|
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() {
|
static void __declspec(naked) new_obj_id_hook() {
|
||||||
__asm {
|
__asm {
|
||||||
mov eax, 83535;
|
mov eax, 83535;
|
||||||
@@ -1316,6 +1356,10 @@ void ScriptExtenderSetup() {
|
|||||||
HookCall(0x4A38A5, new_obj_id_hook);
|
HookCall(0x4A38A5, new_obj_id_hook);
|
||||||
SafeWrite8(0x4A38B3, 0x90); // fix ID increment
|
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);
|
arraysBehavior = GetPrivateProfileIntA("Misc", "arraysBehavior", 1, ini);
|
||||||
if (arraysBehavior > 0) {
|
if (arraysBehavior > 0) {
|
||||||
arraysBehavior = 1; // only 1 and 0 allowed at this time
|
arraysBehavior = 1; // only 1 and 0 allowed at this time
|
||||||
|
|||||||
@@ -98,6 +98,8 @@ extern DWORD AvailableGlobalScriptTypes;
|
|||||||
// object's unique id
|
// object's unique id
|
||||||
extern long objUniqueID;
|
extern long objUniqueID;
|
||||||
long SetObjectUniqueID(TGameObj* obj);
|
long SetObjectUniqueID(TGameObj* obj);
|
||||||
|
long SetSpecialID(TGameObj* obj);
|
||||||
|
void SetNewEngineID(TGameObj* obj);
|
||||||
|
|
||||||
// types for script variables
|
// types for script variables
|
||||||
#define VAR_TYPE_INT (0xC001)
|
#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_map_enter_position", sf_set_map_enter_position, 3, 3},
|
||||||
{"set_object_data", sf_set_object_data, 3, 3},
|
{"set_object_data", sf_set_object_data, 3, 3},
|
||||||
{"set_outline", sf_set_outline, 2, 2},
|
{"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},
|
{"spatial_radius", sf_spatial_radius, 1, 1},
|
||||||
{"tile_refresh_display", sf_tile_refresh_display, 0, 0},
|
{"tile_refresh_display", sf_tile_refresh_display, 0, 0},
|
||||||
{"unjam_lock", sf_unjam_lock, 1, 1},
|
{"unjam_lock", sf_unjam_lock, 1, 1},
|
||||||
|
|||||||
@@ -645,5 +645,13 @@ static void sf_set_object_data() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void sf_set_unique_id() {
|
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