Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /******************************************************************************/
- /******************************************************************************/
- //"gdi_maze_game_2025-04-25\src\draw_raycast_scene.cpp":
- #include <public_stuff.hpp>
- #include "draw_raycast_scene.hpp"
- void fill_scanlines(u32 y, u32 len, Color8 color,
- Color8* dst)
- {
- if(y >= CANVAS_H) return;
- if(len == 0 ) return;
- if(dst == nullptr ) return;
- if((y+len) >= CANVAS_H) len = CANVAS_H-y;
- memSet((&dst->v) + y*CANVAS_W, color.v, len*CANVAS_W);
- }
- #define pos_angle pos.z
- void draw_raycast_scene(const s32* map, Point2d map_size,
- Fpoint3d pos, // (pos.z is the angle, in radians)
- const Color8 tiles[][RAYCAST_TEX_SIZE_TOTAL],
- Color8 colorCeiling, Color8 colorFloor,
- u32 renderDistance, f32 fov,
- Color8* dst)
- {
- if(map == nullptr || dst == nullptr) return;
- Fpoint2d dir = create_2d_vector(pos_angle, 1.0f);
- // Calculate camera plane's orientation and size
- // (An fov larger than 90 is inaccurate!)
- const f32 plane_angle = M_PI2f + pos_angle; // = pi/2 + pos.z
- const f32 plane_magnitude = (f32)CLAMP(fov, 1e-100, 90) / 90;
- Fpoint2d plane = create_2d_vector(plane_angle, plane_magnitude);
- Fpoint2d rayDir = {0.0f, 0.0f}; // Current ray's direction
- Fpoint2d deltaDist = {0.0f, 0.0f}; // Length of ray from one x or y-side to next x or y-side
- Fpoint2d sideDist = {0.0f, 0.0f}; // Length of ray from current position to next x or y-side
- // Draw the floor and ceiling respectively
- // (floor is at y=0, since GDI bitmaps are drawn bottom-up)
- fill_scanlines( 0, CANVAS_H/2, colorFloor, dst);
- fill_scanlines(CANVAS_H/2, CANVAS_H/2, colorCeiling, dst);
- if(renderDistance == 0) return; // Lol
- // For each column of pixels
- for(s32 x=0; x<CANVAS_W; ++x){
- // The tile the player's currently in
- Point2d pos_map = {s32(pos.x), s32(pos.y)};
- // x-coordinate along the camera plane
- f32 plane_offset = f32(x*2)/CANVAS_W - 1.0f; // -1.0f -> 1.0f
- // Ray's direction and position
- rayDir.x = dir.x + plane.x*plane_offset;
- rayDir.y = dir.y + plane.y*plane_offset;
- // (Replace number with 1e30 if it would otherwise divide by 0)
- deltaDist.x = (rayDir.x) ? fabsf(1.0f/rayDir.x) : 1e30;
- deltaDist.y = (rayDir.y) ? fabsf(1.0f/rayDir.y) : 1e30;
- // Calculate step and initial sideDist
- Point2d step; // What direction to step in x or y-direction (either -1 or 1)
- if(rayDir.x < 0){
- step.x = -1;
- sideDist.x = deltaDist.x * (pos.x - pos_map.x);
- } else {
- step.x = 1;
- sideDist.x = deltaDist.x * (pos_map.x+1 - pos.x);
- }
- if(rayDir.y < 0){
- step.y = -1;
- sideDist.y = (pos.y - pos_map.y) * deltaDist.y;
- } else {
- step.y = 1;
- sideDist.y = (pos_map.y+1 - pos.y) * deltaDist.y;
- }
- // = 'Was an EW (horizontal) or NS (vertical) tile hit?'
- bool sideVert;
- // = 'Which tile did the current ray hit?' (0 means it hit nothing at all!)
- s32 tileHit = 0;
- // For each step in the current ray (perform DDA)
- for(u32 i=0; i<renderDistance; ++i){
- // Jump to next map square, either in the x-direction, or the y-direction
- if(sideDist.x < sideDist.y){
- sideDist.x += deltaDist.x;
- pos_map.x += step.x;
- sideVert = false;// Closest tile is horizontal
- } else{
- sideDist.y += deltaDist.y;
- pos_map.y += step.y;
- sideVert = true; // Closest tile is vertical
- }
- s32 tileWhich = pos_map.x + pos_map.y*map_size.x;
- // Check if ray has hit a wall
- if(pos_map.x >= 0 && pos_map.x < map_size.x &&
- pos_map.y >= 0 && pos_map.y < map_size.y &&
- map[tileWhich] !=/*>*/ 0)
- {
- tileHit = map[tileWhich];
- break;
- }
- }
- // Calculate distance projected on camera direction,
- // because raw euclidean distance would give as fisheye effect.
- //
- // (No joke, this was the hardest part of the raycasting tutorial to understand!)
- f32 perpWallDist; // (AKA the final length of the current ray)
- if(!sideVert) perpWallDist = (sideDist.x - deltaDist.x);
- else perpWallDist = (sideDist.y - deltaDist.y);
- // Where exactly the wall was hit
- f32 wallX;
- if(!sideVert) wallX = pos.y + perpWallDist * rayDir.y;
- else wallX = pos.x + perpWallDist * rayDir.x;
- wallX -= floor(wallX); // wallX %= 1.0f
- // x coordinate on the texture
- // (0.0f -> 1.0f, which is what draw_vslice uses for this parameter)
- f32 texX = wallX;
- if( !sideVert && rayDir.x > 0) texX = 1.0f - texX;
- else if(sideVert && rayDir.y < 0) texX = 1.0f - texX;
- // I don't know why but draw_vslice is rendering the textures backwards
- texX = 1.0f-texX;
- // A multiplier for the height of the pixel column,
- // relative to the height of the framebuffer
- // (0.0f -> 1.0f, which is what draw_vslice uses for this parameter)
- f32 columnHeight = 1.0f/perpWallDist;
- // -1 is applied to tileHit so that tiles[0] can be accessed
- /* if(tileHit > 0) */
- if(tileHit != 0){
- if(tileHit < 0){ tileHit = -tileHit; }
- draw_vslice(x, columnHeight, texX, tiles[tileHit-1], dst);
- }
- }
- }
- /******************************************************************************/
- /******************************************************************************/
- //"gdi_maze_game_2025-04-25\src\generate_maze.cpp":
- #include <public_stuff.hpp>
- #include "generate_maze.hpp"
- // Direction vectors (up, right, down, left)
- const s32 d_x[] = { 0, 1, 0, -1};
- const s32 d_y[] = {-1, 0, 1, 0};
- static void carve(s32 x, s32 y,
- s32* map, s32 w, s32 h)
- {
- s32 dir[] = {0, 1, 2, 3};
- // Shuffle dir
- for(u32 i=3; i>0; --i){
- u32 j = rand() % (i+1);
- s32 tmp = dir[i];
- dir[i] = dir[j];
- dir[j] = tmp;
- }
- for(s32 i=0; i<4; ++i){
- s32 cur = dir[i];
- s32 nx = x + d_x[cur] * 2;
- s32 ny = y + d_y[cur] * 2;
- s32 which_a = nx + ny*w;
- if(nx > 0 && nx < w &&
- ny > 0 && ny < h &&
- map[which_a] != 0)
- {
- s32 which_b = nx-d_x[cur] + (ny-d_y[cur])*w;
- map[which_b] = 0;
- map[which_a] = 0;
- carve(nx, ny, map, w, h);
- }
- }
- }
- // w & h must be odd, otherwise the south-eastern edges will be carved!
- bool generate_maze(s32* map, u32 w, u32 h,
- s32 fillValue)
- {
- if(w > INT_MAX || h > INT_MAX) return false;
- if(fillValue == 0) return false;
- // Initialize all cells to fill value,
- // or at random up to -fillValue if negative
- u32 numCells = w*h;
- if(fillValue > 0){
- for(u32 i=0; i<numCells; ++i) map[i] = fillValue;
- } else {
- fillValue = -fillValue;
- for(u32 i=0; i<numCells; ++i) map[i] = 1+rand()%fillValue;
- }
- map[START_X + START_Y*w] = 0;
- carve(START_X, START_Y, map, w, h);
- return true;
- }
- /******************************************************************************/
- /******************************************************************************/
- //"gdi_maze_game_2025-04-25\src\user_audio.cpp":
- #include <public_stuff.hpp>
- SoundEngine sfx;
- void user_audio(StereoF32* stream, u32 len, u32 sampleRate, f64 timeStamp)
- {
- // This plays a normal 440Hz sine wave (commented!)
- /*
- // The static keyword here just makes the variable persistent, in that
- // its state is preserved between each call to user_audio
- static u64 t = 0; // u[nsigned] 64-bit integer
- for(u32 i=0; i<len; ++i){ //For every sample to be filled in the audio buffer
- // t is incremented every sample, and is divided by the sample rate to
- // basically make a percentage of how far it is through a second.
- // This is then multiplied by 2pi, which effectively makes it oscillate
- // once every second if you fed cycle into sin directly
- f32 cycle = ((f32)(t++)/sampleRate) * M_2PIf;
- // cycle is multiplied by 440 so that it oscillates 440 times a second
- // instead of just once.
- // (Also, sinF is just sin but it uses 32-bit f[loats] instead of 64-bit,
- // and unlike the actual sinf, this is a custom implementation)
- f32 smp = sinF(cycle*440); // This ofc returns a number from -1.0 to 1.0
- // 20% volume
- smp *= 0.20f;
- // Since the audio stream is stereo, the sample is applied to
- // both 'ears' so-to-speak.
- stream[i].l = smp;
- stream[i].r = smp;
- }
- // Unnecessary, since a u64 is so big, but this makes it so that
- // even if it was a smaller integer size, it will never overflow.
- // (The % operator does a division, but gets the remainder instead)
- t %= sampleRate;
- */
- sfx.sampleRate = sample_rate;
- if(!sfx.mixTracks(stream, len, timeStamp)){
- _printf("ERROR: sfx.mixTracks failed!\n");
- }
- }
- /******************************************************************************/
- /******************************************************************************/
- //"gdi_maze_game_2025-04-25\src\user_main.cpp":
- #include <public_stuff.hpp>
- #include "draw_raycast_scene.hpp"
- #include "generate_maze.hpp"
- #include "map_and_textures.hpp"
- extern SoundEngine sfx;
- //draw_textf_box is function is broken with CENTERED_TEXT
- //todo: fix it inside the template before moving it to a separate source file
- #define DRAW_TEXT_BOX(_fmt, _x, _y, _npm) \
- { \
- Rect2d textRect = draw_textf(_fmt, (_x), (_y), 0, (_npm), font_pico8, pixels); \
- if(textRect.h != -1){ \
- textRect.x -= 2, textRect.y -= 2; \
- textRect.w += 4, textRect.h += 4; \
- fill_rect(textRect.x, textRect.y, textRect.w, textRect.h, 0); \
- draw_rect(textRect.x, textRect.y, textRect.w, textRect.h, 192); \
- textRect = draw_textf(_fmt, (_x), (_y), 0, (_npm), font_pico8, pixels); \
- } \
- }
- //FUCK YOU GCC FUCK YOU FUCK YOU FUCK YOU
- #define DRAW_TEXTF_BOX_1(_fmt, _x, _y, _npm, value) \
- { \
- Rect2d textRect = draw_textf(_fmt, (_x), (_y), 0, (_npm), font_pico8, pixels, value); \
- if(textRect.h != -1){ \
- textRect.x -= 2, textRect.y -= 2; \
- textRect.w += 4, textRect.h += 4; \
- fill_rect(textRect.x, textRect.y, textRect.w, textRect.h, 0); \
- draw_rect(textRect.x, textRect.y, textRect.w, textRect.h, 192); \
- textRect = draw_textf(_fmt, (_x), (_y), 0, (_npm), font_pico8, pixels, value); \
- } \
- }
- #define DRAW_TEXTF_BOX_2(_fmt, _x, _y, _npm, value_a, value_b) \
- { \
- Rect2d textRect = draw_textf(_fmt, (_x), (_y), 0, (_npm), font_pico8, pixels, value_a, value_b); \
- if(textRect.h != -1){ \
- textRect.x -= 2, textRect.y -= 2; \
- textRect.w += 4, textRect.h += 4; \
- fill_rect(textRect.x, textRect.y, textRect.w, textRect.h, 0); \
- draw_rect(textRect.x, textRect.y, textRect.w, textRect.h, 192); \
- textRect = draw_textf(_fmt, (_x), (_y), 0, (_npm), font_pico8, pixels, value_a, value_b); \
- } \
- }
- Color24 hsv2rgb(f32 h, f32 s, f32 v){
- f32 r, g, b;
- // Normalize values
- h /= 360;
- s /= 100;
- v /= 100;
- // The rest is magic, idk
- signed int i = floor(h*6);
- f32 f = h * 6 - i;
- f32 p = v * (1.0f - s);
- f32 q = v * (1.0f - f * s);
- f32 t = v * (1.0f - (1.0f-f) * s);
- switch(i % 6){
- case 0: r = v, g = t, b = p; break;
- case 1: r = q, g = v, b = p; break;
- case 2: r = p, g = v, b = t; break;
- case 3: r = p, g = q, b = v; break;
- case 4: r = t, g = p, b = v; break;
- case 5: r = v, g = p, b = q; break;
- }
- // It should be impossible for r,g,b to be uninitialized
- // due to how the switch is set up, so the warning can be ignored.
- #if defined(__GNUC__)
- #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
- #pragma GCC diagnostic push
- // TBD: do macro checks for other compilers
- #endif
- Color24 out;
- out.r = r*255;
- out.g = g*255;
- out.b = b*255;
- out._ = 0;
- return out;
- #if defined(__GNUC__)
- #pragma GCC diagnostic pop
- #endif
- }
- #define SHOW_MINIMAP_ALWAYS 1
- Event evt;
- f32 fov = 90;
- u64 frame = 0;
- #define FOOTSTEP_DELAY 20 // In frames
- #define MOVE_SPEED 1.0f/FOOTSTEP_DELAY
- s32 map_w = 9;
- s32 map_h = 9;
- int user_main(int argc, char** argv){
- if(0){ _lbl_exit:
- sfx.stop();
- while(sfx.getActiveTracks() > 0) timeSleep(5);
- return 0;
- }
- #define XOR_WHICH 3
- textures[XOR_WHICH][0] = textures[XOR_WHICH][1] = TEX_SIZE;
- for(int y=0; y<TEX_SIZE; ++y)
- for(int x=0; x<TEX_SIZE; ++x)
- {
- textures[XOR_WHICH][ 8 + x + y*TEX_SIZE ] = (x^y)<<1;
- }
- AudioData footstep0(SMPFMT_S16, sizeof(footstep0_data)/2, 1, 22050);
- memCpy(footstep0.hdr->samples, footstep0_data, sizeof(footstep0_data));
- footstep0.convertFormat(SMPFMT_F32);
- u32 numSamples = f32(sample_rate)*0.60f; //600ms
- AudioData sinewave(SMPFMT_F32, numSamples, 1, sample_rate);
- f32* sinewave_smp = (f32*)sinewave.hdr->samples;
- for(u32 i=0; i<numSamples; ++i){
- f32 cycle = (f32(i)/sample_rate) * M_2PIf;
- sinewave_smp[i] = sinf(cycle*880);
- sinewave_smp[i] *= 1.0f-(f32(i)/numSamples);
- }
- numSamples /= 40;
- for(u32 i=0; i<numSamples; ++i)
- sinewave_smp[i] *= f32(i)/numSamples;
- /******************************************************************************/
- _lbl_start:
- bool selecting_size = true;
- while(selecting_size){
- while(pollEvent(&evt))
- switch(evt.type){
- case EVENT_QUIT: goto _lbl_exit;
- case EVENT_MOUSE_VWHEEL: {
- s32 newSize = CLAMP(map_w+s32(evt.mouse.dy)*2, 9, MAP_MAX);
- map_w = map_h = newSize;
- } break;
- case EVENT_MOUSE_DOWN: {
- if(evt.mouse.button&MBUTTON_LEFT) selecting_size = false;
- } break;
- }
- Color24 newColor = hsv2rgb((f32)((frame*1)%720)/2, 100.0f, 100.0f);
- update_palette(254, 1, &newColor);
- canvas_clear(254);
- DRAW_TEXTF_BOX_2("Map Size: %ix%i", CENTERED_TEXT, CENTERED_TEXT, false, map_w, map_h);
- DRAW_TEXT_BOX("(Scroll wheel to change size; Left click to start!)",
- CENTERED_TEXT, CANVAS_H/2-12, false);
- canvas_present(true);
- timeSleep(16);
- ++frame;
- }
- #define NUM_ACTIVE_TEX 4
- s32 fillValue = rand()%(NUM_ACTIVE_TEX+1);
- if(fillValue == 0) fillValue = -NUM_ACTIVE_TEX; // 0 for Random selection
- generate_maze(map, map_w, map_h, fillValue);
- // Fill end point with the right tile
- #define END_POINT -5 // Negative means 'render without collision'
- map[map_w-2 + (map_h-2)*map_w] = END_POINT;
- #if defined(_DEBUG) || SHOW_MINIMAP_ALWAYS==1
- s32 map_scale = (map_w < 27) ? 2 : 1;
- #endif
- Fpoint3d pos = {START_X+0.5f, START_Y+0.5f, 0.0f};
- u64 counterStart = 0;
- bool move_forward = false;
- bool move_back = false;
- bool move_left = false;
- bool move_right = false;
- while(1){
- //Handle events
- while(pollEvent(&evt))
- switch(evt.type){
- case EVENT_QUIT: goto _lbl_exit;
- case EVENT_MOUSE_MOVED: if(is_cursor_trapped()){
- pos.z += (evt.mouse.dx/1000)*2;
- } break;
- case EVENT_MOUSE_VWHEEL: fov = CLAMP(fov+evt.mouse.dy, 1, 90); break;
- case EVENT_KEY_DOWN: if(!evt.key.repeat){
- switch(evt.key.vkey){
- case VKEY_UP : move_forward = true; break;
- case VKEY_LEFT : move_left = true; break;
- case VKEY_BACK : move_back = true; break;
- case VKEY_RIGHT: move_right = true; break;
- default: switch(evt.key.pkey){ // WASD (on my keyboard, at least)
- case 0x11: move_forward = true; break;
- case 0x1E: move_left = true; break;
- case 0x1F: move_back = true; break;
- case 0x20: move_right = true; break;
- default:;
- }
- }
- } break;
- case EVENT_KEY_UP: {
- switch(evt.key.vkey){
- case VKEY_UP : move_forward = false; break;
- case VKEY_LEFT : move_left = false; break;
- case VKEY_BACK : move_back = false; break;
- case VKEY_RIGHT: move_right = false; break;
- default: switch(evt.key.pkey){ // WASD (on my keyboard, at least)
- case 0x11: move_forward = false; break;
- case 0x1E: move_left = false; break;
- case 0x1F: move_back = false; break;
- case 0x20: move_right = false; break;
- default:;
- }
- }
- } break;
- default:;
- }
- if(is_cursor_trapped()){
- if(!counterStart) counterStart = timeGetPerfCounter();
- s32 movement = 0;
- switch(move_left<<3|move_right<<2 | move_back<<1|move_forward){
- case 0b1000:/*movement = 4; break;*/// l + 0 + ~~~~~
- case 0b1011: movement = 4; break; // l + 0 + ~~~~~
- case 0b0100:/*movement = 0; break;*/// 0 + r + ~~~~~
- case 0b0111: movement = 0; break; // 0 + r + ~~~~~
- case 0b0010:/*movement = 6; break;*/// 0 + 0 + b + 0
- case 0b1110: movement = 6; break; // ~~~~~ + b + 0
- case 0b0001:/*movement = 2; break;*/// 0 + 0 + 0 + f
- case 0b1101: movement = 2; break; // ~~~~~ + 0 + f
- case 0b0101: movement = 1; break; // 0 + r + 0 + f
- case 0b1001: movement = 3; break; // l + 0 + 0 + f
- case 0b1010: movement = 5; break; // l + 0 + b + 0
- case 0b0110: movement = 7; break; // 0 + r + b + 0
- default: movement = -1;
- }
- if(movement >= 0){
- Fpoint2d delta = create_2d_vector(pos.z-(movement-2)*M_PI4f,(MOVE_SPEED));
- bool success = false;
- Point2d newpos_int = {s32(pos.x+delta.x),s32(pos.y+delta.y)};
- if(newpos_int.x >= 0 && newpos_int.x < map_w &&
- newpos_int.y >= 0 && newpos_int.y < map_h &&
- map[newpos_int.x + newpos_int.y*map_w] < 1)
- {
- pos.x += delta.x;
- pos.y += delta.y;
- success = true;
- }
- if(success && (frame%(FOOTSTEP_DELAY))==5)
- sfx.play(footstep0, .1,.1, 1.0+frand2()*.05);
- }
- }
- pos.z = fmodf(pos.z, M_2PIf);
- if(pos.z < 0) pos.z += M_2PIf;
- Point2d pos_int = {s32(pos.x),s32(pos.y)};
- if(map[pos_int.x + pos_int.y*map_w] == END_POINT) break; // You're winner!
- // Uncomment to activate the funny
- /*
- if(!(frame%8)){
- Color24 replacementColor = pixels_palette[255];
- for(int i=255; i>0; --i) pixels_palette[i] = pixels_palette[i-1];
- pixels_palette[0] = replacementColor;
- update_palette(0, 256);
- }
- */
- // This shifts the color of the finishing tile as a cool visual effect
- Color24 newColor = hsv2rgb((f32)((frame*3)%360), 100.0f, 100.0f);
- update_palette(254, 1, &newColor);
- // Draw stuff
- //canvas_clear(64); // Redundant
- draw_raycast_scene(map, {map_w, map_h}, pos, textures,
- 0x90, 0x14, 1000, fov);
- u64 counterDelta = (counterStart) ? timeGetPerfCounter()-counterStart : 0;
- DRAW_TEXTF("TIME: %.1f",1,CANVAS_H-6*1,false, (f64)counterDelta/timeGetPerfFreq());
- DRAW_TEXTF("FOV=%.0f",1,CANVAS_H-6*2-1,false, fov);
- DRAW_TEXTF("%5.2f, %5.2f",1,CANVAS_H-6*3-1,false, pos.x, pos.y);
- #ifdef _DEBUG
- DRAW_TEXTF("%5.2f",1,CANVAS_H-6*4-1,false, (pos.z/M_2PIf)*360.0f);
- #endif
- #define ESC_NOTIF "PRESS ESCAPE!"
- #define NOTIF_LEN (sizeof(ESC_NOTIF-1))
- if(!is_cursor_trapped())
- DRAW_TEXT_BOX(ESC_NOTIF, CENTERED_TEXT, CENTERED_TEXT, false);
- #if defined(_DEBUG) || SHOW_MINIMAP_ALWAYS==1
- for(int y=0; y<map_h; ++y)
- for(int x=0; x<map_w; ++x)
- {
- fill_rect(map_scale*x, map_scale*(map_h-1-y),
- map_scale, map_scale,
- map[x + y*map_w]<<5);
- }
- fill_rect(map_scale*START_X, map_scale*(map_h-1-START_Y),
- map_scale, map_scale, 0b00111000);
- fill_rect(map_scale*(map_w-2), map_scale*(1),
- map_scale, map_scale, 0b00000111);
- pos_int = {s32(pos.x),map_h-1-s32(pos.y)};
- fill_rect(map_scale*pos_int.x, map_scale*pos_int.y,
- map_scale, map_scale, 128 + ((frame>>5)&1)*127);
- #endif
- // Frames take about 16ms to approximate 60fps
- canvas_present(true);
- timeSleep(16);
- ++frame;
- }
- u64 counterEnd = timeGetPerfCounter();
- f64 timeDelta = (f64)(counterEnd-counterStart)/timeGetPerfFreq();
- sfx.play(sinewave, .1,.1);
- bool end_screen = true;
- while(end_screen){
- while(pollEvent(&evt))
- switch(evt.type){
- case EVENT_QUIT: goto _lbl_exit;
- case EVENT_MOUSE_VWHEEL: {
- s32 newSize = CLAMP(map_w+s32(evt.mouse.dy)*2, 9, MAP_MAX);
- map_w = map_h = newSize;
- } break;
- case EVENT_MOUSE_DOWN: {
- _printf("%X\n",evt.mouse.button);
- if(evt.mouse.button&2/*MBUTTON_RIGHT*/) end_screen = false;
- } break;
- }
- Color24 newColor = hsv2rgb((f32)((frame*1)%720)/2, 100.0f, 100.0f);
- update_palette(254, 1, &newColor);
- canvas_clear(254);
- DRAW_TEXTF_BOX_1("Final Time: %.4f seconds!", CENTERED_TEXT, CENTERED_TEXT, false, timeDelta);
- DRAW_TEXT_BOX("(Right click to play again!)",
- CENTERED_TEXT, CANVAS_H/2-12, false);
- canvas_present(true);
- timeSleep(16);
- ++frame;
- }
- goto _lbl_start;
- }
- /******************************************************************************/
- /******************************************************************************/
- //"gdi_maze_game_2025-04-25\src\kit_utils\draw\blit.cpp":
- #include <public_stuff.hpp>
- #ifdef BLIT_USED
- // Blits a bitmap, where the first two bytes are its width and height,
- // the third byte corresponding to its transparent color,
- // the fourth byte corresponding to flags (unused by this function),
- // the fifth and sixth bytes are position offsets (of type s8),
- // the seventh and eighth bytes are padding and are currently unused,
- // with the bytes after being the pixels.
- void blit(s32 x, s32 y, const Color8* src, Color8* dst){
- // Bitmap cannot be given as NULL
- if(src == nullptr) return;
- // Apply offsets before doing any position bounds checking
- x += (s8)src[4].v;
- y += (s8)src[5].v;
- // Don't draw if source bitmap would appear off-screen anyway
- if(x >= CANVAS_W) return;
- if(y >= CANVAS_H) return;
- const s32 w = (s32)(src[0].v);
- const s32 h = (s32)(src[1].v);
- // Don't draw if source bitmap would appear off-screen anyway
- if(x <= -w) return;
- if(y <= -h) return;
- // Bitmap's width and/or height cannot be 0
- if(!w || !h) return;
- const s32 x_start = MAX(x, 0);
- const s32 y_start = MAX(y, 0);
- const s32 x_end = MIN(x+w, CANVAS_W);
- const s32 y_end = MIN(y+h, CANVAS_H);
- const Color8 transparent = src[2];
- src += 8;
- if(x<0) src -= x;
- if(y<0) src -= y*w;
- dst += MIN(x_start, CANVAS_W-1);
- dst += MIN(y_start, CANVAS_H-1) * CANVAS_W;
- for(y=y_start; y<y_end; ++y){
- s32 column = 0;
- for(x=x_start; x<x_end; ++x){
- Color8 pxl = src[column];
- if(pxl.v != transparent.v)
- dst[column].v = pxl.v;
- ++column;
- }
- src += w;
- dst += CANVAS_W;
- }
- }
- #endif
- /******************************************************************************/
- /******************************************************************************/
- //"gdi_maze_game_2025-04-25\src\kit_utils\draw\draw_line.cpp":
- #include <public_stuff.hpp>
- #ifdef DRAW_LINE_USED
- void draw_line(s32 x_0, s32 y_0,
- s32 x_1, s32 y_1,
- Color8 color,
- Color8* dst)
- {
- const s32 d_x = abs(x_1 - x_0);
- const s32 d_y = -abs(y_1 - y_0);
- const s32 s_x = (x_0<x_1) ? 1 : -1;
- const s32 s_y = (y_0<y_1) ? 1 : -1;
- const s32 s_y_row = s_y * CANVAS_W;
- s32 err = d_x + d_y;
- dst += x_0 + y_0*CANVAS_W;
- while(true){
- if( (x_0 >= 0) && (x_0 < CANVAS_W) &&
- (y_0 >= 0) && (y_0 < CANVAS_H) )
- {
- dst->v = color.v;
- }
- if(x_0 == x_1 && y_0 == y_1) break;
- const s32 err2 = err<<1;
- if(err2 >= d_y){
- err += d_y;
- x_0 += s_x;
- dst += s_x;
- }
- if(err2 <= d_x){
- err += d_x;
- y_0 += s_y;
- dst += s_y_row;
- }
- }
- }
- #endif
- /******************************************************************************/
- /******************************************************************************/
- //"gdi_maze_game_2025-04-25\src\kit_utils\draw\draw_rect.cpp":
- #include <public_stuff.hpp>
- #ifdef DRAW_RECT_USED
- void draw_rect(s32 x, s32 y,
- s32 w, s32 h,
- Color8 color,
- Color8* dst)
- {
- if(!w || !h) return;
- if(x >= CANVAS_W) return;
- if(y >= CANVAS_H) return;
- if(x <= -w) return;
- if(y <= -h) return;
- const s32 x_start = MAX(x, 0);
- const s32 y_start = MAX(y, 0);
- const s32 x_upper = x + w; // Upper boundary
- const s32 y_upper = y + h;
- const s32 x_end = MIN(x_upper, CANVAS_W);
- const s32 y_end = MIN(y_upper, CANVAS_H);
- Color8* n_w = dst;
- n_w += MIN(x_start, CANVAS_W-1);
- n_w += MIN(y_start, CANVAS_H-1) * CANVAS_W;
- Color8* n_e = n_w + ((x_end-x_start)-1);
- Color8* s_w = n_w + ((y_end-y_start)-1)*CANVAS_W;
- // From north-west, going down
- if(x >= 0){
- Color8* _n_w = n_w;
- for(s32 _y=y_start; _y<y_end; ++_y){
- _n_w->v = color.v;
- _n_w += CANVAS_W;
- }
- }
- // From north-west, going right
- if(y >= 0){
- for(x=x_start; x<x_end; ++x)
- (n_w++)->v = color.v;
- }
- // From north-east, going down
- if(x_upper < (CANVAS_W+1)){
- for(y=y_start; y<y_end; ++y){
- n_e->v = color.v;
- n_e += CANVAS_W;
- }
- }
- // From south-west, going right
- if(y_upper < (CANVAS_H+1)){
- for(x=x_start; x<x_end; ++x)
- (s_w++)->v = color.v;
- }
- }
- #endif
- /******************************************************************************/
- /******************************************************************************/
- //"gdi_maze_game_2025-04-25\src\kit_utils\draw\draw_text.cpp":
- #include <public_stuff.hpp>
- #ifdef DRAW_TEXT_USED
- void draw_char(u8 chr, s32 x, s32 y,
- const Color8* font,
- Color8* dst)
- {
- if(font == nullptr || dst == nullptr) return;
- if(x >= CANVAS_W || y >= CANVAS_H) return;
- const s32 w = font[0].v;
- const s32 h = font[1].v;
- if(x <= -w || y <= -h) return;
- const Color8 t = font[2]; // Transparent color
- const s32 x_start = MAX(x, 0);
- const s32 y_start = MAX(y, 0);
- const s32 x_end = MIN(x+w, CANVAS_W);
- const s32 y_end = MIN(y+h, CANVAS_H);
- const s32 x_span = x_end-x_start;
- const s32 y_span = y_end-y_start;
- font += 4 + chr * w*h; // Seek to start of char data
- if(x >= 0) dst += x;
- else font -= x; // -= because x is negative
- if(y >= 0) dst += y*CANVAS_W;
- else font -= y*w;
- for(y=0; y<y_span; ++y){
- for(x=0; x<x_span; ++x){
- if(font[x].v != t.v)
- dst[x] = font[x];
- }
- font += w;
- dst += CANVAS_W;
- }
- }
- #define ERR_RECT TEXT_ERROR_RECT
- Rect2d get_text_rect(const char* str,
- s32 x, s32 y,
- bool use_negative_position_margin,
- const Color8* font)
- {
- if(str == nullptr || font == nullptr) return ERR_RECT;
- u8 chr;
- const u8* text = (const u8*)str;
- s32 maxWidth = 0;
- s32 w = font[0].v;
- s32 h = font[1].v;
- // Add 1 pixel of space per char and scanline respectively
- ++w, ++h;
- // Seek to start of font data
- font += 4;
- Rect2d rect = {0, 0, 0, h*(text[0]!=0)};
- // Calculate text size
- while( (chr=*(text++)) )
- if(chr != '\n'){
- rect.w += w;
- } else { // chr == '\n'
- if(maxWidth < rect.w) maxWidth = rect.w;
- rect.w = 0;
- rect.h += h;
- }
- if(rect.w < maxWidth) rect.w = maxWidth;
- // Remove last bit of spacing from final rect
- --rect.w;
- --rect.h;
- // Calculate text position
- if(x == CENTERED_TEXT){
- rect.x = CANVAS_W/2 - rect.w/2;
- } else if(x < 0 && use_negative_position_margin){
- rect.x = CANVAS_W - rect.w + x + 1;
- } else {
- rect.x = x;
- }
- if(y == CENTERED_TEXT){
- rect.y = CANVAS_H/2 - rect.h/2;
- } else if(y < 0 && use_negative_position_margin){
- rect.y = CANVAS_H - rect.h + y + 1;
- } else {
- rect.y = y;
- }
- return rect;
- }
- // Returns final rect of the text
- Rect2d draw_text(const char* str, s32 x, s32 y, u32 maxLength,
- bool use_negative_position_margin,
- const Color8* font, Color8* dst)
- {
- Rect2d rect = get_text_rect(str, x, y, use_negative_position_margin, font);
- if(rect.h == -1 || dst == nullptr) return ERR_RECT;
- x = rect.x;
- y = rect.y;
- u8 chr;
- s32 chr_w = font[0].v;
- s32 chr_h = font[1].v;
- const u8* text = (const u8*)str;
- s32 x_start = x;
- // Since GDI bitmaps are drawn bottom-up, the start of the text is actually
- // its upper boundary, minus the font's glyph height
- y += rect.h - chr_h;
- ++chr_w, ++chr_h;
- // This effectively uncaps the length
- if(!maxLength) --maxLength;
- while( (chr=*(text++)) && (maxLength--) )
- if(chr != '\n'){
- draw_char(chr, x, y, font, dst);
- x += chr_w;
- } else { //chr == '\n'
- x = x_start;
- y -= chr_h;
- }
- return rect;
- }
- #endif
Add Comment
Please, Sign In to add comment