[Include] Procedural .Rec Generator
#1

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.
    }
}
Reply
#2

Dude YOU AWESOME, you've released it, i LOVE YOU. Thanks a lot.
Reply
#3

thanks awsome...Keep up pal +1
Reply
#4

Aww, you could have sold some copies, its epic ^^ Great Release.
Reply
#5

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.
Reply
#6

Any examples ?
Reply
#7

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
Reply
#8

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

-- Used code from video.
Reply
#9

Just AWESOME, really good !
Reply
#10

Very nice <3
Reply
#11

Awesome, just awesome! Thank you very much for releasing this
Reply
#12

I think (for future versions) it would be good to make a plugin out of it.
With the plugin, it maybe would be faster (if this is important) and the plugin could directly write the file into the correct folder.

But for now, this is just awesome.
I will test it right away!
Reply
#13

Quote:
Originally Posted by NaS
Посмотреть сообщение
I think (for future versions) it would be good to make a plugin out of it.
With the plugin, it maybe would be faster (if this is important) and the plugin could directly write the file into the correct folder.

But for now, this is just awesome.
I will test it right away!
Yup since File Manager Plugin is also a source code, he could implement this code into it we'll have more speed, simplified, easy to use.
Reply
#14

Sorry for double posting but i want to know how do i make it work. I have tried in many ways without success, yes of course i didn't have much practice with NPCs but to set up the NPC looks a bit complicated. I do not really know how to spawn a NPC using this. Well, i didn't get where to put the FS example, and what files i have to create. :S I hope you understand what i meant.
Reply
#15

Finally can't wait till i try it.
Reply
#16

Going to be out of the country, so I won't be able to make a solid response for 10 days.
Reply
#17

What about NPC following in car?
Reply
#18

Nice FS.
Reply
#19

It looks cool and funny.
Reply
#20

Quote:
Originally Posted by Joe Staff
View Post
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.
Wow I like it.
Just keep it up!
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)