-- $LastChangedDate: 2008-12-26 21:33:34 -0500 (Fri, 26 Dec 2008) $
-- Mission (aircraft carrier at Golden Gate).
-- mission.lua defines an equivalent of a Mission base class which this extends.
-- See module.txt for documentation and pitfalls.

-- Specialize mission class.
assert( Mission )  -- mission.lua defines base methods
Mission.BaseReset  = Mission.Reset
Mission.friends    = {}
Mission.viewCnt    = 1
Mission.playerPos  = Vec3( 8110.0, -16500.0, -500.0 )
Mission.carrierPos = Vec3( 8000.0, -15500.0, -550.0 )
Mission.light = {
	pos      = Vec3( -15000.0, -500.0, 70000.0 ),
	ambient  = RGBA(0.20, 0.20, 0.20, 1.00),
	diffuse  = RGBA(0.60, 0.60, 0.60, 1.00),
	specular = RGBA(0.30, 0.30, 0.30, 1.00),
}

-- Place aircraft on carrier, tilted up appropriately.
function Mission:PlaceAircraftOnCarrier( aircraft, carrier, carrierSlot )
	carrier:PlaceAircraft( aircraft, sim.Carrier.slots[carrierSlot] )
	if ( aircraft:IfHasPropeller() ) then
		aircraft:Rotate( AXIS_PITCH, Deg2Rad(10.0) )
	else
		aircraft:Rotate( AXIS_PITCH, Deg2Rad(0.5) )
	end
end

-- Decorate carrier with aircraft.
function Mission:DecorateCarrier( carrier, aircraftType )
	local firstCarrier = (carrier == carriers[1])
	for k,v in pairs(sim.Carrier.slots) do
		-- Leave the first few slots empty (reserved for player).
		if ( not firstCarrier or k > sim.Carrier.takeoffSlotCnt ) then
			local aircraft = sim.Aircraft:new( Vec3(0,0,0), aircraftType, sim.ModelCache.LOAD_SHARED )
			local physics = aircraft:GetPhysics()
			physics:Enable( false )  -- don't need to compute physics
			sim.World:AttachObject( aircraft )
			aircraft:Rotate( AXIS_YAW, RADIAN_90 )
			-- NOTE: Finish any orientation before PlaceAircraftOnCarrier().
			self:PlaceAircraftOnCarrier( aircraft, carrier, k )
		end
	end
end

-- Stop animation of friendly aircraft.
function Mission:StopFriendReplay()
	-- Call Replay::Stop() since Lua's garbage-collector will delay destroying!
	if ( self.friendReplay ) then
		self.friendReplay:Stop()
	end
	self.friendReplay = nil
end

-- Create friend.
function Mission:MakeFriends()
	self.friends = {}  -- recreate

	local friend = AircraftActor:new( "Friend", Game.TEAM_NUM_PLAYER, sim.Aircraft.type_F18 )
	table.insert( self.friends, friend )
	self.carrier0:PlaceAircraft( friend.aircraft, sim.Carrier.slots[self.friendSlot] )
	sim.World:AttachObject( friend.aircraft )
	sim.World:RotateTangential( friend.aircraft, friend.aircraft:GetPosition() )
	friend.aircraft:Rotate( AXIS_YAW, Deg2Rad(8.5) )
	friend.aircraft:SetLandingGear( sim.LANDING_GEAR_DOWN )

	-- Keep orientation of auxillary chase-plane after subsequent program resets.
	if ( ChaseView.chaseViewpointFriend ) then
		ChaseView.chaseViewpointFriend:SetTarget( friend.aircraft )
		if ( self.ifDoneFriendViewpoint == nil ) then
			self.ifDoneFriendViewpoint = true
			ChaseView.chaseViewpointFriend:SetDistance( 0.0, 0.0, sim.World.Meters2Sim(ChaseView.distance) )
			ChaseView.chaseViewpointFriend:Rotate( sim.ChaseViewpoint.AXIS_CHASE_PLANE_HORZ, ChaseView.horzAngle )
			ChaseView.chaseViewpointFriend:Rotate( sim.ChaseViewpoint.AXIS_CHASE_PLANE_VERT, ChaseView.vertAngle )
		end
		--  Save state for when chase-plane is reset.
		ChaseView.chaseViewpointFriend:Save()
	end

	-- Animate friend using replay data.
	self:StopFriendReplay()
	self.friendReplay = Replay:new( friend.aircraft )
	self.friendReplay:Load( self.friendReplayFilename )
	self.friendReplay:Play()
end

-- They served their purpose, Lord Vader.
function Mission:KillFriends()
	if ( self.friends and self.friends[1] ) then
		self:StopFriendReplay()

		sim.World:DetachObject( self.friends[1].aircraft )
		self.friends[1] = nil
	end
end

-- Make/remake targets.
function Mission:MakeTargets()
	local initialTarget = self:MakeTarget( "Radar1", Actor, Scene.objects.radar, Game.TEAM_NUM_ENEMY )
	PlayerActor:SelectTarget( initialTarget )
	self:MakeTarget( "Radar2", Actor, Scene.objects.radar2, Game.TEAM_NUM_ENEMY )
	self:MakeTarget( "Sam", SamActor, Scene.objects.missileLauncher, Game.TEAM_NUM_ENEMY )
	self:MakeTarget( "Sam2", SamActor, Scene.objects.trailerLauncher, Game.TEAM_NUM_ENEMY )
	if ( Scene.objects.ufo ) then  -- UFO could be absent
		self:MakeTarget( "Ufo", Actor, Scene.objects.ufo, Game.TEAM_NUM_NEUTRAL )
	end
end

-- Reset mission.
function Mission:Reset()
	-- Call base Reset().
	self:BaseReset()

	-- RESTART command is generated by the user changing mission by GUI.
	local cmdFields = CommandQueue:GetCmdFields()
	if ( cmdFields and (cmdFields[CommandQueue.COMMAND_FIELD] == "RESTART") ) then
		local missionName = cmdFields[CommandQueue.RESTART_FIELD_MISSION]
		if ( missionName == "takeoff" ) then
			self.mode = self.MODE_TAKEOFF
		elseif ( missionName == "land" ) then
			self.mode = self.MODE_LAND
		end
	end

	-- Reset HUD.
	if ( self.mode == self.MODE_LAND ) then
		hudTextInfo:SetText( "Land on carrier." )
	else
		hudTextInfo:SetText( "Destroy SAM." )
	end

	-- Reset player.
	PlayerActor.aircraft:Reset()
	local physics = PlayerActor.aircraft:GetPhysics()

	-- Orient player.
	if ( self.mode == self.MODE_TAKEOFF and self.playerSlot == 3 ) then
		-- Front slot.
		PlayerActor.aircraft:Rotate( AXIS_YAW, Deg2Rad(2.5) )  -- yaw left
	else
		-- Rear slot -OR- when landing.
		PlayerActor.aircraft:Rotate( AXIS_YAW, Deg2Rad(8.5) )  -- yaw left
	end

	-- SUBTLE: Call SetSpeed() before SetLandingGear().
	-- SetLandingGear() is supposed to be a NOP if speed is zero.
	if ( self.mode == self.MODE_LAND ) then
		physics:SetSpeed( sim.Physics.KPH2Speed( 125.0 ) )
		PlayerActor.aircraft:SetState( sim.Craft.eState_FLYING )
		PlayerActor.aircraft:SetThrottle( 0.3 )
		PlayerActor.aircraft:SetLandingGear( sim.LANDING_GEAR_UP )

		sim.BeginPlacement( PlayerActor.aircraft )  -- disable collision
		PlayerActor.aircraft:SetPosition( self.playerPos )
		sim.EndPlacement( PlayerActor.aircraft )  -- enable collision
	else
		physics:SetSpeed( sim.Physics.KPH2Speed( 0.0 ) )
		PlayerActor.aircraft:SetState( sim.Craft.eState_PARKED )
		PlayerActor.aircraft:SetThrottle( 0.0 )
		PlayerActor.aircraft:SetLandingGear( sim.LANDING_GEAR_DOWN )

		sim.BeginPlacement( PlayerActor.aircraft )  -- disable collision
		self:PlaceAircraftOnCarrier( PlayerActor.aircraft, self.carrier0, self.playerSlot )
		sim.EndPlacement( PlayerActor.aircraft )  -- enable collision
	end

	-- Reset view.
	sim.SuperView:SetViewMode( sim.SuperView.eViewMode_OTW_FRONT )
	if ( self.mode ~= self.MODE_TAKEOFF ) then
		-- Adjust chase-plane so that HudText is readable.
		ChaseView.chaseViewpoint:Rotate( sim.ChaseViewpoint.AXIS_CHASE_PLANE_VERT, Deg2Rad(5.0) )
	end
    if ( ChaseView.chaseViewpointFriend ) then  -- unless -fast
		ChaseView.chaseViewpointFriend:Reset()
    end

	-- Make/remake friends and enemies.
	self:KillFriends()
	self:MakeFriends()
	self:MakeTargets()
end

-- Initialization that's done once.
function Mission:Start()
	-- Initial position.
	self.initialPos = sim.World:SphereVertex2WorldVertex( 37.518800077449, -122.37326847224, 2.0 )
	if ( sim.argvMap["-front-slot"] ) then
		self.playerSlot = 3
		self.friendSlot = 2  -- rear slot
		self.friendReplayFilename = "replays/replayTakeoffRearSlot.dat.gz"
	else
		self.playerSlot = 1
		self.friendSlot = 3  -- front slot
		self.friendReplayFilename = "replays/replayTakeoffFrontSlot.dat.gz"
	end

	-- Initialize sky and lights.
	sim.Sky:SetLightColor( self.light.ambient, self.light.diffuse, self.light.specular )
	sim.Sky:SetLightPosition( self.light.pos )

	-- How to begin the mission.
	self.MODE_TAKEOFF = 1
	self.MODE_LAND    = 2
	self.mode         = self.MODE_TAKEOFF  -- default
	if ( sim.argvMap["-takeoff"] ) then
		self.mode = self.MODE_TAKEOFF
	elseif ( sim.argvMap["-land"] ) then
		self.mode = self.MODE_LAND
	end

	-- Initialize views.
	ChaseView.chaseViewpoint = ChaseView.chaseViewpointPlayer
	if ( not sim.argvMap["-fast"] ) then
		self.viewCnt = 2  -- disable view of friend
	end
	if ( self.viewCnt == 3 ) then
		-- Create a view to show a friendly aircraft.
		local sx,sy = sim.Window:GetWindowSize()
		ChaseView.chaseViewpointFriend = sim.ChaseViewpoint:new()
		self.view3 = sim.View:new( sx * 0.75, sy * 0.75, sx, sy )
		self.view3:SetViewpoint( ChaseView.chaseViewpointFriend )
		self.view3:SetOutlineColor( defs.VIEW_OUTLINE_COLOR_DISABLED )
	end
	ChaseView.chaseViewpoint:Save()
	sim.SuperView:MakeSecondView()

	-- Create aircraft carrier(s).
	self.carriers = {
		sim.Carrier:new( self.carrierPos, 0 ),
	}
	self.carrier0 = self.carriers[1]

	-- Info.
	hudTextInfo = sim.HudText:new( { text="", orientation="TOP_LEFT" } )
	hudTextInfo:SetColor( 1.0, 1.0, 0.6, 1.0 )
	hudTextFriend = sim.HudText:new( { text="", orientation="BOTTOM_RIGHT" } )
	hudTextFriend:SetFontSize( 24.0 )

	-- Load scene.
	assert( Scene:Load( "GoldenGate" ) )

	-- Initial reset.
	self:Reset()
end

-- Mission-specific timer-tick handler.
function Mission:Tick()
	-- Did the player land on the carrier?
	if ( self.mode == self.MODE_LAND ) then
		local state = PlayerActor.aircraft:GetState()
		if ( state == sim.Craft.eState_LANDING ) then
			local playerSpeedKPH = sim.AircraftComputeSpeedKPH( PlayerActor.aircraft )
			if ( playerSpeedKPH > 3.0 ) then
				hudTextInfo:SetText( "Landing..." )
			else
				Game:WriteScoreInt( "game_score_landings", 1 )
				hudTextInfo:SetText( "Success!" )
				-- Switch state.
				PlayerActor.aircraft:SetState( sim.Craft.eState_PARKED )
			end
		elseif ( state == sim.Craft.eState_PARKED ) then
			hudTextInfo:SetText( "Success!" )
		elseif ( state == sim.Craft.eState_FLYING ) then
			hudTextInfo:SetText( "Land on carrier." )
		end
	end

	-- Animate UFO.
	-- UFO impossibly changes direction ;-)
	-- The meter constants are from Scene.
	if ( Scene.objects.ufo ) then
		local pos = Scene.objects.ufo.object:GetPosition()
		if ( self.mode == self.MODE_TAKEOFF ) then
			if ( pos[LON] < self.initialPos[LON] + M2W(250.0) ) then
				pos[LON] = pos[LON] + M2W(2.5)
			else
				if ( pos[ALT] < self.initialPos[ALT] + M2W(100.0) ) then
					pos[ALT] = pos[ALT] + M2W(1.5)
				else
					pos[LAT] = pos[LAT] - M2W(2.5)
				end
			end
		else
			if ( pos[LON] < self.initialPos[LON] + M2W(-200.0) ) then
				pos[LON] = pos[LON] + M2W(4.0)
			else
				pos[LAT] = pos[LAT] + M2W(4.0)
			end
		end
		Scene.objects.ufo.object:SetPosition( pos )
	end
end

Timer:Register( Mission, defs.MISSION_TICK_FREQ )

-- Let startup.lua continue...
