GameEngine.Mod 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  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;
  99. forBush: BOOLEAN): SET;
  100. VAR s: SET;
  101. PROCEDURE P(VAR map: Map; kind, x, y, dir: INTEGER;
  102. forBush: BOOLEAN; VAR s: SET);
  103. VAR k: INTEGER;
  104. BEGIN
  105. IF (0 <= x) & (x < map.w) & (0 <= y) & (y < map.h) THEN
  106. k := map.cells[y, x].kind;
  107. IF (k = kind) OR
  108. (k = 32) & (kind = 64) OR
  109. ~forBush & (k = 96) & ((kind = 32) OR (kind = 64))
  110. THEN INCL(s, dir)
  111. END
  112. END
  113. END P;
  114. BEGIN
  115. s := {};
  116. P(map, kind, x , y - 1, up , forBush, s);
  117. P(map, kind, x + 1, y , right , forBush, s);
  118. P(map, kind, x , y + 1, down , forBush, s);
  119. P(map, kind, x - 1, y , left , forBush, s);
  120. P(map, kind, x - 1, y - 1, upLeft , forBush, s);
  121. P(map, kind, x + 1, y - 1, upRight , forBush, s);
  122. P(map, kind, x - 1, y + 1, downLeft , forBush, s);
  123. P(map, kind, x + 1, y + 1, downRight, forBush, s)
  124. RETURN s END CheckNeighbours;
  125. PROCEDURE UpdateMapTile*(VAR map: Map; x, y: INTEGER);
  126. VAR kind, tile, xx, yy: INTEGER;
  127. dirs: SET; (* Set of directions of neighbours where kind is the same *)
  128. corners: SET;
  129. BEGIN
  130. kind := map.cells[y, x].kind;
  131. tile := kind;
  132. corners := {};
  133. IF (kind >= 32) & (kind MOD 32 = 0) THEN
  134. dirs := CheckNeighbours(map, x, y, kind, FALSE);
  135. xx := 3; yy := 3;
  136. IF up IN dirs THEN
  137. IF {left, right, down} - dirs = {} THEN
  138. IF ~(upLeft IN dirs) & ~(downLeft IN dirs) THEN xx := 0; yy := 1
  139. ELSIF ~(upLeft IN dirs) & ~(upRight IN dirs) THEN xx := 1; yy := 0
  140. ELSIF ~(upRight IN dirs) & ~(downRight IN dirs) THEN xx := 2; yy := 1
  141. ELSIF ~(downLeft IN dirs) & ~(downRight IN dirs) THEN xx := 1; yy := 2
  142. ELSIF ~(upLeft IN dirs) THEN xx := 5; yy := 1
  143. ELSIF ~(upRight IN dirs) THEN xx := 4; yy := 1
  144. ELSIF ~(downLeft IN dirs) THEN xx := 5; yy := 0
  145. ELSIF ~(downRight IN dirs) THEN xx := 4; yy := 0
  146. ELSE xx := 1; yy := 1
  147. END
  148. ELSIF {left , right} - dirs = {} THEN xx := 1; yy := 2
  149. ELSIF {left , down } - dirs = {} THEN xx := 2; yy := 1
  150. ELSIF {right, down } - dirs = {} THEN xx := 0; yy := 1
  151. ELSIF left IN dirs THEN xx := 2; yy := 2
  152. ELSIF right IN dirs THEN xx := 0; yy := 2
  153. ELSIF down IN dirs THEN xx := 3; yy := 1
  154. ELSE xx := 3; yy := 2
  155. END
  156. ELSIF down IN dirs THEN
  157. IF {left, right} - dirs = {} THEN xx := 1; yy := 0
  158. ELSIF left IN dirs THEN xx := 2; yy := 0
  159. ELSIF right IN dirs THEN xx := 0; yy := 0
  160. ELSE xx := 3; yy := 0
  161. END
  162. ELSIF left IN dirs THEN
  163. IF right IN dirs THEN xx := 1; yy := 3
  164. ELSE xx := 2; yy := 3
  165. END
  166. ELSIF right IN dirs THEN xx := 0; yy := 3
  167. END;
  168. tile := kind + xx + yy * tilesInRow;
  169. IF {up, left, upLeft} - dirs = {upLeft} THEN INCL(corners, upLeft) END;
  170. IF {up, right, upRight} - dirs = {upRight} THEN INCL(corners, upRight) END;
  171. IF {down, left, downLeft} - dirs = {downLeft} THEN INCL(corners, downLeft) END;
  172. IF {down, right, downRight} - dirs = {downRight} THEN INCL(corners, downRight) END;
  173. corners := corners + dirs * allSides
  174. END;
  175. map.cells[y, x].tile := tile;
  176. map.cells[y, x].corners := corners;
  177. IF (kind # 32) & (kind # 96) THEN
  178. map.cells[y, x].bush := CheckNeighbours(map, x, y, 32, TRUE) * allSides
  179. ELSE
  180. map.cells[y, x].bush := {}
  181. END
  182. END UpdateMapTile;
  183. PROCEDURE UpdateMapTiles*(VAR map: Map);
  184. VAR x, y: INTEGER;
  185. BEGIN
  186. FOR y := 0 TO map.h - 1 DO
  187. FOR x := 0 TO map.w - 1 DO
  188. UpdateMapTile(map, x, y)
  189. END
  190. END
  191. END UpdateMapTiles;
  192. PROCEDURE UpdateMapTileAround*(VAR map: Map; x, y: INTEGER);
  193. BEGIN
  194. UpdateMapTile(map, x, y);
  195. IF x > 0 THEN
  196. UpdateMapTile(map, x - 1, y);
  197. IF y > 0 THEN UpdateMapTile(map, x - 1, y - 1) END;
  198. IF y < map.h - 1 THEN UpdateMapTile(map, x - 1, y + 1) END
  199. END;
  200. IF x < map.w - 1 THEN
  201. UpdateMapTile(map, x + 1, y);
  202. IF y > 0 THEN UpdateMapTile(map, x + 1, y - 1) END;
  203. IF y < map.h - 1 THEN UpdateMapTile(map, x + 1, y + 1) END
  204. END;
  205. IF y > 0 THEN UpdateMapTile(map, x, y - 1) END;
  206. IF y < map.h - 1 THEN UpdateMapTile(map, x, y + 1) END
  207. END UpdateMapTileAround;
  208. PROCEDURE SetCell*(VAR map: Map; x, y, kind: INTEGER);
  209. PROCEDURE P(VAR cell: Cell; kind: INTEGER);
  210. BEGIN
  211. IF cell.kind # kind THEN
  212. cell.kind := kind;
  213. UpdateMapTileAround(map, x, y)
  214. END
  215. END P;
  216. BEGIN
  217. P(map.cells[y, x], kind)
  218. END SetCell;
  219. PROCEDURE MakeRandomMap*(VAR map: Map; w, h: INTEGER);
  220. VAR x, y: INTEGER;
  221. BEGIN
  222. map.w := w; map.h := h;
  223. FOR y := 0 TO h - 1 DO
  224. FOR x := 0 TO w - 1 DO
  225. map.cells[y, x].kind := 0 (*Random.Int(4)*)
  226. END
  227. END;
  228. UpdateMapTiles(map)
  229. END MakeRandomMap;
  230. PROCEDURE ReadCell(VAR r: Files.Rider; VAR cell: Cell);
  231. BEGIN
  232. Files.ReadInt(r, cell.kind)
  233. END ReadCell;
  234. PROCEDURE ReadMap(VAR r: Files.Rider; VAR map: Map): BOOLEAN;
  235. VAR x, y: INTEGER;
  236. BEGIN
  237. Files.ReadInt(r, map.w);
  238. Files.ReadInt(r, map.h);
  239. FOR y := 0 TO map.h - 1 DO
  240. FOR x := 0 TO map.w - 1 DO
  241. ReadCell(r, map.cells[y, x])
  242. END
  243. END;
  244. UpdateMapTiles(map)
  245. RETURN TRUE END ReadMap;
  246. PROCEDURE WriteCell(VAR r: Files.Rider; cell: Cell);
  247. BEGIN
  248. Files.WriteInt(r, cell.kind)
  249. END WriteCell;
  250. PROCEDURE WriteMap(VAR r: Files.Rider; map: Map);
  251. VAR x, y: INTEGER;
  252. BEGIN
  253. Files.WriteInt(r, map.w);
  254. Files.WriteInt(r, map.h);
  255. FOR y := 0 TO map.h - 1 DO
  256. FOR x := 0 TO map.w - 1 DO
  257. WriteCell(r, map.cells[y, x])
  258. END
  259. END
  260. END WriteMap;
  261. PROCEDURE LoadGame*(VAR game: Game; fname: ARRAY OF CHAR): BOOLEAN;
  262. VAR F: Files.File;
  263. r: Files.Rider;
  264. ok: BOOLEAN;
  265. BEGIN
  266. ok := FALSE;
  267. F := Files.Old(fname);
  268. IF F # NIL THEN
  269. Files.Set(r, F, 0);
  270. ok := ReadMap(r, game.map);
  271. Files.Close(F)
  272. END
  273. RETURN ok END LoadGame;
  274. PROCEDURE SaveGame*(VAR game: Game; fname: ARRAY OF CHAR);
  275. VAR F: Files.File;
  276. r: Files.Rider;
  277. BEGIN
  278. F := Files.New(fname);
  279. IF F # NIL THEN
  280. Files.Set(r, F, 0);
  281. WriteMap(r, game.map);
  282. Files.Register(F);
  283. Files.Close(F)
  284. END
  285. END SaveGame;
  286. (** Returns a bitmap with the given name (appends strings to make a file name).
  287. On error sets ok to FALSE and ouputs an error message.
  288. Never sets ok to TRUE. *)
  289. PROCEDURE LoadBitmap(name: ARRAY OF CHAR; VAR ok: BOOLEAN): G.Bitmap;
  290. VAR bmp: G.Bitmap;
  291. s: ARRAY 256 OF CHAR;
  292. key: G.Color;
  293. BEGIN
  294. s := 'Data/Graph/';
  295. Strings.Append(name, s);
  296. Strings.Append('.png', s);
  297. bmp := G.LoadBitmap(s);
  298. IF bmp = NIL THEN
  299. ok := FALSE;
  300. Out.String('Error: Could not load bitmap "');
  301. Out.String(s); Out.String('".'); Out.Ln
  302. ELSE
  303. G.MakeCol(key, 255, 0, 128);
  304. G.ApplyMaskColor(bmp, key)
  305. END
  306. RETURN bmp END LoadBitmap;
  307. PROCEDURE InitGame*(VAR game: Game);
  308. BEGIN
  309. MakeRandomMap(game.map, 128, 128)
  310. END InitGame;
  311. PROCEDURE Init*(): BOOLEAN;
  312. VAR ok: BOOLEAN;
  313. BEGIN ok := TRUE;
  314. tiles := LoadBitmap('tiles', ok)
  315. RETURN ok END Init;
  316. END GameEngine.