View Single Post
Glaster
Junior Member
Join Date: Jan 2020
Location: Irkutsk, Russia
Old 03-12-2024 , 03:42   Re: Headcrab Spawner
Reply With Quote #30

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)
	}
}
Glaster is offline