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:
NovaRain
2019-04-16 20:53:41 +08:00
parent ceb55cdfa9
commit 0c8794661c
7 changed files with 86 additions and 22 deletions
+5 -2
View File
@@ -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
View File
@@ -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();
+8
View File
@@ -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);
+45 -1
View File
@@ -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
+2
View File
@@ -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)
+1 -1
View File
@@ -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},
+9 -1
View File
@@ -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);
}