Attack Script: Difference between revisions
Jump to navigation
Jump to search
No edit summary |
No edit summary |
||
| Line 12: | Line 12: | ||
|- | |- | ||
|} | |} | ||
==The npc-Script== | |||
attack_script.nut | |||
<source lang="lua"> | |||
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; | |||
} | |||
</source> | |||
weapons.nut | |||
<source lang="lua"> | |||
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); | |||
} | |||
} | |||
*/ | |||
</source> | |||
</big> | </big> | ||
Revision as of 08:50, 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
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;
}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);
}
}
*/