diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini
index 22287317..3304a2ce 100644
--- a/artifacts/ddraw.ini
+++ b/artifacts/ddraw.ini
@@ -1,5 +1,5 @@
;sfall configuration settings
-;v4.1.1
+;v4.1.2
[Main]
;Change to 1 if you want to use command line args to tell sfall to use another ini file.
@@ -524,7 +524,7 @@ ObjCanSeeObj_ShootThru_Fix=0
;Set to 1 to fix the broken obj_can_hear_obj script function
ObjCanHearObjFix=0
-;Set to 1 to fix the mood argument of start_gdialog script function to be available for talking heads
+;Set to 1 to enable the mood argument of start_gdialog script function to be available for talking heads
;If the argument value is -1, the mood will be determined by the local variable 0 of the script (vanilla behavior)
StartGDialogFix=0
diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h
index 69fe6418..7a94942a 100644
--- a/artifacts/scripting/headers/sfall.h
+++ b/artifacts/scripting/headers/sfall.h
@@ -143,6 +143,9 @@
#define set_attack_is_explosion(x) metarule2_explosions(4, x, 0)
#define set_attack_is_explosion_fire set_attack_is_explosion(DMG_fire)
#define set_explosion_radius(grenade, rocket) metarule2_explosions(5, grenade, rocket)
+#define get_explosion_damage(itemPid) metarule2_explosions(6, itemPid, 0)
+#define set_dynamite_damage(minDmg, maxDmg) metarule2_explosions(7, minDmg, maxDmg)
+#define set_plastic_damage(minDmg, maxDmg) metarule2_explosions(8, minDmg, maxDmg)
#define GAME_MSG_COMBAT (0)
@@ -245,6 +248,7 @@
#define intface_redraw sfall_func0("intface_redraw")
#define intface_show sfall_func0("intface_show")
#define inventory_redraw(invSide) sfall_func1("inventory_redraw", invSide)
+#define item_make_explosive(pid, aPid, min, max) sfall_func4("item_make_explosive", pid, aPid, min, max)
#define item_weight(obj) sfall_func1("item_weight", obj)
#define lock_is_jammed(obj) sfall_func1("lock_is_jammed", obj)
#define outlined_object sfall_func0("outlined_object")
diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt
index 05e68bd3..978ff6db 100644
--- a/artifacts/scripting/sfall function notes.txt
+++ b/artifacts/scripting/sfall function notes.txt
@@ -216,7 +216,7 @@ Some additional reg_anim_* functions were introduced. They all work in the same
> void reg_anim_turn_towards(object, tile/target, delay)
- makes object change its direction to face given tile num or target object.
-> int metarule2_explosions(int arg1, int arg2, int arg3)
+> int/array metarule2_explosions(int arg1, int arg2, int arg3)
was made as a dirty easy hack to allow dynamically change some explosion parameters (ranged attack). All changed parameters are reset to vanilla state automatically after each attack action. Following macros are available in sfall.h:
> void set_attack_explosion_pattern(x, y)
@@ -235,6 +235,17 @@ was made as a dirty easy hack to allow dynamically change some explosion paramet
- sets a permanent radius of the explosion for grenades and/or rockets. Passing 0 means not changing the corresponding radius.
- changed radius will be reset each time the player reloads the game.
+> array get_explosion_damage(itemPid)
+- returns array of the minimum and maximum damage of the explosive item.
+
+> void set_dynamite_damage(minDmg, maxDmg)
+- sets the minimum and maximum damage for Dynamite.
+- changed damage will be reset each time the player reloads the game.
+
+> void set_plastic_damage(minDmg, maxDmg)
+- sets the minimum and maximum damage for Plastic Explosives.
+- changed damage will be reset each time the player reloads the game.
+
Some utility/math functions are available:
@@ -491,6 +502,15 @@ Some utility/math functions are available:
- redraws inventory list in the inventory/use inventory item on/loot/barter screens
- invSide specifies which side needs to be redrawn: 0 - the player, 1 - target (container/NPC in loot/barter screens)
+> void sfall_func3("item_make_explosive", int pid, int activePid, int damage)
+> void sfall_func4("item_make_explosive", int pid, int activePid, int min, int max)
+- makes the specified item (pid) an explosive item like Dynamite or Plastic Explosives
+- activePid is for an item with an active timer, can be the same as the pid argument
+- the item proto must be "Misc Item" type and have "Use" action flag
+- min/max are the minimum and maximum explosion damage
+- using the function on an item that is already set as an explosive will override its previous settings
+- NOTE: this function does not work for pid's of Dynamite and Plastic Explosives
+
> int sfall_func1("get_string_pointer", string text)
- returns a pointer to a string variable or to a text
@@ -502,7 +522,7 @@ Some utility/math functions are available:
> void sfall_func5("create_win", string winName, int x, int y, int width, int height)
> void sfall_func6("create_win", string winName, int x, int y, int width, int height, int flags)
-- works just like vanilla CreateWin function, but creates a window with MoveOnTop flag if flags argument is not specified, and allows to set additional flags for the created window
+- works just like vanilla CreateWin function, but creates a window with MoveOnTop flag if the flags argument is not specified, and allows to set additional flags for the created window
- MoveOnTop flag allows the created window to be placed on top of the game interface
> void sfall_func3("set_can_rest_on_map", int mapNum, int elev, bool value)
diff --git a/sfall/Modules/Criticals.cpp b/sfall/Modules/Criticals.cpp
index a91d3c5f..691d48d4 100644
--- a/sfall/Modules/Criticals.cpp
+++ b/sfall/Modules/Criticals.cpp
@@ -28,6 +28,7 @@
namespace sfall
{
+static const char* critTableFile = ".\\CriticalOverrides.ini";
const DWORD Criticals::critTableCount = 2 * 19 + 1; // Number of species in new critical table
static DWORD mode;
@@ -86,7 +87,7 @@ void CritLoad() {
fo::CritInfo& newEffect = baseCritTable[newCritter][part][crit];
fo::CritInfo& defaultEffect = fo::var::crit_succ_eff[critter][part][crit];
for (int i = 0; i < 7; i++) {
- newEffect.values[i] = GetPrivateProfileIntA(section, critNames[i], defaultEffect.values[i], ".\\CriticalOverrides.ini");
+ newEffect.values[i] = GetPrivateProfileIntA(section, critNames[i], defaultEffect.values[i], critTableFile);
if (isDebug) {
char logmsg[256];
if (newEffect.values[i] != defaultEffect.values[i]) {
@@ -109,11 +110,11 @@ void CritLoad() {
for (int critter = 0; critter < Criticals::critTableCount; critter++) {
sprintf_s(buf, "c_%02d", critter);
int all;
- if (!(all = GetPrivateProfileIntA(buf, "Enabled", 0, ".\\CriticalOverrides.ini"))) continue;
+ if (!(all = GetPrivateProfileIntA(buf, "Enabled", 0, critTableFile))) continue;
for (int part = 0; part < 9; part++) {
if (all < 2) {
sprintf_s(buf2, "Part_%d", part);
- if (!GetPrivateProfileIntA(buf, buf2, 0, ".\\CriticalOverrides.ini")) continue;
+ if (!GetPrivateProfileIntA(buf, buf2, 0, critTableFile)) continue;
}
sprintf_s(buf2, "c_%02d_%d", critter, part);
@@ -121,7 +122,7 @@ void CritLoad() {
fo::CritInfo& effect = baseCritTable[critter][part][crit];
for (int i = 0; i < 7; i++) {
sprintf_s(buf3, "e%d_%s", crit, critNames[i]);
- effect.values[i] = GetPrivateProfileIntA(buf2, buf3, effect.values[i], ".\\CriticalOverrides.ini");
+ effect.values[i] = GetPrivateProfileIntA(buf2, buf3, effect.values[i], critTableFile);
}
}
}
@@ -244,10 +245,9 @@ void CriticalTableOverride() {
void RemoveCriticalTimeLimitsPatch() {
if (GetConfigInt("Misc", "RemoveCriticalTimelimits", 0)) {
dlog("Removing critical time limits.", DL_INIT);
- SafeWrite8(0x42412B, 0x90);
- BlockCall(0x42412C);
- SafeWrite16(0x4A3052, 0x9090);
- SafeWrite16(0x4A3093, 0x9090);
+ SafeWrite8(0x424118, 0xEB); // jmps 0x424131
+ SafeWrite8(0x4A3053, 0x0);
+ SafeWrite8(0x4A3094, 0x0);
dlogr(" Done", DL_INIT);
}
}
diff --git a/sfall/Modules/Explosions.cpp b/sfall/Modules/Explosions.cpp
index c9b32478..bbae8dbf 100644
--- a/sfall/Modules/Explosions.cpp
+++ b/sfall/Modules/Explosions.cpp
@@ -16,6 +16,8 @@
* along with this program. If not, see .
*/
+#include
+
#include "..\main.h"
#include "..\FalloutEngine\Fallout2.h"
@@ -24,14 +26,28 @@
#include "LoadGameHook.h"
#include "MainLoopHook.h"
#include "ScriptExtender.h"
+#include "Scripting\Arrays.h"
#include "Explosions.h"
namespace sfall
{
+using namespace fo::Fields;
+
+struct explosiveInfo {
+ DWORD pid;
+ DWORD pidActive;
+ DWORD minDamage;
+ DWORD maxDamage;
+};
+
+std::vector explosives;
+
+static bool onlyOnce = false;
static bool lightingEnabled = false;
static bool explosionsMetaruleReset = false;
+static bool explosionsDamageReset = true;
static const DWORD ranged_attack_lighting_fix_back = 0x4118F8;
@@ -40,7 +56,7 @@ static void __declspec(naked) ranged_attack_lighting_fix() {
__asm {
mov eax, [esp+40]; // projectile ptr - 1st arg
mov ecx, [eax+0x70]; // check existing light intensity
- cmp ecx, 0; // if non-zero, skip obj_set_light call
+ cmp ecx, 0; // if non-zero, skip obj_set_light call
jnz skip;
mov ecx, [esp+0x1C]; // protoPtr of projectile
mov edx, [ecx+0x0C]; // light radius - 2nd arg
@@ -53,7 +69,7 @@ skip:
}
// misc functions from the engine..
-static const DWORD register_object_play_sfx = 0x41541C;
+//static const DWORD register_object_play_sfx = 0x41541C;
static const DWORD explosion_effect_hook_back = 0x411AB9;
static DWORD explosion_effect_starting_dir = 0;
@@ -154,6 +170,168 @@ static void __declspec(naked) fire_dance_lighting_fix1() {
}
}
+//-----------------------------------------------------------------
+
+static DWORD __fastcall CheckExplosives(DWORD register pid) {
+ for (const auto &item: explosives) {
+ if (item.pid == pid) return item.pidActive;
+ }
+ return 0;
+}
+
+static DWORD __fastcall CheckActiveExplosives(DWORD register pid) {
+ for (const auto &item: explosives) {
+ if (item.pidActive == pid) return 0;
+ }
+ return 1;
+}
+
+static DWORD __fastcall GetDamage(DWORD register pid, DWORD &min, DWORD &max) {
+ DWORD result = 0;
+ for (const auto &item: explosives) {
+ if (item.pidActive == pid) {
+ min = item.minDamage;
+ max = item.maxDamage;
+ result = 1;
+ break;
+ }
+ }
+ return result;
+}
+
+static DWORD __fastcall SetQueueExplosionDamage(DWORD register pid) {
+ DWORD min, max;
+ DWORD result = GetDamage(pid, min, max);
+
+ if (result) {
+ _asm mov edx, min;
+ _asm mov ecx, max;
+ }
+
+ return result;
+}
+
+static void __declspec(naked) obj_use_explosive_hack() {
+ using namespace fo;
+ __asm {
+ cmp edx, PID_PLASTIC_EXPLOSIVES;
+ jz end;
+ // end engine code
+ pushad;
+ mov ecx, edx;
+ call CheckExplosives;
+ test eax, eax;
+ popad;
+ jnz end;
+ retn;
+end:
+ mov dword ptr [esp], 0x49BCE0;
+ retn;
+ }
+}
+
+static void __declspec(naked) obj_use_explosive_active_hack() {
+ using namespace fo;
+ __asm {
+ cmp eax, PID_PLASTIC_EXPLOSIVES;
+ jz end;
+ // end engine code
+ push ebx;
+ push ecx;
+ push edx;
+ mov ecx, eax;
+ call CheckExplosives;
+ pop edx;
+ pop ecx;
+ pop ebx;
+ test eax, eax;
+ jz skipSet;
+ mov dword ptr [esi + protoId], eax; // change item pid to active;
+skipSet:
+ mov dword ptr [esp], 0x49BD62;
+end:
+ retn;
+ }
+}
+
+static void __declspec(naked) queue_do_explosion_hack() {
+ using namespace fo;
+ __asm {
+ cmp edx, PID_ACTIVE_DYNAMITE;
+ jz dynamite;
+ cmp edx, PID_ACTIVE_PLASTIC_EXPLOSIVE;
+ jz end;
+ push edx;
+ mov ecx, edx;
+ call SetQueueExplosionDamage;
+ test eax, eax;
+ mov ebx, edx;
+ pop edx;
+ jz end;
+ mov dword ptr [esp], 0x4A2888;
+end:
+ retn;
+dynamite:
+ mov dword ptr [esp], 0x4A2872;
+ retn;
+ }
+}
+
+static void __declspec(naked) inven_action_cursor_drop_hack() {
+ using namespace fo;
+ __asm {
+ cmp ebx, PID_ACTIVE_PLASTIC_EXPLOSIVE;
+ jz end;
+ // end engine code
+ push eax;
+ push ecx;
+ mov ecx, ebx;
+ call CheckActiveExplosives;
+ test eax, eax; // check in engine
+ pop ecx;
+ pop eax;
+end:
+ retn;
+ }
+}
+
+static void __declspec(naked) protinstTestDroppedExplosive_hack() {
+ __asm {
+ jz end;
+ mov ecx, edx;
+ call CheckActiveExplosives;
+ test eax, eax;
+ jz end;
+ mov dword ptr [esp], 0x49C112; // exit, no active item explosive
+end:
+ retn;
+ }
+}
+
+static void apply_expl_hack() {
+ onlyOnce = true;
+ MakeCall(0x49BCC7, obj_use_explosive_hack); // check explosives
+ MakeCall(0x49BD56, obj_use_explosive_active_hack); // set active explosive
+ MakeCall(0x4A2865, queue_do_explosion_hack); // set damage explosive
+ MakeCall(0x4737F2, inven_action_cursor_drop_hack); // check drop explosives
+ SafeWrite8(0x4737F7, 0x90);
+ MakeCall(0x49C005, protinstTestDroppedExplosive_hack); // check drop explosives
+ SafeWrite8(0x49C00A, 0x90);
+}
+
+void Explosions::AddToExplosives(DWORD pid, DWORD activePid, DWORD minDmg, DWORD maxDmg) {
+ for (unsigned int i = 0; i < explosives.size(); i++) {
+ if (explosives[i].pid == pid) {
+ explosives.erase(explosives.begin() + i);
+ break;
+ }
+ }
+ explosives.push_back({ pid, activePid, minDmg, maxDmg });
+
+ if (!onlyOnce) apply_expl_hack();
+}
+
+//-----------------------------------------------------------------
static const DWORD explosion_dmg_check_adr[] = {0x411709, 0x4119FC, 0x411C08, 0x4517C1, 0x423BC8, 0x42381A};
static const DWORD explosion_art_adr[] = {0x411A19, 0x411A29, 0x411A35, 0x411A3C};
@@ -161,6 +339,15 @@ static const DWORD explosion_art_defaults[] = {10, 2, 31, 29};
static const DWORD explosion_radius_grenade = 0x479183;
static const DWORD explosion_radius_rocket = 0x47918B;
+static const DWORD dynamite_min_dmg = 0x4A2878;
+static const DWORD dynamite_max_dmg = 0x4A2873;
+static const DWORD plastic_min_dmg = 0x4A2884;
+static const DWORD plastic_max_dmg = 0x4A287F;
+static DWORD dynamite_min_setting;
+static DWORD dynamite_max_setting;
+static DWORD plastic_min_setting;
+static DWORD plastic_max_setting;
+
static DWORD set_expl_radius_grenade = 2;
static DWORD set_expl_radius_rocket = 3;
@@ -172,7 +359,10 @@ enum MetaruleExplosionsMode {
EXPL_FORCE_EXPLOSION_ART = 2,
EXPL_FORCE_EXPLOSION_RADIUS = 3,
EXPL_FORCE_EXPLOSION_DMGTYPE = 4,
- EXPL_STATIC_EXPLOSION_RADIUS = 5
+ EXPL_STATIC_EXPLOSION_RADIUS = 5,
+ EXPL_GET_EXPLOSION_DAMAGE = 6,
+ EXPL_SET_DYNAMITE_EXPLOSION_DAMAGE = 7,
+ EXPL_SET_PLASTIC_EXPLOSION_DAMAGE = 8
};
static void SetExplosionRadius(int arg1, int arg2) {
@@ -180,6 +370,42 @@ static void SetExplosionRadius(int arg1, int arg2) {
SafeWrite32(explosion_radius_rocket, arg2);
}
+static void SetExplosionDamage(int pid, int min, int max) {
+ explosionsDamageReset = true;
+ switch (pid) {
+ case fo::ProtoId::PID_DYNAMITE:
+ SafeWrite32(dynamite_min_dmg, min);
+ SafeWrite32(dynamite_max_dmg, max);
+ break;
+ case fo::ProtoId::PID_PLASTIC_EXPLOSIVES:
+ SafeWrite32(plastic_min_dmg, min);
+ SafeWrite32(plastic_max_dmg, max);
+ break;
+ }
+}
+
+static int GetExplosionDamage(int pid) {
+ DWORD min = 0, max = 0;
+ switch (pid) {
+ case fo::ProtoId::PID_DYNAMITE:
+ min = *(DWORD*)dynamite_min_dmg;
+ max = *(DWORD*)dynamite_max_dmg;
+ break;
+ case fo::ProtoId::PID_PLASTIC_EXPLOSIVES:
+ min = *(DWORD*)plastic_min_dmg;
+ max = *(DWORD*)plastic_max_dmg;
+ break;
+ default:
+ GetDamage(pid, min, max);
+ }
+
+ DWORD arrayId = script::TempArray(2, 0);
+ script::arrays[arrayId].val[0] = min;
+ script::arrays[arrayId].val[1] = max;
+
+ return arrayId;
+}
+
int _stdcall ExplosionsMetaruleFunc(int mode, int arg1, int arg2) {
switch (mode) {
case EXPL_FORCE_EXPLOSION_PATTERN:
@@ -209,6 +435,14 @@ int _stdcall ExplosionsMetaruleFunc(int mode, int arg1, int arg2) {
if (arg2 > 0) set_expl_radius_rocket = arg2;
SetExplosionRadius(set_expl_radius_grenade, set_expl_radius_rocket);
break;
+ case EXPL_GET_EXPLOSION_DAMAGE:
+ return GetExplosionDamage(arg1);
+ case EXPL_SET_DYNAMITE_EXPLOSION_DAMAGE:
+ SetExplosionDamage(fo::ProtoId::PID_DYNAMITE, arg1, arg2);
+ return 0;
+ case EXPL_SET_PLASTIC_EXPLOSION_DAMAGE:
+ SetExplosionDamage(fo::ProtoId::PID_PLASTIC_EXPLOSIVES, arg1, arg2);
+ return 0;
default:
return -1;
}
@@ -239,6 +473,17 @@ void ResetExplosionRadius() {
SetExplosionRadius(2, 3);
}
+static void ResetExplosionDamage() {
+ if (!explosives.empty()) explosives.clear();
+
+ if (!explosionsDamageReset) return;
+ SafeWrite32(dynamite_max_dmg, dynamite_max_setting);
+ SafeWrite32(dynamite_min_dmg, dynamite_min_setting);
+ SafeWrite32(plastic_max_dmg, plastic_max_setting);
+ SafeWrite32(plastic_min_dmg, plastic_min_setting);
+ explosionsDamageReset = false;
+}
+
void Explosions::init() {
MakeJump(0x411AB4, explosion_effect_hook); // required for explosions_metarule
@@ -248,20 +493,23 @@ void Explosions::init() {
MakeJump(0x4118E1, ranged_attack_lighting_fix);
MakeJump(0x410A4A, fire_dance_lighting_fix1);
MakeJump(0x415A3F, anim_set_check__light_fix); // this allows to change light intensity
-
dlogr(" Done", DL_INIT);
}
- DWORD tmp;
- tmp = SimplePatch(0x4A2873, "Misc", "Dynamite_DmgMax", 50, 0, 9999);
- SimplePatch(0x4A2878, "Misc", "Dynamite_DmgMin", 30, 0, tmp);
- tmp = SimplePatch(0x4A287F, "Misc", "PlasticExplosive_DmgMax", 80, 0, 9999);
- SimplePatch(0x4A2884, "Misc", "PlasticExplosive_DmgMin", 40, 0, tmp);
+ // initialize explosives
+ dynamite_max_setting = SimplePatch(dynamite_max_dmg, "Misc", "Dynamite_DmgMax", 50, 0, 9999);
+ dynamite_min_setting = SimplePatch(dynamite_min_dmg, "Misc", "Dynamite_DmgMin", 30, 0, dynamite_max_setting);
+ plastic_max_setting = SimplePatch(plastic_max_dmg, "Misc", "PlasticExplosive_DmgMax", 80, 0, 9999);
+ plastic_min_setting = SimplePatch(plastic_min_dmg, "Misc", "PlasticExplosive_DmgMin", 40, 0, plastic_max_setting);
+ if (!explosives.empty()) explosives.clear();
// after each combat attack, reset metarule_explosions settings
MainLoopHook::OnAfterCombatAttack() += ResetExplosionSettings;
- LoadGameHook::OnGameReset() += ResetExplosionRadius;
+ LoadGameHook::OnGameReset() += []() {
+ ResetExplosionRadius();
+ ResetExplosionDamage();
+ };
}
}
diff --git a/sfall/Modules/Explosions.h b/sfall/Modules/Explosions.h
index f69b5003..d907c37a 100644
--- a/sfall/Modules/Explosions.h
+++ b/sfall/Modules/Explosions.h
@@ -27,6 +27,8 @@ class Explosions : public Module {
public:
const char* name() { return "Explosions"; }
void init();
+
+ static void AddToExplosives(DWORD pid, DWORD activePid, DWORD minDmg, DWORD maxDmg);
};
int _stdcall ExplosionsMetaruleFunc(int mode, int arg1, int arg2);
diff --git a/sfall/Modules/MiscPatches.cpp b/sfall/Modules/MiscPatches.cpp
index 2535d626..70e9e935 100644
--- a/sfall/Modules/MiscPatches.cpp
+++ b/sfall/Modules/MiscPatches.cpp
@@ -495,7 +495,7 @@ void NpcAutoLevelPatch() {
if (GetConfigInt("Misc", "OverrideArtCacheSize", 0)) {
dlog("Applying override art cache size patch.", DL_INIT);
- SafeWrite32(0x418867, 0x90909090);
+ SafeWrite8(0x41886A, 0x0);
SafeWrite32(0x418872, 256);
dlogr(" Done", DL_INIT);
}
@@ -581,7 +581,7 @@ void FashShotTraitFix() {
break;
case 2:
dlog("Applying Fast Shot Trait Fix. (Fallout 1 version)", DL_INIT);
- SafeWrite16(0x478C9F, 0x9090);
+ SafeWrite8(0x478CA0, 0x0);
for (int i = 0; i < sizeof(FastShotFixF1) / 4; i++) {
HookCall(FastShotFixF1[i], (void*)0x478C7D);
}
@@ -705,7 +705,7 @@ void MotionScannerFlagsPatch() {
if (flags & 1) MakeJump(0x41BBE9, ScannerAutomapHook);
if (flags & 2) {
// automap_
- SafeWrite16(0x41BC24, 0x9090);
+ SafeWrite8(0x41BC25, 0x0);
BlockCall(0x41BC3C);
// item_m_use_charged_item_
SafeWrite8(0x4794B3, 0x5E); // jbe short 0x479512
diff --git a/sfall/Modules/Scripting/Handlers/Metarule.cpp b/sfall/Modules/Scripting/Handlers/Metarule.cpp
index dcf3c4ac..8863bb26 100644
--- a/sfall/Modules/Scripting/Handlers/Metarule.cpp
+++ b/sfall/Modules/Scripting/Handlers/Metarule.cpp
@@ -101,6 +101,7 @@ static const SfallMetarule metarules[] = {
{"intface_redraw", sf_intface_redraw, 0, 0},
{"intface_show", sf_intface_show, 0, 0},
{"inventory_redraw", sf_inventory_redraw, 1, 1, {ARG_INT}},
+ {"item_make_explosive", sf_item_make_explosive, 3, 4, {ARG_INT, ARG_INT, ARG_INT, ARG_INT}},
{"item_weight", sf_item_weight, 1, 1, {ARG_OBJECT}},
{"lock_is_jammed", sf_lock_is_jammed, 1, 1, {ARG_OBJECT}},
{"outlined_object", sf_outlined_object, 0, 0},
diff --git a/sfall/Modules/Scripting/Handlers/Objects.cpp b/sfall/Modules/Scripting/Handlers/Objects.cpp
index aca92f00..614913b3 100644
--- a/sfall/Modules/Scripting/Handlers/Objects.cpp
+++ b/sfall/Modules/Scripting/Handlers/Objects.cpp
@@ -17,6 +17,7 @@
*/
#include "..\..\..\FalloutEngine\Fallout2.h"
+#include "..\..\Explosions.h"
#include "..\..\Knockback.h"
#include "..\..\Inventory.h"
#include "..\..\LoadGameHook.h"
@@ -429,6 +430,24 @@ void sf_set_unjam_locks_time(OpcodeContext& ctx) {
}
}
+void sf_item_make_explosive(OpcodeContext& ctx) {
+ DWORD pid = ctx.arg(0).rawValue();
+ DWORD pidActive = ctx.arg(1).rawValue();
+ DWORD min = ctx.arg(2).rawValue();
+ DWORD max = (ctx.numArgs() == 4) ? ctx.arg(3).rawValue() : min;
+
+ if (min > max) {
+ max = min;
+ ctx.printOpcodeError("item_make_explosive() - Warning: value of max argument is less than the min argument.");
+ }
+
+ if (pid > 0 && pidActive > 0) {
+ Explosions::AddToExplosives(pid, pidActive, min, max);
+ } else {
+ ctx.printOpcodeError("item_make_explosive() - PID arguments must be greater than 0.");
+ }
+}
+
void sf_get_current_inven_size(OpcodeContext& ctx) {
ctx.setReturn(sf_item_total_size(ctx.arg(0).asObject()));
}
diff --git a/sfall/Modules/Scripting/Handlers/Objects.h b/sfall/Modules/Scripting/Handlers/Objects.h
index 9cde7f58..9cac2466 100644
--- a/sfall/Modules/Scripting/Handlers/Objects.h
+++ b/sfall/Modules/Scripting/Handlers/Objects.h
@@ -91,6 +91,8 @@ void sf_unjam_lock(OpcodeContext&);
void sf_set_unjam_locks_time(OpcodeContext&);
+void sf_item_make_explosive(OpcodeContext&);
+
void sf_get_current_inven_size(OpcodeContext&);
void sf_get_dialog_object(OpcodeContext&);
diff --git a/sfall/version.h b/sfall/version.h
index c315d970..f5d652f7 100644
--- a/sfall/version.h
+++ b/sfall/version.h
@@ -24,10 +24,10 @@
#define VERSION_MAJOR 4
#define VERSION_MINOR 1
-#define VERSION_BUILD 1
+#define VERSION_BUILD 2
#define VERSION_REV 0
-#define VERSION_STRING "4.1.1"
+#define VERSION_STRING "4.1.2"
#define CHECK_VAL (4)