Fallout 2 mod FO2 Engine Tweaks (Sfall)

You know what would be really awesome (annoying JtD request #4995 incoming), an roll_vs_skill/do_check hook; imagine it, overriding all those annoying random stat/skill checks in one fell swoop. Especially when the Fallout 1 conversion is done, this could produce miracles (the Fallout 2 devs were a bit more reasonable and made stat checks a bit less random). You could change every random skill roll into a threshold purely based on the modifier alone (e.g. roll vs. skill with -20 modifier could become 50 - -20*3 = 110 skill threshold), but more refined possibilities based on a critter's stats if what calls the hook is a critter, or an area's "difficulty" if it's not a critter (e.g. a door).

edit: ideally this would even allow you to make the reaction system actually matter: change the speech skill success condition based on LVAR_reaction. Of course, this would presume being able to tap into local vars from hookscripts.

I actually originally considered the advantages of being able to tap into local variables in the case of LVAR_Hostile: there's no reliable way to make an enemy surrender or flee in Fallout because their critter_proc will keep telling them to initiate combat. Unfortunately, the devs didn't consistently use the same lvar for hostility; however, LVAR_reaction is used consistently.

Anyway, long story short: a man can dream :P. Wish I were a programmer and not a total amateur and I'd try and figure it out myself instead of whining to phobos, heh (might still give it a try at some point all the same though, being a masochist).
 
Last edited:
I actually originally considered the advantages of being able to tap into local variables in the case of LVAR_Hostile: there's no reliable way to make an enemy surrender or flee in Fallout because their critter_proc will keep telling them to initiate combat. Unfortunately, the devs didn't consistently use the same lvar for hostility; however, LVAR_reaction is used consistently.

You can do that:
Code:
set_self(otherDude);
set_local_var(4 /* or whatever the number is */, 0);
 
Weird, thought I'd already tested that... that's very interesting; if only the devs had used a more consistent lvar numbering system for things other than reactions.

edit: ah well, this is probably more an ingredient for mass compile anyway; messing with lvars from outside would be just asking for crazy bugs all over the place. Might as well do the same with skill rolls I suppose.

maybe instead of individualized hostility, team based hostility could be the best approach; replace all instances of LVAR_Hostile and and calls for hostile in LVAR_Flags with a reference to an array storing the varying degrees of hostility in a team hostility array; this'd finally ensure that terminate_combat can be used safely...
 
Last edited:
An update. CorpseLineOfFireFix was rewritten. Now it has only 2 states (enabled/disabled) and prevents corpses from blocking fire altogether at low level (they will act like bushes or other scenery - completely irrelevant to burst fire logic).
Also added hotkey (configurable) to reload current weapon.
 
sfall 3.6 is released on SourceForge, along with the modders pack and win2k version.

changelog said:
v3.6
>Fixed a bug in books.ini handling that caused not all books to be loaded
>Fixed several different minor bugs that may theoretically cause unstable crashes
>Changed the way get_script function works: now returns proper scriptID (line number in scripts.lst)
>Fixed set_script function to make it accept proper scriptID, execute map_enter by default, and create proper script types for given object
>Fixed set_self function again (hopefully)
>Fixed AllowUnsafeScripting option not being in [Debugging] section of ddraw.ini

>Unified and changed how all sfall scripts are initialized. Exported procedures, scripted UI event handlers, named events (and maybe some other stuff) will finally work properly in all hs_ and gl_ scripts
>Slight change to when sfall resets state for all its components. Previously it happened after the game was loaded from a save, now it will happen before actually loading. This prevents a few nasty bugs
>Added safechecks to some script functions so the game won't crash if object argument is 0
>Expanded set_self function so you can access object script's local variables from another script
>hs_withinperception hook script can now override the blocking check in obj_can_see_obj script function
>New script functions: obj_blocking_line, obj_blocking_tile, tile_get_objs, party_member_list, path_find_to, create_spatial, art_exists, obj_is_carrying_obj
>list_* functions can now return spatial scripts as well, so you can delete them if you want
>A new hook script: hs_inventorymove (when moving objects around in the inventory screen)
>Added more arguments and return values to hs_combatdamage hook script, so you can now implement your own damage formula entirely in a script

>Rewrote npc combat control from scratch, with many bug fixes and improvements
>Rewrote CorpseLineOfFireFix to fix the conflict with ToHit hooks and the bug that corpse in the same hex as target still absorbs bullets
>Added an option to setup a key to reload your currently equipped weapon
>Added an option to skip the executable file size check
>Moved FalloutClient.exe to the modders pack
EDIT: re-uploaded modders pack because the previously included int2ssl requires MinGW libraries.
 
Last edited:
In array.txt it says:

> int len_array(int arrayID):
- returns number of elements or key=>value pairs in a given array
- if array is not found, returns -1 (can be used to check if given array exist)

I guess this can't be used to check anything for being an array, right?

Code:
  nougat:=1;
  debug_msg("len_array(1) " + len_array(1));
  debug_msg("len_array(nougat:=1) " + len_array(nougat));
  nougat:=create_array(1,0);
  debug_msg("len_array(nougat[0]) " + len_array(get_array(nougat, 0)));
  nougat[0]:=1;
  debug_msg("len_array(nougat[0]:=1) " + len_array(get_array(nougat, 0)));

returns this:

Code:
len_array(1) 0
len_array(nougat:=1) 0
len_array(nougat[0]) -1
len_array(nougat[0]:=1) 0

Is there a way to find out whether or not something is an array regardless of whether it ever was (as obviously something with len 0 could just be an array without size)?
 
Last edited:
In array.txt it says:

> int len_array(int arrayID):
- returns number of elements or key=>value pairs in a given array
- if array is not found, returns -1 (can be used to check if given array exist)

I guess this can't be used to check anything for being an array, right?

Is there a way to find out whether or not something is an array regardless of whether it ever was (as obviously something with len 0 could just be an array without size)?
Well this works like that because you don't have separate data type for arrays. As you know there are 3 data types in fallout scripts: Integer, Float and String. Array references are integer values, so there is no reliable way to check if some value is an array reference and not just some number. This also depends on what exactly you want to achieve. If you want to make recursive debug_array_str function then I guess you will have to go for some other approach, like if you have a proper multi-dimensional array, you can pass number of dimensions (nesting depth) as a procedure argument.

I actually thought about using specially crafted strings (with non-character bytes) as array references, but dropped this idea eventually for the sake of simplicity. The downside was that if at some point this string would be printed somewhere in the game (could happen very easily during debugging), it will crash the game. And you can't introduce another data type to the engine, as you will have to modify a lot of vanilla opcode handlers (using ASM hacks) - too much work and risk.
This could be done easily with new engine, however. Wait a few years :P
 
Last edited:
I just changed it so that only len_array's > 0 get debugged as arrays. Makes it confusing when you don't know it's empty beforehand, but I should be able to piece it together heh.

Another thing I just can't seem to figure out is your description of return arguments in registered hooks:

Code:
NOTE: you can hook several scripts to a single hook point, for example if it's different mods from different authors or just some different aspects of one larger mod. In this case scripts are executed in reverse order of how they were registered. When one of the scripts in a chain returns value with "set_sfall_return", the next script may override this value if calls "set_sfall_return" again. Sometimes you need to multiply certain value in a chain of hook scripts.
Example: let's say we have a Mod A which reduces all "to hit" chances by 50%. The code might look like this:


    original_chance = get_sfall_arg;
    set_sfall_return(original_chance / 2);


Mod B also want to affect hit chances globally, by increasing them by 50%. Now in order for both mods to work well together, we need to add this line to Mod A hook script:


    set_sfall_arg(original_chance / 2);


This basically changes hook argument for the next script. Mod B code:


    original_chance = get_sfall_arg;
    set_sfall_return(original_chance * 1.5);
    set_sfall_arg(original_chance * 1.5);


So if you combine both mods together, they will run in chain and the end result will be a 75% from original hit chance (hook register order doesn't matter in this case, if you use "set_sfall_arg" in both hooks).

What decides which of these two is MOD A and MOD B? From the description they seem identical, and you say register order doesn't matter (I don't even know what you meant by that tbh), so what does matter then to decide in what order the sfall return is modified (i.e. first /2 then *1.5, rather than first *1.5 and only afterwards /2)?
 
That's because A*B == B*A (first grade math :D). When first hook returns multiplied value it also modifies the argument to the same value, so the next hook will start it's modifications from that value.

Not sure what exactly you are asking :)
 
That's because A*B == B*A (first grade math :D). When first hook returns multiplied value it also modifies the argument to the same value, so the next hook will start it's modifications from that value.

Not sure what exactly you are asking :)

Haha, oh right, ok then different example: Mod A does (original_chance + 20), while Mod B does (original_chance * 1.5). What decides which of these operations is carried out first (e.g. if original_chance is 40, what decides whether the outcome is 90 or 80)?
 
Well not all mods can be combined. In docs I just pointed out that you CAN make some mods inter-compatible with each other.
In your example the outcome is undefined so you shouldn't use those mods together :)
 
Last edited:
Just a little note: apparently when you set the global script type to 1 (or, less surprisingly, 2), the generic procedures (map_enter, map_exit) no longer hook. Not a big problem of course, but took me a while to figure out :P.
 
So with all of that new Sfall stuff and stuff I wonder one thing.... Can we get back the little red dots while traveling on the worldmap? It's one of the few Fo1 features that I really want to see in Fo2 again.
 
I'm getting a lot of errors in the script editor when performing actions that would also crash it in older version:

Code:
************** Exception Text **************System.IO.IOException: The process cannot access the file 'C:\GOG.com\Fallout 2 Mapper\modderspack 3.6.7\ScriptEditor\errors.txt' because it is being used by another process.
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options)
   at System.IO.StreamReader..ctor(String path, Encoding encoding, Boolean detectEncodingFromByteOrderMarks, Int32 bufferSize)
   at System.IO.StreamReader..ctor(String path, Encoding encoding)
   at System.IO.File.ReadAllText(String path, Encoding encoding)
   at ScriptEditor.TextEditor.bwSyntaxParser_RunWorkerCompleted(Object sender, RunWorkerCompletedEventArgs e)
   at System.ComponentModel.BackgroundWorker.OnRunWorkerCompleted(RunWorkerCompletedEventArgs e)
   at System.ComponentModel.BackgroundWorker.AsyncOperationCompleted(Object arg)

It usually happens when I try changing the amount of variables carried over to a procedure without first changing that in the declaration. E.g. going from

Code:
procedure herp;
procedure herp begin
end

to

Code:
procedure herp;
procedure herp(variable derp) begin
end

Needless to say I don't have a single clue what "other process" could be using error.txt...
 
Looks like that's an oversight on my part. Other process is most likely the Parser which is running in another thread...

I suggest discussing Script Editor and other scripting tools here (to make this thread cleaner).
 
I was wondering, is there a way to use art_exists to find out whether a certain critter can perform a certain animation? E.g. whether obj_art_fid(dude_obj) can perform ANIM_burned_to_nothing?

I managed to get it to work for weapon animations per your instructions, which is already very helpful, but no clue how to address other types of art...
 
I was wondering, is there a way to use art_exists to find out whether a certain critter can perform a certain animation? E.g. whether obj_art_fid(dude_obj) can perform ANIM_burned_to_nothing?

I managed to get it to work for weapon animations per your instructions, which is already very helpful, but no clue how to address other types of art...

Yeah, sure. The third byte of FID is most likely the animation. Try art_exists((artFid bwand 0xff000fff) bwor (animId * 0x10000))
 
Yeah, sure. The third byte of FID is most likely the animation. Try art_exists((artFid bwand 0xff000fff) bwor (animId * 0x10000))

Hmm, doesn't seem to work:

Code:
    display_msg("exist tribal " + art_exists((FID_HMWARR bwand 0xff000fff) bwor (ANIM_sliced_in_half * 0x10000)) );
    display_msg("exist jmps " + art_exists((FID_HMJMPS bwand 0xff000fff) bwor (ANIM_sliced_in_half * 0x10000)) );

both return 0 where the second one should return 1.
 
@phobos2077, I looked all around, and the only way to set or read a object's flags (e.g. the "trans steam" flag that creates the ghost/stealth boy effect for critters) on an individual basis is with the mapper, right? If so, could there perhaps be added some script functions to accomplish this (e.g. get_obj_flags(obj) and set_obj_flags(obj, value))?

edit: then again, maybe these values are just hidden in has_trait already, but just not added to define.h?
edit2: guess not, at least I tested it for every has_trait(TRAIT_OBJECT, etc.) between 0 and 1000 and nothing turned up :p.
 
Last edited:
I'll just quote myself in this thread:

Just found out that I can't use the mapper anymore with my current graphic card. Either the screen stays black or everything seems to be "out of screen" with totally fucked up colors. Same happens to Fallout2.exe - but I can kinda fix this with switching to dx9 mode (everything is highly pixelated and shitty looking, though. That was never the case before (/edit: ok, fixed this with setting GraphicsWidth/Height the same values as my screen resolution with the high res patch)... which I apparently can't do with the mapper.

Any ideas on how I can fix this? Not being able to use the mapper is like... well... kinda shitty.

Fo2 runs in dx9 mode with Sfall, but the mapper doesn't have such a feature, therefore I can't use it anymore. Anyone got any idea or solution for my problem? If I can't fix this, I will be out of Fo2 modding officially from now on.
 
Back
Top