Another Attempted Tutorial: Caravans

JimTheDinosaur

Vault Dweller
Modder
To conclude my trilogy of tutorials by beginners for beginners, here's one on caravans. My last tutorial didn't get a single comment; which, being an eternal optimist I interpreted as a sign that the tutorial was perfect and every veteran simply nodded his head approvingly while reading it.

As an aside, caravans were always something I was a bit dissappointed in in Fallout. I mean, they were a lot of fun, but they basically only existed as little more than a get rich quick scheme, whereas you would think a world where even plants and ants have mutated into giant killing machines, moving around from town to town would generally require armed caravans.

But anyway, let's say we want to make a caravan of trappers from Klamath to the Den, what do we do? As previously, I'm going to assume you understand the basics (this time I'm also going to assume you understand that whenever you add a new procedure/node/variable, you need to introduce it in the script).

1: There are no existing caravan routes from Klamath to the Den, so we need to make one in BHRNDDST.H. Start by adding the line:

Code:
#define WM_In_Klamath_Den(x,y)                          ((x >= 300) and (x <500>= 100) and (y <= 300))

Here we delineate the area in which our encounters will take place. Unless there's a worldmap with x and y coordinates lying around somewhere I don't know of, you just need to look at the city coordinates in city.txt to get an idea where you are on the world map (e.g. the example is a box with Klamath at the top left corner and the Den at the lower right corner). Also, check that the area doesn't overlap with an already existing one. Now for the route:

To the bottom of the file, add:

Code:
// Klamath --> Den, 1 Encounter Only
#define Klamath_Den_1_1_x                        (420)
#define Klamath_Den_1_1_y                        (175)

// Klamath --> Den, 2 Encounters
#define Klamath_Den_2_1_x                        (390)
#define Klamath_Den_2_1_y                        (150)
#define Klamath_Den_2_2_x                        (450)
#define Klamath_Den_2_2_y                        (250)

The x,y coordinates show where you end up on the world map if the caravan "fails", and which encounter is fired (so, since all the coordinates are within our Klamath to Den box, these are the encounters that are fired). You can of course have as many possible encounters as you like, but 2 sounds about right here.

2: Now for the encounters, go to BHRNDDST.SSL (so this time the script, not the header file) and add the line:

Code:
                                        else if (WM_In_Klamath_Den(worldmap_xpos,worldmap_ypos)) then                  \
                                            call The_Den_Encounter;

Now let's define the procedure The_Den_Encounter:

Code:
procedure The_Den_Encounter begin
   variable Encounter;

   Encounter:=random(0,99);

   // enc_00
   if (Encounter < 50) then begin
       debug_msg("Klamath_Encounter Table, Encounter 01");
       display_msg(mstr(403));
       Encounter_Counter:=random(8,12);
       Encounter_Rotation:=dude_cur_rot;
       call Klamath_Rats;
   end
   // enc_01
   else if (Encounter < 100) then begin
       debug_msg("Den_Encounter Table, Encounter 01");
       display_msg(mstr(501));
       Encounter_Counter:=random(1,3);
       Encounter_Rotation:=dude_cur_rot;
       call Klamath_Robbers;
   end
end

I've kept it simple here: there's two possible encounters (50/50 chance) on the caravan: klamath_rats and klamath_robbers, which we'll get to in a second. Encounter_Counter is what determines how many critters show up, though you shouldn't mistake it for the exact number (e.g. in this example, there won't be 8-12 rats). To see what it does mean, let's set up the Klamath_Rats procedure:

Code:
procedure Klamath_Rats begin
   variable Critter_Counter:=0;
   variable Critter_Total;
   //variable Critter;
   variable Critter_Tile;
   variable item;

   Critter_Tile:=tile_num_in_direction(tile_num(dude_obj),Encounter_Rotation,dude_perception*2);

   Critter_Total:=(Encounter_Counter*2)/5;
   Critter_Counter:=0;
   if (Critter_Total < 1) then
       Critter_Total:=1;

   while (Critter_Counter < Critter_Total) do begin
       Create_Enemy(PID_MOLE_RAT,-1,SCRIPT_ECRAT);
       inc_map_var(MVAR_Hostile_Total);
   end

   Critter_Total:=(Encounter_Counter*4)/5;
   Critter_Counter:=0;
   if (Critter_Total < 1) then
       Critter_Total:=1;

   while (Critter_Counter < Critter_Total) do begin
       Create_Enemy(PID_PIG_RAT,-1,SCRIPT_ECRAT);
       inc_map_var(MVAR_Hostile_Total);
   end
end

Suppose the Encounter_Counter comes out as 10, then the outcome will be 10*2/5 = 4 mole rats and 10*4/5 = 8 pig rats, so twelve in total (in case you're wondering, non-round numbers get rounded down).

The thing I still can't get a handle on is how critters are placed. In this example, we defined Encounter_Rotation as dude_cur_rot so with
Code:
Critter_Tile:=tile_num_in_direction(tile_num(dude_obj),Encounter_Rotation,dude_perception*2);

you'd expect the enemies to get placed in front of where the player is facing; instead they end up all over the place. If anyone can tell me why I'd love to know.

3: Now to set up the caravan. First, make a global variable called TRAPPER_CARAVAN. Then, in the script of the trappers in Trapper Town (kctrapr.ssl), add the Start_Caravan procedure and have it called through dialogue or something:

Code:
procedure StartCaravan begin
   variable Caravan_Carts;
   variable Caravan_Encounters;
   variable Caravan_Guards;

   set_global_var(GVAR_CARAVAN_START, CARAVAN_KLAMATH);
   set_global_var(GVAR_CARAVAN_END, CARAVAN_THE_DEN);
   set_global_var(GVAR_TRAPPER_CARAVAN,CARAVAN_STATUS_ON_JOB);

   set_caravan_status(CARAVAN_STATUS_ON_JOB);
   Caravan_Encounters := random( 0, 2 );
   set_caravan_encounters( Caravan_Encounters );

   //debug_msg("ENCOUNTERS=" + k);

   if (global_var(GVAR_CARAVAN_ENCOUNTERS_TOTAL) == 0) then begin
      Caravan_Guards := 1;
      set_caravan_guards( Caravan_Guards );
      
      game_time_advance(6*ONE_GAME_DAY);
      load_map(MAPSTR_DEBBUS1,0);
      
   end
   else begin
      Caravan_Carts:=1;
      Caravan_Guards:=0;

      set_caravan_masters(1);
      set_caravan_guards( Caravan_Guards );
      set_caravan_carts( Caravan_Carts );
      set_caravan_brahmin( 2 * Caravan_Carts );
      set_caravan_drivers( 2 * Caravan_Carts );

      load_map(MAPSTR_BHRNDDST,11); //<-robs map script ignores this index...must be above 10
                                    //however to put caravan in center of map
   end
 end

As you can deduce, this caravan goes from Klamath to the Den, with 0 to 2 encounters (if you want more you need to add the coordinates to the header file in step 1), one caravan master, one cart (how many gecko pelts can they have), two brahmin, and two drivers.

4: Now, go back to BHRNDDST.ssl and add the following line to map_enter_p_proc:

Code:
       else if (global_var(GVAR_TRAPPER_CARAVAN) == CARAVAN_STATUS_ON_JOB) then begin
           call Trapper_Caravan;
       end

For the Trapper_Caravan procedure:

Code:
procedure Trapper_Caravan begin
   variable Encounter_Number;
   
   call Klamath_Den_Encounters;

   call Build_Caravan_Team;
end

Now, scroll down to the Build_Caravan_Team procedure. For my example, I just tried to replicate the three trappers in Trapper Town, but you can do whatever you feel like of course. For instance, for the first caravan driver I used:

Code:
       else if (global_var(GVAR_TRAPPER_CARAVAN) == CARAVAN_STATUS_ON_JOB) then begin
                 Critter:=create_object_sid(PID_MALE_TRAPPER,0,0,SCRIPT_RCCVNGRD);  
                 add_obj_to_inven(Critter,create_object(PID_10MM_JHP,0,0));     
                 item:=create_object(PID_SPRINGER_RIFLE,0,0);   
       end

This creates the leather jacket trapper with a pipe rifle (wielded) and some rounds. This is different from the system the script usually uses, which is in the form of a random male/female of a certain type, with a random weapon chosen from a few options. To simply copy this system is risky given that most models only support a few types of weapons. So, for instance, if I had used this system with either male or female trapper, with either a spear or a pipe gun, then the female trapper could end up with a pipe gun she couldn't use (because of no animation being there).

Finally, just add one additional procedure:

Code:
procedure Klamath_Den_Encounters begin
   variable Encounter_Number;

   Encounter_Number:=total_encounters - encounters_left;

   if (total_encounters == 1) then begin
       set_caravan_pos(Klamath_Den_1_1_x,Klamath_Den_1_1_y);
       set_exit_grids(0,-2,0,20100,0);                          // Exit grids on elevation 0 goto Worldmap
       game_time_advance(6*ONE_GAME_DAY);
       check_area;
   end

   else if (total_encounters == 2) then begin
       if (Encounter_Number == 0) then begin                    // this is the first encounter
           set_caravan_pos(Klamath_Den_2_2_x,Klamath_Den_2_2_y);
           set_exit_grids(0,-2,0,20100,0);                      // Exit grids on elevation 0 goto Worldmap
           game_time_advance(3*ONE_GAME_DAY);
           check_area;
       end

       else if (Encounter_Number == 1) then begin               // this is the second encounter
           set_caravan_pos(Klamath_Den_2_1_x,Klamath_Den_2_1_y);
           set_exit_grids(0,-2,0,20100,0);                      // Exit grids on elevation 0 goto Worldmap
           game_time_advance(3*ONE_GAME_DAY);
           check_area;
       end
   end

end

The only additional thing this really does in practice is let you decide the time that elapses between encounters.

And there you have it.

IMPORTANT NOTE: After hours of dealing with an incredibly annoying CTD problem when making a new caravan, I finally found the solution. If you look at the build_caravan procedure, you should take note of the fact that beneath the part where the guards are placed there's this line:

Code:
       add_obj_to_inven(Critter,item);
       wield_obj_critter(Critter,item);

This gives a shortcut to having critters get and wield weapons. Somewhat counter-intuitively, this also apparently means that if you try to place a critter WITHOUT this shortcut in place (say, you try placing an unarmed critter), the game goes berserk. Just keep this in mind.

EDIT 2: Given that this is a very barebones tutorial, I didn't add a lot of features that are easy enough to implement (for instance, if you want to use a script different than the generic guard/master/driver ones, you should remember to add the commands that remove them from the caravan should they die, etc.). However, there's one last element of the caravan script that is counterintuitive enough to warrant an additional note, namely that the system used by the developers is that you automatically "fail" the run the moment an encounter takes place, and that you lose this failure the moment you kill all the baddies. This is to make sure you don't go to the next encounter once you leave the map during an encounter.
 
Heh, "tutorials by beginners for beginners". :)
You do realize BHRNDDST.SSL is by far the biggest script in the game, right? So if you understand that I'd say you are a bit more than just a beginner.

Anyway, just wanted to point out that the worldmap coordinates in BHRNDDST.H aren't 100% accurate. Several encounters falls in between the gaps (no matching coordinates) and result in empty encounters.

Also, while looking at BHRNDDST.H, it leads you to believe you can use coordinates bigger than 1400 (or if 1399 was max, can't remember). If you do, you will go out of bounds and the game will use the default coordinates instead (X 120 Y 350).

Oh, and there's a problem with the game checking what your coordinates are with the caravan encounters.
When you enter an encounter the game will check the coordinates of the map you just left (your previous location), not of the one you are currently on. Maybe it sets up the encounter before the coordinates have had time to change or something. I don't know, but something is wrong anyway.
 
Darek said:
Heh, "tutorials by beginners for beginners". :)

Well, I've been modding Fallout for two weeks now, which in any sensible undertaking would qualify me as a beginner. Though it's true that in the context of Fallout modding, where 99.9% probably gives up in frustration within the first few hours, I probably count as something of an eminence grise by now.

You do realize BHRNDDST.SSL is by far the biggest script in the game, right? So if you understand that I'd say you are a bit more than just a beginner.

"Understand" is a big word; like I said, I still have no clue how the critter placing system works for instance.

Anyway, just wanted to point out that the worldmap coordinates in BHRNDDST.H aren't 100% accurate. Several encounters falls in between the gaps (no matching coordinates) and result in empty encounters.

For some reason, the system I outlined above also results in empty maps every now and then, while the encounters are all safely within the bounds of the box I made for them.
 
Back
Top