share/hedgewars/Data/Missions/Training/Basic_Training_-_Flying_Saucer.lua
changeset 11405 307832da2756
child 12424 b9cc405541c1
equal deleted inserted replaced
11380:ff0fa38bdb18 11405:307832da2756
       
     1 --[[
       
     2 	Flying Saucer Training
       
     3 	This is a training mission which teaches many basic (and not-so-basic) moves
       
     4 	with the flying saucer.
       
     5 
       
     6 	Lesson plan:
       
     7 	- Taking off
       
     8 	- Basic flight
       
     9 	- Landing safely
       
    10 	- Managing fuel
       
    11 	- Changing saucers in mid-flight
       
    12 	- Diving
       
    13 	- Dropping weapons from flying saucer
       
    14 	- Firing from flying saucer with [Precise] + [Attack]
       
    15 	- Aiming in flying saucer with [Precise] + [Up]/[Down]
       
    16 	- Underwater attack
       
    17 	- Free flight with inf. fuel and some weapons at end of training
       
    18 
       
    19 	FIXME:
       
    20 	- Bad respawn animation ("explosion" just happens randomly because of the way the resurrection effect works)
       
    21 	- Hide fuel if infinite (probably needs engine support)
       
    22 ]]
       
    23 
       
    24 HedgewarsScriptLoad("/Scripts/Locale.lua")
       
    25 HedgewarsScriptLoad("/Scripts/Tracker.lua")
       
    26 
       
    27 local Player = nil -- Pointer to hog created in: onGameInit
       
    28 local Target = nil -- Pointer to target hog
       
    29 local Objective = false -- Get to the target
       
    30 
       
    31 local TargetNumber = 0 -- The current target number
       
    32 local GrenadeThrown = false -- Used for the Boom Target
       
    33 local BazookasLeft = 0 -- Used by the Launch Target and the Unterwater Attack Target
       
    34 
       
    35 local InfFuel = false -- If true, flying saucer has infinite fuel
       
    36 local SaucerGear = nil -- Store flying saucer gear here (if one exists)
       
    37 local TargetGears = {} -- List of remaining gears to collect or destroy in the current round
       
    38 local TargetsRemaining = 0
       
    39 local Barrels = {} -- Table contraining the explosive barrel gears
       
    40 
       
    41 local CheckTimer = 500 -- Time to wait at least before checking safe landing
       
    42 local Check = false -- The last target has recently been collected/destroyed and the CheckTimer is running
       
    43 local GrenadeTimer = 0 -- Time after a grenade has been thrown
       
    44 
       
    45 local TargetPos = {} -- Table of targets
       
    46 
       
    47 local StartPos = { X = 742, Y = 290 }
       
    48 
       
    49 --[[
       
    50 List of all targets (or "objectives"). The player has to complete them one-by-one and must always land safely afterwards.
       
    51 Some target numbers have names for easier reference.
       
    52 ]]
       
    53 TargetPos[1] =  {
       
    54 	Targets = {{ X = 1027, Y = 217 }},
       
    55 	Ammo = { },
       
    56 	Message = loc("Here you will learn how to fly the flying saucer|and get so learn some cool tricks.") .. "|" ..
       
    57 	loc("Collect the first crate to begin!"),
       
    58 	MessageIcon = -amJetpack, }
       
    59 TargetPos[2] = {
       
    60 	Targets = {{ X = 1369, Y = 265 }},
       
    61 	Ammo = { [amJetpack] = 100 },
       
    62 	InfFuel = true,
       
    63 	MessageTime = 10000,
       
    64 	Message = loc("Get to the crate using your flying saucer!") .. "|" ..
       
    65 	loc("Press [Attack] (space bar by default) to start,|repeadedly tap the up, left and right movement keys to accelerate.") .. "|" ..
       
    66 	loc("Try to land softly, as you can still take fall damage!"), }
       
    67 TargetPos[3] = {
       
    68 	Targets = {{ X = 689, Y = 58 }},
       
    69 	Ammo = { [amJetpack] = 100 },
       
    70 	MessageTime = 5000,
       
    71 	Message = loc("Now collect the next crate!") .. "|" .. loc("Be careful, your fuel is limited from now on!") .."|" ..
       
    72 	loc("Tip: If you get stuck in this training, use \"Skip turn\" to restart the current objective.") }
       
    73 
       
    74 -- The Double Target
       
    75 local DoubleTarget = 4
       
    76 TargetPos[4] = {
       
    77 	Targets = { { X = 84, Y = -20 }, { X = 1980 , Y = -20 } },
       
    78 	Ammo = { [amJetpack] = 2 },
       
    79 	MessageTime = 9000,
       
    80 	Message = loc("Now collect the 2 crates to the far left and right.") .. "|" ..
       
    81 	loc("You only have 2 flying saucers this time.") .. "|" ..
       
    82 	loc("Tip: You can change your flying saucer|in mid-flight by hitting the [Attack] key twice."), }
       
    83 TargetPos[5] = {
       
    84 	Targets = {{ X = 47, Y = 804 }},
       
    85 	Ammo = { [amJetpack] = 100 },
       
    86 	MessageTime = 5000,
       
    87 	Message = loc("Time for a more interesting stunt, but first just collect the next crate!"), }
       
    88 TargetPos[6] = {
       
    89 	Targets = {{ X = 604, Y = 871}},
       
    90 	MessageTime = 15000,
       
    91 	Message = loc("You can dive with your flying saucer!") .. "|" ..
       
    92 	loc("Try it now and dive here to collect the crate on the right girder.") .. "|" ..
       
    93 	loc("You only have one flying saucer this time.") .. "|" ..
       
    94 	loc("Beware, though, you will only be able to move slowly through the water.") .. "|" ..
       
    95 	loc("Warning: Never ever leave the flying saucer while in water!"),
       
    96 	Ammo = { [amJetpack] = 1 }, }
       
    97 
       
    98 TargetPos[7] = { 
       
    99 	Targets = {{ X = 1884, Y = 704 }},
       
   100 	MessageTime = 6500,
       
   101 	Message = loc("Now dive just one more time and collect the next crate." .. "|" ..
       
   102 		loc("Tip: Don't remain for too long in the water, or you won't make it.")),
       
   103 	Ammo = { [amJetpack] = 1}, }
       
   104 
       
   105 -- The Boom Target
       
   106 local BoomTarget = 8
       
   107 TargetPos[8] = {
       
   108 	Modifier = true, Func = function()
       
   109 		Info(loc("Instructions"),
       
   110 		loc("Now let's try to drop weapons while flying!") .. "|" ..
       
   111 		loc("You have to destroy the target above by dropping a grenade on it from your flying saucer.") .. "|" ..
       
   112 		loc("It's not that easy, so listen carefully:") .. "|" ..
       
   113 		loc("Step 1: Activate your flying saucer but do NOT move yet!") .. "|" ..
       
   114 		loc("Step 2: Select your grenade.") .. "|" ..
       
   115 		loc("Step 3: Start flying and get yourself right above the target.") .. "|" ..
       
   116 		loc("Step 4: Drop your grenade by pressing the [Long jump] key.") .. "|" ..
       
   117 		loc("Step 5: Get away quickly and land safely anywhere." .. "| |" ..
       
   118 		loc("Note: We only give you grenades if you stay in your flying saucer.")), nil, 20000)
       
   119 
       
   120 		SpawnBoomTarget()
       
   121 
       
   122 		if SaucerGear ~= nil then
       
   123 			AddAmmo(Player, amGrenade, 1)
       
   124 		else
       
   125 			AddAmmo(Player, amGrenade, 0)
       
   126 		end
       
   127 		GrenadeThrown = false
       
   128 
       
   129 	end,
       
   130 	Ammo = { [amJetpack] = 100 },
       
   131 	Respawn = { X = 2000, Y = 742 }, }
       
   132 
       
   133 -- The Launch Target
       
   134 local LaunchTarget = 9
       
   135 TargetPos[9] = {
       
   136 	Targets = {{ X = 1700, Y = 640, Type = gtTarget }, { X = 1460, Y = 775, Type = gtTarget }},
       
   137 	MessageTime = 20000,
       
   138 	Message = loc("Only the best pilots can master the following stunts.") .. "|" ..
       
   139 		loc("As you've seen, the dropped grenade roughly fell into your flying direction.") .. "|" ..
       
   140 		loc("You have to destroy two targets, but the previous technique would be very difficult or dangerous to use.") .. "|" ..
       
   141 		loc("So you are able to launch projectiles into your aiming direction, always at full power.") .."|"..
       
   142 		loc("To launch a projectile in mid-flight, hold [Precise] and press [Long jump].") .. "|" ..
       
   143 		loc("You can even change your aiming direction in mid-flight if you first hold [Precice] and then press [Up] or [Down].") .. "|" ..
       
   144 		loc("Tip: Changing your aim while flying is very difficult, so adjust it before you take off."),
       
   145 	Ammo = { [amJetpack] = 1, },
       
   146 	Respawn = { X = 1764, Y = 916 },
       
   147 	ExtraFunc = function()
       
   148 		HogTurnLeft(Player, true)
       
   149 		if SaucerGear ~= nil then
       
   150 			AddAmmo(Player, amBazooka, 2)
       
   151 		else
       
   152 			AddAmmo(Player, amBazooka, 0)
       
   153 		end
       
   154 		BazookasLeft = 2
       
   155 
       
   156 	end }
       
   157 
       
   158 -- The Underwater Attack Target
       
   159 local UnderwaterAttackTarget = 10
       
   160 TargetPos[10] = {
       
   161 	MessageTime = 17000,
       
   162 	Message = loc("Now for the supreme discipline of saucer flying, the underwater attack.") .. "|" ..
       
   163 	loc("Basically this is a combination of diving and launching.") .. "|" ..
       
   164 	loc("Dropping a weapon while in water would just drown it, but launching one would work.") .."|" ..
       
   165 	loc("Based on what you've learned, destroy the target on the girder and as always, land safely!"), 
       
   166 	Targets = {{ X = 1200, Y = 930, Type = gtTarget }},
       
   167 	Ammo = { [amJetpack] = 1, },
       
   168 	Respawn = { X = 1027, Y = 217 },
       
   169 	ExtraFunc = function()
       
   170 		if SaucerGear ~= nil then
       
   171 			AddAmmo(Player, amBazooka, 1)
       
   172 		else
       
   173 			AddAmmo(Player, amBazooka, 0)
       
   174 		end
       
   175 		BazookasLeft = 1
       
   176 	end }
       
   177 TargetPos[11] = {
       
   178 	Targets = {{ X = 742, Y = 290 }},
       
   179 	MessageTime = 5000,
       
   180 	Message = loc("This almost concludes our tutorial.") .. "|" ..
       
   181 	loc("You now have infinite fuel, grenades and bazookas for fun.") .. "|" ..
       
   182 	loc("Collect or destroy the final crate to finish the training."),
       
   183 	Ammo = { [amJetpack] = 100, [amGrenade] = 100, [amBazooka] = 100 },
       
   184 	InfFuel = true, }
       
   185 TargetPos[12] = { Modifier = true, Func = function()
       
   186 	Objective = true
       
   187 	AddCaption(loc("Training complete!"), 0xFFFFFFFF, capgrpGameState)
       
   188 	Info(loc("Training complete!"), loc("Good bye!"), 4, 5000)
       
   189 
       
   190 	if SaucerGear ~= nil then
       
   191 		DeleteGear(SaucerGear)
       
   192 	end
       
   193 	SetState(Player, band(GetState(Player), bnot(gstHHDriven)))
       
   194 	SetState(Player, bor(GetState(Player), gstWinner))
       
   195 	PlaySound(sndVictory, Player)
       
   196 
       
   197 	SendStat(siGameResult, loc("You have finished the Flying Saucer Training!"))
       
   198 	SendStat(siCustomAchievement, loc("Good job!"))
       
   199 	SendStat(siPlayerKills, "0", loc("Hogonauts"))
       
   200 
       
   201 	TurnTimeLeft = 0
       
   202 	EndGame()
       
   203 end,
       
   204 }
       
   205 
       
   206 -- Just a wrapper for ShowMission
       
   207 function Info(Title, Text, Icon, Time)
       
   208 	if Time == nil then Time = 0 end
       
   209 	if Icon == nil then Icon = 2 end
       
   210 	ShowMission(loc("Flying Saucer Training"), Title, Text, Icon, Time)
       
   211 end
       
   212 
       
   213 -- Spawn all the gears for the Boom Target
       
   214 function SpawnBoomTarget()
       
   215 	if TargetsRemaining < 1 then
       
   216 		TargetGears[1] = AddGear(1602, 507, gtTarget, 0, 0, 0, 0)
       
   217 		TargetsRemaining = TargetsRemaining + 1
       
   218 	end
       
   219 
       
   220 	if Barrels[1] == nil then
       
   221 		Barrels[1] = AddGear(1563, 532, gtExplosives, 0, 0, 0, 0)
       
   222 	end
       
   223 	if Barrels[2] == nil then
       
   224 		Barrels[2] = AddGear(1648, 463, gtExplosives, 0, 0, 0, 0)
       
   225 	end
       
   226 
       
   227 	for i=1,#Barrels do
       
   228 		SetHealth(Barrels[i], 1)
       
   229 	end
       
   230 end
       
   231 
       
   232 -- Generic target spawning for the current target
       
   233 function SpawnTargets()
       
   234 	for i=1,#TargetPos[TargetNumber].Targets do
       
   235 		if TargetGears[i] == nil then
       
   236 			SpawnTarget(TargetPos[TargetNumber].Targets[i].X, TargetPos[TargetNumber].Targets[i].Y,
       
   237 				TargetPos[TargetNumber].Targets[i].Type, i)
       
   238 		end
       
   239 	end
       
   240 end
       
   241 
       
   242 function SpawnTarget( PosX, PosY, Type, ID )
       
   243 	if Type ~= nil and Type ~= gtCase then
       
   244 		if Type == gtTarget then
       
   245 			TargetGears[ID] = AddGear(PosX, PosY, gtTarget, 0, 0, 0, 0)
       
   246 		end
       
   247 	else
       
   248 		TargetGears[ID] = SpawnFakeUtilityCrate(PosX, PosY, false, false)
       
   249 	end
       
   250 	TargetsRemaining = TargetsRemaining + 1
       
   251 end
       
   252 
       
   253 function AutoSpawn() -- Auto-spawn the next target after you've obtained the current target!
       
   254 	TargetNumber = TargetNumber + 1
       
   255 	TargetsRemaining = 0
       
   256 
       
   257 	if TargetPos[TargetNumber].Ammo then
       
   258 		for ammoType, count in pairs(TargetPos[TargetNumber].Ammo) do
       
   259 			AddAmmo(Player, ammoType, count)
       
   260 		end
       
   261 		if GetCurAmmoType() ~= amJetpack then
       
   262 			SetWeapon(amJetpack)
       
   263 		end
       
   264 	end
       
   265 	if TargetPos[TargetNumber].InfFuel then
       
   266 		InfFuel = true
       
   267 	else
       
   268 		InfFuel = false
       
   269 	end
       
   270 
       
   271 	-- Func (if present) will be run instead of the ordinary spawning handling
       
   272 	if TargetPos[TargetNumber].Modifier then -- If there is a modifier, run the function
       
   273 		TargetPos[TargetNumber].Func()
       
   274 		return true
       
   275 	end
       
   276 
       
   277 	-- ExtraFunc is for additional events for a target
       
   278 	if TargetPos[TargetNumber].ExtraFunc ~= nil then
       
   279 		TargetPos[TargetNumber].ExtraFunc()
       
   280 	end
       
   281 
       
   282 	local subcap
       
   283 	if TargetNumber == 1 then
       
   284 		subcap = loc("Training")
       
   285 	else
       
   286 		subcap = loc("Instructions")
       
   287 	end
       
   288 	Info(subcap, TargetPos[TargetNumber].Message, TargetPos[TargetNumber].MessageIcon, TargetPos[TargetNumber].MessageTime)
       
   289 
       
   290 	-- Spawn targets on the next position
       
   291 	SpawnTargets()
       
   292 
       
   293 	if TargetNumber > 1 then
       
   294 		AddCaption(loc("Next target is ready!"), 0xFFFFFFFF, capgrpMessage2)
       
   295 	end
       
   296 end
       
   297 
       
   298 -- Returns true if the hedgehog has safely "landed" (alive, no flying saucer gear and not moving)
       
   299 -- This is to ensure the training only continues when the player didn't screw up and to restart the current target
       
   300 function HasHedgehogLandedYet()
       
   301 	if band(GetState(Player), gstMoving) == 0 and SaucerGear == nil and GetHealth(Player) > 0 then
       
   302 		return true
       
   303 	else
       
   304 		return false
       
   305 	end
       
   306 end
       
   307 
       
   308 -- Clean up the gear mess left behind when the player failed to get a clean state after restarting
       
   309 function CleanUpGears()
       
   310 	-- (We track flames, grenades, bazooka shells)
       
   311 	runOnGears(DeleteGear)
       
   312 end
       
   313 
       
   314 -- Completely restarts the current target/objective; the hedgehog is spawned at the last "checkpoint"
       
   315 -- Called when hedgeghog is resurrected or skips turn
       
   316 function ResetCurrentTarget()
       
   317 	GrenadeThrown = false
       
   318 	GrenadeTimer = 0
       
   319 	if TargetNumber == LaunchTarget then
       
   320 		BazookasLeft = 2
       
   321 	elseif TargetNumber == UnderwaterAttackTarget then
       
   322 		BazookasLeft = 1
       
   323 	else
       
   324 		BazookasLeft = 0
       
   325 	end
       
   326 	Check = false
       
   327 
       
   328 	CleanUpGears()
       
   329 
       
   330 	local X, Y
       
   331 	if TargetNumber == 1 then
       
   332 		X, Y = StartPos.X, StartPos.Y
       
   333 	else
       
   334 		if TargetPos[TargetNumber-1].Modifier or TargetPos[TargetNumber-1].Respawn ~= nil then
       
   335 			X, Y = TargetPos[TargetNumber-1].Respawn.X, TargetPos[TargetNumber-1].Respawn.Y
       
   336 		else
       
   337 			X, Y = TargetPos[TargetNumber-1].Targets[1].X, TargetPos[TargetNumber-1].Targets[1].Y
       
   338 		end
       
   339 	end
       
   340 	if TargetNumber == BoomTarget then
       
   341 		SpawnBoomTarget()
       
   342 	end
       
   343 	if TargetPos[TargetNumber].Modifier ~= true then
       
   344 		SpawnTargets()
       
   345 	end
       
   346 	if TargetPos[TargetNumber].Ammo then
       
   347 		for ammoType, count in pairs(TargetPos[TargetNumber].Ammo) do
       
   348 			AddAmmo(Player, ammoType, count)
       
   349 		end
       
   350 		if GetCurAmmoType() ~= amJetpack then
       
   351 			SetWeapon(amJetpack)
       
   352 		end
       
   353 	end
       
   354 	if TargetPos[TargetNumber].InfFuel then
       
   355 		InfFuel = true
       
   356 	else
       
   357 		InfFuel = false
       
   358 	end
       
   359 
       
   360 	SetGearPosition(Player, X, Y)
       
   361 end
       
   362 
       
   363 function onGameInit()
       
   364 	Seed = 1
       
   365 	GameFlags = gfInfAttack + gfOneClanMode + gfSolidLand + gfDisableWind
       
   366 	TurnTime = 2000000 --[[ This rffectively hides the turn time; a turn time above 1000s is not displayed.
       
   367 				We will also ensure this timer always stays above 999s later ]]
       
   368 	CaseFreq = 0
       
   369 	MinesNum = 0
       
   370 	Explosives = 0
       
   371 	Map = "Eyes"
       
   372 	Theme = "EarthRise"
       
   373 	SuddenDeathTurns = 50
       
   374 	WaterRise = 0
       
   375 	HealthDecrease = 0
       
   376 
       
   377 	-- Team name is a pun on “hedgehog” and “astronauts”
       
   378 	AddTeam( loc( "Hogonauts" ), 0xDDDD00, "earth", "Earth", "Default", "cm_galaxy" )
       
   379 
       
   380 	-- Hedgehog name is a pun on “Neil Armstrong”
       
   381 	Player = AddHog( loc( "Neil Hogstrong" ), 0, 1, "NoHat" )
       
   382 	SetGearPosition( Player, StartPos.X, StartPos.Y)
       
   383 	SetEffect( Player, heResurrectable, 1 )
       
   384 end
       
   385 
       
   386 function onGameStart()
       
   387 	SendHealthStatsOff()
       
   388 
       
   389 	-- Girder near first crate
       
   390 	PlaceGirder(1257, 204, 6)
       
   391 
       
   392 	-- The upper girders
       
   393 	PlaceGirder(84, 16, 0)
       
   394 	PlaceGirder(1980, 16, 0)
       
   395 
       
   396 	-- The lower girder platform at the water pit
       
   397 	PlaceGirder(509, 896, 4)
       
   398 	PlaceGirder(668, 896, 4)
       
   399 	PlaceGirder(421, 896, 2)
       
   400 	PlaceGirder(758, 896, 2)
       
   401 
       
   402 	-- Girders for the Launch Target and the Underwater Attack Target
       
   403 	PlaceGirder(1191, 960, 4)
       
   404 	PlaceGirder(1311, 960, 0)
       
   405 	PlaceGirder(1460, 827, 3)
       
   406 	PlaceGirder(1509, 763, 2)
       
   407 	PlaceGirder(1605, 672, 4)
       
   408 	PlaceGirder(1764, 672, 4)
       
   409 	PlaceGirder(1803, 577, 6)
       
   410 
       
   411 	-- Spawn our 1st target using the wrapper function
       
   412 	AutoSpawn()
       
   413 end
       
   414 
       
   415 function onAmmoStoreInit()
       
   416 	SetAmmo(amJetpack, 0, 0, 0, 0)
       
   417 	SetAmmo(amGrenade, 0, 0, 0, 0)
       
   418 	SetAmmo(amBazooka, 0, 0, 0, 0)
       
   419 
       
   420 	-- Added for resetting current target/objective when player is stuck somehow
       
   421 	SetAmmo(amSkip, 9, 0, 0, 0)
       
   422 end
       
   423 
       
   424 function onGearAdd(Gear)
       
   425 	if GetGearType(Gear) == gtJetpack then
       
   426 		SaucerGear = Gear
       
   427 		if TargetNumber == BoomTarget and GrenadeThrown == false then
       
   428 			AddAmmo(Player, amGrenade, 1)
       
   429 		end
       
   430 		if (TargetNumber == LaunchTarget or TargetNumber == UnderwaterAttackTarget) and BazookasLeft > 0 then
       
   431 			AddAmmo(Player, amBazooka, BazookasLeft)
       
   432 		end
       
   433 	end
       
   434 	if GetGearType(Gear) == gtGrenade then
       
   435 		GrenadeThrown = true
       
   436 		GrenadeTimer = 0
       
   437 	end
       
   438 	if GetGearType(Gear) == gtShell then
       
   439 		BazookasLeft = BazookasLeft - 1
       
   440 	end
       
   441 	if GetGearType(Gear) == gtFlame or GetGearType(Gear) == gtGrenade or GetGearType(Gear) == gtShell then
       
   442 		trackGear(Gear)
       
   443 	end
       
   444 end
       
   445 
       
   446 function onGearDelete(Gear)
       
   447 	if GetGearType(Player) ~= nil and (GetGearType(Gear) == gtTarget or GetGearType(Gear) == gtCase) then
       
   448 		for i=1, #TargetGears do
       
   449 			if Gear == TargetGears[i] then
       
   450 				TargetGears[i] = nil
       
   451 				TargetsRemaining = TargetsRemaining - 1
       
   452 			end
       
   453 		end
       
   454 		if TargetsRemaining <= 0 then
       
   455 			if TargetNumber == BoomTarget or not HasHedgehogLandedYet() then
       
   456 				if SaucerGear then
       
   457 					AddCaption(loc("Objective completed! Now land safely."), 0xFFFFFFFF, capgrpMessage2)
       
   458 				end
       
   459 				Check = true
       
   460 				CheckTimer = 500
       
   461 			else
       
   462 				AutoSpawn()
       
   463 			end
       
   464 		end
       
   465 	end
       
   466 	if GetGearType(Gear) == gtGrenade then
       
   467 		GrenadeTimer = 0
       
   468 		GrenadeExploded = true
       
   469 	end
       
   470 	if GetGearType(Gear) == gtJetpack then
       
   471 		SaucerGear = nil
       
   472 		if TargetNumber == BoomTarget then
       
   473 			AddAmmo(Player, amGrenade, 0)
       
   474 		end
       
   475 		if TargetNumber == LaunchTarget or TargetNumber == UnderwaterAttackTarget then
       
   476 			AddAmmo(Player, amBazooka, 0)
       
   477 		end
       
   478 	end
       
   479 	if GetGearType(Gear) == gtCase and GetGearType(Player) ~= nil then
       
   480 		PlaySound(sndShotgunReload)
       
   481 	end
       
   482 	if Gear == Barrels[1] then
       
   483 		Barrels[1] = nil
       
   484 	end
       
   485 	if Gear == Barrels[2] then
       
   486 		Barrels[2] = nil
       
   487 		AddCaption(loc("Kaboom!"), 0xFFFFFFFF, capgrpMessage)
       
   488 	end
       
   489 end
       
   490 
       
   491 
       
   492 
       
   493 function onNewTurn()
       
   494 	SetWeapon(amJetpack)
       
   495 end
       
   496 
       
   497 function onGameTick20()
       
   498 	if (TurnTimeLeft < 1500000 and not Objective) then
       
   499 		TurnTimeLeft = TurnTime
       
   500 	end
       
   501 	if Check then
       
   502 		CheckTimer = CheckTimer - 20
       
   503 		if CheckTimer <= 0 then
       
   504 			if HasHedgehogLandedYet() then
       
   505 				AutoSpawn()
       
   506 				Check = false
       
   507 				GrenadeThrown = false
       
   508 			end
       
   509 		end
       
   510 	end
       
   511 	if GrenadeExploded and TargetNumber == BoomTarget then
       
   512 		GrenadeTimer = GrenadeTimer + 20
       
   513 		if GrenadeTimer > 1500 then
       
   514 			GrenadeTimer = 0
       
   515 			GrenadeThrown = false
       
   516 			GrenadeExploded = false
       
   517 			if SaucerGear and TargetNumber == BoomTarget and TargetsRemaining > 0 then
       
   518 				PlaySound(sndShotgunReload)
       
   519 				AddCaption(loc("+1 Grenade"), 0xDDDD00FF, capgrpAmmoinfo)
       
   520 				AddAmmo(Player, amGrenade, 1)
       
   521 			end
       
   522 		end
       
   523 	end
       
   524 	ResetFuel()
       
   525 end
       
   526 
       
   527 -- Used to ensure infinite fuel
       
   528 function ResetFuel()
       
   529 	if SaucerGear and InfFuel then
       
   530 		SetHealth(SaucerGear, 2000)
       
   531 	end
       
   532 end
       
   533 
       
   534 onUp = ResetFuel
       
   535 onLeft = ResetFuel
       
   536 onRight = ResetFuel
       
   537 
       
   538 function onGearDamage(Gear)
       
   539 	if Gear == Player then
       
   540 		CleanUpGears()
       
   541 		GrenadeThrown = false
       
   542 		Check = false
       
   543 	end
       
   544 end
       
   545 
       
   546 function onGearResurrect(Gear)
       
   547 	if Gear == Player then
       
   548 		AddCaption(loc("Oh no! You have died. Try again!"), 0xFFFFFFFF, capgrpMessage2)
       
   549 		ResetCurrentTarget()
       
   550 	end
       
   551 end
       
   552 
       
   553 function onHogAttack(ammoType)
       
   554 	if ammoType == amSkip then
       
   555 		AddCaption(loc("Try again!"), 0xFFFFFFFF, capgrpMessage2)
       
   556 		ResetCurrentTarget()
       
   557 	end
       
   558 end