Fallout 2 mod FO2 Engine Tweaks (Sfall)

Really great that you can now force animations from global/hook scripts, but I was wondering why the following still won't work for me. If I put this in Klint's script under description_p_proc:

Code:
                  reg_anim_clear(self_obj);
                  reg_anim_begin();
                  reg_anim_obj_run_to_tile(self_obj, dude_tile, -1);
                  reg_anim_obj_run_to_tile(self_obj, tile_under_cursor, -1);
                  reg_anim_end();

then the chap runs towards me, then back towards where I clicked on him. If, however, I put the same thing in hs_keypress (checking for the critter on the tile_under_cursor rather than self_obj), then all he, or any other critter, does is perform the first action: he runs towards me, then stops in his tracks without going back to the location of the cursor.


edit:

Just tested it again myself and here's the deal:

  • you can use exported procedures between normal scripts (objects and maps), but you must respect scripts loading order. In other words, scripts with "export" should be the one's that loaded first. Typically export everything in map script and import in object script
  • however, there is obviously something wrong with them when using from sfall scripts, when I call imported procedure, it works, but the script stops working after that call, and nothing is printed in debug.log (no crash)
  • they were even more broken before sfall, some TeamX guy made patches very long time ago, which Timeslip incorporated - it fixed several crashes. Though, looks like more fixing is required for full usage.


Script load order is:

  1. Map script
  2. Map objects scripts
  3. Sfall Hook scripts
  4. Sfall global scripts

Would I be right to conclude from this that the safest way to import procedures across hook and global script would be to export them from the dude script? After all, it's the only one reliably called every time and before all the hook and global scripts, right?
 
Last edited:
then the chap runs towards me, then back towards where I clicked on him. If, however, I put the same thing in hs_keypress (checking for the critter on the tile_under_cursor rather than self_obj), then all he, or any other critter, does is perform the first action: he runs towards me, then stops in his tracks without going back to the location of the cursor.
Can you show the code in hs_heypress?

Would I be right to conclude from this that the safest way to import procedures across hook and global script would be to export them from the dude script? After all, it's the only one reliably called every time and before all the hook and global scripts, right?

Dude, this post is outdated. Exported procs should work everywhere. The only limitation is when you use them in "start" procedure while the game is loading. In other cases, all exported procedures regardless of load order or script type should be available in all other scripts.

I double-checked the engine again, looks like there is no way to export procedures without calling "start" proc at the same time. So you will have to make sure not to call imported procedures while game_loaded is true.
 
Last edited:
Can you show the code in hs_keypress?

Code:
            clubs:=tile_get_objs(tile_under_cursor, dude_elevation);
            foreach value in clubs begin
               if (obj_type(value) == OBJ_TYPE_CRITTER) then begin
                  reg_anim_clear(value);
                  reg_anim_begin();
                  reg_anim_obj_run_to_tile(value, dude_tile, -1);
                  reg_anim_obj_run_to_tile(value, tile_under_cursor, -1);
                  reg_anim_end();
                  display_msg("" + obj_name(value));
               end
            end

Exported procs should work everywhere.

Weird... I was doing my testing with hs_keypress, outside of game_loaded... ah well, I was probably doing something wrong.

edit: btw, is there any way of finding out who it is you are in dialogue with/initiated dialogue with? I would love to be able to port the contents of containers to critters for the purpose of bartering via global scripts. I suppose I could do it for every critter on the map while in dialogue mode but that seems like overkill :P.
 
Last edited:
I finally got around to trying the new RP and checking how fhe CorpseLineOfFireFix affects the burst fire bug. My finding was that it doesn't.

On both of the available CorpseLineOfFireFix settings, burst attacks worked the same way as in the unmodded game. That is to say, they did normal, full damage when no corpses were on the line of fire. But if the target was merely standing in the same hex as a corpse, then only about 2 bullets hit the target even though Chance To Hit was 95%.

Apparently, this is a separate bug from the one that the fix addresses, i.e. corpses in the line of fire affecting Chance To Hit.

Instead, this is a bug whereby the Chance To Hit is ostensibly unaffected by the corpse, but the corpse still absorbs many or most of the burst rounds if it's in the line of fire. Hence the 95% Chance To Hit coincides with most of the burst rounds going to waste.

It seems to me that the way burst attacks work is the following:

The Chance To Hit that is displayed applies strictly to the first bullet only, i.e. if it's 95%, then the first bullet will most likely hit the intended target. However, the rest of the burst rounds follow an altogether different, unknown formula. We know about the damage cone, which makes an increasing percentage of the bullets stray off course and hit secondary targets as distance from target increases. But there's another, buggy phenomenon which causes all the rounds except the first one to hit corpses just as easily as if the critters were alive and standing. In fact, a corpse sharing the same hex with a living critter has priority over the living critter when determining who catches the bullets. This may be because the corpse "was there first".

This bug sounds like a bitch to fix. Yet Fallout 1 doesn't have it. :/
 
Burst fire is an enigma. I read Atom's post. I'm not sure I'm understanding it correctly. This is how I interpreted it:

First, 1/6 of the bullets try to hit the primary target. Chance To Hit determines their chance of success. Then, another 1/6 of the bullets (plus the ones from the first 1/6 that missed the primary target) try to hit another target in the Center path.

Then, 1/3 of the bullets try to hit the first target that they encounter one hex left of the Center Path. Chance To Hit determines their chance of success. If any of them miss, then those bullets will try to hit the next target that they encounter on the same path (1 hex left of center).

Then, 1/3 of the bullets try to hit the first target that they encounter one hex right of the Center Path. Chance To Hit determines their chance of success. If any of them miss, then those bullets will try to hit the next target that they encounter on the same path (1 hex right of center).

Is this what it says?

If so, it makes sense. But that formula clearly does not apply when the main target is 1 hex away from the player. Because in such a scenario, way more bullets than 1/6 or 1/3 hit the main target. I've done quite a lot of experimenting with burst fire from 1 hex away, and have found that the % of misses increases the more rounds there are in a burst, but most bullets still hit the primary target. I shot 100 (sometimes 200) bursts at a target 1 hex away with several weapons, and then calculated the average number of bullets hitting by dividing the average damage per burst by the weapon's average damage per bullet, and found that:

-When rounds per burst was from 5 to 12, 90-95% of the rounds hit the intended target. This was tested with the CAWS, the G11, the G11E, the FN FAL, and the P90c.
-A 15-round burst weapon (Bozar) averaged about 13 hits (86.7%) on the target.
-A 20-round burst weapon (FN FAL HPFA) averaged about 17 hits (85%) on the target.
-A 25-round burst weapon (Vindicator) averaged about 21 hits (84%) on the target.
-Both the Minigun and the Avenger only averaged about 30 hits with 40 bullets (75%).

While I don't understand the formula behind these results, it is clear than when the main target is 1 hex away, then it does absorb most of the burst rounds.

Unless it's standing in the same hex as a corpse, that is. In that case, it seems to only catch 1/6. This is exactly what I was reporting in my previous post. The H&K P90c was doing the expected 200-300 HP of damage on mantises when they were standing 1 hex away. But whenever one mantis died and another came to occupy the same hex and was subsequently attacked with a burst, that burst would only do the damage equivalent to 2 bullets hitting. And this was in the newest Restoration Project, which has CorpseLineOfFireFix set to 2, "completely block corpses from absorbing fire", by default.
 
While I don't understand the formula behind these results, it is clear than when the main target is 1 hex away, then it does absorb most of the burst rounds.
If the target is only 1 hex away, all groups will most likely meet it first. When your target standing in the same hex as a corpse, the corpse will also be taken into account by the groups of bullets, just works as if it's between you and your target.
Currently CorpseLineOfFireFix doesn't work as the description, it seems the modified hit chance (5% for mode 1, 0% for mode 2) for corpses still got "override" by your actual hit chance.

EDIT: I only found a partial solution for the "corpses absorb burst rounds" issue. With hs_tohit hook script:
Code:
procedure start;

procedure start begin
    variable target;
    if not init_hook then begin
        target:=get_sfall_arg;
        target:=get_sfall_arg;
        target:=get_sfall_arg;
        if (critter_state(target) bwand 1) then begin // is_critter_dead in define.h
            set_sfall_return(0);
        end
    end
end
It works when the corpse is between the target and the attacker (no longer absorbs bullets), but it still cannot solve the issue if the target is in the same hex as the corpse.
My guess is critter_state couldn't return proper state of the corpse because there's a living critter "on it". It might only get the state of the living critter, so the game still thinks there are two valid targets for burst rounds. At least for now it's better than nothing I guess.

EDIT 2: phobos2077 found the reason why CorpseLineOfFireFix doesn't work: it conflicts with ToHit hook (in main.cpp - HookCall(0x42331F, CorpseHitFix) ; in hookscripts.cpp - HookCall(0x42331F, &ToHitHook))
The code of the fix itself does work (if I comment out the line of ToHit hook and recompile) but only in the same way as the hs_tohit hook script above. The "same hex" issue is still there.
 
Last edited:
CombatDamage hook script was expanded with hooks in more places (where original function is called by the engine), additional arguments and return values. Here's updated description:
Code:
Runs when:
1) Game calculates how much damage each target will get. This includes primary target as well as all extras (explosions and bursts). This happens BEFORE the actual attack animation.
2) AI decides whether it is safe to use area attack (burst, grenades), if he might hit friendlies.
Does not run for misses, or non-combat damage like dynamite explosions.

critter arg1  - The target
critter arg2  - The attacker
int     arg3  - The amount of damage to the target
int     arg4  - The amount of damage to the attacker
int     arg5  - The special effect flags for the target
int     arg6  - The special effect flags for the attacker
int     arg7  - The weapon used in the attack
int     arg8  - The bodypart that was struck
int     arg9  - Roll result of the attack (check with is_success, etc functions; basically: 0-crit. fail, 1-fail, 2-success, 3-crit. success)
int     arg10 - Number of bullets actually hit the target (1 for melee attacks)
int     arg11 - The amount of knockback to the target

int     ret1 - The damage to the target
int     ret2 - The damage to the attacker
int     ret3 - The special effect flags for the target
int     ret4 - The special effect flags for the attacker
int     ret5 - The amount of knockback to the target

If anyone was considering to make yet another "ammo mod" (damage formula), please use this hook instead. It has all necessary info to replace damage calculations from scratch.

Though it still doesn't get called for critter_dmg, right? At least it didn't fire when I tested it. That'd still cause some problems for people making new formulas using only a combatdamage script.
 
Do grenades have a chance for a critical failure? I've never seen it occur. It could make a Jinxed game very entertaining. :V
 
Though it still doesn't get called for critter_dmg, right? At least it didn't fire when I tested it. That'd still cause some problems for people making new formulas using only a combatdamage script.

AFAIK, all new formulas place their code inside compute_dmg_ function, which is now fully hooked in all places. You can create new formulas with scripts alone.

What do you mean by "critter_dmg"? A scripting function with the same name? In this case I bet the current "formulas" doesn't affect it either. I believe damage-vs-armor stats calculations are made at a more lower level then compute_dmg_.
 
Though it still doesn't get called for critter_dmg, right? At least it didn't fire when I tested it. That'd still cause some problems for people making new formulas using only a combatdamage script.

AFAIK, all new formulas place their code inside compute_dmg_ function, which is now fully hooked in all places. You can create new formulas with scripts alone.

What do you mean by "critter_dmg"? A scripting function with the same name? In this case I bet the current "formulas" doesn't affect it either. I believe damage-vs-armor stats calculations are made at a more lower level then compute_dmg_.

Interesting, yeah I meant the "critter_dmg(WHO,AMT,DMG_TYPE)" function you use to script a critter receiving damage (e.g. in traps). Damn, that speaks in favor of YAAM's and @Glovz 's ammo mods which don't involve changing the DR/DT fundamentals, but just the way ammo affects it... Guess it's back to the drawing board for me.

Unless you'd feel like investing time in a damage-vs-armor hook of course ;) .

edit: damnit, I actually missed your comment at the end about dynamite where you already explained the problem, I have to learn how to read.
 
Last edited:
I want to transport my UI additions from the obj_dude script to a global one, but I can't get it to work:

Code:
procedure start begin
	set_global_var(643, (get_screen_width/2) - (640/2));
	set_global_var(644, get_screen_height-(480-362));
		call intface;  
end            


procedure DisplayThing begin
   display_msg("henk");
end


procedure do_nothing begin
end


procedure intface
begin
	base_pos:=250;
	createWin("newface", global_var(643), global_var(644), 640, 17);
	selectWin("newface");
	display("PCX\\newface\\face_bg.pcx");
	showWin;
	addButton("item_in_tile", base_pos, 1, 30, 15);
	addButtonGfx("item_in_tile", "PCX\\newface\\face_item_tile2.pcx", "PCX\\newface\\face_item_tile1.pcx", "PCX\\newface\\face_item_tile1.pcx");
	addButtonProc("item_in_tile", do_nothing, do_nothing, do_nothing, DisplayThing);
	showWin;
end

The requisite graphics all pop up as intended and when I click on the button it lights up per the graphic change. But the button proc doesn't fire: there's no displayed message (or anything else I try).
 
Hm.. Are you sure you using the latest sfall build? If so, can you give me your compiled script (.int)?

My ddraw.dll is two weeks old so I doubt that's the issue, but I'll try some stuff out with different builds (again, I should get more organized, heh).

In the meantime, I was wondering something. I put together some more interesting (to me at least) party member leveling stuff (you can now tell them which skills to focus on from that point on, and then they start improving in those on leveling, etc.), and I've decided to go all out and embrace full party member control in the way you've improved it (as I understood from your notes almost all the kinks are out now, right?).

But, I'm still not completely comfortable with how party member control fits in Fallout: it feels a bit "gamey" and breaks the immersion of party members as individuals with their own personalities. So I was wondering if it were possible to change which party member pids can be controlled via script: only a great leader (I have a leadership skill for that, but you could also use CH) can control his most unruly troops (e.g. Myron), while a poor leader will still see them charge into the enemy head first.

It would also make it more flexible generally, let you enable which ones you control on the fly, in the same way you can do outside of combat with my new party member movement stuff.
 
Do grenades have a chance for a critical failure? I've never seen it occur. It could make a Jinxed game very entertaining. :V

I think you can set them to do so with Cubik's editor. I know there is some sort of options there numbered 1 through 5 with things like explode in hand and lose a turn, or something to that effect. I've used this with my own tinkering.
 
I ran into a very weird bug (I think); I tested it both in the lastest release version of sfall and in the test version phobos sent me. It seems that using BWOR DMG_BYPASS_ARMOR (or BWOR DMG_NO_ANIMATE) breaks the loop through the critter list for some reason:

Code:
      foreach critter in list_as_array(LIST_CRITTERS) begin
         debug_msg("critter " + critter);
         critter_dmg(critter,20,(0 BWOR DMG_BYPASS_ARMOR));
       end

This only damage the first critter on the map (i.e. the dude) and then returns 0 as the next critter, as shown in the debug message:

Code:
critter 74843528critter 0Script Error: scripts\gltesting3.int: op_critter_damage: obj is NULL

Weird, works perfectly fine without the BWOR extras... I could get around it by making a temporary array of all the critters and then damaging them all using a for loop, but it's annoying all the same (edit: actually not sure why I thought this worked, cause it doesn't... I guess there's enough alternative methods though).

edit: it seems to specifically be a problem with critter_dmg, e.g. this:

Code:
      foreach critter in list_as_array(LIST_CRITTERS) begin
         display_msg("critter " + critter);
         critter_injure(critter,(DAM_CRITICAL BWOR DAM_LOSE_TURN));
       end

doesn't break the loop.
 
Last edited:
Well I see only two possible explanations. Either LIST_CRITTERS actually include null objects for some weird reason, or there is a bug in compiler. Send your .int of compiled script where loop breaks.
Also try adding additional condition, like this:
Code:
       foreach critter in list_as_array(LIST_CRITTERS) begin
         debug_msg("critter " + critter);
         if (critter) then 
            critter_dmg(critter,20,(0 BWOR DMG_BYPASS_ARMOR));
       end
 
It looks like the list itself is fine:

Code:
      debug_msg("first: " + debug_array_str(list_as_array(LIST_CRITTERS)));
      foreach critter in list_as_array(LIST_CRITTERS) begin
         debug_msg("second: " + debug_array_str(list_as_array(LIST_CRITTERS)));
         if (critter) then begin
            debug_msg("critter1 " + critter + " len " + len_array(list_as_array(LIST_CRITTERS)) + " 0 " + get_array(list_as_array(LIST_CRITTERS), 0) + " 1 " + get_array(list_as_array(LIST_CRITTERS), 1));
            critter_dmg(critter,20,(6 BWOR 256));
         end
         debug_msg("critter2 " + critter + " len " + len_array(list_as_array(LIST_CRITTERS)) + " 0 " + get_array(list_as_array(LIST_CRITTERS), 0) + " 1 " + get_array(list_as_array(LIST_CRITTERS), 1));
   	end
      debug_msg("third: " + debug_array_str(list_as_array(LIST_CRITTERS)));

I deleted every other script in my mod folder and didn't include any of my custom headers. The only other critter is Klint. This gives this in the debug message:

Code:
first: List(2): [76940680, 94968896]
second: List(2): [76940680, 94968896]
critter1 76940680 len 2 0 76940680 1 94968896
critter2 76940680 len 2 0 76940680 1 94968896
second: List(2): [76940680, 94968896]
critter2 0 len 2 0 76940680 1 94968896
third: List(2): [76940680, 94968896]

Sorry for the messiness, but wanted to make sure I didn't miss anything. The list seems proper at every stage, and yet when iterating over it, the one after dude becomes null.
 
Ok, I've found the problem. When you call list_as_array sfall creates a temp array. As you know temp arrays are erased at the end of the frame. Now they funny part is, looks like critter_dmg function halts script execution for a few frames to start animation of critter being hit. So while you iterating over your array with foreach at the end of first iteration your array is lost, that explains why you get NULL object.

Quick solution:
Code:
   temp := list_as_array(LIST_CRITTERS);
   fix_array(temp);
   foreach (critter in temp) begin 
....
   end
   free_array(temp);

I never knew there was scripting functions that could force frames to pass. Maybe we need to adjust when temp arrays are cleared.
 
Back
Top