You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

382 lines
7.5 KiB

  1. /*
  2. Sandbox game
  3. - paint() make checkerboard
  4. - force 60fps
  5. - count pixels per physics frame, make sure nothing is fabricated
  6. */
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <stdint.h>
  10. #include <math.h>
  11. #ifndef CUSTOM
  12. #define CNFG_IMPLEMENTATION
  13. #define CNFGOGL
  14. #include "rawdraw/CNFG.h"
  15. #include "rawdraw/os_generic.h"
  16. #endif
  17. //#define STEP
  18. enum Pixels {
  19. PIX_AIR,
  20. PIX_SAND,
  21. PIX_WATER,
  22. PIX_STONE,
  23. PIX_DRAIN,
  24. };
  25. struct PixelInfo {
  26. int mass;
  27. int isFluid;
  28. };
  29. struct Pixel {
  30. uint8_t type;
  31. int8_t vel;
  32. uint8_t fountainType;
  33. };
  34. struct Mouse {
  35. int down;
  36. int x;
  37. int y;
  38. }mouse;
  39. struct Game {
  40. int scale;
  41. int brush;
  42. int brushSize;
  43. int width;
  44. int height;
  45. }game = {
  46. .scale = 4,
  47. .brush = PIX_SAND,
  48. .brushSize = 3,
  49. .width = 640,
  50. .height = 480,
  51. };
  52. struct Buffer {
  53. uint32_t *screen;
  54. struct Pixel **physics;
  55. struct Pixel **temp;
  56. }buffer;
  57. void physics(int w, int h);
  58. void HandleKey( int keycode, int bDown ) {
  59. if (!bDown) return;
  60. switch (keycode) {
  61. case '1':
  62. game.brush = PIX_SAND;
  63. return;
  64. case '2':
  65. game.brush = PIX_WATER;
  66. return;
  67. case '3':
  68. game.brush = PIX_AIR;
  69. return;
  70. case '4':
  71. game.brush = PIX_STONE;
  72. return;
  73. case '5':
  74. game.brush = PIX_DRAIN;
  75. return;
  76. #ifdef STEP
  77. case ' ':
  78. physics(game.width / scale, HEIGHT / scale);
  79. return;
  80. #endif
  81. case 'q':
  82. CNFGTearDown();
  83. return;
  84. }
  85. }
  86. void HandleButton(int x, int y, int button, int bDown) {
  87. mouse.down = bDown;
  88. }
  89. void HandleMotion(int x, int y, int mask) {
  90. mouse.x = x;
  91. mouse.y = y;
  92. }
  93. void HandleDestroy() {
  94. exit(0);
  95. }
  96. struct Pixel createPixel(int type) {
  97. struct Pixel pixel = {
  98. .type = type,
  99. .vel = 0,
  100. };
  101. return pixel;
  102. }
  103. struct PixelInfo pixelInfo(int x, int y) {
  104. int type = buffer.physics[x][y].type;
  105. switch (type) {
  106. case PIX_AIR: {
  107. struct PixelInfo info = {
  108. .mass = 1,
  109. .isFluid = 0,
  110. };
  111. return info;
  112. }
  113. case PIX_SAND: {
  114. struct PixelInfo info = {
  115. .mass = 8,
  116. .isFluid = 0,
  117. };
  118. return info;
  119. }
  120. case PIX_WATER: {
  121. struct PixelInfo info = {
  122. .mass = 5,
  123. .isFluid = 1,
  124. };
  125. return info;
  126. }
  127. case PIX_STONE: {
  128. struct PixelInfo info = {
  129. .mass = 10,
  130. .isFluid = 0,
  131. };
  132. return info;
  133. }
  134. }
  135. // Catastrophic error
  136. exit(1);
  137. }
  138. int outOfBounds(int x, int y) {
  139. return (x <= 0) || ((game.width / game.scale) <= x) ||
  140. (y >= (game.height / game.scale)) || (y <= 0);
  141. }
  142. // Check if pixel is something in game buffer,
  143. // and make sure it isn't in temp buffer either
  144. int pixelIs(int x, int y, int type) {
  145. return buffer.physics[x][y].type == type && buffer.temp[x][y].type == type;
  146. }
  147. void physics(int w, int h) {
  148. for (int x = 0; x < game.width; x++) {
  149. for (int y = 0; y < game.height; y++) {
  150. buffer.temp[x][y] = buffer.physics[x][y];
  151. }
  152. }
  153. for (int y = 0; y < h; y++) {
  154. for (int x = 0; x < w; x++) {
  155. struct Pixel currPixel = buffer.physics[x][y];
  156. // used for randomizing directions
  157. int direction = rand() % 2;
  158. // If current pixel is nothing
  159. if (currPixel.type == PIX_AIR || currPixel.type == PIX_STONE) {
  160. continue;
  161. }
  162. if (currPixel.type == PIX_DRAIN) {
  163. if (!outOfBounds(x, y - 1)) {
  164. buffer.temp[x][y - 1] = createPixel(PIX_AIR);
  165. }
  166. continue;
  167. }
  168. // Pixel below is empty
  169. if (pixelIs(x, y + 1, PIX_AIR) && !outOfBounds(x, y + 1)) {
  170. buffer.temp[x][y] = buffer.temp[x][y + 1];
  171. buffer.temp[x][y + 1] = currPixel;
  172. if (direction) {
  173. buffer.temp[x][y + 1].vel = 1;
  174. } else {
  175. buffer.temp[x][y + 1].vel = -1;
  176. }
  177. continue;
  178. }
  179. // Move bottom left pixel
  180. if (pixelIs(x - 1, y + 1, PIX_AIR) && !outOfBounds(x - 1, y + 1)
  181. && direction) {
  182. buffer.temp[x][y] = buffer.temp[x - 1][y + 1];
  183. buffer.temp[x - 1][y + 1] = currPixel;
  184. buffer.temp[x - 1][y + 1].vel = -1;
  185. continue;
  186. }
  187. // Move bottom right pixel
  188. if (pixelIs(x + 1, y + 1, PIX_AIR) && !outOfBounds(x + 1, y + 1)
  189. && !direction) {
  190. buffer.temp[x][y] = buffer.temp[x + 1][y + 1];
  191. buffer.temp[x + 1][y + 1] = currPixel;
  192. buffer.temp[x - 1][y + 1].vel = 1;
  193. continue;
  194. }
  195. // Fluid dynamics
  196. if (pixelInfo(x, y).isFluid) {
  197. // Fluid hit right solid partical
  198. if (!pixelIs(x + 1, y, PIX_AIR) || outOfBounds(x + 1, y)) {
  199. buffer.temp[x][y].vel = -1;
  200. }
  201. // Fluid hit left pixel
  202. if (!pixelIs(x - 1, y, PIX_AIR) || outOfBounds(x - 1, y)) {
  203. buffer.temp[x][y].vel = 1;
  204. }
  205. // Move left pixel
  206. if (pixelIs(x - 1, y, PIX_AIR) && !outOfBounds(x - 1, y) && buffer.physics[x][y].vel < 0) {
  207. buffer.temp[x][y] = buffer.temp[x - 1][y];
  208. buffer.temp[x - 1][y] = currPixel;
  209. continue;
  210. }
  211. // Move right pixel
  212. if (pixelIs(x + 1, y, PIX_AIR) && !outOfBounds(x + 1, y) && buffer.physics[x][y].vel > 0) {
  213. buffer.temp[x][y] = buffer.temp[x + 1][y];
  214. buffer.temp[x + 1][y] = currPixel;
  215. continue;
  216. }
  217. } else {
  218. // heavy pixel sink
  219. if (!outOfBounds(x, y + 1) && pixelInfo(x, y + 1).isFluid) {
  220. buffer.temp[x][y] = buffer.physics[x][y + 1];
  221. buffer.temp[x][y + 1] = currPixel;
  222. continue;
  223. }
  224. // heavy pixel sink to the left
  225. if (!outOfBounds(x - 1, y + 1) && pixelInfo(x - 1, y + 1).isFluid
  226. && direction) {
  227. buffer.temp[x][y] = buffer.physics[x - 1][y + 1];
  228. buffer.temp[x - 1][y + 1] = currPixel;
  229. continue;
  230. }
  231. // heavy pixel sink to the right
  232. if (!outOfBounds(x + 1, y + 1) && pixelInfo(x + 1, y + 1).isFluid
  233. && !direction) {
  234. buffer.temp[x][y] = buffer.physics[x + 1][y + 1];
  235. buffer.temp[x + 1][y + 1] = currPixel;
  236. continue;
  237. }
  238. }
  239. }
  240. }
  241. for (int x = 0; x < w; x++) {
  242. for (int y = 0; y < h; y++) {
  243. buffer.physics[x][y] = buffer.temp[x][y];
  244. }
  245. }
  246. }
  247. // Draw a checkerboard style brush
  248. void paint(int x, int y) {
  249. y /= game.scale;
  250. x /= game.scale;
  251. int toggle = 1;
  252. for (int y2 = -1 * game.brushSize; y2 < game.brushSize - 1; y2++) {
  253. for (int x2 = -1 * game.brushSize; x2 < game.brushSize - 1; x2++) {
  254. if (toggle && !outOfBounds(x + x2, y + y2)) {
  255. buffer.physics[(x + x2)][(y + y2)] = createPixel(game.brush);
  256. }
  257. toggle = !toggle;
  258. }
  259. }
  260. }
  261. // Create rect on bitmap
  262. void bitRect(int x, int y, int col) {
  263. y *= game.scale;
  264. x *= game.scale;
  265. for (int x2 = 0; x2 < game.scale; x2++) {
  266. for (int y2 = 0; y2 < game.scale; y2++) {
  267. buffer.screen[(y + y2) * game.width + (x + x2)] = col;
  268. }
  269. }
  270. }
  271. void renderFrame() {
  272. for (int x = 0; x < game.width / game.scale; x++) {
  273. for (int y = 0; y < game.height / game.scale; y++) {
  274. switch (buffer.temp[x][y].type) {
  275. case PIX_AIR:
  276. bitRect(x, y, 0xffffffff);
  277. break;
  278. case PIX_SAND:
  279. bitRect(x, y, 0xe3e29dff);
  280. break;
  281. case PIX_WATER:
  282. bitRect(x, y, 0x5278c4ff);
  283. break;
  284. case PIX_STONE:
  285. bitRect(x, y, 0x123456ff);
  286. break;
  287. case PIX_DRAIN:
  288. bitRect(x, y, 0x63ffacff);
  289. }
  290. }
  291. }
  292. }
  293. void alloc2D(struct Pixel ***a, int w, int h) {
  294. w += 5;
  295. h += 5;
  296. *a = malloc(sizeof(struct Pixel *) * w);
  297. for (int i = 0; i < game.width; i++) {
  298. (*a)[i] = malloc(sizeof(struct Pixel) * h);
  299. }
  300. }
  301. int main() {
  302. buffer.screen = malloc(game.width * game.height * sizeof(uint32_t));
  303. alloc2D(&buffer.physics, game.width, game.height);
  304. alloc2D(&buffer.temp, game.width, game.height);
  305. CNFGSetup("Sandbox", game.width, game.height);
  306. while (1) {
  307. CNFGHandleInput();
  308. CNFGClearFrame();
  309. if (mouse.down && mouse.x <= game.width && mouse.y <= game.height) {
  310. paint(mouse.x, mouse.y);
  311. }
  312. #ifndef STEP
  313. for (int i = 0; i < 5; i++) {
  314. physics(game.width / game.scale, game.height / game.scale);
  315. }
  316. #endif
  317. renderFrame();
  318. CNFGBlitImage(buffer.screen, 0, 0, game.width, game.height);
  319. CNFGSwapBuffers();
  320. OGUSleep((int)( 0.5 * 1000 ));
  321. }
  322. }