Fallout 2 utility Party Member Script Template

Discussion in 'Fallout General Modding' started by QuantumApprentice, Apr 8, 2021.

  1. QuantumApprentice

    QuantumApprentice Still Mildly Glowing

    276
    Feb 9, 2018
    EDIT2: A few more fixes I found as I was trying to implement other things, and a couple of new optional additions.
    EDIT: I've made changes to the script to fix the bugs Lexx pointed out. Keep em coming :)

    I've been spending the last few weeks working on a generic script template for new party members in the hopes it assists modders old and new with creating individual party members.

    I'd like some feedback if anybody is interested.
    I have basic instructions throughout the script, including how to add this character to party.h and party.txt.

    To use just open this script in script editor, press CTRL + F to open the find function, and replace 'Character_Name" with your new character's name, then add your character to party.h and party.txt.
    There are instructions for each step, but I'm not sure if they're clear or concise enough, so any feedback here is welcome.
    In addition, if there are any generic party things that I have missed or that should be added, please let me know :)
    https://github.com/QuantumApprentice/Fallout-2-Party-Member-Template
    Code:
    /*******************************************************************************
            Name:        Fallout 2 Party Member Template
            Location:
            Description: NPC template script
    
                Created: by sFall Script Editor
                Updated:
    
    *******************************************************************************/
    
    /* Include Files */
    #include "..\headers\define.h"
    #include "..\headers\command.h"
    #include "..\headers\ModReact.h"
    
    // Character_Name
    /* Highlight "Character_Name" above and press CTRL+F to activate the Find function.
       Type your new character's name in the replace box, check "Find all matches",
       check "Find in current scripts", fill your character name in the Replace Text box,
       then select "Find & Replace". */
    
    
    /* Don't forget to change the NAME and TOWN_REP_VAR variables to match your new critter */
    #define NAME                    SCRIPT_Character_Name
    #define TOWN_REP_VAR            GVAR_TOWN_REP_ARROYO              // Need to specify global.h variable for town rep modifiers if used (replace "GVAR_TOWN_REP_" with actual global variable name)
    
    
    
    /* Standard Script Procedures */
    procedure start;
    procedure critter_p_proc;
    procedure pickup_p_proc;
    procedure talk_p_proc;
    procedure destroy_p_proc;
    procedure look_at_p_proc;
    procedure description_p_proc;
    procedure use_skill_on_p_proc;
    procedure damage_p_proc;
    procedure map_enter_p_proc;
    procedure push_p_proc;
    
    /* Script Specific Procedure Calls */
    procedure Node998;                                      // This Node is Always Combat
    procedure Node999;                                      // This Node is Always Ending
    
    /*****************************************************************
    Party Member pre-requisites
    These lines are necessary for the critter to join the player
    as a party member.
    *****************************************************************/
    procedure Node000;
    procedure Node001;
    procedure Node002;
    procedure Join_Party;                                    // Party Join node
    procedure Abandon_Party;                                 // Party Abandon node
    procedure Node1000;                                      // Main party member node
    procedure Node1001;                                      // heal yourself
    procedure Node1002;                                      // wait here
    procedure Node1003;                                      // put your weapon away
    procedure Node1004;                                      // follow close
    procedure Node1005;                                      // follow medium
    procedure Node1006;                                      // follow far
    procedure Node1007;                                      // change distance menu
    procedure Node1008;                                      // look at gear menu
    procedure Node1009;                                      // remove armor menu
    procedure Node1010;                                      // weapon abilities
    procedure Node1100;                                      // rejoin party
    
    
    /* Change "Character_Name" to match the name of your new critter */
    #define Character_Name_joins_party             party_add_self;                                                   \
                                                   critter_add_trait(self_obj,TRAIT_OBJECT,OBJECT_TEAM_NUM,TEAM_PLAYER)
    #define dude_has_free_slot_for_Character_Name  ((dude_at_max_party_size == false) and (Character_Name_In_Party == false))     // Need to declare Character_Name_In_Party in PARTY.H
          /*
          Right click "dude_at_max_party_size" and select "Go to declaration" to open PARTY.H
          Scroll to the top and add lines with this format:
             #define Character_Name_Ptr                             party_member_obj(PID_Character_Name)
          and:
             #define Character_Name_In_Party                        (Character_Name_Ptr != 0)
          You don't have to organize them in the same sections as their counterparts, but it helps :)
          Make sure PID_Character_Name is declared in CRITRPID.H.
          If you used Proto Manager to create your critter then it should have
          automatically added it to this list.
    
          In order to get Party AI options for combat, open Party.TXT in data\data\ folder
          and add your new party member to the end of the list.
          Simply copy and paste the options for the previous party member, and adjust to your liking.
          Make sure the party_member_PID is pointing to the correct number and is declared in CRITRPID.H
          */
    
    /*****************************************************************
       Local Variables which are saved.
       All Local Variables need to be prepended by LVAR_
    
          To have LVars work, open scripts.LST, scroll to your character's script name,
          and change the "LVars" setting to at least 8,
          Since there are 8 Local Variables used below.
    
          (LVAR_ 0 thru 3 are reserved for NPC Reaction Modifiers...
          ...will add more when I learn how they work.)
    
          If you would rather use the LVAR_Hostile, LVAR_Herebefore, LVAR_Personal_Enemy
          variables instead of LVAR_Flags, uncomment the relevent lines and comment out
          lines with set_herebefore, set_hostile, if (hostile) and (self_can_see_dude).
    *****************************************************************/
    
    #define LVAR_Flags                      (4)
    #define LVAR_WAITING                    (5)
    #define LVAR_FOLLOW_DISTANCE            (6)
    #define LVAR_TEAM                       (7)
    #define LVAR_Joined_Player              (8)
    
    //#define LVAR_Hostile                    (9)
    //#define LVAR_Herebefore                 (10)
    //#define LVAR_Personal_Enemy             (11)
    
    #define PARTY_NODE_X                      Node1000
    
    /*******************************************************************
       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.
    *******************************************************************/
    variable begin
    end
    
    /*******************************************************************
    *                           PROCEDURES                             *
    *******************************************************************/
    procedure start begin
    
    end
    
    procedure map_enter_p_proc begin
       /*  This procedure will get called each time that the map is first entered. It will
           set up the Team number and AI packet for this critter. This will override the
           default from the prototype, and needs to be set in scripts. */
    
       party_member_map_enter;
       if (Character_Name_In_Party) then begin
          //do nothing
       end
    
    //   critter_add_trait(self_obj, TRAIT_OBJECT, OBJECT_TEAM_NUM,TEAM_);
    //   critter_add_trait(self_obj, TRAIT_OBJECT, OBJECT_AI_PACKET,AI_);
    
    end
    
    procedure critter_p_proc begin
       /* Every heartbeat that the critter gets, this procedure will be called. Anything from
          Movement to attacking the player on sight can be placed in here.
          If the critter is mad at the player for any reason, it will attack and remember to attack
          the player should the game be saved and loaded repeatedly. Additionally, if any special
          actions need to be taken by the critter based on previous combat, the critter will remember
          this as well. */
    
    //   if ((local_var(LVAR_Hostile) > 1) and (obj_can_see_obj(self_obj, dude_obj))) then begin
    //      set_local_var(LVAR_Hostile,1);
       if (hostile) and (self_can_see_dude) then begin
          self_attack_dude; //Macro made by Tom to keep the critter fleeing.
       end
    
       /* This next script moves the new party member to follow the player after they've joined and
          when entering new maps. */
       if (Character_Name_In_Party) then begin
          if (party_is_waiting == false) then begin
             party_member_follow_dude
          end
       end
    
       /*
       -----   These are examples of common Abandon_Party calls, use...or don't...at your discretion   -----
    
       else if (Character_Name_In_Party) then begin
          if (global_var(GVAR_PLAYER_REPUTATION) < Character_Name_Bad_Karma) then begin
             call Abandon_Party;
             debug_msg("Character_Name Bad Player Rep");
          end
          else if (has_rep_childkiller) then begin
             call Abandon_Party;
             debug_msg("Character_Name: Child Killer");
          end
          else if (has_rep_berserker) then begin
             call Abandon_Party;
             debug_msg("Character_Name: Berserker");
          end
          else if (has_rep_slaver) then begin
             call Abandon_Party;
             debug_msg("Character_Name: Slaver Rep");
          end*/
    
    end
    
    procedure damage_p_proc begin
       /* Whenever the critter takes damage of any type, this procedure will be called. Things
          like setting ENEMY_ and LVAR_Personal_Enemy can be set here.
          If the player causes damage to this critter, then he will instantly consider the player
          his personal enemy. In Critter_Proc or through dialog, actions will be taken against
          the player for his evil acts. */
    
       if (obj_in_party(source_obj)) then begin
    //       set_local_var(LVAR_Personal_Enemy, 1);
          set_personal_enemy;
       end
    
    end
    
    procedure pickup_p_proc begin
       /* Any time that the player is caught stealing from this critter, Pickup_proc will be called.
          In here, various things can happen. The most common response is instant hostility which
          will be remembered. */
    
       if (source_obj == dude_obj) then begin
    //      set_local_var(LVAR_Hostile, 2);
          set_hostile;
       end
    
    end
    
    procedure talk_p_proc begin
       /* These next two scripts are for recruiting a new character to your party,
          Node1000 in particular is necessary to connect to the generic party member inventory
          and combat settings.
          Open Party.h and create a new pointer for your new party member critter at the top of the #defines.
          Replace "Character_Name_Ptr" with the pointer for your new character created in Party.h */
    
       /* The dialog system is setup and prepares the player to talk to this NPC. Where To Go
          written by designers are placed in here. Additionally, Reactions are generated and
          stored which affects player interactions. */
       Evil_Critter:=0;
       Slavery_Tolerant:=SLAVE_TOLERANT;
       Karma_Perception:=KARMA_PERCEPTION1;
    
       CheckKarma;
    
       GetReaction;
    
    start_dialogue(self_obj, neutral_fidget);
    gsay_start;
       if ((Character_Name_Ptr != 0) or (party_is_waiting)) then begin      //Character_Name_Ptr needs to be declared in Party.h with your own "Character_Name"
          call Node1000;
          end
       else begin
          call Node001;
       end
    gsay_end;
    end_dialogue;
    
    end
    
    procedure destroy_p_proc begin
       /* This procedure gets called only on the death of this NPC. Special things like
           incrementing the death count for reputation purposes and Enemy Counters are placed
           in here. */
    
       /* Increment the aligned critter counter
            This counter affects your karma? */
       //inc_good_critter
       //inc_evil_critter
       inc_neutral_critter
    
       /* Set global_variable for Enemy status*/
    
    end
    
    procedure look_at_p_proc begin
       /* Look_at_p_proc gets called any time that the player passes the cursor over any object.
           This should only hold the most cursory of glances for the player. */
    
       script_overrides;
    //   if (local_var(LVAR_Herebefore) == 0) then
    //      set_local_var(LVAR_Herebefore, 1);
       if not herebefore then begin
          set_herebefore;
          display_mstr(100);
       end else
          display_mstr(101);
    
    end
    
    procedure description_p_proc begin
       /* The player will see more indepth descriptions from this procedure. They are actively
       looking at the critter and want more information. Things like names can be added here
       if the critter is known to the player. */
    
       script_overrides;
       display_mstr(102);
    
    end
    
    procedure use_skill_on_p_proc begin
       /* Any time a skill is used on a critter this call is made. This can be to give examinations
           for things like Doctor skill or messages for various other skills. */
    end
    
    procedure push_p_proc begin
    
       floater_rand(780, 790);
    
    end
    
    procedure Node001 begin
    
       Reply(random(750,751));                               // Random dialogue greetings
       if (Character_Name_In_Party == false) then begin
          NLowOption(760, Node002);                          // This NLowOption message(760) is a Low Int request to join the player's party
          NOption(765, Node002, 4);                          // This NOption message(765) is Normal Int request to join the player's party
       end else begin
          call Node1000;
       end
    
    end
    
    procedure Node002 begin
       /* This node checks for stuff before allowing NPC to join party.
           You can add other conditions for joining here. */
       if (dude_charisma <= 1) then begin
         NMessage(800);                                      // This NOption message(800) is a response if player has 1 Charisma or less (can't have party members because too ugly)
    end else begin
       if (dude_at_max_party_size) then begin
       Reply(810);                                           // This line is for when the character does NOT join the player's party       (because too many followers)
          NOption(820,Node999,4);                            // This NOption message is Normal Int player response to NOT joining party    (because too many followers)
          NLowOption(825,Node999);                           // This NLowOption message is Low Int player response to NOT joining party    (because too many followers)
    
       end else if town_rep_is_hated then begin
       Reply(830);                                           // This line is for if the player is hated by the NPC's home town (hates player because TOWN_REPUTATION<=-15)
          NOption(835, Node998, 4);                          // This line is Normal Int response to being hated by the NPC's home town  (default points to combat Node998)
          NLowOption(840, Node998);                          // This line is Low Int response to being hated by town                    (default points to Combat Node998)
    
       end else begin
       Reply(850);                                           // This line is for when the character agrees to Join the player's party
          NOption(855, Join_Party, 4);                       // This NOption message(855) is Normal Int player response to joining party
          NLowOption(860, Join_Party);                       // This NLowOption message(860) is Low Int player response to joining party
       end
    end
    
    end
    
    
    procedure Node998 begin
       /* Should the Player ever cause the NPC too much discomfort that he desires to attack the player,
           this call will be made. Essentially, it stores the Hostile vaule so that the critter remembers
           he was once hostile towards the player.*/
    //   set_local_var(LVAR_Hostile,2);
       set_hostile;
    
    end
    
    
    procedure Node999 begin
       /* Anytime that there is a need for an ending to dialog, this node is to be called. It will just
           exit from the dialog system without any reprisals from the NPC. */
    end
    
    
    procedure Join_Party begin
       /* This node places the NPC in the player's party.
          Call this node any time you want an action from the player to result in the NPC joining their party. */
    
       Character_Name_joins_party;
       set_local_var(LVAR_Joined_Player,1);
       float_msg(self_obj, message_str(NAME, 870), FLOAT_COLOR_HIGH);      // Personalize this message or remove if you feel like it
    
    end
    
    procedure Abandon_Party begin
    
       //set_local_var(LVAR_never_again,1);
       //party_remove(self_obj);
       //leave party
       if (self_visible) then
       set_self_abandon_party;
    
    end
    
    
       /* Node1000 and on are default dialogue nodes for a generic NPC joining the player's party. */
    
    procedure Node1000 begin
       Reply(880);                                        // This reply is when the NPC is in your party and you talk to them
       party_member_default_options;
    end
    procedure Node1001 begin
       // heal yourself
       obj_heal(self_obj)
       if (party_healed_max) then begin
          Reply(random(900,905));                         // This reply is when your party member is healed to max
       end else if (party_healed_good) then begin
         Reply(910);                                      // This reply message is for when the NPC is Barely Hurt (90% health or above)
       end else if (party_healed_hurt) then begin
          Reply(915);                                     // This reply message is for when the NPC is Wounded (70%-90% health?)
       end else begin
          Reply(920);                                     // This reply message is for Failures
       end
       party_member_default_options;
    end
    procedure Node1002 begin
       // wait here
       set_party_waiting;
       Reply(random(930,931));                            // These reply messages are in response to wait request
       party_member_default_options;
    end
    procedure Node1003 begin
       // put your weapon away
       inven_unwield(self_obj);
       Reply(940);                                        // Response to removing weapon
       party_member_default_options;
    end
    procedure Node1004 begin
       // follow close
       set_follow_close;
       Reply(random(950,951));                            // Random responses for distance change
       party_member_default_options;
    end
    procedure Node1005 begin
       // follow medium
       set_follow_medium;
       Reply(random(960,961));                            // Random responses for distance change
       party_member_default_options;
    end
    procedure Node1006 begin
       // follow far
       set_follow_far;
       Reply(random(970,975));                            // Random responses for distance change
       party_member_default_options;
    end
    procedure Node1007 begin
       // distance
       //Reply("TEMP MESSAGE: DISTANCE");
       Reply(980);                                        // Generic response to "Change follow distance" menu
       party_member_def_follow_options;
    end
    procedure Node1008 begin
       // gear
       //Reply("TEMP MESSAGE: GEAR");
       Reply(985);                                        // Generic response to "Look at your gear"
       party_member_def_gear_options;
    end
    procedure Node1009 begin
       // remove armor
       //NEED TO FIND OUT WHY THIS DOESNT WORK!!! - NBK
       remove_armor(self_obj)
       //Reply("TEMP MESSAGE: REMOVE ARMOR");
       Reply(990);                                        // Generic response to "Remove armor"
       party_member_default_options;
    end
    procedure Node1010 begin
       // weapons that can be used
       //Reply("TEMP MESSAGE: CAN USE...");
       Reply(995);                                        // Specific reply, indicating what weapons the NPC can use.
       party_member_default_options;
    end
    procedure Node1100 begin                              // This node is for when the player is asking the NPC to re-join
       // join me
       if (dude_charisma <= 1) then begin
          set_self_abandon_party;
          Reply(800);                                     // NPC abandons party because Player Character is too ugly
          NOption(g_mstr(10007),Node999,001);             // Predefined generic messages in GENERIC.MSG
       end else if (dude_at_max_party_size) then begin
          set_self_abandon_party;
          Reply(810); //was 2000                          // Generic "Too many followers" reply
          NOption(g_mstr(10007),Node999,001);             // Predefined generic messages in GENERIC.MSG
       end else if (town_rep_is_hated) then begin
          set_self_abandon_party;
          Reply(830);                                     // Will abandon party if Town_Rep becomes Hated
          NOption(g_mstr(10007),Node999,001);             // Predefined generic messages in GENERIC.MSG
       end else begin
          end_party_waiting;
          Reply(random(750,751));                         // Random dialogue greetings
          party_member_default_options;
       end
    end
    
     
    Last edited: May 6, 2021
    • [Rad] [Rad] x 1
  2. Lexx

    Lexx Testament to the ghoul lifespan
    Moderator Modder

    Apr 24, 2005
    Your template is using the bitwise set_hostile macro, which utilizes LVAR_Flags, and at the same time the LVAR_Hostile local variable. This is duplicated - you can (or probably should) only use either of those for clarity. Originally the LVAR_Flags stuff was used to cut down on the amount of local variables in a script, but it seems to not have been used by every Fo2 scripter, or they probably just forgot that this existed in the first place.

    For example, right now there will be an issue with your script. If you go into combat with the critter via dialog (Node998), the script will set the hostile bit in LVAR_Flags. However, the critter_p_proc only checks for LVAR_Hostile ... the LVAR_Flags bit is never checked, thus the critter will not go into combat mode.

    Oh, and in damage_p_proc is a similar issue ... you are setting LVAR_Personal_Enemy, which is not checked again in critter_p_proc either, so here the critter will also not remember you as an enemy.

    The only time the critter will go into combat mode is if you try to steal from him (pickup_p_proc), because here the LVAR_Hostile will be set to 2.
     
    Last edited: Apr 8, 2021
    • [Rad] [Rad] x 1
  3. QuantumApprentice

    QuantumApprentice Still Mildly Glowing

    276
    Feb 9, 2018
    Weird...I must have copied that from Vic's code. Thanks for catching it! Which method would you recommend: LVAR_Hostile or LVAR_Flags?
    I looked through all the party members I could find and Sulik, Dogmeat, Marcus, Lenny, Goris, Casidy, K9, Cyberdog, and Brainbot all use LVAR_Hostile, while Vic is the only one using "set_hostile" which uses LVAR_Flags.
    lol indeed it looks like it got ignored by a lot of scripters.
     
  4. Lexx

    Lexx Testament to the ghoul lifespan
    Moderator Modder

    Apr 24, 2005
    It really depends on what you want to do. Now in 2021, the amount of LVARs doesn't really seem to matter anymore ... even Fixt just went all crowbar on it and increased the LVARs of every single script to minimum 15. Personally I find the way of LVAR_Flags much more elegant, though, especially because all the macros for it are already written and it is used for more than just "check if hostile". But yeah... nobody cares, especially not the generic hobby Fallout modder.

    That reminds me of one more issue in your template: LVAR 4 is defined twice. The bit-variable is overwritten by the normal one. This screams for problems down the line.

    Also the reason why almost all LVAR defines start at 4 is because 0 - 3 are reserved for the modreact.h macros. So similar to LVAR_Flags, the scripter needs to be aware of that if NPC reaction code is supposed to work.
     
    Last edited: Apr 8, 2021
    • [Rad] [Rad] x 1
  5. QuantumApprentice

    QuantumApprentice Still Mildly Glowing

    276
    Feb 9, 2018
    Awesome thanks! I'll start updating the script.

    I have no idea how this works. Can you point me to anything describing modreact.h macros? Or why defining macros as numbers does anything at all?
     
  6. Lexx

    Lexx Testament to the ghoul lifespan
    Moderator Modder

    Apr 24, 2005
    Hard to explain. I think the modreact stuff is mostly used for talking heads (bad mood, neutral, good mood, etc which is modified by the "UpReact", "DownReact" etc. macros in a dialog node). Of course it also works for every other dialog, but afaik it is only really used in Fo1. In Fo2 the dialogues were written in a different style and the whole reaction thing is broken because of that.

    Fo1 has much more "random" elements in interactions, while Fo2 is often using fixed values (don't remember if it's all the time or not right now).

    Example: In Fo1 you pick a dialog option that will piss off an NPC (DownReact). If you trigger 2 DownReact in the dialog, the critter stops liking you. So the next time when the script checks if the NPC likes you ... it will take that into consideration.

    But what most modern games will do: If you pick a dialog option that pisses off an NPC, you will set a variable "I am pissed off" ... and the next time the script checks if the NPC likes you, it will just check if the "I am pissed off" variable is set ..... same result, but no random involved.

    /Edit: Check out Aradesh in Fo1. If you are a dick to him, it will trigger a DownReact. If you are nice to him or bring back Tandi, it will trigger an UpReact. This has influence on his face.
    I don't have any Fo2 examples for this stuff right now. I tried to find some right now, but it's used almost nowhere.

    /Edit2: Oh, here is another one... NOption, BOption, GOption, etc. dialog options in the scripts are also just macros and they also affect the modreact system, which in return has influence on character faces.

    Generally I don't know how much of this is hardcoded or not ... thus, I prefer not to touch it and just take it the way it comes.

    tl;dr -- it's hard to explain.



    About "macros as numbers" - this is not what this is. Every LVAR in the script is just a number... you are giving it a name for readability. Technically you don't have to write "set_local_var(LVAR_Hostile, 1)" ... might as well just write "set_local_var(4, 1)" ... it's exactly the same, but now you won't remember what the fuck is going on if you go back to the script in a few months.
     
    Last edited: Apr 8, 2021
    • [Rad] [Rad] x 1
  7. QuantumApprentice

    QuantumApprentice Still Mildly Glowing

    276
    Feb 9, 2018
    Ok the modreact stuff I kind of get, and you gave me a direction to look so I can experiment with it.
    but this doesn't make much sense. Your explanation is legible, but making the name interchangeable with a number on purpose? Why?

    Is every local variable assigned a number by default without user input?

    If so, are there more numbers I should avoid because they are pre-assigned to other things?

    Why not simply create named variables under the "variable begin" section and let the compiler assign them?

    Do the numbers themselves point to other macro's that are built into the engine?

    Or is this just some sort of formatting that some scripters used and others didn't?
     
  8. Lexx

    Lexx Testament to the ghoul lifespan
    Moderator Modder

    Apr 24, 2005
    I don't understand what you mean.

    You define the amount of LVARs in the scripts.lst file. From that moment on, the script has access to x amount of LVARs.
    /Edit: All the variables, GVAR, LVAR, MVAR are just numbers. The compiler doesn't care about the names. What you define them as is just for us to read them better, not much else.

    Scripts usually only include modreact.h which already defines LVARs for the 0 - 3 range. Party.h uses defined LVARs, but those are usually defined in the actual party member script itself and not in the .h file, so they can be whatever number (LVAR_WAITING, etc).

    This part is talking about *temporary variables* which are not the same as LVARs. Temporary variables are cleared on map change / game load while LVARs are always saved.

    Usually no. About the macros you will have to check the header files. As far as I am aware right now, there shouldn't be anything else using that, except maybe some exceptions in location related macros.

    Macros from header files only work if you actually use them in your script, so I wouldn't worry about that.
     
    Last edited: Apr 8, 2021
    • [Rad] [Rad] x 1
  9. QuantumApprentice

    QuantumApprentice Still Mildly Glowing

    276
    Feb 9, 2018
    I meant why would you do this operation at all? Seems kind of pointless unless:

    So you're saying that defining a variable as a number between 0 and the upper limit set in scripts.lst is how you actually declare an LVAR?
    And then the engine takes care of saving the variable itself?
     
  10. Lexx

    Lexx Testament to the ghoul lifespan
    Moderator Modder

    Apr 24, 2005
    Sorry I have no idea how to explain this in other words. :D

    If scripts.lst says a script has 15 LVARs, then it has 15 LVARs. 0, 1, 2, 3, ... are all there.

    What you do is just giving them names, so you can use them easier.
     
    • [Rad] [Rad] x 1
  11. QuantumApprentice

    QuantumApprentice Still Mildly Glowing

    276
    Feb 9, 2018
    I think I understand.
    You declare the number of LVARs you want in scripts.lst.
    The LVARs are by default named 1 (or 0?) through the number declared. ie 1 thru 15 (or 0 thru 14?) if 15 is what's declared.
    The numbers can be used? but it's easier to read if proper names are defined for them.
    And variables declared and defined in this way are stored by the engine in savegames and between maps? whereas simple script variables are not stored at all outside of running.
    Declaring two different names for an LVAR is probably ok, but it probably won't work if they are supposed to store different things.

    So if I give a script 15 LVARs in scripts.lst, but I don't use all 15, or any, will that have an effect or an impact on the script running?

    Oh and...huh...did not know they didn't use "Node000" procedures in the original fallout scripts.
     
  12. QuantumApprentice

    QuantumApprentice Still Mildly Glowing

    276
    Feb 9, 2018
    In Aradesh's script the LVARs are:
    Code:
    #define LVAR_Herebefore              (4)
    #define LVAR_Invasion                (6)
    #define LVAR_Caught_Stealing         (9)
    
    Why did they use 4,6, and 9 instead of 0,1, and 2 if these are the only LVARs in the script?

    /Edit: Nevermind I see a lot of LVARs being called by their numbers.
     
    Last edited: Apr 9, 2021
  13. Lexx

    Lexx Testament to the ghoul lifespan
    Moderator Modder

    Apr 24, 2005
    Procedure names can be whatever. The reason why Fo2 scripts use Node001, 002, etc. is because that's what their dialog tool did.


    0 to 3 are used by the modreact system. The LVARs are defined in the modreact.h file.
    Code:
    #define LVAR_reaction                   (0)             // Holds reaction number value.
    #define LVAR_reaction_level             (1)             // Holds reaction level: BAD, NEUTRAL, GOOD.
    #define LVAR_got_reaction               (2)             // Makes sure to modify reaction only once.
    #define LVAR_base_reaction              (3)
    Aradesh.ssl is using 10 LVARs in total, but I have only defined 3 which I identified myself. We don't have Fo1 script sources - they were decompiled and thus don't show variable names. This is what I mean with readability .. the numbers are shit, you don't know what they mean and have to guess.
     
    • [Rad] [Rad] x 2
  14. QuantumApprentice

    QuantumApprentice Still Mildly Glowing

    276
    Feb 9, 2018
    What dialogue tool did they use? Is it similar to the one built into sfall scripter?
    My bad I didn't know that.

    Ok so I think the only thing I'm not clear on is the modreact thing. I did a ctrl+f search and couldn't find anything in Aradesh's script that calls local_var(0) or 1,2, or 3 directly. Are they called in the DownReact/UpReact macros (and thus are hidden)?
     
  15. Lexx

    Lexx Testament to the ghoul lifespan
    Moderator Modder

    Apr 24, 2005
    We don't know. I think they wrote dialog in MS Word and exported it via macros and shit. At least that's the format the Planescape: Torment dialog examples had. Or was it in Van Buren ... I have no idea, it's too many years since.

    Yes, they are used in the DownReact / UpReact macros. You will have to read modreact.h for that ..

    Code:
    #define UpReact      set_local_var(LVAR_reaction, local_var(LVAR_reaction) + NORMAL_BOOST); \
                         ReactToLevel
    
    #define DownReact    set_local_var(LVAR_reaction, local_var(LVAR_reaction) - NORMAL_BOOST); \
                         ReactToLevel
    
     
    • [Rad] [Rad] x 2
  16. QuantumApprentice

    QuantumApprentice Still Mildly Glowing

    276
    Feb 9, 2018
    Dude you have been awesome! Thanks for taking the time to explain everything to me!
    I'll have start working on a reaction section now that I have a clue where to start :P
     
  17. QuantumApprentice

    QuantumApprentice Still Mildly Glowing

    276
    Feb 9, 2018
    Ok, I've edited the original post to reflect what I hope are better LVar defines. Let me know if that works :)
     
    • [Rad] [Rad] x 1
  18. QuantumApprentice

    QuantumApprentice Still Mildly Glowing

    276
    Feb 9, 2018
    I think I've seen an interview where either Tim Cain or Leonard Boyarsky said they used excel spreadsheets and macros for Arcanum, so that sounds about right for fallout :P
     
  19. QuantumApprentice

    QuantumApprentice Still Mildly Glowing

    276
    Feb 9, 2018
    One more question that just occurred to me today as I was trying to learn how to script,
    If I use a number in a variable or in a command
    ie
    Code:
    tile_num_in_direction(tile_num(self_obj), (cardoor - 2), 2)
    and I have LVars declared, is there something to worry about one of the LVars being accidentally used instead of the integer?
     
  20. Lexx

    Lexx Testament to the ghoul lifespan
    Moderator Modder

    Apr 24, 2005
    No, because to access a variable you use
    Code:
    local_var(x)
    map_var(x)
    global_var(x)
     
    • [Rad] [Rad] x 1