Advertisement
Kitomas

2024-07-19 (5/13)

Jul 19th, 2024
130
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 43.44 KB | None | 0 0
  1. /******************************************************************************/
  2. /******************************************************************************/
  3. //"kit_xmp_sfx\src\kit_xmp_sfx\kit_SoundEngine_callbackXmp.cpp":
  4. #include "_kit_shared.hpp"
  5.  
  6.  
  7. namespace kit {
  8.  
  9.  
  10.  
  11.  
  12. static inline void applyFadeXmp(_SoundEngineOpaque* opq,
  13.                                 smp_ptr buffer, u32 buffer_len)
  14. {
  15.   f32 fDelta  = opq->fadeDelta;
  16.   f32 fVolume = opq->fadeVolumeXmp;
  17.  
  18.   u32 i = 0; //this index is shared, as the loops can jump to others at will
  19.  
  20.  
  21.   //FADING OUT
  22.   if(opq->fadeOutXmp){ _FadeOut:;
  23.     if(opq->streamIsMono){ //mono fade out
  24.       for(; i<buffer_len; ++i){
  25.         if(!opq->fadeOutXmp) goto _FadeIn;
  26.         buffer._s16[i] = (s16)(buffer._s16[i]*fVolume);
  27.         fVolume -= fDelta;
  28.         if(fVolume < 0.0f) fVolume = 0.0f;
  29.       }
  30.  
  31.     } else { //stereo fade out
  32.       for(; i<buffer_len; ++i){
  33.         if(!opq->fadeOutXmp) goto _FadeIn;
  34.         buffer._s16s[i] *= fVolume;
  35.         fVolume -= fDelta;
  36.         if(fVolume < 0.0f) fVolume = 0.0f;
  37.       }
  38.  
  39.     }
  40.  
  41.     //stop music if fade out finishes
  42.     if(fVolume <= 0.0f){
  43.       xmp_end_player(opq->xmpCtx);
  44.       opq->xmpPlaying = false;
  45.     }
  46.  
  47.  
  48.   //FADING IN
  49.   } else if(fVolume < 1.0f){ _FadeIn:;
  50.     if(opq->streamIsMono){ //mono fade out
  51.       for(; i<buffer_len; ++i){
  52.         if(opq->fadeOutXmp) goto _FadeOut;
  53.         else if(fVolume >= 1.0f){ fVolume = 1.0f; break; }
  54.         buffer._s16[i] = (s16)(buffer._s16[i]*fVolume);
  55.         fVolume += fDelta;
  56.       }
  57.  
  58.     } else {
  59.       for(; i<buffer_len; ++i){ //stereo fade out
  60.         if(opq->fadeOutXmp) goto _FadeOut;
  61.         else if(fVolume >= 1.0f){ fVolume = 1.0f; break; }
  62.         buffer._s16s[i] *= fVolume;
  63.         fVolume += fDelta;
  64.       }
  65.  
  66.     }
  67.  
  68.   }
  69.  
  70.  
  71.   opq->fadeVolumeXmp = fVolume; //update fade volume to new value
  72. }
  73.  
  74.  
  75.  
  76.  
  77. //this is a ThreadFunction that uses a _SoundEngineOpaque* as its userdata.
  78. //the SoundEngine's mutex should be locked at this point,
  79.  //so it should be fine to modify xmp-related members of its opaque struct
  80. s32 _SoundEngine_callbackXmp(void* userdata){
  81.   _SoundEngineOpaque* opq = (_SoundEngineOpaque*)userdata;
  82.  
  83.   smp_ptr buffer        = opq->xmpBuffer; //s16 or smp_s16s
  84.   u32     buffer_len    = opq->streamSampleFrames;
  85.   int     buffer_stereo = !opq->streamIsMono; //int so i can do << w/o a warning
  86.   s32     buffer_size   = buffer_len * sizeof(s16) << buffer_stereo;
  87.   bool    checkLoop     = opq->xmpCheckLoop;
  88.  
  89.   memory::set(buffer._data, 0, buffer_size);
  90.   xmp_play_buffer(opq->xmpCtx, buffer._data, buffer_size, checkLoop);
  91.  
  92.  
  93.  
  94.   //apply pan and volume, while interpolating their values
  95.    //from their old state to its new one
  96.   f32 t           = 0.0f;
  97.   f32 t_increment = 1.0f/buffer_len;
  98.  
  99.   smp_f32s volume_old = opq->volumeXmp_old;
  100.   smp_f32s volume_new = opq->volumeXmp_new;
  101.  
  102.   opq->volumeXmp_old = volume_new; //update old volume to the new one
  103.  
  104.  
  105.   if(buffer_stereo){
  106.     //panning is only applied if samples are stereo,
  107.      //so there's no need to declare it outside of this scope
  108.     f32 pan_old = opq->panXmp_old;
  109.     f32 pan_new = opq->panXmp_new;
  110.  
  111.     opq->panXmp_old = pan_new; //update old pan to the new one
  112.  
  113.     for(u32 i=0; i<buffer_len; ++i){
  114.       smp_f32s sampleIn = buffer._s16s[i]; //get current sample
  115.       sampleIn *= interpolateF32S(volume_old, volume_new, t); //apply volume
  116.       smp_f32s sampleOut = applyPan(sampleIn, LERP2(pan_old,pan_new, t) ); //apply pan
  117.       buffer._s16s[i] = to_s16s(sampleOut); //update current sample to new value
  118.       t += t_increment; //add to interpolation t value
  119.     }
  120.  
  121.   } else { //mono
  122.     for(u32 i=0; i<buffer_len; ++i){
  123.       f32 sample = s16_conv( buffer._s16[i] ); //get current sample
  124.       sample *= LERP2(volume_old.l, volume_new.l, t); //apply volume
  125.       buffer._s16[i] = to_s16( sample ); //update current sample to new value
  126.       t += t_increment; //add to interpolation t value
  127.     }
  128.  
  129.   }
  130.  
  131.  
  132.  
  133.   //handle fade-in and fade-outs
  134.   applyFadeXmp(opq, buffer, buffer_len);
  135.  
  136.  
  137.  
  138.   return 0;
  139. }
  140.  
  141.  
  142.  
  143.  
  144. }; /* namespace kit */
  145. /******************************************************************************/
  146. /******************************************************************************/
  147. //"kit_xmp_sfx\src\kit_xmp_sfx\kit_SoundEngine_music.cpp":
  148. #include "_kit_shared.hpp"
  149.  
  150. #define KIT_INVALID_SOUNDENGINE "invalid sound engine"
  151.  
  152. #define KIT_IS_INVALID_SOUNDENGINE (!_valid && !_constructing)
  153.  
  154.  
  155. namespace kit {
  156.  
  157.  
  158. //i'll keep these around, and uncomment one if/when i end up using it
  159. static const char _kit_xmp_err_format[]   = "XMP_ERROR_FORMAT";
  160. static const char _kit_xmp_err_depack[]   = "XMP_ERROR_DEPACK";
  161. static const char _kit_xmp_err_load[]     = "XMP_ERROR_LOAD";
  162. static const char _kit_xmp_err_system[]   = "XMP_ERROR_SYSTEM";
  163. static const char _kit_xmp_err_internal[] = "XMP_ERROR_INTERNAL";
  164. static const char _kit_xmp_err_invalid[]  = "XMP_ERROR_INVALID";
  165. //static const char _kit_xmp_err_state[]    = "XMP_ERROR_STATE";
  166.  
  167.  
  168.  
  169.  
  170. bool SoundEngine::musicIsPlaying(){
  171.   return _opq->xmpPlaying; //no lock needed
  172. }
  173.  
  174.  
  175.  
  176. bool SoundEngine::musicIsModuleLoaded(){
  177.   return _opq->xmpModuleLoaded; //no lock needed
  178. }
  179.  
  180.  
  181.  
  182.  
  183. void SoundEngine::musicSetPan(f32 pan){
  184.   if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  185.  
  186.   lock(true);
  187.   _opq->panXmp_new = convertPan(pan);
  188.   lock(false);
  189. }
  190.  
  191.  
  192.  
  193. void SoundEngine::musicSetVolume(f32 volumeL, f32 volumeR){
  194.   if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  195.  
  196.   smp_f32s volumeNew(volumeL, volumeR);
  197.  
  198.   lock(true);
  199.   _opq->volumeXmp_new = volumeNew;
  200.   lock(false);
  201. }
  202.  
  203.  
  204.  
  205. void SoundEngine::musicSetPlayback(bool playing){
  206.   if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  207.  
  208.   //locking is probably not necessary here, due to the way fades work
  209.  
  210.   _opq->fadeOutXmp = !playing;
  211.  
  212.   if(playing && !_opq->xmpPlaying){
  213.     int formatFlags = (_opq->streamIsMono) ? XMP_FORMAT_MONO : 0;
  214.  
  215.     int result = xmp_start_player(_opq->xmpCtx, (int)_opq->streamSampleRate, formatFlags);
  216.  
  217.     if(result == XMP_ERROR_INTERNAL) throw _kit_xmp_err_internal;
  218.     if(result == XMP_ERROR_INVALID ) throw _kit_xmp_err_invalid;
  219.     if(result == XMP_ERROR_SYSTEM  ) throw _kit_xmp_err_system;
  220.  
  221.     _opq->xmpPlaying = true;
  222.  
  223.  
  224.     int interpolation_mode = (_opq->xmpUseNearest) ? XMP_INTERP_NEAREST : XMP_INTERP_LINEAR;
  225.  
  226.     xmp_set_player(_opq->xmpCtx, XMP_PLAYER_INTERP, interpolation_mode);
  227.  
  228.   }
  229.  
  230.   //(xmp_end_player is called inside the callback, not here)
  231.  
  232. }
  233.  
  234.  
  235.  
  236. void SoundEngine::musicSetPlaybackAndWait(bool playing){
  237.   //(musicSetPlayback already does a validity check)
  238.   //if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  239.  
  240.   bool wasPlaying = _opq->xmpPlaying;
  241.   //mutex is locked inside musicSetPlayback,
  242.    //and there's no need to lock in this function otherwise
  243.   musicSetPlayback(playing);
  244.  
  245.  
  246.   if((playing && !wasPlaying)  ||  (!playing && wasPlaying))
  247.     time::sleep( (u32)(_fadeDeltaSeconds*1000) ); //yep, this is all it does (lol)
  248. }
  249.  
  250.  
  251.  
  252. void SoundEngine::musicSetCheckLoop(bool enable){
  253.   if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  254.  
  255.   lock(true);
  256.   _opq->xmpCheckLoop = enable;
  257.   lock(false);
  258. }
  259.  
  260.  
  261.  
  262. void SoundEngine::musicSetUseNearest(bool enable){
  263.   if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  264.  
  265.   lock(true);
  266.   _opq->xmpUseNearest = enable;
  267.   lock(false);
  268. }
  269.  
  270.  
  271.  
  272. void SoundEngine::musicSetVolumeForced(f32 volumeL, f32 volumeR){
  273.   if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  274.  
  275.   smp_f32s volumeNew(volumeL, volumeR);
  276.  
  277.   lock(true);
  278.   _opq->volumeXmp_old = volumeNew;
  279.   _opq->volumeXmp_new = volumeNew;
  280.   lock(false);
  281. }
  282.  
  283.  
  284.  
  285.  
  286. void SoundEngine::musicLoadModule(const char* filePath){
  287.   //musicReleaseModule already does a validity check
  288.   //if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  289.  
  290.   if(!fileio::fileExists(filePath)) throw "file does not exist";
  291.  
  292.  
  293.   musicReleaseModule();
  294.  
  295.  
  296.   int result = xmp_load_module(_opq->xmpCtx, filePath);
  297.  
  298.   if(result == XMP_ERROR_FORMAT) throw _kit_xmp_err_format;
  299.   if(result == XMP_ERROR_DEPACK) throw _kit_xmp_err_depack;
  300.   if(result == XMP_ERROR_LOAD  ) throw _kit_xmp_err_load;
  301.   if(result == XMP_ERROR_SYSTEM) throw _kit_xmp_err_system;
  302.  
  303.  
  304.   _opq->xmpModuleLoaded = true; //will be set only if load succeeds
  305. }
  306.  
  307.  
  308.  
  309. void SoundEngine::musicReleaseModule(){
  310.   if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  311.  
  312.   if(_opq->xmpPlaying) xmp_end_player(_opq->xmpCtx);
  313.  
  314.   if(_opq->xmpModuleLoaded) xmp_release_module(_opq->xmpCtx);
  315.  
  316.   _opq->xmpModuleLoaded = false;
  317. }
  318.  
  319.  
  320.  
  321.  
  322. void SoundEngine::musicStopForced(){
  323.   if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  324.  
  325.   if(_opq->xmpPlaying){
  326.     xmp_end_player(_opq->xmpCtx);
  327.     _opq->xmpPlaying = false;
  328.  
  329.   }
  330. }
  331.  
  332.  
  333.  
  334.  
  335. }; /* namespace kit */
  336. /******************************************************************************/
  337. /******************************************************************************/
  338. //"kit_xmp_sfx\src\kit_xmp_sfx\kit_SoundEngine_sfx.cpp":
  339. #include "_kit_shared.hpp"
  340.  
  341. #define KIT_INVALID_SOUNDENGINE "invalid sound engine"
  342. #define KIT_TRACK_OOB _kit_track_oob
  343.  
  344. #define KIT_IS_INVALID_SOUNDENGINE (!_valid && !_constructing)
  345. #define KIT_IS_TRACK_OOB (track < 0  ||  track >= _opq->tracks_len)
  346.  
  347.  
  348. namespace kit {
  349.  
  350.  
  351. const char _kit_track_oob[] = "track number is out of bounds";
  352.  
  353.  
  354.  
  355.  
  356. bool SoundEngine::sfxIsTrackPlaying(s32 track){
  357.   if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  358.   if(KIT_IS_TRACK_OOB) throw KIT_TRACK_OOB;
  359.  
  360.   return _opq->tracks[track].audio != nullptr;
  361. }
  362.  
  363.  
  364.  
  365. u32 SoundEngine::sfxGetActiveTracks(){
  366.   if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  367.  
  368.   u32 activeTracks = 0;
  369.  
  370.   _SoundEngineTrack* tracks     = _opq->tracks;
  371.   s32                tracks_len = _opq->tracks_len;
  372.  
  373.   lock(true);
  374.   for(s32 t=0; t<tracks_len; ++t)
  375.     if(tracks[t].audio != nullptr) ++activeTracks;
  376.   lock(false);
  377.  
  378.   return activeTracks;
  379. }
  380.  
  381.  
  382.  
  383.  
  384. void SoundEngine::sfxSetPanAll(f32 pan){
  385.   if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  386.  
  387.   lock(true);
  388.   _opq->panSfx_new = convertPan(pan);
  389.   lock(false);
  390. }
  391.  
  392.  
  393.  
  394. void SoundEngine::sfxSetPan(s32 track, f32 pan){
  395.   if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  396.   if(KIT_IS_TRACK_OOB) throw KIT_TRACK_OOB;
  397.  
  398.   lock(true);
  399.   _opq->tracks[track].pan_new = convertPan(pan);
  400.   lock(false);
  401. }
  402.  
  403.  
  404.  
  405. void SoundEngine::sfxSetVolumeAll(f32 volumeL, f32 volumeR){
  406.   if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  407.  
  408.   smp_f32s volumeNew(volumeL, volumeR);
  409.  
  410.   lock(true);
  411.   _opq->volumeSfx_new = volumeNew;
  412.   lock(false);
  413. }
  414.  
  415.  
  416.  
  417. void SoundEngine::sfxSetVolume(s32 track, f32 volumeL, f32 volumeR){
  418.   if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  419.   if(KIT_IS_TRACK_OOB) throw KIT_TRACK_OOB;
  420.  
  421.   smp_f32s volumeNew(volumeL, volumeR);
  422.  
  423.   lock(true);
  424.   _opq->tracks[track].volume_new = volumeNew;
  425.   lock(false);
  426. }
  427.  
  428.  
  429.  
  430. void SoundEngine::sfxSetVolumeDelta(s32 track, f32 deltaSecondsL, f32 deltaSecondsR){
  431.   if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  432.   if(KIT_IS_TRACK_OOB) throw KIT_TRACK_OOB;
  433.  
  434.   f64 streamSampleRate = _opq->streamSampleRate;
  435.  
  436.   smp_f32s volumeDelta((1.0f/deltaSecondsL)/streamSampleRate,
  437.                        (1.0f/deltaSecondsR)/streamSampleRate);
  438.  
  439.   //locking when modifying deltas is unnecessary
  440.   _opq->tracks[track].volumeDelta = volumeDelta;
  441. }
  442.  
  443.  
  444.  
  445. void SoundEngine::sfxSetSpeedDelta(s32 track, f64 deltaSeconds){
  446.   if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  447.   if(KIT_IS_TRACK_OOB) throw KIT_TRACK_OOB;
  448.  
  449.   f64 speedDelta = (1.0f/deltaSeconds)/_opq->streamSampleRate;
  450.  
  451.   //locking when modifying deltas is unnecessary
  452.   _opq->tracks[track].speedDelta = speedDelta;
  453. }
  454.  
  455.  
  456.  
  457. void SoundEngine::sfxSetVolumeAllForced(f32 volumeL, f32 volumeR){
  458.   if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  459.  
  460.   smp_f32s volumeNew(volumeL, volumeR);
  461.  
  462.   lock(true);
  463.   _opq->volumeSfx_old = volumeNew;
  464.   _opq->volumeSfx_new = volumeNew;
  465.   lock(false);
  466. }
  467.  
  468.  
  469.  
  470. void SoundEngine::sfxSetVolumeForced(s32 track, f32 volumeL, f32 volumeR){
  471.   if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  472.   if(KIT_IS_TRACK_OOB) throw KIT_TRACK_OOB;
  473.  
  474.   smp_f32s volumeNew(volumeL, volumeR);
  475.  
  476.   lock(true);
  477.   _opq->tracks[track].volume_old = volumeNew;
  478.   _opq->tracks[track].volume_new = volumeNew;
  479.   lock(false);
  480. }
  481.  
  482.  
  483.  
  484.  
  485. bool SoundEngine::sfxWaitForTracks(size_t timeoutMS){
  486.   u64 timeStamp = time::getTicks(); //intentionally done before validity check
  487.   if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  488.  
  489.   if(timeoutMS == 0) timeoutMS = KIT_U64_MAX; //if 0 is given, wait indefinitely
  490.  
  491.   u64 timeoutTicks = timeoutMS * (time::getTicksPerSecond()/1000);
  492.  
  493.   //sfxGetActiveTracks already locks, so there's no need to do it here
  494.   while(sfxGetActiveTracks() > 0){
  495.     time::sleep(10);
  496.     if((time::getTicks()-timeStamp) > timeoutTicks) return false;
  497.   }
  498.  
  499.   return true;
  500. }
  501.  
  502.  
  503.  
  504. bool SoundEngine::sfxWaitForTrack(s32 track, size_t timeoutMS){
  505.   u64 timeStamp = time::getTicks();
  506.   if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  507.   if(KIT_IS_TRACK_OOB) throw KIT_TRACK_OOB;
  508.  
  509.   if(timeoutMS == 0) timeoutMS = KIT_U64_MAX;
  510.  
  511.   u64 timeoutTicks = timeoutMS * (time::getTicksPerSecond()/1000);
  512.  
  513.   _SoundEngineTrack& _track = _opq->tracks[track];
  514.   while(_track.audio != nullptr){
  515.     time::sleep(5);
  516.     if((time::getTicks()-timeStamp) > timeoutTicks) return false;
  517.   }
  518.  
  519.   return true; //will return true only if track finished before timeout
  520. }
  521.  
  522.  
  523.  
  524. s32 SoundEngine::sfxPlay(const AudioData* audio,
  525.                          f32 volumeL, f32 volumeR,
  526.                          f64 speed, f32 pan)
  527. {
  528.   if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  529.   //streamTimestamp declared after validity check,
  530.    //just to make sure that stream was actually created
  531.   f64 streamTimestamp = _opq->stream->getTime();
  532.   if(audio == nullptr) throw "audio = nullptr";
  533.  
  534.   //compiler is complaining about const qualifiers unless i cast audio here
  535.   const AudioDataHeader* audioDataHeader = ((AudioData*)audio)->getData();
  536.   if(audioDataHeader->magic != KIT_MAGIC_KPM) throw "audio is invalid";
  537.  
  538.  
  539.   //will return -1 if no available track was found
  540.   s32 queuedTrackNum = -1;
  541.  
  542.   _SoundEngineTrack* tracks     = _opq->tracks;
  543.   s32                tracks_len = _opq->tracks_len;
  544.  
  545.  
  546.   lock(true);
  547.   for(s32 t=0; t<tracks_len; ++t){
  548.     _SoundEngineTrack& track = tracks[t];
  549.  
  550.  
  551.     if(track.audio == nullptr){
  552.       track.timestamp      = streamTimestamp;
  553.  
  554.     //track.position       = <set inside callback>;
  555.  
  556.       track.speed_old      = speed;
  557.       track.speed_new      = speed;
  558.  
  559.       track.speedDelta     = 0.0;
  560.  
  561.       track.pan_old        = pan;
  562.       track.pan_new        = pan;
  563.  
  564.       track.volume_old.l   = volumeL;
  565.       track.volume_old.r   = volumeR;
  566.       track.volume_new.l   = volumeL;
  567.       track.volume_new.r   = volumeR;
  568.  
  569.       track.volumeDelta.l  = 0.0f;
  570.       track.volumeDelta.r  = 0.0f;
  571.  
  572.       track.volumeMaster.l = audio->volumeL;
  573.       track.volumeMaster.r = audio->volumeR;
  574.  
  575.       track.loops          = audioDataHeader->loopCount;
  576.       track.stopping       = false;
  577.  
  578.       //set last to make sure that all relevant members
  579.        //of the track are set before beginning
  580.       track.audio         = audioDataHeader;
  581.  
  582.       queuedTrackNum = t; break;
  583.     }
  584.  
  585.   }
  586.   lock(false);
  587.  
  588.  
  589.   return queuedTrackNum;
  590. }
  591.  
  592.  
  593.  
  594. void SoundEngine::sfxStop(s32 track){
  595.   if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  596.   if(KIT_IS_TRACK_OOB) throw KIT_TRACK_OOB;
  597.  
  598.   //'stop' the track by setting a fade-out of _fadeDeltaSeconds,
  599.    //whereupon the track will *actually* stop automatically
  600.  
  601.   const smp_f32s fadeoutDelta( (1.0f/(-_fadeDeltaSeconds))/_opq->streamSampleRate );
  602.  
  603.   //locking when modifying deltas is unnecessary
  604.   _opq->tracks[track].volumeDelta = fadeoutDelta;
  605. }
  606.  
  607.  
  608.  
  609. void SoundEngine::sfxStopForced(s32 track){
  610.   if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  611.   if(KIT_IS_TRACK_OOB) throw KIT_TRACK_OOB;
  612.  
  613.   //locking (probably) isn't necessary here, due to the way tracks are managed
  614.   _opq->tracks[track].audio = nullptr; //forcibly mark track as completed
  615. }
  616.  
  617.  
  618.  
  619. void SoundEngine::sfxStopAll(){
  620.   if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  621.  
  622.   const smp_f32s fadeoutDelta( (1.0f/(-_fadeDeltaSeconds))/_opq->streamSampleRate );
  623.  
  624.   _SoundEngineTrack* tracks     = _opq->tracks;
  625.   s32                tracks_len = _opq->tracks_len;
  626.  
  627.   //locking when modifying deltas is unnecessary
  628.   for(s32 t=0; t<tracks_len; ++t) tracks[t].volumeDelta = fadeoutDelta;
  629. }
  630.  
  631.  
  632.  
  633. void SoundEngine::sfxStopAllForced(){
  634.   if(KIT_IS_INVALID_SOUNDENGINE) throw KIT_INVALID_SOUNDENGINE;
  635.  
  636.   _SoundEngineTrack* tracks     = _opq->tracks;
  637.   s32                tracks_len = _opq->tracks_len;
  638.  
  639.   //locking (probably) isn't necessary here, due to the way tracks are managed
  640.   for(s32 t=0; t<tracks_len; ++t) tracks[t].audio = nullptr;
  641. }
  642.  
  643.  
  644.  
  645.  
  646. }; /* namespace kit */
  647. /******************************************************************************/
  648. /******************************************************************************/
  649. //"kw32g\src\globals.cpp":
  650. #include <globals.hpp>
  651. #include <tile.hpp>
  652. #include <player.hpp>
  653. #include <object.hpp>
  654. #include <save.hpp>
  655.  
  656. #include <stdio.h>
  657. #include <stdlib.h>
  658.  
  659. using namespace kit;
  660.  
  661.  
  662.  
  663.  
  664.  
  665. //under normal conditions this should never be called
  666.  //(as in, if i put a type 0 object in a scene, i did something wrong)
  667. bool obj_dummy(Object* _obj_a, bool render_only){
  668.   _printf("obj_dummy() was called!\n");
  669.   return false;
  670. }
  671.  
  672.  
  673.  
  674.  
  675.  
  676. char falseStr[] = "false";
  677. char trueStr[]  = "true" ;
  678.  
  679. f32          gl_death_prog           = DEATH_PROG_MAX;
  680. Thread*      gl_audioManager_thread  = nullptr;
  681. bool         gl_audioManager_exit    = false;
  682. bool         gl_audioManager_occ_thr = false;
  683. bool         gl_audioManager_occ_sfx = false;
  684. bool         gl_saveRead_called   = false;
  685. u64          gl_tickCounter       = 0;
  686. TimerSimple* gl_frameTimer        = nullptr;
  687. SoundEngine* gl_snd               = nullptr;
  688. Window*      gl_win               = nullptr;
  689. BitmapFont*  gl_text              = nullptr;
  690. FStr*        gl_fstr              = nullptr;
  691. Bitmap*      gl_spritesheetPlayer = nullptr;
  692. Bitmap*      gl_spritesheetFont   = nullptr; //a copy of gl_text's texture atlas
  693. Bitmap*      gl_spritesheetAbilityIndicators = nullptr;
  694.  
  695.  
  696.  
  697. //+1 for the array lengths, since the first valid id is 1, not 0
  698. ControlStates    gl_ctrlStates;
  699. Player           gl_player;
  700. Scene            gl_scene;
  701. SceneDescriptor* gl_scenes[gl_scenes_len+1];
  702. u16              gl_scene_indices[gl_scenes_len+1];
  703. Object*          gl_objsCache[gl_scenes_len+1];
  704. Bitmap*          gl_backgrounds[gl_backgrounds_len+1];
  705. Bitmap*          gl_tileset_missing = nullptr;
  706. Bitmap*          gl_tilesets[gl_tilesets_len+1];
  707. Bitmap*          gl_objImg[gl_objImg_len+1];
  708. AudioData*       gl_ambience[gl_ambience_len+1];
  709. SoundEffect*     gl_sfx[gl_sfx_len+1];
  710.  
  711.  
  712. const char gl_sfxNames[gl_sfx_len+1][32] = { //file paths, relative to ./dat/sfx/
  713.   "(none)",              // 0
  714.   "player_footstep.qoa", // 1
  715.   "player_jumping.qoa",  // 2
  716.   "player_landing.qoa",  // 3
  717.   "activate_1.qoa",      // 4
  718.   "ability_pickup.qoa",  // 5
  719.   "ability_removed.qoa", // 6
  720.   "nope_1.qoa",          // 7
  721.   "dlelele.qoa",         // 8
  722.   "fwshwshwsh.qoa",      // 9
  723.   "death.qoa"            //10
  724. };
  725.  
  726. //.x is volume, .y is speedRange (which should be casted to f64)
  727. #define PLV_ PLAYER_LANDING_VOLUME
  728. const shape::fpoint gl_sfxSettings[gl_sfx_len+1] = {
  729.   { .00f, .00f }, // 0 = none
  730.   { .02f, .10f }, // 1 = player_footstep
  731.   { .03f, .00f }, // 2 = player_jumping
  732.   { PLV_, .20f }, // 3 = player_landing
  733.   { .08f, .00f }, // 4 = activate_1
  734.   { .08f, .00f }, // 5 = ability_pickup
  735.   { .07f, .00f }, // 6 = ability_removed
  736.   { .07f, .00f }, // 7 = nope_1
  737.   { .07f, .00f }, // 8 = dlelele
  738.   { .07f, .00f }, // 9 = fwshwshwsh
  739.   { .13f, .00f }, //10 = death
  740. };
  741. #undef PLV_
  742.  
  743.  
  744. const char gl_music[gl_music_len+1][32] = { //file paths, relative to ./dat/music/
  745.   "(no change)",     //0
  746.   "sine_half.mod",   //1
  747.   "SilverSurfer.xm", //2
  748.   "2ND_PM.S3M",      //3
  749.   "tao tao.xm",      //4
  750. };
  751.  
  752.  
  753. const Object_TickCallback gl_objCallbacks[gl_objCallbacks_len+1] = {
  754.   obj_dummy          , //0 = no object
  755.   obj_1lever         , //1 = lever that controls a binary scene state
  756.   obj_ability        , //2 = for enabling abilities
  757.   obj_ability_remover, //3 = for disabling abilities
  758.   obj_generic_sprite , //4 = generic sprite with optional animation
  759.   obj_msgbox         , //5 = a messagebox that shows when colliding w/ it (optionally always)
  760.   obj_save_interface , //6 = a way to save, load, or wipe save file data
  761.   obj_teleporter     , //7 = teleports the player to a given position and scene
  762.   obj_death_zone     , //8 = a static area that kills the player on touch
  763.   obj_death_ball     , //9 = a ball of death that also kills the player on touch
  764. };
  765.  
  766.  
  767. #define TEXT_LINES_DEFINITION
  768. #include <text_lines.hpp>
  769.  
  770.  
  771.  
  772.  
  773.  
  774. size_t handleInit(){ //returns number of allocations from snd
  775.   gl_frameTimer = new TimerSimple;
  776.   gl_frameTimer->setTimer(0.101);
  777.  
  778.  
  779.   srand((u32)time::getTicks());
  780.  
  781.  
  782.  
  783.   size_t allocations_before_snd = memory::getNumAllocations();
  784.  
  785.   gl_snd = new SoundEngine(SND_TRACK_COUNT);
  786.   gl_snd->streamStart();
  787.  
  788.   size_t allocations_after_snd = memory::getNumAllocations();
  789.   size_t snd_allocations = allocations_after_snd - allocations_before_snd;
  790.  
  791.  
  792.  
  793.   gl_win = new Window(WINDOW_TITLE, CANVSIZ_X*2, CANVSIZ_Y*2,
  794.                       WINFLAG_RESIZABLE | WINFLAG_HIDDEN,
  795.                       WINPOS_CENTERED, WINPOS_CENTERED,
  796.                       CANVSIZ_X, CANVSIZ_Y, false);
  797.  
  798.   gl_text = new BitmapFont("dat/img/_font8x8.qoi", gl_win);
  799.  
  800.   //unnecessary, given that the entire game
  801.    //loads into memory in less than a second
  802.   //gl_textf(0, 0, "loading...");
  803.   //gl_win->present(true);
  804.   //gl_win->setVisibility(true);
  805.  
  806.  
  807.  
  808.   gl_spritesheetPlayer = new Bitmap("dat/img/spritesheet_player.qoi", gl_win);
  809.   gl_spritesheetFont   = new Bitmap("dat/img/_font8x8.qoi", gl_win);
  810.   gl_spritesheetAbilityIndicators = new Bitmap("dat/img/ability_indicators.qoi", gl_win);
  811.  
  812.  
  813.  
  814.   //initialize asset pointer arrays
  815.   memset0(gl_scenes); //(these memsets are probably redundant, but just in case)
  816.   memset0(gl_objsCache);
  817.   memset0(gl_backgrounds);
  818.   memset0(gl_tilesets);
  819.   memset0(gl_ambience);
  820.   char* fstr_ptr = gl_fstr->ptr(); //valid until gl_fstr gets destroyed
  821.  
  822.  
  823.   //scenes
  824.   for(u32 i=1; i<(gl_scenes_len+1); ++i){
  825.     //(file check is done inside SceneDescriptor's constructor)
  826.     gl_scenes[i] = new SceneDescriptor(i);
  827.     gl_scene_indices[i] = gl_scenes[i]->scene;
  828.  
  829.   }
  830.  
  831.   //object caches
  832.   for(u32 i=1; i<(gl_scenes_len+1); ++i){
  833.     //unlike the other asset arrays, the objects in an object cache are zero-indexed
  834.     size_t cache_size = sizeof(Object) * gl_scenes[i]->objs_len;
  835.     if(cache_size) gl_objsCache[i] = (Object*)memory::alloc2( cache_size );
  836.     loadObjects(true, i); //force load all objects in that scene
  837.  
  838.   }
  839.  
  840.   //backgrounds
  841.   for(u32 i=1; i<(gl_backgrounds_len+1); ++i){
  842.     gl_fstr->fmt("dat/background/background_%u.qoi", i);
  843.     if(!fileio::fileExists(fstr_ptr))
  844.       throw (const char*)gl_fstr->fmt("\"dat/background/background_%u.qoi\" doesn't exist", i);
  845.     gl_backgrounds[i] = new Bitmap(fstr_ptr, gl_win);
  846.  
  847.   }
  848.  
  849.   //tilesets
  850.   for(u32 i=1; i<(gl_tilesets_len+1); ++i){
  851.     gl_fstr->fmt("dat/tileset/tileset_%u.qoi", i);
  852.     if(!fileio::fileExists(fstr_ptr))
  853.       throw (const char*)gl_fstr->fmt("\"dat/tileset/tileset_%u.qoi\" doesn't exist", i);
  854.     gl_tilesets[i] = new Bitmap(fstr_ptr, gl_win);
  855.  
  856.   }
  857.  
  858.   //object images
  859.   for(u32 i=1; i<(gl_objImg_len+1); ++i){
  860.     gl_fstr->fmt("dat/obj-img/obj-img_%u.qoi", i);
  861.     if(!fileio::fileExists(fstr_ptr))
  862.       throw (const char*)gl_fstr->fmt("\"dat/obj-img/obj-img_%u.qoi\" doesn't exist", i);
  863.     gl_objImg[i] = new Bitmap(fstr_ptr, gl_win);
  864.   }
  865.  
  866.   //ambience
  867.   for(u32 i=1; i<(gl_ambience_len+1); ++i){
  868.     gl_fstr->fmt("dat/ambience/ambience_%u.qoa", i);
  869.     if(!fileio::fileExists(fstr_ptr))
  870.       throw (const char*)gl_fstr->fmt("\"dat/ambience/ambience_%u.qoa\" doesn't exist", i);
  871.     gl_ambience[i] = new AudioData(fstr_ptr, gl_snd);
  872.  
  873.     AudioDataHeader* adata = (AudioDataHeader*)gl_ambience[i]->getData();
  874.     if(!adata) throw "ambience's ._data member was nullptr";
  875.     adata->loopCount = 0xFFFF; //infinite loop
  876.  
  877.   }
  878.  
  879.   //sfx
  880.   for(u32 i=1; i<(gl_sfx_len+1); ++i){
  881.     gl_fstr->fmt("dat/sfx/%s", gl_sfxNames[i]);
  882.     if(!fileio::fileExists(fstr_ptr))
  883.       throw (const char*)gl_fstr->fmt("\"dat/sfx/%s\" doesn't exist", gl_sfxNames[i]);
  884.     f32 volume     =      gl_sfxSettings[i].x;
  885.     f64 speedRange = (f64)gl_sfxSettings[i].y;
  886.     gl_sfx[i] = new SoundEffect(fstr_ptr, volume, speedRange);
  887.  
  888.   }
  889.  
  890.  
  891.   //get length of gl_text_lines
  892.   gl_text_lines_len = 0;
  893.   for(char** p = (char**)gl_text_lines; *p != 0; ++p)
  894.     ++gl_text_lines_len;
  895.  
  896.  
  897.  
  898.   gl_tileset_missing = new Bitmap("dat/tileset/tileset_missing.qoi", gl_win);
  899.  
  900.  
  901.  
  902.   gl_audioManager_thread = new Thread(audioManager);
  903.  
  904.  
  905.  
  906.   gl_frameTimer->wait(2000);
  907.  
  908.   gl_win->setVisibility(true);
  909.   gl_win->setFocus(true);
  910.  
  911.   return snd_allocations;
  912.  
  913. }
  914.  
  915.  
  916.  
  917.  
  918.  
  919. void handleQuit(){
  920.   if(gl_frameTimer) gl_frameTimer->setTimer(0.05);
  921.  
  922.  
  923.  
  924.   gl_audioManager_exit = true;
  925.  
  926.   gl_audioManager_thread->waitUntilDone(500);
  927.  
  928.   delete gl_audioManager_thread;
  929.  
  930.  
  931.   if(gl_snd){
  932.     gl_snd->musicStopForced();
  933.     gl_snd->sfxStopAllForced();
  934.   }
  935.  
  936.  
  937.   delete gl_text;
  938.   delete gl_win;
  939.  
  940.  
  941.   delete gl_spritesheetPlayer;
  942.   delete gl_spritesheetFont;
  943.   delete gl_spritesheetAbilityIndicators;
  944.  
  945.  
  946.   //both delete and memory::free should handle nullptrs automagically
  947.   for(u32 i=1; i<(gl_scenes_len     +1); ++i){  delete gl_scenes[i];
  948. /*for(u32 i=1; i<(gl_scenes_len     +1); ++i)*/ memory::free(&gl_objsCache[i]); }
  949.   for(u32 i=1; i<(gl_backgrounds_len+1); ++i) delete gl_backgrounds[i];
  950.   for(u32 i=1; i<(gl_tilesets_len   +1); ++i) delete gl_tilesets[i];
  951.   for(u32 i=1; i<(gl_objImg_len     +1); ++i) delete gl_objImg[i];
  952.   for(u32 i=1; i<(gl_ambience_len   +1); ++i) delete gl_ambience[i];
  953.   for(u32 i=1; i<(gl_sfx_len        +1); ++i) delete gl_sfx[i];
  954.  
  955.  
  956.   delete gl_tileset_missing;
  957.  
  958.  
  959.  
  960.   if(gl_frameTimer) gl_frameTimer->wait(2000);
  961.  
  962.   delete gl_frameTimer;
  963.  
  964. }
  965.  
  966.  
  967.  
  968.  
  969.  
  970. extern int gameMain(int argc, char** argv);
  971.  
  972.  
  973.  
  974. int main(int argc, char** argv){ try {
  975.   //fstr takes priority in initialization, since it's occasionally used to
  976.    //format error messages, so it's important for it to always be valid
  977.   gl_fstr = new FStr(4096);
  978.  
  979.   size_t snd_allocs = handleInit();
  980.  
  981.   saveReadSystem();
  982.  
  983.   int result = gameMain(argc, argv);
  984.  
  985.   saveWriteSystem();
  986.  
  987.   handleQuit();
  988.  
  989.   delete gl_fstr;
  990.  
  991.   printf("# OF CONTROLLED ALLOCATIONS = %lli\n",
  992.          memory::getNumAllocations()-snd_allocs-2-1);
  993.  
  994.   return result;
  995.  
  996.  
  997. } catch(const char* errorText){
  998. #ifdef _DEBUG
  999.   printf("FATAL EXCEPTION OCCURRED!: \"%s\"\n", errorText);
  1000. #else
  1001.   showMessageBox(errorText, "FATAL EXCEPTION OCCURRED! COMPLAIN TO THE DEV! (lol)", MSGBOX_ICN_ERROR);
  1002. #endif /* _DEBUG */
  1003.  
  1004.   return -1;
  1005.  
  1006.  
  1007. }}
  1008.  
  1009.  
  1010.  
  1011.  
  1012.  
  1013. void gl_win_drawRectEmpty(shape::rect rect, color::ARGB color, u32 width){
  1014.   rect.w += rect.x-1;
  1015.   rect.h += rect.y-1;
  1016.  
  1017.   shape::point points[5];
  1018.   points[0] = shape::point(rect.x, rect.y);
  1019.   points[1] = shape::point(rect.w, rect.y);
  1020.   points[2] = shape::point(rect.w, rect.h);
  1021.   points[3] = shape::point(rect.x, rect.h);
  1022.   points[4] = points[0];
  1023.  
  1024.   gl_win->drawLines((shape::point*)points, 5, color, width);
  1025.  
  1026. }
  1027.  
  1028.  
  1029.  
  1030.  
  1031.  
  1032. f64 frand(){
  1033.   u32 value = (rand()<<15) | rand();
  1034.   return (f64)value/(KIT_U32_MAX>>2);
  1035. }
  1036.  
  1037.  
  1038.  
  1039. f32 frandf(){
  1040.   u32 value = (rand()<<15) | rand();
  1041.   return (f32)value/(KIT_U32_MAX>>2);
  1042. }
  1043.  
  1044.  
  1045.  
  1046.  
  1047. f64 frandRange(f64 start, f64 maxDeviation){
  1048.   return start + (frand()*2.0-1.0)*maxDeviation;
  1049. }
  1050.  
  1051.  
  1052.  
  1053.  
  1054.  
  1055. bool rects_overlapping(shape::rect& object_rect,
  1056.                        shape::rect& obstacle_rect,
  1057.                        shape::point* delta_p)
  1058. {
  1059.   //get bounding boxes of object and obstacle
  1060.   shape_quad obj = object_rect;
  1061.   shape_quad obs = obstacle_rect;
  1062.  
  1063.  
  1064.   bool overlapping = obj.a.x < obs.b.x  &&  obj.b.x > obs.a.x  &&
  1065.                      obj.a.y < obs.b.y  &&  obj.b.y > obs.a.y;
  1066.  
  1067.   //delta is initialized so *delta_p can be set regardless of overlap
  1068.   shape::point delta(0, 0);
  1069.  
  1070.   if(!overlapping || !delta_p) goto _draw_collision_;
  1071.  
  1072.  
  1073.   //find minimum translation vector
  1074.    //(movement delta needed to resolve collision)
  1075.   s32 overlapX1 = obs.b.x - obj.a.x; //obs right edge relative to obj left edge
  1076.   s32 overlapX2 = obj.b.x - obs.a.x; //obj right edge relative to obs left edge
  1077.   s32 overlapY1 = obs.b.y - obj.a.y; //obs bottom edge relative to obj top edge
  1078.   s32 overlapY2 = obj.b.y - obs.a.y; //obj bottom edge relative to obs top edge
  1079.  
  1080.   s32 overlapX = (overlapX1 < overlapX2) ? overlapX1 : -overlapX2;
  1081.   s32 overlapY = (overlapY1 < overlapY2) ? overlapY1 : -overlapY2;
  1082.  
  1083.   if(abs(overlapX) < abs(overlapY)) delta.x = overlapX; //.y will remain 0
  1084.   else                              delta.y = overlapY; //.x will remain 0
  1085.  
  1086.  
  1087. _draw_collision_:
  1088. #if defined(_DEBUG) && defined(SHOW_COLLISIONS)
  1089.   gl_win_drawRectEmpty(object_rect,   (overlapping) ? 0x00ff00 : 0xff0000);
  1090.   gl_win_drawRectEmpty(obstacle_rect, (overlapping) ? 0x00ff00 : 0xff0000);
  1091. #endif
  1092.  
  1093.  
  1094.   if(delta_p) *delta_p = delta;
  1095.   return overlapping;
  1096.  
  1097. }
  1098.  
  1099.  
  1100.  
  1101.  
  1102.  
  1103. //returns true if subtile has collision
  1104. static inline bool subtile_is_collidable(s32 x, s32 y){
  1105.   //this line is probably redundant, since bounds checks should already be done
  1106.   //if(x<0 || x>=(TILESIZ_X*2)  ||  y<0 || y>=(TILESIZ_Y*2)) return false;
  1107.  
  1108.   //get the associated tile
  1109.   s32 index = ARR2D(x>>1, y>>1, TILESIZ_X);
  1110.   Tile tile_m = gl_scene.pat_mg[ index ];
  1111.   Tile tile_f = gl_scene.pat_fg[ index ];
  1112.   if(tile_m.pass|tile_f.pass) return false; //tile has collision completely disabled
  1113.  
  1114.   s32 which_bit = (y&1)<<1 | x&1;
  1115.   return ((tile_m.collision|tile_f.collision)>>which_bit)&1;
  1116.  
  1117. }
  1118.  
  1119.  
  1120.  
  1121. //returns true only if resulting bounds have volume
  1122. static inline bool get_tile_bounds(shape_quad& bounds){
  1123.   //find the boundaries of all tiles that the player is touching
  1124.    //(+= 11 so that an east/south subtile boundary is crossed only when
  1125.    // a pixel of the player sprite actually overlaps a given tile)
  1126.   bounds.b += shape::point(11, 11);
  1127.   bounds /= 12; //convert pixels to subtiles
  1128.    //
  1129.   bounds.a.x = CLAMP(bounds.a.x, 0, TILESIZ_X*2); //skip subtiles that are off-screen
  1130.   bounds.a.y = CLAMP(bounds.a.y, 0, TILESIZ_Y*2);  //(2 subtiles in a tile lengthwise)
  1131.    //
  1132.   bounds.b.x = CLAMP(bounds.b.x, 0, TILESIZ_X*2);
  1133.   bounds.b.y = CLAMP(bounds.b.y, 0, TILESIZ_Y*2);
  1134.  
  1135.   s32 xmin, xmax,  ymin, ymax;
  1136.  
  1137.   if(abs(bounds.a.x)<abs(bounds.b.x)){ xmin = abs(bounds.a.x),  xmax = abs(bounds.b.x); }
  1138.   else                               { xmin = abs(bounds.b.x),  xmax = abs(bounds.a.x); }
  1139.  
  1140.   if(abs(bounds.a.y)<abs(bounds.b.y)){ ymin = abs(bounds.a.y),  ymax = abs(bounds.b.y); }
  1141.   else                               { ymin = abs(bounds.b.y),  ymax = abs(bounds.a.y); }
  1142.  
  1143.   return (xmax-xmin)>0 && (ymax-ymin)>0;
  1144.  
  1145. }
  1146.  
  1147.  
  1148.  
  1149. #define SO_LOOP_PATTERN 1
  1150. bool subtiles_overlapping(shape::rect& object_rect_ref, shape::point* delta_p){
  1151.   shape::rect object_rect = object_rect_ref;
  1152.   shape_quad  bounds      = object_rect;
  1153.  
  1154.   shape::point delta(0, 0); //neutral delta by default
  1155.  
  1156.   if(!get_tile_bounds(bounds)){ //there are no subtiles to check; exit early
  1157.     if(delta_p) *delta_p = delta;
  1158.     return false;
  1159.   }
  1160.  
  1161.   shape::point min = bounds.a;
  1162.   shape::point max = bounds.b;
  1163.   shape::point sub;             //current subtile position
  1164.   shape::rect  col(0,0, 12,12); //subtile collider
  1165.  
  1166. #if SO_LOOP_PATTERN == 1
  1167.   s32 xmid = (min.x+max.x)/2; //x midpoint
  1168.   /* bottom-to-top vertically (y = max.y-1;  y >= min.y;  --y)
  1169.        right-to-left on the left half  (x = xmid-1;  x >= min.x;  --x)
  1170.        left-to-right on the right half (x = xmid  ;  x <  max.x;  ++x) */
  1171.   for(sub.y = max.y-1;  sub.y >= min.y;  --sub.y){ //bottom-to-top vertically
  1172.     for(sub.x = xmid-1;  sub.x >= min.x;  --sub.x){ //right-to-left on the left half
  1173.       if(subtile_is_collidable(sub.x, sub.y)){
  1174.         col.x = sub.x*12;
  1175.         col.y = sub.y*12;
  1176.         if(rects_overlapping(object_rect, col, &delta))
  1177.           object_rect += delta;
  1178.       }
  1179.     }
  1180.  
  1181.     for(sub.x = xmid;  sub.x < max.x; ++sub.x){ //left-to-right on the right half
  1182.       if(subtile_is_collidable(sub.x, sub.y)){
  1183.         col.x = sub.x*12;
  1184.         col.y = sub.y*12;
  1185.         if(rects_overlapping(object_rect, col, &delta))
  1186.           object_rect += delta;
  1187.       }
  1188.     }
  1189.  
  1190.   }
  1191.  
  1192.  
  1193. #else
  1194.   for(sub.y = max.y-1;  sub.y >= min.y;  --sub.y) //bottom-to-top vertically
  1195.   for(sub.x = min.x  ;  sub.x <  max.x;  ++sub.x) //left-to-right horizontally
  1196.     if(subtile_is_collidable(sub.x, sub.y)){
  1197.       col.x = sub.x*12;
  1198.       col.y = sub.y*12;
  1199.       if(rects_overlapping(object_rect, col, &delta))
  1200.         object_rect += delta;
  1201.  
  1202.     }
  1203.  
  1204. #endif
  1205.  
  1206.  
  1207.   delta.x = object_rect.x - object_rect_ref.x;
  1208.   delta.y = object_rect.y - object_rect_ref.y;
  1209.  
  1210.   if(delta_p) *delta_p = delta;
  1211.  
  1212.   return delta.x!=0 || delta.y!=0;
  1213.  
  1214. }
  1215.  
  1216.  
  1217.  
  1218.  
  1219.  
  1220. void handleEdgeWrap(){
  1221.   if(!gl_scene.scene)
  1222.     throw "gl_scene.scene == 0 (loadScene(>0) before calling handleEdgeWrap()!)";
  1223.  
  1224.   u16  new_scene_id     = 0;
  1225.   bool call_loadObjects = false;
  1226.  
  1227.  
  1228.   if(       gl_player.pos.x < 0        ){ //player went past west side
  1229.     gl_player.pos.x  = CANVSIZ_X;
  1230.     new_scene_id     = CURRENT_SCENE_PTR->edge_w;
  1231.     call_loadObjects = true;
  1232.  
  1233.   } else if(gl_player.pos.x > CANVSIZ_X){ //player went past east side
  1234.     gl_player.pos.x = 0;
  1235.     new_scene_id     = CURRENT_SCENE_PTR->edge_e;
  1236.     call_loadObjects = true;
  1237.  
  1238.   }
  1239.  
  1240. #if DISABLE_NS_EDGES == 0
  1241.   else if(gl_player.pos.y < 0        ){ //player went past north side
  1242.     gl_player.pos.y  = CANVSIZ_Y;
  1243.     new_scene_id     = CURRENT_SCENE_PTR->edge_n;
  1244.     call_loadObjects = true;
  1245.  
  1246.   } else if(gl_player.pos.y > CANVSIZ_Y){ //player went past south side
  1247.     gl_player.pos.y  = 0;
  1248.     new_scene_id     = CURRENT_SCENE_PTR->edge_s;
  1249.     call_loadObjects = true;
  1250.  
  1251.   }
  1252. #endif
  1253.  
  1254.  
  1255.   if(new_scene_id    ) loadScene(new_scene_id);
  1256.   if(call_loadObjects) loadObjects();
  1257.  
  1258. }
  1259.  
  1260.  
  1261.  
  1262.  
  1263.  
  1264. #define INDICATOR_SPACING 2
  1265. static inline void drawAbilityIndicators(){
  1266.   shape::rect dst(INDICATOR_SPACING,INDICATOR_SPACING, 10,10);
  1267.   shape::rect src(0,0, 10,10);
  1268.  
  1269.   //shrink
  1270.   if(gl_player.has_shrink){
  1271.     //src.x = 0;
  1272.     gl_spritesheetAbilityIndicators->blitRect(&dst, &src, 0xff00ff);
  1273.     dst.x += 10+INDICATOR_SPACING;
  1274.   }
  1275.  
  1276.   //double jump
  1277.   if(gl_player.has_dbljmp){
  1278.     src.x = 10;
  1279.     gl_spritesheetAbilityIndicators->blitRect(&dst, &src, 0xff00ff);
  1280.     dst.x += 10+INDICATOR_SPACING;
  1281.   }
  1282.  
  1283.   //speed
  1284.   if(gl_player.has_speed){
  1285.     src.x = 20;
  1286.     gl_spritesheetAbilityIndicators->blitRect(&dst, &src, 0xff00ff);
  1287.     //dst.x += 10+INDICATOR_SPACING;
  1288.   }
  1289.  
  1290. }
  1291.  
  1292. void drawStuff_and_processObjects(){
  1293.   bool ro_back = false; //r[ender]o[only] objects that are !in_front
  1294.   bool ro_frnt = false; //same thing but only ones with in_front set instead
  1295.  
  1296.   //I Can't Believe It's Not Assembly!
  1297. /*_clear_gray :*/ gl_win->clear(0x808080);
  1298.   _draw_bg    :   gl_scene.drawBg();
  1299. /*_draw_mg    :*/ gl_scene.drawTiles(false);
  1300. /*_proc_obj_bk:*/ if(processObjects(false,ro_back)){ ro_back=true; goto _draw_bg; }
  1301. /*_draw_player:*/ gl_player.blit();
  1302. /*_draw_fg    :*/ gl_scene.drawTiles(true);
  1303. /*_proc_obj_fr:*/ if(processObjects(true ,ro_frnt)){ ro_back=ro_frnt=true; goto _draw_bg; }
  1304. /*_draw_ablity:*/ drawAbilityIndicators();
  1305.  
  1306. }
  1307.  
  1308.  
  1309.  
  1310.  
  1311.  
  1312. //unit should be between 0.0f and 1.0f
  1313. color::ARGB unit_to_ryg(f32 unit){
  1314.   unit = CLAMP(unit*2.0f, 0.0f, 2.0f);
  1315.   u8 red = 255,  green = 255;
  1316.  
  1317.   #define _RND(_v) (  (u8)( (_v)+0.5f )  )
  1318.   if(unit < 1.0f) green = _RND(255.0f*(     unit));
  1319.   else            red   = _RND(255.0f*(2.0f-unit));
  1320.   #undef _RND
  1321.  
  1322.   return color::ARGB(red, green, 0, 0);
  1323.  
  1324. }
  1325.  
  1326.  
  1327.  
  1328.  
  1329.  
  1330. //assumes 8x8 monospaced with 1 pixel of spacing in both x and y
  1331. shape::point get_text_size(const char* str){
  1332.   if(!str) throw "nullptr string was passed to get_text_size()";
  1333.   shape::point result(0,9);
  1334.   s32 current_width = 0;
  1335.  
  1336.   for(; *str != 0; ++str){
  1337.     if(*str != '\n'){
  1338.       current_width += 9;
  1339.     } else {
  1340.       result.x  = MAX(result.x, current_width);
  1341.       result.y += 9;
  1342.       current_width = 0;
  1343.     }
  1344.   }
  1345.  
  1346.   result.x = MAX(result.x, current_width);
  1347.   if(!result.x) result.y = 0;
  1348.  
  1349.   return result;
  1350.  
  1351. }
  1352.  
  1353.  
  1354.  
  1355. //x&y determine the CENTER position of the text, not its top-left corner
  1356. //calculates text_size if either text_size.x or .y is 0
  1357. void drawTextBox(s32 x, s32 y, const char* str, shape::point text_size){
  1358.   if(!str) throw "nullptr string was passed to drawTextBox()";
  1359.   shape::rect box_rect(x,y, 0,0);
  1360.  
  1361.   if(!text_size.x || !text_size.y) text_size = get_text_size(str);
  1362.  
  1363.   box_rect.w  = text_size.x+2;
  1364.   box_rect.h  = text_size.y+2;
  1365.   box_rect.x -= box_rect.w/2;
  1366.   box_rect.y -= box_rect.h/2;
  1367.  
  1368.   gl_win->drawRectangles(&box_rect, 1, 0x000000);
  1369.   gl_win_drawRectEmpty(box_rect, 0x808080);
  1370.   gl_text->print(box_rect.x+2, box_rect.y+2, str);
  1371.  
  1372. }
  1373.  
  1374.  
  1375.  
  1376.  
  1377.  
  1378. bool gl_fullscreen = false;
  1379.  
  1380.  
  1381.  
  1382.  
  1383.  
  1384. static inline void winevent_key_down(u8 vkey){
  1385.   switch(vkey){
  1386.     case VKEY_ESCAPE: CTRLSTATE_SET(esc_h);                    break;
  1387.     case VKEY_F11   : gl_win->setFullscreen(gl_fullscreen^=1); break;
  1388.  
  1389. #ifdef _DEBUG
  1390.     case VKEY_BSLASH: gl_player.confused^=1; break;
  1391.     case VKEY_ENTER : gl_player.kill();      break;
  1392.     case VKEY_MINUS : saveWrite();           break;
  1393.     case VKEY_PLUS  : saveRead();            break;
  1394. #endif
  1395.  
  1396.     case VKEY_UP   : {
  1397.       CTRLSTATE_SET(inst.up_p);
  1398.       if(PLAYER_CAN_JUMP){
  1399.         PLAYER_SET_JMP_FLAGS;
  1400.         gl_player.vel.y = -PLAYER_JUMP_STRENGTH;
  1401.         gl_player.play_sfx_jumping(!gl_player.dying);
  1402.       }
  1403.     } break;
  1404.  
  1405.     case VKEY_DOWN : {
  1406.       CTRLSTATE_SET(inst.down_p);
  1407.     } break;
  1408.  
  1409.     case VKEY_LEFT : {
  1410.       gl_player.going_left = true;
  1411.       CTRLSTATE_SET(inst.left_p);
  1412.     } break;
  1413.  
  1414.     case VKEY_RIGHT: {
  1415.       gl_player.going_right = true;
  1416.       CTRLSTATE_SET(inst.right_p);
  1417.     } break;
  1418.  
  1419.     case VKEY_Z    : {
  1420.       if(gl_player.has_shrink){
  1421.         f32 new_scale = (gl_player.scale == 2.0f) ? 1.0f : 2.0f;
  1422.         if(!gl_player.setScale(new_scale)) goto _play_nope_1;
  1423.  
  1424.       } else {
  1425.         _play_nope_1: SFXPTR_PLAY(SFXPTR_NOPE_1);
  1426.  
  1427.       }
  1428.     } break;
  1429.  
  1430.     case VKEY_X    : {
  1431.       if(gl_player.has_speed) gl_player.enforceMaxVel^=1;
  1432.       else                    SFXPTR_PLAY(SFXPTR_NOPE_1);
  1433.     } break;
  1434.  
  1435.   }
  1436.  
  1437. }
  1438.  
  1439.  
  1440.  
  1441. static inline void winevent_key_up(u8 vkey){
  1442.   switch(vkey){
  1443.     case VKEY_ESCAPE: {
  1444.       CTRLSTATE_CLR(esc_h);
  1445.     } break;
  1446.  
  1447.     case VKEY_UP    : {
  1448.       CTRLSTATE_SET(inst.up_r);
  1449.       if(PLAYER_JUMPED && gl_player.vel.y<0)
  1450.         gl_player.vel.y *= PLAYER_JUMP_CANCEL;
  1451.     } break;
  1452.  
  1453.     case VKEY_DOWN  : {
  1454.       CTRLSTATE_SET(inst.down_r);
  1455.     } break;
  1456.  
  1457.     case VKEY_LEFT  : {
  1458.       gl_player.going_left  = false;
  1459.       CTRLSTATE_SET(inst.left_r);
  1460.     } break;
  1461.  
  1462.     case VKEY_RIGHT : {
  1463.       gl_player.going_right = false;
  1464.       CTRLSTATE_SET(inst.right_r);
  1465.     } break;
  1466.  
  1467.   }
  1468.  
  1469. }
  1470.  
  1471.  
  1472.  
  1473. bool handleEvents(){
  1474.   bool run = true;
  1475.  
  1476.  
  1477.   WindowEvent e;
  1478.   while(pollWindowEvent(&e))
  1479.   switch(e.type){
  1480.     case WINEVENT_WIN_CLOSE: run = false; break;
  1481.  
  1482.     case WINEVENT_KEY_DOWN: if(!e.key.repeat){ winevent_key_down(e.key.vkey); } break;
  1483.  
  1484.     case WINEVENT_KEY_UP: winevent_key_up(e.key.vkey); break;
  1485.  
  1486.   }
  1487.  
  1488.  
  1489.   return run;
  1490. }
  1491.  
  1492.  
  1493.  
  1494.  
  1495.  
  1496. void attemptReset(bool skipRead){
  1497.   gl_player.dying = false;
  1498.   if(skipRead || !saveRead()){
  1499.     //objects should already have been loaded during init
  1500.     loadScene(STARTING_SCENE);
  1501.     gl_player.pos = STARTING_POSITION;
  1502.     gl_player.vel = {0.0f, 0.0f};
  1503.  
  1504.     gl_player.resetAbilities();
  1505.  
  1506.     gl_player.going_left  = false;
  1507.     gl_player.going_right = false;
  1508.  
  1509.     gl_player.visible = true;
  1510.  
  1511.     for(u32 i=1; i<(gl_scenes_len+1); ++i)
  1512.       loadObjects(true, i);
  1513.  
  1514.   }
  1515. }
  1516.  
  1517.  
  1518.  
  1519.  
  1520. #ifndef LERP
  1521. #define LERP(_v0, _v1, _t) (  (1.0f-(_t)) * (_v0)  +  (_t) * (_v1)  )
  1522. #endif
  1523.  
  1524. //this is an easing function
  1525. static inline f32 ease(f32 unit){
  1526.   unit = CLAMP(unit, 0.0f, 1.0f);
  1527.   return LERP(unit, 1.0f, unit);
  1528. }
  1529.  
  1530.  
  1531.  
  1532. #define burst_separation 20
  1533. #define hide_rect_width 16
  1534. #define death_prog_inc 0.015f
  1535.  
  1536. void deathAnim(){
  1537.   if(gl_death_prog >= DEATH_PROG_MAX) return; //probably unnecessary
  1538.  
  1539.   static bool loaded_save;
  1540.   static shape::rect player_src;
  1541.   static color::ARGB hide_rect_color;
  1542.  
  1543.   if(gl_death_prog == 0.0f){
  1544.     loaded_save = false;
  1545.  
  1546.     //this will only fill player_src with the current
  1547.      //player spritesheet rect without actually blitting
  1548.     player_src = gl_player.blit(true);
  1549.     player_src.y = (8+1)*4; //shift down by 4 8px tiles, with 1px of spacing
  1550.     player_src.w = 4; //4x4 to make src rect 1/4 the size of the full sprite
  1551.     player_src.h = 4;
  1552.  
  1553.     //unnecessary, but a cool effect so i'm keeping it in
  1554.     hide_rect_color.r = RND(255.0f*frandf());
  1555.     hide_rect_color.g = RND(255.0f*frandf());
  1556.     hide_rect_color.b = RND(255.0f*frandf());
  1557.  
  1558.   }
  1559.  
  1560.  
  1561.  
  1562.   //for drawing rectangles that hide the screen during saveRead
  1563.   shape::rect hide[2];
  1564.   hide[0].w = hide_rect_width;
  1565.   hide[1].w = hide_rect_width;
  1566.  
  1567.  
  1568.  
  1569.   if(gl_death_prog < 2.0f){
  1570.     f32 prog_unit      = CLAMP(gl_death_prog, 0.0f, 1.0f);
  1571.     s32 burst_distance = (s32)(ease(prog_unit)*burst_separation);
  1572.     shape::rect player_dst = gl_player.getRect();
  1573.  
  1574.     shape::rect burst_chunk;
  1575.     burst_chunk.w = RND(4.0f*gl_player.scale*(1.0f-prog_unit));
  1576.     burst_chunk.h = burst_chunk.w;
  1577.  
  1578.     //top-left
  1579.     burst_chunk.x = player_dst.x - burst_distance;
  1580.     burst_chunk.y = player_dst.y - burst_distance;
  1581.     gl_spritesheetPlayer->blitRect(&burst_chunk, &player_src, 0xff00ff);
  1582.     player_src.x += 4;
  1583.  
  1584.     //top-right
  1585.     burst_chunk.x = player_dst.x + burst_distance + 4;
  1586.     burst_chunk.y = player_dst.y - burst_distance;
  1587.     gl_spritesheetPlayer->blitRect(&burst_chunk, &player_src, 0xff00ff);
  1588.     player_src.y += 4;
  1589.  
  1590.     //bottom-right
  1591.     burst_chunk.x = player_dst.x + burst_distance + 4;
  1592.     burst_chunk.y = player_dst.y + burst_distance + 4;
  1593.     gl_spritesheetPlayer->blitRect(&burst_chunk, &player_src, 0xff00ff);
  1594.     player_src.x -= 4;
  1595.  
  1596.     //bottom-left
  1597.     burst_chunk.x = player_dst.x - burst_distance;
  1598.     burst_chunk.y = player_dst.y + burst_distance + 4;
  1599.     gl_spritesheetPlayer->blitRect(&burst_chunk, &player_src, 0xff00ff);
  1600.     player_src.y -= 4;
  1601.  
  1602.  
  1603.  
  1604.     f32 height_multiplier = MAX(ease(gl_death_prog-1.0f), 0.0f);
  1605.     hide[0].h  = (s32)(CANVSIZ_Y*height_multiplier);
  1606.     hide[1].h  = (s32)(CANVSIZ_Y*height_multiplier);
  1607.     hide[1].y += CANVSIZ_Y - hide[1].h;
  1608.  
  1609.   } else {
  1610.     f32 height_multiplier = ease(DEATH_PROG_MAX-gl_death_prog);
  1611.     hide[0].h  = (s32)(CANVSIZ_Y*height_multiplier);
  1612.     hide[1].h  = (s32)(CANVSIZ_Y*height_multiplier);
  1613.     hide[0].y += CANVSIZ_Y - hide[0].h;
  1614.  
  1615.   }
  1616.  
  1617.  
  1618.  
  1619.   if(gl_death_prog >= 1.0f){
  1620.     hide[1].x += hide_rect_width;
  1621.  
  1622.     for(u32 i=0; i<CANVSIZ_X; i+=hide_rect_width*2){
  1623.       gl_win->drawRectangles(hide, 2, hide_rect_color);
  1624.       hide[0].x += hide_rect_width*2;
  1625.       hide[1].x += hide_rect_width*2;
  1626.     }
  1627.   }
  1628.  
  1629.  
  1630.  
  1631.   gl_death_prog += death_prog_inc;
  1632.  
  1633.  
  1634.   if(!loaded_save && gl_death_prog >= 2.0f){
  1635.     attemptReset();
  1636.     loaded_save = true;
  1637.  
  1638.   }
  1639.  
  1640. }
  1641.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement