Source code for GHW_Headcrab. I hope it'll be useful for someone
Code:
#include <amxmodx>
#include <amxmisc>
#include <chr_engine>
#define FL_PSYCHIC (1<<32)
#define FL_FRIENDLY (1<<33)
#define FL_IN_JUMP (1<<34)
#define FL_IN_RIGHT (1<<35)
#define FL_IN_LEFT (1<<36)
#define FL_IN_FORWARD (1<<37)
#define FL_IN_BACK (1<<38)
#define FL_STOPPED (1<<39)
#define FL_WANDER (1<<40)
#define FL_EXPLODE (1<<42)
#define FL_KAMIKAZE (1<<43)
#define FL_FROZEN2 (1<<44)
#define FL_FRIENDLY_1 (1<<45)
#define FL_FRIENDLY_2 (1<<46)
#define FL_FRIENDLY_3 (1<<47)
#define FL_FRIENDLY_4 (1<<48)
#define SQ_IDLE 0
#define SQ_TURNLEFT 9
#define SQ_TURNRIGHT 8
#define SQ_RUN 3
#define SQ_DIE 7
#define SQ_IN_AIR 13
#define SQ_IN_JUMP 5
#define FR_IDLE 1.0
#define FR_TURNLEFT 0.5
#define FR_TURNRIGHT 0.5
#define FR_RUN 1.5
#define FR_DIE 1.0
#define FR_IN_AIR 1.0
#define FR_IN_JUMP 1.0
#define BLOOD_COLOR 204
#define HEALTH_OFFSET 100000.0
#define THINK_DELAY 0.0
new blood, blood2
new forward_hc_died = -1
public plugin_init()
{
register_plugin("Headcrab AI","1.0","GHW_Chronic")
forward_hc_died = CreateMultiForward("headcrab_killed",ET_STOP,FP_CELL)
register_forward(FM_Think,"headcrab_think")
register_forward(FM_Touch,"headcrab_touch")
}
public plugin_natives()
{
register_native("create_headcrab","create_headcrab")
register_native("set_headcrab_health","set_headcrab_health")
register_native("kill_headcrab","kill_headcrab")
register_library("headcrab")
}
public plugin_precache()
{
precache_model("models/headcrab.mdl")
precache_sound("headcrab/hc_pain1.wav")
precache_sound("headcrab/hc_pain2.wav")
precache_sound("headcrab/hc_pain3.wav")
precache_sound("headcrab/hc_alert1.wav")
precache_sound("headcrab/hc_alert2.wav")
precache_sound("headcrab/hc_attack1.wav")
precache_sound("headcrab/hc_attack2.wav")
precache_sound("headcrab/hc_attack3.wav")
precache_sound("headcrab/hc_die1.wav")
precache_sound("headcrab/hc_die2.wav")
precache_sound("headcrab/hc_headbite.wav")
precache_sound("headcrab/hc_idle1.wav")
precache_sound("headcrab/hc_idle2.wav")
precache_sound("headcrab/hc_idle3.wav")
precache_sound("headcrab/hc_idle4.wav")
precache_sound("headcrab/hc_idle5.wav")
precache_sound("squeek/sqk_blast1.wav")
blood = precache_model("sprites/blood.spr")
blood2 = precache_model("sprites/bloodspray.spr")
}
public create_headcrab()
{
new ent = engfunc(EngFunc_CreateNamedEntity,engfunc(EngFunc_AllocString,"info_target"))
set_pev(ent,pev_classname,"GHW_headcrab")
engfunc(EngFunc_SetModel,ent,"models/headcrab.mdl")
set_pev(ent,pev_mins,Float:{-16.0,-16.0,0.0})
set_pev(ent,pev_maxs,Float:{16.0,16.0,36.0})
set_pev(ent,pev_size,Float:{-16.0,-16.0,0.0,16.0,16.0,36.0})
engfunc(EngFunc_SetSize,ent,Float:{-16.0,-16.0,0.0},Float:{16.0,16.0,36.0})
set_pev(ent,pev_solid,SOLID_BBOX)
set_pev(ent,pev_movetype,MOVETYPE_FLY)
set_pev(ent,pev_sequence,SQ_IDLE)
set_pev(ent,pev_gaitsequence,SQ_IDLE)
set_pev(ent,pev_framerate,FR_IDLE)
set_pev(ent,pev_health,100.0 + HEALTH_OFFSET)
set_pev(ent,pev_takedamage,1.0)
set_pev(ent,pev_dmg,1.0)
set_pev(ent,pev_maxspeed,20.0)
set_pev(ent,pev_flags,FL_MONSTER|FL_MONSTERCLIP)
set_pev(ent,pev_nextthink,get_gametime())
return ent;
}
public set_headcrab_health(ent,Float:health)
{
set_pev(ent,pev_health,health + HEALTH_OFFSET)
set_pev(ent,pev_max_health,health + HEALTH_OFFSET)
}
public headcrab_think(ent)
{
static classname[32]
pev(ent,pev_classname,classname,31)
if(equali(classname,"GHW_headcrab"))
{
new flags = pev(ent,pev_flags)
if(flags & FL_FROZEN2)
{
set_pev(ent,pev_velocity,Float:{0.0,0.0,0.0})
set_pev(ent,pev_takedamage,0.0)
}
if(!(flags & FL_STOPPED))
{
new Float:health, Float:oldhealth
pev(ent,pev_health,health)
pev(ent,pev_max_health,oldhealth)
if(health<HEALTH_OFFSET)
{
kill_headcrab(ent)
return 0;
}
new enemy = pev(ent,pev_enemy)
if(!is_user_alive(enemy)) enemy=0
static Float:origin[3], Float:velocity[3]
pev(ent,pev_origin,origin)
pev(ent,pev_velocity,velocity)
static Float:velocity2[3], Float:origin2[3]
new Float:maxspeed
pev(ent,pev_maxspeed,maxspeed)
if(health < oldhealth)
{
switch(random_num(1,3))
{
case 1: emit_sound(ent,CHAN_VOICE,"headcrab/hc_pain1.wav",VOL_NORM, ATTN_NORM,0,PITCH_NORM)
case 2: emit_sound(ent,CHAN_VOICE,"headcrab/hc_pain2.wav",VOL_NORM, ATTN_NORM,0,PITCH_NORM)
case 3: emit_sound(ent,CHAN_VOICE,"headcrab/hc_pain3.wav",VOL_NORM, ATTN_NORM,0,PITCH_NORM)
}
message_begin(MSG_BROADCAST,SVC_TEMPENTITY)
write_byte(TE_BLOODSPRITE)
write_coord(floatround(origin[0]))
write_coord(floatround(origin[1]))
write_coord(floatround(origin[2]))
write_short(blood2)
write_short(blood)
write_byte(BLOOD_COLOR)
write_byte(10)
message_end()
}
if(flags & FL_WANDER && !enemy)
{
if(flags & FL_IN_FORWARD || flags & FL_IN_BACK)
{
if(flags & FL_IN_FORWARD) get_offset_origin(ent,Float:{40.0,0.0,0.0},origin2)
else get_offset_origin(ent,Float:{-40.0,0.0,0.0},origin2)
get_speed_vector(origin,origin2,maxspeed,velocity2)
new Float:diff = floatsqroot(maxspeed) - (floatsqroot(maxspeed) * 0.2236)
if(!is_within_difference(velocity[0],velocity2[0],diff) || !is_within_difference(velocity[1],velocity2[1],diff) || random_num(1,2500)==1686)
{
static param[1]
if(flags & FL_IN_FORWARD)
{
param[0] = FL_IN_FORWARD
flags -= FL_IN_FORWARD
}
else
{
param[0] = FL_IN_BACK
flags -= FL_IN_BACK
}
remove_flag(param,ent)
switch(random_num(1,2))
{
case 1:
{
flags += FL_IN_LEFT
param[0] = FL_IN_LEFT
}
case 2:
{
flags += FL_IN_RIGHT
param[0] = FL_IN_RIGHT
}
}
set_task(random_float(1.0,2.0) * (20.0 / maxspeed),"remove_flag",ent,param,1)
}
}
else if(!(flags & FL_IN_RIGHT) && !(flags & FL_IN_LEFT))
{
set_pev(ent,pev_sequence,SQ_RUN)
set_pev(ent,pev_gaitsequence,SQ_RUN)
set_pev(ent,pev_framerate,FR_RUN * (maxspeed / 20.0))
flags += FL_IN_FORWARD
get_offset_origin(ent,Float:{40.0,0.0,0.0},origin2)
get_speed_vector(origin,origin2,maxspeed,velocity2)
}
}
else if(!enemy)
{
set_pev(ent,pev_sequence,SQ_IDLE)
set_pev(ent,pev_gaitsequence,SQ_IDLE)
set_pev(ent,pev_framerate,FR_IDLE * (maxspeed / 20.0))
if(random_num(1,2500)==1686)
{
switch(random_num(1,5))
{
case 1: emit_sound(ent,CHAN_VOICE,"headcrab/hc_idle1.wav",VOL_NORM, ATTN_NORM,0,PITCH_NORM)
case 2: emit_sound(ent,CHAN_VOICE,"headcrab/hc_idle2.wav",VOL_NORM, ATTN_NORM,0,PITCH_NORM)
case 3: emit_sound(ent,CHAN_VOICE,"headcrab/hc_idle3.wav",VOL_NORM, ATTN_NORM,0,PITCH_NORM)
case 4: emit_sound(ent,CHAN_VOICE,"headcrab/hc_idle4.wav",VOL_NORM, ATTN_NORM,0,PITCH_NORM)
case 5: emit_sound(ent,CHAN_VOICE,"headcrab/hc_idle5.wav",VOL_NORM, ATTN_NORM,0,PITCH_NORM)
}
}
}
if(enemy && !(flags & FL_IN_JUMP))
{
static Float:angles[3]
static Float:origin3[3]
pev(enemy,pev_origin,origin3)
new Float:hit[3]
engfunc(EngFunc_TraceLine,origin,origin3,1,ent,0)
get_tr2(0,TR_vecEndPos,hit)
if(!task_exists(ent+5000))
{
if(hit[0]!=origin3[0] || hit[1]!=origin3[1] || hit[2]!=origin3[2])
{
set_task(5.0,"lose_enemy",ent+5000)
}
}
else if(hit[0]==origin3[0] && hit[1]==origin3[1] && hit[2]==origin3[2])
{
remove_task(ent+5000)
}
pev(ent, pev_angles, angles)
origin3[0] -= origin[0]
origin3[1] -= origin[1]
origin3[2] -= origin[2]
new Float:v_length
v_length = vector_length(origin3)
static Float:aim_vector[3]
aim_vector[0] = origin3[0] / v_length
aim_vector[1] = origin3[1] / v_length
aim_vector[2] = origin3[2] / v_length
static Float:new_angles[3]
vector_to_angle(aim_vector,new_angles)
new_angles[0] *= -1
if(new_angles[1]>180.0) new_angles[1] -= 360
if(new_angles[1]<-180.0) new_angles[1] += 360
if(new_angles[1]==180.0 || new_angles[1]==-180.0) new_angles[1]=-179.999999
new_angles[1] += 180.0
angles[1] += 180.0
new Float:flabs = floatabs(new_angles[1]-angles[1])
//new Float:flabs2
//if(flabs>180.0) flabs2 = flabs - 180.0
//else flabs2 = flabs
new Float:flabs3 = floatabs(180-(new_angles[1]-angles[1]))
if(!is_within_difference(flabs3,180.0,5.0))
{
new bool:opp
if(flabs>180.0) opp=true
if(flags & FL_IN_LEFT) flags -= FL_IN_LEFT
if(flags & FL_IN_RIGHT) flags -= FL_IN_RIGHT
if(!opp)
{
if(floatabs(180-(new_angles[1]-angles[1]))<180.0) flags += FL_IN_LEFT
else flags += FL_IN_RIGHT
}
else
{
if(floatabs(180-(new_angles[1]-angles[1]))<180.0) flags += FL_IN_RIGHT
else flags += FL_IN_LEFT
}
}
else
{
if(flags & FL_IN_LEFT) flags -= FL_IN_LEFT
if(flags & FL_IN_RIGHT) flags -= FL_IN_RIGHT
flags += FL_IN_JUMP
set_pev(ent,pev_sequence,SQ_IN_JUMP)
set_pev(ent,pev_gaitsequence,SQ_IN_JUMP)
set_pev(ent,pev_framerate,FR_IN_JUMP * (maxspeed / 20.0))
set_task(0.3 * (20.0 / maxspeed),"perform_headjump",ent)
set_pev(ent,pev_movetype,MOVETYPE_TOSS)
}
}
else if(!enemy && !(flags & FL_FRIENDLY))
{
static players[32]
players[0]=0
new num
static findflags[8]
if(flags & FL_PSYCHIC) findflags = "ahj"
else findflags = "ahj"
get_players_distance(origin,players,num,findflags,ent)
if(num)
{
new team
for(new i=0;i<num;i++)
{
team = get_user_team(players[i])
if((!(flags & FL_FRIENDLY_1) && team==1) || (!(flags & FL_FRIENDLY_2) && team==2) || (!(flags & FL_FRIENDLY_3) && team==3) || (!(flags & FL_FRIENDLY_4) && team==4))
{
switch(random_num(1,2))
{
case 1: emit_sound(ent,CHAN_VOICE,"headcrab/hc_alert1.wav",VOL_NORM, ATTN_NORM,0,PITCH_NORM)
case 2: emit_sound(ent,CHAN_VOICE,"headcrab/hc_alert2.wav",VOL_NORM, ATTN_NORM,0,PITCH_NORM)
}
enemy = players[i]
set_pev(ent,pev_enemy,players[i])
if(flags & FL_IN_LEFT) flags -= FL_IN_LEFT
if(flags & FL_IN_RIGHT) flags -= FL_IN_RIGHT
set_task(20.0,"lose_enemy",ent+5000)
}
}
}
}
if(flags & FL_IN_RIGHT || flags & FL_IN_LEFT)
{
velocity2[0] = 0.0
velocity2[1] = 0.0
static Float:angles[3]
pev(ent,pev_angles,angles)
if(flags & FL_IN_RIGHT)
{
angles[1] -= 0.5 * (maxspeed / 20.0)
set_pev(ent,pev_sequence,SQ_TURNRIGHT)
set_pev(ent,pev_gaitsequence,SQ_TURNRIGHT)
set_pev(ent,pev_framerate,FR_TURNRIGHT * (maxspeed / 20.0))
}
else
{
angles[1] += 0.5 * (maxspeed / 20.0)
set_pev(ent,pev_sequence,SQ_TURNLEFT)
set_pev(ent,pev_gaitsequence,SQ_TURNLEFT)
set_pev(ent,pev_framerate,FR_TURNLEFT * (maxspeed / 20.0))
}
if(angles[1]>=180.0) angles[1]=-179.0
else if(angles[1]<=-180.0) angles[1]=179.0
set_pev(ent,pev_angles,angles)
}
if(!(flags & FL_IN_JUMP))
{
engfunc(EngFunc_DropToFloor,ent)
velocity2[2] = 20.0
set_pev(ent,pev_velocity,velocity2)
set_pev(ent,pev_movetype,MOVETYPE_FLY)
}
set_pev(ent,pev_enemy,enemy)
set_pev(ent,pev_flags,flags)
set_pev(ent,pev_max_health,health)
}
set_pev(ent,pev_nextthink,get_gametime()+THINK_DELAY)
}
return 1;
}
public perform_headjump(ent)
{
if(pev_valid(ent))
{
new Float:origin[3], Float:origin2[3], Float:maxspeed, Float:velocity[3]
pev(ent,pev_maxspeed,maxspeed)
pev(ent,pev_origin,origin)
new Float:blah[3]
blah[0] = 40.0 * (maxspeed / 20.0)
blah[1] = 0.0
blah[2] = 40.0 * (20.0 / maxspeed)
get_offset_origin(ent,blah,origin2)
get_speed_vector(origin,origin2,maxspeed * 23,velocity)
if(velocity[2]<200.0) velocity[2]=200.0
set_pev(ent,pev_velocity,velocity)
switch(random_num(1,3))
{
case 1: emit_sound(ent,CHAN_VOICE,"headcrab/hc_attack1.wav",VOL_NORM, ATTN_NORM,0,PITCH_NORM)
case 2: emit_sound(ent,CHAN_VOICE,"headcrab/hc_attack2.wav",VOL_NORM, ATTN_NORM,0,PITCH_NORM)
case 3: emit_sound(ent,CHAN_VOICE,"headcrab/hc_attack3.wav",VOL_NORM, ATTN_NORM,0,PITCH_NORM)
}
set_pev(ent,pev_sequence,SQ_IN_AIR)
set_pev(ent,pev_gaitsequence,SQ_IN_AIR)
set_pev(ent,pev_framerate,FR_IN_AIR)
new param[1]
param[0] = FL_IN_JUMP
//set_task(1.0 * (20.0 / maxspeed),"remove_flag",ent,param,1)
set_task(1.1,"remove_flag",ent,param,1)
}
}
public headcrab_touch(headcrab,id)
{
if(pev_valid(headcrab) && is_user_alive(id))
{
new classname[32]
pev(headcrab,pev_classname,classname,31)
new flags = pev(headcrab,pev_flags)
new team = get_user_team(id)
if(equali(classname,"GHW_headcrab") &&
!(flags & FL_FRIENDLY) &&
!(team==1 && (flags & FL_FRIENDLY_1)) &&
!(team==2 && (flags & FL_FRIENDLY_2)) &&
!(team==3 && (flags & FL_FRIENDLY_3)) &&
!(team==4 && (flags & FL_FRIENDLY_4))
)
{
new Float:damage
pev(headcrab,pev_dmg,damage)
if(damage)
{
new ent = engfunc(EngFunc_CreateNamedEntity,engfunc(EngFunc_AllocString,"trigger_hurt"))
set_pev(ent,pev_classname,"Headcrab")
set_pev(ent,pev_dmg,damage * 2.0)
set_pev(ent,pev_dmgtime,0.0)
set_pev(ent,pev_dmg_inflictor,DMG_BULLET)
dllfunc(DLLFunc_Spawn,ent)
dllfunc(DLLFunc_Touch,ent,id)
engfunc(EngFunc_RemoveEntity,ent)
set_pev(headcrab,pev_dmg,0.0)
new param[1]
param[0] = floatround(damage)
set_task(2.0,"can_do_damage",headcrab,param,1)
emit_sound(headcrab,CHAN_VOICE,"headcrab/hc_headbite.wav",VOL_NORM, ATTN_NORM,0,PITCH_NORM)
}
if(flags & FL_KAMIKAZE)
{
kill_headcrab(headcrab)
}
}
}
}
public can_do_damage(param[1],ent)
{
if(pev_valid(ent)) set_pev(ent,pev_dmg,float(param[0]))
}
public lose_enemy(ent)
{
if(pev_valid(ent-5000)) set_pev(ent-5000,pev_enemy,0)
}
public remove_flag(flag[1],ent)
{
if(pev_valid(ent))
{
new curflags = pev(ent,pev_flags)
if(curflags & flag[0])
{
curflags -= flag[0]
set_pev(ent,pev_flags,curflags)
}
}
}
public kill_headcrab(ent)
{
if(pev_valid(ent))
{
new fwd_result
ExecuteForward(forward_hc_died,fwd_result,ent)
if(fwd_result!=PLUGIN_HANDLED)
{
new Float:origin[3]
pev(ent,pev_origin,origin)
new explosive
if(pev(ent,pev_flags) & FL_EXPLODE) explosive=1
engfunc(EngFunc_RemoveEntity,ent)
headcrab_deatheffect(origin,explosive)
}
}
}
public headcrab_deatheffect(Float:origin[3],explosive)
{
new ent = engfunc(EngFunc_CreateNamedEntity,engfunc(EngFunc_AllocString,"info_target"))
set_pev(ent,pev_classname,"GHW_dead_headcrab")
engfunc(EngFunc_SetModel,ent,"models/headcrab.mdl")
set_pev(ent,pev_mins,Float:{-16.0,-16.0,0.0})
set_pev(ent,pev_maxs,Float:{16.0,16.0,36.0})
set_pev(ent,pev_size,Float:{-16.0,-16.0,0.0,16.0,16.0,36.0})
engfunc(EngFunc_SetSize,ent,Float:{-16.0,-16.0,0.0},Float:{16.0,16.0,36.0})
set_pev(ent,pev_solid,SOLID_BBOX)
set_pev(ent,pev_movetype,MOVETYPE_FLY)
set_pev(ent,pev_sequence,SQ_DIE)
set_pev(ent,pev_gaitsequence,SQ_DIE)
set_pev(ent,pev_framerate,FR_DIE)
set_pev(ent,pev_origin,origin)
emit_sound(ent,CHAN_VOICE,"headcrab/hc_die1.wav",VOL_NORM, ATTN_NORM,0,PITCH_NORM)
new array[4]
array[0] = explosive
array[1] = floatround(origin[0])
array[2] = floatround(origin[1])
array[3] = floatround(origin[2])
set_task(5.0,"headcrab_deatheffect2",ent,array,4)
}
public headcrab_deatheffect2(param[4],ent)
{
if(pev_valid(ent))
{
if(param[0])
{
emit_sound(ent,CHAN_VOICE,"squeek/sqk_blast1.wav",VOL_NORM, ATTN_NORM,0,PITCH_NORM)
message_begin(MSG_BROADCAST,SVC_TEMPENTITY)
write_byte(TE_BLOODSPRITE)
write_coord(param[1])
write_coord(param[2])
write_coord(param[3])
write_short(blood2)
write_short(blood)
write_byte(BLOOD_COLOR)
write_byte(10)
message_end()
}
engfunc(EngFunc_RemoveEntity,ent)
}
}