SHOW:
|
|
- or go back to the newest paste.
1 | --[[ | |
2 | Gold Runner | |
3 | Inspired by the game by Doug Smith | |
4 | ||
5 | Written by: Nitrogen Fingers | |
6 | ]]-- | |
7 | w,h = term.getSize() | |
8 | ||
9 | running = true | |
10 | started = false | |
11 | nextLevel = false | |
12 | ||
13 | inLevelSelect = false | |
14 | inLevelEditor = false | |
15 | local levelEditName = nil | |
16 | local hexnums = { [10] = "a", [11] = "b", [12] = "c", [13] = "d", [14] = "e" , [15] = "f" } | |
17 | ||
18 | titleLoaded = false | |
19 | local menSel = "none" | |
20 | local titleOptions = { "New Game", "Select Level", "Create Level", "Quit" } | |
21 | local inGameOptions = { "Restart", "Edit Level", "Back to Title", "Quit" } | |
22 | local levelEditOptions = { "Save", "Play Level", "Save and Exit", "Discard and Exit" } | |
23 | local menIndex = 1 | |
24 | ||
25 | local maxnamelen = 14 | |
26 | ||
27 | local drawOffsetX = 1 | |
28 | local drawOffsetY = 0 | |
29 | ||
30 | local map = {} | |
31 | local goldMap = {} | |
32 | local blockTimers = {} | |
33 | local blockIntv = 5 | |
34 | ||
35 | local monks = {} | |
36 | local monkTimer = -1 | |
37 | local monkSpawnIntv = 3 | |
38 | local monkTrapIntv = blockIntv/2 | |
39 | ||
40 | local goldCount = 0 | |
41 | local maxGoldCount = 0 | |
42 | local playerLives = 3 | |
43 | local playerScore = 0 | |
44 | local plspawnX = 0 | |
45 | local plspawnY = 0 | |
46 | ||
47 | local plX = 0 | |
48 | local plY = 0 | |
49 | local pfalling = false | |
50 | local moveTimer = -1 | |
51 | local shootTimer = -1 | |
52 | local spawnTimer = -1 | |
53 | local moveIntv = 0.15 | |
54 | ||
55 | local exX = 0 | |
56 | local exY = 0 | |
57 | ||
58 | local levelList = {} | |
59 | local currentLevel = 1 | |
60 | local levelLot = 1 | |
61 | ||
62 | local titleLevel = { | |
63 | " "; | |
64 | " dddddddddddc "; | |
65 | " 4 c "; | |
66 | " 4 4 c "; | |
67 | " bbbbbbc bcbbbb "; | |
68 | " b 4 b c c "; | |
69 | " bbbbb c 4 dd 0 4 c 4 "; | |
70 | " bbcb bbb bbbbc "; | |
71 | " c c "; | |
72 | " c ddd c eb"; | |
73 | " dddddc bcb cbbbbb"; | |
74 | " c c c bbbb"; | |
75 | "b4 c 4c bb44b"; | |
76 | "bbb c 4 e bbbcbbbbbbbbbbbbb"; | |
77 | "bbbbbbbbbbbbbbc 4 cbbbbb 4 bbbb"; | |
78 | "bbbbbbfff44fbbc 4 cbbbbbbb cbbbbbbb bbbbb"; | |
79 | "bbbbffffbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 4 bbbbbbb"; | |
80 | "bbbffbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb5 bbbbbbbbb"; | |
81 | } | |
82 | ||
83 | local function parseValue(x, y, lchar) | |
84 | if tonumber(lchar, 16) then | |
85 | lchar = math.pow(2, tonumber(lchar,16)) | |
86 | ||
87 | if lchar == colours.blue then | |
88 | map[y][x] = 0 | |
89 | elseif lchar == colours.brown then | |
90 | map[y][x] = 'H' | |
91 | elseif lchar == colours.yellow then | |
92 | goldMap[y][x] = 1 | |
93 | goldCount = goldCount + 1 | |
94 | elseif lchar == colours.orange then | |
95 | map[y][x] = 'V' | |
96 | elseif lchar == colours.green then | |
97 | map[y][x] = '-' | |
98 | elseif lchar == colours.lightGrey then | |
99 | map[y][x] = 'h' | |
100 | elseif lchar == colours.grey then | |
101 | map[y][x] = '#' | |
102 | elseif lchar == colours.white then | |
103 | plX = x | |
104 | plspawnX = x | |
105 | plY = y | |
106 | plspawnY = y | |
107 | elseif lchar == colours.lime then | |
108 | exX = x | |
109 | exY = y | |
110 | elseif lchar == colours.red then | |
111 | table.insert(monks, { | |
112 | --X and Y, clear enough | |
113 | x = x, y = y; | |
114 | --Where they spawn when they die | |
115 | spawnX = x, spawnY = y; | |
116 | -- Any gold they're carring- it's a 1 in 5 | |
117 | gold = false; | |
118 | -- Whether or not they're falling | |
119 | falling = false; | |
120 | -- Timer if they're dead to respawn | |
121 | dead = nil; | |
122 | --Whether or not the monk has just spawned | |
123 | justSpawned = true; | |
124 | --Whether or not the monk has just escaped | |
125 | justEscaped = false; | |
126 | -- Current aim- it's "up", "down", "across" or "none" | |
127 | behaviour = "none"; | |
128 | -- The desired x position to travel to, when one is necessary. | |
129 | desX = nil; | |
130 | -- The escape timer | |
131 | trapped = nil; | |
132 | }) | |
133 | end | |
134 | end | |
135 | end | |
136 | ||
137 | local function loadMap(_sPath) | |
138 | if not fs.exists(_sPath) then return false end | |
139 | map = {} | |
140 | goldMap = {} | |
141 | monks = {} | |
142 | goldCount = 0 | |
143 | ||
144 | local file = fs.open(_sPath, "r") | |
145 | local line = file:readLine() | |
146 | while line do | |
147 | goldMap[#map+1] = {} | |
148 | map[#map+1] = {} | |
149 | for i=1,math.min(#line,49) do | |
150 | local lchar = string.sub(line,i,i) | |
151 | parseValue(i, #map, lchar) | |
152 | end | |
153 | if #map == 18 then break end | |
154 | line = file:readLine() | |
155 | end | |
156 | file:close() | |
157 | maxGoldCount = goldCount | |
158 | titleLoaded = false | |
159 | return true | |
160 | end | |
161 | ||
162 | --When something moves or something needs to be drawn, we | |
163 | --just change the appropriate tile with this method. | |
164 | local function updateMap(x,y) | |
165 | term.setCursorPos(x + drawOffsetX, y + drawOffsetY) | |
166 | term.setBackgroundColour(colours.black) | |
167 | if plX == x and plY == y and map[y][x] ~= 0 then | |
168 | term.setTextColour(colours.white) | |
169 | if map[y][x] == 1 then term.setBackgroundColour(colours.lightBlue) | |
170 | elseif map[y][x] == "V" then term.setBackgroundColour(colours.blue) end | |
171 | - | if map[y][x] == 1 then term.setBackgroundColour(colours.lightBlue) end |
171 | + | |
172 | elseif map[y][x] == 'H' then | |
173 | term.setTextColour(colours.brown) | |
174 | term.write("H") | |
175 | --Level Editor stuff | |
176 | elseif map[y][x] == 'h' and (goldCount == 0 or inLevelEditor) then | |
177 | if inLevelEditor then term.setTextColour(colours.lightGrey) | |
178 | else term.setTextColour(colours.brown) end | |
179 | term.write("H") | |
180 | elseif map[y][x] == '&' and inLevelEditor then | |
181 | term.setTextColour(colours.pink) | |
182 | term.write('&') | |
183 | elseif map[y][x] == 'V' then | |
184 | term.setBackgroundColour(colours.blue) | |
185 | if inLevelEditor then | |
186 | term.setTextColour(colours.orange) | |
187 | term.write("V") | |
188 | else | |
189 | term.write(" ") | |
190 | end | |
191 | elseif map[y][x] == '-' then | |
192 | term.setTextColour(colours.brown) | |
193 | term.write(map[y][x]) | |
194 | - | elseif map[y][x] == 0 and goldMap[y][x] == 1 and inLevelEditor then |
194 | + | |
195 | term.setBackgroundColour(colours.grey) | |
196 | term.write(" ") | |
197 | elseif type(map[y][x]) == "number" then | |
198 | local uchar = ' ' | |
199 | if map[y][x] == 3 then | |
200 | term.setBackgroundColour(colours.lightBlue) | |
201 | elseif map[y][x] == 2 and goldMap[y][x] == 1 then | |
202 | term.setTextColour(colours.yellow) | |
203 | uchar = '$' | |
204 | elseif map[y][x] == 1 then | |
205 | term.setBackgroundColour(colours.lightBlue) | |
206 | elseif map[y][x] == 0 then | |
207 | term.setBackgroundColour(colours.blue) | |
208 | end | |
209 | term.write(uchar) | |
210 | elseif goldMap[y][x] == 1 then | |
211 | term.setTextColour(colours.yellow) | |
212 | term.write("$") | |
213 | elseif exX == x and exY == y and (goldCount == 0 or inLevelEditor) then | |
214 | term.setTextColour(colours.lime) | |
215 | term.write("@") | |
216 | else | |
217 | term.write(" ") | |
218 | end | |
219 | end | |
220 | ||
221 | --It's silly to iterate through all monks when drawing tiles, so | |
222 | --we do it separately. | |
223 | local function drawMonk(monk) | |
224 | term.setCursorPos(monk.x + drawOffsetX, monk.y + drawOffsetY) | |
225 | if monk.justSpawned then term.setTextColour(colours.pink) | |
226 | else term.setTextColour(colours.red) end | |
227 | if map[monk.y][monk.x] == 1 then term.setBackgroundColour(colours.lightBlue) | |
228 | elseif map[monk.y][monk.x] == "V" then term.setBackgroundColour(colours.blue) | |
229 | else term.setBackgroundColour(colours.black) end | |
230 | term.write("&") | |
231 | end | |
232 | ||
233 | --Draws the map for the first time. It barely changes, so we really | |
234 | --only call this the once. | |
235 | local function drawMap() | |
236 | term.setBackgroundColour(colours.black) | |
237 | term.clear() | |
238 | for y=1,#map do | |
239 | for x=1,49 do | |
240 | updateMap(x,y) | |
241 | end | |
242 | end | |
243 | for _,monk in pairs(monks) do drawMonk(monk)end | |
244 | end | |
245 | ||
246 | --When all coins have been collected, we add in invisble ladders and | |
247 | --the end game portal. | |
248 | local function drawEndgameMap() | |
249 | for y=1,#map do | |
250 | for x=1,49 do | |
251 | if map[y][x] == 'h' or (exX == x and exY == y) then | |
252 | updateMap(x,y) | |
253 | end | |
254 | end | |
255 | end | |
256 | end | |
257 | ||
258 | --Sets the map back to defaults, so we can start afresh | |
259 | local function resetMap() | |
260 | goldCount = maxGoldCount | |
261 | for i=1,#goldMap do | |
262 | for j=1,49 do | |
263 | if goldMap[i][j] == 0 then goldMap[i][j] = 1 end | |
264 | end | |
265 | end | |
266 | for _,monk in pairs(monks) do | |
267 | monk.justSpawned = true | |
268 | monk.dead = nil | |
269 | monk.trapped = nil | |
270 | monk.justEscaped = false | |
271 | monk.falling = false | |
272 | monk.behaviour = "none" | |
273 | monk.x = monk.spawnX | |
274 | monk.y = monk.spawnY | |
275 | end | |
276 | ||
277 | for _,timer in pairs(blockTimers) do | |
278 | map[timer.y][timer.x] = 0 | |
279 | end | |
280 | blockTimers = {} | |
281 | plX = plspawnX | |
282 | plY = plspawnY | |
283 | ||
284 | moveTimer = -1 | |
285 | shootTimer = -1 | |
286 | spawnTimer = -1 | |
287 | monkTimer = -1 | |
288 | pfalling = false | |
289 | end | |
290 | ||
291 | --Draws the HUD. This also rarely changes, so we update it when something happens. | |
292 | local function drawHUD() | |
293 | term.setCursorPos(2,19) | |
294 | term.setBackgroundColour(colours.black) | |
295 | term.clearLine() | |
296 | term.setTextColour(colours.blue) | |
297 | term.write("Score: ") | |
298 | term.setTextColour(colours.yellow) | |
299 | term.write(string.rep("0", 5-math.floor(math.log10(playerScore + 1))) | |
300 | ..playerScore) | |
301 | term.setTextColour(colours.yellow) | |
302 | term.setCursorPos(25 - #levelList[currentLevel]/2, 19) | |
303 | term.write(levelList[currentLevel]) | |
304 | local lstr = "Men: " | |
305 | term.setCursorPos(50 - #lstr - math.floor(math.log10(playerLives)), 19) | |
306 | term.setTextColour(colours.blue) | |
307 | term.write(lstr) | |
308 | term.setTextColour(colours.yellow) | |
309 | term.write(playerLives.."") | |
310 | end | |
311 | ||
312 | --Draws the list of levels known, with respect to screen | |
313 | --real estate | |
314 | local function drawLevelList() | |
315 | local minLev = ((levelLot-1) * 10 + 1) | |
316 | local maxLev = minLev + math.min(10, #levelList - (levelLot-1) * 10) - 1 | |
317 | ||
318 | term.setCursorPos(7, 2) | |
319 | term.setBackgroundColour(colours.black) | |
320 | term.clearLine() | |
321 | for j = 1,49 do updateMap(j,2) end | |
322 | ||
323 | term.setBackgroundColour(colours.black) | |
324 | term.setTextColour(colours.white) | |
325 | term.setCursorPos(7, 2) | |
326 | local msg = "Levels "..minLev.." to "..maxLev.." of "..#levelList | |
327 | term.write(msg) | |
328 | ||
329 | term.setTextColour(colours.yellow) | |
330 | term.setCursorPos(4, 2) | |
331 | if levelLot > 1 then term.write("<-") | |
332 | else term.write(" ") end | |
333 | ||
334 | term.setCursorPos(8 + #msg, 2) | |
335 | if maxLev < #levelList then term.write("->") | |
336 | else term.write(" ") end | |
337 | ||
338 | for i = 1,10 do | |
339 | term.setCursorPos(1, 3+i) | |
340 | for j = 1,49 do updateMap(j,3+i) end | |
341 | term.setTextColour(colours.white) | |
342 | term.setBackgroundColour(colours.black) | |
343 | term.setCursorPos(17, 3+i) | |
344 | if i + (levelLot-1)*10 - 1 < maxLev then | |
345 | term.write(levelList[10 * (levelLot-1) + i]) | |
346 | end | |
347 | end | |
348 | end | |
349 | ||
350 | --Loads up and draws up the title screen, for a nice | |
351 | --intro to Gold Runner | |
352 | local function loadTitleScreen() | |
353 | map = {} | |
354 | goldMap = {} | |
355 | monks = {} | |
356 | goldCount = 0 | |
357 | for i=1,#titleLevel do | |
358 | local line = titleLevel[i] | |
359 | goldMap[#map+1] = {} | |
360 | map[#map+1] = {} | |
361 | for i=1,math.min(#line,49) do | |
362 | local lchar = string.sub(line,i,i) | |
363 | parseValue(i, #map, lchar) | |
364 | end | |
365 | if #map == 18 then break end | |
366 | end | |
367 | maxGoldCount = goldCount | |
368 | ||
369 | drawMap() | |
370 | term.setCursorPos(1,19) | |
371 | term.setBackgroundColour(colours.blue) | |
372 | term.clearLine() | |
373 | ||
374 | menIndex = 1 | |
375 | titleLoaded = true | |
376 | end | |
377 | ||
378 | --Opens an in-game menu to display a series of options. | |
379 | local function inGameMenu(menuList) | |
380 | menIndex = 1 | |
381 | ||
382 | local squareTop,squareBottom = 4,6 + #menuList * 2 | |
383 | local squareSize = 0 | |
384 | for i=1,#menuList do squareSize = math.max(squareSize, #menuList[i] + 6) end | |
385 | ||
386 | for y=squareTop,squareBottom do | |
387 | term.setCursorPos(w/2 - squareSize/2, y) | |
388 | term.setBackgroundColour(colours.lightBlue) | |
389 | term.write(string.rep(" ", squareSize)) | |
390 | ||
391 | if y ~= squareTop and y ~= squareBottom then | |
392 | term.setCursorPos(w/2 - squareSize/2 + 1, y) | |
393 | term.setBackgroundColour(colours.black) | |
394 | term.write(string.rep(" ", squareSize - 2)) | |
395 | end | |
396 | ||
397 | if y ~= squareTop and y ~= squareBottom and y % 2 == 0 then | |
398 | local opt = menuList[(y - squareTop) / 2] | |
399 | term.setCursorPos(w/2 - #opt/2, y) | |
400 | term.setTextColour(colours.white) | |
401 | term.write(opt) | |
402 | end | |
403 | end | |
404 | ||
405 | local p1 = nil | |
406 | repeat | |
407 | for i=1,#menuList do | |
408 | term.setBackgroundColour(colours.black) | |
409 | term.setTextColour(colours.yellow) | |
410 | if i == menIndex then | |
411 | term.setCursorPos(w/2 - squareSize/2 + 1, squareTop + i * 2) | |
412 | term.write(">") | |
413 | term.setCursorPos(w/2 + squareSize/2 - 2, squareTop + i * 2) | |
414 | term.write("<") | |
415 | else | |
416 | term.setCursorPos(w/2 - squareSize/2 + 1, squareTop + i * 2) | |
417 | term.write(" ") | |
418 | term.setCursorPos(w/2 + squareSize/2 - 2, squareTop + i * 2) | |
419 | term.write(" ") | |
420 | end | |
421 | end | |
422 | _,p1 = os.pullEvent("key") | |
423 | ||
424 | if p1 == keys.up and menIndex > 1 then menIndex = menIndex - 1 | |
425 | elseif p1 == keys.down and menIndex < #menuList then menIndex = menIndex + 1 end | |
426 | until p1 == keys.enter | |
427 | ||
428 | return menuList[menIndex] | |
429 | end | |
430 | ||
431 | --Checks to see if any given desired move is legal. Monks and players both use this. | |
432 | local function isLegalMove(initX,initY,finX,finY) | |
433 | if finY < 1 or finY > #map or finX < 1 or finX > 49 then | |
434 | return false | |
435 | end | |
436 | ||
437 | if map[finY][finX] ~= 0 and map[finY][finX] ~= '#' then | |
438 | --This reports 'self moves' as being illegal, but that's fine | |
439 | for _,monk in pairs(monks) do | |
440 | if monk.x == finX and monk.y == finY then return false end | |
441 | end | |
442 | ||
443 | - | map[finY][finX] == "-" or (map[finY][finX] == 'h' and goldCount ~= 0)) |
443 | + | |
444 | then return true | |
445 | - | elseif finX == initX-1 or finX == initX+1 then return true |
445 | + | |
446 | or (type(map[finY][finX]) == "number" and map[finY][finX] > 0) or map[finY][finX] == nil or | |
447 | map[finY][finX] == "V" or map[finY][finX] == "-" or (map[finY][finX] == 'h' and goldCount ~= 0)) | |
448 | then return true | |
449 | elseif finX == initX-1 or finX == initX+1 then | |
450 | return true | |
451 | end | |
452 | end | |
453 | end | |
454 | ||
455 | --Moves the player to a given step. | |
456 | local function movePlayer(x,y,ignoreLegal) | |
457 | if not ignoreLegal and not isLegalMove(plX,plY,x,y) then return false end | |
458 | ||
459 | local ox = plX | |
460 | local oy = plY | |
461 | plX = x | |
462 | plY = y | |
463 | ||
464 | updateMap(ox,oy) | |
465 | updateMap(x,y) | |
466 | if goldMap[y][x] == 1 then | |
467 | goldMap[y][x] = 0 | |
468 | goldCount = goldCount - 1 | |
469 | playerScore = playerScore + 5 | |
470 | if started then drawHUD() end | |
471 | if (goldCount == 0) then | |
472 | drawEndgameMap() | |
473 | end | |
474 | elseif exX == plX and exY == plY and goldCount == 0 then | |
475 | - | and (map[y+1][x] == nil or map[y+1][x] == 2 or map[y+1][x] == '-')) |
475 | + | |
476 | nextLevel = true | |
477 | end | |
478 | ||
479 | pfalling = (y < #map and map[y][x] ~= '-' and map[y][x] ~= 'H' and not (map[y][x] == 'h' and goldCount == 0) | |
480 | and (map[y+1][x] == nil or map[y+1][x] == "V" or map[y+1][x] == 2 or map[y+1][x] == '-')) | |
481 | if (y < #map and map[y+1][x] == 'h' and goldCount ~= 0) then pfalling = true end | |
482 | for _,monk in pairs(monks) do | |
483 | if monk.x == plX and monk.y == plY + 1 then pfalling = false break end | |
484 | end | |
485 | ||
486 | return true | |
487 | end | |
488 | ||
489 | local function updateMonks() | |
490 | for _,monk in pairs(monks) do | |
491 | --Absolute first step- if he's trapped or dead, he's going nowhere | |
492 | - | (map[monk.y][monk.x] == 'h' and goldCount == 0) and (map[monk.y+1][monk.x] == nil or |
492 | + | |
493 | --If he's just spawned he takes a second to orient himself | |
494 | elseif monk.justSpawned then | |
495 | monk.justSpawned = false | |
496 | --We evaluate their falling behaviour here (as freed monks CAN stand on air) | |
497 | monk.falling = (monk.y < #map and map[monk.y][monk.x] ~= '-' and map[monk.y][monk.x] ~= "H" and not | |
498 | (map[monk.y][monk.x] == 'h' and goldCount == 0) and (map[monk.y+1][monk.x] == nil or map[monk.y+1][monk.x] == "V" or | |
499 | map[monk.y+1][monk.x] == 2 or map[monk.y+1][monk.x] == '-') and type(map[monk.y][monk.x] ~= "number")) | |
500 | for _,omonk in pairs(monks) do | |
501 | if omonk.x == monk.x and omonk.y == monk.y + 1 then monk.falling = false break end | |
502 | end | |
503 | if monk.x == plX and monk.y == plY + 1 then monk.falling = false end | |
504 | --Then we consider if he's just gotten out of a hole | |
505 | elseif monk.justEscaped then | |
506 | monk.justEscaped = false | |
507 | --He tries the player side first | |
508 | local playerSide = (plX-monk.x) / math.abs(plX-monk.x) | |
509 | if isLegalMove(monk.x, monk.y, monk.x + playerSide, monk.y) then | |
510 | monk.x = monk.x + playerSide | |
511 | updateMap(monk.x - playerSide, monk.y) | |
512 | elseif isLegalMove(monk.x, monk.y, monk.x - playerSide, monk.y) then | |
513 | monk.x = monk.x - playerSide | |
514 | updateMap(monk.x + playerSide, monk.y) | |
515 | end | |
516 | drawMonk(monk) | |
517 | --Then we evaluate falling | |
518 | elseif monk.falling then | |
519 | monk.behaviour = "none" | |
520 | monk.y = monk.y + 1 | |
521 | updateMap(monk.x, monk.y-1) | |
522 | drawMonk(monk) | |
523 | - | (map[monk.y][monk.x] == 'h' and goldCount == 0) and (map[monk.y+1][monk.x] == nil or |
523 | + | |
524 | if type(map[monk.y][monk.x]) == "number" then | |
525 | monk.trapped = os.startTimer(monkTrapIntv) | |
526 | monk.falling = false | |
527 | else | |
528 | monk.falling = (monk.y < #map and map[monk.y][monk.x] ~= '-' and map[monk.y][monk.x] ~= "H" and not | |
529 | (map[monk.y][monk.x] == 'h' and goldCount == 0) and (map[monk.y+1][monk.x] == nil or map[monk.y+1][monk.x] == "V" or | |
530 | map[monk.y+1][monk.x] == 2 or map[monk.y+1][monk.x] == '-') and type(map[monk.y][monk.x] ~= "number")) | |
531 | for _,omonk in pairs(monks) do | |
532 | if omonk.x == monk.x and omonk.y == monk.y + 1 then monk.falling = false break end | |
533 | end | |
534 | if monk.x == plX and monk.y == plY + 1 then monk.falling = false end | |
535 | if monk.justEscaped then monk.falling = false end | |
536 | end | |
537 | --If he's on his feet and not trapped, he's allowed to think about where to move | |
538 | elseif monk.y == plY then | |
539 | --Is the monk on the same level as the player? How lucky! They'll just walk towards him | |
540 | monk.desX = plX | |
541 | monk.behaviour = "across" | |
542 | --Y difference takes precedence over X (as in the original, makes them a bit smarter) | |
543 | elseif monk.y < plY then | |
544 | --If they can move up, they will | |
545 | if isLegalMove(monk.x,monk.y,monk.x,monk.y+1) and not monk.justEscaped then | |
546 | - | (map[monk.y][monk.x] == 'h' and goldCount == 0) and (map[monk.y+1][monk.x] == nil or |
546 | + | |
547 | updateMap(monk.x, monk.y-1) | |
548 | drawMonk(monk) | |
549 | monk.desX = nil | |
550 | --A down move can lead to a fall, so we check if they're now falling. | |
551 | monk.falling = (monk.y < #map and map[monk.y][monk.x] ~= '-' and map[monk.y][monk.x] ~= "H" and not | |
552 | (map[monk.y][monk.x] == 'h' and goldCount == 0) and (map[monk.y+1][monk.x] == nil or map[monk.y+1][monk.x] == "V" or | |
553 | map[monk.y+1][monk.x] == 2 or map[monk.y+1][monk.x] == '-') and type(map[monk.y][monk.x] ~= "number")) | |
554 | for _,omonk in pairs(monks) do | |
555 | if omonk.x == monk.x and omonk.y == monk.y + 1 then monk.falling = false break end | |
556 | end | |
557 | if monk.x == plX and monk.y == plY + 1 then monk.falling = false end | |
558 | --Otherwise, it's off to the nearest ladder, monkey bars or perilous ledge to jump off | |
559 | --assuming they haven't found one already | |
560 | elseif monk.desX == nil then | |
561 | if monk.behaviour ~= "down" then monk.desX = nil end | |
562 | monk.behaviour = "down" | |
563 | monk.desX = nil | |
564 | local cmLeft = true | |
565 | local cmRight = true | |
566 | --We try to find the nearest by searching alternate left and right at variable distance | |
567 | for i=1,math.max(monk.x - 1, 49 - monk.x) do | |
568 | if monk.x-i > 0 and cmLeft then | |
569 | --If a wall blocks the monks path, they can't keep going left or right | |
570 | cmLeft = map[monk.y][monk.x-i] ~= 0 | |
571 | --But if it's all clear, they look for something to climb/jump down | |
572 | if cmLeft and (map[monk.y+1][monk.x-i] == "H" or (map[monk.y+1][monk.x-i] == 'h' and goldCount == 0) | |
573 | or map[monk.y+1][monk.x-i] == nil or map[monk.y][monk.x-i] == '-') then | |
574 | monk.desX = monk.x-i | |
575 | break | |
576 | end | |
577 | end | |
578 | if monk.x+i < 50 and cmRight then | |
579 | --If a wall blocks the monks path, they can't keep going left or right | |
580 | cmRight = map[monk.y][monk.x+i] ~= 0 | |
581 | --But if it's all clear, they look for something to climb/jump down | |
582 | if cmRight and (map[monk.y+1][monk.x+i] == "H" or (map[monk.y+1][monk.x+i] == 'h' and goldCount == 0) | |
583 | or map[monk.y+1][monk.x+i] == nil or map[monk.y][monk.x+i] == '-') then | |
584 | monk.desX = monk.x+i | |
585 | break | |
586 | end | |
587 | end | |
588 | end | |
589 | end | |
590 | elseif monk.y > plY then | |
591 | if monk.behaviour ~= "up" then monk.desX = nil end | |
592 | monk.behaviour = "up" | |
593 | --Same deal again- try moving up first | |
594 | if isLegalMove(monk.x,monk.y,monk.x,monk.y-1) then | |
595 | monk.y = monk.y-1 | |
596 | updateMap(monk.x, monk.y+1) | |
597 | drawMonk(monk) | |
598 | monk.desX = nil | |
599 | --You can never move up and start falling, so we don't bother to check | |
600 | --Otherwise they need ladders to climb up | |
601 | elseif monk.desX == nil then | |
602 | monk.behaviour = "up" | |
603 | monk.desX = nil | |
604 | local cmLeft = true | |
605 | local cmRight = true | |
606 | --We try to find the nearest by searching alternate left and right at variable distance | |
607 | for i=1,math.max(monk.x - 1, 49 - monk.x) do | |
608 | if monk.x-i > 0 and cmLeft then | |
609 | --If a wall blocks the monks path or a pit is in the way, they can't keep going left or right | |
610 | cmLeft = map[monk.y][monk.x-i] ~= 0 and (monk.y == #map or map[monk.y+1][monk.x-i] ~= nil | |
611 | or map[monk.y][monk.x-i] == '-' or map[monk.y][monk.x-i] == "H" or (map[monk.y][monk.x-i] == "h" | |
612 | and goldCount == 0)) | |
613 | --But if it's all clear, they look for a ladder | |
614 | if cmLeft and (map[monk.y][monk.x-i] == "H" or (map[monk.y][monk.x-i] == 'h' and goldCount == 0)) then | |
615 | monk.desX = monk.x-i | |
616 | break | |
617 | end | |
618 | end | |
619 | if monk.x+i < 50 and cmRight then | |
620 | cmRight = map[monk.y][monk.x+i] ~= 0 and (monk.y == #map or map[monk.y+1][monk.x+i] ~= nil | |
621 | or map[monk.y][monk.x+i] == '-' or map[monk.y][monk.x+i] == "H" or (map[monk.y][monk.x+i] == "h" | |
622 | and goldCount == 0)) | |
623 | if cmRight and (map[monk.y][monk.x+i] == "H" or (map[monk.y][monk.x+i] == 'h' and goldCount == 0)) then | |
624 | monk.desX = monk.x+i | |
625 | break | |
626 | end | |
627 | end | |
628 | end | |
629 | end | |
630 | end | |
631 | ||
632 | if not (monk.trapped or monk.dead) then | |
633 | --Has the monk decided on moving left or right? If so we try to move him | |
634 | if monk.desX and not monk.falling then | |
635 | local mdir = monk.desX - monk.x | |
636 | local mdir = mdir / math.abs(mdir) | |
637 | if isLegalMove(monk.x,monk.y,monk.x+mdir,monk.y) then | |
638 | monk.x = monk.x + mdir | |
639 | updateMap(monk.x - mdir, monk.y) | |
640 | drawMonk(monk) | |
641 | - | (map[monk.y][monk.x] == 'h' and goldCount == 0) and (map[monk.y+1][monk.x] == nil or |
641 | + | |
642 | --This allows re-evaluations if they get stuck- not ideal but good enough | |
643 | monk.desX = nil | |
644 | end | |
645 | end | |
646 | monk.falling = (monk.y < #map and map[monk.y][monk.x] ~= '-' and map[monk.y][monk.x] ~= "H" and not | |
647 | (map[monk.y][monk.x] == 'h' and goldCount == 0) and (map[monk.y+1][monk.x] == nil or map[monk.y+1][monk.x] == "V" or | |
648 | map[monk.y+1][monk.x] == 2 or map[monk.y+1][monk.x] == '-') and type(map[monk.y][monk.x] ~= "number")) | |
649 | for _,omonk in pairs(monks) do | |
650 | if omonk.x == monk.x and omonk.y == monk.y + 1 then monk.falling = false break end | |
651 | end | |
652 | if monk.x == plX and monk.y == plY + 1 then monk.falling = false end | |
653 | --We have caught and killed the player | |
654 | if monk.x == plX and monk.y == plY and spawnTimer == -1 then | |
655 | spawnTimer = os.startTimer(2) | |
656 | end | |
657 | end | |
658 | end | |
659 | end | |
660 | - | map[v.y][v.x] = 2 |
660 | + | |
661 | - | v.timer = os.startTimer(blockIntv) |
661 | + | |
662 | local remAt = nil | |
663 | for i,v in ipairs(blockTimers) do | |
664 | if v.timer == tid then | |
665 | if map[v.y][v.x] == 3 then | |
666 | for _,monk in pairs(monks) do | |
667 | if monk.x == v.x and monk.y == v.y-1 then | |
668 | map[v.y][v.x] = 0 | |
669 | remAt = i | |
670 | break | |
671 | end | |
672 | end | |
673 | if not remAt then | |
674 | map[v.y][v.x] = 2 | |
675 | v.timer = os.startTimer(blockIntv) | |
676 | end | |
677 | elseif map[v.y][v.x] == 2 then | |
678 | map[v.y][v.x] = 1 | |
679 | v.timer = os.startTimer(0.1) | |
680 | elseif map[v.y][v.x] == 1 then | |
681 | map[v.y][v.x] = 0 | |
682 | --If the player is caught in a block, he dies | |
683 | if v.y == plY and v.x == plX then | |
684 | spawnTimer = os.startTimer(2) | |
685 | end | |
686 | for _,monk in pairs(monks) do | |
687 | if monk.x == v.x and monk.y == v.y then | |
688 | monk.dead = os.startTimer(monkSpawnIntv) | |
689 | --Easiest way to get them out of the way rather than evaluation | |
690 | monk.x = -1 | |
691 | monk.y = -1 | |
692 | monk.trapped = nil | |
693 | end | |
694 | end | |
695 | remAt = i | |
696 | end | |
697 | updateMap(v.x,v.y) | |
698 | break | |
699 | end | |
700 | end | |
701 | if remAt then table.remove(blockTimers,remAt) end | |
702 | end | |
703 | ||
704 | local function shootBlock(x,y) | |
705 | if y <= #map and map[y][x] == 0 and (map[y-1][x] == nil | |
706 | or map[y-1][x] == 2 or (map[y-1][x] == 'h' and goldCount > 0)) then | |
707 | map[y][x] = 3 | |
708 | table.insert(blockTimers, {x = x; y = y; timer = os.startTimer(0.1);} ) | |
709 | updateMap(x,y) | |
710 | end | |
711 | end | |
712 | ||
713 | local function handleEvents() | |
714 | local id,p1,p2,p3 = os.pullEvent() | |
715 | ||
716 | if id == "key" then | |
717 | --Menu Handling | |
718 | if p1 == keys.up then | |
719 | if menIndex > 1 then menIndex = menIndex - 1 end | |
720 | elseif p1 == keys.down then | |
721 | if inLevelSelect then | |
722 | if menIndex < math.min(10, #levelList - (levelLot-1)*10) then | |
723 | menIndex = menIndex + 1 | |
724 | end | |
725 | elseif menIndex < #titleOptions then menIndex = menIndex + 1 end | |
726 | elseif p1 == keys.left and inLevelSelect and levelLot > 1 then | |
727 | levelLot = levelLot - 1 | |
728 | drawLevelList() | |
729 | elseif p1 == keys.right and inLevelSelect and levelLot * 10 < #levelList then | |
730 | levelLot = levelLot + 1 | |
731 | drawLevelList() | |
732 | end | |
733 | ||
734 | --Game Handling | |
735 | if p1 == keys.a and moveTimer == -1 and spawnTimer == -1 then | |
736 | movePlayer(plX-1,plY) | |
737 | moveTimer = os.startTimer(moveIntv) | |
738 | elseif p1 == keys.d and moveTimer == -1 and spawnTimer == -1 then | |
739 | movePlayer(plX+1,plY) | |
740 | moveTimer = os.startTimer(moveIntv) | |
741 | elseif p1 == keys.w and moveTimer == -1 and spawnTimer == -1 then | |
742 | movePlayer(plX,plY-1) | |
743 | moveTimer = os.startTimer(moveIntv) | |
744 | elseif p1 == keys.s and moveTimer == -1 and spawnTimer == -1 then | |
745 | movePlayer(plX,plY+1) | |
746 | moveTimer = os.startTimer(moveIntv) | |
747 | elseif p1 == keys.q and shootTimer == -1 and not pfalling and spawnTimer == -1 then | |
748 | shootBlock(plX-1,plY+1) | |
749 | shootTimer = os.startTimer(moveIntv) | |
750 | elseif p1 == keys.e and shootTimer == -1 and not pfalling and spawnTimer == -1 then | |
751 | shootBlock(plX+1,plY+1) | |
752 | shootTimer = os.startTimer(moveIntv) | |
753 | elseif p1 == keys.space and started then | |
754 | started = false | |
755 | elseif p1 == keys.enter then | |
756 | if not started then | |
757 | if inLevelSelect then | |
758 | currentLevel = menIndex + (levelLot - 1) * 10 | |
759 | menSel = "New Game" | |
760 | else | |
761 | menSel = titleOptions[menIndex] | |
762 | end | |
763 | else | |
764 | started = false | |
765 | menIndex = 1 | |
766 | menSel = inGameMenu(inGameOptions) | |
767 | end | |
768 | end | |
769 | elseif id == "timer" then | |
770 | if p1 == shootTimer then shootTimer = -1 | |
771 | elseif p1 == spawnTimer then | |
772 | started = false | |
773 | elseif p1 == moveTimer then | |
774 | if pfalling then | |
775 | movePlayer(plX,plY+1) | |
776 | moveTimer = os.startTimer(moveIntv) | |
777 | else | |
778 | moveTimer = -1 | |
779 | end | |
780 | elseif p1 == monkTimer then | |
781 | updateMonks() | |
782 | monkTimer = os.startTimer(moveIntv * 2) | |
783 | elseif updateBlockTimer(p1) then | |
784 | else | |
785 | for _,monk in pairs(monks) do | |
786 | if p1 == monk.trapped then | |
787 | --You can stand on a monk to force them to be killed- so we check for that | |
788 | --along with being buried in tunnels, etc. | |
789 | local stillTrapped = map[monk.y-1][monk.x] == 0 or (plX == monk.x and plY == monk.y-1) | |
790 | for _,omonk in pairs(monks) do | |
791 | if omonk.x == monk.x and omonk.y == monk.y-1 then | |
792 | stillTrapped = true | |
793 | break | |
794 | end | |
795 | end | |
796 | --Perpetually trapped monks will try to excape much more quickly | |
797 | if stillTrapped then | |
798 | --This needs to be tweaked | |
799 | monk.trapped = os.startTimer(0.75) | |
800 | else | |
801 | --When free, they head in your general direction, re-evaluate later | |
802 | monk.y = monk.y - 1 | |
803 | --This is necessary to stop 'double jumping' | |
804 | monk.desX = nil | |
805 | monk.trapped = nil | |
806 | monk.behaviour = "none" | |
807 | monk.justEscaped = true | |
808 | ||
809 | updateMap(monk.x, monk.y+1) | |
810 | drawMonk(monk) | |
811 | end | |
812 | break | |
813 | elseif p1 == monk.dead then | |
814 | --Same deal- you can camp spawn | |
815 | local stillDead = plX == monk.spawnX and plY == monk.spawnY | |
816 | for _,omonk in pairs(monks) do | |
817 | if omonk.x == monk.spawnX and omonk.y == monk.spawnY then | |
818 | stillDead = true | |
819 | break | |
820 | end | |
821 | end | |
822 | --They'll spawn the second you give them the chance | |
823 | if stillDead then | |
824 | monk.dead = os.startTimer(0.5) | |
825 | else | |
826 | monk.x = monk.spawnX | |
827 | monk.y = monk.spawnY | |
828 | - | { t = colours.yellow, b = colours.blue, s = "$", n = "Buried Gold", v = colours.orange }, |
828 | + | |
829 | monk.justSpawned = true | |
830 | monk.behaviour = "none" | |
831 | - | { t = colours.brown, b = colours.black, s = "-", n = "Hand-to-hand Bars", v = "-" }, |
831 | + | |
832 | break | |
833 | end | |
834 | end | |
835 | end | |
836 | end | |
837 | end | |
838 | end | |
839 | ||
840 | --[[ Level Editor ]]-- | |
841 | ||
842 | local pallette = { { t = colours.black, b = colours.blue, s = " ", n = "Solid Ground", v = 0 }, | |
843 | { t = colours.orange, b = colours.blue, s = "V", n = "Trap Ground", v = "V" }, | |
844 | { t = colours.grey, b = colours.grey, s = " ", n = "Cement Ground", v = "#" }, | |
845 | { t = colours.brown, b = colours.black, s = "H", n = "Ladder", v = "H" }, | |
846 | { t = colours.brown, b = colours.black, s = "-", n = "Monkey Bars", v = "-" }, | |
847 | { t = colours.white, b = colours.black, s = "&", n = "Player Spawn", v = "player" }, | |
848 | { t = colours.red, b = colours.black, s = "&", n = "Mad Monk", v = "&" }, | |
849 | { t = colours.yellow, b = colours.black, s = "$", n = "Gold", v = "$" }, | |
850 | { t = colours.lightGrey, b = colours.black, s = "H", n = "Hidden Ladder", v = "h" }, | |
851 | { t = colours.lime, b = colours.black, s = "@", n = "Exit Portal", v = "@" }, | |
852 | { t = colours.red, b = colours.black, s = "ERASE", n = "Eraser", v = nil } } | |
853 | local brushType = 1 | |
854 | ||
855 | local function getHexOf(colour) | |
856 | if not colour or not tonumber(colour) then | |
857 | return " " | |
858 | end | |
859 | local value = math.log(colour)/math.log(2) | |
860 | if value > 9 then | |
861 | value = hexnums[value] | |
862 | end | |
863 | return value | |
864 | end | |
865 | ||
866 | local function drawFooter() | |
867 | for i=1,h-1 do | |
868 | if i % 2 == 0 then term.setBackgroundColour(colours.grey) | |
869 | else term.setBackgroundColour(colours.yellow) end | |
870 | term.setCursorPos(1,i) | |
871 | term.write(" ") | |
872 | term.setCursorPos(w,i) | |
873 | term.write(" ") | |
874 | end | |
875 | ||
876 | term.setBackgroundColour(colours.black) | |
877 | term.setTextColour(colours.blue) | |
878 | term.setCursorPos(2,h) | |
879 | term.clearLine() | |
880 | term.write("Editor Mode: ") | |
881 | term.setTextColour(colours.yellow) | |
882 | term.write(levelEditName) | |
883 | local msg = "Tool: "..pallette[brushType].n.." "..pallette[brushType].s | |
884 | term.setCursorPos(w - #msg - 1, 19) | |
885 | term.setTextColour(colours.blue) | |
886 | term.write("Tool: ") | |
887 | term.setTextColour(colours.yellow) | |
888 | term.write(pallette[brushType].n.." ") | |
889 | term.setBackgroundColour(pallette[brushType].b) | |
890 | term.setTextColour(pallette[brushType].t) | |
891 | term.write(pallette[brushType].s) | |
892 | end | |
893 | ||
894 | local function drawPallette(xpos,ypos) | |
895 | local xdim = 7 | |
896 | local ydim = 5 | |
897 | local left = xpos | |
898 | local top = ypos | |
899 | if xpos + xdim > w then left = left + (w - xpos - xdim) end | |
900 | if ypos + ydim > h then top = top + (h - ypos - ydim) end | |
901 | ||
902 | --There's no easy way to do this... so we draw it manually :( | |
903 | for i=0,4 do | |
904 | term.setCursorPos(left, top + i) | |
905 | term.setBackgroundColour(colours.black) | |
906 | term.setTextColour(colours.red) | |
907 | if i == 0 or i == 4 then term.write("*-----*") | |
908 | else term.write("* *") end | |
909 | end | |
910 | ||
911 | for i=1,#pallette-1 do | |
912 | local ypl = 1 | |
913 | local xmv = i | |
914 | if i > 5 then ypl = 2 xmv = i - 5 end | |
915 | ||
916 | term.setCursorPos(left + xmv, top+ypl) | |
917 | term.setBackgroundColour(pallette[i].b) | |
918 | term.setTextColour(pallette[i].t) | |
919 | term.write(pallette[i].s) | |
920 | end | |
921 | ||
922 | term.setCursorPos(left + 1, top + 3) | |
923 | term.setBackgroundColour(colours.red) | |
924 | term.setTextColour(colours.black) | |
925 | term.write("ERASE") | |
926 | ||
927 | local _,button,x,y = os.pullEvent("mouse_click") | |
928 | ||
929 | if button == 1 then | |
930 | if y == top + 1 and x > left and x < left + 6 then | |
931 | brushType = x-left | |
932 | elseif y == top + 2 and x > left and x < left + 6 then | |
933 | brushType = x-left+5 | |
934 | elseif y == top + 3 and x > left and x < left + 6 then | |
935 | brushType = 11 | |
936 | end | |
937 | end | |
938 | ||
939 | for y = top,top+ydim do | |
940 | for x = left,left+xdim do | |
941 | --Not sure why the -2 is necessary | |
942 | if map[y+drawOffsetY] then updateMap(x-2,y+drawOffsetY) end | |
943 | end | |
944 | end | |
945 | drawFooter() | |
946 | return | |
947 | end | |
948 | ||
949 | local function saveCurrentMap(path) | |
950 | local file = io.open(shell.resolve(".").."/levels/"..path, "w") | |
951 | if not file then return false end | |
952 | ||
953 | drawMap() | |
954 | drawFooter() | |
955 | - | if map[y][x] == 0 then |
955 | + | |
956 | - | if goldMap[y][x] == 1 then xstr = xstr..getHexOf(colours.orange) |
956 | + | |
957 | - | else xstr = xstr..getHexOf(colours.blue) end |
957 | + | |
958 | term.setBackgroundColour(colours.blue) | |
959 | term.write(msg) | |
960 | term.setCursorPos(w/2-9, 6) | |
961 | term.setBackgroundColour(colours.red) | |
962 | term.write(string.rep(" ", 18)) | |
963 | term.setCursorPos(w/2-9,6) | |
964 | term.setBackgroundColour(colours.lime) | |
965 | ||
966 | for y=1,#map do | |
967 | local xstr = "" | |
968 | for x=1,49 do | |
969 | --This again... | |
970 | if map[y][x] == 0 then xstr = xstr..getHexOf(colours.blue) | |
971 | elseif map[y][x] == "V" then xstr = xstr..getHexOf(colours.orange) | |
972 | elseif map[y][x] == "#" then xstr = xstr..getHexOf(colours.grey) | |
973 | elseif map[y][x] == "H" then xstr = xstr..getHexOf(colours.brown) | |
974 | elseif map[y][x] == "h" then xstr = xstr..getHexOf(colours.lightGrey) | |
975 | elseif map[y][x] == "-" then xstr = xstr..getHexOf(colours.green) | |
976 | elseif map[y][x] == "&" then xstr = xstr..getHexOf(colours.red) | |
977 | elseif goldMap[y][x] == 1 then xstr = xstr..getHexOf(colours.yellow) | |
978 | elseif plX == x and plY == y then xstr = xstr..getHexOf(colours.white) | |
979 | elseif exX == x and exY == y then xstr = xstr..getHexOf(colours.lime) | |
980 | else xstr = xstr.." " | |
981 | end | |
982 | end | |
983 | file:write(xstr.."\n") | |
984 | term.write(" ") | |
985 | sleep(0) | |
986 | end | |
987 | file:close() | |
988 | return true | |
989 | end | |
990 | ||
991 | local function runLevelEditor() | |
992 | inLevelEditor = true | |
993 | term.setBackgroundColour(colours.black) | |
994 | term.clear() | |
995 | if not fs.exists(shell.resolve(".").."/levels/"..levelEditName) then | |
996 | map = {} | |
997 | goldMap = {} | |
998 | monks = {} | |
999 | for i=1,18 do map[i] = {} goldMap[i] = {} end | |
1000 | plX = 2 | |
1001 | plY = 2 | |
1002 | plspawnX = plX | |
1003 | plspawnY = plY | |
1004 | exX = 48 | |
1005 | exY = 17 | |
1006 | else | |
1007 | loadMap(shell.resolve(".").."/levels/"..levelEditName) | |
1008 | for _,monk in pairs(monks) do | |
1009 | map[monk.y][monk.x] = "&" | |
1010 | end | |
1011 | monks = {} | |
1012 | end | |
1013 | ||
1014 | drawMap() | |
1015 | drawFooter() | |
1016 | ||
1017 | while inLevelEditor do | |
1018 | local id,button,x,y = os.pullEvent() | |
1019 | if id == "mouse_click" or id == "mouse_drag" then | |
1020 | if button == 2 then | |
1021 | drawPallette(x,y) | |
1022 | elseif x > drawOffsetX and x <= 49 + drawOffsetX and y > drawOffsetY and y <= 18 + drawOffsetY then | |
1023 | if pallette[brushType].v == "player" then | |
1024 | local ox = plX | |
1025 | local oy = plY | |
1026 | if plX == exX and plY == exY then | |
1027 | exX = ox | |
1028 | exY = oy | |
1029 | - | elseif pallette[brushType].v == colours.orange then |
1029 | + | |
1030 | - | map[y-drawOffsetY][x-drawOffsetX] = 0 |
1030 | + | |
1031 | plY = y - drawOffsetY | |
1032 | map[plY][plX] = nil | |
1033 | goldMap[plY][plX] = nil | |
1034 | updateMap(ox,oy) | |
1035 | elseif pallette[brushType].v == "@" then | |
1036 | local ox = exX | |
1037 | local oy = exY | |
1038 | if plX == exX and plY == exY then | |
1039 | plX = ox | |
1040 | plY = oy | |
1041 | end | |
1042 | exX = x - drawOffsetX | |
1043 | exY = y - drawOffsetY | |
1044 | map[plY][plX] = nil | |
1045 | goldMap[plY][plX] = nil | |
1046 | updateMap(ox,oy) | |
1047 | elseif pallette[brushType].v == "$" then | |
1048 | goldMap[y-drawOffsetY][x-drawOffsetX] = 1 | |
1049 | map[y-drawOffsetY][x-drawOffsetX] = nil | |
1050 | elseif pallette[brushType].v == nil then | |
1051 | map[y-drawOffsetY][x-drawOffsetX] = nil | |
1052 | goldMap[y-drawOffsetY][x-drawOffsetX] = nil | |
1053 | else | |
1054 | map[y-drawOffsetY][x-drawOffsetX] = pallette[brushType].v | |
1055 | goldMap[y-drawOffsetY][x-drawOffsetX] = nil | |
1056 | --term.setCursorPos(1,19) | |
1057 | --print("At "..(x-drawOffsetX)..", "..(y-drawOffsetY).." have placed "..pallette[brushType].v) | |
1058 | end | |
1059 | updateMap(x-drawOffsetX, y-drawOffsetY) | |
1060 | end | |
1061 | elseif id == "mouse_scroll" then | |
1062 | brushType = brushType + button | |
1063 | if brushType == 0 then brushType = #pallette | |
1064 | elseif brushType > #pallette then brushType = 1 end | |
1065 | drawFooter() | |
1066 | elseif id == "key" and button == keys.enter then | |
1067 | menSel = inGameMenu(levelEditOptions) | |
1068 | if menSel == "Save" then | |
1069 | saveCurrentMap(levelEditName) | |
1070 | drawMap() | |
1071 | drawFooter() | |
1072 | elseif menSel == "Save and Exit" then | |
1073 | saveCurrentMap(levelEditName) | |
1074 | menSel = "none" | |
1075 | inLevelEditor = false | |
1076 | elseif menSel == "Discard and Exit" then | |
1077 | menSel = "none" | |
1078 | inLevelEditor = false | |
1079 | elseif menSel == "Play Level" then | |
1080 | saveCurrentMap(levelEditName) | |
1081 | inLevelEditor = false | |
1082 | end | |
1083 | end | |
1084 | end | |
1085 | end | |
1086 | ||
1087 | ||
1088 | local function runLevelSelect() | |
1089 | if not titleLoaded then | |
1090 | loadTitleScreen() | |
1091 | monkTimer = os.startTimer(moveIntv * 1.5) | |
1092 | else | |
1093 | drawMap() | |
1094 | drawEndgameMap() | |
1095 | term.setCursorPos(1,19) | |
1096 | term.setBackgroundColour(colours.blue) | |
1097 | term.clearLine() | |
1098 | end | |
1099 | drawLevelList() | |
1100 | ||
1101 | menSel = "none" | |
1102 | repeat | |
1103 | handleEvents() | |
1104 | ||
1105 | term.setBackgroundColour(colours.black) | |
1106 | term.setTextColour(colours.yellow) | |
1107 | for i=1,10 do | |
1108 | term.setCursorPos(16,3+i) | |
1109 | if i == menIndex then | |
1110 | term.write(">") | |
1111 | else | |
1112 | term.write(" ") | |
1113 | end | |
1114 | end | |
1115 | until menSel ~= "none" | |
1116 | inLevelSelect = false | |
1117 | menSel = "New Game" | |
1118 | end | |
1119 | ||
1120 | local function runTitle() | |
1121 | loadTitleScreen() | |
1122 | term.setCursorPos(15,3) | |
1123 | term.setTextColour(colours.red) | |
1124 | term.setBackgroundColour(colours.black) | |
1125 | term.write("Gold Runner") | |
1126 | term.setCursorPos(16,4) | |
1127 | term.write("By Nitrogen Fingers") | |
1128 | ||
1129 | term.setTextColour(colours.white) | |
1130 | for i=1,#titleOptions do | |
1131 | term.setCursorPos(19, 5 + (i*2)) | |
1132 | term.write(titleOptions[i]) | |
1133 | end | |
1134 | ||
1135 | term.setCursorPos(16, 7) | |
1136 | term.setTextColour(colours.yellow) | |
1137 | term.write("->") | |
1138 | ||
1139 | menSel = "none" | |
1140 | monkTimer = os.startTimer(moveIntv * 1.5) | |
1141 | ||
1142 | repeat | |
1143 | handleEvents() | |
1144 | ||
1145 | term.setBackgroundColour(colours.black) | |
1146 | term.setTextColour(colours.yellow) | |
1147 | for i=1,#titleOptions do | |
1148 | term.setCursorPos(16, 5 + i*2) | |
1149 | if menIndex == i then term.write("->") | |
1150 | else term.write(" ") end | |
1151 | end | |
1152 | until menSel ~= "none" | |
1153 | end | |
1154 | ||
1155 | local function playLevel() | |
1156 | loadMap(shell.resolve(".").."/levels/"..levelList[currentLevel]) | |
1157 | running = true | |
1158 | while running do | |
1159 | drawMap() | |
1160 | drawHUD() | |
1161 | os.pullEvent("key") | |
1162 | movePlayer(plX,plY,true) | |
1163 | ||
1164 | monkTimer = os.startTimer(moveIntv * 1.5) | |
1165 | moveTimer = os.startTimer(moveIntv) | |
1166 | shootTimer = -1 | |
1167 | spawnTimer = -1 | |
1168 | ||
1169 | started = true | |
1170 | while started do | |
1171 | handleEvents() | |
1172 | end | |
1173 | ||
1174 | if menSel == "Quit" or menSel == "Back to Title" or menSel == "Edit Level" then | |
1175 | running = false | |
1176 | return | |
1177 | end | |
1178 | menSel = "none" | |
1179 | ||
1180 | if nextLevel then | |
1181 | if currentLevel == #levelList then | |
1182 | - | local msg = "All levels defeated, Code Runner!" |
1182 | + | |
1183 | running = false | |
1184 | break | |
1185 | else | |
1186 | currentLevel = currentLevel + 1 | |
1187 | playerLives = playerLives + 1 | |
1188 | resetMap() | |
1189 | loadMap(shell.resolve(".").."/levels/"..levelList[currentLevel]) | |
1190 | end | |
1191 | nextLevel = false | |
1192 | else | |
1193 | playerLives = playerLives-1 | |
1194 | if playerLives > 0 then resetMap() | |
1195 | else | |
1196 | running = false | |
1197 | end | |
1198 | end | |
1199 | end | |
1200 | ||
1201 | if nextLevel then | |
1202 | local msg = "All levels defeated, Gold Runner!" | |
1203 | term.setBackgroundColour(colours.black) | |
1204 | term.setTextColour(colours.lime) | |
1205 | term.setCursorPos(25 - #msg/2, 2) | |
1206 | term.write(msg) | |
1207 | else | |
1208 | local msg = "Game over!" | |
1209 | term.setBackgroundColour(colours.black) | |
1210 | term.setTextColour(colours.red) | |
1211 | term.setCursorPos(25 - #msg/2, 2) | |
1212 | term.write(msg) | |
1213 | end | |
1214 | currentLevel = 1 | |
1215 | sleep(2) | |
1216 | end | |
1217 | ||
1218 | term.clear() | |
1219 | if not fs.exists(shell.resolve(".").."/levels") then | |
1220 | error("Level directory not present!") | |
1221 | end | |
1222 | levelList = fs.list(shell.resolve(".").."/levels") | |
1223 | if #levelList == 0 then | |
1224 | error("Level directory is empty!") | |
1225 | end | |
1226 | ||
1227 | runTitle() | |
1228 | menIndex = 1 | |
1229 | ||
1230 | while menSel ~= "Quit" do | |
1231 | if menSel == "Select Level" then | |
1232 | inLevelSelect = true | |
1233 | runLevelSelect() | |
1234 | elseif menSel == "New Game" then | |
1235 | playerLives = 3 | |
1236 | playerScore = 0 | |
1237 | playLevel() | |
1238 | elseif menSel == "Create Level" then | |
1239 | --This is a bit lazy... well it's all been a bit lazy :P | |
1240 | drawMap() | |
1241 | term.setCursorPos(1,19) | |
1242 | term.setBackgroundColour(colours.blue) | |
1243 | term.clearLine() | |
1244 | ||
1245 | term.setCursorPos(16,10) | |
1246 | term.setBackgroundColour(colours.black) | |
1247 | term.setTextColour(colours.white) | |
1248 | term.write("Enter level name:") | |
1249 | term.setTextColour(colours.lime) | |
1250 | term.setCursorPos(17,11) | |
1251 | term.setCursorBlink(true) | |
1252 | local levelName = "" | |
1253 | ||
1254 | local id,p1 | |
1255 | repeat | |
1256 | id,p1 = os.pullEvent() | |
1257 | if id == "key" and p1 == keys.backspace then | |
1258 | levelName = string.sub(levelName, 1, #levelName - 1) | |
1259 | elseif id == "timer" and p1 == monkTimer then | |
1260 | updateMonks() | |
1261 | monkTimer = os.startTimer(moveIntv * 2) | |
1262 | elseif id == "char" and #levelName < 14 then | |
1263 | levelName = levelName..p1 | |
1264 | end | |
1265 | term.setTextColour(colours.lime) | |
1266 | term.setCursorPos(17,11) | |
1267 | term.write(levelName..string.rep(" ",14 - #levelName)) | |
1268 | term.setCursorPos(17 + #levelName ,11) | |
1269 | until id == "key" and p1 == keys.enter and #levelName > 0 | |
1270 | ||
1271 | term.setCursorBlink(false) | |
1272 | levelEditName = levelName | |
1273 | runLevelEditor() | |
1274 | ||
1275 | if menSel == "Play Level" then | |
1276 | currentLevel = nil | |
1277 | levelList = fs.list(shell.resolve(".").."/levels") | |
1278 | for num,name in pairs(levelList) do | |
1279 | if name == levelName then | |
1280 | currentLevel = num | |
1281 | break | |
1282 | end | |
1283 | end | |
1284 | menSel = "New Game" | |
1285 | else | |
1286 | menSel = "none" | |
1287 | end | |
1288 | elseif menSel == "Edit Level" then | |
1289 | levelEditName = levelList[currentLevel] | |
1290 | runLevelEditor() | |
1291 | term.setBackgroundColour(colours.black) | |
1292 | term.clear() | |
1293 | ||
1294 | if menSel == "Play Level" then | |
1295 | menSel = "New Game" | |
1296 | else | |
1297 | menSel = "none" | |
1298 | end | |
1299 | elseif menSel == "none" or menSel == "Back to Title" then | |
1300 | runTitle() | |
1301 | end | |
1302 | menIndex = 1 | |
1303 | end | |
1304 | ||
1305 | term.setBackgroundColour(colours.black) | |
1306 | shell.run("clear") | |
1307 | term.setTextColour(colours.white) | |
1308 | print("Thanks for playing Gold Runner!") |