SA-MP Forums

Go Back   SA-MP Forums > SA-MP Scripting and Plugins > Filterscripts > Includes

Reply
 
Thread Tools Display Modes
Old 22/05/2012, 02:25 PM   #1
Joe Staff
High-roller
 
Join Date: Aug 2007
Posts: 2,593
Reputation: 379
Default Procedural .Rec Generator

The following code is used to generate .REC files for NPCs to follow. In essence, this is a pawn scripted NPC controller which gives you the ability to create NPCs that can go directly from point A to point B while (client sided) attempting to maintain standard physics. What that means is the NPC will actually stick to the floor and be stopped by walls, but only momentarily. If their recording has them passing a certain threshold, they will simply walk through the wall or fly off of the ground.

Important Notes:
When creating a path that is too short, the NPC will simply be stuck there for an undetermined amount of time. I believe this to be connected with a Divide By Zero error, but I never got around to fixing it.

I've left it up to you to find a method to get the .REC file from the Scriptfiles folder to the NPC Recordings folder. I used a FILE MANAGER. Please note however; I ran into an issue with the moving function in that particular plugin which required me to delete the previous .rec file before moving it.

I highly suggest spending time finding out the exact speeds of motion when having the NPC follow it. Otherwise the NPC will teleport every now and then. A good speed for running (not sprinting) was 0.006.

On average, a created .REC file will be in the 5KB size range, about 60-100 separate blocks of motion.

Lastly, I hadn't spent a lot of time optimizing the process, so I highly suggest you do so before using it.

Video:
[ame]http://www.youtube.com/watch?v=FH6vteZCOgM[/ame]

Code:
pawn Code:
#include <a_samp>
#define ONFOOT_RATE 100 //milliseconds (Not equal to that in Server.cfg, but similar point) -- Numbers reaching the 500ms range will have issues with repeating recordings
#define PI 3.14159265

stock BuildRecording(name[],Float:x1,Float:y1,Float:z1,Float:x2,Float:y2,Float:z2, Float:speed)
{
    new time;
    new steps;
    new Float:angle=atan2(y2-y1,x2-x1);
    while(angle<0)angle+=360.0;
    while(angle>360)angle-=360.0;
   
   
    new Float:xvel= (speed * floatcos(angle, degrees));
    new Float:yvel= (speed * floatsin(angle, degrees));
   
    new Float:distance=floatsqroot( (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)+(z2-z1)*(z2-z1) );
    if(speed<0.00001)time=0;
    else time=floatround(distance/speed,floatround_ceil);
   
    //printf("Total Time = %d, Distance=%f, Angle=%f, File Name: %s",time,distance,angle,name);
   
    steps=time/ONFOOT_RATE;
   
   
    new Float:xrate=xvel*ONFOOT_RATE;//(x2-x1)/steps;
    new Float:yrate=yvel*ONFOOT_RATE;//(y2-y1)/steps;
    new Float:zrate=(z2-z1)/steps;
   
   
    if(steps==0)time=0;
    else time=time/steps;
   
    //printf("Time=%d, Steps=%d, Rate:%f,%f,%f",time,steps,xrate,yrate,zrate);
   
    new end=0;
    new process=0;
    new step=0;
    new input;
    new piece=1;
    new File:file=fopen(name,io_write);
   
    //Angle Quaternion
    new Float:w,Float:x,Float:y,Float:z;
    EulerToQuaternion(-angle+90,w,x,y,z);
   
    //print("Created File -- Writing header");

    fputchar(file,0xE8,false);
    fputchar(file,0x03,false);
    fputchar(file,0x00,false);
    fputchar(file,0x00,false);
    fputchar(file,0x02,false);
    fputchar(file,0x00,false);
    fputchar(file,0x00,false);
    fputchar(file,0x00,false);

    //print("Header written, creating content");
    while(!end)
    {
        switch(process)
        {
            ////////////////////////////////////////////////////////
            //Information taken from wiki.sa-mp.com/wiki/.rec_file//
            ////////////////////////////////////////////////////////
            //I had done most of the research, I had almost       //
            //finished when someone turned me to that link which  //
            //had all the information found, so I used it.        //
            ////////////////////////////////////////////////////////
       
            case 0..3:      //Time before performing the immediately following step (is incremental, step 1 = 0ms, step 2 = 100ms, etc)
            {
                input=(time*step)<<(32-(piece*8))>>24;
                if(piece>3)piece=1;
                else piece++;
            }
            case 4..5:      //LeftRight
            {
                input=0<<(32-(piece*8))>>24;
                if(piece>1)piece=1;
                else piece++;
            }
            case 6..7:      //UpDown Keys
            {
                if(step==steps)input=0x0000;                //Don't make the NPC walk on the last step or he will walk in place.
                else input=0x00FF<<(32-(piece*8))>>24;      //0x00FF = Forward, 0xFF00=Backward
                if(piece>1)piece=1;
                else piece++;
            }
            case 8..9:      //Special Keys
            {
                input=0;
            }

            //Position
            case 10..13:    //X1
            {
                input=_:floatadd( x1,floatmul(xrate,step) )<<(32-(piece*8))>>24; //Need to convert Floats to Standard Integers (use _:)
                if(piece>3)piece=1;
                else piece++;
            }
            case 14..17:    //Y1
            {
                input=_:floatadd( y1,floatmul(yrate,step) )<<(32-(piece*8))>>24;
                if(piece>3)piece=1;
                else piece++;
            }
            case 18..21:    //Z1
            {
                input=_:floatadd( z1,floatmul(zrate,step) )<<(32-(piece*8))>>24;
                if(piece>3)piece=1;
                else piece++;
            }
            //

            //Angle Quaternion
            case 22..25:    //Quaternion W
            {
                input=_:w<<32-(piece*8)>>24;
                if(piece>3)piece=1;
                else piece++;
            }
            case 26..29:    //Quaternion X
            {
                input=_:x<<32-(piece*8)>>24;
                if(piece>3)piece=1;
                else piece++;
            }
            case 30..33:    //Quaternion Y
            {
                input=_:y<<32-(piece*8)>>24;
                if(piece>3)piece=1;
                else piece++;
            }
            case 34..37:    //Quaternion Z
            {
                input=_:z<<32-(piece*8)>>24;
                if(piece>3)piece=1;
                else piece++;
            }

            case 38:    //Health
            {
                input=0xFF;
            }
            case 39:    //Armor
            {
                input=0xFF;
            }
           
            //Weapon ID
            case 40:
            {
                input=0;
            }
            //SPECIAL_ACTION
            case 41:
            {
                input=0;
            }
           
            //Velocities
            case 42..45:    //X Velocity
            {
                if(step==steps)input=0;
                else input=_:xvel<<32-(piece*8)>>24;
                if(piece>3)piece=1;
                else piece++;
            }
            case 46..49:    //Y Velocity
            {
                if(step==steps)input=0;
                else input=_:yvel<<32-(piece*8)>>24;
                if(piece>3)piece=1;
                else piece++;
            }
            case 50..53:    //Z Velocity
            {
                input=0<<32-(piece*8)>>24;
                if(piece>3)piece=1;
                else piece++;
            }

            //
            case 54..67:input=0;    //Surfing variables

            //Animation
            case 68..69:input=0;
            case 70..71:input=0;    //Animation parameters


            default: input=0x00;

        }
        fputchar(file,input,false);
        process++;

        if (process==72)
        {
            step++; //Reset for next step
            process=0;
        }
        if(step>steps)
        {
            //print("Ending file");
            break;
        }

    }
    fclose(file);
    //printf("File closed - %d steps created",steps);
}

stock Float:ToRadian(Float:Degrees)return Degrees*(PI/180);
stock EulerToQuaternion(Float:angle,&Float:w,&Float:x,&Float:y,&Float:z)    //Requires radian angle
{
    new Float:c1,Float:c2,Float:c3,Float:s1,Float:s2,Float:s3;
    c1=floatcos(0.0);
    c2=floatcos(ToRadian(angle)/2);
    c3=floatcos(0.0);
    s1=floatsin(0.0);
    s2=floatsin(ToRadian(angle)/2);
    s3=floatsin(0.0);
    w=(c1*c2*c3)-(s1*s2*s3);
    x=(s1*s2*c3)+(c1*c2*s3);
    y=(s1*c2*c3)+(c1*s2*s3);
    z=(c1*s2*c3)-(s1*c2*s3);
}

Feel free to take this code, release it, and claim it as your own, I don't care.
*I say 'claim it as your own' as this code is simply one of the only ways to actually perform this task. I don't wish to claim that if you created your very own system that you copied mine.



Example:
Filterscript -- USES JaTochNietDan's File Manager
Makes all NPCs follow a single player if he is within range, only stops when player spawns.

pawn Code:
#define FILTERSCRIPT
#include <a_samp>
#include <FileManager>
new NPCTarget[MAX_PLAYERS];
public OnFilterScriptInit()
{
    SetTimer("NPCFollow",1000,1);
    return 1;
}
public OnPlayerConnect(playerid)
{
    if(IsPlayerNPC(playerid))
    {
        NPCTarget[playerid]=INVALID_PLAYER_ID; //Reset NPCs target
        return 1;
    }
    return 1;
}
public OnPlayerStateChange(playerid, newstate, oldstate)
{
    if(newstate==PLAYER_STATE_SPAWNED) //Reset NPCs target when player dies
        for(new npc;npc<MAX_PLAYERS;npc++)if(NPCTarget[npc]==playerid)NPCTarget[npc]=INVALID_PLAYER_ID;
    return 1;
}
public OnPlayerText(playerid, text[])
{
    if(IsPlayerNPC(playerid))
    {
        if(!strcmp(text,"Command Accepted: npc",false,21)) //Only method I could think of to communicate between NPC Mode and Filterscript
        {
            new tmps[48];
            format(tmps,48,"npcmodes\\recordings\\npc%03d.rec",strval(text[21]));
            file_delete(tmps);
            return 0;
        }
    }
    return 1;
}
forward NPCFollow();
public NPCFollow()
{
    new Float:npx,Float:npy,Float:npz;
    new Float:px,Float:py,Float:pz;
    new tmps[48];
    new tmps2[48];
    for(new npc=0;npc<MAX_PLAYERS;npc++)
    {
        if(!IsPlayerNPC(npc)||!IsPlayerConnected(npc))continue;
        GetPlayerPos(npc,npx,npy,npz);
        if(NPCTarget[npc]==INVALID_PLAYER_ID) //Has no Target
        {
            for(new playerid=0;playerid<MAX_PLAYERS;playerid++) //Search for targets
            {
                if(IsPlayerNPC(playerid)||!IsPlayerConnected(playerid)||(npc==playerid))continue;
                if(IsPlayerInRangeOfPoint(playerid,30,npx,npy,npz))
                {
                    NPCTarget[npc]=playerid;
                    printf("NPC[%d]: Target Player %d",npc,playerid);
                }
            }
        }else{ //Has Target
            GetPlayerPos(NPCTarget[npc],px,py,pz);
            if(IsPlayerInRangeOfPoint(npc,0.5,px,py,pz))continue; //Poor attempt at preventing lock up
            format(tmps2,48,"npcmodes\\recordings\\npc%03d.rec",npc);
            if(!file_exists(tmps2))
            {
                format(tmps,48,"npc%03d.rec",npc);
                BuildRecording(tmps,npx,npy,npz,px,py,pz, 0.006);
                format(tmps,48,"scriptfiles\\npc%03d.rec",npc);
                format(tmps2,48,"npcmodes\\recordings\\npc%03d.rec",npc);
                file_move(tmps,tmps2);
                SendClientMessage(npc,0,"GO TO FUNCTION CALLED"); //Communicate to NPC
            }
        }
    }
}
NPC Mode
pawn Code:
#include <a_npc>
new MYID;
main(){}
public OnNPCConnect(myplayerid)
{
    MYID=myplayerid;
}
public OnClientMessage(color, text[])
{
    if (!strcmp(text,"GO TO FUNCTION CALLED",false)) //Received communica from server
    {
        new tmps[32];
        format(tmps,32,"npc%03d",MYID);
        StartRecordingPlayback(PLAYER_RECORDING_TYPE_ONFOOT,tmps);
        format(tmps,32,"Command Accepted: %s",tmps);
        SendChat(tmps); //Return communica to server.
    }
}

Last edited by Joe Staff; 22/05/2012 at 04:38 PM.
Joe Staff is offline   Reply With Quote
Old 22/05/2012, 03:06 PM   #2
TheArcher
High-roller
 
TheArcher's Avatar
 
Join Date: Dec 2009
Location: Home
Posts: 2,334
Reputation: 251
Default Re: Procedural .Rec Generator

Dude YOU AWESOME, you've released it, i LOVE YOU. Thanks a lot.
__________________
TheArcher is offline   Reply With Quote
Old 22/05/2012, 03:07 PM   #3
ℓмяαη_кнαη
Little Clucker
 
ℓмяαη_кнαη's Avatar
 
Join Date: May 2012
Location: Karachi - pk
Posts: 43
Reputation: 2
Default Re: Procedural .Rec Generator

thanks awsome...Keep up pal +1
__________________
Give me If i have helped you


My Rank|Server Name|Mode|Player's|Stats|Ip Addres
Lvl 3SAMPEVER Party ServerHouse+Stunt+Race+Dm+Jumps0/100ONLINE208.115.203.239:7777

ℓмяαη_кнαη is offline   Reply With Quote
Old 22/05/2012, 03:11 PM   #4
RanSEE
Huge Clucker
 
RanSEE's Avatar
 
Join Date: Apr 2011
Posts: 351
Reputation: 57
Default Re: Procedural .Rec Generator

Aww, you could have sold some copies, its epic ^^ Great Release.
__________________
RanSEE is offline   Reply With Quote
Old 22/05/2012, 04:02 PM   #5
Joe Staff
High-roller
 
Join Date: Aug 2007
Posts: 2,593
Reputation: 379
Default Re: Procedural .Rec Generator

Quote:
Originally Posted by RanSEE View Post
Aww, you could have sold some copies, its epic ^^ Great Release.
In most cases, I'd prefer to give something away.

My philosophy is, If you're doing something for money, you're no longer doing it for fun.
Joe Staff is offline   Reply With Quote
Old 22/05/2012, 04:04 PM   #6
rbN.
Banned
 
Join Date: Jan 2010
Location: fissa
Posts: 1,079
Reputation: 289
Default Re: Procedural .Rec Generator

Any examples ?
rbN. is offline   Reply With Quote
Old 22/05/2012, 04:09 PM   #7
Nanory
Big Clucker
 
Nanory's Avatar
 
Join Date: Feb 2009
Posts: 118
Reputation: 12
Default AW: Procedural .Rec Generator

It's really cool and a big step for people, who can make a plugin with this function, using a threaded file and file move system, which manage also the NPC's
__________________

Life of German
German Reallife since 2008
Nanory is offline   Reply With Quote
Old 22/05/2012, 04:25 PM   #8
Joe Staff
High-roller
 
Join Date: Aug 2007
Posts: 2,593
Reputation: 379
Default Re: Procedural .Rec Generator

Quote:
Originally Posted by RobinOwnz View Post
Any examples ?
I'll update the mainpost with an example.

-- Used code from video.
Joe Staff is offline   Reply With Quote
Old 22/05/2012, 05:08 PM   #9
Vukilore
Huge Clucker
 
Vukilore's Avatar
 
Join Date: Jan 2011
Location: http://forum.sa-mp-fr.com/index.php
Posts: 300
Reputation: 110
Default Re : Procedural .Rec Generator

Just AWESOME, really good !
__________________
http://forum.sa-mp-fr.com/index.php. << here rest the french forum, in piece.
Vukilore is offline   Reply With Quote
Old 22/05/2012, 05:15 PM   #10
FireCat
High-roller
 
FireCat's Avatar
 
Join Date: Jul 2010
Posts: 2,322
Reputation: 609
Default Re: Procedural .Rec Generator

Very nice <3
FireCat is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
[Tool/Web/Other] [WIP]SA-MP ALS Hooking Generator Jonny5 Tools and Files 15 14/10/2012 03:38 PM
Procedural Plants MP2 Scripting Help 42 08/05/2012 05:48 PM


All times are GMT. The time now is 05:30 AM.


Powered by vBulletin® Version 3.8.6
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.