Fallout 2 mod filling tiles a critter can see

QuantumApprentice

Where'd That 6th Toe Come From?
Hey!
Here's another question from my adventures into the scripting realm.

I'm trying to write a script that will place an object on every tile the critter can see in sneak mode.
I borrowed the gl_withinperception.ssl script to start working with, and these are the modifications I've made so far:
Code:
#include "..\headers\define.h"
#include "..\headers\command.h"
#include "..\headers\sfall.h"
#include "..\headers\define_extra.h"

procedure start;
procedure can_see(variable obj1, variable obj2);
procedure rand(variable min, variable max);
procedure poop(variable tile, variable source);

variable state := 0;

procedure start begin
   variable facing_direction;
   variable back_direction;
   variable tile;
   variable temp_poo;
   variable rotation;

   if game_loaded then begin
      register_hook(HOOK_WITHINPERCEPTION);
      //register_hook(HOOK_SNEAK);              //TODO: add hooks for this function?
   end else begin
      variable
         source := get_sfall_arg,
         target := get_sfall_arg,
         inrange:= get_sfall_arg,
         type   := get_sfall_arg, /* new arg */
         result := 0,
         distance, seeDistance, seeText;

         distance := tile_distance_objs(source, target);
         rotation := rotation_to_tile(tile_num(source), tile_num(target));


         seeDistance := get_critter_stat(source, STAT_pe);
         facing_direction := obj_get_rot(source);
         back_direction  := ((facing_direction + 3) % 6);

   variable i;
         for (i := 0; i < 3; i++) begin
            tile := tile_num_in_direction(tile_num(source), facing_direction + i, seeDistance);
            call poop(tile, source);
         end
         for (i := 0; i < 3; i++) begin
            tile := tile_num_in_direction(tile_num(source), back_direction + i, seeDistance);
            call poop(tile, source);
         end

         float_msg(dude_obj,
                   "Tile distance: " + seeDistance
                  + " tileNum: "     + tile
                  + " rotation: "    + rotation,
                   FLOAT_COLOR_WHISPER);
   end
end

procedure poop (variable tile, variable source) begin
   if (tile_contains_obj_pid(tile, elevation(source), PID_SMALL_GOO_1) or
       tile_contains_obj_pid(tile, elevation(source), PID_SMALL_GOO_2) or
       tile_contains_obj_pid(tile, elevation(source), PID_SMALL_GOO_3)) then begin
   end else begin
      variable temp_poo := create_object_sid(random(PID_SMALL_GOO_1, PID_SMALL_GOO_3), 0, 0, 0);
      move_to(temp_poo, tile, 0);
   end
end

This currently places the poop item at one for each cardinal direction (well, 6 directions for Fallout).
Problem is, I haven't been able to figure out how to add objects in the tiles between these.
The idea is to have an indicator for the viewable area of a critter when in sneak mode, making sneaking MUUUUCH more accessible for new and old players.

I could have sworn there was a way to draw a line between two tiles, but I haven't been able to find that yet. I also figure there's a better way to do this, but I have no clue on my own.
Just posting this here for the weekend.
 
Ok, I went a completely different direction this weekend, and just passed an object around the screen checking if the critter could see it or not. After some iterations trying to speed up the process (passing a critter object around 40k tiles sure does bog the engine down) I've come up with this:

Code:
#include "..\headers\define.h"
#include "..\headers\command.h"
#include "..\headers\sfall.h"
#include "..\headers\define_extra.h"

procedure start;
procedure poop(variable temp_obj, variable tile, variable source);
procedure sneak_check;

procedure start begin
   if game_loaded then begin
      register_hook_proc(HOOK_SNEAK, sneak_check);
      display_msg("Super Sneak Script loaded!");
   end
end

procedure poop (variable temp_obj, variable tile, variable source) begin
   move_to(temp_obj, tile, elevation(source));
   if(obj_can_see_obj(source, temp_obj)) then begin
      if not (tile_contains_obj_pid(tile, elevation(source), PID_SMALL_GOO_1) or
              tile_contains_obj_pid(tile, elevation(source), PID_SMALL_GOO_2) or
              tile_contains_obj_pid(tile, elevation(source), PID_SMALL_GOO_3)) then begin
         variable temp_poo := create_object_sid(random(PID_SMALL_GOO_1, PID_SMALL_GOO_3), tile, elevation(source), -1);
      end
   end
end

procedure sneak_check begin
   //TODO: pass an object around the critter within
   //      a certain range to run obj_can_see_obj() on them

   variable check = get_sfall_arg_at(0);

   if (check == 1 or check == 0) then begin

   variable sneak_range := 10;
   variable tile_var;
   variable temp_obj;
   variable critter;
   variable last_tile := tile_num_in_direction(dude_tile, 0, sneak_range);
   variable critter_array = [];
   critter_array := objects_in_radius(tile_num(dude_obj), 20, elevation(dude_obj), OBJ_TYPE_CRITTER);

   foreach critter in critter_array begin
      display_msg("critter: " + critter);
   if not (critter == dude_obj) then begin
      if(using_skill(dude_obj, SKILL_SNEAK)) then begin
         temp_obj := create_object_sid(PID_SMALL_RADSCORPION, 0, elevation(critter), -1);
         display_msg("temp_obj " + temp_obj);

         variable tile_array = create_array(6, 0);
         tile_array[0] := tile_num_in_direction(dude_tile, 0, 1);
         tile_array[1] := tile_num_in_direction(dude_tile, 1, 1);
         tile_array[2] := tile_num_in_direction(dude_tile, 2, 1);
         tile_array[3] := tile_num_in_direction(dude_tile, 3, 1);
         tile_array[4] := tile_num_in_direction(dude_tile, 4, 1);
         tile_array[5] := tile_num_in_direction(dude_tile, 5, 1);

         tile_var := tile_array[0];
         while not (tile_array[0] == last_tile) do begin
            variable i;
            for (i:=0; i<6; i++) begin
               variable next_dir := (i+1) % 6;
               while not (tile_var == tile_array[next_dir]) do begin
                  call poop(temp_obj, tile_var, critter);
                  tile_var := tile_num_in_direction(tile_var, rotation_to_tile(tile_var, tile_array[next_dir]), 1);
               end
            end

            variable j;
            for (j := 0; j < 6; j++) begin
               tile_array[j] := tile_num_in_direction(tile_array[j], j, 1);
            end

         end
      end
      destroy_object(temp_obj);
   end
      end
   end
end

Problems this script currently has:
1) I can't think of a way to make the script run again whenever the player moves dude_obj. Maybe a better solution would be to just run the script every 10 ticks or something, but then it could bog down the engine pretty badly. (also, I'm unsure how to use timer scripts yet)
2) The same problem applies to critters that move around the map.
3) Old visual range markers (the "poops" :P) aren't removed when the player (or critters) move yet. One possible solution is to attach a script to them that destroys them within a certain amount of time, another is to remove any outside the visual sneak_range of the player (currently set to 10 hexes). Will explore these more when I figure out how to get the script to run more often.
4) The critter being passed around, used to run obj_can_see_obj() on, has their own is_sneaking and sneak_success values that I'm not sure how to manipulate yet. As such, the tiles that are marked right now are actually the full visual range of the critter, not the reduced visual range that sneak mode offers. Will work on this as soon as I figure out the first couple of problems :P
5) The critter being passed around isn't set to invisible. And, while I can't see the critter on my PC, it's possible it may be visible when being passed around on others PC's. It's possible that setting the critter to invisible, or creating a critter with no animation art, could improve the speed. Unsure how the engine processes the visuals during critter placement yet.
6) Need to exclude party members from this script, would be a pita to have players try to sort that out visually :P

Commands I'm looking at:
add_g_timer_event()
set_object_dat()
set_critter_base_stat()
set_critter_extra_sta()



TLDR:
If anybody is interested in helping me out, I'm looking for examples of how to use a timer in a global script (or alternatively how to get a global script to run whenever a critter is moved), and how to use a global script to set critter attributes.
This script can be used to show the visual range of critters in the vicinity of the player right now, but that's about it.
 
Last edited:
If anybody is interested in helping me out, I'm looking for examples of how to use a timer in a global script
There is a command to set the global script repeat time. But it's tied to framerate.

"set_global_script_repeat(int frames)"

To call the script every 300 frames:
Code:
procedure start begin
       if game_loaded then begin
             set_global_script_repeat(300);
      end
end

and how to use a global script to set critter attributes
"set_object_data(obj, offset, value)" or "set_critter_base_stat(CritterPtr,int StatID, int value)" or other "set_critter_*" commands

offsets for "set_object_data" can be found in define_extra.h
StatID for "set_critter_stat" can be found in define.h

This script can be used to show the visual range of critters in the vicinity of the player right now, but that's about it.
At first I also wanted to do this mod, but when calling "create_object_sid" the game "froze" while spawning new items on a ground. And if you do this over all area of detection zone of all NPCs, then the game will constantly "freeze".
One of the options was to draw only the edge of the detection zone, but it was also accompanied by "microfreezes" when NPC moving or player stops sneaking.
Perhaps there is a way to change the color of tiles instead of spawn objects, then this mod can be implemented :)

In the end, I decided to do this:
Sneak Detection Mod demonstration
 
At first I also wanted to do this mod, but when calling "create_object_sid" the game "froze" while spawning new items on a ground. And if you do this over all area of detection zone of all NPCs, then the game will constantly "freeze".
One of the options was to draw only the edge of the detection zone, but it was also accompanied by "microfreezes" when NPC moving or player stops sneaking.
Perhaps there is a way to change the color of tiles instead of spawn objects, then this mod can be implemented :)

In the end, I decided to do this:
Sneak Detection Mod demonstration

I saw that last weekend, looks pretty awesome!

There is a command to set the global script repeat time. But it's tied to framerate.

"set_global_script_repeat(int frames)"
Going to try out set_global_script_repeat() this weekend, see how well it works.

Yeah, there is a short stutter whenever the player moves, and I haven't quite worked out how to get the script to detect critter movement yet, but I'll probably try and address that this weekend too.
I'm currently dropping objects on only the tiles within 10 hexes of the player (which I plan on tying to the players Perception stat eventually), and this performs reasonably well, but could use some optimization still.

"set_object_data(obj, offset, value)" or "set_critter_base_stat(CritterPtr,int StatID, int value)" or other "set_critter_*" commands

offsets for "set_object_data" can be found in define_extra.h
StatID for "set_critter_stat" can be found in define.h
I can't find anything in either header file that explicitly states it can be used to set the critter to use sneak mode or not.
I'll test out set_critter_stat(SKILL_SNEAK) and see if that works (here's hoping), but I have this sneaking suspicion it might only affect the skill level, not whether it's sneaking or not.

Anyway, here's my current version of the script, went through and refactored a bunch of stuff after last Saturday, will probably add more stuff this Saturday.
This currently works for at least 2 critters without becoming unplayable, but only works for critters that don't move so far.
I also ended up changing the "poop" objects that are outside the viewable range to invisible so I could clear the entire array in one step, but leave the objects on the ground in case they need to be made visible again...works ok...ish.
Code:
/*******************************************************************************
        Name:           gl_super_sneak
        Location:       global script
        Description:    Displays the currently visible area for critters
                        when the player is in sneak mode
                        Passes an object around the critter within
                        a certain range to run obj_can_see_obj() on
                        and drops a poop at that location if it returns success

         Created: by QuantumApprentice
         Updated: 11.18.2022
*******************************************************************************/
#include "..\headers\command.h"
#include "..\headers\sfall.h"
#include "..\headers\define.h"
#include "..\headers\define_extra.h"

procedure start;
procedure poop(variable temp_obj, variable tile, variable source);
procedure sneak_check;
procedure hook_sneak;
procedure perc_hook;
procedure destroy_poops;
procedure check_tiles(variable tile_array, variable temp_critter_obj, variable critter);
procedure expand_tile_array(variable tile_array);
procedure deal_with_poops_outside_sneak_range(variable tile_array);

// defines
#define visible 0
#define invisible 1

//global variables
variable poop_array

procedure start begin
   poop_array := create_array(1, 0);
   fix_array(poop_array);
   if game_loaded then begin
//////////////////////////////////////////////////////////////////////////
      register_hook_proc(HOOK_SNEAK, hook_sneak);
      register_hook_proc(HOOK_WITHINPERCEPTION, perc_hook);
      display_msg("Super Sneak Script loaded!");
//////////////////////////////////////////////////////////////////////////
   end
end

procedure perc_hook begin
//only sfall_arg_at(1) will ever be the dude_obj (no clue why this is)
   variable target = get_sfall_arg_at(1);
// if (target == party_member_obj) then begin
// end
   if (target == dude_obj) then begin
   variable tile_dude_obj = tile_num(dude_obj);
   // set_self(obj_name(target));
   float_msg(dude_obj, "timer: " + game_time,
                        FLOAT_COLOR_WHISPER);

      if(using_skill(dude_obj, SKILL_SNEAK)) then begin

         if (get_sfall_global_int("plyr_tle") != tile_dude_obj) then begin
            set_sfall_global("plyr_tle", tile_dude_obj);
            call sneak_check;
         end
      end else begin
         call destroy_poops;
      end
   end
end

procedure destroy_poops begin
   if (len_array(poop_array) > 1) then begin
   // destroy all poops and free the array
      variable stupid;
      foreach stupid in poop_array begin
         if (obj_pid(stupid) == PID_SMALL_GOO_3) then begin
            destroy_object(stupid);
         end
      end
      free_array(poop_array);
      poop_array := create_array(1, 0);
      fix_array(poop_array);
   // debug msg
   // display_msg("end of line");
   end
end

procedure hook_sneak begin
   call sneak_check;
   display_msg("hook_sneak running...");
end

procedure poop (variable temp_critter_obj, variable tile, variable source) begin
   move_to(temp_critter_obj, tile, elevation(source));
   if(obj_can_see_obj(source, temp_critter_obj)) then begin

      variable invisible_obj = tile_contains_pid_obj(tile, elevation(source), PID_SMALL_GOO_3);
      if (invisible_obj) then begin
         if not (obj_is_visible_flag(invisible_obj)) then begin
            set_obj_visibility(invisible_obj, visible);
         end
      end

      else begin
         variable temp_poo := create_object_sid(PID_SMALL_GOO_3, tile, elevation(source), -1);
         variable len_poop_array = len_array(poop_array);
         poop_array[len_poop_array-1] := temp_poo;
         resize_array(poop_array, len_poop_array + 1);
      end
   end
end

procedure sneak_check begin

   variable sneak_range  := 10;
   variable visual_range := 20;
   variable temp_critter_obj;
   variable dude_obj_tile      := dude_tile;
   variable dude_obj_elevation := dude_elevation;
   variable last_tile := tile_num_in_direction(dude_obj_tile, 0, sneak_range);
   variable critter;
   variable critter_array = [];
   critter_array := objects_in_radius(dude_obj_tile, visual_range, dude_elevation, OBJ_TYPE_CRITTER);

   foreach critter in critter_array begin
   if not (critter == dude_obj) then begin
   // Create a temp_critter_obj and pass it around the player
   // checking if other critters can see it
   // then dropping a poop where temp_critter_obj can be seen
      temp_critter_obj := create_object_sid(PID_SMALL_RADSCORPION, 0, dude_elevation, -1);

      //TODO: set_object_data() to match player pass/fail sneak check
      //      set_object_data(temp_obj, )?

         // display_msg("temp_obj " + temp_critter_obj);

         variable tile_array = create_array(6, 0);
         tile_array[0] := tile_num_in_direction(dude_obj_tile, 0, 1);
         tile_array[1] := tile_num_in_direction(dude_obj_tile, 1, 1);
         tile_array[2] := tile_num_in_direction(dude_obj_tile, 2, 1);
         tile_array[3] := tile_num_in_direction(dude_obj_tile, 3, 1);
         tile_array[4] := tile_num_in_direction(dude_obj_tile, 4, 1);
         tile_array[5] := tile_num_in_direction(dude_obj_tile, 5, 1);

         while not (tile_array[0] == last_tile) do begin
            call check_tiles(tile_array, temp_critter_obj, critter);
            call expand_tile_array(tile_array);
         end

      destroy_object(temp_critter_obj);
      last_tile := tile_num_in_direction(dude_tile, 0, sneak_range + 1);

         while not (tile_array[0] == last_tile) do begin
            call expand_tile_array(tile_array);
            call deal_with_poops_outside_sneak_range(tile_array);
         end
   end
      end
end

//move temp_critter_obj around the player and spawn poops
//where the other critter/s can see it
procedure check_tiles(variable tile_array, variable temp_critter_obj, variable critter) begin
   variable tile_var := tile_array[0];
   variable i;
   for (i:=0; i<6; i++) begin

      variable next_dir := (i+1) % 6;
      while not (tile_var == tile_array[next_dir]) do begin
         call poop(temp_critter_obj, tile_var, critter);
         tile_var := tile_num_in_direction(tile_var, rotation_to_tile(tile_var, tile_array[next_dir]), 1);
      end

   end
end

procedure expand_tile_array(variable tile_array) begin
   variable j;
   for (j := 0; j < 6; j++) begin
      tile_array[j] := tile_num_in_direction(tile_array[j], j, 1);
   end
end

procedure deal_with_poops_outside_sneak_range(variable tile_array) begin
   variable k;
   variable tile_var := tile_array[0];

   for (k:=0; k<6; k++) begin
      variable next_dir := (k+1) % 6;

      while not (tile_var == tile_array[next_dir]) do begin
         tile_var := tile_num_in_direction(tile_var, rotation_to_tile(tile_var, tile_array[next_dir]), 1);

         variable obj_to_set_invisible = tile_contains_pid_obj(tile_var, elevation(dude_obj), PID_SMALL_GOO_3);
         if(obj_to_set_invisible) then begin
      // Set poops to invisible (1 = invisible for some reason
      // so I created defines for readability)
            set_obj_visibility(obj_to_set_invisible, invisible);
         end
      end
   end
end
 
Last edited:
I'll test out set_critter_stat(SKILL_SNEAK) and see if that works (here's hoping), but I have this sneaking suspicion it might only affect the skill level, not whether it's sneaking or not.

Nope, that doesn't seem to do anything, about what I expected.
So the next step is to look at accessing the game functions directly if I can (and I think sfall has something for that), but I'm not entirely sure how it works. Started a conversation with alexbatalov already, but if anybody knows how to do this, I'd love to learn.
(I've already isolated the specific engine function I want to access
https://github.com/alexbatalov/fall...8269b6bb7c13/src/int/support/intextra.c#L2314 if anybody's interested)

For now, here's the latest update to my script:
Code:
/*******************************************************************************
       Name:           gl_super_sneak
       Location:       global script
       Description:    Displays the currently visible area for critters
                       when the player is in sneak mode
                       Passes an object around the critter within
                       a certain range to run obj_can_see_obj() on
                       and drops a poop at that location if it returns success

        Created: by QuantumApprentice
        Updated: 12.30.2022
*******************************************************************************/
#include "..\headers\command.h"
#include "..\headers\sfall.h"
#include "..\headers\define.h"
#include "..\headers\define_extra.h"

procedure start;
procedure poop(variable temp_obj, variable tile);
procedure sneak_check;
procedure hook_sneak;
procedure perception_hook;
procedure destroy_poops;
procedure check_tiles(variable tile_array, variable temp_critter_obj);
procedure expand_tile_array(variable tile_array);
procedure deal_with_poops_outside_sneak_range(variable tile_array);
procedure store_tiles(variable tile_array, variable hex_array);
procedure objects_in_radius_not_dude_obj;
procedure hex_square(variable hex_array, variable trash_array);
procedure manual_unwrap(variable hex_array, variable trash_array);

// defines
#define visible 0
#define invisible 1
#define move_w_hex_array

//global variables
variable critter_array
variable poop_array
variable dude_obj_tile
variable dude_obj_elevation
variable counter = 0
variable hex_counter = 0;


procedure start begin
   dude_obj_tile      := dude_tile;
   dude_obj_elevation := dude_elevation;

   poop_array := create_array(1, 0);
   fix_array(poop_array);
   // hex_array := create_array(270, 0);
   // fix_array(hex_array);

   if game_loaded then begin
     register_hook_proc(HOOK_SNEAK, hook_sneak);
     register_hook_proc(HOOK_WITHINPERCEPTION, perception_hook);
     display_msg("Super Sneak Script loaded!");
   end
end


//only sfall_arg_at(1) will ever be the dude_obj (no clue why this is)
procedure perception_hook begin
   variable target = get_sfall_arg_at(1);

   if (target == dude_obj) then begin
   float_msg(dude_obj, "tile: " + dude_obj_tile, FLOAT_COLOR_WHISPER);

     if(using_skill(dude_obj, SKILL_SNEAK)) then begin
        if (dude_obj_tile != dude_tile) then begin
           dude_obj_tile := dude_tile;
           call sneak_check;
        end

     end else begin
        call destroy_poops;
     end
   end
end

// destroy all poops and free the array
procedure destroy_poops begin
   if (len_array(poop_array) > 1) then begin
     variable stupid;
     foreach stupid in poop_array begin
        if (obj_pid(stupid) == PID_SMALL_GOO_3) then begin
           destroy_object(stupid);
        end
     end
     free_array(poop_array);
     poop_array := create_array(1, 0);
     fix_array(poop_array);
   end
end

procedure hook_sneak begin
   call sneak_check;
   display_msg("hook_sneak running...");
end

procedure poop(variable temp_critter_obj, variable tile) begin
//usage counter//////////////////////////////////////////////////////
   //  display_msg("how often? " + counter);
   //  counter++;
//counter end////////////////////////////////////////////////////////

   variable already_checked := false;
   variable invisible_obj;
   variable critter;

foreach critter in critter_array begin

   if not (already_checked) then begin

   if(obj_can_see_obj(critter, temp_critter_obj)) then begin
     already_checked := true;

     invisible_obj = tile_contains_pid_obj(tile, dude_obj_elevation, PID_SMALL_GOO_3);
     if (invisible_obj /*and not (obj_is_visible_flag(invisible_obj))*/) then begin
        if not(obj_is_visible_flag(invisible_obj)) then begin
           set_obj_visibility(invisible_obj, visible);
        end
     end
     else begin
     //TODO: use tile numbers in a shader?
     //      or possibly spawn something more lightweight than PID_SMALL_GOO_3?
     // Alternatives:
     // draw_image()
     // win_fill_color()
     // load_shader()
     // activate_shader()
        variable temp_poo := create_object_sid(PID_SMALL_GOO_3, tile, dude_obj_elevation, -1);
        variable len_poop_array = len_array(poop_array);
        poop_array[len_poop_array-1] := temp_poo;
        resize_array(poop_array, len_poop_array + 1);
     end
   end
   else begin
     invisible_obj = tile_contains_pid_obj(tile, dude_obj_elevation, PID_SMALL_GOO_3);
     if (obj_is_visible_flag(invisible_obj)) then set_obj_visibility(invisible_obj, invisible);
end
   end
   end
end

procedure sneak_check begin

   call objects_in_radius_not_dude_obj;

   variable sneak_range := 10;
   variable last_tile := tile_num_in_direction(dude_obj_tile, 0, sneak_range);

   // Store corner tile numbers for all 6 directions
   // These are used as waypoints to travel around the player
     variable tile_array = create_array(6, 0);
     tile_array[0] := tile_num_in_direction(dude_obj_tile, 0, 1);
     tile_array[1] := tile_num_in_direction(dude_obj_tile, 1, 1);
     tile_array[2] := tile_num_in_direction(dude_obj_tile, 2, 1);
     tile_array[3] := tile_num_in_direction(dude_obj_tile, 3, 1);
     tile_array[4] := tile_num_in_direction(dude_obj_tile, 4, 1);
     tile_array[5] := tile_num_in_direction(dude_obj_tile, 5, 1);

   variable temp_critter_obj;
   temp_critter_obj := create_object_sid( PID_RAT, 0, dude_obj_elevation, -1);
     ////TODO: set critter to sneaking?//////////////////////////////////
     // set_object_data(temp_critter_obj, SKILL_SNEAK, 1);
     // display_msg("" + has_skill(temp_critter_obj, SKILL_SNEAK));
     // display_msg("temp_obj " + temp_critter_obj);


#ifdef move_w_hex_array
   ////////////////////////////////////////////////////////
   //create an array containing all hexes around the player
   //that need to be checked with obj_can_see_obj()
   //TODO: make hex_array[]/trash_array[] the right size

   // variable hex_array := create_array(401, 0);
   variable hex_array:= [
                                            -1010, -810, -610, -410, -210, 10, 210,  410,  610,  810, 1010,
     /*-2009, -1809, -1609, -1409, -1209,*/ -1009, -809, -609, -409, -209,  9, 209,  409,  609,  809, 1009,  1209,  //1409,  1609,  1809,  2009,
     /*-2008, -1808, -1608, -1408,*/ -1208, -1008, -808, -608, -408, -208,  8, 208,  408,  608,  808, 1008,  1208,  //1408,  1608,  1808,  2008,
     /*-2007, -1807, -1607, -1407,*/ -1207, -1007, -807, -607, -407, -207,  7, 207,  407,  607,  807, 1007,  1207,  //1407,  1607,  1807,  2007,
     /*-2006, -1806, -1606,*/ -1406, -1206, -1006, -806, -606, -406, -206,  6, 206,  406,  606,  806, 1006,  1206,  1406,  //1606,  1806,  2006,
     /*-2005, -1805, -1605,*/ -1405, -1205, -1005, -805, -605, -405, -205,  5, 205,  405,  605,  805, 1005,  1205,  1405,  1605,  //1805,  2005,
     /*-2004, -1804,*/ -1604, -1404, -1204, -1004, -804, -604, -404, -204,  4, 204,  404,  604,  804, 1004,  1204,  1404,  1604,  //1804,  2004,
     /*-2003, -1803,*/ -1603, -1403, -1203, -1003, -803, -603, -403, -203,  3, 203,  403,  603,  803, 1003,  1203,  1403,  1603,  1803,  //2003,
     /*-2002,*/ -1802, -1602, -1402, -1202, -1002, -802, -602, -402, -202,  2, 202,  402,  602,  802, 1002,  1202,  1402,  1602,  1802,  //2002,
     /*-2001,*/ -1801, -1601, -1401, -1201, -1001, -801, -601, -401, -201,  1, 201,  401,  601,  801, 1001,  1201,  1401,  1601,  1801,  2001,
         -2000, -1800, -1600, -1400, -1200, -1000, -800, -600, -400, -200,-10, 200,  400,  600,  800, 1000,  1200,  1400,  1600,  1800,  2000,

     /*-1999,*/ -1799, -1599, -1399, -1199,  -999, -799, -599, -399, -199, -9, 199,  399,  599,  799,  999,  1199,  1399,  1599,  1799,  1999,
     /*-1998,*/ -1798, -1598, -1398, -1198,  -998, -798, -598, -398, -198, -8, 198,  398,  598,  798,  998,  1198,  1398,  1598,  1798,  //1998,
     /*-1997, -1797,*/ -1597, -1397, -1197,  -997, -797, -597, -397, -197, -7, 197,  397,  597,  797,  997,  1197,  1397,  1597,  1797,  //1997,
     /*-1996, -1796,*/ -1596, -1396, -1196,  -996, -796, -596, -396, -196, -6, 196,  396,  596,  796,  996,  1196,  1396,  1596,  //1796,  1996,
     /*-1995, -1795, -1595,*/ -1395, -1195,  -995, -795, -595, -395, -195, -5, 195,  395,  595,  795,  995,  1195,  1395,  1595,  //1795,  1995,
     /*-1994, -1794, -1594,*/ -1394, -1194,  -994, -794, -594, -394, -194, -4, 194,  394,  594,  794,  994,  1194,  1394,  //1594,  1794,  1994,
     /*-1993, -1793, -1593, -1393,*/ -1193,  -993, -793, -593, -393, -193, -3, 193,  393,  593,  793,  993,  1193,  1393,  //1593,  1793,  1993,
     /*-1992, -1792, -1592, -1392,*/ -1192,  -992, -792, -592, -392, -192, -2, 192,  392,  592,  792,  992,  1192,  //1392,  1592,  1792,  1992,
     /*-1991, -1791, -1591, -1391, -1191,*/  -991, -791, -591, -391, -191, -1, 191,  391,  591,  791,  991,  1191,  //1391,  1591,  1791,  1991,
                                             -990, -790, -590, -390, -190,     190,  390,  590,  790,  990

        ];


   // // variable trash_array := create_array(271, 0);
   // variable trash_array := [
   // -2200, 2200, -2190,  2190,       10,  -10,
   // -2201, 2201, -2191,  2191,      210, -210,  190,  -190,
   // -2202, 2202, -2192,  2192,      410, -410,  390,  -390,
   // -2203, 2203, -2193,  2193,      610, -610,  590,  -590,
   // -2204, 2204, -2194,  2194,      810, -810,  790,  -790,
   // -2205, 2205, -2195,  2195,     1010,-1010,  990,  -990,
   // -2206, 2206, -2196,  2196,     1210,-1210, 1190, -1190,
   // -2207, 2207, -2197,  2197,     1410,-1410, 1390, -1390,
   // -2208, 2208, -2198,  2198,     1610,-1610, 1590, -1590,
   // -2209, 2209, -2199,  2199,     1810,-1810, 1790, -1790,
   // -2210, 2210,                   2010,-2010, 1990, -1990
   // ];

   variable trash_array := [
                                                                                      11,
                                           -1211,   -1011,  -811, -611,    -411, -211,   211,  411, 611, 811, 1011, 1211,
                                           -1210,                                                                   1210,   1410,
           /*-2009, -1809, -1609,*/ -1409, -1209, /*-1009,   -809, -609,   -409, -209,   209,  409,      809, 1009, 1209,*/ 1409,/*1609,   1809,  2009,*/
           /*-2008, -1808, -1608,*/ -1408,/* -1208, -1008,                                               808, 1008, 1208,*/ 1408,  1608, //1808,  2008,
           /*-2007, -1807,*/-1607,  -1407,                                                                          /*1207, 1407,*/1607, /*1807,  2007,*/
          /*-2006, -1806,*/ -1606,                                                                                        /*1406,*/1606,  1806, //2006,
          /*-2005,*/-1805,  -1605,                                                                                               /*1605,*/1805, //2005,
          /*-2004,*/-1804,                                                                                                       /*1604,*/1804,   2004,
           -2003,   -1803,                                                                                                              /*1803,*/ 2003,
           -2002,                                                                                                                       /*1802,*/ 2002,   2202,
    -2201, -2001,                                                                                                                               /*2001,*/ 2201,
    -2200,                                                                                                                                      /*2000,*/ 2200,
    -2199, -1999,                                                                                                                               /*1999,*/ 2199,
           -1998,                                                                                                                                1998,    2198,
           -1997,  -1797,                                                                                                                        1997,
        /*-1996,*/ -1796,                                                                                                                 1796,  1996,
        /*-1995,*/ -1795,    -1595,                                                                                                       1795, //1995,
           /*-1994, -1794,*/ -1594,                                                                                                 1594, 1794, //1994,
           /*-1993, -1793,*/ -1593, -1393,                                                                                          1593,//1793, 1993,
           /*-1992, -1792, -1592,*/ -1392, /*-1192,*/                                                             /*1192,*/ 1392,   1592,/*1792, 1992,*/
           /*-1991, -1791, -1591,*/ -1391, -1191, /*-991,             -390, -190,   190,  390, */     /*791,  991, 1191,*/  1391, /*1591,  1791, 1991,*/
                                           -1190,                                                                   1190,   1390,
                                           -1189,   -989, -789, -589, -389, -189,   189,  389,   589,   789,  989,  1189, /*1389, 1589,*/
                                                                                 -11
   ];

   //stores hexes in a square pattern around dude_obj without the walk function
   // call hex_square(hex_array, trash_array);

   //Stores hexes in a hex pattern around dude_obj without walk function
   call manual_unwrap(hex_array, trash_array);


   hex_counter = 0;
     variable hex;
     foreach hex in hex_array begin
     ////uncomment to display "viewable area" around player
     // create_object(PID_SMALL_GOO_3, hex, dude_obj_elevation);
     move_to(temp_critter_obj, hex, dude_obj_elevation);
        call poop(temp_critter_obj, hex);
     end


   // Set poops to invisible
   // (1 = invisible for some reason so I created defines for readability)
   foreach hex in trash_array begin
     ////uncomment to display "invisible ring" around player
     //create_object(PID_BLOOD_1, hex, dude_obj_elevation);
     variable obj_to_set_invisible = tile_contains_pid_obj(hex, elevation(dude_obj), PID_SMALL_GOO_3);
     if(obj_to_set_invisible) then begin
        set_obj_visibility(obj_to_set_invisible, invisible);
     end
   end

#else
   ////Uncomment this to store hexes in array
   // while not (tile_array[0] == last_tile) do begin
   //    call store_tiles(tile_array, hex_array);
   // end

   ////////////////////////////////////////////////////////
   //instead of an array of hexes, just get the next hex number
   //using tile_num_in_direction() and move temp_critter_obj
     while not (tile_array[0] == last_tile) do begin
        call check_tiles(tile_array, temp_critter_obj);
        call expand_tile_array(tile_array);
     end
   // reset last_tile to new position for one more set of waypoints
   // deal with outer ring of vision
     last_tile := tile_num_in_direction(dude_tile, 0, sneak_range + 1);
     while not (tile_array[0] == last_tile) do begin
        call expand_tile_array(tile_array);
        call deal_with_poops_outside_sneak_range(tile_array);
     end
#endif
//cleanup
   destroy_object(temp_critter_obj);
   free_array(tile_array);
   free_array(critter_array);
   free_array(hex_array);
end

//store hex numbers around the player using tile_num_in_direction()
procedure store_tiles(variable tile_array, variable hex_array) begin

   variable tile_var := tile_array[0];
   variable i;
   for (i:=0; i<6; i++;) begin
   // Store all the tiles that need to be checked in an array
     variable next_dir := (i+1) % 6;
     while not (tile_var == tile_array[next_dir]) do begin
     tile_var := tile_num_in_direction(tile_var, rotation_to_tile(tile_var, tile_array[next_dir]), 1);
        hex_array[hex_counter] := tile_var;
        hex_counter++;
   display_msg("counter: " + hex_counter);
     end
   end
   call expand_tile_array(tile_array);
end

//move temp_critter_obj around the player and spawn poops
//where the other critter/s can see it
procedure check_tiles(variable tile_array, variable temp_critter_obj) begin
   //TODO: set_object_data() to match player pass/fail sneak check
   //      also have to set critter to use sneak mode

   variable tile_var := tile_array[0];
   variable i;

   for (i:=0; i<6; i++) begin
   // Move temp_critter_obj around the player
     variable next_dir := (i+1) % 6;
     while not (tile_var == tile_array[next_dir]) do begin

        ////////////////////////////////////////////////////////
        move_to(temp_critter_obj, tile_var, dude_obj_elevation);
        ////////////////////////////////////////////////////////

        call poop(temp_critter_obj, tile_var);
        tile_var := tile_num_in_direction(tile_var, rotation_to_tile(tile_var, tile_array[next_dir]), 1);
     end
   end
end

//copy all non-dude_obj critters to a new array for easy sorting
procedure objects_in_radius_not_dude_obj begin
   variable visual_range := 20;

   critter_array = create_array(1, 0);
   variable temp := objects_in_radius(dude_obj_tile, visual_range, dude_obj_elevation, OBJ_TYPE_CRITTER);
   variable i, j:=0;
   for (i := 0; i < len_array(temp); i++) begin
     if not (temp[i] == dude_obj) and not (is_critter_dead(temp[i])) then begin
        critter_array[j] := temp[i];
        j++;
        resize_array(critter_array, j+1);
     end
   end
   free_array(temp);
end

procedure expand_tile_array(variable tile_array) begin
   variable j;
   for (j := 0; j < 6; j++) begin
     tile_array[j] := tile_num_in_direction(tile_array[j], j, 1);
   end
end

procedure deal_with_poops_outside_sneak_range(variable tile_array) begin
   variable k;
   variable tile_var := tile_array[0];

   for (k:=0; k<6; k++) begin
     variable next_dir := (k+1) % 6;

     while not (tile_var == tile_array[next_dir]) do begin
        tile_var := tile_num_in_direction(tile_var, rotation_to_tile(tile_var, tile_array[next_dir]), 1);

        variable obj_to_set_invisible = tile_contains_pid_obj(tile_var, elevation(dude_obj), PID_SMALL_GOO_3);
        if(obj_to_set_invisible) then begin
     // Set poops to invisible
     // (1 = invisible for some reason so I created defines for readability)
           set_obj_visibility(obj_to_set_invisible, invisible);
        end
     end
   end
end

procedure hex_square(variable hex_array, variable trash_array) begin
   variable tile_var := tile_num_in_direction(dude_obj_tile, 0, 10);
   variable i, j;

   for (i := 0; i < 11; i++) begin
     hex_array[hex_counter] := tile_var + i;

     trash_array[i] := hex_array[i] - 200;
     trash_array[i+10] := hex_array[i] + 2000;
   // create_object_sid(PID_SMALL_GOO_3, hex_array[hex_counter], dude_obj_elevation, -1);
     hex_counter++;
   end

   variable trash_counter := 20;
   for(i := 0; i < 2001; i+=200) begin
     trash_array[trash_counter]      := hex_array[0] - 1 + i;
     trash_array[trash_counter + 10] := hex_array[9] + 1 + i;
     trash_counter++;
   end

   for (i := 0; i < 2001; i+=200) begin
     for (j := 0; j < 11; j++) begin
        hex_array[hex_counter] := hex_array[i] + j;
   // Uncomment to see viewable area
   // create_object_sid(PID_SMALL_GOO_3, hex_array[hex_counter], dude_obj_elevation, -1);
     hex_counter++;
     end
   end
end

procedure manual_unwrap(variable hex_array, variable trash_array) begin
   variable i;
   for (i := 0; i<401; i++) begin
     hex_array[i] += dude_obj_tile;
   end
   for (i := 0; i<401; i++) begin
     trash_array[i] += dude_obj_tile;
   end
end

This one runs slightly smoother than the last, still a little chunky though.
And for now the game crashes on map exit if the player is in sneak mode.
I'm assuming this is because the arrays keeping track of the poops placed on the ground aren't getting destroyed when dude_obj hits an exit grid, but I'm not sure how to verify this at all.
Even running the destroy_poops function in the map_exit_p_proc for the world map still causes a crash :(
 
Last edited:
Back
Top