Advertisement
nitrogenfingers

Rat Trap Repository

Sep 19th, 2013
453
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.55 KB | None | 0 0
  1. MKDIR rattrap
  2. MKDIR rattrap/levels
  3. MKFIL rattrap/levels/01_box.nfp
  4. WRITE 19
  5. 77777777777777777777777777777777777
  6. 7 7
  7. 7 7
  8. 7 8 8 8 7
  9. 7 7
  10. 7 5555555555555555557555555 7
  11. 7 5555755555555555555755557 7
  12. 7 8 5555555555555555555555555 8 7
  13. 7 5555555555555555555555555 7
  14. 7 5555555575550555555557555 7
  15. 7 5555555555555555555555555 7
  16. 7 8 55575555555555557555555558 7
  17. 7 5555555555555555555555555 7
  18. 7 5555555555755555555555555 7
  19. 7 8 7
  20. 7 8 8 7
  21. 7 7
  22. 7 7
  23. 77777777777777777777777777777777777
  24. MKFIL rattrap/rattrap
  25. WRITE 460
  26. --[[
  27. Rat Trap
  28. Inspired by the game by Christopher Lee Fraley
  29.  
  30. Written by: Nitrogen Fingers
  31. ]]--
  32. local w,h = term.getSize()
  33. local running = true
  34.  
  35. local map = {}
  36. local objectMap = {}
  37.  
  38. local mapXSize = 35
  39. local mapYSize = 19
  40. local drawOffsetX = 0
  41. local drawOffsetY = 0
  42.  
  43. local enemies = {}
  44. local enemyTimer = 0
  45. local enemyInterval = 1
  46.  
  47. local spawnInterval = 60
  48. local spawnTimer = 0
  49. local levelEnemies = 2
  50. local levelName = "01_box.nfp"
  51.  
  52. local electricity = {}
  53.  
  54. local plX = 0
  55. local plY = 0
  56. local pltrapped = 0
  57. local pldead = 0
  58. local lives = 3
  59. local score = 0
  60.  
  61. --[[Section: Level Loading and Updating]]--
  62.  
  63. --Takes a file character and converts it to a map or game entity
  64. local function parseValue(x, y, lchar)
  65. if tonumber(lchar, 16) then
  66. lchar = math.pow(2, tonumber(lchar,16))
  67.  
  68. if lchar == colours.lime then
  69. map[y][x] = 0
  70. elseif lchar == colours.grey then
  71. map[y][x] = 1
  72. elseif lchar == colours.red then
  73. map[y][x] = "*"
  74. elseif lchar == colours.lightGrey then
  75. map[y][x] = "o"
  76. elseif lchar == colours.white then
  77. plX = x
  78. plY = y
  79. end
  80. end
  81. end
  82.  
  83. --Loads a file and reads it in as a level
  84. local function loadMap(_sPath)
  85. if not fs.exists(_sPath) then return false end
  86. map = {}
  87. objectMap = {}
  88. enemies = {}
  89.  
  90. local file = fs.open(_sPath, "r")
  91. local line = file:readLine()
  92. while line do
  93. map[#map+1] = {}
  94. for i=1,math.min(#line,mapXSize) do
  95. local lchar = string.sub(line,i,i)
  96. parseValue(i, #map, lchar)
  97. end
  98. if #map == mapYSize then break end
  99. line = file:readLine()
  100. end
  101. file:close()
  102. return true
  103. end
  104.  
  105. --When something moves or something needs to be drawn, we
  106. --just change the appropriate tile with this method.
  107. local function updateMap(x,y)
  108. term.setCursorPos(x, y)
  109. term.setBackgroundColour(colours.green)
  110. if plX == x and plY == y then
  111. if pltrapped > 0 then term.setTextColour(colours.grey)
  112. else term.setTextColour(colours.white) end
  113. term.write("&")
  114. elseif map[y][x] == "o" then
  115. term.setTextColour(colours.grey)
  116. term.write("o")
  117. elseif map[y][x] == "*" then
  118. term.setTextColour(colours.red)
  119. term.write("*")
  120. elseif map[y][x] == "$" then
  121. term.setTextColour(colours.orange)
  122. term.write("$")
  123. elseif map[y][x] == 0 then
  124. term.setBackgroundColour(colours.lime)
  125. term.write(" ")
  126. elseif map[y][x] == 1 then
  127. term.setBackgroundColour(colours.grey)
  128. term.write(" ")
  129. else
  130. term.write(" ")
  131. end
  132. end
  133.  
  134. --Draws some HUD information in the sidebar
  135. local function drawInterface()
  136. term.setTextColour(colours.black)
  137. term.setBackgroundColour(colours.lightGrey)
  138.  
  139. local msg = "Rat Trap"
  140. term.setCursorPos(math.ceil(mapXSize + (w-mapXSize)/2 - #msg/2), 2)
  141. term.write(msg)
  142. msg = "Lives"
  143. term.setCursorPos(math.ceil(mapXSize + (w-mapXSize)/2 - #msg/2), 9)
  144. term.write(msg)
  145. msg = "Score"
  146. term.setCursorPos(math.ceil(mapXSize + (w-mapXSize)/2 - #msg/2), 12)
  147. term.write(msg)
  148. msg = "Spawn Timer"
  149. term.setCursorPos(math.ceil(mapXSize + (w-mapXSize)/2 - #msg/2), 15)
  150. term.write(msg)
  151.  
  152. term.setTextColour(colours.blue)
  153. term.setCursorPos(math.ceil(mapXSize + (w-mapXSize)/2 - #levelName/2), 4)
  154. term.write(levelName)
  155.  
  156.  
  157. term.setTextColour(colours.white)
  158. term.setBackgroundColour(colours.grey)
  159. if lives > 3 then
  160. msg = " & x 3 "
  161. else
  162. msg = " "..string.rep("& ", lives)
  163. end
  164. term.setCursorPos(math.ceil(mapXSize + (w-mapXSize)/2 - #msg/2), 10)
  165. term.write(msg)
  166. msg = " "..string.rep("0", 5 - math.log10(score+1))..score.." "
  167. if score == 0 then msg = " 00000 " end
  168. term.setCursorPos(math.ceil(mapXSize + (w-mapXSize)/2 - #msg/2), 13)
  169. term.write(msg)
  170. local mins = math.floor(spawnTimer / 60)
  171. local secs = spawnTimer % 60
  172. msg = " "
  173. if mins < 10 then msg = msg.."0" end
  174. msg = msg..mins..":"
  175. if secs < 10 then msg = msg.."0" end
  176. msg = msg..secs.." "
  177. term.setCursorPos(math.ceil(mapXSize + (w-mapXSize)/2 - #msg/2), 16)
  178. term.write(msg)
  179. end
  180.  
  181. local function drawEnemy(enemy)
  182. term.setCursorPos(enemy.x, enemy.y)
  183. term.setBackgroundColour(colours.green)
  184. if enemy.trapped then term.setTextColour(colours.red)
  185. else term.setTextColour(colours.orange) end
  186. term.write("&")
  187. end
  188.  
  189. --Draws the entire map for the first time. This is only called once per level
  190. local function drawMap()
  191. term.setBackgroundColour(colours.lightGrey)
  192. term.clear()
  193. for y=1,#map do
  194. for x=1,mapXSize do
  195. updateMap(x,y)
  196. end
  197. end
  198. for _,enemy in pairs(enemies) do drawEnemy(enemy) end
  199. for _,ball in pairs(electricity) do drawBall(ball) end
  200. drawInterface()
  201. end
  202.  
  203. --[[Section: Enemy Logic]]--
  204.  
  205. local function killPlayer()
  206. plX = -1
  207. plY = -1
  208. pltrapped = 0
  209. lives = lives - 1
  210. if lives == 0 then
  211. running = false
  212. else
  213. pldead = 5
  214. end
  215. end
  216.  
  217. --Takes in a XY position and generates a list of legal moves enemies can make.
  218. --Length of 0-8.
  219. local function getLegalMoves(x,y)
  220. local legal = {}
  221. for lx = math.max(1,x-1),math.min(mapXSize,x+1) do
  222. for ly = math.max(1,y-1),math.min(mapYSize,y+1) do
  223. if (lx ~= x or ly ~= y) and map[ly][lx] == nil then
  224. local enemyOccupy = false
  225. for _,enemy in pairs(enemies) do
  226. if enemy.x == lx and enemy.y == ly then
  227. enemyOccupy = true
  228. break
  229. end
  230. end
  231.  
  232. if not enemyOccupy then
  233. table.insert(legal, { x = lx, y = ly } )
  234. end
  235. end
  236. end
  237. end
  238.  
  239. return legal
  240. end
  241.  
  242. --Spawns the player, in a similar fashion to enemies
  243. local function spawnPlayer()
  244. local moveLegal = false
  245. local x,y = 0,0
  246. repeat
  247. x = math.random(1,mapXSize)
  248. y = math.random(1,mapYSize)
  249.  
  250. if map[y][x] == nil then
  251. local mindist = math.huge
  252. for _,enemy in pairs(enemies) do
  253. mindist = math.min(mindist, math.sqrt(math.pow(enemy.x - plX, 2) +
  254. math.pow(enemy.y - plY, 2)))
  255. end
  256. if mindist > 5 then moveLegal = true end
  257. end
  258. until moveLegal
  259.  
  260. plX = x
  261. plY = y
  262. updateMap(x,y)
  263. end
  264.  
  265. --Creates a new enemy and places it in the world
  266. local function generateEnemy()
  267. local moveLegal = false
  268. local x,y = 0,0
  269. repeat
  270. --This approach... isn't good but it's good enough I expect
  271. x = math.random(1,mapXSize)
  272. y = math.random(1,mapYSize)
  273. --The starting location HAS to be empty.
  274. if map[y][x] == nil then
  275. local moves = getLegalMoves(x,y)
  276. local pdist = math.sqrt(math.pow(plX-x,2), math.pow(plY-y,2))
  277.  
  278. --It can't be within 5 squares of the player or be immediately trapped.
  279. if pdist > 5 and #moves > 0 then
  280. moveLegal = true
  281. end
  282. end
  283. until moveLegal
  284.  
  285. --Fortunately enemies are really easy; they're mostly stateless.
  286. local enemy = {
  287. --Enemy position
  288. x = x; y = y;
  289. --Whether or not it's trapped (for drawing). Always starts false.
  290. trapped = false;
  291. }
  292. table.insert(enemies, enemy)
  293. drawEnemy(enemy)
  294. end
  295.  
  296. --Moves the enemy. Each time this is called, unless an enemy is trapped, it MUST move.
  297. --This right now is a naive distance function. Later implementations will need to use
  298. --something more sophistocated.
  299. local function moveEnemy(enemy)
  300. local moves = getLegalMoves(enemy.x, enemy.y)
  301. if #moves == 0 then
  302. --Score 5 points for trapping an enemy fresh
  303. if not enemy.trapped then score = score + 5 end
  304. enemy.trapped = true
  305. drawEnemy(enemy)
  306. return
  307. end
  308. enemy.trapped = false
  309.  
  310. local ind = 0
  311. if pldead > 0 then
  312. ind = math.random(1,#moves)
  313. else
  314. local mindist = math.huge
  315. for index,move in pairs(moves) do
  316. local dist = math.sqrt(math.pow(move.x - plX, 2) + math.pow(move.y - plY, 2))
  317. if dist < mindist then
  318. mindist = dist
  319. ind = index
  320. end
  321. end
  322. end
  323.  
  324. local ox,oy = enemy.x, enemy.y
  325. enemy.x = moves[ind].x
  326. enemy.y = moves[ind].y
  327. if enemy.x == plX and enemy.y == plY then
  328. killPlayer()
  329. end
  330.  
  331. updateMap(ox,oy)
  332. drawEnemy(enemy)
  333. end
  334.  
  335. --[[Section: Movement and Input]]--
  336.  
  337. --When the player pushes a block, what happens to that block depends on whats
  338. --in front of it. This method takes the direction and determines if the column
  339. --moves based on its composition.
  340. local function pushBlock(ix,iy,dirX,dirY)
  341. local ctx,cty = ix,iy
  342. while ctx > 0 and ctx < w and cty > 0 and cty < h do
  343. if map[cty][ctx] == 1 or map[cty][ctx] == "*" then
  344. return false
  345. elseif map[cty][ctx] == nil or map[cty][ctx] == "$" then
  346. for _,enemy in pairs(enemies) do
  347. if enemy.x == ctx and enemy.y == cty then
  348. --You can't squash enemies. Trapped foes act as walls.
  349. if enemy.trapped then
  350. return false
  351. else
  352. moveEnemy(enemy)
  353. end
  354. end
  355. end
  356.  
  357. map[cty][ctx] = 0
  358. map[iy][ix] = nil
  359. updateMap(ctx,cty)
  360. return true
  361. elseif map[cty][ctx] == "o" then
  362. map[iy][ix] = nil
  363. return true
  364. end
  365. ctx = ctx + dirX
  366. cty = cty + dirY
  367. end
  368. return false
  369. end
  370.  
  371. --Handles player movements. They attempt to move to a given square
  372. local function movePlayer(x,y)
  373. if pltrapped > 0 or pldead > 0 then return end
  374.  
  375. local ox,oy = plX, plY
  376. local ptile = map[y][x]
  377. if ptile == "o" then
  378. map[y][x] = nil
  379. pltrapped = 10
  380. plX = x
  381. plY = y
  382. updateMap(ox, oy)
  383. updateMap(plX, plY)
  384. elseif ptile == "$" then
  385. map[y][x] = nil
  386. --20 points for getting a corpse
  387. score = score + 20
  388. plX = x
  389. plY = y
  390. updateMap(ox,oy)
  391. updateMap(plX, plY)
  392. elseif ptile == nil then
  393. plX = x
  394. plY = y
  395. updateMap(ox, oy)
  396. for _,enemy in pairs(enemies) do
  397. if enemy.x == plX and enemy.y == plY then killPlayer() end
  398. end
  399. for _,ball in pairs(electricity) do
  400. if ball.x == plX and ball.y == plY then killPlayer() end
  401. end
  402. updateMap(plX, plY)
  403. else
  404. local dirX, dirY = x - plX, y - plY
  405. if pushBlock(x,y,dirX,dirY) then
  406. plX = x
  407. plY = y
  408. updateMap(ox, oy)
  409. updateMap(plX, plY)
  410. end
  411. end
  412.  
  413. drawInterface()
  414. end
  415.  
  416. local function handleEvents()
  417. local id,p1,p2,p3 = os.pullEvent()
  418.  
  419. if id == "key" then
  420. if p1 == numPad1 then
  421. movePlayer(plX-1, plY+1)
  422. elseif p1 == numPad2 or p1 == keys.down then
  423. movePlayer(plX, plY+1)
  424. elseif p1 == numPad3 then
  425. movePlayer(plX+1, plY+1)
  426. elseif p1 == numPad4 or p1 == keys.left then
  427. movePlayer(plX-1, plY)
  428. elseif p1 == numPad6 or p1 == keys.right then
  429. movePlayer(plX+1, plY)
  430. elseif p1 == numPad7 then
  431. movePlayer(plX-1, plY-1)
  432. elseif p1 == numPad8 or p1 == keys.up then
  433. movePlayer(plX, plY-1)
  434. elseif p1 == numPad9 then
  435. movePlayer(plX+1, plY-1)
  436. elseif p1 == keys.enter then
  437. running = false
  438. end
  439. elseif id == "timer" then
  440. --Right now it's just the one. This'll change with electricity balls.
  441. local allEnemiesTrapped = true
  442. for _,enemy in pairs(enemies) do
  443. moveEnemy(enemy)
  444. if not enemy.trapped then allEnemiesTrapped = false end
  445. end
  446.  
  447. if allEnemiesTrapped then
  448. spawnTimer = spawnTimer + spawnInterval
  449. for _,enemy in pairs(enemies) do
  450. map[enemy.y][enemy.x] = "$"
  451. updateMap(enemy.x, enemy.y)
  452. end
  453. enemies = {}
  454. for i=1,levelEnemies do generateEnemy() end
  455. end
  456.  
  457. if spawnTimer == 0 then
  458. spawnTimer = spawnInterval
  459. for i=1,levelEnemies do generateEnemy() end
  460. else spawnTimer = spawnTimer - 1 end
  461.  
  462. if pltrapped > 0 then
  463. pltrapped = pltrapped - 1
  464. if pltrapped == 0 then updateMap(plX, plY) end
  465. end
  466. if pldead > 0 then
  467. pldead = pldead - 1
  468. if pldead == 0 then
  469. spawnPlayer()
  470. end
  471. end
  472.  
  473. enemyTimer = os.startTimer(enemyInterval)
  474. drawInterface()
  475. end
  476. end
  477.  
  478. local fpath = shell.resolve(".").."/levels/"..levelName
  479. if not loadMap(fpath) then error("Couldn't load map!") end
  480. drawMap()
  481.  
  482. enemyTimer = os.startTimer(enemyInterval)
  483. while running do handleEvents() end
  484. term.setBackgroundColour(colours.black)
  485. shell.run("clear")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement