crawl.lua 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. function floorRef(currentFloor, type, row, col)
  2. if (type == "mob") then
  3. local mobTable = currentFloor["mobTable"]
  4. if (mobTable[row] == nil or mobTable[row][col] == nil) then
  5. return nil
  6. else
  7. return mobTable[row][col]
  8. end
  9. else
  10. local tileTable = currentFloor["tileTable"]
  11. if (tileTable[row] == nil or tileTable[row][col] == nil) then
  12. return nil
  13. else
  14. return tileTable[row][col]
  15. end
  16. end
  17. end
  18. function floorSet(currentFloor, type, row, col, newVal)
  19. if (type == "mob") then
  20. local mobTable = currentFloor["mobTable"]
  21. if (mobTable[row] == nil) then
  22. mobTable[row] = {}
  23. end
  24. mobTable[row][col] = newVal
  25. else
  26. local tileTable = currentFloor["tileTable"]
  27. if (tileTable[row] == nil) then
  28. tileTable[row] = {}
  29. end
  30. tileTable[row][col] = newVal
  31. end
  32. end
  33. function calculatePCStats(level, race, class)
  34. local statNames = {"maxHP", "maxMP", "melAtk", "rngAtk", "def", "splAtk", "splSup"}
  35. local result = {}
  36. for i, stat in pairs(statNames) do
  37. local levelFactor = level + 4
  38. local subtotalStat = levelFactor * raceStatFactors[race][stat] * classStatFactors[class][stat]
  39. local totalStat = math.min(100, math.floor(subtotalStat))
  40. result[stat] = totalStat
  41. end
  42. return result
  43. end
  44. function randInt(min, max)
  45. local scale = max - min + 1
  46. return math.floor(math.random() * scale) + min
  47. end
  48. function pickRandomTile(room)
  49. return randInt(room[1][1], room[2][1]), randInt(room[1][2], room[2][2])
  50. end
  51. function setSpecialRoom(c, stopAt, specialRooms, value)
  52. local j = 0
  53. for i=1,stopAt do
  54. if (specialRooms[i] == nil) then
  55. j = j + 1
  56. end
  57. if (j == c) then
  58. specialRooms[i] = { id = value }
  59. return i
  60. end
  61. end
  62. end
  63. function coordinatesWithinBoundary(xCheck, yCheck, x1, y1, x2, y2)
  64. return xCheck >= math.min(x1, x2)
  65. and xCheck <= math.max(x1, x2)
  66. and yCheck >= math.min(y1, y2)
  67. and yCheck <= math.max(y1, y2)
  68. end
  69. function coordinatesWithinRoom(xCheck, yCheck, room)
  70. for i, _ in pairs(room) do
  71. if (room[i+1] ~= nil) then
  72. if (coordinatesWithinBoundary(xCheck, yCheck, room[i][1], room[i][2], room[i+1][1], room[i+1][2])) then
  73. return true
  74. end
  75. end
  76. end
  77. return false
  78. end
  79. function calculateCenter(room)
  80. return math.floor((room[1][1] + room[2][1]) / 2.0), math.floor((room[1][2] + room[2][2]) / 2.0)
  81. end
  82. function calculateRoomDistance(room1, room2)
  83. local x1, y1 = calculateCenter(room1)
  84. local x2, y2 = calculateCenter(room2)
  85. local base = x2 - x1
  86. local height = y2 - y1
  87. return math.sqrt(base * base + height * height)
  88. end
  89. function findClosestRoom(room, listOfExistingRooms, skipRooms)
  90. local bestRoomIndex, bestRoomDistance
  91. bestRoomDistance = 10000000
  92. for i, checkRoom in pairs(listOfExistingRooms) do
  93. local distance = calculateRoomDistance(room, checkRoom)
  94. if (distance < bestRoomDistance and skipRooms[i] ~= nil) then
  95. bestRoomDistance = distance
  96. bestRoomIndex = i
  97. end
  98. end
  99. return bestRoomIndex
  100. end
  101. function calculateHallwayCoordinates(a1, b1, a4, b4, diffA)
  102. a2 = a1 + math.floor(diffA / 2)
  103. b2 = b1
  104. a3 = a2
  105. b3 = b4
  106. return a1, b1, a2, b2, a3, b3, a4, b4
  107. end
  108. function generateHallway(room1, room2)
  109. local room1CenterX, room1CenterY = calculateCenter(room1)
  110. local room2CenterX, room2CenterY = calculateCenter(room2)
  111. local xDiff = room2CenterX - room1CenterX
  112. local yDiff = room2CenterY - room1CenterY
  113. local isVertical = math.abs(xDiff) <= math.abs(xDiff)
  114. local x1, y1, x2, y2, x3, y3, x4, y4
  115. if (isVertical) then
  116. y1, x1, y2, x2, y3, x3, y4, x4 = calculateHallwayCoordinates(room1CenterY, room1CenterX, room2CenterY, room2CenterX, yDiff)
  117. else
  118. x1, y1, x2, y2, x3, y3, x4, y4 = calculateHallwayCoordinates(room1CenterX, room1CenterY, room2CenterX, room2CenterY, xDiff)
  119. end
  120. return { {x1, y1}, {x2, y2}, {x3, y3}, {x4, y4} }
  121. end
  122. function generateRoom(roomRange, listOfExistingRooms, minWidth, minHeight, maxWidth, maxHeight, xPadding, yPadding)
  123. local width = randInt(minWidth, maxWidth)
  124. local height = randInt(minHeight, maxHeight)
  125. local startX = randInt(roomRange[1], roomRange[3] - width)
  126. local startY = randInt(roomRange[2], roomRange[4] - height)
  127. for i, existingRoom in pairs(listOfExistingRooms) do
  128. local compareX = startX - existingRoom[2][1] + xPadding
  129. local compareY = startY - existingRoom[2][2] + yPadding
  130. if (compareX < 0 and compareY < 0) then
  131. if (compareX < compareY) then
  132. startX = existingRoom[2][1] + xPadding
  133. else
  134. startY = existingRoom[2][2] + yPadding
  135. end
  136. end
  137. end
  138. return {{startX, startY}, {startX + width, startY + height}}
  139. end
  140. function generateTown(worldSeed)
  141. local townGenSeed = worldSeed + 20152314
  142. math.randomseed(townGenSeed)
  143. local result = {}
  144. for i=1,9 do
  145. result[i] = {}
  146. for j=1,9 do
  147. if (i == 1 or i == 9 or j == 1 or j == 9) then
  148. result[i][j] = 1
  149. elseif (i == 5 and j == 5) then
  150. result[i][j] = 11
  151. else
  152. result[i][j] = 0
  153. end
  154. end
  155. end
  156. return result
  157. end
  158. function generateFloor(worldSeed, floorNum)
  159. local floorGenSeed = worldSeed + 612151518 + floorNum
  160. math.randomseed(floorGenSeed)
  161. local xRoomSpacing = 4
  162. local yRoomSpacing = 4
  163. local xFloorPadding = 2
  164. local yFloorPadding = 2
  165. local roomWidth = 10
  166. local roomHeight = 10
  167. local innerRoomWidthMin = 3
  168. local innerRoomHeightMin = 3
  169. local innerRoomWidthMax = 8
  170. local innerRoomHeightMax = 8
  171. local innerRoomPaddingX = 2
  172. local innerRoomPaddingY = 2
  173. local roomRows = 4
  174. local roomCols = 4
  175. local roomRanges = {}
  176. for i=1,roomRows do
  177. roomRanges[i] = {}
  178. for j=1,roomCols do
  179. local x1 = (roomWidth + xRoomSpacing) * (j - 1)
  180. local y1 = (roomHeight + yRoomSpacing) * (i - 1)
  181. local x2 = x1 + roomWidth
  182. local y2 = y1 + roomHeight
  183. roomRanges[i][j] = {x1, y1, x2, y2}
  184. end
  185. end
  186. local listOfExistingRooms = {}
  187. local floorWidth = 0
  188. local floorHeight = 0
  189. for i=1,roomRows do
  190. for j=1,roomCols do
  191. local newRoom = generateRoom(roomRanges[i][j], listOfExistingRooms, innerRoomWidthMin, innerRoomHeightMin, innerRoomWidthMax, innerRoomHeightMax, innerRoomPaddingX, innerRoomPaddingY)
  192. table.insert(listOfExistingRooms, newRoom)
  193. floorWidth = math.max(floorWidth, newRoom[2][1])
  194. floorHeight = math.max(floorHeight, newRoom[2][2])
  195. end
  196. end
  197. floorWidth = floorWidth + xFloorPadding * 2
  198. floorHeight = floorHeight + yFloorPadding * 2
  199. local skipRooms = {}
  200. skipRooms[1] = true
  201. local hallways = {}
  202. for i=2,roomRows*roomCols do
  203. local j = findClosestRoom(listOfExistingRooms[i], listOfExistingRooms, skipRooms)
  204. table.insert(hallways, generateHallway(listOfExistingRooms[i], listOfExistingRooms[j]))
  205. skipRooms[i] = true
  206. end
  207. local specialRooms = {}
  208. local specialRoomCount = 0
  209. local result = {}
  210. if (floorNum < 100) then
  211. local c = randInt(1, roomRows*roomCols - specialRoomCount)
  212. local i = setSpecialRoom(c, roomRows*roomCols, specialRooms, 11)
  213. local x, y = pickRandomTile(listOfExistingRooms[i])
  214. specialRooms[i]["x"] = x
  215. specialRooms[i]["y"] = y
  216. specialRoomCount = specialRoomCount + 1
  217. end
  218. if (floorNum > 0) then
  219. local c = randInt(1, roomRows*roomCols - specialRoomCount)
  220. local i = setSpecialRoom(c, roomRows*roomCols, specialRooms, 12)
  221. local x, y = pickRandomTile(listOfExistingRooms[i])
  222. specialRooms[i]["x"] = x
  223. specialRooms[i]["y"] = y
  224. specialRoomCount = specialRoomCount + 1
  225. end
  226. if (floorNum % 5 == 1) then
  227. local c = randInt(1, roomRows*roomCols - specialRoomCount)
  228. local i = setSpecialRoom(c, roomRows*roomCols, specialRooms, 13)
  229. local x, y = pickRandomTile(listOfExistingRooms[i])
  230. specialRooms[i]["x"] = x
  231. specialRooms[i]["y"] = y
  232. specialRoomCount = specialRoomCount + 1
  233. end
  234. if (floorNum % 5 == 0) then
  235. local c = randInt(1, roomRows*roomCols - specialRoomCount)
  236. local i = setSpecialRoom(c, roomRows*roomCols, specialRooms, 14)
  237. local x, y = pickRandomTile(listOfExistingRooms[i])
  238. specialRooms[i]["x"] = x
  239. specialRooms[i]["y"] = y
  240. specialRoomCount = specialRoomCount + 1
  241. end
  242. local specialTiles = {}
  243. for i, room in pairs(specialRooms) do
  244. if (specialTiles[room["x"]] == nil) then
  245. specialTiles[room["x"]] = {}
  246. end
  247. specialTiles[room["x"]][room["y"]] = room["id"]
  248. end
  249. for i=1,floorHeight do
  250. result[i] = {}
  251. for j=1,floorWidth do
  252. local insideRoom = false
  253. if (specialTiles[j - xFloorPadding] ~= nil and specialTiles[j - xFloorPadding][i - yFloorPadding] ~= nil) then
  254. result[i][j] = specialTiles[j - xFloorPadding][i - yFloorPadding]
  255. else
  256. for _, room in pairs(listOfExistingRooms) do
  257. if (coordinatesWithinRoom(j - xFloorPadding, i - yFloorPadding, room)) then
  258. insideRoom = true
  259. break
  260. end
  261. end
  262. if (not insideRoom) then
  263. for _, hallway in pairs(hallways) do
  264. if (coordinatesWithinRoom(j - xFloorPadding, i - yFloorPadding, hallway)) then
  265. insideRoom = true
  266. break
  267. end
  268. end
  269. end
  270. if (insideRoom) then
  271. result[i][j] = 0
  272. else
  273. result[i][j] = 1
  274. end
  275. end
  276. end
  277. end
  278. return result
  279. end
  280. function getSpecialTile(type, floor)
  281. local codeList = {stairsUp = 11, stairsDown = 12, save = 13, boss = 14}
  282. local code = codeList[type]
  283. for i, row in pairs(floor["tileTable"]) do
  284. for j, cell in pairs(row) do
  285. if (cell == code) then
  286. return i, j
  287. end
  288. end
  289. end
  290. end
  291. moveableMobValues = {}
  292. moveableMobValues[0] = true
  293. moveableTileValues = {}
  294. moveableTileValues[0] = true
  295. moveableTileValues[11] = true
  296. moveableTileValues[12] = true
  297. moveableTileValues[13] = true
  298. function changeFloors(globalState, changeType, targetFloor)
  299. local player = globalState["currentFloor"]["player"]
  300. local tileTable
  301. if (targetFloor == 0) then
  302. tileTable = generateTown(globalState["worldSeed"])
  303. else
  304. tileTable = generateFloor(globalState["worldSeed"], targetFloor)
  305. end
  306. local mobTable = {}
  307. local newFloor = {
  308. mobTable = mobTable,
  309. tileTable = tileTable,
  310. player = player,
  311. id = targetFloor
  312. }
  313. local playerRow, playerCol
  314. if (changeType == "up") then
  315. playerRow, playerCol = getSpecialTile("stairsDown", newFloor)
  316. elseif (changeType == "down") then
  317. playerRow, playerCol = getSpecialTile("stairsUp", newFloor)
  318. else
  319. playerRow, playerCol = getSpecialTile("save", newFloor)
  320. end
  321. player["row"] = playerRow
  322. player["col"] = playerCol
  323. floorSet(newFloor, "mob", player["row"], player["col"], 1)
  324. globalState["currentFloor"] = newFloor
  325. end
  326. function attemptToMove(globalState, direction)
  327. local currentFloor = globalState["currentFloor"]
  328. local mobTable = currentFloor["mobTable"]
  329. local tileTable = currentFloor["tileTable"]
  330. local player = currentFloor["player"]
  331. local playerRow, playerCol = player["row"], player["col"]
  332. local targetRow, targetCol
  333. if (direction == "up") then
  334. targetRow = playerRow - 1
  335. targetCol = playerCol
  336. elseif (direction == "down") then
  337. targetRow = playerRow + 1
  338. targetCol = playerCol
  339. elseif (direction == "left") then
  340. targetRow = playerRow
  341. targetCol = playerCol - 1
  342. else
  343. targetRow = playerRow
  344. targetCol = playerCol + 1
  345. end
  346. local targetMob = floorRef(currentFloor, "mob", targetRow, targetCol)
  347. local targetTile = floorRef(currentFloor, "tile", targetRow, targetCol)
  348. if ((targetMob == nil or moveableMobValues[targetMob]) and moveableTileValues[targetTile]) then
  349. executeRound(globalState, "move", targetRow, targetCol)
  350. elseif (targetMob ~= nil and targetMob > 0 and moveableTileValues[targetTile]) then
  351. executeRound(globalState, "attack", targetRow, targetCol)
  352. end
  353. end
  354. function executeTurn(globalState, mob, actionType, targetRow, targetCol)
  355. local currentFloor = globalState["currentFloor"]
  356. local mobTable = currentFloor["mobTable"]
  357. local mobRow = mob["row"]
  358. local mobCol = mob["col"]
  359. local id = mob["id"]
  360. if (actionType == "move") then
  361. floorSet(currentFloor, "mob", mobRow, mobCol, 0)
  362. floorSet(currentFloor, "mob", targetRow, targetCol, id)
  363. mob["row"] = targetRow
  364. mob["col"] = targetCol
  365. end
  366. end
  367. function executeRound(globalState, actionType, targetRow, targetCol)
  368. local currentFloor = globalState["currentFloor"]
  369. local mobTable = currentFloor["mobTable"]
  370. executeTurn(globalState, currentFloor["player"], actionType, targetRow, targetCol)
  371. for i, row in pairs(mobTable) do
  372. for j, cell in pairs(row) do
  373. if (cell > 1) then
  374. local mobLogic, targetRow, targetCol = getMobLogic(mob)
  375. executeTurn(globalState, cell, mobLogic, targetRow, targetCol)
  376. end
  377. end
  378. end
  379. if (actionType == "move") then
  380. local newTile = floorRef(currentFloor, "tile", targetRow, targetCol)
  381. if (newTile == 11) then
  382. changeFloors(globalState, "up", currentFloor["id"] + 1)
  383. elseif (newTile == 12) then
  384. changeFloors(globalState, "down", currentFloor["id"] - 1)
  385. end
  386. end
  387. end
  388. raceStatFactors = {
  389. human = { maxHP = 1.05, maxMP = 1.05, melAtk = 1.05, rngAtk = 1.05, def = 1.05, splAtk = 1.05, splSup = 1.05 }
  390. }
  391. classStatFactors = {
  392. warrior = { maxHP = 1.5, maxMP = 0.0, melAtk = 1.5, rngAtk = 1.25, def = 1.5, splAtk = 0.0, splSup = 0.0 }
  393. }
  394. -- below will be loaded from save
  395. myClass = "warrior"
  396. mySubclass = "knight"
  397. myRace = "human"
  398. mySubrace = "plains"
  399. myLevel = 5
  400. myExperience = 12
  401. myBossLevelCode = 0
  402. mySaveFloorCode = 1
  403. myWorldSeed = 12345
  404. -- above will be loaded from save
  405. myStats = calculatePCStats(myLevel, myRace, myClass)
  406. myStats["currentHP"] = myStats["maxHP"]
  407. myStats["currentMP"] = myStats["maxMP"]
  408. myPlayer = {
  409. stats = myStats,
  410. id = 1,
  411. row = 19,
  412. col = 19
  413. }
  414. myCurrentFloor = {
  415. player = myPlayer,
  416. id = 2
  417. }
  418. globalState = {
  419. worldSeed = myWorldSeed,
  420. currentFloor = myCurrentFloor
  421. }
  422. changeFloors(globalState, "up", 1)