Last active
March 21, 2019 13:25
-
-
Save felixjones/ce5b7776823ce8446fbbe7dc9bdb5a32 to your computer and use it in GitHub Desktop.
C++ fire effect for Game Boy Advance
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <gba.hpp> | |
#define EVER ;; | |
using namespace gba; | |
// Palette of fire "heat" values | |
static constexpr color::bgr555 fire_colors[] = { | |
color::rgb888( 0x070707 ), color::rgb888( 0x1F0707 ), color::rgb888( 0x2F0F07 ), color::rgb888( 0x470F07 ), | |
color::rgb888( 0x571707 ), color::rgb888( 0x671F07 ), color::rgb888( 0x771F07 ), color::rgb888( 0x8F2707 ), | |
color::rgb888( 0x9F2F07 ), color::rgb888( 0xAF3F07 ), color::rgb888( 0xBF4707 ), color::rgb888( 0xC74707 ), | |
color::rgb888( 0xDF4F07 ), color::rgb888( 0xDF5707 ), color::rgb888( 0xDF5707 ), color::rgb888( 0xD75F07 ), | |
color::rgb888( 0xD75F07 ), color::rgb888( 0xD7670F ), color::rgb888( 0xCF6F0F ), color::rgb888( 0xCF770F ), | |
color::rgb888( 0xCF7F0F ), color::rgb888( 0xCF8717 ), color::rgb888( 0xC78717 ), color::rgb888( 0xC78F17 ), | |
color::rgb888( 0xC7971F ), color::rgb888( 0xBF9F1F ), color::rgb888( 0xBF9F1F ), color::rgb888( 0xBFA727 ), | |
color::rgb888( 0xBFA727 ), color::rgb888( 0xBFAF2F ), color::rgb888( 0xB7AF2F ), color::rgb888( 0xB7B72F ), | |
color::rgb888( 0xB7B737 ), color::rgb888( 0xCFCF6F ), color::rgb888( 0xDFDF9F ), color::rgb888( 0xEFEFC7 ), | |
color::rgb888( 0xFFFFFF ) | |
}; | |
static constexpr auto fire_colors_num = sizeof( fire_colors ) / sizeof( fire_colors[0] ); | |
static void IWRAM_ main_fire(); | |
static void IWRAM_ spread_fire( uint8 xx, int8 wind ); | |
int main( int argc, char * argv[] ) { | |
display::control = display::mode( 4 ).enable_layers( { 2 } ); | |
// Upload 16bit palette colours | |
dma::channel[3].submit( dma::transfer( video::mode4.get_palette_address(), fire_colors, fire_colors_num ) ); | |
// Clear VRAM | |
video::mode4.get_frontpage().fill( 0 ); | |
// Begin the loop | |
main_fire(); | |
return 0; | |
} | |
// Generates psuedo random 8-bit values | |
static rng<uint8> random_generator( 56, 199, 143 ); | |
// Buffer line read/writes to avoid 16bit VRAM | |
static uint8 line_buffer_read[240]; | |
static uint8 line_buffer_write[248]; | |
void spread_fire( uint8 xx, int8 wind ) { | |
uint8 pixel = line_buffer_read[xx]; | |
if ( pixel ) { | |
uint8 randIdx = ( random_generator.roll() * 3 ) >> 8; // modulo 3 | |
uint8 dst = xx + 3 + randIdx + wind; | |
line_buffer_write[dst] = pixel - ( randIdx & 1 ); | |
} else { | |
line_buffer_write[xx + 4] = 0; | |
} | |
} | |
// DMA control to copy 240 bytes of VRAM | |
static constexpr dma::control line_dma_copy = dma::control( 60 ).transfer_32bit( true ); | |
void main_fire() { | |
// Front buffer rendering | |
auto& drawpage = video::mode4.get_frontpage(); | |
// Seed value starts the burn | |
uint8 seed = 0; | |
for ( EVER ) { | |
auto keystate = io::keys.read_keys(); | |
if ( keystate.button.A && seed > 0 ) { | |
seed--; | |
drawpage.fill( seed, 0, 159, 240, 1 ); | |
} else if ( !keystate.button.A && seed < ( fire_colors_num - 1 ) ) { | |
seed++; | |
drawpage.fill( seed, 0, 159, 240, 1 ); | |
} | |
int8 wind = keystate.pad.axis_x(); | |
for ( uint8 yy = 1; yy < 160; yy++ ) { | |
// Read line | |
dma::channel[1].submit( line_dma_copy, line_buffer_read, &drawpage.get_line( yy ) ); | |
if ( yy & 1 ) { // Odd lines | |
// Loop left to right | |
for ( uint8 xx = 0; xx < 240; xx++ ) { | |
spread_fire( xx, wind ); | |
} | |
} else { // Even lines | |
// Loop right to left | |
uint8 xx = 240; | |
while ( xx-- ) { | |
spread_fire( xx, wind ); | |
} | |
} | |
// Write above line | |
dma::channel[2].submit( line_dma_copy, &drawpage.get_line( yy - 1 ), line_buffer_write + 4 ); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment