-- NoXious vehicles system
-- By JetBoom

VehicleTypes = {}
Vehicles = {}
PlayerHealths = {}
PlayerArmors = {}

local files = _file.Find( "lua/gamemodes/noxctf/vehiclescripts/*.lua" )
for i in files do
    if files[i] ~= "." and files[i] ~= ".." then
		if _file.Exists( "lua/gamemodes/noxctf/vehiclescripts/"..files[i]) then
			_Msg("Opening vehicle script "..files[i].."\n")
			_OpenScript("gamemodes/noxctf/vehiclescripts/"..files[i])
		end
    end
end

function InVehicle(userid)
	for i in Vehicles do
		if Vehicles[i].Driver == userid then return true end
		for x=1, table.getn(Vehicles[vehicle].Passenger) do
			if Vehicles[vehicle].Passenger[x] == userid then return true end
		end
	end
	return false
end

for i in teams do
	teams[i].VehicleStock = {}
	for x in VehicleTypes do
		teams[i].VehicleStock[x] = VehicleTypes[x].Stock or 1
	end
end

function VehiclesThink()
	for i in Vehicles do
		if i <= 0 or Vehicles[i].Type == nil then
			Vehicles[i] = nil
		else
			doVehicleThink(i)
		end
	end
end
AddThinkFunction(VehiclesThink)

function doVehicleThink(vehicle)
	local vehicletype = Vehicles[vehicle].Type
	if VehicleTypes[vehicletype].onThink ~= nil then
		VehicleTypes[vehicletype].onThink(vehicle)
	end
	if not _PlayerInfo(Vehicles[vehicle].Driver, "alive") or not IsPlayerOnline(Vehicles[vehicle].Driver) then Vehicles[vehicle].Driver = 0 end
	for i=1, table.getn(Vehicles[vehicle].Passenger) do
		if not _PlayerInfo(Vehicles[vehicle].Passenger[i], "alive") or not IsPlayerOnline(Vehicles[vehicle].Passenger[i]) then Vehicles[vehicle].Passenger[i] = 0
		elseif _PlayerIsKeyDown(Vehicles[vehicle].Passenger[i], IN_ATTACK2) then
			PassengerExit(Vehicles[vehicle].Passenger[i], vehicle)
		elseif _PlayerIsKeyDown(Vehicles[vehicle].Passenger[i], IN_ATTACK) then
			if VehicleTypes[vehicletype].onPassengerAttack ~= nil then
				VehicleTypes[vehicletype].onPassengerAttack(vehicle, i)
			end
		end
	end
	if Vehicles[vehicle].Driver <= 0 then return end
	if _PlayerIsKeyDown(Vehicles[vehicle].Driver, IN_ATTACK2) then
		DriverExit(Vehicles[vehicle].Driver, vehicle)
		return
	end
	if _PlayerIsKeyDown(Vehicles[vehicle].Driver, IN_FORWARD) then
		if VehicleTypes[vehicletype].onForward ~= nil then
			VehicleTypes[vehicletype].onForward(vehicle)
		end
	elseif _PlayerIsKeyDown(Vehicles[vehicle].Driver, IN_BACK) then
		if VehicleTypes[vehicletype].onBackward ~= nil then
			VehicleTypes[vehicletype].onBackward(vehicle)
		end
	end
	if _PlayerIsKeyDown(Vehicles[vehicle].Driver, IN_MOVELEFT) then
		if VehicleTypes[vehicletype].onLeft ~= nil then
			VehicleTypes[vehicletype].onLeft(vehicle)
		end
	elseif _PlayerIsKeyDown(Vehicles[vehicle].Driver, IN_MOVERIGHT) then
		if VehicleTypes[vehicletype].onRight ~= nil then
			VehicleTypes[vehicletype].onRight(vehicle)
		end
	end
	if _PlayerIsKeyDown(Vehicles[vehicle].Driver, IN_ATTACK) then
		if VehicleTypes[vehicletype].onAttack ~= nil then
			VehicleTypes[vehicletype].onAttack(vehicle)
		end
	end
	if _PlayerIsKeyDown(Vehicles[vehicle].Driver, IN_JUMP) then
		if VehicleTypes[vehicletype].onJump ~= nil then
			VehicleTypes[vehicletype].onJump(vehicle)
		end
	elseif _PlayerIsKeyDown(Vehicles[vehicle].Driver, IN_DUCK) then
		if VehicleTypes[vehicletype].onDuck ~= nil then
			VehicleTypes[vehicletype].onDuck(vehicle)
		end
	end
end

function OpenVehicleSlot(vehicle)
	if table.getn(Vehicles[vehicle].Passenger) <= 0 then return -1 end
	for i in Vehicles[vehicle].Passenger do
		if Vehicles[vehicle].Passenger[i] <= 0 then
			return i
		end
	end
	return -1
end

function ProcessVehicleDamage(vehicle, attacker, damage)
	attacker = attacker or Vehicles[vehicle].Driver or Vehicles[vehicle].Owner
	if _PlayerInfo(attacker, "team") == Vehicles[vehicle].Team then return end
	if Vehicles[vehicle] == nil then return end
	if Vehicles[vehicle].Type == nil then return end
	if VehicleTypes[Vehicles[vehicle].Type] == nil then return end
	damage = math.abs(damage*(VehicleTypes[Vehicles[vehicle].Type].DamageMultiply))
	Vehicles[vehicle].Health = Vehicles[vehicle].Health-damage
	if VehicleTypes[Vehicles[vehicle].Type].onDamaged ~= nil then
		VehicleTypes[Vehicles[vehicle].Type].onDamaged(vehicle, attacker, damage)
	end
	_GModText_Start( "Default" )
	 _GModText_SetPos( 0.0, 0.0 )
	 _GModText_SetColor( 230, 0, 0, 255 )
	 _GModText_SetTime( 0.75, 0.2, 0.2 )
	 _GModText_SetAdditive(true)
	 _GModText_SetEntity(vehicle)
	 _GModText_SetEntityOffset(vector3(math.random(-25,25),math.random(-25,25),math.random(40,50)))
	 _GModText_SetText( "- "..math.floor(damage))
	_GModText_Send(attacker, attacker+vehicle+math.random(10000,22000))
	if VehicleTypes[Vehicles[vehicle].Type].HUD ~= nil then
		VehicleTypes[Vehicles[vehicle].Type].HUD(vehicle)
	end
	if Vehicles[vehicle].Health <= 0 then
		VehicleDestroyed(attacker, vehicle)
	end
end

function CreateVehicle(owner, pos, vehicletype)
	if VehicleTypes[vehicletype] == nil then _Msg("Invalid vehicle type\n") return end
	if VehicleTypes[vehicletype].Model == nil and VehicleTypes[vehicletype].EntityType == "prop_physics" then return end
	local enttype = VehicleTypes[vehicletype].EntityType or "prop_physics"
	if not pos then
		PlayerLookTrace(owner, 300)
		pos = vecAdd(_TraceEndPos(), vector3(0,0,250))
	end
	if not vehicletype then	for i in VehicleTypes do vehicletype = VehicleTypes[i] break end end
	local ent = _EntCreate(enttype)
	if ent > 0 then
		if string.find(VehicleTypes[vehicletype].EntityType, "prop_") then
			_EntPrecacheModel(VehicleTypes[vehicletype].Model)
			_EntSetModel(ent, VehicleTypes[vehicletype].Model)
		end
		_EntSetPos(ent, pos)
		_EntSetKeyValue(ent, "rendermode", "1")
		if VehicleTypes[vehicletype].onPreCreated ~= nil then
			VehicleTypes[vehicletype].onPreCreated(ent)
		end
		_EntSpawn(ent)
		_EntSetName(ent, "scriptedvehicle:"..vehicletype)
		Vehicles[ent] = {}
		Vehicles[ent].Owner = owner
		Vehicles[ent].Team = _PlayerInfo(owner, "team")
		Vehicles[ent].Driver = 0
		Vehicles[ent].Passenger = {}
		Vehicles[ent].Type = vehicletype
		Vehicles[ent].Health = VehicleTypes[vehicletype].MaxHealth or 100
		Vehicles[ent].CarJacked = false
		for i=1, VehicleTypes[vehicletype].PassengerSlots do
			Vehicles[ent].Passenger[i] = 0
		end
		VehicleLock(ent, true)
		for i=1, 64 do
			_EntFire(ent, "alpha", i*4, i*0.1)
		end
		_EntFire(ent, "alpha", "255", 6.4)
		_EntFire(ent, "color", TeamColor(_PlayerInfo(owner,"team")), 6.4)
		AddTimer(6.4, 1, VehicleLock, ent, false)
		--AddTimer(6.4, 1, ProcessVehicleEntry, owner, ent)
		_EntEmitSoundEx(ent, "ambient/machines/thumper_startup1.wav", 1.0, 0.5)
		if VehicleTypes[vehicletype].onCreated ~= nil then
			VehicleTypes[vehicletype].onCreated(ent)
		end
		teams[Vehicles[ent].Team].VehicleStock[vehicletype] = teams[Vehicles[ent].Team].VehicleStock[vehicletype]-1
	end
end

function VehicleLock(ent, bool)
	if Vehicles[ent] ~= nil then
		Vehicles[ent].Locked = bool
	end
end

function ProcessVehicleEntry(userid, vehicle)
	if not _PlayerInfo(userid, "alive") or not IsPlayerOnline(userid) then return end
	if not Vehicles[vehicle] then return end
	if Vehicles[vehicle].Locked then
		_PlaySoundPlayer(userid, "buttons/button8.wav")
		_PrintMessage(userid, 4, "It's locked.")
		return
	end
	if Vehicles[vehicle].Driver <= 0 then
		if Vehicles[vehicle].Team ~= _PlayerInfo(userid, "team") then
			for i in Vehicles[vehicle].Passenger do
				if Vehicles[vehicle].Passenger[i] > 0 then
					_PlaySoundPlayer(userid, "buttons/button8.wav")
					_PrintMessage(userid, 4, "It's locked.")
					return
				end
			end
			CarJack(userid, vehicle)
		end
		DriverEnter(userid, vehicle)
	else
		PassengerEnter(userid, vehicle)
	end
end

function CarJack(userid, vehicle)
	local vehicletype = Vehicles[vehicle].Type
	if not Vehicles[vehicle].CarJacked then
		teams[Vehicles[vehicle].Team].VehicleStock[vehicletype] = teams[Vehicles[vehicle].Team].VehicleStock[vehicletype]+1
	end
	_EntEmitSoundEx(vehicle, "nox/carjacked.wav", 1.25, 1.0)
	_EntFire(vehicle, "color", "255 255 255", 0)
	_EntFire(vehicle, "color", TeamColor(_PlayerInfo(userid, "team")), 1.0)
	Vehicles[vehicle].Team = _PlayerInfo(userid, "team")
	Vehicles[vehicle].Owner = userid
	Vehicles[vehicle].CarJacked = true
end

function DriverEnter(userid, vehicle)
	if VehicleTypes[Vehicles[vehicle].Type].onDriverEnter ~= nil then
		VehicleTypes[Vehicles[vehicle].Type].onDriverEnter(vehicle)
	end
	Vehicles[vehicle].Driver = userid
	PlayerHealths[userid] = _PlayerInfo(userid, "health")
	PlayerArmors[userid] = _PlayerInfo(userid, "armor")
	_PlayerSpectatorStart(userid, OBS_MODE_CHASE)
	_PlayerSetChaseCamDistance(userid, VehicleTypes[Vehicles[vehicle].Type].DriverCamDistance or 0)
	_PlayerSpectatorTarget(userid, vehicle)
	_PlayerRemoveAllWeapons(userid)
	if VehicleTypes[Vehicles[vehicle].Type].HUD ~= nil then
		VehicleTypes[Vehicles[vehicle].Type].HUD(vehicle)
	end
	_EntEmitSound(vehicle, "ambient/levels/citadel/pod_close1.wav")
end

function DriverExit(userid, vehicle)
	if not _PlayerInfo(userid, "alive") or not IsPlayerOnline(userid) then return end
	if VehicleTypes[Vehicles[vehicle].Type].onDriverExit ~= nil then
		VehicleTypes[Vehicles[vehicle].Type].onDriverExit(vehicle)
	end
	Vehicles[vehicle].Driver = 0
	_PlayerSpectatorEnd(userid)
	_PlayerRespawn(userid)
	_EntSetPos(userid, vecAdd(_EntGetPos(vehicle), VehicleTypes[Vehicles[vehicle].Type].ExitOffset))
	_PlayerSetHealth(userid, PlayerHealths[userid])
	if PlayerArmors[userid] > 0 then
		_PlayerSetArmor(userid, PlayerArmors[userid])
	end
	if VehicleTypes[Vehicles[vehicle].Type].HUD ~= nil then
		VehicleTypes[Vehicles[vehicle].Type].HUD(vehicle)
	end
	if VehicleTypes[Vehicles[vehicle].Type].HideHUD ~= nil then
		VehicleTypes[Vehicles[vehicle].Type].HideHUD(userid)
	end
	if Vehicles[vehicle].Health > 0 then
		_EntEmitSound(vehicle, "ambient/levels/citadel/pod_open1.wav")
	end
end

function PassengerEnter(userid, vehicle)
	if Vehicles[vehicle].Team == _PlayerInfo(userid, "team") then
		local n = OpenVehicleSlot(vehicle)
		if n > 0 then
			Vehicles[vehicle].Passenger[n] = userid
			PlayerHealths[userid] = _PlayerInfo(userid, "health")
			_PlayerSpectatorStart(userid, OBS_MODE_CHASE)
			_PlayerSetChaseCamDistance(userid, VehicleTypes[Vehicles[vehicle].Type].PassengerCamDistance or 0)
			_PlayerSpectatorTarget(userid, vehicle)
			_PlayerRemoveAllWeapons(userid)
			_EntEmitSound(vehicle, "ambient/levels/citadel/pod_close1.wav")
			if VehicleTypes[Vehicles[vehicle].Type].onPassengerEnter ~= nil then
				VehicleTypes[Vehicles[vehicle].Type].onPassengerEnter(vehicle, userid)
			end
		else
			_PlaySoundPlayer(userid, "buttons/button8.wav")
			_PrintMessage(userid, 4, "This vehicle is full.")
		end
	else
		_PlaySoundPlayer(userid, "buttons/button8.wav")
		_PrintMessage(userid, 4, "It's locked.")
		return
	end
	if VehicleTypes[Vehicles[vehicle].Type].HUD ~= nil then
		VehicleTypes[Vehicles[vehicle].Type].HUD(vehicle)
	end
end

function PassengerExit(userid, vehicle)
	if VehicleTypes[Vehicles[vehicle].Type].onPassengerExit ~= nil then
		VehicleTypes[Vehicles[vehicle].Type].onPassengerExit(vehicle, userid)
	end
	for i in Vehicles[vehicle].Passenger do
		if Vehicles[vehicle].Passenger[i] == userid then
			Vehicles[vehicle].Passenger[i] = 0
		end
	end
	_PlayerSpectatorEnd(userid)
	_PlayerRespawn(userid)
	_EntSetPos(userid, vecAdd(_EntGetPos(vehicle), VehicleTypes[Vehicles[vehicle].Type].ExitOffset))
	_PlayerSetHealth(userid, PlayerHealths[userid])
	if VehicleTypes[Vehicles[vehicle].Type].HUD ~= nil then
		VehicleTypes[Vehicles[vehicle].Type].HUD(vehicle)
	end
	if VehicleTypes[Vehicles[vehicle].Type].HideHUD ~= nil then
		VehicleTypes[Vehicles[vehicle].Type].HideHUD(userid)
	end
	if Vehicles[vehicle].Health > 0 then
		_EntEmitSound(vehicle, "ambient/levels/citadel/pod_open1.wav")
	end
end

function VehicleDestroyed(attacker, vehicle)
	local vehicletype = Vehicles[vehicle].Type
	if VehicleTypes[Vehicles[vehicle].Type].onDestroyed ~= nil then
		VehicleTypes[Vehicles[vehicle].Type].onDestroyed(vehicle)
	end
	if not IsPlayerOnline(attacker) then
		for i=1, _MaxPlayers() do
			if IsPlayerOnline(i) then
				attacker = i
				break
			end
		end
	end
	if Vehicles[vehicle].Driver > 0 then
		local driver = Vehicles[vehicle].Driver
		_PlayerSpectatorEnd(driver)
		_PlayerRespawn(driver)
		_EntSetPos(driver, _EntGetPos(vehicle))
		if _PlayerInfo(attacker, "team") ~= _PlayerInfo(driver, "team") then
			AddTimer(0.5, 1, _TraceAttack, driver, attacker, attacker, 1000)
		else
			AddTimer(0.5, 1, _PlayerKill, driver)
		end
		if VehicleTypes[Vehicles[vehicle].Type].HideHUD ~= nil then
			VehicleTypes[Vehicles[vehicle].Type].HideHUD(driver)
		end
	end
	if VehicleTypes[vehicletype].PassengerSlots > 0 then
		for i=1, table.getn(Vehicles[vehicle].Passenger) do
			if Vehicles[vehicle].Passenger[i] > 0 then
				local passenger = Vehicles[vehicle].Passenger[i]
				_PlayerSpectatorEnd(passenger)
				_PlayerRespawn(passenger)
				_EntSetPos(passenger, _EntGetPos(vehicle))
				if _PlayerInfo(attacker, "team") == _PlayerInfo(passenger, "team") then
					AddTimer(0.5, 1, _PlayerKill, passenger)
				else
					AddTimer(0.5, 1, _TraceAttack, passenger, attacker, attacker, 1000)
				end
				if VehicleTypes[Vehicles[vehicle].Type].HideHUD ~= nil then
					VehicleTypes[Vehicles[vehicle].Type].HideHUD(passenger)
				end
			end
		end
	end
	if not Vehicles[vehicle].CarJacked then
		teams[Vehicles[vehicle].Team].VehicleStock[vehicletype] = teams[Vehicles[vehicle].Team].VehicleStock[vehicletype]+1
	end
	Vehicles[vehicle] = nil
end
