UGX-Mods

Call of Duty: Black Ops 3 => Tutorial Desk => Scripting => Topic started by: qwerty195 on September 17, 2018, 08:33:07 pm

Title: Abnormal202 Scripting Tutorial 1: Functions
Post by: qwerty195 on September 17, 2018, 08:33:07 pm
ABNORMAL202 SCRIPTING TUTORIAL 1: FUNCTIONS

See ALL Tutorials (https://www.ugx-mods.com/forum/scripting/91/abnormal202-scripting-tutorials-master/16746/)

DESCRIPTION

I'm lean, mean, and ready to code! but where exactly can I do that?

The bulk of all code is contained with functions. The only time you will be scripting outside of a function in a .gsc is when you loading things in the preamble of the script. This is the place where you can see all the #using's, #insert's, #precache's to name a few, which I will get to later.

So how do I make a function?

FUNCTION RULES

You can create a function by typing:
function my_function_name_here()
{
 
}
notice the parts:
  • the word function before everything
  • the name of the function (in this case I called it my_function_name_here but it can be anything, so long as there are no spaces, and it uses basic alphanumeric characters)
  • the () (later we'll see that we can actually fit things inside these, as arguments. But for now we won't have any)
  • the {} (These define where my function is occuring. Everything I type must be in between these two {}, much like how everything I'm typing right now is in between two parenthesis)
There are some quick no-no's with functions that I see sometimes:
  • you can't put a function inside another function. Ever.
  • you can't have more than one function with the exact same name in the same .gsc file.
  • you don't put a semicolon ; at the end of declaring a function, like you do with most lines.

ENGINE FUNCTIONS

Alright alright hold up. I don't even know what file to open up yet. Where should I start to script?
When your map is first loading, one file that will always be read is yourmapname.gsc. You can find this in (Bo3Root\usermaps\yourmapname\scripts\zm). Open this up with Sublime. You Should Something Similar to this:
#using scripts\codescripts\struct;

#using scripts\shared\array_shared;
#using scripts\shared\callbacks_shared;
#using scripts\shared\clientfield_shared;
#using scripts\shared\compass;
#using scripts\shared\exploder_shared;
#using scripts\shared\flag_shared;
#using scripts\shared\laststand_shared;
#using scripts\shared\math_shared;
#using scripts\shared\scene_shared;
#using scripts\shared\util_shared;

#insert scripts\shared\shared.gsh;
#insert scripts\shared\version.gsh;

#insert scripts\zm\_zm_utility.gsh;

#using scripts\zm\_load;
#using scripts\zm\_zm;
#using scripts\zm\_zm_audio;
#using scripts\zm\_zm_powerups;
#using scripts\zm\_zm_utility;
#using scripts\zm\_zm_weapons;
#using scripts\zm\_zm_zonemgr;

#using scripts\shared\ai\zombie_utility;

//Perks
#using scripts\zm\_zm_pack_a_punch;
#using scripts\zm\_zm_pack_a_punch_util;
#using scripts\zm\_zm_perk_additionalprimaryweapon;
#using scripts\zm\_zm_perk_doubletap2;
#using scripts\zm\_zm_perk_deadshot;
#using scripts\zm\_zm_perk_juggernaut;
#using scripts\zm\_zm_perk_quick_revive;
#using scripts\zm\_zm_perk_sleight_of_hand;
#using scripts\zm\_zm_perk_staminup;

//Powerups
#using scripts\zm\_zm_powerup_double_points;
#using scripts\zm\_zm_powerup_carpenter;
#using scripts\zm\_zm_powerup_fire_sale;
#using scripts\zm\_zm_powerup_free_perk;
#using scripts\zm\_zm_powerup_full_ammo;
#using scripts\zm\_zm_powerup_insta_kill;
#using scripts\zm\_zm_powerup_nuke;
//#using scripts\zm\_zm_powerup_weapon_minigun;

//Traps
#using scripts\zm\_zm_trap_electric;

#using scripts\zm\zm_usermap;

//*****************************************************************************
// MAIN
//*****************************************************************************

function main()
{
   zm_usermap::main();
   
   level._zombie_custom_add_weapons =&custom_add_weapons;
   
   //Setup the levels Zombie Zone Volumes
   level.zones = ;
   level.zone_manager_init_func =&usermap_test_zone_init;
   init_zones[0] = "start_zone";
   level thread zm_zonemgr::manage_zones( init_zones );

   level.pathdist_type = PATHDIST_ORIGINAL;
}

function usermap_test_zone_init()
{
   level flag::init( "always_on" );
   level flag::set( "always_on" );
}   

function custom_add_weapons()
{
   zm_weapons::load_weapon_spec_from_table("gamedata/weapons/zm/zm_levelcommon_weapons.csv", 1);
}

You should see:
  • A whole bunch of #using's and #insert's up top (ignore these for now)
  • the function main() (this is run on map startup)
  • the function usermap_test_zone_init() (this is for setting up zones)
  • the function custom_add_weapons() (this is a simple one-line function for loading your map's weapontable)
We'll leave the bottom two functions alone, as they serve their own purposes, and don't need to be interfered with.
However we will need to mess with function main(), because it is run during map startup, and we'll also want our function to be run on map startup.
if you haven't already, place your new function below the preamble, anywhere that's not inside another function. For example like this:
//*****************************************************************************
// MAIN
//*****************************************************************************

function main()
{
   zm_usermap::main();
   
   level._zombie_custom_add_weapons =&custom_add_weapons;
   
   //Setup the levels Zombie Zone Volumes
   level.zones = ;
   level.zone_manager_init_func =&usermap_test_zone_init;
   init_zones[0] = "start_zone";
   level thread zm_zonemgr::manage_zones( init_zones );

   level.pathdist_type = PATHDIST_ORIGINAL;
}
function my_awesome_function()
{

}

function usermap_test_zone_init()
{
   level flag::init( "always_on" );
   level flag::set( "always_on" );
}   

function custom_add_weapons()
{
   zm_weapons::load_weapon_spec_from_table("gamedata/weapons/zm/zm_levelcommon_weapons.csv", 1);
}
Before we even get to calling it, let's give it something simple it can actually do, since currently it does nothing. Let's use a very common Engine Function which we can call from anywhere. The engine function I'm talking about is IPrintLnBold():
void IPrintLnBold()
You can find all the Engine functions in the
Modme ScriptDocs (http://aviacreations.com/modme/scriptdocs/). These can be used anywhere. The IPrintLnBold() is simple. It takes one argument: a string of text, and when the line is read by the computer it will print that text to the screen in-game. This is very useful for testing scripts, as it let's us see where the computer is reading lines and where it isn't.
Let's add this line to our function, and give it some text:
function my_awesome_function()
{
   IPrintLnBold("Hello Everybody");
}
notice:
  • I put the words Hello Everybody within quotation marks "". The actual quotation marks will not get printed to the screen.
  • I put a semicolon ; at the end of the line. By default, get into the habit of putting these at the end of every line. there are some exceptions however, such as when declaring functions, as I said earlier.
if you forget a ; launcher will throw an error similar to this one when linking:
********************************************************************************
UNRECOVERABLE ERROR:
 ^1SCRIPT ERROR: No generated data for 'scripts/zm/zm_yourmapname.gsc'
ERR(0) scripts/zm/zm_yourmapname.gsc (76,1) in "my_awesome_function()" : syntax error, unexpected TOKEN_RIGHT_CURLY : }


Linker will now terminate.
********************************************************************************
You see, when computers read .gsc code, they ignore spaces and newlines. That way I can have as many spaces and newlines to make myself comfortable. However this means we must manually define where the line ends using a ; or launcher will keep reading everything as one line and get confused.

CALLING FUNCTIONS

If I run my script as-is right now, nothing new will happen. This is because while the function is there, it's not getting called. We can tell the computer to call a function like this:
my_awesome_function();
if we place this line inside a function that we know will be run before the start of the game, we can effectively call it:
function main()
{
   zm_usermap::main();
   
   level._zombie_custom_add_weapons =&custom_add_weapons;
   
   //Setup the levels Zombie Zone Volumes
   level.zones = ;
   level.zone_manager_init_func =&usermap_test_zone_init;
   init_zones[0] = "start_zone";
   level thread zm_zonemgr::manage_zones( init_zones );

   level.pathdist_type = PATHDIST_ORIGINAL;

   my_awesome_function();
}
function my_awesome_function()
{
   IPrintLnBold("Hello Everybody");
}
There's another way to call a function though, and that's called threading. Bascially if we do the same thing except type the word thread before it, we can thread the function:
thread my_awesome_function();
but what does it mean to thread? This can be a difficult concept to understand immediately. Basically when we thread, we split the computer up into two instances: one which will jump to and start reading the function we threaded; and the other which will keep on reading the lines below it as normal. if we don't thread however, then the computer will see the function call, jump to the function, finish reading that function, then jump back to where it was it the other function it was in and keep reading as normal.
In this case it doesn't matter if we thread or not because there's nothing at the bottom of function main() for it to read anyway.

WAITS & WAITTILLS

I realize this is a bit off topic from functions, but in order to make this function work, we need to understand waits and waittills.
Because if we try to run the code as-is, yes our function is called, and the line will print to the screen, but it will all happen before the player's blackscreen has even gone away and before they can start moving.
if only there was a way we could make the computer waittill the blackscreen was gone?
There are many notifies that Treyarch sends when certain events happen that we can waittill it happens. For example the waittill "all_players_connected":
level waittill("all_players_connected");
notice:
  • There is the word level before the waittill. This designates we are waiting for the entity known as the "level" to be notified that all players have connected. The entity "level" is special as it can be accessed from any .gsc with ease and is the same entity touched by all scripts. We'll use it more later.
  • the word waittill
  • the string "all_players_connected" inside the (). This string is specific to this waittill and is set by Treyarch when all players have connected.
  • the ;
if we put this before our function is called, then it will not get called until all players have connected:
function main()
{
   zm_usermap::main();
   
   level._zombie_custom_add_weapons =&custom_add_weapons;
   
   //Setup the levels Zombie Zone Volumes
   level.zones = ;
   level.zone_manager_init_func =&usermap_test_zone_init;
   init_zones[0] = "start_zone";
   level thread zm_zonemgr::manage_zones( init_zones );

   level.pathdist_type = PATHDIST_ORIGINAL;

   level waittill("all_players_connected");
   
   thread my_awesome_function();
}
function my_awesome_function()
{
   IPrintLnBold("Hello Everybody");
}
We're almost ready to run the code and see the results. One more thing though.
From my experience, the blackscreen still remains for a fair amount of time after all players have connected. So to guarantee that we will be able to see our print to the screen, let's just have the computer wait some more seconds after players have loaded in before our function is called.
we can do this by using a wait:
wait(10);
notice:
  • the word wait
  • the number 10 is the amount of seconds I want it to wait.
  • the ;
now let's place that accordingly:
function main()
{
   zm_usermap::main();
   
   level._zombie_custom_add_weapons =&custom_add_weapons;
   
   //Setup the levels Zombie Zone Volumes
   level.zones = ;
   level.zone_manager_init_func =&usermap_test_zone_init;
   init_zones[0] = "start_zone";
   level thread zm_zonemgr::manage_zones( init_zones );

   level.pathdist_type = PATHDIST_ORIGINAL;

   level waittill("all_players_connected");
   wait(10);

   thread my_awesome_function();
}
function my_awesome_function()
{
   IPrintLnBold("Hello Everybody");
}
Now if we save, link in launcher, and run the map, we should see our line print to the screen shortly after starting the game!
See ALL Tutorials (https://www.ugx-mods.com/forum/scripting/91/abnormal202-scripting-tutorials-master/16746/)