Sort all core missions in training menu more logically
The missions are now sorted as specified in the new order.cfg files in the Missions subfolders.
Training missions are now sorted in the order you should play them.
Challenges are semi-logically grouped.
Scenarios are roughly sorted by difficulty.
All missions not mentioned in order.cfg are simply added in no particular
order at the end.
--[[
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
{757, 679},
-- 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, 0xFF0204, "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("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 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)
-- 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 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