1 -- Hedgewars Bazooka Training |
1 --[[ |
2 -- Scripting Example |
2 Basic Bazooka Training |
3 |
3 |
4 -- Lines such as this one are comments - they are ignored |
4 This training missions teaches players how to use the bazooka. |
5 -- by the game, no matter what kind of text is in there. |
5 Lesson plan: |
6 -- It's also possible to place a comment after some real |
6 - Selecting bazooka |
7 -- instruction as you see below. In short, everything |
7 - Aiming and shooting |
8 -- following "--" is ignored. |
8 - Wind |
9 |
9 - Limited ammo |
10 --------------------------------------------------------------- |
10 - “Bouncing bomb” / water skip |
11 -- At first we implement the localization library using loadfile. |
11 - Precise aiming |
12 -- This allows us to localize strings without needing to think |
12 ]] |
13 -- about translations. |
|
14 -- We can use the function loc(text) to localize a string. |
|
15 |
13 |
16 HedgewarsScriptLoad("/Scripts/Locale.lua") |
14 HedgewarsScriptLoad("/Scripts/Locale.lua") |
17 |
15 |
18 -- This variable will hold the number of destroyed targets. |
16 local hog -- Hog gear |
19 local score = 0 |
17 local weaponSelected = false -- Player has selected the weapon |
20 -- This variable represents the number of targets to destroy. |
18 local gamePhase = 0 -- Used to track progress |
21 local score_goal = 5 |
19 local targetsLeft = 0 -- # of targets left in this round |
22 -- This variable controls how many milliseconds/ticks we'd |
20 local targetGears = {} -- list of target gears |
23 -- like to wait before we end the round once all targets |
21 local bazookasInGame = 0 -- # of bazookas currently flying |
24 -- have been destroyed. |
22 local bazookaGears = {} -- list of bazooka gears |
25 local end_timer = 1000 -- 1000 ms = 1 s |
23 local limitedAmmo = 5 -- amount of ammo for the limited ammo challenge |
26 -- This variable is set to true if the game is lost (i.e. |
24 local limitedAmmoReset = -1 -- Timer for resetting ammo if player fails in |
27 -- time runs out). |
25 -- limited ammo challenge. -1 = no-op |
28 local game_lost = false |
26 local gameOver = false -- If true, game has ended |
29 -- This variable will point to the hog's gear |
27 local shotsFired = 0 -- Total # of bazookas fired |
30 local player = nil |
28 local maxTargets = 0 -- Target counter, used together with flawless |
31 -- This variable will grab the time left at the end of the round |
29 local flawless = true -- track flawless victory (100% accuracy, no hurt, no death) |
32 local time_goal = 0 |
30 local missedTauntTimer = -1 -- Wait timer for playing sndMissed. -1 = no-op |
33 -- This variable stores the number of bazooka shots |
31 |
34 local shots = 0 |
|
35 |
|
36 -- This is a custom function to make it easier to |
|
37 -- spawn more targets with just one line of code |
|
38 -- You may define as many custom functions as you |
|
39 -- like. |
|
40 function spawnTarget() |
|
41 -- add a new target gear |
|
42 gear = AddGear(0, 0, gtTarget, 0, 0, 0, 0) |
|
43 |
|
44 -- move it to a random position within 0 and |
|
45 -- LAND_WIDTH - the width of the map |
|
46 FindPlace(gear, true, 0, LAND_WIDTH) |
|
47 |
|
48 -- move the target to a higher vertical position |
|
49 -- to ensure it's not somewhere down below |
|
50 x, y = GetGearPosition(gear) |
|
51 SetGearPosition(gear, x, 0) |
|
52 end |
|
53 |
|
54 -- This function is called before the game loads its |
|
55 -- resources. |
|
56 -- It's one of the predefined function names that will |
|
57 -- be called by the game. They give you entry points |
|
58 -- where you're able to call your own code using either |
|
59 -- provided instructions or custom functions. |
|
60 function onGameInit() |
32 function onGameInit() |
61 -- At first we have to overwrite/set some global variables |
33 |
62 -- that define the map, the game has to load, as well as |
34 ClearGameFlags() |
63 -- other things such as the game rules to use, etc. |
35 EnableGameFlags(gfDisableWind, gfOneClanMode, gfInfAttack, gfSolidLand) |
64 -- Things we don't modify here will use their default values. |
36 Map = "" |
65 |
37 Seed = 0 |
66 -- The base number for the random number generator |
38 Theme = "Nature" |
67 Seed = 1 |
39 MapGen = mgDrawn |
68 -- Game settings and rules |
40 TurnTime = 9999000 |
69 EnableGameFlags(gfMultiWeapon, gfOneClanMode, gfSolidLand, gfDisableWind) |
41 Explosives = 0 |
70 -- Uncommenting this wouldn't do anything |
42 MinesNum = 0 |
71 --EnableGameFlags(gfMultiWeapon, gfOneClanMode, gfSolidLand) |
|
72 -- Neither this |
|
73 --DisableGameFlags(gfArtillery) |
|
74 -- Uncommenting this would make the terrain damageable |
|
75 --DisableGameFlags(gfSolidLand) |
|
76 -- Uncommenting this would remove all flags set previously |
|
77 --ClearGameFlags() |
|
78 -- The time the player has to move each round (in ms) |
|
79 TurnTime = 60000 |
|
80 -- The frequency of crate drops |
|
81 CaseFreq = 0 |
43 CaseFreq = 0 |
82 -- The number of mines being placed |
44 WaterRise = 0 |
83 MinesNum = 0 |
45 HealthDecrease = 0 |
84 -- The number of explosives being placed |
46 |
85 Explosives = 0 |
47 ------ TEAM LIST ------ |
86 -- The delay between each round |
48 |
87 Delay = 0 |
49 AddTeam(loc("Bazooka Team"), 0xFF0204, "Flower", "Earth", "Default", "hedgewars") |
88 -- The map to be played |
50 hog = AddHog(loc("Greenhorn"), 0, 100, "NoHat") |
89 Map = "Bamboo" |
51 SetGearPosition(hog, 1485, 2001) |
90 -- The theme to be used |
52 SetEffect(hog, heResurrectable, 1) |
91 Theme = "Bamboo" |
53 |
92 -- Setting these 2 values to 0 is the official way to disable Sudden Death cleanly |
54 SendHealthStatsOff() |
93 HealthDecrease = 0 -- Sudden Death damage |
55 end |
94 WaterRise = 0 -- Water rise in Sudden Death |
56 |
95 |
57 function onGearResurrect(gear) |
96 -- Create the player team |
58 if gear == hog then |
97 AddTeam(loc("'Zooka Team"), 14483456, "Simple", "Island", "Default", "cm_crosshair") |
59 flawless = false |
98 -- And add a hog to it |
60 SetGearPosition(hog, 1485, 2001) |
99 player = AddHog(loc("Hunter"), 0, 1, "NoHat") |
61 AddCaption(loc("Your hedgehog has been revived!")) |
100 SetGearPosition(player, 936, 136) |
62 end |
101 end |
63 end |
102 |
64 |
103 -- This function is called when the round starts |
65 function placeGirders() |
104 -- it spawns the first target that has to be destroyed. |
66 PlaceGirder(1520, 2018, 4) |
105 -- In addition it shows the scenario goal(s). |
67 PlaceGirder(1449, 1927, 6) |
|
68 PlaceGirder(1341, 1989, 0) |
|
69 PlaceGirder(1141, 1990, 0) |
|
70 PlaceGirder(2031, 1907, 6) |
|
71 PlaceGirder(2031, 1745, 6) |
|
72 PlaceGirder(2398, 1985, 4) |
|
73 PlaceGirder(2542, 1921, 7) |
|
74 PlaceGirder(2617, 1954, 6) |
|
75 PlaceGirder(2565, 2028, 0) |
|
76 PlaceGirder(2082, 1979, 0) |
|
77 PlaceGirder(2082, 1673, 0) |
|
78 PlaceGirder(1980, 1836, 0) |
|
79 PlaceGirder(1716, 1674, 0) |
|
80 PlaceGirder(1812, 1832, 0) |
|
81 PlaceGirder(1665, 1744, 6) |
|
82 PlaceGirder(2326, 1895, 6) |
|
83 PlaceGirder(2326, 1734, 6) |
|
84 PlaceGirder(2326, 1572, 6) |
|
85 PlaceGirder(2275, 1582, 0) |
|
86 PlaceGirder(1738, 1714, 7) |
|
87 PlaceGirder(1818, 1703, 0) |
|
88 PlaceGirder(1939, 1703, 4) |
|
89 PlaceGirder(2805, 1781, 3) |
|
90 PlaceGirder(2905, 1621, 3) |
|
91 PlaceGirder(3005, 1441, 3) |
|
92 end |
|
93 |
|
94 function spawnTargets(phase) |
|
95 if not phase then |
|
96 phase = gamePhase |
|
97 end |
|
98 if phase == 0 then |
|
99 AddGear(1734, 1656, gtTarget, 0, 0, 0, 0) |
|
100 AddGear(1812, 1814, gtTarget, 0, 0, 0, 0) |
|
101 AddGear(1974, 1818, gtTarget, 0, 0, 0, 0) |
|
102 elseif phase == 2 then |
|
103 AddGear(2102, 1655, gtTarget, 0, 0, 0, 0) |
|
104 AddGear(2278, 1564, gtTarget, 0, 0, 0, 0) |
|
105 AddGear(2080, 1961, gtTarget, 0, 0, 0, 0) |
|
106 elseif phase == 3 then |
|
107 AddGear(1141, 1972, gtTarget, 0, 0, 0, 0) |
|
108 AddGear(1345, 1971, gtTarget, 0, 0, 0, 0) |
|
109 AddGear(1892, 1680, gtTarget, 0, 0, 0, 0) |
|
110 elseif phase == 4 then |
|
111 AddGear(2584, 2010, gtTarget, 0, 0, 0, 0) |
|
112 elseif phase == 5 then |
|
113 AddGear(2794, 1759, gtTarget, 0, 0, 0, 0) |
|
114 AddGear(2894, 1599, gtTarget, 0, 0, 0, 0) |
|
115 AddGear(2994, 1419, gtTarget, 0, 0, 0, 0) |
|
116 end |
|
117 end |
|
118 |
106 function onGameStart() |
119 function onGameStart() |
107 -- Disable the graph in the stats screen, we don't need it |
120 placeGirders() |
108 SendHealthStatsOff() |
121 spawnTargets() |
109 -- Spawn the first target. |
122 ShowMission(loc("Basic Bazooka Training"), loc("Basic Training"), loc("Destroy all the targets!"), -amBazooka, 0) |
110 spawnTarget() |
123 end |
111 SetWind(-20) |
124 |
112 |
125 function newGamePhase() |
113 -- Show some nice mission goals. |
126 -- Spawn targets, update wind and ammo, show instructions |
114 -- Parameters are: caption, sub caption, description, |
127 if gamePhase == 0 then |
115 -- extra text, icon and time to show. |
128 ShowMission(loc("Basic Bazooka Training"), loc("Select Weapon"), loc("To begin with the training, select the bazooka from the ammo menu!").."|".. |
116 -- A negative icon parameter (-n) represents the n-th weapon icon |
129 loc("Open ammo menu: [Right click]").."|".. |
117 -- A positive icon paramter (n) represents the (n+1)-th mission icon |
130 loc("Select weapon: [Left click]"), 2, 5000) |
118 -- A timeframe of 0 is replaced with the default time to show. |
131 elseif gamePhase == 1 then |
119 ShowMission(loc("Bazooka Training"), loc("Aiming Practice"), loc("Eliminate all targets before your time runs out.|You have unlimited ammo for this mission."), -amBazooka, 0) |
132 ShowMission(loc("Basic Bazooka Training"), loc("My First Bazooka"), loc("Let's get started!").."|".. |
|
133 loc("Launch some bazookas to destroy the targets!").."|".. |
|
134 loc("Hold the Attack key pressed for more power.").."|".. |
|
135 loc("Don't hit yourself!").."|".. |
|
136 loc("Attack: [Space]").."|".. |
|
137 loc("Aim: [Up]/[Down]").."|".. |
|
138 loc("Walk: [Left]/[Right]"), 2, 10000) |
|
139 spawnTargets() |
|
140 elseif gamePhase == 2 then |
|
141 ShowMission(loc("Basic Bazooka Training"), loc("Wind"), loc("Bazookas are influenced by wind.").."|".. |
|
142 loc("You see the wind strength at the bottom right corner.").."|".. |
|
143 loc("Destroy the targets!"), 2, 5000) |
|
144 SetWind(50) |
|
145 spawnTargets() |
|
146 elseif gamePhase == 3 then |
|
147 -- Vaporize any bazookas still in the air |
|
148 for gear, _ in pairs(bazookaGears) do |
|
149 AddVisualGear(GetX(gear), GetY(gear), vgtSteam, 0, false) |
|
150 DeleteGear(gear) |
|
151 PlaySound(sndVaporize) |
|
152 end |
|
153 ShowMission(loc("Basic Bazooka Training"), loc("Limited Ammo"), loc("Your ammo is limited this time.").."|".. |
|
154 loc("Destroy all targets with no more than 5 bazookas."), |
|
155 2, 8000) |
|
156 SetWind(-20) |
|
157 AddAmmo(hog, amBazooka, limitedAmmo) |
|
158 spawnTargets() |
|
159 elseif gamePhase == 4 then |
|
160 ShowMission(loc("Basic Bazooka Training"), loc("Bouncing Bomb"), loc("The next target can only be reached by something called “bouncing bomb”.").."|".. |
|
161 loc("Hint: Launch the bazooka horizontally at full power."), |
|
162 2, 8000) |
|
163 SetWind(90) |
|
164 spawnTargets() |
|
165 AddAmmo(hog, amBazooka, 100) |
|
166 if GetCurAmmoType() ~= amBazooka then |
|
167 SetWeapon(amBazooka) |
|
168 end |
|
169 elseif gamePhase == 5 then |
|
170 ShowMission(loc("Basic Bazooka Training"), loc("Final Targets"), loc("The final targets are quite tricky. You need to aim well.").."|".. |
|
171 loc("Precise Aim: [Left Shift] + [Up]/[Down]"), |
|
172 2, 8000) |
|
173 SetWind(75) |
|
174 spawnTargets() |
|
175 elseif gamePhase == 6 then |
|
176 ShowMission(loc("Basic Bazooka Training"), loc("Training complete!"), loc("Congratulations!"), 0, 0) |
|
177 SetInputMask(0) |
|
178 AddAmmo(CurrentHedgehog, amBazooka, 0) |
|
179 if shotsFired > maxTargets then |
|
180 flawless = false |
|
181 end |
|
182 if flawless then |
|
183 PlaySound(sndFlawless, hog) |
|
184 else |
|
185 PlaySound(sndVictory, hog) |
|
186 end |
|
187 SendStat(siCustomAchievement, loc("Good job!")) |
|
188 SendStat(siGameResult, loc("You have completed the Basic Bazooka Training!")) |
|
189 SendStat(siPlayerKills, "0", loc("Bazooka Team")) |
|
190 EndGame() |
|
191 gameOver = true |
|
192 end |
|
193 gamePhase = gamePhase + 1 |
120 end |
194 end |
121 |
195 |
122 function onNewTurn() |
196 function onNewTurn() |
123 SetWeapon(amBazooka) |
197 if gamePhase == 0 then |
124 end |
198 newGamePhase() |
125 |
199 end |
126 -- This function is called every game tick. |
200 end |
127 -- Note that there are 1000 ticks within one second. |
201 |
128 -- You shouldn't try to calculate too complicated |
202 function onSetWeapon(ammoType) |
129 -- code here as this might slow down your game. |
203 if ammoType == amBazooka and not weaponSelected and gamePhase == 1 then |
|
204 newGamePhase() |
|
205 weaponSelected = true |
|
206 end |
|
207 end |
|
208 function onSlot(msgParam) |
|
209 if msgParam == 0 and not weaponSelected and gamePhase == 1 then |
|
210 newGamePhase() |
|
211 weaponSelected = true |
|
212 end |
|
213 end |
|
214 |
|
215 function onHogAttack(ammoType) |
|
216 if ammoType == amBazooka then |
|
217 HideMission() |
|
218 end |
|
219 end |
|
220 |
|
221 function onGearAdd(gear) |
|
222 if GetGearType(gear) == gtTarget then |
|
223 targetsLeft = targetsLeft + 1 |
|
224 maxTargets = maxTargets + 1 |
|
225 targetGears[gear] = true |
|
226 elseif GetGearType(gear) == gtShell then |
|
227 bazookasInGame = bazookasInGame + 1 |
|
228 bazookaGears[gear] = true |
|
229 shotsFired = shotsFired + 1 |
|
230 end |
|
231 end |
|
232 |
|
233 function onGearDelete(gear) |
|
234 if GetGearType(gear) == gtTarget then |
|
235 targetsLeft = targetsLeft - 1 |
|
236 targetGears[gear] = nil |
|
237 if targetsLeft <= 0 then |
|
238 newGamePhase() |
|
239 end |
|
240 elseif GetGearType(gear) == gtShell then |
|
241 bazookasInGame = bazookasInGame - 1 |
|
242 bazookaGears[gear] = nil |
|
243 if bazookasInGame == 0 and GetAmmoCount(hog, amBazooka) == 0 then |
|
244 limitedAmmoReset = 20 |
|
245 flawless = false |
|
246 end |
|
247 elseif gear == hog then |
|
248 SendStat(siCustomAchievement, loc("Your hedgehog died.")) |
|
249 SendStat(siCustomAchievement, loc("Oh no! Just try again!")) |
|
250 SendStat(siGameResult, loc("You lose!")) |
|
251 SendStat(siPlayerKills, "0", loc("Bazooka Team")) |
|
252 EndGame() |
|
253 gameOver = true |
|
254 end |
|
255 end |
|
256 |
|
257 function onGearDamage(gear) |
|
258 if gear == hog then |
|
259 flawless = false |
|
260 end |
|
261 end |
|
262 |
130 function onGameTick20() |
263 function onGameTick20() |
131 -- If time's up, set the game to be lost. |
264 -- Reset targets and ammo if ammo depleted |
132 -- We actually check the time to be "1 ms" as it |
265 if limitedAmmoReset > 0 then |
133 -- will be at "0 ms" right at the start of the game. |
266 limitedAmmoReset = limitedAmmoReset - 20 |
134 if TurnTimeLeft < 40 and TurnTimeLeft > 0 and score < score_goal and not game_lost then |
267 end |
135 game_lost = true |
268 if limitedAmmoReset == 0 then |
136 -- ... and show a short message. |
269 if not gameOver and bazookasInGame == 0 and GetAmmoCount(hog, amBazooka) == 0 then |
137 ShowMission(loc("Bazooka Training"), loc("Aiming Practice"), loc("Oh no! Time's up! Just try again."), -amSkip, 0) |
270 for gear, _ in pairs(targetGears) do |
138 -- How about killing our poor hog due to his poor performance? |
271 DeleteGear(gear) |
139 SetHealth(player, 0) |
|
140 -- Just to be sure set the goal time to 1 ms |
|
141 time_goal = 1 |
|
142 end |
|
143 |
|
144 if band(GetState(player), gstDrowning) == gstDrowning and game_lost == false and score < score_goal then |
|
145 game_lost = true |
|
146 time_goal = 1 |
|
147 AddCaption(loc("You lose!"), 0xFFFFFFFF, capgrpGameState) |
|
148 ShowMission(loc("Bazooka Training"), loc("Aiming Practice"), loc("Oh no! You failed! Just try again."), -amSkip, 0) |
|
149 end |
|
150 |
|
151 -- If the goal is reached or we've lost ... |
|
152 if score == score_goal or game_lost then |
|
153 -- ... check to see if the time we'd like to |
|
154 -- wait has passed and then ... |
|
155 if end_timer == 0 then |
|
156 -- Let’s create some stats for the stats screen! |
|
157 -- We will expose the number of hit targets hit, launched bazooka and the accuracy |
|
158 |
|
159 SendStat(siPointType, loc("hits")) |
|
160 SendStat(siPlayerKills, tostring(score), loc("'Zooka Team")) |
|
161 SendStat(siCustomAchievement, string.format(loc("You have destroyed %d of %d targets."), score, score_goal)) |
|
162 SendStat(siCustomAchievement, string.format(loc("You have launched %d bazookas."), shots)) |
|
163 |
|
164 -- We must avoid a division by zero |
|
165 if(shots > 0) then |
|
166 SendStat(siCustomAchievement, string.format(loc("Your accuracy was %.1f%%."), (score/shots)*100)) |
|
167 end |
272 end |
168 if score == score_goal then |
273 spawnTargets(3) |
169 SendStat(siGameResult, loc("You have finished the bazooka training!")) |
274 AddCaption(loc("Out of ammo! Try again!")) |
170 SendStat(siCustomAchievement, string.format(loc("%.1f seconds were remaining."), (time_goal/1000), math.ceil(time_goal/12))) |
275 AddAmmo(hog, amBazooka, limitedAmmo) |
171 end |
276 SetWeapon(amBazooka) |
172 if game_lost then |
277 missedTauntTimer = 1000 |
173 SendStat(siGameResult, loc("You lose!")) |
278 end |
174 end |
279 limitedAmmoReset = -1 |
175 |
280 end |
176 -- Finally we end the game ... |
281 if missedTauntTimer > 0 then |
177 EndGame() |
282 missedTauntTimer = missedTauntTimer - 20 |
178 else |
283 end |
179 -- ... or just lower the timer by 20ms. |
284 if missedTauntTimer == 0 then |
180 -- Reset the time left to stop the timer |
285 PlaySound(sndMissed, hog) |
181 TurnTimeLeft = time_goal |
286 missedTauntTimer = -1 |
182 end |
287 end |
183 end_timer = end_timer - 20 |
288 end |
184 end |
289 |
185 end |
|
186 |
|
187 -- This function is called when the game is initialized |
|
188 -- to request the available ammo and probabilities |
|
189 function onAmmoStoreInit() |
290 function onAmmoStoreInit() |
190 -- add an unlimited supply of bazooka ammo |
|
191 SetAmmo(amBazooka, 9, 0, 0, 0) |
291 SetAmmo(amBazooka, 9, 0, 0, 0) |
192 end |
292 end |
193 |
|
194 -- This function is called when a new gear is added. |
|
195 -- We don't need it for this training, so we can |
|
196 -- keep it empty. |
|
197 -- function onGearAdd(gear) |
|
198 -- end |
|
199 |
|
200 -- This function is called before a gear is destroyed. |
|
201 -- We use it to count the number of targets destroyed. |
|
202 function onGearDelete(gear) |
|
203 -- We're only interested in target gears. |
|
204 if GetGearType(gear) == gtTarget then |
|
205 -- Add one point to our score/counter |
|
206 score = score + 1 |
|
207 -- If we haven't reached the goal ... |
|
208 if score < score_goal then |
|
209 -- ... spawn another target. |
|
210 spawnTarget() |
|
211 else |
|
212 if not game_lost then |
|
213 -- Otherwise show that the goal was accomplished |
|
214 ShowMission(loc("Bazooka Training"), loc("Aiming Practice"), loc("Congratulations! You've eliminated all targets|within the allowed time frame."), 0, 0) |
|
215 -- Also let the hogs shout "victory!" |
|
216 PlaySound(sndVictory) |
|
217 -- Save the time left so we may keep it. |
|
218 time_goal = TurnTimeLeft |
|
219 end |
|
220 end |
|
221 end |
|
222 end |
|
223 |
|
224 -- This function is called when a gear has been damaged. |
|
225 -- We only use it to determine wheather our hog took damage in order to abort the mission. |
|
226 function onGearDamage(gear, damage) |
|
227 if GetGearType(gear) == gtHedgehog then |
|
228 if not game_lost then |
|
229 game_lost = true |
|
230 AddCaption(loc("You lose!"), 0xFFFFFFFF, capgrpGameState) |
|
231 ShowMission(loc("Bazooka Training") , loc("Aiming Practice"), loc("Oh no! You failed! Just try again."), -amSkip, 0) |
|
232 |
|
233 time_goal = 1 |
|
234 end |
|
235 end |
|
236 end |
|
237 |
|
238 |
|
239 -- This function is called after a gear is added. |
|
240 -- We use it to count the number of bazooka shots. |
|
241 function onGearAdd(gear) |
|
242 -- Count the number of bazooka shots for our stats |
|
243 if GetGearType(gear) == gtShell then |
|
244 shots = shots + 1 |
|
245 end |
|
246 end |
|