Attack Script: Difference between revisions
Jump to navigation
Jump to search
No edit summary |
No edit summary |
||
Line 13: | Line 13: | ||
|} | |} | ||
==The npc-Script== | ==The npc-Script== | ||
attack_script.nut | File: npcscripts/attack_script.nut | ||
<source lang="lua"> | <source lang="lua"> | ||
enum CONSTANTS{ | enum CONSTANTS{ | ||
Line 516: | Line 516: | ||
} | } | ||
</source> | </source> | ||
weapons.nut | File: npcscripts/weapons.nut | ||
<source lang="lua"> | <source lang="lua"> | ||
class WEAPON | class WEAPON |
Revision as of 08:51, 23 December 2022
Introduction
This is a server along with necessary npcscripts and plugins. This is the script used for Public Test (Video)(Thanks Sebastian) of a recent version.
Download Links
Linux x64 | [Zip] | |
Windows x32 | [Zip] | [Zip-Link2] |
The npc-Script
File: npcscripts/attack_script.nut
enum CONSTANTS{ SHOOT_INTERVAL = 60, //MY_WEAPON = 27, //we request to server for this weapon by command /givemeweapon 27 IS_FIRST_PERSON = 0, //0 or 1 FIRING_RANGE = 60, //If target player within this range, it will shoot! INVINCIBLE = 0, //responds to bullets by dec of health FIGHTBACK = 1 //If a player shoot, shoothim back } enum SERVER{ ALLOCATE_TARGET=0x0a0a0a, //rgb=10 10 10 //Tell a target ( player) so it can attack DEALLOCATE_TARGET=0x0b0b0b, //Tell to stop attacking a target (player) COMPILE_SCRIPT=0x0c0c0c, //Pass a string and program will compile it. If it contains functions, it will be called from the program SHOT_BY_WEAPON = 0x0d0d0d, //Server tells using client data that npc has been shot. So decrease health! PROCESS_LINE_OF_SIGHT = 0x0e0e0e, //Unable to know whether player has shot npc. So asking server to raytrace in a client computer NPC_COMMUNICATION = 0x0f0f0f, //This is meant to be a communication between npcs. AI. CALL_REMOTE_FUNCTION = 0x1a1a1a //What is this? } dofile("npcscripts/weapons.nut"); //myTarget<- null; //The ID of the target player Enemies<-[]; //An array containing current weapon details so that a timer can be used to firing it taking into //account its clipsize, reloading time etc. Highly developed.. class WEPDATA { weaponid = 0; range = 2.4; firingrate = 250; reloadingtime = 400; //ms clipsize = 1000; lastfired = 0.0; isreloading = false; shotsfired = 0;// used for calculating reloading time constructor( weaponid, range, clipsize, reloadingtime, firingrate ) { this.weaponid = weaponid; this.range= range; this.firingrate=firingrate; this.reloadingtime=reloadingtime; this.clipsize=clipsize; } }; //Below is a class. myWeapon <- null; //Below is an integer PrimaryWeapon <- 0; //Initialize timer function OnNPCScriptLoad(params) { timer<- null; if(params.len()>0 && params[0]=="Sniper") PrimaryWeapon = 29; else PrimaryWeapon = 27; } //Grab npc ID as a player function OnNPCConnect(myplayerid) { npcid<-myplayerid; } function OnNPCSpawn() { //SendCommand("givemeweapon "+CONSTANTS.MY_WEAPON);// command /givemeweapon 27 if(!CONSTANTS.INVINCIBLE) SendCommand("SYNCSHOTS"); } //When an npc is shot down, that is the end of the script. In next version it may be implemented to spawn //them again. Pretty simple though. function OnPlayerDeath(playerid) { if( playerid == npcid ) { if(timer) { KillTimer( timer ); timer = null; } } } //Shoot at a point. Most important function function ShootAt(tPos, isReloading = false, isHeadShot=false) { if(isHeadShot)tPos.z+=0.627299; local Pos= GetMyPos();//print(GetPlayerArmedWeapon(npcid)); local angle= atan2(-(tPos.x-Pos.x), tPos.y-Pos.y); /* This will always work*/ local aimPos= Vector(tPos.x,tPos.y,tPos.z); local aimDir = Vector( PI, PI, -angle ); //local aimPos= Pos; //local aimDir= GetAimDir( aimPos, tPos ); local keys= KEY_ONFOOT_FIRE + CONSTANTS.IS_FIRST_PERSON ; SetLocalValue(I_KEYS, keys ); local ammo = GetLocalValue(I_CURWEP_AMMO ); SendOnFootSyncData( keys, Pos.x,Pos.y, Pos.z, angle, GetPlayerHealth( npcid ), GetPlayerArmour( npcid ), GetPlayerArmedWeapon( npcid ),ammo, 0.0, 0.0, 0.0, aimPos.x, aimPos.y, aimPos.z, PI, PI, -angle, false, isReloading ); } //Sniper Rifle function SnipeAt(tPos, isReloading=false, isHeadShot=false) { if(isHeadShot)tPos.z+=0.627299; local Pos= GetMyPos(); local aimPos = Vector( Pos.x, Pos.y, Pos.z-0.2); local angle= atan2(-(tPos.x-aimPos.x), tPos.y-aimPos.y); local alpha = atan2( tPos.z - aimPos.z, sqrt( pow( tPos.y - aimPos.y, 2 ) + pow( tPos.x - aimPos.x , 2 ) ) ); local dx = cos(alpha) * sin(-angle); local dy = cos(alpha) * cos(-angle); local dz = sin(alpha); local keys= 577 ; local ammo = GetLocalValue(I_CURWEP_AMMO ); aimPos+=Vector( dx, dy, dz); SendOnFootSyncData( keys, Pos.x,Pos.y, Pos.z, angle, GetPlayerHealth( npcid ), GetPlayerArmour( npcid ), GetPlayerArmedWeapon( npcid ),ammo, 0.0, 0.0, 0.0, aimPos.x, aimPos.y, aimPos.z, dx, dy, dz, false, isReloading ); SetTimerEx("FireSniperRifle", 60, 1, GetPlayerArmedWeapon(npcid), Pos.x, Pos.y, Pos.z, 16.0*dx, 16.0*dy, 16.0*dz ); } function DoMeleeAttack( pid ) { local ppos = GetPlayerPos( pid ); local mpos = GetMyPos( ); local angle = atan2( - (ppos.x- mpos.x), ppos.y-mpos.y); SetLocalValue( F_ANGLE , angle ); SetLocalValue( I_KEYS, 576 ); SendOnFootSyncDataLV(); } function AttackPrimaryTarget() { /*if(myTarget==null) return; local pid = myTarget;*/ if( Enemies.len() == 0 ) return; local pid = Enemies[0]; //Primary Target at position Zero if(IsPlayerStreamedIn(pid) && GetPlayerState(pid)!=PLAYER_STATE_WASTED) { local pos = GetPlayerPos(pid); if(GetDistanceFromMeToPoint(pos) < myWeapon.range ) { if( myWeapon.weaponid <= 11 ) { DoMeleeAttack( pid ); } else if( myWeapon.weaponid>=17 && myWeapon.weaponid<= 29 ) { if( ! myWeapon.isreloading ) { local t=GetTickCount(); if( (t - myWeapon.lastfired) >= ( myWeapon.firingrate) ) //time comparison in milliseconds { SetLocalValue(I_CURWEP_AMMO, GetLocalValue(I_CURWEP_AMMO)-1 > 0?GetLocalValue(I_CURWEP_AMMO)-1 :1); if(myWeapon.weaponid<=27) ShootAt( pos ); else SnipeAt( pos, false, true ); myWeapon.shotsfired++; if(myWeapon.shotsfired % myWeapon.clipsize == 0 ) myWeapon.isreloading=true; myWeapon.lastfired = GetTickCount(); }else { //Send a packet, same as last one. if(myWeapon.weaponid<= 27) SendOnFootSyncDataLV(); } }else { //Weapon is reloading if( (GetTickCount() - myWeapon.lastfired) >= (myWeapon.reloadingtime) ) { //reloading done ! myWeapon.isreloading=false; SetLocalValue(I_CURWEP_AMMO, GetLocalValue(I_CURWEP_AMMO)-1 > 0?GetLocalValue(I_CURWEP_AMMO)-1 :1); if(myWeapon.weaponid<=27) ShootAt( pos ); else SnipeAt( pos, false, true ); myWeapon.shotsfired++; if( (myWeapon.shotsfired % myWeapon.clipsize) == 0 ) myWeapon.isreloading=true; }else { //still reloading... if(myWeapon.weaponid<= 27) ShootAt( pos, true ); } } } }else { SetLocalValue(I_KEYS, 0 ); //Face NPC towards player if near if( GetDistanceFromMeToPoint( pos ) < 30 ) { local turnhead = WhereIsPlayer( pid ); if( abs( turnhead ) > 0.1 ) SetLocalValue( F_ANGLE, atan2(-(pos.x-GetMyPosX()), pos.y-GetMyPosY()) ); } SendOnFootSyncDataLV(); } } } function AddEnemy( pid ) { if(Enemies.find( pid ) == null ) Enemies.push( pid ); } function SetTargetPlayerForShooting(pid, weaponid=-1) { weaponid= weaponid==-1?PrimaryWeapon:weaponid; if( Enemies.find( pid ) != null ) { //If it is at index 0? Enemies.remove( Enemies.find( pid ) ); Enemies.insert( 0, pid ); }else Enemies.insert(0, pid ); //Set as primary target. (index 0) local wid = GetPlayerSlotWeapon( npcid, GetSlotFromWeaponId(weaponid) ); local s = SetLocalValue(I_CURWEP, wid );//s=true means server given that weapon already if(!s)return ; if( wid != 0 ) { local range = Weapon[wid].range; local clipsize = Weapon[wid].clipsize; //30 local reloadingtime = Weapon[wid].reload; //1000 ms local firingrate = Weapon[wid].firingrate; //150 ms myWeapon = WEPDATA( wid, range, clipsize, reloadingtime, firingrate ) }else myWeapon = WEPDATA( 0, 2.4, 250, 400, 1000 ); if(!timer) { timer = SetTimerEx( "AttackPrimaryTarget", 60, 0 ); print(format("Weapon: %d, Range: %0.1f, Clipsize: %d, reloadingtime: %d, firingrate: %d \n", wid, myWeapon.range, myWeapon.clipsize, myWeapon.reloadingtime, myWeapon.firingrate) ); } } function UnsetTarget() { //myTarget = null; KillTimer( timer ); timer = null; return true; } function ToInteger(s) { try{return s.tointeger();}catch(e){return null;} } function OnClientMessage(r,g,b,message) { local hex = ( r << 16 ) + ( g << 8 ) + b; switch(hex) { case SERVER.ALLOCATE_TARGET: local pid = ToInteger(message); if( pid!=null && IsPlayerConnected( pid )) { SetTargetPlayerForShooting( pid ); } break; case SERVER.DEALLOCATE_TARGET: local pid = ToInteger(message); if( pid!=null && IsPlayerConnected( pid )) { if( Enemies.find( pid ) != null ) Enemies.remove( Enemies.find( pid ) ); } break; case SERVER.SHOT_BY_WEAPON://ClientMessage("pid damage bodypart boolheadshot",r,g,b); //If headshot, it assumes npc is shot down by ruger or etc.( and not with colt 75) //print("data arrived\n"); local idx= [0]; local plrid = strtok( message, idx ); local weapon = strtok( message, idx ); local damage = strtok( message, idx ); local bodypart = strtok( message, idx ); try { plrid = plrid.tointeger(); bodypart = bodypart.tointeger() ; damage = damage.tointeger() ; weapon = weapon.tointeger() ; }catch(e){ break; } PlayerAttackedNPC( plrid, bodypart, damage, weapon); break; case SERVER.COMPILE_SCRIPT: try { local script = compilestring(message ); script(); }catch(e) { print(e); } break; case SERVER.NPC_COMMUNICATION: { local idx=[0]; local cmd = strtok( message, idx ); switch( cmd ) { case "ATTACKED" : { local npcid = strtok( message, idx ); local plrid = strtok( message, idx ); //if(myTarget==null) try{ plrid = plrid.tointeger(); }catch(e){ return;} if(Enemies.len()==0) { if(IsPlayerConnected( plrid )) { SetTargetPlayerForShooting( plrid ); } }else if( Enemies.find( plrid ) == null ) { //Enemy of another npc is still enemey..right? Enemies.push( plrid ); } } break; } } break; case SERVER.CALL_REMOTE_FUNCTION: { local idx=[0]; local funcname = strtok( message, idx ); local f = strtok( message, idx ); local params=[]; //empty array for passing arguments if( funcname!="" ) { try { for(local i=0; i < f.len(); i++ ) { local parameter = strtok( message, idx ); if( parameter=="" && f[i]!='s')break; switch( f[i] ) { case 'f': params.push( parameter.tofloat() ); break; case 'i': params.push( parameter.tointeger() ); break; case 's': params.push( parameter ); break; default: return; } } }catch(e) { print(e); } if( rawin( funcname ) ) { local functionObj = rawget( funcname ); params.insert(0, this); functionObj.acall(params ); } } } break; default: break; } } function PlayerAttackedNPC(plrid, bodypart, damage, weapon) { local health= GetPlayerHealth(npcid); local armour= GetPlayerArmour(npcid); if(armour>0) armour-=damage; else health-=damage; if(armour< 0) { health+=armour; armour=0; } if( health <= 0 || damage == 255 ) { local anim; switch( bodypart ) { case BODYPART_BODY: anim = 0xd; break; case BODYPART_HEAD: anim = 17; break; case BODYPART_TORSO : anim = 13; break; case BODYPART_LEFTARM : anim = 19; break; case BODYPART_RIGHTARM : anim = 20; break; case BODYPART_LEFTLEG: anim = 21; break; case BODYPART_RIGHTLEG : anim = 22; break; default: anim = 0xd;break; } //clear targets //myTarget = null; Enemies.clear(); SendShotInfo( bodypart, anim ); SetTimerEx("SendOnFootSyncDataLV", 100, 1); SetTimerEx("SendDeathInfo", 2000, 1, weapon, plrid, bodypart ); }else { SetLocalValue( I_HEALTH, health ); SetLocalValue( I_ARMOUR, armour ); SendOnFootSyncDataLV(); //Small Artificial intelligence if( CONSTANTS.FIGHTBACK && Enemies.len()==0 ) { if( weapon<= 11 ) { local wep = GetPlayerSlotWeapon( npcid, 1 ); if(wep == 0 )wep= GetPlayerSlotWeapon( npcid, 0 );//Brass knuckes available?? SetTargetPlayerForShooting( plrid, wep ); }else { SetTargetPlayerForShooting( plrid ); SendCommand("NPC_COMMU ATTACKED "+npcid+" "+ plrid ); } } } } function OnSniperRifleFired( playerid, weaponid, x, y, z, dx, dy, dz ) { local outputstr=format("Sniper Rifle, x,y,z= %f, %f, %f and dir = ( %f, %f, %f )\n", x,y,z, dx/16, dy/16, dz/16); local command= format("PLOS %d %.4f %.4f %.4f %.4f %.4f %.4f %d %d", playerid, x, y, z, dx/16, dy/16, dz/16, 2, weaponid ); SendCommand( command ); } //Attack every player coming into that area. function OnPlayerStreamIn(playerid) { if( GetPlayerName(playerid).len()!=2 ) { if(GetPlayerState(npcid) != PLAYER_STATE_WASTED ) { if( Enemies.len() == 0 ) SetTargetPlayerForShooting( playerid ); else AddEnemy( playerid ); } } } function OnPlayerUpdate( playerid, updateType ) { if(updateType == PLAYERUPDATE_NORMAL || updateType == PLAYERUPDATE_AIMING) { if( (GetPlayerKeys(playerid) & 576)== 576 ) //On server and player on same pc, health may be reduced before hit { local mypos = GetMyPos(); local plrangle = GetPlayerFacingAngle( playerid ); local plrpos = GetPlayerPos( playerid ); local _angle = atan2( -(mypos.x - plrpos.x ), mypos.y - plrpos.y ); if( _angle < 0 && plrangle > 0 )_angle+=2*PI; else if( plrangle <0 && _angle > 0 )_angle-=2*PI; if( abs( _angle - plrangle ) < PI/2 ) //take PI/2. 0 means straight to npc { //player is facing npc local weapon = GetPlayerArmedWeapon( playerid ); if( weapon >=0 && weapon <= 11 ) { local range = Weapon[ weapon ].range ; if( GetDistanceFromMeToPoint( plrpos ) <= range ) { //player is in range of weapon local damage = Weapon[ weapon ].damage /5 ; //many packets arrive. so damage must be controlled PlayerAttackedNPC(playerid, 0, damage, weapon); } } } } } } function strtok(string,array) { local index=array[0]; local length=string.len(); /* Skip the leading white space */ while(index<length && string[index]<=' ') index++; local result=""; local offset = index; while( index < length && string[index] > ' ' && index-offset < 20-1) { result+=string.slice(index,index+1); index++; } array[0]=index; return result; }
File: npcscripts/weapons.nut
class WEAPON { range=80; firingrate=250; //interval ms reload=500; //ms clipsize=30; damage=25; constructor( range, firingrate, reload, clipsize, damage) { this.range= range; this.firingrate=firingrate; this.reload=reload; this.clipsize=clipsize; this.damage=damage; } }; Weapon<-array( 34 ); //minigun id is 33. plus 1 for weapon 0 Weapon[0] = WEAPON(2.4,250, 100, 1000, 8); Weapon[1] = WEAPON(2.0,450, 100, 1000, 16); Weapon[2] = WEAPON(1.8,250, 100, 1000, 45); Weapon[3] = WEAPON(1.5,450, 100, 1000, 21); Weapon[4] = WEAPON(1.5,450, 100, 1000, 21); Weapon[5] = WEAPON(1.8,250, 100, 1000, 21); Weapon[6] = WEAPON(2.0,450, 100, 1000, 21); Weapon[7] = WEAPON(1.5,450, 100, 1000, 21); Weapon[8] = WEAPON(1.9,250, 100, 1000, 24); Weapon[9] = WEAPON(2.0,250, 100, 1000, 24); Weapon[10] = WEAPON(2.1,250, 100, 1000, 30); Weapon[11] = WEAPON(1.7,900, 100, 1000, 35); Weapon[12] = WEAPON(30.0,100, 1, 1, 75); Weapon[13] = WEAPON(30.0,100, 1, 1, 75); Weapon[14] = WEAPON(30.0,100, 1, 1, 75); Weapon[15] = WEAPON(25.0,100, 1, 1, 75); Weapon[16] = WEAPON(30.0,100, 1, 1, 75); Weapon[17] = WEAPON(30.0,210, 450, 17, 25); Weapon[18] = WEAPON(30.0,630, 1000, 6, 135); Weapon[19] = WEAPON(20.0,250, 450, 1, 80); Weapon[20] = WEAPON(15.0,250, 750, 7, 100); Weapon[21] = WEAPON(10.0,250, 250, 1, 120); Weapon[22] = WEAPON(30.0,119, 500, 50, 20); Weapon[23] = WEAPON(45.0,59, 500, 30, 20); Weapon[24] = WEAPON(30.0,60, 500, 30, 15); Weapon[25] = WEAPON(45.0,90, 500, 30, 35); Weapon[26] = WEAPON(90.0,90, 1000, 30, 40); Weapon[27] = WEAPON(90.0,150, 1000, 30, 35); Weapon[28] = WEAPON(100.0,89, 1401, 1, 125); Weapon[29] = WEAPON(100.0,180, 1401, 7, 125); Weapon[30] = WEAPON(55.0,100, 1200, 1, 75); Weapon[31] = WEAPON(5.1,100, 100, 500, 25); Weapon[32] = WEAPON(75.0,119, 500, 100, 130); Weapon[33] = WEAPON(75.0,30, 350, 500, 140); /* Generated from server side code function makecode() { for(local i=0; i< 34; i++) { local string=format("Weapon[%d] = WEAPON(%0.1f,%d, %d, %d, %d);", i, GetWeaponDataValue( i, 2 ), GetWeaponDataValue( i, 3 ), GetWeaponDataValue( i, 4 ), GetWeaponDataValue( i, 5 ), GetWeaponDataValue( i, 6 ) ); print(string); } } */