PDA

View Full Version : Code Reference



ZERO
04-04-2011, 02:04 PM
This thread archives tips and tricked used in coding war3 races to help improve efficiency of the code used and thus reduce lag, improve performance and prevent crashes. As no true guide serves for making war3 races this thread serves to place important code snippets in one place on a post by post basis.

---------- Post added at 03:04 PM ---------- Previous post was at 02:56 PM ----------

Get weapon to prevent ability damage stacking, use for all ability's that require hit detection regardless of internal function use for consistency:

OLD


public PlayerHurtEvent(Handle:event,const String:name[],bool:dontBroadcast)
{
decl String:weapon[64];
GetEventString(event,"weapon",weapon,63);
CORRECT

public OnWar3EventPostHurt(victim,attacker,dmgamount)
{
decl String:weapon[64];
GetEventString(W3GetVar(SmEvent),"weapon",weapon,63);Advantages:
Removes the need for a hook event like:
HookEvent( "player_hurt", PlayerHurtEvent );Also removes the need for all of the following code:

new userid=GetEventInt(event,"userid");
new attacker_userid=GetEventInt(event,"attacker");

new victim=GetClientOfUserId(userid);
new attacker=GetClientOfUserId(attacker_userid);

new dmg=GetEventInt(event,"dmg_health");Lastly as seen it additionally removes the problem of dmg being a Float value and thus the need for RoundToFloor() to be used to convert back to an Integer.

ZERO
04-04-2011, 04:42 PM
This is the method to be used with timers to prevent errors:

In Variable Declaration:


//timers
new Handle:NewTimer[MAXPLAYERS+1];
In Program Include this Method: Must be included to remove the timer to prevent errors


//CLEAR TIMERS
public OnClientDisconnect(client)
{
new Handle:Timer;
Timer = NewTimer[client];
if(Timer != INVALID_HANDLE)
{
KillTimer(NewTimer);
Timer = INVALID_HANDLE;
}
}
In Method Using Timer: This is used to create the timer


NewTimer[client] = CreateTimer(LENGTH_OF_TIMER,NAME_OF_METHOD,GetClie ntUserId(client));
In Program Include this Method: Place code to run when timer ends within this method too


public Action:NAME_OF_METHOD(Handle:timer,any:userid)
{
//Get the current client of the target userid
new client = GetClientOfUserId(userid);

//Code to run when timer ends
Timer[client] = INVALID_HANDLE;

//If client is a valid player
if(client!=0)
{

}
}


New userid based tracking ensures that the client target at the end of the timer is the intended one. This can protect against crashes related to a client reference value changing unexpectedly.

ZERO
07-30-2011, 01:09 PM
This is a better order for if statemets in OnWar3EventPostHurt



//Hit based abilities
public OnWar3EventPostHurt(victim,attacker,dmg)
{
//Better order:

//Confirm valid trigger
if(victim>0&&attacker>0&&victim!=attacker)
{
//Confirm that it is for this race
new race_attacker=War3_GetRace(attacker);
if(race_attacker==thisRaceID && !Hexed(attacker,false) && !W3HasImmunity(victim,Immunity_Skills))
{
decl String:weapon[64];
GetEventString(W3GetVar(SmEvent),"weapon",weapon,63);

//Will only trigger from things not listed below
if(!StrEqual(weapon,"hegrenade",false)&&!StrEqual(weapon,"athena_relm",false)&&!StrEqual(weapon,"athena_spear",false))
{
//abilities below
}
}
}
}


Advantages: Causes less if statements to be read on normal event triggers. The ones that are read are less costly to check. Thus the speed of the overall function should be improved.

ZERO
08-03-2011, 01:14 PM
Below is the switch statements for optimal dmg stack prevention on hit based abilities:


//Binary check for mult skill trigger
new skillcheck = 0;

//Relm Level
new relm_level=skill_level_relm[attacker];

//Spear Level
new spear_level=skill_level_spear[attacker];

//Relm Prop
if(relm_level>0 && Math_GetRandomFloat( 0.0, 1.0 ) <= RelmChanse[relm_level])
{
skillcheck+=1;
}

//Spear Prop
if(spear_level>0 && Math_GetRandomFloat( 0.0, 1.0 ) <= SpearChanse[spear_level])
{
skillcheck+=2;
}

switch (skillcheck)
{
//Runs if Relm was selected
case 1:
{
Relm(victim,attacker,dmg,relm_level);
}
//Runs if Spear was selected
case 2:
{
Spear(victim,attacker,dmg,spear_level);
}
//Runs if both were selected and selects 1
case 3:
{
new chance = Math_GetRandomInt( 1, 2 );
switch (chance)
{
//Runs if Relm was selected
case 1:
{
Relm(victim,attacker,dmg,relm_level);
}
//Runs if Spear was selected
case 2:
{
Spear(victim,attacker,dmg,spear_level);
}
//Checks for erros
default:
{
PrintToChat(attacker,"[ERROR] Out of bounds vale for stacked skill, please report.");
}
}
}
}

Note that in this code the abilities themselves are methods which are called by the appropriate switch. An example of a method is below:


public Relm(any:victim,any:attacker,any:dmg,any:skill_lev el)
{
//skill code here
}

This methodology makes it impossible for the dmg stack prevention to effect the probability of a skill actually occurring. Also the use of switches dramatically improves performance which is especially important for a function that can be called very often. Also note the use of && vs nested ifs for when a new variable creation is not needed. This reduces the number of calls.

ZERO
08-03-2011, 02:19 PM
Below is the best method for tracking the level of a given ability:


//Better tracking of current level for increased performance!
public OnSkillLevelChanged(client,race,skill,newskillleve l)
{
if(race==thisRaceID)
{
if(skill==SKILL_RELM) //1
{
skill_level_relm[client]=newskilllevel;
}
else if(skill==SKILL_SPEAR) //2
{
skill_level_spear[client]=newskilllevel;
}
else if(skill==SKILL_IMMU) //3
{
skill_level_immu[client]=newskilllevel;
}
else if(skill==SKILL_HELM) //4
{
skill_level_helm[client]=newskilllevel;
}
else if(skill==ULT_GAZE) //5
{
skill_level_gaze[client]=newskilllevel;
}
}
}

I would use a switch if I could but the variables are not constant (compiler throws error). Optimizing this function is very important as it is called at the start of every round, when ever a player changes races and when ever a players skill level changes.

ZERO
03-19-2012, 10:03 PM
Below is the code NOT to be used for removing weapons:


if( ValidPlayer( client, true ) )
{
new wep1 = GetPlayerWeaponSlot( client, CS_SLOT_PRIMARY );

if( IsValidEdict( wep1 ) )
{
CS_DropWeapon(client, wep1, true, true);
AcceptEntityInput(wep1, "Kill");
}
}


This removes the need for the bad:

W3DropWeapon(client,ent);
UTIL_Remove(ent);

Use stocks from zerostocks instead such as:

KnifeOnly(client);

ZERO
07-27-2012, 09:00 PM
Here is a way to easily create effective ward timers. Rather than just one large timer that always runs even when not needed these times are created and killed on demand. Callback and heper functions make this very clean and easy to use (requires zero stocks):

Global:


//For multiple wards per player add [x] where x is the number of wards at once allowed for the client
new Handle:WardTimer[MAXPLAYERS+1];
new Handle:WardLoopTimer[MAXPLAYERS+1];
//For multiple wards per player add [x] before [3] where x is the number of wards at once allowed for the client
new Float:WardLocation[MAXPLAYERS+1][3];


In Method:


//End any existing sparks first
//END SPARKS TIMER
new Handle:Timer;

//End Sparks Timer
Timer = WardTimer[client];
if(Timer != INVALID_HANDLE)
{
KillTimer(Timer);
Timer = INVALID_HANDLE;
}

//Sparks Timer
Timer = WardLoopTimer[client];
if(Timer != INVALID_HANDLE)
{
KillTimer(Timer);
Timer = INVALID_HANDLE;
}


War3_CachedPosition(client,WardLocation[client]);
WardTimer[client] = CreateTimer(10.0, EndSparks, client);
WardLoopTimer[client] = CreateTimer(0.14, Sparks, client, TIMER_REPEAT);


Required Methods:


public Action:EndSparks(Handle:timer,any:client)
{
//END TIMER
WardTimer[client] = INVALID_HANDLE;

//END SPARKS TIMER
new Handle:Timer;
Timer = WardLoopTimer[client];
if(Timer != INVALID_HANDLE)
{
KillTimer(Timer);
Timer = INVALID_HANDLE;
}
}


public Action:Sparks(Handle:timer,any:client)
{
CalculateWard(client,WardLocation[client],260.0);
}

//This function is a customizable call back to allow for you to preform anything on any client found within the ward!
public WardTargetCallback(client, target)
{
if(War3_DealDamage(target,1,client,DMG_ENERGYBEAM,"death_sparks",_,W3DMGTYPE_MAGIC))
{
//Damage suscess
}
}

This code of <50 lines replaces the existing methods of over 85 lines of code and 4 for loops with 1 single loop. Replaces single permanent global looping timers with multiple temporary on demand timers. Uses less variables and is easier to control and customize on a user by user and method call by method call basis.

ZERO
10-25-2014, 11:35 PM
Color table: https://www.doctormckay.com/morecolors.php