|
1 HedgewarsScriptLoad("/Scripts/Locale.lua") |
|
2 HedgewarsScriptLoad("/Scripts/Utils.lua") |
|
3 |
|
4 local hhs = {} |
|
5 local deadHogs = {} |
|
6 local missionWon = nil |
|
7 local endTimer = 1000 |
|
8 local hogsKilled = 0 |
|
9 local totalEnemies = 0 |
|
10 local finishTime |
|
11 local playerFail = false |
|
12 local ropeGear = nil |
|
13 local endGameCalled = false |
|
14 local missionEndHandled = false |
|
15 local valkyriesTimer = -1 |
|
16 |
|
17 local valkyriesDuration = 20000 |
|
18 local timeBonus = 6000 |
|
19 local killBonus = 6000 |
|
20 local playValkyries = false |
|
21 |
|
22 local extraTime |
|
23 |
|
24 local playerTeamName |
|
25 local missionName = loc("Rope-knocking Challenge") |
|
26 -- Mission type: |
|
27 -- 0 = none (no special handling) |
|
28 -- 1 = challenge (saves mission vars) |
|
29 local missionType = 1 |
|
30 |
|
31 local function getKillScore() |
|
32 return div(hogsKilled * killBonus, totalEnemies) |
|
33 end |
|
34 |
|
35 local function protectEnemies() |
|
36 -- Protect enemy hogs |
|
37 for i=1, totalEnemies do |
|
38 if hhs[i] and GetHealth(hhs[i]) then |
|
39 SetEffect(hhs[i], heInvulnerable, 1) |
|
40 SetEffect(hhs[i], heResurrectable, 1) |
|
41 end |
|
42 end |
|
43 end |
|
44 |
|
45 local function killStr(killed, total, score) |
|
46 if total == 16 then |
|
47 return string.format(loc("You have killed %d of 16 hedgehogs (+%d points)."), killed, score) |
|
48 else |
|
49 return string.format(loc("You have killed %d of %d hedgehogs (+%d points)."), killed, total, score) |
|
50 end |
|
51 end |
|
52 |
|
53 local function gameOver() |
|
54 StopMusicSound(sndRideOfTheValkyries) |
|
55 valkyriesTimer = -1 |
|
56 missionWon = false |
|
57 SendStat(siGameResult, loc("Challenge over!")) |
|
58 local score = getKillScore() |
|
59 SendStat(siCustomAchievement, killStr(hogsKilled, totalEnemies, score)) |
|
60 SendStat(siPointType, "!POINTS") |
|
61 SendStat(siPlayerKills, tostring(score), playerTeamName) |
|
62 protectEnemies() |
|
63 if not endGameCalled then |
|
64 EndGame() |
|
65 endGameCalled = true |
|
66 end |
|
67 if missionType == 1 then |
|
68 -- Update highscore |
|
69 updateChallengeRecord("Highscore", score) |
|
70 end |
|
71 end |
|
72 |
|
73 local function victory(onVictory) |
|
74 missionWon = true |
|
75 local e = 0 |
|
76 if extraTime then |
|
77 e = extraTime |
|
78 end |
|
79 local totalTime = TurnTime + e * totalEnemies |
|
80 local completeTime = (totalTime - finishTime) / 1000 |
|
81 ShowMission(missionName, loc("Challenge completed!"), loc("Congratulations!") .. "|" .. string.format(loc("Completion time: %.2fs"), completeTime), 0, 0) |
|
82 PlaySound(sndHomerun) |
|
83 -- Protect player hog |
|
84 if hhs[0] and GetHealth(hhs[0]) then |
|
85 SetEffect(hhs[0], heInvulnerable, 1) |
|
86 SetEffect(hhs[0], heResurrectable, 1) |
|
87 end |
|
88 SendStat(siGameResult, loc("Challenge completed!")) |
|
89 local hogScore = getKillScore() |
|
90 local timeScore = div(finishTime * timeBonus, totalTime) |
|
91 local score = hogScore + timeScore |
|
92 SendStat(siCustomAchievement, killStr(hogsKilled, totalEnemies, hogScore)) |
|
93 SendStat(siCustomAchievement, string.format(loc("You have completed this challenge in %.2f s (+%d points)."), completeTime, timeScore)) |
|
94 SendStat(siPointType, "!POINTS") |
|
95 SendStat(siPlayerKills, tostring(score), playerTeamName) |
|
96 SetTeamLabel(playerTeamName, tostring(score)) |
|
97 SetTurnTimeLeft(MAX_TURN_TIME) |
|
98 |
|
99 if missionType == 1 then |
|
100 -- Update highscore |
|
101 updateChallengeRecord("Highscore", score) |
|
102 end |
|
103 if onVictory then |
|
104 onVictory() |
|
105 end |
|
106 end |
|
107 |
|
108 local function knockTaunt() |
|
109 local r = math.random(0,23) |
|
110 local taunt |
|
111 if r == 0 then taunt = loc("%s has been knocked out.") |
|
112 elseif r == 1 then taunt = loc("%s hit the ground.") |
|
113 elseif r == 2 then taunt = loc("%s splatted.") |
|
114 elseif r == 3 then taunt = loc("%s was smashed.") |
|
115 elseif r == 4 then taunt = loc("%s felt unstable.") |
|
116 elseif r == 5 then taunt = loc("%s exploded.") |
|
117 elseif r == 6 then taunt = loc("%s fell from a high cliff.") |
|
118 elseif r == 7 then taunt = loc("%s goes the way of the lemming.") |
|
119 elseif r == 8 then taunt = loc("%s was knocked away.") |
|
120 elseif r == 9 then taunt = loc("%s was really unlucky.") |
|
121 elseif r == 10 then taunt = loc("%s felt victim to rope-knocking.") |
|
122 elseif r == 11 then taunt = loc("%s had no chance.") |
|
123 elseif r == 12 then taunt = loc("%s was a good target.") |
|
124 elseif r == 13 then taunt = loc("%s spawned at a really bad position.") |
|
125 elseif r == 14 then taunt = loc("%s was doomed from the beginning.") |
|
126 elseif r == 15 then taunt = loc("%s has fallen victim to gravity.") |
|
127 elseif r == 16 then taunt = loc("%s hates Newton.") -- Isaac Newton |
|
128 elseif r == 17 then taunt = loc("%s had it coming.") |
|
129 elseif r == 18 then taunt = loc("%s is eliminated!") |
|
130 elseif r == 19 then taunt = loc("%s fell too fast.") |
|
131 elseif r == 20 then taunt = loc("%s flew like a rock.") |
|
132 elseif r == 21 then taunt = loc("%s stumbled.") |
|
133 elseif r == 22 then taunt = loc("%s was shoved away.") |
|
134 elseif r == 23 then taunt = loc("%s didn't expect that.") |
|
135 end |
|
136 return taunt |
|
137 end |
|
138 |
|
139 local function declareEnemyKilled(gear, onVictory) |
|
140 if deadHogs[gear] or playerFail then |
|
141 return |
|
142 end |
|
143 deadHogs[gear] = true |
|
144 hogsKilled = hogsKilled + 1 |
|
145 |
|
146 -- Award extra time, if available |
|
147 if extraTime and extraTime ~= 0 then |
|
148 SetTurnTimeLeft(TurnTimeLeft + extraTime) |
|
149 AddCaption(string.format(loc("+%d seconds!"), div(extraTime, 1000)), 0xFFFFFFFF, capgrpMessage2) |
|
150 end |
|
151 |
|
152 SetTeamLabel(playerTeamName, tostring(getKillScore())) |
|
153 |
|
154 if hogsKilled == totalEnemies - 1 then |
|
155 if playValkyries then |
|
156 PlayMusicSound(sndRideOfTheValkyries) |
|
157 valkyriesTimer = valkyriesDuration |
|
158 end |
|
159 elseif hogsKilled == totalEnemies then |
|
160 finishTime = TurnTimeLeft |
|
161 victory(onVictory) |
|
162 end |
|
163 end |
|
164 |
|
165 --[[ |
|
166 RopeKnocking function! |
|
167 |
|
168 This creates a rope-knocking challenge. |
|
169 The player spawns with one hog and a rope and must kill all other hogs |
|
170 by rope-knocking before the time runs out. |
|
171 The player wins points for each kill and gets a time bonus for killing |
|
172 all enemies. |
|
173 |
|
174 params is a table with all the required parameters. |
|
175 Fields of the params table: |
|
176 |
|
177 MANDATORY: |
|
178 - map: Map name |
|
179 - theme: Theme name |
|
180 - turnTime: Turn time |
|
181 - playerTeam: Player team info: |
|
182 { |
|
183 x, y: Start position |
|
184 faceLeft: If true, hog faces left |
|
185 } |
|
186 - enemyTeams: Table of enemy team tables. each enemy team table has this format: |
|
187 { |
|
188 name: Team name |
|
189 flag: Flag |
|
190 hogs: Hogs table: |
|
191 { |
|
192 x, y: Position |
|
193 faceLeft: If true, hog faces left |
|
194 hat: Hat name |
|
195 name: Hog name |
|
196 } |
|
197 } |
|
198 |
|
199 OPTIONAL: |
|
200 - missionName: Mission name |
|
201 - missionType: |
|
202 0: None/other: No special handling |
|
203 1: Challenge: Will save mission variables at end (default) |
|
204 - killBonus: Score for killing all hogs (one hog scores ca. (killBonus/<number of enemies)) (default: 6000) |
|
205 - timeBonus: Maximum theoretically possible time bonus (default: 6000) |
|
206 - gameFlags: List of game flags, if you want to set your own |
|
207 - extraTime: Extra time awarded for each kill, in milliseconds (default: 0) |
|
208 - valkyries: If true, play "Ride of the Valkyries" at final enemy (default: false) |
|
209 - onGameInit: Custom onGameInit callback |
|
210 - onGameStart: Custom onGameStart callback |
|
211 - onVictory: Function that is called when the mission is won. |
|
212 |
|
213 Hint: Use onGameInit and onGameStart to place custom gears and girders |
|
214 Hint: Use onVictory to save campaign variables if using this in a campaign |
|
215 |
|
216 ]] |
|
217 function RopeKnocking(params) |
|
218 if params.missionName then |
|
219 missionName = params.missionName |
|
220 end |
|
221 if params.extraTime then |
|
222 extraTime = params.extraTime |
|
223 end |
|
224 if params.valkyries then |
|
225 playValkyries = params.valkyries |
|
226 end |
|
227 if params.missionType then |
|
228 missionType = params.missionType |
|
229 end |
|
230 if params.killBonus then |
|
231 killBonus = params.killBonus |
|
232 end |
|
233 if params.timeBonus then |
|
234 timeBonus = params.timeBonus |
|
235 end |
|
236 |
|
237 _G.onGameInit = function() |
|
238 |
|
239 if params.gameFlags then |
|
240 for g=1, #params.gameFlags do |
|
241 EnableGameFlags(params.gameFlags[g]) |
|
242 end |
|
243 end |
|
244 |
|
245 EnableGameFlags(gfBorder, gfSolidLand) |
|
246 |
|
247 TurnTime = params.turnTime |
|
248 Delay = 500 |
|
249 Map = params.map |
|
250 Theme = params.theme |
|
251 |
|
252 -- Disable Sudden Death |
|
253 WaterRise = 0 |
|
254 HealthDecrease = 0 |
|
255 |
|
256 CaseFreq = 0 |
|
257 MinesNum = 0 |
|
258 Explosives = 0 |
|
259 |
|
260 -- Player team |
|
261 playerTeamName = AddMissionTeam(-1) |
|
262 hhs[0] = AddMissionHog(1) |
|
263 SetGearPosition(hhs[0], params.playerTeam.x, params.playerTeam.y) |
|
264 if params.playerTeam.faceLeft == true then |
|
265 HogTurnLeft(hhs[0], true) |
|
266 end |
|
267 |
|
268 -- Enemy teams |
|
269 for t=1, #params.enemyTeams do |
|
270 local team = params.enemyTeams[t] |
|
271 params.enemyTeams[t].name = AddTeam(team.name, -2, "Simple", "Tank", "Default_qau", team.flag) |
|
272 for h=1, #team.hogs do |
|
273 local hogData = team.hogs[h] |
|
274 local name = hogData.name |
|
275 local hat = hogData.hat |
|
276 if not hat then |
|
277 hat = "NoHat" |
|
278 end |
|
279 local hog = AddHog(name, 0, 1, hat) |
|
280 SetGearPosition(hog, hogData.x, hogData.y) |
|
281 if hogData.faceLeft == true then |
|
282 HogTurnLeft(hog, true) |
|
283 end |
|
284 table.insert(hhs, hog) |
|
285 |
|
286 totalEnemies = totalEnemies + 1 |
|
287 end |
|
288 end |
|
289 |
|
290 if params.onGameInit then |
|
291 params.onGameInit() |
|
292 end |
|
293 end |
|
294 |
|
295 _G.onGameStart = function() |
|
296 SendHealthStatsOff() |
|
297 |
|
298 local timeTxt = "" |
|
299 local displayTime = 4000 |
|
300 if extraTime and extraTime ~= 0 then |
|
301 timeTxt = string.format(loc("For each kill you win %d seconds."), div(extraTime, 1000)) |
|
302 displayTime = 5000 |
|
303 end |
|
304 local recordInfo = getReadableChallengeRecord("Highscore") |
|
305 if recordInfo == nil then |
|
306 recordInfo = "" |
|
307 else |
|
308 recordInfo = "|" .. recordInfo |
|
309 end |
|
310 ShowMission( |
|
311 missionName, |
|
312 loc("Challenge"), |
|
313 loc("Use the rope to knock your enemies to their doom.") .. "|" .. |
|
314 loc("Finish this challenge as fast as possible to earn bonus points.") .. "|" .. |
|
315 timeTxt .. recordInfo, -amRope, displayTime) |
|
316 |
|
317 SetTeamLabel(playerTeamName, "0") |
|
318 |
|
319 if params.onGameStart then |
|
320 params.onGameStart() |
|
321 end |
|
322 end |
|
323 |
|
324 _G.onGameTick = function() |
|
325 |
|
326 if (TurnTimeLeft == 1) and (missionWon == nil) then |
|
327 PlaySound(sndBoring, CurrentHedgehog) |
|
328 gameOver() |
|
329 end |
|
330 |
|
331 if missionWon ~= nil then |
|
332 |
|
333 endTimer = endTimer - 1 |
|
334 if endTimer == 1 then |
|
335 if not endGameCalled then |
|
336 EndGame() |
|
337 endGameCalled = true |
|
338 end |
|
339 end |
|
340 |
|
341 if not missionEndHandled then |
|
342 if missionWon == true then |
|
343 AddCaption(loc("Victory!"), 0xFFFFFFFF, capgrpGameState) |
|
344 if missionType == 1 then |
|
345 SaveMissionVar("Won", "true") |
|
346 end |
|
347 else |
|
348 AddCaption(loc("Challenge over!"), 0xFFFFFFFF, capgrpGameState) |
|
349 end |
|
350 missionEndHandled = true |
|
351 end |
|
352 |
|
353 end |
|
354 |
|
355 end |
|
356 |
|
357 _G.onGameTick20 = function() |
|
358 if (valkyriesTimer > 0) then |
|
359 valkyriesTimer = valkyriesTimer - 20 |
|
360 if valkyriesTimer <= 0 then |
|
361 StopMusicSound(sndRideOfTheValkyries) |
|
362 end |
|
363 end |
|
364 local drown = (hhs[0]) and (band(GetState(hhs[0]), gstDrowning) ~= 0) |
|
365 if drown and missionWon == nil then |
|
366 -- Player hog drowns |
|
367 playerFail = true |
|
368 return |
|
369 end |
|
370 for i=1, totalEnemies do |
|
371 local hog = hhs[i] |
|
372 drown = (hog) and (not deadHogs[hog]) and (band(GetState(hhs[i]), gstDrowning) ~= 0) |
|
373 if drown then |
|
374 declareEnemyKilled(hog, params.onVictory) |
|
375 end |
|
376 end |
|
377 |
|
378 if ropeGear and not missionWon and band(GetState(ropeGear), gstCollision) ~= 0 then |
|
379 -- Hide mission on first rope attach |
|
380 HideMission() |
|
381 end |
|
382 end |
|
383 |
|
384 _G.onGearDamage = function(gear, damage) |
|
385 |
|
386 if gear == hhs[0] then |
|
387 -- Player hog hurts itself |
|
388 playerFail = true |
|
389 StopMusicSound(sndRideOfTheValkyries) |
|
390 valkyriesTimer = -1 |
|
391 protectEnemies() |
|
392 end |
|
393 |
|
394 if gear ~= hhs[0] and GetGearType(gear) == gtHedgehog and not deadHogs[gear] and missionWon == nil and playerFail == false then |
|
395 -- Enemy hog took damage |
|
396 AddVisualGear(GetX(gear), GetY(gear), vgtBigExplosion, 0, false) |
|
397 DeleteGear(gear) |
|
398 PlaySound(sndExplosion) |
|
399 AddCaption(string.format(knockTaunt(), GetHogName(gear)), 0xFFFFFFFF, capgrpMessage) |
|
400 |
|
401 declareEnemyKilled(gear, params.onVictory) |
|
402 end |
|
403 |
|
404 end |
|
405 |
|
406 _G.onGearAdd = function(gear) |
|
407 if GetGearType(gear) == gtRope then |
|
408 ropeGear = gear |
|
409 end |
|
410 end |
|
411 |
|
412 _G.onGearDelete = function(gear) |
|
413 |
|
414 if (gear == hhs[0]) and (missionWon == nil) then |
|
415 playerFail = true |
|
416 gameOver() |
|
417 end |
|
418 |
|
419 if GetGearType(gear) == gtHedgehog and gear ~= hhs[0] and not deadHogs[gear] then |
|
420 declareEnemyKilled(gear, params.onVictory) |
|
421 end |
|
422 |
|
423 if GetGearType(gear) == gtRope then |
|
424 ropeGear = nil |
|
425 end |
|
426 |
|
427 end |
|
428 |
|
429 if params.onAmmoStoreInit then |
|
430 _G.onAmmoStoreInit = params.onAmmoStoreInit |
|
431 else |
|
432 _G.onAmmoStoreInit = function() |
|
433 SetAmmo(amRope, 9, 0, 0, 0) |
|
434 end |
|
435 |
|
436 _G.onNewTurn = function() |
|
437 SetWeapon(amRope) |
|
438 end |
|
439 end |
|
440 |
|
441 end |