Kitomas

work for 2025-04-25 (0/5)

Apr 25th, 2025
45
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 29.45 KB | None | 0 0
  1. /******************************************************************************/
  2. /******************************************************************************/
  3. //"gdi_maze_game_2025-04-25\src\draw_raycast_scene.cpp":
  4. #include <public_stuff.hpp>
  5. #include "draw_raycast_scene.hpp"
  6.  
  7.  
  8.  
  9. void fill_scanlines(u32 y, u32 len, Color8 color,
  10.                     Color8* dst)
  11. {
  12.   if(y   >= CANVAS_H) return;
  13.   if(len == 0       ) return;
  14.   if(dst == nullptr ) return;
  15.  
  16.   if((y+len) >= CANVAS_H) len = CANVAS_H-y;
  17.  
  18.   memSet((&dst->v) + y*CANVAS_W, color.v, len*CANVAS_W);
  19.  
  20. }
  21.  
  22.  
  23.  
  24.  
  25.  
  26. #define pos_angle pos.z
  27.  
  28. void draw_raycast_scene(const s32* map, Point2d map_size,
  29.                         Fpoint3d pos, // (pos.z is the angle, in radians)
  30.                         const Color8 tiles[][RAYCAST_TEX_SIZE_TOTAL],
  31.                         Color8 colorCeiling, Color8 colorFloor,
  32.                         u32 renderDistance, f32 fov,
  33.                         Color8* dst)
  34. {
  35.   if(map == nullptr  ||  dst == nullptr) return;
  36.  
  37.   Fpoint2d dir = create_2d_vector(pos_angle, 1.0f);
  38.  
  39.   // Calculate camera plane's orientation and size
  40.   // (An fov larger than 90 is inaccurate!)
  41.   const f32 plane_angle     = M_PI2f + pos_angle; // = pi/2 + pos.z
  42.   const f32 plane_magnitude = (f32)CLAMP(fov, 1e-100, 90) / 90;
  43.   Fpoint2d  plane           = create_2d_vector(plane_angle, plane_magnitude);
  44.  
  45.   Fpoint2d rayDir    = {0.0f, 0.0f}; // Current ray's direction
  46.   Fpoint2d deltaDist = {0.0f, 0.0f}; // Length of ray from one x or y-side to next x or y-side
  47.   Fpoint2d sideDist  = {0.0f, 0.0f}; // Length of ray from current position to next x or y-side
  48.  
  49.  
  50.  
  51.   // Draw the floor and ceiling respectively
  52.   // (floor is at y=0, since GDI bitmaps are drawn bottom-up)
  53.   fill_scanlines(         0, CANVAS_H/2, colorFloor, dst);
  54.   fill_scanlines(CANVAS_H/2, CANVAS_H/2, colorCeiling, dst);
  55.  
  56.   if(renderDistance == 0) return; // Lol
  57.  
  58.  
  59.  
  60.   // For each column of pixels
  61.   for(s32 x=0; x<CANVAS_W; ++x){
  62.     // The tile the player's currently in
  63.     Point2d pos_map = {s32(pos.x), s32(pos.y)};
  64.  
  65.     // x-coordinate along the camera plane
  66.     f32 plane_offset = f32(x*2)/CANVAS_W - 1.0f; // -1.0f -> 1.0f
  67.  
  68.     // Ray's direction and position
  69.     rayDir.x = dir.x + plane.x*plane_offset;
  70.     rayDir.y = dir.y + plane.y*plane_offset;
  71.  
  72.     // (Replace number with 1e30 if it would otherwise divide by 0)
  73.     deltaDist.x = (rayDir.x) ? fabsf(1.0f/rayDir.x) : 1e30;
  74.     deltaDist.y = (rayDir.y) ? fabsf(1.0f/rayDir.y) : 1e30;
  75.  
  76.     // Calculate step and initial sideDist
  77.     Point2d step; // What direction to step in x or y-direction (either -1 or 1)
  78.  
  79.     if(rayDir.x < 0){
  80.       step.x = -1;
  81.       sideDist.x = deltaDist.x * (pos.x - pos_map.x);
  82.     } else {
  83.       step.x = 1;
  84.       sideDist.x = deltaDist.x * (pos_map.x+1 - pos.x);
  85.     }
  86.  
  87.     if(rayDir.y < 0){
  88.       step.y = -1;
  89.       sideDist.y = (pos.y - pos_map.y) * deltaDist.y;
  90.     } else {
  91.       step.y = 1;
  92.       sideDist.y = (pos_map.y+1 - pos.y) * deltaDist.y;
  93.     }
  94.  
  95.  
  96.  
  97.     // = 'Was an EW (horizontal) or NS (vertical) tile hit?'
  98.     bool sideVert;
  99.  
  100.     // = 'Which tile did the current ray hit?' (0 means it hit nothing at all!)
  101.     s32 tileHit = 0;
  102.  
  103.     // For each step in the current ray (perform DDA)
  104.     for(u32 i=0; i<renderDistance; ++i){
  105.       // Jump to next map square, either in the x-direction, or the y-direction
  106.       if(sideDist.x < sideDist.y){
  107.         sideDist.x += deltaDist.x;
  108.         pos_map.x += step.x;
  109.         sideVert = false;// Closest tile is horizontal
  110.       } else{
  111.         sideDist.y += deltaDist.y;
  112.         pos_map.y += step.y;
  113.         sideVert = true; // Closest tile is vertical
  114.       }
  115.  
  116.       s32 tileWhich = pos_map.x + pos_map.y*map_size.x;
  117.  
  118.       // Check if ray has hit a wall
  119.       if(pos_map.x >= 0  &&  pos_map.x < map_size.x  &&
  120.          pos_map.y >= 0  &&  pos_map.y < map_size.y  &&
  121.          map[tileWhich] !=/*>*/ 0)
  122.       {
  123.         tileHit = map[tileWhich];
  124.         break;
  125.       }
  126.  
  127.     }
  128.  
  129.  
  130.  
  131.     // Calculate distance projected on camera direction,
  132.     // because raw euclidean distance would give as fisheye effect.
  133.     //
  134.     // (No joke, this was the hardest part of the raycasting tutorial to understand!)
  135.     f32 perpWallDist; // (AKA the final length of the current ray)
  136.     if(!sideVert) perpWallDist = (sideDist.x - deltaDist.x);
  137.     else          perpWallDist = (sideDist.y - deltaDist.y);
  138.  
  139.  
  140.  
  141.     // Where exactly the wall was hit
  142.     f32 wallX;
  143.     if(!sideVert) wallX = pos.y + perpWallDist * rayDir.y;
  144.     else          wallX = pos.x + perpWallDist * rayDir.x;
  145.     wallX -= floor(wallX); // wallX %= 1.0f
  146.  
  147.     // x coordinate on the texture
  148.     // (0.0f -> 1.0f, which is what draw_vslice uses for this parameter)
  149.     f32 texX = wallX;
  150.     if(    !sideVert && rayDir.x > 0) texX = 1.0f - texX;
  151.     else if(sideVert && rayDir.y < 0) texX = 1.0f - texX;
  152.  
  153.     // I don't know why but draw_vslice is rendering the textures backwards
  154.     texX = 1.0f-texX;
  155.  
  156.     // A multiplier for the height of the pixel column,
  157.     // relative to the height of the framebuffer
  158.     // (0.0f -> 1.0f, which is what draw_vslice uses for this parameter)
  159.     f32 columnHeight = 1.0f/perpWallDist;
  160.  
  161.     // -1 is applied to tileHit so that tiles[0] can be accessed
  162.     /* if(tileHit > 0) */
  163.     if(tileHit != 0){
  164.       if(tileHit < 0){ tileHit = -tileHit; }
  165.       draw_vslice(x, columnHeight, texX, tiles[tileHit-1], dst);
  166.     }
  167.  
  168.   }
  169.  
  170. }
  171.  
  172. /******************************************************************************/
  173. /******************************************************************************/
  174. //"gdi_maze_game_2025-04-25\src\generate_maze.cpp":
  175. #include <public_stuff.hpp>
  176. #include "generate_maze.hpp"
  177.  
  178.  
  179.  
  180.  
  181.  
  182. // Direction vectors (up, right, down, left)
  183. const s32 d_x[] = { 0,  1,  0, -1};
  184. const s32 d_y[] = {-1,  0,  1,  0};
  185.  
  186.  
  187.  
  188. static void carve(s32 x, s32 y,
  189.                   s32* map, s32 w, s32 h)
  190. {
  191.   s32 dir[] = {0, 1, 2, 3};
  192.  
  193.   // Shuffle dir
  194.   for(u32 i=3; i>0; --i){
  195.     u32 j = rand() % (i+1);
  196.     s32 tmp = dir[i];
  197.     dir[i] = dir[j];
  198.     dir[j] = tmp;
  199.   }
  200.  
  201.  
  202.  
  203.   for(s32 i=0; i<4; ++i){
  204.     s32 cur = dir[i];
  205.     s32 nx  =  x + d_x[cur] * 2;
  206.     s32 ny  =  y + d_y[cur] * 2;
  207.     s32 which_a  =  nx + ny*w;
  208.  
  209.     if(nx > 0  &&  nx < w  &&
  210.        ny > 0  &&  ny < h  &&
  211.        map[which_a] != 0)
  212.     {
  213.       s32 which_b = nx-d_x[cur] + (ny-d_y[cur])*w;
  214.       map[which_b] = 0;
  215.       map[which_a] = 0;
  216.       carve(nx, ny, map, w, h);
  217.     }
  218.  
  219.   }
  220.  
  221. }
  222.  
  223.  
  224.  
  225.  
  226.  
  227. // w & h must be odd, otherwise the south-eastern edges will be carved!
  228. bool generate_maze(s32* map, u32 w, u32 h,
  229.                    s32 fillValue)
  230. {
  231.   if(w > INT_MAX  ||  h > INT_MAX) return false;
  232.   if(fillValue == 0) return false;
  233.  
  234.   // Initialize all cells to fill value,
  235.   // or at random up to -fillValue if negative
  236.   u32 numCells = w*h;
  237.   if(fillValue > 0){
  238.     for(u32 i=0; i<numCells; ++i) map[i] = fillValue;
  239.   } else {
  240.     fillValue = -fillValue;
  241.     for(u32 i=0; i<numCells; ++i) map[i] = 1+rand()%fillValue;
  242.   }
  243.  
  244.   map[START_X + START_Y*w] = 0;
  245.   carve(START_X, START_Y, map, w, h);
  246.  
  247.   return true;
  248.  
  249. }
  250. /******************************************************************************/
  251. /******************************************************************************/
  252. //"gdi_maze_game_2025-04-25\src\user_audio.cpp":
  253. #include <public_stuff.hpp>
  254.  
  255.  
  256.  
  257. SoundEngine sfx;
  258.  
  259.  
  260.  
  261. void user_audio(StereoF32* stream, u32 len, u32 sampleRate, f64 timeStamp)
  262. {
  263.   // This plays a normal 440Hz sine wave (commented!)
  264. /*
  265.   // The static keyword here just makes the variable persistent, in that
  266.   // its state is preserved between each call to user_audio
  267.   static u64 t = 0; // u[nsigned] 64-bit integer
  268.  
  269.  
  270.   for(u32 i=0; i<len; ++i){ //For every sample to be filled in the audio buffer
  271.  
  272.     // t is incremented every sample, and is divided by the sample rate to
  273.     // basically make a percentage of how far it is through a second.
  274.     // This is then multiplied by 2pi, which effectively makes it oscillate
  275.     // once every second if you fed cycle into sin directly
  276.     f32 cycle = ((f32)(t++)/sampleRate) * M_2PIf;
  277.  
  278.     // cycle is multiplied by 440 so that it oscillates 440 times a second
  279.     // instead of just once.
  280.     // (Also, sinF is just sin but it uses 32-bit f[loats] instead of 64-bit,
  281.     //  and unlike the actual sinf, this is a custom implementation)
  282.     f32 smp = sinF(cycle*440); // This ofc returns a number from -1.0 to 1.0
  283.  
  284.     // 20% volume
  285.     smp *= 0.20f;
  286.  
  287.     // Since the audio stream is stereo, the sample is applied to
  288.     // both 'ears' so-to-speak.
  289.     stream[i].l = smp;
  290.     stream[i].r = smp;
  291.  
  292.   }
  293.  
  294.  
  295.   // Unnecessary, since a u64 is so big, but this makes it so that
  296.   // even if it was a smaller integer size, it will never overflow.
  297.   // (The % operator does a division, but gets the remainder instead)
  298.   t %= sampleRate;
  299. */
  300.  
  301.   sfx.sampleRate = sample_rate;
  302.   if(!sfx.mixTracks(stream, len, timeStamp)){
  303.     _printf("ERROR: sfx.mixTracks failed!\n");
  304.   }
  305.  
  306. }
  307. /******************************************************************************/
  308. /******************************************************************************/
  309. //"gdi_maze_game_2025-04-25\src\user_main.cpp":
  310. #include <public_stuff.hpp>
  311. #include "draw_raycast_scene.hpp"
  312. #include "generate_maze.hpp"
  313. #include "map_and_textures.hpp"
  314.  
  315. extern SoundEngine sfx;
  316.  
  317. //draw_textf_box is function is broken with CENTERED_TEXT
  318. //todo: fix it inside the template before moving it to a separate source file
  319.  
  320. #define DRAW_TEXT_BOX(_fmt, _x, _y, _npm) \
  321. { \
  322.   Rect2d textRect = draw_textf(_fmt, (_x), (_y), 0, (_npm), font_pico8, pixels); \
  323.   if(textRect.h != -1){ \
  324.     textRect.x -= 2, textRect.y -= 2; \
  325.     textRect.w += 4, textRect.h += 4; \
  326.     fill_rect(textRect.x, textRect.y, textRect.w, textRect.h,   0); \
  327.     draw_rect(textRect.x, textRect.y, textRect.w, textRect.h, 192); \
  328.     textRect = draw_textf(_fmt, (_x), (_y), 0, (_npm), font_pico8, pixels); \
  329.   } \
  330. }
  331.  
  332. //FUCK YOU GCC FUCK YOU FUCK YOU FUCK YOU
  333.  
  334. #define DRAW_TEXTF_BOX_1(_fmt, _x, _y, _npm, value) \
  335. { \
  336.   Rect2d textRect = draw_textf(_fmt, (_x), (_y), 0, (_npm), font_pico8, pixels, value); \
  337.   if(textRect.h != -1){ \
  338.     textRect.x -= 2, textRect.y -= 2; \
  339.     textRect.w += 4, textRect.h += 4; \
  340.     fill_rect(textRect.x, textRect.y, textRect.w, textRect.h,   0); \
  341.     draw_rect(textRect.x, textRect.y, textRect.w, textRect.h, 192); \
  342.     textRect = draw_textf(_fmt, (_x), (_y), 0, (_npm), font_pico8, pixels, value); \
  343.   } \
  344. }
  345.  
  346. #define DRAW_TEXTF_BOX_2(_fmt, _x, _y, _npm, value_a, value_b) \
  347. { \
  348.   Rect2d textRect = draw_textf(_fmt, (_x), (_y), 0, (_npm), font_pico8, pixels, value_a, value_b); \
  349.   if(textRect.h != -1){ \
  350.     textRect.x -= 2, textRect.y -= 2; \
  351.     textRect.w += 4, textRect.h += 4; \
  352.     fill_rect(textRect.x, textRect.y, textRect.w, textRect.h,   0); \
  353.     draw_rect(textRect.x, textRect.y, textRect.w, textRect.h, 192); \
  354.     textRect = draw_textf(_fmt, (_x), (_y), 0, (_npm), font_pico8, pixels, value_a, value_b); \
  355.   } \
  356. }
  357.  
  358.  
  359.  
  360. Color24 hsv2rgb(f32 h, f32 s, f32 v){
  361.   f32 r, g, b;
  362.  
  363.   // Normalize values
  364.   h /= 360;
  365.   s /= 100;
  366.   v /= 100;
  367.  
  368.   // The rest is magic, idk
  369.   signed int i = floor(h*6);
  370.   f32 f = h * 6 - i;
  371.   f32 p = v * (1.0f -            s);
  372.   f32 q = v * (1.0f -       f  * s);
  373.   f32 t = v * (1.0f - (1.0f-f) * s);
  374.  
  375.   switch(i % 6){
  376.     case 0:  r = v,  g = t,  b = p;  break;
  377.     case 1:  r = q,  g = v,  b = p;  break;
  378.     case 2:  r = p,  g = v,  b = t;  break;
  379.     case 3:  r = p,  g = q,  b = v;  break;
  380.     case 4:  r = t,  g = p,  b = v;  break;
  381.     case 5:  r = v,  g = p,  b = q;  break;
  382.   }
  383.  
  384.   // It should be impossible for r,g,b to be uninitialized
  385.   // due to how the switch is set up, so the warning can be ignored.
  386.   #if defined(__GNUC__)
  387.     #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
  388.     #pragma GCC diagnostic push
  389.   // TBD: do macro checks for other compilers
  390.   #endif
  391.  
  392.   Color24 out;
  393.   out.r = r*255;
  394.   out.g = g*255;
  395.   out.b = b*255;
  396.   out._ = 0;
  397.   return out;
  398.  
  399.   #if defined(__GNUC__)
  400.     #pragma GCC diagnostic pop
  401.   #endif
  402.  
  403. }
  404.  
  405.  
  406.  
  407.  
  408.  
  409. #define SHOW_MINIMAP_ALWAYS 1
  410.  
  411. Event evt;
  412.  
  413. f32 fov = 90;
  414. u64 frame = 0;
  415.  
  416. #define FOOTSTEP_DELAY 20 // In frames
  417. #define MOVE_SPEED 1.0f/FOOTSTEP_DELAY
  418.  
  419. s32 map_w = 9;
  420. s32 map_h = 9;
  421.  
  422.  
  423.  
  424.  
  425.  
  426. int user_main(int argc, char** argv){
  427.   if(0){ _lbl_exit:
  428.     sfx.stop();
  429.     while(sfx.getActiveTracks() > 0) timeSleep(5);
  430.     return 0;
  431.   }
  432.  
  433.   #define XOR_WHICH 3
  434.   textures[XOR_WHICH][0] = textures[XOR_WHICH][1] = TEX_SIZE;
  435.   for(int y=0; y<TEX_SIZE; ++y)
  436.   for(int x=0; x<TEX_SIZE; ++x)
  437.   {
  438.     textures[XOR_WHICH][ 8 + x + y*TEX_SIZE ] = (x^y)<<1;
  439.   }
  440.  
  441.   AudioData footstep0(SMPFMT_S16, sizeof(footstep0_data)/2, 1, 22050);
  442.   memCpy(footstep0.hdr->samples, footstep0_data, sizeof(footstep0_data));
  443.   footstep0.convertFormat(SMPFMT_F32);
  444.  
  445.   u32 numSamples = f32(sample_rate)*0.60f; //600ms
  446.   AudioData sinewave(SMPFMT_F32, numSamples, 1, sample_rate);
  447.   f32* sinewave_smp = (f32*)sinewave.hdr->samples;
  448.   for(u32 i=0; i<numSamples; ++i){
  449.     f32 cycle = (f32(i)/sample_rate) * M_2PIf;
  450.     sinewave_smp[i]  = sinf(cycle*880);
  451.     sinewave_smp[i] *= 1.0f-(f32(i)/numSamples);
  452.   }
  453.   numSamples /= 40;
  454.   for(u32 i=0; i<numSamples; ++i)
  455.     sinewave_smp[i] *= f32(i)/numSamples;
  456.  
  457.  
  458.  
  459.  
  460.  
  461. /******************************************************************************/
  462.  
  463.   _lbl_start:
  464.  
  465.   bool selecting_size = true;
  466.  
  467.   while(selecting_size){
  468.     while(pollEvent(&evt))
  469.     switch(evt.type){
  470.       case EVENT_QUIT: goto _lbl_exit;
  471.       case EVENT_MOUSE_VWHEEL: {
  472.         s32 newSize = CLAMP(map_w+s32(evt.mouse.dy)*2, 9, MAP_MAX);
  473.         map_w = map_h = newSize;
  474.       } break;
  475.       case EVENT_MOUSE_DOWN: {
  476.         if(evt.mouse.button&MBUTTON_LEFT) selecting_size = false;
  477.       } break;
  478.     }
  479.  
  480.     Color24 newColor = hsv2rgb((f32)((frame*1)%720)/2, 100.0f, 100.0f);
  481.     update_palette(254, 1, &newColor);
  482.     canvas_clear(254);
  483.  
  484.     DRAW_TEXTF_BOX_2("Map Size: %ix%i", CENTERED_TEXT, CENTERED_TEXT, false, map_w, map_h);
  485.  
  486.     DRAW_TEXT_BOX("(Scroll wheel to change size; Left click to start!)",
  487.                   CENTERED_TEXT, CANVAS_H/2-12, false);
  488.  
  489.     canvas_present(true);
  490.     timeSleep(16);
  491.     ++frame;
  492.  
  493.   }
  494.  
  495.  
  496.  
  497.  
  498.  
  499.   #define NUM_ACTIVE_TEX 4
  500.   s32 fillValue = rand()%(NUM_ACTIVE_TEX+1);
  501.   if(fillValue == 0) fillValue = -NUM_ACTIVE_TEX; // 0 for Random selection
  502.   generate_maze(map, map_w, map_h, fillValue);
  503.  
  504.   // Fill end point with the right tile
  505.   #define END_POINT -5 // Negative means 'render without collision'
  506.   map[map_w-2 + (map_h-2)*map_w] = END_POINT;
  507.  
  508.   #if defined(_DEBUG) || SHOW_MINIMAP_ALWAYS==1
  509.     s32 map_scale = (map_w < 27) ? 2 : 1;
  510.   #endif
  511.  
  512.  
  513.  
  514.   Fpoint3d pos = {START_X+0.5f, START_Y+0.5f, 0.0f};
  515.   u64 counterStart = 0;
  516.   bool move_forward = false;
  517.   bool move_back    = false;
  518.   bool move_left    = false;
  519.   bool move_right   = false;
  520.  
  521.  
  522.  
  523.   while(1){
  524.     //Handle events
  525.     while(pollEvent(&evt))
  526.     switch(evt.type){
  527.       case EVENT_QUIT: goto _lbl_exit;
  528.  
  529.       case EVENT_MOUSE_MOVED: if(is_cursor_trapped()){
  530.         pos.z += (evt.mouse.dx/1000)*2;
  531.       } break;
  532.  
  533.       case EVENT_MOUSE_VWHEEL: fov = CLAMP(fov+evt.mouse.dy, 1, 90); break;
  534.  
  535.       case EVENT_KEY_DOWN: if(!evt.key.repeat){
  536.         switch(evt.key.vkey){
  537.           case VKEY_UP   : move_forward = true; break;
  538.           case VKEY_LEFT : move_left    = true; break;
  539.           case VKEY_BACK : move_back    = true; break;
  540.           case VKEY_RIGHT: move_right   = true; break;
  541.           default: switch(evt.key.pkey){ // WASD (on my keyboard, at least)
  542.             case 0x11: move_forward = true; break;
  543.             case 0x1E: move_left    = true; break;
  544.             case 0x1F: move_back    = true; break;
  545.             case 0x20: move_right   = true; break;
  546.             default:;
  547.           }
  548.         }
  549.       } break;
  550.  
  551.       case EVENT_KEY_UP: {
  552.         switch(evt.key.vkey){
  553.           case VKEY_UP   : move_forward = false; break;
  554.           case VKEY_LEFT : move_left    = false; break;
  555.           case VKEY_BACK : move_back    = false; break;
  556.           case VKEY_RIGHT: move_right   = false; break;
  557.           default: switch(evt.key.pkey){ // WASD (on my keyboard, at least)
  558.             case 0x11: move_forward = false; break;
  559.             case 0x1E: move_left    = false; break;
  560.             case 0x1F: move_back    = false; break;
  561.             case 0x20: move_right   = false; break;
  562.             default:;
  563.           }
  564.         }
  565.       } break;
  566.  
  567.       default:;
  568.  
  569.     }
  570.  
  571.  
  572.  
  573.     if(is_cursor_trapped()){
  574.       if(!counterStart) counterStart = timeGetPerfCounter();
  575.  
  576.       s32 movement = 0;
  577.  
  578.       switch(move_left<<3|move_right<<2 | move_back<<1|move_forward){
  579.         case 0b1000:/*movement = 4; break;*/// l + 0 + ~~~~~
  580.         case 0b1011:  movement = 4; break;  // l + 0 + ~~~~~
  581.         case 0b0100:/*movement = 0; break;*/// 0 + r + ~~~~~
  582.         case 0b0111:  movement = 0; break;  // 0 + r + ~~~~~
  583.         case 0b0010:/*movement = 6; break;*/// 0 + 0 + b + 0
  584.         case 0b1110:  movement = 6; break;  // ~~~~~ + b + 0
  585.         case 0b0001:/*movement = 2; break;*/// 0 + 0 + 0 + f
  586.         case 0b1101:  movement = 2; break;  // ~~~~~ + 0 + f
  587.  
  588.         case 0b0101:  movement = 1; break;  // 0 + r + 0 + f
  589.         case 0b1001:  movement = 3; break;  // l + 0 + 0 + f
  590.         case 0b1010:  movement = 5; break;  // l + 0 + b + 0
  591.         case 0b0110:  movement = 7; break;  // 0 + r + b + 0
  592.  
  593.         default: movement = -1;
  594.  
  595.       }
  596.  
  597.       if(movement >= 0){
  598.         Fpoint2d delta = create_2d_vector(pos.z-(movement-2)*M_PI4f,(MOVE_SPEED));
  599.  
  600.         bool success = false;
  601.         Point2d newpos_int = {s32(pos.x+delta.x),s32(pos.y+delta.y)};
  602.         if(newpos_int.x >= 0  &&  newpos_int.x < map_w  &&
  603.            newpos_int.y >= 0  &&  newpos_int.y < map_h  &&
  604.            map[newpos_int.x + newpos_int.y*map_w] < 1)
  605.         {
  606.           pos.x += delta.x;
  607.           pos.y += delta.y;
  608.           success = true;
  609.         }
  610.  
  611.         if(success && (frame%(FOOTSTEP_DELAY))==5)
  612.           sfx.play(footstep0, .1,.1, 1.0+frand2()*.05);
  613.  
  614.       }
  615.  
  616.     }
  617.  
  618.     pos.z = fmodf(pos.z, M_2PIf);
  619.     if(pos.z < 0) pos.z += M_2PIf;
  620.  
  621.     Point2d pos_int = {s32(pos.x),s32(pos.y)};
  622.     if(map[pos_int.x + pos_int.y*map_w] == END_POINT) break; // You're winner!
  623.  
  624.  
  625.  
  626.     // Uncomment to activate the funny
  627.     /*
  628.     if(!(frame%8)){
  629.       Color24 replacementColor = pixels_palette[255];
  630.       for(int i=255; i>0; --i) pixels_palette[i] = pixels_palette[i-1];
  631.       pixels_palette[0] = replacementColor;
  632.       update_palette(0, 256);
  633.     }
  634.     */
  635.  
  636.     // This shifts the color of the finishing tile as a cool visual effect
  637.     Color24 newColor = hsv2rgb((f32)((frame*3)%360), 100.0f, 100.0f);
  638.     update_palette(254, 1, &newColor);
  639.  
  640.     // Draw stuff
  641.     //canvas_clear(64); // Redundant
  642.  
  643.     draw_raycast_scene(map, {map_w, map_h}, pos, textures,
  644.                        0x90, 0x14, 1000, fov);
  645.  
  646.     u64 counterDelta = (counterStart) ? timeGetPerfCounter()-counterStart : 0;
  647.     DRAW_TEXTF("TIME: %.1f",1,CANVAS_H-6*1,false, (f64)counterDelta/timeGetPerfFreq());
  648.     DRAW_TEXTF("FOV=%.0f",1,CANVAS_H-6*2-1,false, fov);
  649.     DRAW_TEXTF("%5.2f, %5.2f",1,CANVAS_H-6*3-1,false,  pos.x, pos.y);
  650.     #ifdef _DEBUG
  651.       DRAW_TEXTF("%5.2f",1,CANVAS_H-6*4-1,false, (pos.z/M_2PIf)*360.0f);
  652.     #endif
  653.  
  654.     #define ESC_NOTIF "PRESS ESCAPE!"
  655.     #define NOTIF_LEN (sizeof(ESC_NOTIF-1))
  656.     if(!is_cursor_trapped())
  657.       DRAW_TEXT_BOX(ESC_NOTIF, CENTERED_TEXT, CENTERED_TEXT, false);
  658.  
  659.     #if defined(_DEBUG) || SHOW_MINIMAP_ALWAYS==1
  660.       for(int y=0; y<map_h; ++y)
  661.       for(int x=0; x<map_w; ++x)
  662.       {
  663.         fill_rect(map_scale*x, map_scale*(map_h-1-y),
  664.                   map_scale, map_scale,
  665.                   map[x + y*map_w]<<5);
  666.       }
  667.       fill_rect(map_scale*START_X, map_scale*(map_h-1-START_Y),
  668.                 map_scale, map_scale, 0b00111000);
  669.       fill_rect(map_scale*(map_w-2), map_scale*(1),
  670.                 map_scale, map_scale, 0b00000111);
  671.       pos_int = {s32(pos.x),map_h-1-s32(pos.y)};
  672.       fill_rect(map_scale*pos_int.x, map_scale*pos_int.y,
  673.                 map_scale, map_scale, 128 + ((frame>>5)&1)*127);
  674.     #endif
  675.  
  676.     // Frames take about 16ms to approximate 60fps
  677.     canvas_present(true);
  678.     timeSleep(16);
  679.     ++frame;
  680.  
  681.   }
  682.  
  683.  
  684.  
  685.  
  686.  
  687.   u64 counterEnd = timeGetPerfCounter();
  688.   f64 timeDelta = (f64)(counterEnd-counterStart)/timeGetPerfFreq();
  689.   sfx.play(sinewave, .1,.1);
  690.  
  691.   bool end_screen = true;
  692.  
  693.   while(end_screen){
  694.     while(pollEvent(&evt))
  695.     switch(evt.type){
  696.       case EVENT_QUIT: goto _lbl_exit;
  697.       case EVENT_MOUSE_VWHEEL: {
  698.         s32 newSize = CLAMP(map_w+s32(evt.mouse.dy)*2, 9, MAP_MAX);
  699.         map_w = map_h = newSize;
  700.       } break;
  701.       case EVENT_MOUSE_DOWN: {
  702.         _printf("%X\n",evt.mouse.button);
  703.         if(evt.mouse.button&2/*MBUTTON_RIGHT*/) end_screen = false;
  704.       } break;
  705.     }
  706.  
  707.     Color24 newColor = hsv2rgb((f32)((frame*1)%720)/2, 100.0f, 100.0f);
  708.     update_palette(254, 1, &newColor);
  709.     canvas_clear(254);
  710.  
  711.     DRAW_TEXTF_BOX_1("Final Time: %.4f seconds!", CENTERED_TEXT, CENTERED_TEXT, false, timeDelta);
  712.  
  713.     DRAW_TEXT_BOX("(Right click to play again!)",
  714.                   CENTERED_TEXT, CANVAS_H/2-12, false);
  715.  
  716.     canvas_present(true);
  717.     timeSleep(16);
  718.     ++frame;
  719.  
  720.   }
  721.  
  722.  
  723.  
  724.   goto _lbl_start;
  725.  
  726. }
  727. /******************************************************************************/
  728. /******************************************************************************/
  729. //"gdi_maze_game_2025-04-25\src\kit_utils\draw\blit.cpp":
  730. #include <public_stuff.hpp>
  731. #ifdef BLIT_USED
  732.  
  733.  
  734.  
  735. // Blits a bitmap, where the first two bytes are its width and height,
  736. // the third byte corresponding to its transparent color,
  737. // the fourth byte corresponding to flags (unused by this function),
  738. // the fifth and sixth bytes are position offsets (of type s8),
  739. // the seventh and eighth bytes are padding and are currently unused,
  740. // with the bytes after being the pixels.
  741. void blit(s32 x, s32 y, const Color8* src, Color8* dst){
  742.   // Bitmap cannot be given as NULL
  743.   if(src ==  nullptr) return;
  744.  
  745.   // Apply offsets before doing any position bounds checking
  746.   x += (s8)src[4].v;
  747.   y += (s8)src[5].v;
  748.  
  749.   // Don't draw if source bitmap would appear off-screen anyway
  750.   if(x >= CANVAS_W) return;
  751.   if(y >= CANVAS_H) return;
  752.  
  753.  
  754.  
  755.   const s32 w = (s32)(src[0].v);
  756.   const s32 h = (s32)(src[1].v);
  757.  
  758.   // Don't draw if source bitmap would appear off-screen anyway
  759.   if(x <= -w) return;
  760.   if(y <= -h) return;
  761.   // Bitmap's width and/or height cannot be 0
  762.   if(!w || !h) return;
  763.  
  764.  
  765.  
  766.   const s32 x_start  =  MAX(x, 0);
  767.   const s32 y_start  =  MAX(y, 0);
  768.  
  769.   const s32 x_end  =  MIN(x+w, CANVAS_W);
  770.   const s32 y_end  =  MIN(y+h, CANVAS_H);
  771.  
  772.   const Color8 transparent = src[2];
  773.  
  774.   src += 8;
  775.   if(x<0) src -= x;
  776.   if(y<0) src -= y*w;
  777.  
  778.   dst  +=  MIN(x_start, CANVAS_W-1);
  779.   dst  +=  MIN(y_start, CANVAS_H-1) * CANVAS_W;
  780.  
  781.  
  782.  
  783.   for(y=y_start; y<y_end; ++y){
  784.     s32 column = 0;
  785.  
  786.     for(x=x_start; x<x_end; ++x){
  787.       Color8 pxl = src[column];
  788.  
  789.       if(pxl.v != transparent.v)
  790.         dst[column].v = pxl.v;
  791.  
  792.       ++column;
  793.  
  794.     }
  795.  
  796.     src += w;
  797.     dst += CANVAS_W;
  798.  
  799.   }
  800.  
  801. }
  802.  
  803.  
  804.  
  805.  
  806.  
  807. #endif
  808. /******************************************************************************/
  809. /******************************************************************************/
  810. //"gdi_maze_game_2025-04-25\src\kit_utils\draw\draw_line.cpp":
  811. #include <public_stuff.hpp>
  812. #ifdef DRAW_LINE_USED
  813.  
  814.  
  815.  
  816. void draw_line(s32 x_0, s32 y_0,
  817.                s32 x_1, s32 y_1,
  818.                Color8 color,
  819.                Color8* dst)
  820. {
  821.   const s32 d_x =  abs(x_1 - x_0);
  822.   const s32 d_y = -abs(y_1 - y_0);
  823.   const s32 s_x = (x_0<x_1) ? 1 : -1;
  824.   const s32 s_y = (y_0<y_1) ? 1 : -1;
  825.   const s32 s_y_row = s_y * CANVAS_W;
  826.  
  827.   s32 err = d_x + d_y;
  828.  
  829.   dst  +=  x_0 + y_0*CANVAS_W;
  830.  
  831.  
  832.  
  833.   while(true){
  834.     if(  (x_0 >= 0)  &&  (x_0 < CANVAS_W)  &&
  835.          (y_0 >= 0)  &&  (y_0 < CANVAS_H)  )
  836.     {
  837.       dst->v = color.v;
  838.     }
  839.  
  840.     if(x_0 == x_1  &&  y_0 == y_1) break;
  841.     const s32 err2 = err<<1;
  842.  
  843.     if(err2 >= d_y){
  844.       err += d_y;
  845.       x_0 += s_x;
  846.       dst += s_x;
  847.     }
  848.  
  849.     if(err2 <= d_x){
  850.       err += d_x;
  851.       y_0 += s_y;
  852.       dst += s_y_row;
  853.     }
  854.  
  855.   }
  856.  
  857. }
  858.  
  859.  
  860.  
  861.  
  862.  
  863. #endif
  864. /******************************************************************************/
  865. /******************************************************************************/
  866. //"gdi_maze_game_2025-04-25\src\kit_utils\draw\draw_rect.cpp":
  867. #include <public_stuff.hpp>
  868. #ifdef DRAW_RECT_USED
  869.  
  870.  
  871.  
  872. void draw_rect(s32 x, s32 y,
  873.                s32 w, s32 h,
  874.                Color8 color,
  875.                Color8* dst)
  876. {
  877.   if(!w || !h) return;
  878.  
  879.   if(x >= CANVAS_W) return;
  880.   if(y >= CANVAS_H) return;
  881.  
  882.   if(x <= -w) return;
  883.   if(y <= -h) return;
  884.  
  885.  
  886.  
  887.   const s32 x_start  =  MAX(x, 0);
  888.   const s32 y_start  =  MAX(y, 0);
  889.  
  890.   const s32 x_upper  =  x + w; // Upper boundary
  891.   const s32 y_upper  =  y + h;
  892.  
  893.   const s32 x_end  =  MIN(x_upper, CANVAS_W);
  894.   const s32 y_end  =  MIN(y_upper, CANVAS_H);
  895.  
  896.  
  897.  
  898.   Color8* n_w  =  dst;
  899.   n_w  +=  MIN(x_start, CANVAS_W-1);
  900.   n_w  +=  MIN(y_start, CANVAS_H-1) * CANVAS_W;
  901.  
  902.   Color8* n_e  =  n_w + ((x_end-x_start)-1);
  903.   Color8* s_w  =  n_w + ((y_end-y_start)-1)*CANVAS_W;
  904.  
  905.  
  906.  
  907.   // From north-west, going down
  908.   if(x >= 0){
  909.     Color8* _n_w  =  n_w;
  910.     for(s32 _y=y_start; _y<y_end; ++_y){
  911.       _n_w->v = color.v;
  912.       _n_w += CANVAS_W;
  913.     }
  914.  
  915.   }
  916.  
  917.  
  918.  
  919.   // From north-west, going right
  920.   if(y >= 0){
  921.     for(x=x_start; x<x_end; ++x)
  922.       (n_w++)->v = color.v;
  923.  
  924.   }
  925.  
  926.  
  927.  
  928.   // From north-east, going down
  929.   if(x_upper < (CANVAS_W+1)){
  930.     for(y=y_start; y<y_end; ++y){
  931.       n_e->v = color.v;
  932.       n_e += CANVAS_W;
  933.     }
  934.  
  935.   }
  936.  
  937.  
  938.  
  939.   // From south-west, going right
  940.   if(y_upper < (CANVAS_H+1)){
  941.     for(x=x_start; x<x_end; ++x)
  942.       (s_w++)->v = color.v;
  943.  
  944.   }
  945.  
  946. }
  947.  
  948.  
  949.  
  950.  
  951.  
  952. #endif
  953. /******************************************************************************/
  954. /******************************************************************************/
  955. //"gdi_maze_game_2025-04-25\src\kit_utils\draw\draw_text.cpp":
  956. #include <public_stuff.hpp>
  957. #ifdef DRAW_TEXT_USED
  958.  
  959.  
  960.  
  961. void draw_char(u8 chr, s32 x, s32 y,
  962.                const Color8* font,
  963.                Color8* dst)
  964. {
  965.   if(font == nullptr  ||  dst  == nullptr) return;
  966.   if(x >= CANVAS_W  ||  y >= CANVAS_H) return;
  967.  
  968.  
  969.  
  970.   const s32 w = font[0].v;
  971.   const s32 h = font[1].v;
  972.  
  973.   if(x <= -w  ||  y <= -h) return;
  974.  
  975.   const Color8 t = font[2]; // Transparent color
  976.  
  977.  
  978.  
  979.   const s32 x_start = MAX(x, 0);
  980.   const s32 y_start = MAX(y, 0);
  981.  
  982.   const s32 x_end = MIN(x+w, CANVAS_W);
  983.   const s32 y_end = MIN(y+h, CANVAS_H);
  984.  
  985.   const s32 x_span = x_end-x_start;
  986.   const s32 y_span = y_end-y_start;
  987.  
  988.  
  989.  
  990.   font += 4 + chr * w*h; // Seek to start of char data
  991.  
  992.   if(x >= 0) dst  += x;
  993.   else       font -= x; // -= because x is negative
  994.  
  995.   if(y >= 0) dst  += y*CANVAS_W;
  996.   else       font -= y*w;
  997.  
  998.  
  999.  
  1000.   for(y=0; y<y_span; ++y){
  1001.     for(x=0; x<x_span; ++x){
  1002.       if(font[x].v != t.v)
  1003.         dst[x] = font[x];
  1004.     }
  1005.  
  1006.     font += w;
  1007.     dst  += CANVAS_W;
  1008.  
  1009.   }
  1010.  
  1011. }
  1012.  
  1013.  
  1014.  
  1015.  
  1016.  
  1017. #define ERR_RECT TEXT_ERROR_RECT
  1018.  
  1019. Rect2d get_text_rect(const char* str,
  1020.                      s32 x, s32 y,
  1021.                      bool use_negative_position_margin,
  1022.                      const Color8* font)
  1023. {
  1024.   if(str == nullptr  ||  font == nullptr) return ERR_RECT;
  1025.  
  1026.   u8 chr;
  1027.   const u8* text = (const u8*)str;
  1028.   s32 maxWidth = 0;
  1029.  
  1030.   s32 w = font[0].v;
  1031.   s32 h = font[1].v;
  1032.  
  1033.   // Add 1 pixel of space per char and scanline respectively
  1034.   ++w, ++h;
  1035.  
  1036.   // Seek to start of font data
  1037.   font += 4;
  1038.  
  1039.   Rect2d rect = {0, 0, 0, h*(text[0]!=0)};
  1040.  
  1041.  
  1042.  
  1043.   // Calculate text size
  1044.  
  1045.   while( (chr=*(text++)) )
  1046.   if(chr != '\n'){
  1047.     rect.w += w;
  1048.  
  1049.   } else { // chr == '\n'
  1050.     if(maxWidth < rect.w) maxWidth = rect.w;
  1051.     rect.w  = 0;
  1052.     rect.h += h;
  1053.  
  1054.   }
  1055.  
  1056.   if(rect.w < maxWidth) rect.w = maxWidth;
  1057.  
  1058.   // Remove last bit of spacing from final rect
  1059.   --rect.w;
  1060.   --rect.h;
  1061.  
  1062.  
  1063.  
  1064.   // Calculate text position
  1065.  
  1066.   if(x == CENTERED_TEXT){
  1067.     rect.x = CANVAS_W/2 - rect.w/2;
  1068.   } else if(x < 0  &&  use_negative_position_margin){
  1069.     rect.x = CANVAS_W - rect.w + x + 1;
  1070.   } else {
  1071.     rect.x = x;
  1072.   }
  1073.  
  1074.   if(y == CENTERED_TEXT){
  1075.     rect.y = CANVAS_H/2 - rect.h/2;
  1076.   } else if(y < 0  &&  use_negative_position_margin){
  1077.     rect.y = CANVAS_H - rect.h + y + 1;
  1078.   } else {
  1079.     rect.y = y;
  1080.   }
  1081.  
  1082.  
  1083.  
  1084.   return rect;
  1085.  
  1086. }
  1087.  
  1088.  
  1089.  
  1090.  
  1091.  
  1092. // Returns final rect of the text
  1093. Rect2d draw_text(const char* str, s32 x, s32 y, u32 maxLength,
  1094.                  bool use_negative_position_margin,
  1095.                  const Color8* font, Color8* dst)
  1096. {
  1097.   Rect2d rect = get_text_rect(str, x, y, use_negative_position_margin, font);
  1098.   if(rect.h == -1  ||  dst == nullptr) return ERR_RECT;
  1099.  
  1100.   x = rect.x;
  1101.   y = rect.y;
  1102.  
  1103.  
  1104.  
  1105.   u8 chr;
  1106.   s32 chr_w = font[0].v;
  1107.   s32 chr_h = font[1].v;
  1108.  
  1109.   const u8* text = (const u8*)str;
  1110.  
  1111.   s32 x_start = x;
  1112.  
  1113.   // Since GDI bitmaps are drawn bottom-up, the start of the text is actually
  1114.   // its upper boundary, minus the font's glyph height
  1115.   y += rect.h - chr_h;
  1116.  
  1117.   ++chr_w, ++chr_h;
  1118.  
  1119.  
  1120.  
  1121.   // This effectively uncaps the length
  1122.   if(!maxLength) --maxLength;
  1123.  
  1124.   while( (chr=*(text++)) && (maxLength--) )
  1125.   if(chr != '\n'){
  1126.     draw_char(chr, x, y, font, dst);
  1127.     x += chr_w;
  1128.  
  1129.   } else { //chr == '\n'
  1130.     x  = x_start;
  1131.     y -= chr_h;
  1132.  
  1133.   }
  1134.  
  1135.  
  1136.  
  1137.   return rect;
  1138.  
  1139. }
  1140.  
  1141.  
  1142.  
  1143.  
  1144.  
  1145. #endif
  1146.  
Add Comment
Please, Sign In to add comment