2
0

GameEngine.Mod 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. MODULE GameEngine;
  2. IMPORT G := Graph, Random, Out, Strings, Files;
  3. CONST
  4. maxMapW* = 128;
  5. maxMapH* = maxMapW;
  6. cellW* = 16;
  7. cellH* = cellW;
  8. tilesInRow* = 8;
  9. (** Direction set elements **)
  10. up = 0;
  11. right = 1;
  12. down = 2;
  13. left = 3;
  14. upRight = 4;
  15. upLeft = 5;
  16. downRight = 6;
  17. downLeft = 7;
  18. (** Sets of directions **)
  19. allCorners = {upRight, upLeft, downRight, downLeft};
  20. allSides = {up, right, down, left};
  21. TYPE
  22. Cell* = RECORD (** A single cell of the map *)
  23. kind*: INTEGER; (** What is saved in a file *)
  24. tile*: INTEGER; (** What is displayed on screen *)
  25. corners*: SET; (** Set of upRight, upLeft, downRight, downLeft *)
  26. bush: SET (** Set of up, right, down, left *)
  27. END;
  28. Map* = RECORD
  29. w*, h*: INTEGER;
  30. cells*: ARRAY maxMapH, maxMapW OF Cell
  31. END;
  32. Game* = RECORD
  33. map*: Map
  34. END;
  35. VAR
  36. tiles*: G.Bitmap;
  37. PROCEDURE DrawCell*(cell: Cell; x, y, toX, toY: INTEGER);
  38. VAR kx, ky, k: INTEGER;
  39. PROCEDURE DrawCorner(tile, offX, offY, toX, toY: INTEGER);
  40. BEGIN G.DrawPart(tiles,
  41. tile MOD tilesInRow * cellW + offX,
  42. tile DIV tilesInRow * cellH + offY,
  43. cellW DIV 2, cellH DIV 2, toX + offX, toY + offY)
  44. END DrawCorner;
  45. BEGIN
  46. kx := cell.tile MOD tilesInRow * cellW;
  47. ky := cell.tile DIV tilesInRow * cellH;
  48. G.DrawPart(tiles, kx, ky, cellW, cellH, toX, toY);
  49. IF cell.corners * allCorners # {} THEN
  50. IF ({up, left, down, right, upRight, downLeft} - cell.corners = {}) &
  51. ~(upLeft IN cell.corners)
  52. THEN
  53. DrawCorner(cell.kind + tilesInRow * 3 + 4, 0, 0, toX, toY)
  54. ELSIF upLeft IN cell.corners THEN
  55. DrawCorner(cell.kind + tilesInRow + 5, 0, 0, toX, toY)
  56. ELSIF ({upRight, up, left} - cell.corners = {}) & ~(down IN cell.corners) OR
  57. ({downLeft, left, up} - cell.corners = {}) & ~(right IN cell.corners) THEN
  58. DrawCorner(cell.kind + tilesInRow * 3 + 4, 0, 0, toX, toY)
  59. END;
  60. IF ({up, left, down, right, upLeft, downRight} - cell.corners = {}) &
  61. ~(upRight IN cell.corners)
  62. THEN
  63. DrawCorner(cell.kind + tilesInRow * 3 + 4, cellW DIV 2, 0, toX, toY)
  64. ELSIF upRight IN cell.corners THEN
  65. DrawCorner(cell.kind + tilesInRow + 4, cellW DIV 2, 0, toX, toY)
  66. ELSIF ({upLeft, up, right} - cell.corners = {}) & ~(down IN cell.corners) OR
  67. ({downRight, right, up} - cell.corners = {}) & ~(left IN cell.corners) THEN
  68. DrawCorner(cell.kind + tilesInRow * 3 + 4, cellW DIV 2, 0, toX, toY)
  69. END;
  70. IF ({up, left, down, right, upLeft, downRight} - cell.corners = {}) &
  71. ~(downLeft IN cell.corners)
  72. THEN
  73. DrawCorner(cell.kind + tilesInRow * 3 + 4, 0, cellH DIV 2, toX, toY)
  74. ELSIF downLeft IN cell.corners THEN
  75. DrawCorner(cell.kind + 5, 0, cellH DIV 2, toX, toY)
  76. ELSIF ({downRight, down, left} - cell.corners = {}) & ~(up IN cell.corners) OR
  77. ({upLeft, left, down} - cell.corners = {}) & ~(right IN cell.corners) THEN
  78. DrawCorner(cell.kind + tilesInRow * 3 + 4, 0, cellH DIV 2, toX, toY)
  79. END;
  80. IF ({up, left, down, right, upRight, downLeft} - cell.corners = {}) &
  81. ~(downRight IN cell.corners)
  82. THEN
  83. DrawCorner(cell.kind + tilesInRow * 3 + 4, cellW DIV 2, cellH DIV 2, toX, toY)
  84. ELSIF downRight IN cell.corners THEN
  85. DrawCorner(cell.kind + 4, cellW DIV 2, cellH DIV 2, toX, toY)
  86. ELSIF ({downLeft, down, right} - cell.corners = {}) & ~(up IN cell.corners) OR
  87. ({upRight, right, down} - cell.corners = {}) & ~(left IN cell.corners) THEN
  88. DrawCorner(cell.kind + tilesInRow * 3 + 4, cellW DIV 2, cellH DIV 2, toX, toY)
  89. END
  90. END;
  91. IF cell.bush # {} THEN
  92. k := ORD(cell.bush) + 16;
  93. kx := k MOD tilesInRow * cellW;
  94. ky := k DIV tilesInRow * cellH;
  95. G.DrawPart(tiles, kx, ky, cellW, cellH, toX, toY)
  96. END
  97. END DrawCell;
  98. PROCEDURE CheckNeighbours(VAR map: Map; x, y, kind: INTEGER): SET;
  99. VAR s: SET;
  100. PROCEDURE P(VAR map: Map; x, y, dir: INTEGER; VAR s: SET);
  101. BEGIN
  102. IF (0 <= x) & (x < map.w) & (0 <= y) & (y < map.h) &
  103. ((map.cells[y, x].kind = kind) OR
  104. (kind = 64) & (map.cells[y, x].kind = 96))
  105. THEN INCL(s, dir)
  106. END
  107. END P;
  108. BEGIN
  109. s := {};
  110. P(map, x , y - 1, up , s);
  111. P(map, x + 1, y , right , s);
  112. P(map, x , y + 1, down , s);
  113. P(map, x - 1, y , left , s);
  114. P(map, x - 1, y - 1, upLeft , s);
  115. P(map, x + 1, y - 1, upRight , s);
  116. P(map, x - 1, y + 1, downLeft , s);
  117. P(map, x + 1, y + 1, downRight, s)
  118. RETURN s END CheckNeighbours;
  119. PROCEDURE UpdateMapTile*(VAR map: Map; x, y: INTEGER);
  120. VAR kind, tile, xx, yy: INTEGER;
  121. dirs: SET; (* Set of directions of neighbours where kind is the same *)
  122. corners: SET;
  123. BEGIN
  124. kind := map.cells[y, x].kind;
  125. tile := kind;
  126. corners := {};
  127. IF (kind >= 32) & (kind MOD 32 = 0) THEN
  128. dirs := CheckNeighbours(map, x, y, kind);
  129. xx := 3; yy := 3;
  130. IF up IN dirs THEN
  131. IF {left, right, down} - dirs = {} THEN
  132. IF ~(upLeft IN dirs) & ~(downLeft IN dirs) THEN xx := 0; yy := 1
  133. ELSIF ~(upLeft IN dirs) & ~(upRight IN dirs) THEN xx := 1; yy := 0
  134. ELSIF ~(upRight IN dirs) & ~(downRight IN dirs) THEN xx := 2; yy := 1
  135. ELSIF ~(downLeft IN dirs) & ~(downRight IN dirs) THEN xx := 1; yy := 2
  136. ELSIF ~(upLeft IN dirs) THEN xx := 5; yy := 1
  137. ELSIF ~(upRight IN dirs) THEN xx := 4; yy := 1
  138. ELSIF ~(downLeft IN dirs) THEN xx := 5; yy := 0
  139. ELSIF ~(downRight IN dirs) THEN xx := 4; yy := 0
  140. ELSE xx := 1; yy := 1
  141. END
  142. ELSIF {left , right} - dirs = {} THEN xx := 1; yy := 2
  143. ELSIF {left , down } - dirs = {} THEN xx := 2; yy := 1
  144. ELSIF {right, down } - dirs = {} THEN xx := 0; yy := 1
  145. ELSIF left IN dirs THEN xx := 2; yy := 2
  146. ELSIF right IN dirs THEN xx := 0; yy := 2
  147. ELSIF down IN dirs THEN xx := 3; yy := 1
  148. ELSE xx := 3; yy := 2
  149. END
  150. ELSIF down IN dirs THEN
  151. IF {left, right} - dirs = {} THEN xx := 1; yy := 0
  152. ELSIF left IN dirs THEN xx := 2; yy := 0
  153. ELSIF right IN dirs THEN xx := 0; yy := 0
  154. ELSE xx := 3; yy := 0
  155. END
  156. ELSIF left IN dirs THEN
  157. IF right IN dirs THEN xx := 1; yy := 3
  158. ELSE xx := 2; yy := 3
  159. END
  160. ELSIF right IN dirs THEN xx := 0; yy := 3
  161. END;
  162. tile := kind + xx + yy * tilesInRow;
  163. IF {up, left, upLeft} - dirs = {upLeft} THEN INCL(corners, upLeft) END;
  164. IF {up, right, upRight} - dirs = {upRight} THEN INCL(corners, upRight) END;
  165. IF {down, left, downLeft} - dirs = {downLeft} THEN INCL(corners, downLeft) END;
  166. IF {down, right, downRight} - dirs = {downRight} THEN INCL(corners, downRight) END;
  167. corners := corners + dirs * allSides
  168. END;
  169. map.cells[y, x].tile := tile;
  170. map.cells[y, x].corners := corners;
  171. IF kind # 32 THEN
  172. map.cells[y, x].bush := CheckNeighbours(map, x, y, 32) * allSides
  173. ELSE
  174. map.cells[y, x].bush := {}
  175. END
  176. END UpdateMapTile;
  177. PROCEDURE UpdateMapTiles*(VAR map: Map);
  178. VAR x, y: INTEGER;
  179. BEGIN
  180. FOR y := 0 TO map.h - 1 DO
  181. FOR x := 0 TO map.w - 1 DO
  182. UpdateMapTile(map, x, y)
  183. END
  184. END
  185. END UpdateMapTiles;
  186. PROCEDURE UpdateMapTileAround*(VAR map: Map; x, y: INTEGER);
  187. BEGIN
  188. UpdateMapTile(map, x, y);
  189. IF x > 0 THEN
  190. UpdateMapTile(map, x - 1, y);
  191. IF y > 0 THEN UpdateMapTile(map, x - 1, y - 1) END;
  192. IF y < map.h - 1 THEN UpdateMapTile(map, x - 1, y + 1) END
  193. END;
  194. IF x < map.w - 1 THEN
  195. UpdateMapTile(map, x + 1, y);
  196. IF y > 0 THEN UpdateMapTile(map, x + 1, y - 1) END;
  197. IF y < map.h - 1 THEN UpdateMapTile(map, x + 1, y + 1) END
  198. END;
  199. IF y > 0 THEN UpdateMapTile(map, x, y - 1) END;
  200. IF y < map.h - 1 THEN UpdateMapTile(map, x, y + 1) END
  201. END UpdateMapTileAround;
  202. PROCEDURE SetCell*(VAR map: Map; x, y, kind: INTEGER);
  203. PROCEDURE P(VAR cell: Cell; kind: INTEGER);
  204. BEGIN
  205. IF cell.kind # kind THEN
  206. cell.kind := kind;
  207. UpdateMapTileAround(map, x, y)
  208. END
  209. END P;
  210. BEGIN
  211. P(map.cells[y, x], kind)
  212. END SetCell;
  213. PROCEDURE MakeRandomMap*(VAR map: Map; w, h: INTEGER);
  214. VAR x, y: INTEGER;
  215. BEGIN
  216. map.w := w; map.h := h;
  217. FOR y := 0 TO h - 1 DO
  218. FOR x := 0 TO w - 1 DO
  219. map.cells[y, x].kind := 0 (*Random.Int(4)*)
  220. END
  221. END;
  222. UpdateMapTiles(map)
  223. END MakeRandomMap;
  224. PROCEDURE ReadCell(VAR r: Files.Rider; VAR cell: Cell);
  225. BEGIN
  226. Files.ReadInt(r, cell.kind)
  227. END ReadCell;
  228. PROCEDURE ReadMap(VAR r: Files.Rider; VAR map: Map): BOOLEAN;
  229. VAR x, y: INTEGER;
  230. BEGIN
  231. Files.ReadInt(r, map.w);
  232. Files.ReadInt(r, map.h);
  233. FOR y := 0 TO map.h - 1 DO
  234. FOR x := 0 TO map.w - 1 DO
  235. ReadCell(r, map.cells[y, x])
  236. END
  237. END;
  238. UpdateMapTiles(map)
  239. RETURN TRUE END ReadMap;
  240. PROCEDURE WriteCell(VAR r: Files.Rider; cell: Cell);
  241. BEGIN
  242. Files.WriteInt(r, cell.kind)
  243. END WriteCell;
  244. PROCEDURE WriteMap(VAR r: Files.Rider; map: Map);
  245. VAR x, y: INTEGER;
  246. BEGIN
  247. Files.WriteInt(r, map.w);
  248. Files.WriteInt(r, map.h);
  249. FOR y := 0 TO map.h - 1 DO
  250. FOR x := 0 TO map.w - 1 DO
  251. WriteCell(r, map.cells[y, x])
  252. END
  253. END
  254. END WriteMap;
  255. PROCEDURE LoadGame*(VAR game: Game; fname: ARRAY OF CHAR): BOOLEAN;
  256. VAR F: Files.File;
  257. r: Files.Rider;
  258. ok: BOOLEAN;
  259. BEGIN
  260. ok := FALSE;
  261. F := Files.Old(fname);
  262. IF F # NIL THEN
  263. Files.Set(r, F, 0);
  264. ok := ReadMap(r, game.map);
  265. Files.Close(F)
  266. END
  267. RETURN ok END LoadGame;
  268. PROCEDURE SaveGame*(VAR game: Game; fname: ARRAY OF CHAR);
  269. VAR F: Files.File;
  270. r: Files.Rider;
  271. BEGIN
  272. F := Files.New(fname);
  273. IF F # NIL THEN
  274. Files.Set(r, F, 0);
  275. WriteMap(r, game.map);
  276. Files.Register(F);
  277. Files.Close(F)
  278. END
  279. END SaveGame;
  280. (** Returns a bitmap with the given name (appends strings to make a file name).
  281. On error sets ok to FALSE and ouputs an error message.
  282. Never sets ok to TRUE. *)
  283. PROCEDURE LoadBitmap(name: ARRAY OF CHAR; VAR ok: BOOLEAN): G.Bitmap;
  284. VAR bmp: G.Bitmap;
  285. s: ARRAY 256 OF CHAR;
  286. key: G.Color;
  287. BEGIN
  288. s := 'Data/Graph/';
  289. Strings.Append(name, s);
  290. Strings.Append('.png', s);
  291. bmp := G.LoadBitmap(s);
  292. IF bmp = NIL THEN
  293. ok := FALSE;
  294. Out.String('Error: Could not load bitmap "');
  295. Out.String(s); Out.String('".'); Out.Ln
  296. ELSE
  297. G.MakeCol(key, 255, 0, 128);
  298. G.ApplyMaskColor(bmp, key)
  299. END
  300. RETURN bmp END LoadBitmap;
  301. PROCEDURE InitGame*(VAR game: Game);
  302. BEGIN
  303. MakeRandomMap(game.map, 128, 128)
  304. END InitGame;
  305. PROCEDURE Init*(): BOOLEAN;
  306. VAR ok: BOOLEAN;
  307. BEGIN ok := TRUE;
  308. tiles := LoadBitmap('tiles', ok)
  309. RETURN ok END Init;
  310. END GameEngine.