Fallout 2 mod (Solved) How do I supply the player with sharpened poles during earlygame? I need ideas

Velizar

First time out of the vault
I'm working on a mod which adds new weapons and I really want to add a harpoon gun which is available from the start of the game and shoots sharpened poles and spears. The problem is that they're very scarce in the original game. It will become obsolete slightly after the Den, so I only need to solve the problem until then.

One of the goals of the mod is to leave all the existing game maps unchanged if at all possible. This achieves compatibility with other mods and also simplicity.

I could use Mr. Fixit to make them craftable, but then the player would need to find the materials, and I can't think of anything available in the early game which is a reasonable material for either a spear or a sharpened pole.

I would love it if there was a way to add it to the list of restocked items to ALL auto-restocking vendors as long as the player's level is 1-6, but I don't think SSL has the necessary metaprogramming utilities - vendors restock using check_restock_item, but I'm not aware of a way to add a hook which gets called whenever that gets called. I definitely don't want my mod to look at individual vendors or their boxes - I strongly prefer to automatically recognize them. Maybe someone more advanced knows of a way to do this? Edit: Maybe I can do something with the generic storage box?

I could add a new location next to Arroyo with a pole/spear vendor, but I would hate to do this because it's such a clunky and forced solution just for a single weapon - I would rather not have the harpoon gun at all.

Any better ideas on how to do this?


Edit: Now that I thought I can't solve it without help and wrote up my problem, I came up with a solution which might work... :roll:
Here's my idea: add a global procedure which triggers on HOOK_REMOVEINVENOBJ, and when someone is moving their inventory to generic_temp_box, then they're recognized as a vendor so we say that they'll restock sharpened poles and spears once every 5 days, and we add some to their inventory right now. Problems:
1. This is not in sync with how often they restock their other items, but I think that's ok.
Also the restock starts ticking down whenever they first swap their inventory, not whenever you enter the map, but I think that's ok too.
2. I'm assuming that every vendor swaps their inventory with generic_temp_box, is that true? Is that true even in most mods which aren't wild total conversions?
Edit2: Nope, the above won't work because point 2 is false - most vendors in the game don't use the temp box and use only one box instead.
 
Last edited:
The vendor restock is not some unique process, just a macro consists of various functions. It is checked/run when the player enters a map, so you can try running your additional restock in map_enter_p_proc procedure from a global script.
 
The vendor restock is not some unique process, just a macro consists of various functions. It is checked/run when the player enters a map, so you can try running your additional restock in map_enter_p_proc procedure from a global script.
The problem with this is that my script will need to know which NPCs are vendors or which containers are NPC inventories, and I don't want to hardcode that.
 
You can read the list of vendor data from an INI file, so people can edit the file to adapt the mod to other TCs without recompiling the script itself.
 
I wrote a scipt which automatically detects vendors. It relies on the fact that they receive items from a container on dialog start, as there are hooks for both moving an item and starting a dialog. There are a few small caveats explained in the comment at the beginning of the file, but overall I'm very happy with it.

I tested it in Arroyo, Klamath, and Den, and all seems to be working. The only mistake it made was that it didn't detect Mom as a merchant.

Before writing it I looked at all existing scripts which do an inventory move, and in theory it looked like this approach would work for the vast majority.

Another reason why I wanted a solution like this is because it allows me to seamlessly add any new types of ammo to the game, and I had some ideas for earlygame weapons which need lategame ammo.
Code:
/*******************************************************************************
        Name: Global vendor restock
        Description: Enables restocking of all vendors with custom items
        Imperfections:
          - Restocking happens on opening a dialog, rather than on entering the map
          - This script has their own restock cycle, independent from the NPC's
          - Vendor detection can make mistakes. Must satisfy all of the following:
            * On the first conversation with the NPC, it must "swap inventory"
                immediately before the dialog opens. No game time can pass
                from inventory swap until dialog opening
            * Inventory swap means the NPC must receive items from a container
            * The NPC must be a critter and with bartering enabled when the swap occurs
            * At least two different item types must be transferred to its inventory
          False positives include NPCs which don't restock and more.
          False negatives include NPCs which don't immediately swap inventory and more.
          Both of those are acceptable if you want the items to be findable,
            and you don't mind them ending up in (very few) extra NPC inventories.
*******************************************************************************/

#define NAME                    SCRIPT_GL_H_RSTCK

/* Include Files */
#include "DEFINE.H"
#include "COMMAND.h"
#include "sfall.h"
#include "define_extra.h"

/* Defines */

#define RECEIVED_ONE_ITEM  (1)
#define CONFIRMED_MERCHANT (2)
#define NOT_MERCHANT       (3)

/* Script Procedures */
procedure start;
procedure on_game_mode_change;
procedure on_remove_inventory_object;
procedure check_npc_for_restock(variable obj);

/* Local Variables which are saved. All Local Variables need to be prepended by LVAR_ */

/*
   Imported variables from the Map scripts. These should only be pointers and variables
   that need not be saved. If a variable Needs to be saved, make it a map variable (MVAR_)
*/

/* Local variables which do not need to be saved between map changes */

// map
// key: critter PID
// value: the first time when we saw the NPC get an item from a container
//        or RECEIVED_ONE_ITEM or CONFIRMED_MERCHANT or NOT_MERCHANT
variable critterMerchantStatus;

// map
// key: merchant PID
// value: the next time it must be restocked by our custom script
variable merchantNextRestock;

procedure start begin
   if game_loaded then begin
      critterMerchantStatus := load_array("critterMerchantStatus");
      merchantNextRestock := load_array("merchantNextRestock");
      if (critterMerchantStatus == 0) then
         critterMerchantStatus := {};
      if (merchantNextRestock == 0) then
         merchantNextRestock := {};
      save_array("critterMerchantStatus", critterMerchantStatus);
      save_array("merchantNextRestock", merchantNextRestock);
      register_hook_proc(HOOK_GAMEMODECHANGE, on_game_mode_change);
      register_hook_proc(HOOK_REMOVEINVENOBJ, on_remove_inventory_object);
   end
end

procedure on_game_mode_change begin
   if (get_game_mode == DIALOG) then begin // just entered a dialog
      variable pid = obj_pid(dialog_obj);
      // NPC didn't receive 2+ item types from a container or isn't barterable with
      if (critterMerchantStatus[pid] == 0) then
         critterMerchantStatus[pid] = NOT_MERCHANT;
      else if (critterMerchantStatus[pid] != CONFIRMED_MERCHANT) then begin
         if (game_time == critterMerchantStatus[pid]) then
            critterMerchantStatus[pid] = CONFIRMED_MERCHANT;
         else
            critterMerchantStatus[pid] = NOT_MERCHANT;
      end
      if (critterMerchantStatus[pid] == CONFIRMED_MERCHANT) then
         call check_npc_for_restock(dialog_obj);
   end
end

procedure on_remove_inventory_object begin
   variable srcObj := get_sfall_arg_at(0),
            removeReason := get_sfall_arg_at(3),
            dstObj := get_sfall_arg_at(4),
            dstPid := obj_pid(dstObj),
            srcAContainer := (obj_item_subtype(srcObj) == item_type_container),
            dstNotAnItem := (obj_item_subtype(dstObj) == -1),
            // Note: would return false for dude_obj
            dstBarterableWith := (CFLG_BARTER == (CFLG_BARTER bwand get_proto_data(dstPid, PROTO_CR_FLAGS)));
   if (removeReason == RMOBJ_CONTAINER and srcAContainer and dstNotAnItem and dstBarterableWith) then begin
      if (critterMerchantStatus[dstPid] == 0) then
         critterMerchantStatus[dstPid] := RECEIVED_ONE_ITEM;
      else if (critterMerchantStatus[dstPid] == RECEIVED_ONE_ITEM) then
         critterMerchantStatus[dstPid] := game_time;
   end
end

// rewritten check_restock_item using variable who_obj in place of self_obj
procedure check_restock_item0(variable who_obj, variable the_item, variable min_amt, variable max_amt, variable res_perc) begin
   restock_amt := random(min_amt, max_amt);
   if (obj_is_carrying_obj_pid(who_obj, the_item) < restock_amt) then begin
      if (res_perc >= random(1,100)) then begin
         stock_pid_qty(who_obj, the_item, restock_amt)
      end
   end else begin
      stock_pid_qty(who_obj, the_item, restock_amt)
   end
end

procedure check_npc_for_restock(variable npc_obj) begin
   variable pid = obj_pid(npc_obj);
   if (merchantNextRestock[pid] < game_time) then begin
      variable stockProbability := 50;
      if (get_sfall_global_int("h_1stNpc") == 0) then begin
         set_sfall_global("h_1stNpc", 1);
         stockProbability := 100;
      end
      if (get_pc_stat(PCSTAT_level) >= 7) then begin
         stockProbability := 0;
      end
      set_self(npc_obj);
      call check_restock_item0(npc_obj, PID_SHARPENED_POLE, 10, 20, stockProbability);
      merchantNextRestock[pid] := (random(2, 4) * ONE_GAME_DAY) + game_time;
   end
end
 
Last edited:
The hook is probably fine.

I haven't investigated, but by design my script can't detect every merchant correctly (it has both false positives and false negatives), since there was no way to do it better. So the error was most likely from my script.

My assumption was that probably Mom doesn't get a container inventory transferred to her in all dialog branches, or that the container inventory was empty or had only 1 item, but I decided it didn't matter much because unfixable errors were expected and are tolerated.

If you're curious then I could investigate further to find out exactly why she isn't being recognized.
 
Back
Top