Left 4 Dead 2

Left 4 Dead 2

Not enough ratings
How to Create Simple Think Functions (Beginner Guide)
By kurochama
Some simple guides for beginners about how to make "Think" functions in easy & simple ways
Think function can be useful for various things. For example, the simplest one would be giving health regen on survivors or special infected. The more complicated one would be creating custom enemies/ AIs that attack survivors. But we'll talk about the simple ones, as this is for beginners with the purpose to stimulate their creativity & imagination first.
   
Award
Favorite
Favorited
Unfavorite
Step 1: What To Do First?
If you're already familiar about making script mods using "director_base_addon.nut", "mapspawn_addon.nut", "scriptedmode_addon.nut" etc, you can create either one of them first as usual. Depending on which one you use, the think functions can have other customizations. Here are the examples of content inside "director_base_addon.nut" & "mapspawn_addon.nut":

    
        
director_base_addon.nut
        
mapspawn_addon.nut
    
    
        
IncludeScript("your_vscript_name");
        
SpawnEntityFromTable("env_soundscape_triggerable", { vscripts = "your_vscript_name" });
    


After making either one of them, make a vscript file, "your_vscript_name.nut". Now you're ready for the "Step 2"
Step 2: Create Your Own "Think" Functions
Now, open your "your_vscript_name.nut" via "Notepad++" or similar program. For an early note, "AddThinkToEnt" can only generate one think function on one entity (based on Valve page). Let's start from "director_base_addon.nut"
"director_base_addon.nut"
YourTableName <- { function OnGameEvent_player_first_spawn(event) { local player = GetPlayerFromUserID(event.userid); if(player.IsSurvivor()) { if(player.ValidateScriptScope() ) { local player_entityscript = player.GetScriptScope(); player_entityscript["SurvThinker"] <- function() { if(self.GetHealth() < 100) { self.SetHealth(self.GetHealth() + 1); } return 0.5; } } AddThinkToEnt(player, "SurvThinker"); printl("Added 'Survivor Thinker' to "+ player); } } function OnGameEvent_round_start(event) { printl("The 'YOUR MOD NAME' is started..."); } } __CollectGameEventCallbacks(YourTableName);
Now let me explain some of the scripts above:
  • "OnGameEvent_player_first_spawn" is an event function triggered only on player's first spawn. It's triggered only on the 1st chapter of a map when starting a map. This is just an example, so, you can use other event functions based on what you need.

  • The "Think Function" starts from "if(player.ValidateScriptScope() )". Just follow the format there until you reach "SurvThinker"

  • That "SurvThinker" is the name of the think function. It can be named anything. What you need to focus is the "self" there

  • "self" there represents the "Think Owner". You can put any scripts there. The example on the scripts above gives health regen to the "Think Owner".

  • "return 0.5;" is about how fast the loop of the "Think Function". "0.5" there means that the loop is repeated every 0.5 second. It means that in 1 second, the "Think Owner" will get +2 health regen in total. The fastest loop of this think function is 0.033 (based on Valve's page).

  • "AddThinkToEnt(player, "SurvThinker");" is to finalize the "Think Function" by adding the think function on the entity (on the example, it's the survivor.).

  • After you're done in making the event function, don't forget to add this "__CollectGameEventCallbacks(YourTableName);" to finalize the making of the script mod.

  • Now your mod with "Think Function" via "director_base_addon.nut" is ready.

"mapspawn_addon.nut"
function OnGameEvent_player_first_spawn(event) { local player = GetPlayerFromUserID(event.userid); if(player.IsSurvivor()) { if(player.ValidateScriptScope() ) { local player_entityscript = player.GetScriptScope(); player_entityscript["SurvThinker"] <- function() { if(self.GetHealth() < 100) { self.SetHealth(self.GetHealth() + 1); } return 0.5; } } AddThinkToEnt(player, "SurvThinker"); printl("Added 'Survivor Thinker' to "+ player); } } function OnGameEvent_round_start(event) { printl("The 'YOUR MOD NAME' is started..."); }

"mapspawn_addon.nut" is much simpler to use, as you only need to list the scripts & event functions & they'll run automatically. There are several forms that can be used & also another way to make "Think Function", like:
  • Alternative 1:
    function YourThinkName() { for(local player; player = Entities.FindByClassname(player, "player"); ) { if(player.IsSurvivor() && !player.IsDead()) { if(player.GetHealth() < 100) { player.SetHealth(player.GetHealth() + 1); } } } return 0.5; } function OnGameEvent_round_start(event) { printl("The 'YOUR MOD NAME' is started..."); AddThinkToEnt(self, "YourThinkName"); }
    This "Alternative 1" makes use of the entity used on "mapspawn_addon.nut" (it's "env_soundscape_triggerable") to trigger the "Think Function". The "self" there refers to the "env_soundscape_triggerable". To put it simply, this method gives that "env_soundscape_triggerable" a "Think Function" with loop in every 0.5 second

  • Alternative 2:
    function YourThinkName() { for(local player; player = Entities.FindByClassname(player, "player"); ) { if(player.IsSurvivor() && !player.IsDead()) { if(player.GetHealth() < 100) { player.SetHealth(player.GetHealth() + 1); } } } EntFire("!self", "CallScriptFunction", "YourThinkName", 0.5); } function YourThinkName2() { for(local player; player = Entities.FindByClassname(player, "player"); ) { //Gives health regen to special infected & tank if(player.GetZombieType() <= 8 && !player.IsDead()) { if(player.GetHealth() < 100) { player.SetHealth(player.GetHealth() + 100); } } } EntFire("!self", "CallScriptFunction", "YourThinkName2", 0.5); } function OnGameEvent_round_start(event) { printl("The 'YOUR MOD NAME' is started..."); EntFire("!self", "CallScriptFunction", "YourThinkName", 0.5); EntFire("!self", "CallScriptFunction", "YourThinkName2", 0.5); }
    As you can see, there's a new one here, "EntFire". This one is another method to trigger "Think Function". This one has a benefit in its capability of making more than one "Think Function". The "EntFire" put on the bottom of each function ("YourThinkName" & "YourThinkName2") works as the loop to repeat every 0.5 second

Depending on what you need, you can use only one of them or two of them combined. But remember not to add too many "Think Functions" (in case if you use "Alternative 2" or combination of the basic & the "Alternative 1", or all of them combined), as too many of them can affect the gaming performance.
Conclusions
As you've read "Step 1" & "Step 2", you'll notice that adding "Think Function" isn't that complicated at all. At the same time, you can create many more features with "Think Function". Some examples out there are bot mods (bot's custom thinking mechanisms use "Think Function"), long jump (or double jumps), weapon scope feature (yes, scope feature uses "Think Function"), & custom weapons.

So, with this guide, hopefully you'll be one step closer to become a modder that can make mods with more fun & limitless features to make L4D2 more fun.
8 Comments
3ipKa* 18 Jan @ 12:24am 
I very thankful for your guide and response :teslacat:
kurochama  [author] 16 Jan @ 6:13pm 
@3ipKa* , yes, especially on bots. We don't know what bot mods do on bots, so if we overwrite a useful think function that the bot mods do on bots, the bots might become bugged. There are alternatives for this, like:
1. Use "EntFire" (like what I suggested before). This doesn't consume entity.
2. Create a simple entity via "SpawnEntityFromTable", like "info_target", & make it stick to certain player by naming the "targetname" the same as the player's name or use "m_hOwnerEntity" netprops to make a player own that entity. Then, use "Think Function" on that entity. That way, you can use multiple think functions (but at the cost of adding several entities, if you give each survivor one entity).
3ipKa* 16 Jan @ 4:11pm 
Damn… So It's not safe to use Think for example on the players because of incompatibility with other addons?
kurochama  [author] 16 Jan @ 3:58pm 
@3ipKa* , afaik, the new think function will overwrite the old one instead. If you want multiple timers on one entity, you can try "DoEntFire" or "EntFire" instead, like the one on the last example.
3ipKa* 16 Jan @ 3:28pm 
Is there any compatibility between addons using Think function on the same entity?
MilkWizard 21 Dec, 2024 @ 6:58am 
alright :steamthumbsup:
kurochama  [author] 21 Dec, 2024 @ 5:34am 
@Milkduds , no problem. Let me know if you have any questions.
MilkWizard 21 Dec, 2024 @ 5:08am 
thanks