Advertisement
Kitomas

not asteroids sceneMain.cpp as of 2024-10-25

Oct 25th, 2024
72
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 34.03 KB | None | 0 0
  1. #include <scene.hpp>
  2.  
  3.  
  4. //1 ship, 1 'skybox', 1 gear icon, 1 door, <bullet_count> bullets,
  5.  //4 asteroids, 16 mini-asteroids, and 64 micro-asteroids
  6. #define bullet_count 8
  7. #define asteroid_count (4 + 4*4 + 4*4*4)
  8. MeshSimple* meshes[1 + 1 + 1 + 1 + bullet_count + asteroid_count];
  9. #define     meshes_len (sizeof(meshes)/sizeof(MeshSimple*))
  10.  
  11. #define bullet_offset 4
  12. #define asteroid_offset (bullet_offset+bullet_count)
  13.  
  14. #define _mesh_ship (meshes[0])
  15. #define _mesh_skybox (meshes[1])
  16. #define _mesh_gear (meshes[2])
  17. #define _mesh_door (meshes[3])
  18. #define _mesh_bullet(which) (meshes[bullet_offset+(which)])
  19. #define _mesh_asteroid(which) (meshes[asteroid_offset+(which)])
  20.  
  21. #define mesh_ship (*_mesh_ship)
  22. #define mesh_skybox (*_mesh_skybox)
  23. #define mesh_gear (*_mesh_gear)
  24. #define mesh_door (*_mesh_door)
  25. #define mesh_bullet(which) (*_mesh_bullet(which))
  26. #define mesh_asteroid(which) (*_mesh_asteroid(which))
  27.  
  28. #define asteroid_normal(which) mesh_asteroid(      which )
  29. #define asteroid_mini(which)   mesh_asteroid(4+   (which))
  30. #define asteroid_micro(which)  mesh_asteroid(4+16+(which))
  31. #define asteroid_menu asteroid_micro
  32.  
  33. #define state_normal(which) state.asteroids[      which ]
  34. #define state_mini(which)   state.asteroids[4+   (which)]
  35. #define state_micro(which)  state.asteroids[4+16+(which)]
  36.  
  37.  
  38.  
  39. #define bullet_size 0.25f
  40.  
  41. #define nrml_siz 6.0f
  42. #define mini_siz (nrml_siz*0.75f)
  43. #define mcro_siz (nrml_siz*0.50f)
  44. #define menu_siz 0.50f
  45.  
  46. #define skybox_bounds_formula (((CLIP_FAR/32) - 1.0f)  *  (0.5 + (float)cfg.num_asteroids/8))
  47.  
  48.  
  49. //application of values are as follows:
  50.  //camera's current position = old*mod + new*(1.0f-mod)
  51.  //old = camera's current position
  52. #define camera_mod 0.85f
  53. #define camera_lpf(_member) \
  54.   camera_old._member*camera_mod + camera_new._member*(1.0f-camera_mod)
  55. Location camera_old;
  56. Location camera_cur; //subject to different rules to that of scene_camera
  57. Location camera_new;
  58.  
  59. #define ship_camera_distance    1.3f
  60. #define ship_camera_y_offset    0.25f
  61. #define ship_camera_down_tilt   0.02f
  62. #define ship_decay_multiplier_a 0.88f
  63. #define ship_decay_multiplier_b 0.5f
  64.  
  65.  
  66.  
  67. #define speed_modifier(v) ( (v) * (CLIP_FAR/768.0f) )
  68.  
  69. #define bullet_speed speed_modifier(1.50f)
  70.  
  71. #define ship_speed_x speed_modifier(0.20f)
  72. #define ship_speed_y speed_modifier(0.20f)
  73. #define ship_speed_z speed_modifier(0.30f)
  74.  
  75. #define asteroid_speed speed_modifier(0.1)
  76.  
  77. #define asteroid_rotation ((_2pi/60) * 1.0f) //speed_modifier isn't needed here
  78.  
  79.  
  80.  
  81.  
  82.  
  83. //
  84.  
  85.  
  86.  
  87.  
  88.  
  89. #define CFG_FILENAME "./config.bin"
  90. #define CFG_VERSION 12
  91.  
  92. struct _cfg_s {
  93.   unsigned short version = CFG_VERSION;
  94.  
  95.   bool maximized = false;
  96.   bool fullscreen = false;
  97.  
  98.   float high_scores[4] = {0.0f, 0.0f, 0.0f, 0.0f};
  99.  
  100.   #define BIND_COUNT 6
  101.   unsigned short bind_right   = SDL_SCANCODE_D;
  102.   unsigned short bind_left    = SDL_SCANCODE_A;
  103.   unsigned short bind_forward = SDL_SCANCODE_W;
  104.   unsigned short bind_back    = SDL_SCANCODE_S;
  105.   unsigned short bind_up      = SDL_SCANCODE_LSHIFT;
  106.   unsigned short bind_down    = SDL_SCANCODE_LCTRL;
  107.  
  108.   float lastrand = 0.9f; //used to modify srand when starting a game
  109.  
  110.   float sensitivity = 0.8f; //mouse look sensitivity
  111.  
  112.   unsigned short win_w = WIN_W,  win_h = WIN_H;
  113.  
  114.   char num_asteroids = 4; //# of normal asteroids that appear when game resets
  115.  
  116.   bool invert_y = false; //inverts vertical mouse input
  117.  
  118. };
  119.  
  120. _cfg_s cfg;
  121.  
  122. //WEIRD POINTER STUFF LMAOOO
  123. unsigned short* bind_array = (unsigned short*)(&cfg.high_scores[3+1]);
  124. unsigned short  bind_array_defaults[BIND_COUNT];
  125.  
  126. const char* bind_names[] = {
  127.   "right",
  128.   "left",
  129.   "forward",
  130.   "back",
  131.   "up",
  132.   "down",
  133. };
  134.  
  135.  
  136.  
  137. #define MENU_REBINDING -3
  138. #define MENU_DEAD      -2
  139. #define MENU_PLAYING   -1
  140. #define MENU_QUIT       0
  141. #define MENU_PLAY       1
  142. #define MENU_ASTEROIDS  2
  143. #define MENU_REBIND     3
  144.  
  145. #define MENU_MAX        MENU_REBIND
  146.  
  147. struct bulletElement {
  148.   Vec3    vel = {0,0,0};
  149.   bool active = false;
  150. };
  151.  
  152. enum asteroidTypeEnum { ATYPE_NORMAL = 0, ATYPE_MINI, ATYPE_MICRO, };
  153.  
  154. struct asteroidElement {
  155.   int   children[4]; //indexes of children; set during init
  156.   Location      vel; //velocity
  157.   char         type = -1; //asteroidTypeEnum
  158.   bool has_children = true;
  159.   bool       active = false;
  160.   char    _padding8;
  161. };
  162.  
  163. struct _state_s {
  164.   bulletElement bullets[bullet_count];
  165.   int active_bullets = 0;
  166.  
  167.   asteroidElement asteroids[asteroid_count];
  168.   int active_asteroids = 0;
  169.  
  170.   int menu = MENU_PLAY;
  171.  
  172.   int which_rebind = 0;
  173.   bool pressed_right   = false;
  174.   bool pressed_left    = false;
  175.   bool pressed_forward = false;
  176.   bool pressed_back    = false;
  177.   bool pressed_up      = false;
  178.   bool pressed_down    = false;
  179.  
  180.   bool quit = false;
  181.  
  182.   bool cursorIsTrapped = false;
  183.  
  184.   float score = 0.0f;
  185.  
  186.   float skybox_bounds = skybox_bounds_formula;
  187.  
  188. };
  189.  
  190. _state_s state;
  191.  
  192. bool* pressed_array = (bool*)(&state.which_rebind+1);
  193.  
  194. const float asteroid_sizes[3] = { nrml_siz, mini_siz, mcro_siz };
  195.  
  196.  
  197.  
  198.  
  199.  
  200. static inline bool keepInBounds(Location& loc){
  201.   bool wrappedAround = false;
  202.   if(fabsf(loc.x)>state.skybox_bounds){ loc.x = (1-2*(loc.x>=0))*state.skybox_bounds; wrappedAround = true; }
  203.   if(fabsf(loc.y)>state.skybox_bounds){ loc.y = (1-2*(loc.y>=0))*state.skybox_bounds; wrappedAround = true; }
  204.   if(fabsf(loc.z)>state.skybox_bounds){ loc.z = (1-2*(loc.z>=0))*state.skybox_bounds; wrappedAround = true; }
  205.   return wrappedAround;
  206. }
  207.  
  208.  
  209.  
  210. //bounces on edge instead of modulo
  211. static inline bool keepInBounds_asteroid(int which){
  212.   bool bounced = false;
  213.  
  214.   asteroidElement& ast = state.asteroids[which];
  215.   float bounce_dist = state.skybox_bounds - asteroid_sizes[(int)ast.type]/2;
  216.  
  217.   if(fabsf(mesh_asteroid(which).p.x)>=bounce_dist){ ast.vel.x = -ast.vel.x; bounced = true; }
  218.   if(fabsf(mesh_asteroid(which).p.y)>=bounce_dist){ ast.vel.y = -ast.vel.y; bounced = true; }
  219.   if(fabsf(mesh_asteroid(which).p.z)>=bounce_dist){ ast.vel.z = -ast.vel.z; bounced = true; }
  220.  
  221.   return bounced;
  222.  
  223. }
  224.  
  225.  
  226.  
  227. template<class T> inline T umod(T a, int b){
  228.   a %= b;
  229.   if(a < 0) a += b;
  230.   return a;
  231. }
  232.  
  233.  
  234.  
  235. #define SQR(thing) ((thing)*(thing))
  236.  
  237. static inline float locHypot(const Location& loc){
  238.   return sqrtf(SQR(loc.x)+SQR(loc.y)+SQR(loc.z));
  239. }
  240.  
  241. static inline float locDistance(const Location& l_a, const Location& l_b){
  242.   return sqrtf(SQR(l_b.x-l_a.x)+SQR(l_b.y-l_a.y)+SQR(l_b.z-l_a.z));
  243. }
  244.  
  245.  
  246.  
  247. //assumes RAND_MAX is 32767
  248. static inline float randf(){ //0.0f -> 1.0f
  249.   return (float)(rand()<<15|rand())/0x3fffffff;
  250. }
  251.  
  252. static inline float randf2(){ //-1.0f -> 1.0f
  253.   return randf()*2.0f - 1.0f;
  254. }
  255.  
  256.  
  257.  
  258.  
  259.  
  260. void trapCursor(bool enable){
  261.   if(enable == state.cursorIsTrapped) return;
  262.   SDL_SetWindowTitle(canvas->getWin(), fstr(WIN_TITLE " %s", (enable) ? UNTRAP_PROMPT : TRAP_PROMPT));
  263.   if(SDL_SetRelativeMouseMode((enable) ? SDL_TRUE : SDL_FALSE)<0) throw SDL_GetError();
  264.   state.cursorIsTrapped = enable;
  265. }
  266.  
  267.  
  268. void setFullscreen(bool enable){
  269.   SDL_SetWindowFullscreen(canvas->getWin(), (enable) ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
  270.   cfg.fullscreen = enable;
  271. }
  272.  
  273.  
  274.  
  275.  
  276.  
  277. void mouseMoved(s32 dx, s32 dy);
  278. void mouseButton(u8 button, bool clicked);
  279. void mouseWheel(int amount);
  280. void keyPress(SDL_Keysym key, bool pressed, u16 modifiers);
  281.  
  282. //returns false if state.quit gets flipped
  283. bool handleEvents(){
  284.   SDL_Event evt;
  285.   while(SDL_PollEvent(&evt))
  286.   switch(evt.type){
  287.     case SDL_QUIT: state.quit = true; SDL_ShowCursor(true); return false;
  288.  
  289.     case SDL_WINDOWEVENT: {
  290.       switch(evt.window.event){
  291.         case SDL_WINDOWEVENT_FOCUS_LOST: trapCursor(false); break;
  292.         case SDL_WINDOWEVENT_RESIZED: cfg.win_w = evt.window.data1, cfg.win_h = evt.window.data2; break;
  293.         case SDL_WINDOWEVENT_RESTORED: cfg.maximized = false; break;
  294.         case SDL_WINDOWEVENT_MAXIMIZED: cfg.maximized = true; break;
  295.         default: ;//Log("%X", evt.window.event);
  296.       }
  297.     } break;
  298.  
  299.     case SDL_KEYDOWN: {
  300.       if(evt.key.repeat) break;
  301.       if(evt.key.keysym.sym == SDLK_ESCAPE){ trapCursor(false); break; }
  302.       if(evt.key.keysym.sym == SDLK_F11){ setFullscreen(!cfg.fullscreen); break; }
  303.     } SDL_FALLTHROUGH;
  304.     case SDL_KEYUP: {
  305.       keyPress(evt.key.keysym,
  306.                (evt.key.state == SDL_PRESSED) ? true : false,
  307.                evt.key.keysym.mod);
  308.     } break;
  309.  
  310.     case SDL_MOUSEBUTTONDOWN:{
  311.       if(state.cursorIsTrapped)
  312.         mouseButton(evt.button.button, evt.button.state == SDL_PRESSED);
  313.       if(evt.button.button == SDL_BUTTON_LEFT)
  314.         trapCursor(true);
  315.     } break;
  316.  
  317.     case SDL_MOUSEMOTION: {
  318.       if(cfg.invert_y) evt.motion.yrel = -evt.motion.yrel;
  319.       if(state.cursorIsTrapped) mouseMoved(evt.motion.xrel, evt.motion.yrel);
  320.     } break;
  321.  
  322.     case SDL_MOUSEWHEEL: {
  323.       if(state.cursorIsTrapped)
  324.         mouseWheel(evt.wheel.y);
  325.     } break;
  326.  
  327.     default: ;//Log("%X", evt.type);
  328.  
  329.   }
  330.  
  331.   return !state.quit;
  332.  
  333. }
  334.  
  335.  
  336.  
  337.  
  338.  
  339. Vec3 rotatePRY(float pitch, float roll, float yaw,
  340.                float   x_0, float  y_0, float z_0)
  341. {
  342.   const float cosPitch = cosf(pitch);
  343.   const float sinPitch = sinf(pitch);
  344.   const float cosRoll  = cosf(roll );
  345.   const float sinRoll  = sinf(roll );
  346.   const float cosYaw   = cosf(yaw  );
  347.   const float sinYaw   = sinf(yaw  );
  348.  
  349.   float y_1 = cosPitch*y_0 - sinPitch*z_0;
  350.   float z_1 = sinPitch*y_0 + cosPitch*z_0;
  351.  
  352.   float x_2 = cosRoll*x_0 - sinRoll*y_1;
  353.   float y_2 = sinRoll*x_0 + cosRoll*y_1;
  354.  
  355.   float x_3 =  cosYaw*x_2 + sinYaw*z_1;
  356.   float z_2 = -sinYaw*x_2 + cosYaw*z_1;
  357.  
  358.   return Vec3(x_3, y_2, z_2);
  359.  
  360. }
  361.  
  362. //motion is relative to camera's current direction
  363. static inline void moveCamera(float x_0, float y_0, float z_0){
  364.   Vec3 motion = rotatePRY(-camera_cur.pitch,
  365.                           -camera_cur.roll,
  366.                           -camera_cur.yaw,
  367.                           x_0, y_0, z_0);
  368.  
  369.   camera_cur.x += motion.x;
  370.   camera_cur.y += motion.y;
  371.   camera_cur.z += motion.z;
  372.  
  373. }
  374.  
  375.  
  376.  
  377. bool bulletFire(){
  378.   if(state.active_bullets >= bullet_count) return false; //max bullets reached
  379.  
  380.   int which = -1;
  381.   for(int i=0; i<bullet_count; ++i){
  382.     if(!state.bullets[i].active){ which = i; break; }
  383.   }
  384.   if(which == -1)
  385.     throw "could not find empty spot in bullet queue, despite active bullets being < maximum"; //return false;
  386.  
  387.  
  388.   Vec3 direction = rotatePRY(-camera_cur.pitch,
  389.                              -camera_cur.roll,
  390.                              -camera_cur.yaw,
  391.                              0.0f, 0.0f, 1.0f);
  392.  
  393.  
  394.   mesh_bullet(which).p.x     = -camera_cur.x + direction.x;
  395.   mesh_bullet(which).p.y     = -camera_cur.y + direction.y;
  396.   mesh_bullet(which).p.z     = -camera_cur.z + direction.z;
  397.   mesh_bullet(which).p.pitch = -camera_cur.pitch;
  398.   mesh_bullet(which).p.roll  = -camera_cur.roll;
  399.   mesh_bullet(which).p.yaw   = -camera_cur.yaw;
  400.  
  401.  
  402.   state.bullets[which].vel  = direction;
  403.   state.bullets[which].vel *= bullet_speed;
  404.  
  405. //lol
  406. #pragma GCC diagnostic push
  407. #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
  408.   state.bullets[which].active = true;
  409. #pragma GCC diagnostic pop
  410.  
  411.  
  412.   ++state.active_bullets;
  413.  
  414.   return true;
  415.  
  416. }
  417.  
  418.  
  419.  
  420. void bulletMoveAll(){
  421.   for(unsigned i=0; i<bullet_count; ++i){
  422.     if(!state.bullets[i].active) continue;
  423.  
  424.     mesh_bullet(i).p.x += state.bullets[i].vel.x;
  425.     mesh_bullet(i).p.y += state.bullets[i].vel.y;
  426.     mesh_bullet(i).p.z += state.bullets[i].vel.z;
  427.  
  428.     if(keepInBounds(mesh_bullet(i).p)){
  429.       mesh_bullet(i).p.y      = CLIP_FAR*2;
  430.       state.bullets[i].active = false;
  431.       --state.active_bullets;
  432.     }
  433.  
  434.   }
  435.  
  436. }
  437.  
  438.  
  439.  
  440.  
  441.  
  442. void asteroidCreate(int which, int parent = -1){
  443.   asteroidElement& ast = state.asteroids[which];
  444.  
  445.   if(parent == -1){
  446.     //move asteroid to a new location in-bounds that is far enough from the ship
  447.     Location newloc = mesh_asteroid(which).p;
  448.     float ast_size_mod = asteroid_sizes[(int)ast.type];
  449.  
  450.     do { //keep rolling for a new position until a suitable one is found
  451.       newloc.x = randf2() * (state.skybox_bounds-ast_size_mod);
  452.       newloc.y = randf2() * (state.skybox_bounds-ast_size_mod);
  453.       newloc.z = randf2() * (state.skybox_bounds-ast_size_mod);
  454.     } while( locDistance(newloc, camera_cur) < (state.skybox_bounds/3) );
  455.  
  456.     mesh_asteroid(which).p = newloc;
  457.  
  458.  
  459.   } else {
  460.     mesh_asteroid(which).p.x = mesh_asteroid(parent).p.x;
  461.     mesh_asteroid(which).p.y = mesh_asteroid(parent).p.y;
  462.     mesh_asteroid(which).p.z = mesh_asteroid(parent).p.z;
  463.  
  464.   }
  465.  
  466.  
  467.  
  468.   float max_type_speed    = asteroid_speed   *(1+ast.type);
  469.   //float max_type_rotation = asteroid_rotation*(1+ast.type);
  470.  
  471.   ast.vel.x     = randf2() * max_type_speed;
  472.   ast.vel.y     = randf2() * max_type_speed;
  473.   ast.vel.z     = randf2() * max_type_speed;
  474.   ast.vel.pitch = randf2() * asteroid_rotation;
  475.   ast.vel.roll  = randf2() * asteroid_rotation;
  476.   ast.active = true;
  477.  
  478.   ++state.active_asteroids;
  479.  
  480. }
  481.  
  482.  
  483.  
  484. void asteroidDestroy(int which){
  485.   asteroidElement& ast = state.asteroids[which];
  486.   ast.active = false;
  487.  
  488.   state.score += 1 + ast.type;
  489.  
  490.   if(ast.has_children){
  491.     asteroidCreate(ast.children[0], which);
  492.     asteroidCreate(ast.children[1], which);
  493.     asteroidCreate(ast.children[2], which);
  494.     asteroidCreate(ast.children[3], which);
  495.  
  496.   }
  497.  
  498.   mesh_asteroid(which).p.y = CLIP_FAR*2; //move asteroid mesh out of play area
  499.  
  500.   --state.active_asteroids;
  501.  
  502. }
  503.  
  504.  
  505.  
  506. void asteroidMoveAll(){
  507.   for(unsigned i=0; i<asteroid_count; ++i){
  508.     if(!state.asteroids[i].active) continue;
  509.  
  510.     mesh_asteroid(i).p.x     += state.asteroids[i].vel.x;
  511.     mesh_asteroid(i).p.y     += state.asteroids[i].vel.y;
  512.     mesh_asteroid(i).p.z     += state.asteroids[i].vel.z;
  513.     mesh_asteroid(i).p.pitch += state.asteroids[i].vel.pitch;
  514.     mesh_asteroid(i).p.roll  += state.asteroids[i].vel.roll;
  515.  
  516.     keepInBounds_asteroid(i);
  517.  
  518.   }
  519.  
  520. }
  521.  
  522.  
  523.  
  524.  
  525.  
  526. void checkForBulletHits(){
  527.   for(unsigned b=0; b<bullet_count; ++b){
  528.     if(!state.bullets[b].active) continue;
  529.     Location bullet_loc = mesh_bullet(b).p;
  530.  
  531.     //checking the little asteroids first (as in, in reverse order)
  532.      //will hopefully negate the usefulness of spam shooting a single asteroid a bit
  533.     for(int a=(asteroid_count-1); a>=0; --a){
  534.       if(!state.asteroids[a].active) continue;
  535.       //*0.45f instead of *0.5f to account for inconsistent geometry
  536.       float collide_dist = bullet_size/2 + asteroid_sizes[(int)state.asteroids[a].type]*0.45f;
  537.  
  538.       if(locDistance(bullet_loc,mesh_asteroid(a).p) < collide_dist){
  539.         asteroidDestroy(a);
  540.         mesh_bullet(b).p.y      = CLIP_FAR*2;
  541.         state.bullets[b].active = false;
  542.         --state.active_bullets;
  543.         break; //go to next bullet, since this one is now destroyed
  544.  
  545.       }
  546.  
  547.     }
  548.  
  549.   }
  550.  
  551. }
  552.  
  553.  
  554.  
  555.  
  556.  
  557. //
  558.  
  559.  
  560.  
  561.  
  562.  
  563. void activateMenuItem(){
  564.   switch(state.menu){
  565.     case MENU_DEAD     : state.menu = MENU_PLAY; break;
  566.     case MENU_QUIT     : state.quit = true; break;
  567.     case MENU_PLAY     : state.menu = MENU_PLAYING; break;
  568.     case MENU_ASTEROIDS: cfg.num_asteroids = cfg.num_asteroids%4+1; break;
  569.     case MENU_REBIND   : state.menu = MENU_REBINDING; state.which_rebind = 0; break;
  570.     default:;
  571.   }
  572. }
  573.  
  574.  
  575.  
  576.  
  577.  
  578. #define mdelta_divisor_a 200
  579. #define mdelta_divisor_b 200
  580. #define mdelta_divisor_c 1000
  581.  
  582. void mouseMoved(s32 dx, s32 dy){
  583.   if(state.menu != MENU_PLAYING) return;
  584.  
  585.   #define rotmod(value) fmodf(value, _2pi)
  586.   #define rotclamp(value) CLAMP(value, -_pi, _pi)
  587.   #define rotclamph(value) CLAMP(value, -(_pi/2), (_pi/2))
  588.  
  589.   camera_cur.yaw   = rotmod(camera_cur.yaw  -((float)dx/mdelta_divisor_a)*cfg.sensitivity);
  590.   camera_cur.pitch = rotmod(camera_cur.pitch+((float)dy/mdelta_divisor_a)*cfg.sensitivity);
  591.  
  592.   mesh_ship.p.roll  = rotclamph(mesh_ship.p.roll -((float)dx/mdelta_divisor_b)*cfg.sensitivity);
  593.   mesh_ship.p.pitch = rotclamph(mesh_ship.p.pitch-((float)dy/mdelta_divisor_b)*cfg.sensitivity);
  594.  
  595. }
  596.  
  597.  
  598.  
  599.  
  600.  
  601. void mouseButton(u8 button, bool clicked){
  602.   if(button == SDL_BUTTON_LEFT  &&  clicked){
  603.     if(     state.menu == MENU_PLAYING) bulletFire();
  604.     else if(state.menu >=            0) activateMenuItem();
  605.   }
  606.  
  607. }
  608.  
  609.  
  610.  
  611.  
  612.  
  613. void mouseWheel(int amount){
  614.   if(state.menu >= 0) state.menu = umod(state.menu+amount, MENU_MAX+1);
  615. }
  616.  
  617.  
  618.  
  619.  
  620.  
  621. static inline bool bindingIsValid(SDL_Scancode scancode, unsigned index){
  622.   if(index > INT_S32_MAX) //just in case
  623.     throw "bindingIsValid(): index > 2147483647";
  624.  
  625.   //bindings shouldn't share the same scancode,
  626.    //so check current bindings until right before current index (not BIND_COUNT)
  627.   for(unsigned i=0; i<index; ++i) //loop shouldn't start at all if index is 0
  628.     if(bind_array[i] == scancode) return false;
  629.  
  630.   return true;
  631.  
  632. }
  633.  
  634.  
  635. void keyPress(SDL_Keysym key, bool pressed, u16 modifiers){
  636.   //Log("0x%02X, %i, 0x%04X", key.scancode, pressed, modifiers);
  637.  
  638.   if(state.menu == MENU_PLAYING){
  639.     for(unsigned i=0; i<BIND_COUNT; ++i){
  640.       if(key.scancode == bind_array[i])
  641.         pressed_array[i] = pressed;
  642.     }
  643.  
  644.     if(pressed){
  645.       if(key.sym == SDLK_SPACE    ) bulletFire();
  646.       if(key.sym == SDLK_BACKSPACE) state.menu = MENU_PLAY;
  647.     }
  648.  
  649.  
  650.   } else if(state.menu == MENU_REBINDING){
  651.     if(!pressed) return;
  652.     if(key.sym == SDLK_RETURN  ||  key.sym == SDLK_SPACE) return;
  653.     if( key.sym == SDLK_UP    ||  key.sym == SDLK_DOWN  ||
  654.         key.sym == SDLK_LEFT  ||  key.sym == SDLK_RIGHT )
  655.     {
  656.       return;
  657.     }
  658.  
  659.     if(key.sym == SDLK_BACKSPACE){
  660.       for(int i=0; i<BIND_COUNT; ++i) bind_array[i] = bind_array_defaults[i];
  661.       state.menu = MENU_REBIND;
  662.       return;
  663.     }
  664.  
  665.     if(bindingIsValid(key.scancode, state.which_rebind)){
  666.       bind_array[state.which_rebind] = key.scancode;
  667.       ++state.which_rebind;
  668.     }
  669.  
  670.     if(state.which_rebind >= BIND_COUNT) state.menu = MENU_REBIND;
  671.  
  672.  
  673.   } else if(state.menu >= 0  ||  state.menu == MENU_DEAD){
  674.     if(!pressed) return;
  675.  
  676.     if(key.sym == SDLK_RETURN  ||  key.sym == SDLK_SPACE)
  677.       activateMenuItem();
  678.  
  679.     if(state.menu < 0) return;
  680.  
  681.  
  682.     if(key.scancode == cfg.bind_left  ||  key.scancode == SDL_SCANCODE_LEFT){
  683.       state.menu = umod(state.menu-1, MENU_MAX+1);
  684.  
  685.     } else if(key.scancode == cfg.bind_right  ||  key.scancode == SDL_SCANCODE_RIGHT){
  686.       state.menu = umod(state.menu+1, MENU_MAX+1);
  687.  
  688.     } else switch(state.menu){
  689. #ifdef _DEBUG
  690.       case MENU_QUIT: {
  691.         if(key.scancode == SDL_SCANCODE_RSHIFT) mesh_ship.p.yaw = 0.0f;
  692.       } break;
  693. #endif
  694.       case MENU_ASTEROIDS: {
  695.         if(key.scancode == cfg.bind_back  ||  key.scancode == SDL_SCANCODE_DOWN)
  696.           cfg.num_asteroids = MAX(cfg.num_asteroids-1, 1);
  697.         else if(key.scancode == cfg.bind_forward  ||  key.scancode == SDL_SCANCODE_UP)
  698.           cfg.num_asteroids = MIN(cfg.num_asteroids+1, 4);
  699.       } break;
  700.       default:;
  701.  
  702.     }
  703.  
  704.  
  705.   } else {
  706.  
  707.  
  708.   }
  709.  
  710. }
  711.  
  712.  
  713.  
  714.  
  715.  
  716. int sceneMain(int argc, char** argv){
  717.  
  718. /************************************ INIT ************************************/
  719.  
  720.   _mesh_ship   = new MeshSimple("ship.obj");
  721.   _mesh_skybox = new MeshSimple("skybox.obj");
  722.   _mesh_gear   = new MeshSimple("gear.obj");
  723.   _mesh_door   = new MeshSimple("door.obj");
  724.   for(unsigned i=0; i<bullet_count; ++i)
  725.     _mesh_bullet(i) = new MeshSimple("bullet.obj");
  726.   for(unsigned i=0; i<asteroid_count; ++i)
  727.     _mesh_asteroid(i) = new MeshSimple("asteroid.obj");
  728.  
  729.   for(int i=0; i<BIND_COUNT; ++i)
  730.     bind_array_defaults[i] = bind_array[i];
  731.  
  732.   cfg.version = CFG_VERSION;
  733.  
  734.   if(file::exists(CFG_FILENAME)){
  735.     BinaryData _cfg_file(CFG_FILENAME);
  736.     _cfg_s* cfg_file = (_cfg_s*)_cfg_file.data;
  737.  
  738.     //always make sure high score carries over, since i won't be changing its
  739.      //placement between changes to the overall structure of _cfg_s
  740.     cfg.high_scores[0] = cfg_file->high_scores[0];
  741.     cfg.high_scores[1] = cfg_file->high_scores[1];
  742.     cfg.high_scores[2] = cfg_file->high_scores[2];
  743.     cfg.high_scores[3] = cfg_file->high_scores[3];
  744.  
  745.     if( _cfg_file.data_len == sizeof(cfg)  &&
  746.          cfg_file->version == cfg.version  )
  747.     {
  748.       cfg = *cfg_file;
  749.     }
  750.  
  751.   }
  752.  
  753.   sceneRebuild(meshes, meshes_len);
  754.  
  755.   for(unsigned i=0; i<bullet_count; ++i) mesh_bullet(i).p.scale = bullet_size;
  756.  
  757.   for(unsigned i=0; i< 4; ++i) asteroid_normal(i).p.scale = nrml_siz;
  758.   for(unsigned i=0; i<16; ++i) asteroid_mini(i).p.scale   = mini_siz;
  759.   for(unsigned i=0; i<64; ++i) asteroid_micro(i).p.scale  = mcro_siz;
  760.  
  761.   state.menu = -1;
  762.  
  763.   if(cfg.maximized) SDL_MaximizeWindow(canvas->getWin());
  764.   else SDL_SetWindowSize(canvas->getWin(), cfg.win_w, cfg.win_h);
  765.   setFullscreen(cfg.fullscreen);
  766.  
  767.   SDL_ShowWindow(canvas->getWin());
  768.   canvas->clear();
  769.   canvas->present(0);
  770.  
  771.  
  772.   #define canvas_present(msDelay) canvas->present((unsigned)(MAX(1, ((msDelay)-(int)(SDL_GetTicks64()-startTime)) )))
  773.   Uint64 startTime;
  774.   SDL_RWops* cfg_file;
  775.  
  776.   float viewport_fov = 75; //degrees of vertical field of view
  777.   float viewport_mod = tan(viewport_fov/(360.0f/_pi));
  778.   SDL_Rect crosshair = {CNV_W/2-1, CNV_H/2-1, 2, 2};
  779.  
  780.  
  781.   for(int n=0; n<4; ++n){
  782.     state_normal(n).children[0]  = 4 + n*4;
  783.     state_normal(n).children[1]  = state_normal(n).children[0] + 1;
  784.     state_normal(n).children[2]  = state_normal(n).children[0] + 2;
  785.     state_normal(n).children[3]  = state_normal(n).children[0] + 3;
  786.     state_normal(n).type         = ATYPE_NORMAL;
  787.     state_normal(n).has_children = true;
  788.     state_normal(n).active       = false;
  789.   }
  790.  
  791.   for(int m=0; m<16; ++m){
  792.     state_mini(m).children[0]  = 4 + 16 + m*4;
  793.     state_mini(m).children[1]  = state_mini(m).children[0] + 1;
  794.     state_mini(m).children[2]  = state_mini(m).children[0] + 2;
  795.     state_mini(m).children[3]  = state_mini(m).children[0] + 3;
  796.     state_mini(m).type         = ATYPE_MINI;
  797.     state_mini(m).has_children = true;
  798.     state_mini(m).active       = false;
  799.   }
  800.  
  801.   for(int u=0; u<64; ++u){
  802.     state_micro(u).type         = ATYPE_MICRO;
  803.     state_micro(u).has_children = false;
  804.     state_micro(u).active       = false;
  805.   }
  806.  
  807.  
  808.  
  809.  
  810.  
  811. /**********************************/ _MENU: /**********************************/
  812.  
  813.   #define menu_mesh_spacing 3.0f
  814.  
  815.   memory::set(pressed_array, 0, sizeof(bool)*BIND_COUNT);
  816.  
  817.   memory::set(&scene_camera, 0, sizeof(scene_camera));
  818.   scene_camera.scale = 1.0f/16;
  819.   scene_camera.pitch = 0.3f;
  820.   scene_camera.y     = 0.25f;
  821.   scene_camera.z     = -0.25f;
  822.  
  823.   memory::set(&mesh_ship.p, 0, sizeof(mesh_ship.p));
  824.   mesh_ship.p.scale =  1.0f;
  825.   mesh_ship.p.x     = menu_mesh_spacing*MENU_PLAY;
  826.   mesh_ship.p.z     =  1.0f;
  827.  
  828.   memory::set(&mesh_skybox.p, 0, sizeof(mesh_skybox.p));
  829.  
  830.   memory::set(&mesh_gear.p, 0, sizeof(mesh_gear.p));
  831.   mesh_gear.p.scale = 1.0f;
  832.   mesh_gear.p.x     = menu_mesh_spacing*MENU_REBIND;
  833.   mesh_gear.p.z     = 1.0f;
  834.  
  835.   memory::set(&mesh_door.p, 0, sizeof(mesh_door.p));
  836.   mesh_door.p.scale = 1.0f;
  837.   mesh_door.p.x     = menu_mesh_spacing*MENU_QUIT;
  838.   mesh_door.p.z     = 1.0f;
  839.  
  840.   for(unsigned i=0; i<bullet_count; ++i){
  841.     mesh_bullet(i).p.y = CLIP_FAR*2;
  842.     state.bullets[i].active = false;
  843.   }
  844.  
  845.   state.active_bullets = 0;
  846.  
  847.   for(unsigned i=0; i<asteroid_count; ++i){
  848.     mesh_asteroid(i).p.x     = menu_mesh_spacing*MENU_ASTEROIDS;
  849.     mesh_asteroid(i).p.y     = CLIP_FAR*2;
  850.     mesh_asteroid(i).p.z     = 1.0f;
  851.     mesh_asteroid(i).p.yaw   = state.asteroids[i].vel.yaw   = 0.0f;
  852.     mesh_asteroid(i).p.pitch = state.asteroids[i].vel.pitch = 0.0f;
  853.     mesh_asteroid(i).p.roll  = state.asteroids[i].vel.roll  = 0.0f;
  854.   }
  855.  
  856.  
  857.  
  858.   const float old_scale[] = {
  859.     asteroid_menu(0).p.scale,
  860.     asteroid_menu(1).p.scale,
  861.     asteroid_menu(2).p.scale,
  862.     asteroid_menu(3).p.scale,
  863.   };
  864.  
  865.   asteroid_menu(0).p.scale =  menu_siz;   //bottom-left
  866.   asteroid_menu(0).p.x    += -menu_siz/2;  //
  867.   asteroid_menu(0).p.y     = -menu_siz/2;  //
  868.  
  869.   asteroid_menu(1).p.scale =  menu_siz;   //bottom-right
  870.   asteroid_menu(1).p.x    +=  menu_siz/2;  //
  871.   asteroid_menu(1).p.y     = -menu_siz/2;  //
  872.  
  873.   asteroid_menu(2).p.scale =  menu_siz;   //top-left
  874.   asteroid_menu(2).p.x    += -menu_siz/2;  //
  875.   asteroid_menu(2).p.y     =  menu_siz/2;  //
  876.  
  877.   asteroid_menu(3).p.scale =  menu_siz;   //top-right
  878.   asteroid_menu(3).p.x    +=  menu_siz/2;  //
  879.   asteroid_menu(3).p.y     =  menu_siz/2;  //
  880.  
  881.  
  882.  
  883.   if(state.menu < 0) state.menu = MENU_PLAY;
  884.  
  885.   camera_old.x   = menu_mesh_spacing * state.menu;
  886.   camera_new.x   = camera_old.x;
  887.   scene_camera.x = camera_old.x;
  888.  
  889.   while(!state.quit && state.menu != MENU_PLAYING){
  890.     startTime = SDL_GetTicks64();
  891.     if(!handleEvents()) break;
  892.  
  893.  
  894.     if(state.menu >= 0){
  895.       mesh_ship.p.yaw += _2pi/60/8;
  896.       mesh_gear.p.yaw  = mesh_ship.p.yaw;
  897.       mesh_door.p.yaw  = mesh_ship.p.yaw;
  898.       for(unsigned i=0; i<4; ++i)
  899.         asteroid_menu(i).p.yaw = mesh_ship.p.yaw;
  900.  
  901.       camera_new.x   = menu_mesh_spacing * state.menu;
  902.       scene_camera.x = camera_lpf(x);
  903.       camera_old.x   = scene_camera.x;
  904.  
  905.       asteroid_menu(0).p.z = (cfg.num_asteroids >= 1) ? 1.0f : -100.0f;
  906.       asteroid_menu(1).p.z = (cfg.num_asteroids >= 2) ? 1.0f : -100.0f;
  907.       asteroid_menu(2).p.z = (cfg.num_asteroids >= 3) ? 1.0f : -100.0f;
  908.       asteroid_menu(3).p.z = (cfg.num_asteroids >= 4) ? 1.0f : -100.0f;
  909.  
  910.       //putting this in this code block technically causes a graphical glitch
  911.        //where the framebuffer elements related to geometry gradually fade
  912.        //out when rebinding controls, but i'll include it until something
  913.        //inexplicably breaks, just because the effect looks so cool
  914.       scene_wka.setAll(scene_ori);
  915.       for(unsigned i=0; i<meshes_len; ++i)
  916.         if(meshes[i]) sceneApplyTransform(meshes[i]);
  917.       sceneApplyTransform(nullptr);
  918.       //(to fix this, simply move the previous 4 lines out of this code block)
  919.  
  920.     }
  921.  
  922.  
  923.     canvas->clear();
  924.     sceneRender(viewport_mod*((float)cfg.win_w/cfg.win_h), viewport_mod);
  925.  
  926.     #define text_margin (16/(CNV_DIV))
  927.     text_box(CENTER_TEXT, text_margin, 0, WIN_TITLE);
  928.  
  929.     switch(state.menu){
  930.       case MENU_QUIT     : {
  931.         //text_box(CENTER_TEXT, CENTER_TEXT, 0, "(there's supposed to be a\nspinning door here, but i\nhaven't modeled that yet)");
  932.         text_box(CENTER_TEXT, -text_margin-1, 0,
  933.                  "           \n-     QUIT GAME     >\n");
  934.       } break;
  935.       case MENU_PLAY     : {
  936.         text_box(CENTER_TEXT, -text_margin-1, 0,
  937.                  "           \n<    START GAME     >\n");
  938.         textf_box(CENTER_TEXT, -text_margin-1 - 27 - 8,
  939.                   "high score (%i asteroid%s): %.1f",
  940.                   cfg.num_asteroids, (cfg.num_asteroids!=1)?"s":"", cfg.high_scores[cfg.num_asteroids-1]);
  941.       } break;
  942.       case MENU_ASTEROIDS: {
  943.         textf_box(CENTER_TEXT, -text_margin-1,
  944.                   "                  ^\n< # OF ASTEROIDS: %i >\n                  V", cfg.num_asteroids);
  945.         textf_box(CENTER_TEXT, -text_margin-1 - 27 - 8,
  946.                   "high score: %.1f", cfg.high_scores[cfg.num_asteroids-1]);
  947.       } break;
  948.       case MENU_REBIND   : {
  949.         text_box(CENTER_TEXT, -text_margin-1, 0,
  950.                  "                 \n<  REBIND CONTROLS  -\n");
  951.       } break;
  952.       case MENU_REBINDING: {
  953.         textf_box(CENTER_TEXT, CENTER_TEXT,
  954.                   "Press a key for \"%s\"\n(backspace will reset all bindings to their defaults!)",
  955.                   bind_names[state.which_rebind]);
  956.       } break;
  957.       default:;
  958.     }
  959.  
  960.     canvas_present(16);
  961.  
  962.  
  963.   }
  964.  
  965.   if(state.quit) goto _QUIT;
  966.  
  967.   asteroid_menu(0).p.scale = old_scale[0];
  968.   asteroid_menu(1).p.scale = old_scale[1];
  969.   asteroid_menu(2).p.scale = old_scale[2];
  970.   asteroid_menu(3).p.scale = old_scale[3];
  971.  
  972.  
  973.  
  974.   while(!state.cursorIsTrapped){
  975.     if(!handleEvents()) break;
  976.     text_box(CENTER_TEXT, CENTER_TEXT, 0, "(click in the window to start)");
  977.     canvas->present(16);
  978.   }
  979.  
  980.   srand((unsigned)( SDL_GetTicks64() + (unsigned)(cfg.lastrand*10000) ));
  981.   cfg.lastrand = randf();
  982.  
  983.   if(!state.quit) goto _GAME;
  984.  
  985.  
  986.  
  987.  
  988.  
  989. /**********************************/ _QUIT: /**********************************/
  990.  
  991.   cfg_file = SDL_RWFromFile(CFG_FILENAME, "wb");
  992.   if(!cfg_file) throw SDL_GetError();
  993.   if(SDL_RWwrite(cfg_file, &cfg, 1, sizeof(cfg))<sizeof(cfg)) throw SDL_GetError();
  994.   if(SDL_RWclose(cfg_file)<0) throw SDL_GetError();
  995.  
  996.   for(unsigned i=0; i<meshes_len; ++i)
  997.     SAFE_DELETE(meshes[i]);
  998.  
  999.   return 0;
  1000.  
  1001.  
  1002.  
  1003.  
  1004.  
  1005. /**********************************/ _GAME: /**********************************/
  1006.  
  1007.   state.score = 0.0f;
  1008.   _reset_game:
  1009.  
  1010.   state.skybox_bounds = skybox_bounds_formula;
  1011.  
  1012.   mesh_skybox.p.scale  = state.skybox_bounds*2;
  1013.   mesh_gear.p.scale    = 0.0f;
  1014.   mesh_door.p.scale    = 0.0f;
  1015.   asteroid_menu(0).p.y = CLIP_FAR*2;
  1016.   asteroid_menu(1).p.y = CLIP_FAR*2;
  1017.   asteroid_menu(2).p.y = CLIP_FAR*2;
  1018.   asteroid_menu(3).p.y = CLIP_FAR*2;
  1019.  
  1020.   memory::set(&camera_old, 0, sizeof(camera_old));
  1021.   memory::set(&camera_cur, 0, sizeof(camera_cur));
  1022.   memory::set(&camera_new, 0, sizeof(camera_new));
  1023.   memory::set(&mesh_ship.p, 0, sizeof(mesh_ship.p));
  1024.   mesh_ship.p.scale = 1.0f;
  1025.  
  1026.   scene_camera.yaw   = 0.0f;
  1027.   scene_camera.pitch = ship_camera_down_tilt;
  1028.   scene_camera.roll  = 0.0f;
  1029.   scene_camera.x     = 0.0f;
  1030.   scene_camera.y     = ship_camera_y_offset;
  1031.   scene_camera.z     = -ship_camera_distance;
  1032.  
  1033.   unsigned ship_offset = mesh_ship.verts_len;
  1034.  
  1035.   state.active_asteroids = 0;
  1036.   for(int i=0; i<asteroid_count; ++i){
  1037.     mesh_asteroid(i).p.y = CLIP_FAR*2;
  1038.     state.asteroids[i].active = false;
  1039.   }
  1040.  
  1041.   for(int i=0; i<cfg.num_asteroids; ++i)
  1042.     asteroidCreate(i);
  1043.  
  1044.  
  1045.  
  1046.  
  1047.  
  1048.   _out_of_game_loop: //jumped to for the purpose of forcefully breaking the loop
  1049.   while(!state.quit  &&  state.menu == MENU_PLAYING){
  1050.     startTime = SDL_GetTicks64();
  1051.     if(!handleEvents()) break;
  1052.  
  1053.     if(!state.cursorIsTrapped){
  1054.       text_box(CENTER_TEXT,CENTER_TEXT, 0, "paused");
  1055.       canvas->present(16);
  1056.       continue;
  1057.     }
  1058.  
  1059.  
  1060.  
  1061.     bulletMoveAll();
  1062.  
  1063.     asteroidMoveAll();
  1064.  
  1065.     //move ship
  1066.     Vec3 moveVector(0.0f, 0.0f, 0.0f);
  1067.     if(state.pressed_right  ) moveVector.x += ship_speed_x;
  1068.     if(state.pressed_left   ) moveVector.x -= ship_speed_x;
  1069.     if(state.pressed_forward) moveVector.z += ship_speed_z;
  1070.     if(state.pressed_back   ) moveVector.z -= ship_speed_z;
  1071.     if(state.pressed_up     ) moveVector.y += ship_speed_y;
  1072.     if(state.pressed_down   ) moveVector.y -= ship_speed_y;
  1073.     moveCamera(-moveVector.x, -moveVector.y, -moveVector.z);
  1074.     keepInBounds(camera_cur);
  1075.  
  1076.  
  1077.  
  1078.     //break asteroids that are touching any active bullets
  1079.     checkForBulletHits();
  1080.  
  1081.     //'win' condition; reset game (without resetting score!)
  1082.     if(state.active_asteroids == 0) goto _reset_game;
  1083.  
  1084.  
  1085.  
  1086.     //since camera_cur is technically a world offset, the actual
  1087.      //player's placement is the direct opposite of that world offset
  1088.     Location negative_cur;
  1089.     negative_cur.x = -camera_cur.x;
  1090.     negative_cur.y = -camera_cur.y;
  1091.     negative_cur.z = -camera_cur.z;
  1092.  
  1093.     //check if player should be dead
  1094.     for(unsigned a=0; a<asteroid_count; ++a){
  1095.       if(!state.asteroids[a].active) continue;
  1096.  
  1097.       //*0.37f instead of *0.5f to account for inconsistent geometry
  1098.       float collide_dist = 0.5f + asteroid_sizes[(int)state.asteroids[a].type]*0.37f;
  1099.  
  1100.       if(locDistance(negative_cur, mesh_asteroid(a).p) < collide_dist){
  1101.         asteroidDestroy(a); //a neat touch
  1102.         state.menu = MENU_DEAD;
  1103.         goto _out_of_game_loop;
  1104.       }
  1105.  
  1106.     }
  1107.  
  1108.  
  1109.  
  1110.     mesh_ship.p.roll  *= ship_decay_multiplier_a;
  1111.     mesh_ship.p.pitch *= ship_decay_multiplier_a;
  1112.  
  1113.     scene_wka.setAll(scene_ori);
  1114.  
  1115.     for(unsigned i=1; i<meshes_len; ++i)
  1116.       if(meshes[i]) sceneApplyTransform(meshes[i]);
  1117.  
  1118.     //rotate all but the ship's verts
  1119.     translateWka(camera_cur, ship_offset, LOOP_END_MAX);
  1120.     rotateWka(camera_cur, ship_offset, LOOP_END_MAX);
  1121.  
  1122.     meshTransform(mesh_ship);
  1123.  
  1124.     sceneApplyTransform(nullptr);
  1125.  
  1126.  
  1127.  
  1128.     canvas->clear();
  1129.     sceneRender(viewport_mod*((float)cfg.win_w/cfg.win_h), viewport_mod);
  1130.     canvas->renderFillRect(&crosshair, 0xffffffff);
  1131.  
  1132.     #define brr_width 20
  1133.     #define brr_height 40
  1134.     #define brr_height_b (int)( (1.0f-((float)(state.active_bullets)/bullet_count))*brr_height )
  1135.     SDL_Rect bullets_remaining_rect = {CNV_W-brr_width, CNV_H-brr_height,
  1136.                                              brr_width,       brr_height};
  1137.     canvas->renderFillRectBordered(bullets_remaining_rect);
  1138.     bullets_remaining_rect.w -= 2;
  1139.     bullets_remaining_rect.h  = MAX(brr_height_b-2, 0);
  1140.     bullets_remaining_rect.x += 1;
  1141.     bullets_remaining_rect.y  = CNV_H-bullets_remaining_rect.h-1;
  1142.     canvas->renderFillRect(&bullets_remaining_rect, 0x00ff00);
  1143.     textf(4-brr_width/2, 3-brr_height/2, "%u", bullet_count-state.active_bullets);
  1144.  
  1145.     textf_box(0,0, "(backspace returns\nto the main menu)");
  1146.     textf_box(CENTER_TEXT,0, "score: %.1f", state.score);
  1147.  
  1148. #ifdef _DEBUG
  1149.     textf_box(0,-10-1, "active bullets: %i", state.bullets[0].active+state.bullets[1].active+state.bullets[2].active+state.bullets[3].active);
  1150.     textf_box(0,-1, "active asteroids: %i", state.active_asteroids);
  1151. #endif
  1152.  
  1153.     canvas_present(16);
  1154.  
  1155.   }
  1156.  
  1157.   mesh_ship.p.y = CLIP_FAR*2;
  1158.  
  1159.  
  1160.  
  1161.  
  1162.  
  1163.   while(!state.quit  &&  state.menu == MENU_DEAD){
  1164.     startTime = SDL_GetTicks64();
  1165.     if(!handleEvents()) break;
  1166.  
  1167.  
  1168.     //uncomment this if i end up wanting to continue animating scene after death
  1169.     /*
  1170.     bulletMoveAll();
  1171.     asteroidMoveAll();
  1172.     checkForBulletHits();
  1173.  
  1174.  
  1175.     scene_wka.setAll(scene_ori);
  1176.  
  1177.     for(unsigned i=0; i<meshes_len; ++i)
  1178.       if(meshes[i]) sceneApplyTransform(meshes[i]);
  1179.  
  1180.     translateWka(camera_cur, 0, LOOP_END_MAX);
  1181.     rotateWka(camera_cur, 0, LOOP_END_MAX);
  1182.  
  1183.     sceneApplyTransform(nullptr);
  1184.     */
  1185.  
  1186.  
  1187.     canvas->clear();
  1188.     //same visual bug with rebinding is utilized again here
  1189.     sceneRender(viewport_mod*((float)cfg.win_w/cfg.win_h), viewport_mod);
  1190.     if(state.score <= cfg.high_scores[cfg.num_asteroids-1]){
  1191.       textf_box(CENTER_TEXT, CENTER_TEXT, "score: %.1f", state.score);
  1192.  
  1193.     } else {
  1194.       textf_box(CENTER_TEXT, CENTER_TEXT, "score: %.1f (new high score!)", state.score);
  1195.  
  1196.     }
  1197.     canvas_present(16);
  1198.  
  1199.   }
  1200.  
  1201.  
  1202.  
  1203.  
  1204.  
  1205.   //note: this will in theory save the high score,
  1206.    //even in the event of something like alt+f4 being pressed!
  1207.   if(state.score > cfg.high_scores[cfg.num_asteroids-1])
  1208.     cfg.high_scores[cfg.num_asteroids-1] = state.score;
  1209.  
  1210.   if(state.quit) goto _QUIT;
  1211.   else           goto _MENU;
  1212.  
  1213. }
  1214.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement