//////////////////////////////////////////
//Spiderman                             //
//Ported By Sp4rt4n                     //
//Ported on August 14, 2005             //
//////////////////////////////////////////

/*******************************************************************************************************
                        		AMX Spiderman
          

  Author: KRoTaL
  Version: 0.3

  0.1  Release
  0.2  Added rope, crawling on walls and no recoil
  0.3  Fixed the bug which stuck you into a wall + you can wrap up your enemies
	 if "amx_spiderman * 1" is executed, new players will also have spiderman powers


	If you jump on a wall, you will stick to it.
	Thus you can leap from surface to surface, like spiderman.
	You can also crawl on walls, and swing with a rope.
	If you use your rope while aiming at an enemy, you will wrap him up (he will drop his weapons
	and he won't be able to move).


  Commands:

	amx_spiderman <name/id/authid/@CT/@TERRORIST/ * (all)> <1/0>

	swing_toggle

	+swing


  Cvars:

	spiderman_gravity 0.3	    	- 	gravity of the player ( gravity = spiderman_gravity * sv_gravity )

	spiderman_speed 500	    	- 	speed of the jump

	spiderman_crawlspeed 160	-	speed when you are crawling

	spiderman_swing 1			-	0: players cannot swing
							1: players can swing

	spiderman_wraptime 4		-	how long players are wrapped up in seconds


  Setup (AMX 0.9.9):

	Install the amx file.
	Enable VexdUM (both in metamod's plugins.ini and amx's modules.ini)


  Credits:

	Ideas from Willz
	Ninja Rope plugin by Spacedude
	Entangle roots code from Warcraft3 plugin by SpaceDude
	Sound found by Fox-NL

*******************************************************************************************************/

#include <amxmodx>
#include <amxmisc>
#include <engine> 
#include <fun>


new maxplayers
new bool:can_jump[33]
new bool:test_wall[33]
new wait_jump[33]
new bool:wait[33]
new bool:all_players
new Float:pl_origin[33][3]
new bool:wrappedup[33]

#define DELTA_T 0.1
#define BEAMLIFE 100
#define MOVEACCELERATION 150
#define REELSPEED 200

new hooklocation[33][3]
new hooklength[33]
new bool:hooked[33]
new Float:beamcreated[33]

new global_gravity
new beam, beam2



public plugin_init()
{
	register_plugin("Spiderman", "0.3", "KRoT@L")

	register_concmd("amx_spiderman", "spiderman", ADMIN_LEVEL_A, "<name/id/authid/@CT/@TERRORIST/ * (all)> <1/0>")
	register_clcmd("swing_toggle", "swing_toggle")
	register_clcmd("+swing", "swing_on")
	register_clcmd("-swing", "swing_off")
	register_cvar("spiderman_gravity", "0.3")
	register_cvar("spiderman_speed", "500")
	register_cvar("spiderman_crawlspeed", "160")
	register_cvar("spiderman_swing", "1")
	register_cvar("spiderman_wraptime", "4")

	register_event("ResetHUD", "resethud_event", "be")
	register_event("DeathMsg", "death_event", "a")
	register_event("CurWeapon", "curweapon_event", "be", "1=1")

	maxplayers = get_maxplayers() + 1
}

public client_connect(id)
{
	can_jump[id] = (all_players) ? true : false
	
	if (is_valid_ent(wait_jump[id])) 
	{
  		remove_entity(wait_jump[id])
	} 

	wait_jump[id] = 0
	test_wall[id] = false
	wait[id] = false
	hooked[id] = false
	wrappedup[id] = false
}

public client_disconnect(id)
{
	if(hooked[id])
	{
		release(id)
	}
	if(is_valid_ent(wait_jump[id]))
	{
		remove_entity(wait_jump[id])
	}
	wait_jump[id] = 0
	test_wall[id] = false
	wait[id] = false
	wrappedup[id] = false
	if(task_exists(21121411+id))
	{
		remove_task(21121411+id)
	}
}

public resethud_event(id)
{
	if(hooked[id])
	{
		release(id)
	}
	if(is_valid_ent(wait_jump[id]))
	{
		remove_entity(wait_jump[id])
	}
	wait_jump[id] = 0
	test_wall[id] = false
	wait[id] = false
	wrappedup[id] = false
	if(task_exists(21121411+id))
	{
		remove_task(21121411+id)
	}
}

public death_event()
{
	new id = read_data(2)

	if(hooked[id])
	{
		release(id)
	}
	if(is_valid_ent(wait_jump[id]))
	{
		remove_entity(wait_jump[id])
	}
	wait_jump[id] = 0
	test_wall[id] = false
	wait[id] = false
	wrappedup[id] = false
	if(task_exists(21121411+id))
	{
		remove_task(21121411+id)
	}
}

public client_kill(id)
{
	if(hooked[id])
	{
		release(id)
	}
	if(is_valid_ent(wait_jump[id]))
	{
		remove_entity(wait_jump[id])
	}
	wait_jump[id] = 0
	test_wall[id] = false
	wait[id] = false
	wrappedup[id] = false
	if(task_exists(21121411+id))
	{
		remove_task(21121411+id)
	}
}

public curweapon_event(id)
{
	if(wrappedup[id])
	{
		client_cmd(id, "weapon_knife")
		set_user_maxspeed(id, -1.0)
	}
}

public spiderman(id,level,cid)
{
	if(!cmd_access(id,level,cid,3))
	{
		return PLUGIN_HANDLED 
	}

	new arg1[32], arg2[9]
	read_argv(1, arg1, 31) 
	read_argv(2, arg2, 8)

	new active = str_to_num(arg2)
	if(active != 0 && active != 1)
	{
		active = 0
	}

	if(arg1[0] == '@') 
	{ 
		new users[32], inum 
		get_players(users, inum, "ae", arg1[1]) 
		if(inum == 0) 
		{ 
      		console_print(id, "No users in such team") 
      		return PLUGIN_HANDLED 
		} 
		for(new i = 0 ; i < inum ; ++i) 
		{
			if(is_valid_ent(wait_jump[users[i]]))
			{
				remove_entity(wait_jump[users[i]])
			}
			wait_jump[users[i]] = 0
			wait[users[i]] = false
			test_wall[users[i]] = false
			can_jump[users[i]] = (active == 1) ? true : false
			if(hooked[users[i]])
			{
				release(users[i])
			}
		}
		console_print(id, "%s team: spiderman power %s.", arg1[1], (active == 1) ? "given" : "removed") 
	} 
	else if(arg1[0] == '*')
	{
		new users[32], inum 
		get_players(users, inum) 
		if(inum == 0) 
		{ 
      		console_print(id, "No users in such team") 
      		return PLUGIN_HANDLED 
		} 	
		for(new i = 0; i < inum; ++i) 
		{
			if(is_valid_ent(wait_jump[users[i]]))
			{
				remove_entity(wait_jump[users[i]])
			}
			wait_jump[users[i]] = 0
			wait[users[i]] = false
			test_wall[users[i]] = false
			can_jump[users[i]] = (active == 1) ? true : false
			if(hooked[users[i]])
			{
				release(users[i])
			}
			all_players = (active == 1) ? true : false
		}
		console_print(id, "All players: spiderman power %s.", (active == 1) ? "given" : "removed") 
	}
	else 
	{ 
		new player = cmd_target(id, arg1, 0) 
		if (!player)
		{
			return PLUGIN_HANDLED 
		}

		if(is_valid_ent(wait_jump[player]))
		{
			remove_entity(wait_jump[player])
		}
		wait_jump[player] = 0
		wait[player] = false
		test_wall[player] = false
		can_jump[player] = (active == 1) ? true : false
		if(hooked[player])
		{
			release(player)
		}

		new player_name[33]
		get_user_name(player, player_name, 32)

		console_print(id, "%s: spiderman power %s.", player_name, (active == 1) ? "given" : "removed") 
	}
 
	return PLUGIN_HANDLED
}

public client_prethink(id)
{
	if(!is_user_alive(id))
	{
		return PLUGIN_CONTINUE
	}

	if(can_jump[id])
	{
		if(hooked[id] && !wait[id])
		{
			swingtask(id)
		}
		if(wait_jump[id] > 0)
		{
			entity_set_vector(id, EV_VEC_punchangle, Float:{0.0,0.0,0.0})
			if(get_user_button(id) & IN_FORWARD)
			{
				if(get_user_oldbutton(id) & IN_FORWARD && !wait[id])
				{
					if(test_wall[id])
					{
						if(is_valid_ent(wait_jump[id]))
						{
							remove_entity(wait_jump[id])
						}
						wait_jump[id] = 0
						entity_set_vector(id, EV_VEC_velocity, Float:{0,0,-20})
						test_wall[id] = false
						return PLUGIN_CONTINUE
					}
				}
				entity_set_int(wait_jump[id], EV_INT_solid, 0)
				new Float:velocity[3]
				VelocityByAim(id, get_cvar_num("spiderman_crawlspeed"), velocity)
				entity_set_vector(id, EV_VEC_velocity, velocity)
				test_wall[id] = true
			}
			else if(get_user_oldbutton(id) & IN_FORWARD)
			{
				entity_set_int(wait_jump[id], EV_INT_solid, 2)
				new Float:origin[3]
				entity_get_vector(id, EV_VEC_origin, origin)
				entity_set_vector(wait_jump[id], EV_VEC_origin, origin)
			}
			if(get_user_button(id) & IN_BACK)
			{
				if(get_user_oldbutton(id) & IN_BACK && !wait[id])
				{
					if(test_wall[id])
					{
						if(is_valid_ent(wait_jump[id]))
						{
							remove_entity(wait_jump[id])
						}
						wait_jump[id] = 0
						entity_set_vector(id, EV_VEC_velocity, Float:{0,0,-20})
						test_wall[id] = false
						return PLUGIN_CONTINUE
					}
				}
				entity_set_int(wait_jump[id], EV_INT_solid, 0)
				new Float:velocity[3]
				VelocityByAim(id, get_cvar_num("spiderman_crawlspeed"), velocity)
				velocity[0] = - velocity[0]
				velocity[1] = - velocity[1]
				velocity[2] = - velocity[2]
				entity_set_vector(id, EV_VEC_velocity, velocity)
				test_wall[id] = true
			}
			else if(get_user_oldbutton(id) & IN_BACK)
			{
				entity_set_int(wait_jump[id], EV_INT_solid, 2)
				new Float:origin[3]
				entity_get_vector(id, EV_VEC_origin, origin)
				entity_set_vector(wait_jump[id], EV_VEC_origin, origin)
			}
			if(get_user_button(id) & IN_JUMP && !(get_user_oldbutton(id) & IN_JUMP))
			{
				set_user_gravity(id, get_cvar_float("spiderman_gravity"))
				if(is_valid_ent(wait_jump[id]))
				{
					remove_entity(wait_jump[id])
				}
				wait_jump[id] = 0
				new Float:velocity[3]
				VelocityByAim(id, get_cvar_num("spiderman_speed"), velocity)
				velocity[2] += 100.0
				entity_set_vector(id, EV_VEC_velocity, velocity)
			}
			new Float:origin[3]
			entity_get_vector(id, EV_VEC_origin, origin)
			if(origin[0] == 0.0 && origin[1] == 0.0 && origin[2] == 0.0)
			{
				if(is_valid_ent(wait_jump[id]))
				{
					remove_entity(wait_jump[id])
				}
				wait_jump[id] = 0
				test_wall[id] = false
				if(pl_origin[id][0] != 0.0 && pl_origin[id][1] != 0.0)
				{
					entity_set_vector(id, EV_VEC_origin, pl_origin[id])
				}
				else
				{
					entity_set_int(id, EV_INT_spawnflags, DEAD_RESPAWNABLE)
					DispatchSpawn(id)
					entity_set_int(id, EV_INT_iuser1, 0)
				}
				return PLUGIN_CONTINUE
			}
			else
			{
				pl_origin[id][0] = origin[0]
				pl_origin[id][1] = origin[1]
				pl_origin[id][2] = origin[2] + 5.0
			}
		}
		else if(get_entity_flags(id) & FL_ONGROUND && !hooked[id])
		{
			if(entity_get_float(id, EV_FL_gravity) != 1.0)
			{
				set_user_gravity(id, 1.0)
			}
		}
	}
	if(!wait[id])
	{
		if(wrappedup[id])
		{
			webeffect(id)
		}
		wait[id] = true
		set_task(0.1, "reset_wait", id)
	}

	return PLUGIN_CONTINUE
}

public reset_wait(id)
{
	wait[id] = false
}

public entity_touch(entity1, entity2) 
{
	if(entity1 == 0 && entity2 > 0 && entity2 < maxplayers)
	{
		if(can_jump[entity2] && is_user_alive(entity2))
		{
			test_wall[entity2] = false
			if(wait_jump[entity2] == 0 && !(get_entity_flags(entity2) & FL_ONGROUND) && !hooked[entity2])
			{
				new ent = create_entity("info_target")
				if(ent > 0)
				{
					entity_set_int(ent, EV_INT_solid, SOLID_BBOX)
					entity_set_int(ent, EV_INT_movetype, MOVETYPE_FLY) 
					entity_set_model(ent, "models/rpgrocket.mdl")
					set_rendering(ent, kRenderFxGlowShell, 0, 0, 0, kRenderTransAlpha, 0)
					entity_set_size(ent, Float:{-1.0,-1.0,-1.0}, Float:{1.0,1.0,1.0})
					new Float:origin[3]
					entity_get_vector(entity2, EV_VEC_origin, origin)
					entity_set_vector(ent, EV_VEC_origin, origin)
					wait_jump[entity2] = ent
				}
			}
		}
	}

	return PLUGIN_CONTINUE
}

public swingtask(id)
{
	new user_origin[3], user_look[3], user_direction[3], move_direction[3]
	new A[3], D[3], buttonadjust[3]
	new acceleration, Float:velocity_towards_A, desired_velocity_towards_A
	new null[3]
	new Float:velocity[3]

	if(beamcreated[id] + BEAMLIFE/10 <= get_gametime())
	{
		beamentpoint(id)
	}

	null[0] = 0
	null[1] = 0
	null[2] = 0

	get_user_origin(id, user_origin)
	get_user_origin(id, user_look, 2)
	entity_get_vector(id, EV_VEC_velocity, velocity)

	buttonadjust[0] = 0
	buttonadjust[1] = 0

	if(get_user_button(id) & IN_FORWARD)
	{
		buttonadjust[0] += 1
	}
	if(get_user_button(id) & IN_BACK)
	{
		buttonadjust[0] -= 1
	}
	if(get_user_button(id) & IN_MOVERIGHT)
	{
		buttonadjust[1] += 1
	}
	if(get_user_button(id) & IN_MOVELEFT)
	{
		buttonadjust[1] -= 1
	}
	if(get_user_button(id) & IN_JUMP)
	{
		buttonadjust[2] += 1
	}
	if(get_user_button(id) & IN_DUCK)
	{
		buttonadjust[2] -= 1
	}

	if(buttonadjust[0] || buttonadjust[1])
	{
		user_direction[0] = user_look[0] - user_origin[0]
		user_direction[1] = user_look[1] - user_origin[1]

		move_direction[0] = buttonadjust[0]*user_direction[0] + user_direction[1]*buttonadjust[1]
		move_direction[1] = buttonadjust[0]*user_direction[1] - user_direction[0]*buttonadjust[1]
		move_direction[2] = 0

		velocity[0] += floatround(move_direction[0] * MOVEACCELERATION * DELTA_T / get_distance(null,move_direction))
		velocity[1] += floatround(move_direction[1] * MOVEACCELERATION * DELTA_T / get_distance(null,move_direction))
	}
	if(buttonadjust[2])
	{
		hooklength[id] -= floatround(buttonadjust[2] * REELSPEED * DELTA_T)
	}
	if(hooklength[id] < 100)
	{
		hooklength[id] = 100
	}

	A[0] = hooklocation[id][0] - user_origin[0]
	A[1] = hooklocation[id][1] - user_origin[1]
	A[2] = hooklocation[id][2] - user_origin[2]

	D[0] = A[0]*A[2] / get_distance(null,A)
	D[1] = A[1]*A[2] / get_distance(null,A)
	D[2] = -(A[1]*A[1] + A[0]*A[0]) / get_distance(null,A)

	acceleration = - global_gravity * D[2] / get_distance(null,D)

	velocity_towards_A = (velocity[0] * A[0] + velocity[1] * A[1] + velocity[2] * A[2]) / get_distance(null,A)
	desired_velocity_towards_A = (get_distance(user_origin,hooklocation[id]) - hooklength[id] - 10) * 4

	if(get_distance(null,D) > 10)
	{
		velocity[0] += (acceleration * DELTA_T * D[0]) / get_distance(null,D)
		velocity[1] += (acceleration * DELTA_T * D[1]) / get_distance(null,D)
		velocity[2] += (acceleration * DELTA_T * D[2]) / get_distance(null,D)
	}

	velocity[0] += ((desired_velocity_towards_A - velocity_towards_A) * A[0]) / get_distance(null,A)
	velocity[1] += ((desired_velocity_towards_A - velocity_towards_A) * A[1]) / get_distance(null,A)
	velocity[2] += ((desired_velocity_towards_A - velocity_towards_A) * A[2]) / get_distance(null,A)

	entity_set_vector(id, EV_VEC_velocity, velocity)
}

public swing_on(id,level,cid)
{ 
	if(!cmd_access(id,level,cid,1))
	{ 
		return PLUGIN_HANDLED 
	} 
	if(can_jump[id] && get_cvar_num("spiderman_swing") == 1 && is_user_alive(id))
	{
		attach(id)
	}
	return PLUGIN_HANDLED
}

public swing_off(id,level,cid)
{ 
	if(!cmd_access(id,level,cid,1))
	{ 
		return PLUGIN_HANDLED 
	} 
	if(can_jump[id] && hooked[id])
	{
		release(id)
	}
	return PLUGIN_HANDLED
}

public swing_toggle(id,level,cid)
{ 
	if(!cmd_access(id,level,cid,1))
	{ 
		return PLUGIN_HANDLED 
	} 
	if(can_jump[id] && get_cvar_num("spiderman_swing") == 1 && !hooked[id] && is_user_alive(id))
	{
		attach(id)
	}
	else if(can_jump[id] && hooked[id])
	{
		release(id)
	}
	return PLUGIN_HANDLED
}

public attach(id)
{
	if(is_valid_ent(wait_jump[id]))
	{
		remove_entity(wait_jump[id])
	}
	wait_jump[id] = 0
	new user_origin[3]
	get_user_origin(id, user_origin)
	get_user_origin(id, hooklocation[id], 3)
	new target, body
	get_user_aiming(id, target, body)
	if(is_user_connected(target) && !wrappedup[target] && get_user_team(target) != get_user_team(id))
	{
		wrapup(target)
		set_task(0.2, "release", id)
		new param[2]
		param[0] = target
		set_task(get_cvar_float("spiderman_wraptime"), "unwrapup", 21121411+target, param, 2)
	}
	else
	{
		hooked[id] = true
		hooklength[id] = get_distance(hooklocation[id], user_origin)
		global_gravity = get_cvar_num("sv_gravity")
		set_user_gravity(id, 0.001)
	}
	beamentpoint(id)
	emit_sound(id, CHAN_STATIC, "weapons/pl_gun2.wav", 1.0, ATTN_NORM, 0, PITCH_NORM)
}

public release(id)
{
	hooked[id] = false
	killbeam(id)
	set_user_gravity(id, get_cvar_float("spiderman_gravity"))
}

public beamentpoint(id)
{
	message_begin(MSG_BROADCAST, SVC_TEMPENTITY)
	write_byte(1)
	write_short(id)
	write_coord(hooklocation[id][0])
	write_coord(hooklocation[id][1])
	write_coord(hooklocation[id][2])
	write_short(beam)
	write_byte(0)
	write_byte(0)
	write_byte(BEAMLIFE)
	write_byte(8)
	write_byte(0)
	write_byte(250)
	write_byte(250)
	write_byte(250)
	write_byte(250)
	write_byte(0)
	message_end()
	beamcreated[id] = get_gametime()
}

public killbeam(id)
{
	message_begin(MSG_BROADCAST, SVC_TEMPENTITY)
	write_byte(99)
	write_short(id)
	message_end()
}

public wrapup(id)
{
	wrappedup[id] = true
	new weapons[32], weapons_num, weapon_name[32]
	get_user_weapons(id, weapons, weapons_num)
	for(new i = 0 ; i < weapons_num ; i++)
	{
		get_weaponname(weapons[i], weapon_name, 31)
		engclient_cmd(id, "drop", weapon_name)
	}
	client_cmd(id, "weapon_knife")
	set_user_maxspeed(id, -1.0)
}

public unwrapup(param[])
{
	new id = param[0]
	wrappedup[id] = false
	set_user_maxspeed(id, 250.0)
}

public webeffect(id) // by SpaceDude
{
	new life = 5
	new radius = 20
	new counter = 0
	new origin[3]
	new x1
	new y1
	new x2
	new y2
	get_user_origin(id,origin)

	//emit_sound(id, CHAN_STATIC, "", 1.0, ATTN_NORM, 0, PITCH_NORM)

	while (counter<=7)
	{
		if (counter==0 || counter==8)
			x1= -radius
		else if (counter==1 || counter==7)
			x1= -radius*100/141
		else if (counter==2 || counter==6)
			x1= 0
		else if (counter==3 || counter==5)
			x1= radius*100/141
		else if (counter==4)
			x1= radius
		if (counter<=4)
			y1 = sqrt(radius*radius-x1*x1)
		else
			y1 = -sqrt(radius*radius-x1*x1)
		++counter
		if (counter==0 || counter==8)
			x2= -radius
		else if (counter==1 || counter==7)
			x2= -radius*100/141
		else if (counter==2 || counter==6)
			x2= 0
		else if (counter==3 || counter==5)
			x2= radius*100/141
		else if (counter==4)
			x2= radius
		if (counter<=4)
			y2 = sqrt(radius*radius-x2*x2)
		else
			y2 = -sqrt(radius*radius-x2*x2)
		new height=16+2*counter

		while (height > -40)
		{
			message_begin( MSG_BROADCAST, SVC_TEMPENTITY )
			write_byte( 0 )
			write_coord(origin[0]+x1)
			write_coord(origin[1]+y1)
			write_coord(origin[2]+height)
			write_coord(origin[0]+x2)
			write_coord(origin[1]+y2)
			write_coord(origin[2]+height+2)
			write_short(beam2) // model
			write_byte( 0 ) // start frame
			write_byte( 0 ) // framerate
			write_byte( life )// life
			write_byte( 10 )  // width
			write_byte( 5 )	// noise
			write_byte( 250 )	// r, g, b
			write_byte( 250 )	// r, g, b
			write_byte( 250 )	// r, g, b
			write_byte( 255 )	// brightness
			write_byte( 0 )	// speed
			message_end()

			height -= 16
		}
	}
	return PLUGIN_CONTINUE
}

public sqrt(num) 
{
	new div = num
	new result = 1
	while (div > result) 
	{			
		div = (div + result) / 2
		result = num / div
	}
	return div
}

public plugin_precache()
{
	precache_model("models/rpgrocket.mdl")
	beam = precache_model("sprites/rope.spr")
	beam2 = precache_model("sprites/zbeam4.spr")
	precache_sound("weapons/pl_gun2.wav")
}