View difference between Paste ID: GF02dzhB and gmkgBssR
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!")