share/hedgewars/Data/Missions/Training/Basic_Training_-_Rope.lua
author Wuzzy <Wuzzy2@mail.ru>
Tue, 07 Aug 2018 13:04:02 +0200
changeset 13635 fe7d2bbf5f3f
parent 13588 141cdfe0f3ca
child 13669 85506d95977d
permissions -rw-r--r--
Fix resurrection animation appearing at wrong position for some missions and styles

--[[
	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")

-- 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 function drawMap()
	for m=1, #map do
		ParseCommand("draw "..map[m])
	end
end

-- Gears
local hog
local ropeGear
local mines = {}

-- 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

-- "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
}

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},
}

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
	{834, 381},
	-- 7: Goal
	{3802, 356},
}

function onGameInit()

	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, -1, "Plinko", "Snail", "Default", "cm_yinyang")
	hog = AddHog(loc("Roper"), 0, initHogHealth, "StrawHat")
	SetGearPosition(hog, cpX, cpY)
	SetEffect(hog, heResurrectable, 1)

	drawMap()

	SendHealthStatsOff()

end

-- 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

-- 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

local function loadGearData()
	------ GIRDERS ------
	for g=1, #girderData do
		PlaceGirder(unpack(girderData[g]))
	end

	------ 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

	------ 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
	if isInMineChallenge then
		resetMineChallenge()
	end
end

function onGameTick()
	if gameOver or (not CurrentHedgehog) then
		return
	end

	-- 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("Retract/Extend 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 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 onGearResurrect(gear, vGear)
	-- Teleport hog to previous checkpoint
	if gear == hog then
		SetGearPosition(hog, cpX, cpY)
		if vGear then
			SetVisualGearValues(vGear, GetX(hog), GetY(hog))
		end
		FollowGear(hog)
		AddCaption(loc("Your hedgehog has been revived!"))
		if isInMineChallenge then
			resetMineChallenge(false)
		end
	end
end

function onGearDelete(gear)
	if GetGearType(gear) == gtTarget then
		-- Update checkpoint
		cpX, cpY = GetGearPosition(gear)

		-- 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(siPlayerKills, "0", teamName)
			EndGame()
			gameOver = true
			SetInputMask(0)
		end
		currentTarget = currentTarget + 1

		if currentTarget <= #targetData then
			AddGear(targetData[currentTarget][1], targetData[currentTarget][2], gtTarget, 0, 0, 0, 0)
		end

	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
end

function onAmmoStoreInit()
	SetAmmo(amRope, 9, 0, 0, 1)
	SetAmmo(amBaseballBat, 9, 0, 0, 1)
end

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