Results 1 to 8 of 8

Thread: Code Reference

  1. Default Code Reference

    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
    Code:
    public PlayerHurtEvent(Handle:event,const String:name[],bool:dontBroadcast)
    {
        decl String:weapon[64];
        GetEventString(event,"weapon",weapon,63);
    CORRECT
    Code:
    public OnWar3EventPostHurt(victim,attacker,dmgamount)
    {
        decl String:weapon[64];
        GetEventString(W3GetVar(SmEvent),"weapon",weapon,63);
    Advantages:
    Removes the need for a hook event like:
    Code:
    HookEvent( "player_hurt", PlayerHurtEvent );
    Also removes the need for all of the following code:
    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.
    Last edited by ZERO; 04-09-2011 at 08:46 PM.



  2. Default

    This is the method to be used with timers to prevent errors:

    In Variable Declaration:

    Code:
    //timers
    new Handle:NewTimer[MAXPLAYERS+1];
    In Program Include this Method: Must be included to remove the timer to prevent errors
    Code:
    //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
    Code:
    NewTimer[client] = CreateTimer(LENGTH_OF_TIMER,NAME_OF_METHOD,GetClientUserId(client));
    In Program Include this Method: Place code to run when timer ends within this method too
    Code:
    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.
    Last edited by ZERO; 03-27-2013 at 10:20 PM.



  3. Default

    This is a better order for if statemets in OnWar3EventPostHurt

    Code:
    //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.
    Last edited by ZERO; 06-25-2012 at 01:34 PM.



  4. Default

    Below is the switch statements for optimal dmg stack prevention on hit based abilities:

    Code:
    //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:

    Code:
    public Relm(any:victim,any:attacker,any:dmg,any:skill_level)
    {
    //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.
    Last edited by ZERO; 08-03-2011 at 02:16 PM.



  5. Default

    Below is the best method for tracking the level of a given ability:

    Code:
    //Better tracking of current level for increased performance!
    public OnSkillLevelChanged(client,race,skill,newskilllevel)
    {
        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.



  6. Default

    Below is the code NOT to be used for removing weapons:
    Code:
    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:
    Code:
    W3DropWeapon(client,ent);
                    UTIL_Remove(ent);
    Use stocks from zerostocks instead such as:
    Code:
    KnifeOnly(client);
    Last edited by ZERO; 01-07-2013 at 02:45 PM.



  7. Default

    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:
    Code:
    //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:
    Code:
    //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:

    Code:
    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.




Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •