share/hedgewars/Data/Missions/Training/Basic_Training_-_Rope.lua
changeset 13087 aa9e871f9685
parent 12776 957e4e2e2802
child 13099 ffa5cdc75826
--- a/share/hedgewars/Data/Missions/Training/Basic_Training_-_Rope.lua	Mon Mar 05 19:26:46 2018 +0100
+++ b/share/hedgewars/Data/Missions/Training/Basic_Training_-_Rope.lua	Tue Mar 06 03:48:03 2018 +0100
@@ -1,205 +1,399 @@
---Created by Patrick Nielsen
---It's been so fun to create this, your welcome to contact me at Trivkz@gmail.com
---
---I've tried to keep the code as clear as possible and with comments.
---But as English is not my first language there may be spelling / grammar mistakes.
---
---I know there need to be more "tutorial" specefic messages, but I had a hard timer figuring out what to type / what would be the best technical description.
+--[[
+	Basic Rope Training
+	Teaches the player how to rope! No advanced tricks, just the basics. But fun! (I hope)
 
+	Lesson plan:
+	- Select rope
+	- Shoot rope, attach, detach
+	- Extend, retract, swing to reach easy target
+	- Multiple shots / rope re-use to go over water hazard
+	- Drop grenade from rope
+	- Special rules when you only got 1 rope (i.e. when the rope is officially used up)
+	- Rope around obstacles and mines
+]]
 
 HedgewarsScriptLoad("/Scripts/Locale.lua")
-HedgewarsScriptLoad("/Scripts/Utils.lua") -- For the gearIsInBox function, wrote my own, but decided it was a waste to include it
+HedgewarsScriptLoad("/Scripts/Utils.lua")
+
+-- Map definition automatically converted from HWMAP file by hwmap2lua.sh
+local map =
+{'\2\30\7\126\135\2\28\5\52\0\1\211\6\203\135\3\207\6\189\0\3\1\5\205\135\4\120\5\203\0\2\30\5\31\135\2\35\4\173\0\1\170\4\216\135\4\150\4\248\0\3\230\4\138\135\4\54\6\22\0\3\205\6\189\135\4\154\7\91\0\4\154\7\91\135\6\57\7\41\0\4\79\6\38\135\4\221\6\112\0\4\221\6\112\135\6\27\6\70\0\3\175\6\196\135\4\51\5\235\0\3\17\6\178\135\3\161\5\226\0\4\33\7\4\135\4\191\6\93\0\4\200\7\75\135\5\45\6\137\0\6\29\6\66\135\6\98\5\123\0\6\107\7\32\135\6\237\5\249\0\6\102\5\95\135\7\169\5\6\0\7\0\5\237\135\7\235\6\20\0\7\233\6\13\135\7\226\8\43\0\13\66\6\47\135\13\100\8\57\0\7\167\5\15\135\7\164\4\51\0\7\71\4\109\135\13\235\4\132\0\13\4\6\75\135\14\242\6\57\0\14\169\6\166\135\15\183\4\86\0',
+'\15\208\5\13\135\15\96\2\248\0\15\206\3\104\135\13\175\2\88\0\14\171\5\17\140\14\219\4\77\138\14\166\3\136\136\13\219\3\31\136\13\184\4\228\136\13\173\3\250\0\14\2\4\19\136\7\36\3\211\0\14\52\2\85\137\7\57\2\76\0\12\196\2\245\137\11\195\3\127\137\10\174\2\223\137\9\174\3\113\137\8\160\2\211\137\8\11\3\111\137\6\249\3\218\131\6\66\3\209\0\5\88\3\202\131\4\38\3\193\0\5\33\3\207\131\5\31\5\31\0\4\235\4\228\131\6\125\4\251\0\6\98\3\214\131\6\80\5\1\0\7\41\2\104\136\3\253\2\72\0\4\31\3\207\133\1\149\3\166\0\2\243\4\15\133\1\117\2\202\0\1\149\3\60\133\1\211\1\87\0\1\138\1\138\133\3\42\0\71\0\2\131\0\96\133\4\168\0\149\0\5\116\1\124\141\7\41\0\204\141\8\252\2\42\141\10\39\0\135\141\11\40\1\124\141\10\17\1\186\141',
+'\12\104\0\151\141\4\29\0\156\137\14\160\0\69\0\13\182\1\133\137\13\171\2\78\0\13\201\0\78\137\13\189\0\197\0\13\194\1\195\137\15\148\2\147\0\14\189\0\59\137\15\222\0\217\0\15\226\0\215\137\15\164\2\145\0\13\217\1\211\137\15\167\1\220\0\14\208\2\37\164\8\89\1\92\139\6\146\1\211\139\8\174\2\39\203\10\19\2\7\203\11\56\1\167\203\12\106\0\217\203\7\36\1\12\203\5\182\1\163\203\6\141\1\165\203\8\71\1\135\203\8\151\3\19\203\8\7\3\70\203\9\227\3\118\203\13\66\3\113\133\13\54\4\104\0\11\200\3\26\133\11\191\4\84\0\9\160\2\255\133\9\160\4\84\0\8\16\3\163\133\8\18\4\109\0\9\108\3\53\197\10\19\3\70\0\14\25\5\228\132\14\43\4\253\130\13\237\3\241\210\13\217\3\225\210\13\95\4\74\140\7\137\4\40\0\2\209\6\75\207\3\255\6\102\0\4\1\6\107\207',
+'\4\182\6\228\0\4\182\6\226\207\6\36\6\189\0\6\36\6\201\207\6\166\5\191\0\4\138\4\100\158\1\183\4\68\0\4\145\5\8\158\4\203\5\201\0\5\141\5\198\158\5\180\5\139\0\7\116\4\230\142\5\49\5\52\0\6\185\4\47\148\6\182\4\186\0\7\64\4\88\153\4\216\4\10\143\13\255\2\42\146\15\100\2\216\0\4\1\6\20\199\2\142\6\29\0\3\255\6\22\199\5\22\6\224\0\7\231\5\198\133\7\235\6\31\0\13\70\6\68\133\13\54\5\237\0\14\36\4\253\197\4\90\2\223\133\4\239\3\97\0\2\181\2\252\133\3\95\2\156\0\2\216\1\90\133\3\113\1\218\0\4\200\1\32\133\4\122\1\167\0\7\169\1\106\133\4\10\4\106\213\1\30\4\86\0\4\58\5\54\219\1\119\5\6\139\1\119\5\157\0\1\238\5\189\139\0\85\5\164\0\0\105\6\63\139\0\119\3\182\0\0\37\3\188\134\1\231\3\195\0',
+'\1\142\5\13\145\1\188\5\102\0\1\199\5\59\145\3\209\5\198\134\5\59\5\102\0\4\63\5\111\134\0\121\3\225\134\4\111\3\234\0\2\255\4\106\134\3\12\4\228\0\2\58\4\120\132\13\148\2\60\132\13\146\1\106\0'}
 
-local Player = nil -- Pointer to hog created in: onGameInit
-local Target = nil -- Pointer to target hog
-local GameLost = false -- You lost the game
-local Objective = false -- Get to the target
-local RopeMaster = false -- Achievement
-local StatsSent = false -- Remember whether the stats have been sent already
+local function drawMap()
+	for m=1, #map do
+		ParseCommand("draw "..map[m])
+	end
+end
+
+-- Gears
+local hog
+local ropeGear
+local mines = {}
 
-local WaitTime = 1000 -- Wait 1sec before quit
-local FollowTime = 1500 -- For use with FollowGear
-local FollowingGear = false
-local BaseballIntro = false -- Fail safe for ticker
-local TargetNumber = 0 -- The current target number
+-- Status vars
+local ropeSelected = false	-- rope was selected the first time, used for msg
+local ropeAttached = false	-- rope was attached, used for msg
+local target1Reached = false	-- hog near 1st first target
+local barrelsBoom = false	-- barrels exploded
+local wasFirstTurn = false	-- first turn msg was displayed
+local gameOver = false		-- game over (only victory possible)
+local currentTarget = 0		-- current target ID. First target = 1
+
+local cpX, cpY = 208, 1384	-- hog checkpoint, initialized with start coords
 
-local TargetPos = {} -- Tabel of targets
-local Timers = {}
-local GetTime = 0
+-- "Constants"
+local initHogHealth = 50
+local teamName = loc("Wannabe Ropers")
+
+local girderData = {
+	{2012, 1366, 6}, -- water gate
+	{1156, 678, 2}, -- post-barrel gate
+	{1206, 905, 2}, -- post-barrel gate
+	{1064, 288, 6}, -- top level gate
+	{1064, 455, 6}, -- top level gate
+	{1557, 1009, 0}, -- barrel pit protection
+	{1436, 1003, 4}, -- barrel pit protection
+	{3607, 1307, 4}, -- post-water gate
+	{3809, 1375, 0}, -- post-water gate
+}
 
-TargetPos[ 1 ] = { X = 1100, Y = 1100, Message = loc("Now find the next target! |Tip: Normally you lose health by falling down, so be careful!") }
-TargetPos[ 2 ] = { X = 1500, Y = 1490, Message = loc("You're getting pretty good! |Tip: When you shorten you rope, you move faster!|And when you lengthen it, you move slower.") }
-TargetPos[ 3 ] = { X = 2200, Y = 800, Message = loc("The next one is pretty hard! |Tip: You have to do multiple swings!") }
-TargetPos[ 4 ] = { X = 2870, Y = 400, Message = loc("I don't know how you did that. But good work!|The next one should be easy as cake for you!") }
-TargetPos[ 5 ] = { X = 4000, Y = 1750, Message = "" }
-TargetPos[ 6 ] = { Modifier = true, Func = function() -- Last target is ALWAYS the "winning" target!
-	Info( loc("Congratulations"), loc("Congratulations! You've completed the Basic Rope Training!"), 0 ) -- Congrats
-	PlaySound( sndVictory, Player )
+local mineData = {
+	{1261, 549},
+	{1395, 554},
+	{1719, 458},
+	{1489, 558},
+	{1686, 565},
+	{1581, 561},
+	{1904, 539},
+	{2047, 539},
+	{2226, 608},
+	{2387, 541},
+	{2523, 396},
+	{2899, 324},
+	{3428, 546},
+	{3373, 546},
+	{3271, 546},
+	{3123, 545},
+	{3012, 544},
+	{2907, 544},
+	{2793, 543},
+}
 
-	AddCaption( loc( "Victory!" ), 0xFFFFFFFF, capgrpGameState)
-	if TurnTimeLeft >= 250000 then -- If you very fast, unlock the ahievement "Rope Master!"
-		RopeMaster = true
-		AddCaption( string.format(loc("Achievement gotten: %s"), loc("Rope Master") ), 0xffba00ff, capgrpMessage2)
-		PlaySound( sndHomerun )
-	end
-	Objective = true
-end }
+local targetData = {
+	-- 1: Start
+	{504, 1215},
+	-- 2: Start 2
+	{1082, 1348},
+	-- 3: Before the water
+	{1941, 1490},
+	-- 4: After the water
+	{3504, 1557},
+	-- 5: Barrel pit
+	{2060, 885},
+	-- 6: Grand Finale
+	{757, 679},
+	-- 7: Goal
+	{3802, 356},
+}
+
+function onGameInit()
 
-function Info( Title, Text, Icon ) -- I made a small wrapper to ease the process
-	ShowMission( loc("Rope Training"), Title, Text, Icon, 0 )
+	ClearGameFlags()
+	EnableGameFlags(gfDisableGirders, gfDisableLandObjects, gfOneClanMode, gfInfAttack, gfSolidLand, gfDisableWind)
+	Seed = "{386439b4-748a-48b1-945a-eba6a817ca83}"
+	Theme = "Bamboo"
+	MapGen = mgDrawn
+	MapFeatureSize = 12
+	TemplateFilter = 0
+	TemplateNumber = 0
+	TurnTime = 9999000
+	Explosives = 0
+	MinesNum = 0
+	CaseFreq = 0
+	MinesTime = 0
+	WaterRise = 0
+	HealthDecrease = 0
+
+	AddTeam(teamName, 0xFF0204, "Plinko", "Snail", "Default", "cm_yinyang")
+	hog = AddHog(loc("Roper"), 0, initHogHealth, "StrawHat")
+	SetGearPosition(hog, cpX, cpY)
+	SetEffect(hog, heResurrectable, 1)
+
+	drawMap()
+
+	SendHealthStatsOff()
+
 end
 
-function NewFollowGear( Gear )
-	FollowingGear = true
-	FollowGear( Gear )
+-- The final challenge is to rope through a mined obstacle course with only 1 rope.
+-- If the player screws up, this functinon will restart it.
+local function resetMineChallenge(setPos)
+	if setPos == nil then
+		setPos = true
+	end
+	SetHealth(hog, initHogHealth)
+	AddAmmo(hog, amRope, 1)
+	for gear, _ in pairs(mines) do
+		DeleteGear(gear)
+	end
+
+	for m=1, #mineData do
+		AddGear(mineData[m][1], mineData[m][2], gtMine, 0, 0, 0, 0)
+	end
+	if setPos then
+		SetGearPosition(hog, cpX, cpY)
+		FollowGear(hog)
+	end
 end
 
-function SpawnTarget( PosX, PosY )
-	Target = AddGear( 0, 0, gtTarget, 0, 0, 0, 0 ) -- Create a new target
-	SetGearPosition( Target, PosX, PosY ) -- Set the position of the target
-	NewFollowGear( Target )
+-- Deletes girder with given girderData ID
+local function eraseGirder(id)
+	EraseSprite(girderData[id][1], girderData[id][2], sprAmGirder, girderData[id][3], false, false, false, false)
+	PlaySound(sndVaporize)
+	AddVisualGear(girderData[id][1], girderData[id][2], vgtSteam, false, 0)
+	AddCaption(loc("Barrier unlocked!"))
 end
 
-function AutoSpawn() -- Auto spawn the next target after you've killed the current target!
-	TargetNumber = TargetNumber + 1
+local function loadGearData()
+	------ GIRDERS ------
+	for g=1, #girderData do
+		PlaceGirder(unpack(girderData[g]))
+	end
 
-	if TargetPos[ TargetNumber ].Modifier then -- If there is a modifier, run the function, only used in the winning target!
-		TargetPos[ TargetNumber ].Func()
-		return true
+	------ BARRELS ------
+	local barrels = {}
+	table.insert(barrels, AddGear(1370, 1223, gtExplosives, 0, 0, 0, 0))
+	table.insert(barrels, AddGear(1430, 1226, gtExplosives, 0, 0, 0, 0))
+	table.insert(barrels, AddGear(1489, 1218, gtExplosives, 0, 0, 0, 0))
+	table.insert(barrels, AddGear(1537, 1211, gtExplosives, 0, 0, 0, 0))
+	table.insert(barrels, AddGear(1578, 1206, gtExplosives, 0, 0, 0, 0))
+	for b=1, #barrels do
+		SetHealth(barrels[b], 1)
+	end
+
+	------ MINES ------
+	for m=1, #mineData do
+		AddGear(mineData[m][1], mineData[m][2], gtMine, 0, 0, 0, 0)
 	end
 
-	if TargetNumber > 1 then
-		Info(loc("Training"), TargetPos[ TargetNumber - 1 ].Message, -amRope )
+	------ FIRST TARGET ------
+	currentTarget = 1
+	AddGear(targetData[currentTarget][1], targetData[currentTarget][2], gtTarget, 0, 0, 0, 0)
+end
+
+function onGameStart()
+	loadGearData()
+
+	ShowMission(loc("Basic Rope Training"), loc("Basic Training"),
+	loc("Use the rope to complete the obstacle course!"), -amRope, 0)
+	FollowGear(hog)
+end
+
+function onNewTurn()
+	if not wasFirstTurn then
+		ShowMission(loc("Basic Rope Training"), loc("Select Rope"),
+		loc("Select the rope to begin!").."|"..
+		loc("Open ammo menu: [Right click]"), 2, 7500)
+		wasFirstTurn = true
 	end
-
-	SpawnTarget( TargetPos[ TargetNumber ].X, TargetPos[ TargetNumber ].Y ) -- Spawn target on the next position
+	if isInMineChallenge then
+		resetMineChallenge()
+	end
 end
 
-function InRange( Gear, PosX, PosY, Distance ) -- Fix as the default function didn't do quite what I needed
-	GearX, GearY = GetGearPosition( Gear )
+function onGameTick()
+	if gameOver or (not CurrentHedgehog) then
+		return
+	end
 
-    return GearX >= PosX - Distance and GearX <= PosX + Distance and GearY >= PosY and GearY - Distance <= PosY + Distance
+	-- First rope selection
+	if not ropeSelected and GetCurAmmoType() == amRope then
+		ShowMission(loc("Basic Rope Training"), loc("Getting Started"),
+		loc("You can use the rope to reach new places.").."|"..
+		loc("Aim at the ceiling and hold [Attack] pressed until the rope attaches.").."|"..
+		loc("Aim: [Up]/[Down]").."|"..
+		loc("Attack: [Space]"), 2, 15000)
+		ropeSelected = true
+	-- Rope attach
+	elseif ropeGear and band(GetState(ropeGear), gstCollision) ~= 0 then
+		-- First rope attach
+		if not ropeAttached and not target1Reached then
+			ShowMission(loc("Basic Rope Training"), loc("How to Rope"),
+			loc("Great!").."|"..
+			loc("Use the rope to get to the target!").."|"..
+			loc("Extend/Retract rope: [Up]/[Down]").."|"..
+			loc("Swing: [Left]/[Right]").."|"..
+			loc("Release rope: [Attack]"), 2, 15000)
+			ropeAttached = true
+		elseif currentTarget > 1 then
+			HideMission()
+		end
+	end
+
+	-- Prevent grenade being thrown by hand (must use from rope instead)
+	local allowAttack = true
+	if GetCurAmmoType() == amGrenade and ropeGear == nil then
+		allowAttack = false
+	end
+	if allowAttack then
+		SetInputMask(bor(GetInputMask(), gmAttack))
+	else
+		SetInputMask(band(GetInputMask(), bnot(gmAttack)))
+	end
 end
 
-function CheckPosition( Hog, Distance ) -- Show a message when you get close to the current target!
-	if (not BaseballIntro and not Objective) and (CurrentHedgehog ~= nil) then --Fail safe check
-		if InRange( Hog, 1100, 1100, Distance ) then -- Check if the player is within predefined position of the first target
-			BaseballIntro = true
-			Info(loc("Training"), loc("Great work! Now hit it with your Baseball Bat! |Tip: You can change weapon with 'Right Click'!"), -amRope ) -- Guide them
-			Timer( 10000, loc("Remember: The rope only bend around objects, |if it doesn't hit anything it's always stright!"))
+function onGameTick20()
+	if not gameOver and not target1Reached and CurrentHedgehog and gearIsInCircle(CurrentHedgehog, targetData[1][1], targetData[1][2], 48, false) then
+		ShowMission(loc("Basic Rope Training"), loc("Target Puncher"),
+		loc("Okay, now destroy the target|using the baseball bat.").."|"..
+		loc("Release rope: [Attack]"), 2, 9000)
+		target1Reached = true
+	end
+end
+
+function onGearAdd(gear)
+	if GetGearType(gear) == gtRope then
+		ropeGear = gear
+	elseif GetGearType(gear) == gtMine then
+		mines[gear] = true
+	elseif GetGearType(gear) == gtGrenade then
+		if not ropeGear then
+			DeleteGear(gear)
 		end
 	end
 end
 
-function Timer( Delay, Message )
-	local Timer = {}
-	Timer.End = GetTime + Delay
-	Timer.Message = Message
-
-	table.insert( Timers, Timer )
-end
-
-function onGameInit() -- Called when the game loads
-	Seed = 1 -- The base number for the random number generator
-	GameFlags = gfInfAttack + gfOneClanMode + gfSolidLand + gfInvulnerable + gfBorder -- Game settings and rules, going with a border to make it easier
-	TurnTime = 300000 -- Player can move for 5min each round
-	CaseFreq = 0 -- No random crate drops
-	MinesNum = 0 -- Never place any mines on the map
-	Explosives = 0 -- Never place any explosives
-	Delay = 1 -- We don't wont to wait between each round ( as the only is one )
-	Map = "Ropes" -- Map name
-	Theme = "Nature" -- Map theme
-	-- Disable Sudden Death
-	SuddenDeathTurns = 50
-	WaterRise = 0
-	HealthDecrease = 0
-
-	AddTeam( loc( "Rope Team" ), 14483456, "Simple", "Island", "Default", "cm_shoppa" ) -- Lets make the team
-	Player = AddHog( loc( "Hunter" ), 0, 1, "StrawHat" ) -- Add a hog for it, and name it "Hunter"
-	SetGearPosition( Player, 420, 1750 ) -- Set player position
-
-	SetEffect( Player, heResurrectable, 1 ) -- By Suggestion :)
-end
-
-function onGameStart() -- Called when the game starts
-	SendHealthStatsOff()
-	AutoSpawn() -- Spawn our 1st target using the wrapper function
-
-	SetHealth( Player, 100 ) -- Give the player 100 Health points
-
-	PlaceGirder(46,1783, 0) -- Place a girder to prevent the player falling into the water
-
-	local message = loc("Get to the target using your rope!") .. "|" .. loc("Controls: Hold the Attack key (space by default) to|fire the rope, then, once attached use:|Left and Right to swing the rope;|Up and Down to contract and expand.")
-	Info(loc("Training"), message, -amRope ) -- Short intro to tell the player what to do
-	Timer( 10000, message .. "|" .. loc("Tip: The rope physics are different than in the real world, |use it to your advantage!") ) -- After 15 sec, give them more help
-end
-
-function onNewTurn()
-	SetWeapon(amRope) -- Set the default weapon to Rope
+function onGearResurrect(gear)
+	-- Teleport hog to previous checkpoint
+	if gear == hog then
+		SetGearPosition(hog, cpX, cpY)
+		FollowGear(hog)
+		AddCaption(loc("Your hedgehog has been revived!"))
+		if isInMineChallenge then
+			resetMineChallenge(false)
+		end
+	end
 end
 
-function onGameTick20()
-	if TurnTimeLeft < 40 and TurnTimeLeft > 0 then -- Round starts at 0, so we check if the round is finished by using 1
-		GameLost = true -- You lost the game
-		Info(loc("Training"), loc("You did not make it in time, try again!"), -amSkip )
-		if not StatsSent then
-			SendStat(siGameResult, loc("You failed!"))
-			SendStat(siCustomAchievement, loc("You did not make it in time, try again!"))
-			StatsSent = true
-		end
-		SetHealth( Player, 0 ) -- Kill the player so he can't keep moving!
+function onGearDelete(gear)
+	if GetGearType(gear) == gtTarget then
+		-- Update checkpoint
+		cpX, cpY = GetGearPosition(gear)
 
-		SetEffect( Player, heResurrectable, 0 )
-
-	end
-
-	-- If the player gets to the last target, they win OR
-	-- If round is finished and your not at the target you lose
-	-- in either case, end the game
-	if (Objective == true) or (GameLost == true) then
-		if (WaitTime == 0 and not StatsSent) then
+		-- New message
+		if currentTarget == 1 then
+			ShowMission(loc("Basic Rope Training"), loc("Obstacle"),
+			loc("Well done! Let's destroy the next target!").."|"..
+			loc("The targets will guide you through the training.").."|"..
+			loc("Use your rope to get to the next target, then destroy it!"), 2, 8000)
+		elseif currentTarget == 2 then
+			ShowMission(loc("Basic Rope Training"), loc("Speed Roping"),
+			loc("Try to reach and destroy the next target quickly.").."|"..
+			loc("Hint: When you shorten the rope, you move faster!|And when you lengthen it, you move slower."), 2, 15000)
+		elseif currentTarget == 3 then
+			ShowMission(loc("Basic Rope Training"), loc("Over the Water"),
+			loc("When you're in mid-air, you can continue to aim|and fire another rope if you're not attached.").."|"..
+			loc("To get over the water, you have to do multiple|rope shots and swings.").."|"..
+			loc("It needs some practice, but you have infinite lives.").."|"..
+			loc("Good luck!"), 2, 22500)
+			eraseGirder(1)
+		elseif currentTarget == 4 then
+			ShowMission(loc("Basic Rope Training"), loc("Little Obstacle Course"),
+			loc("Well done! The next target awaits.").."|"..
+			loc("Hint: The rope only bends around objects.|When it doesn't hit anything, it's always straight."), 2, 7000)
+			eraseGirder(8)
+			eraseGirder(9)
+		elseif currentTarget == 5 then
+			ShowMission(loc("Basic Rope Training"), loc("Rope Weapons"),
+			loc("Some weapons can be dropped from the rope.").."|"..
+			loc("Collect the weapon crate and drop|a grenade from rope to destroy the barrels.").."|"..
+			loc("Step 1: Start roping").."|"..
+			loc("Step 2: Select grenade").."|"..
+			loc("Step 3: Drop the grenade").."| |"..
+			loc("Drop weapon (while on rope): [Long Jump]"), 2, 20000)
+			AddAmmo(hog, amBaseballBat, 0)
+			SpawnAmmoCrate(1849, 920, amGrenade, 100)
+		elseif currentTarget == 6 then
+			ShowMission(loc("Basic Rope Training"), loc("Finite Ropes"),
+			loc("So far, you had infinite ropes, but in the|real world, ropes are usually limited.").."|"..
+			loc("Rules:").." |"..
+			loc("As long you don't touch the ground, you can|re-use the same rope as often as you like.").."|"..
+			loc("If you miss a shot while trying to|re-attach, your rope is gone, too!").."| |"..
+			loc("Final Challenge:").." |"..
+			loc("Reach and destroy the final target to win.").."|"..
+			loc("You only get 1 rope this time, don't waste it!").."|"..
+			loc("Avoid the mines!").."|"..
+			loc("Skip your turn to try again."), 2, 25000)
+			eraseGirder(4)
+			eraseGirder(5)
+			AddAmmo(hog, amRope, 1)
+			AddAmmo(hog, amSkip, 100)
+			isInMineChallenge = true
+		elseif currentTarget == 7 then
+			ShowMission(loc("Basic Rope Training"), loc("Training complete!"),
+			loc("Congratulations!"), 0, 0)
+			PlaySound(sndVictory, hog)
+			AddAmmo(hog, amBaseballBat, 0)
+			AddAmmo(hog, amGrenade, 0)
+			AddAmmo(hog, amRope, 0)
+			SendStat(siCustomAchievement, loc("Oh yeah! You sure know how to rope!"))
 			SendStat(siGameResult, loc("You have finished the Basic Rope Training!"))
-			SendStat(siCustomAchievement, loc("Good job!"))
-			SendStat(siPlayerKills, "0", loc("Rope Team"))
-			if RopeMaster then
-				SendStat(siCustomAchievement, loc("You earned the \"Rope Master\" achievement for finishing in under 50 seconds."))
-			end
-			StatsSent = true
+			SendStat(siPlayerKills, "0", teamName)
 			EndGame()
-		else
-			WaitTime = WaitTime - 20
+			gameOver = true
+			SetInputMask(0)
 		end
-	end
+		currentTarget = currentTarget + 1
+
+		if currentTarget <= #targetData then
+			AddGear(targetData[currentTarget][1], targetData[currentTarget][2], gtTarget, 0, 0, 0, 0)
+		end
 
-	if FollowingGear == true then
-		if FollowTime == 0 then
-			FollowingGear = false
-			FollowTime = 1500
-			FollowGear( Player )
-		else
-			FollowTime = FollowTime - 20
+	elseif GetGearType(gear) == gtExplosives then
+		if not barrelsBoom then
+			barrelsBoom = true
+			AddAmmo(hog, amGrenade, 0)
+			AddAmmo(hog, amBaseballBat, 100)
+			eraseGirder(2)
+			eraseGirder(3)
 		end
+	elseif GetGearType(gear) == gtRope then
+		ropeGear = nil
+		if ropeAttached and not target1Reached then
+			ShowMission(loc("Basic Rope Training"), loc("How to Rope"),
+			loc("Go to the target.").."|"..
+			loc("Hold [Attack] to attach the rope.").."|"..
+			loc("Aim: [Up]/[Down]").."|"..
+			loc("Attack: [Space]"), 2, 13000)
+			ropeAttached = false
+		end
+	elseif GetGearType(gear) == gtMine then
+		mines[gear] = nil
+	elseif GetGearType(gear) == gtCase then
+		eraseGirder(6)
+		eraseGirder(7)
 	end
-
-	for k, v in pairs( Timers ) do
-		if v.End <= GetTime then
-			Info(loc("Training"), v.Message, -amRope )
-			Timers[ k ] = nil
-		end
-	end
-
-	GetTime = GetTime + 20
-
-	CheckPosition( Player, 70 ) -- Run the CheckPosition function to check if the player is close to a target
 end
 
 function onAmmoStoreInit()
-	SetAmmo( amRope, 9, 2, 0, 0 ) -- Player ammo, Rope
-	SetAmmo( amBaseballBat, 9, 2, 0, 0 ) --Baseball bat
+	SetAmmo(amRope, 9, 0, 0, 1)
+	SetAmmo(amBaseballBat, 9, 0, 0, 1)
 end
 
-function onGearDelete( Gear )
-	if GetGearType( Gear ) == gtTarget then
-		AutoSpawn() -- When a target is deleted / destroyed, spawn a new one!
+function onHogAttack(ammoType)
+	-- Allow to manually reset final challenge with skip
+	if ammoType == amSkip then
+		resetMineChallenge()
 	end
 end
+
+function onAttack()
+	if GetCurAmmoType() == amGrenade and not ropeGear then
+		AddCaption(loc("You have to drop the grenade from rope!"), 0xFF4000FF, capgrpMessage)
+		PlaySound(sndDenied)
+	end
+end