| /* |
| * An example that implements the "Doom Fire", as documented by Fabien |
| * Sanglard: http://fabiensanglard.net/doom_fire_psx/ |
| * |
| */ |
| #include <string> |
| #include <cstring> |
| #include <memory> |
| #include <cstdlib> |
| |
| #include "doom-fire.hpp" |
| |
| using namespace blit; |
| |
| const uint16_t screen_width = 320; |
| const uint16_t screen_height = 240; |
| const uint32_t FPS = 1000 / 27; // DoomFire runs at 27FPS. |
| |
| Pen palette[] = { // 36 colours |
| Pen(0x07, 0x07, 0x07), Pen(0x1f, 0x07, 0x07), Pen(0x2f, 0x0f, 0x07), |
| Pen(0x47, 0x0f, 0x07), Pen(0x57, 0x17, 0x07), Pen(0x67, 0x1f, 0x07), |
| Pen(0x77, 0x1f, 0x07), Pen(0x8f, 0x27, 0x07), Pen(0x9f, 0x2f, 0x07), |
| Pen(0xaf, 0x3f, 0x07), Pen(0xbf, 0x47, 0x07), Pen(0xc7, 0x47, 0x07), |
| Pen(0xDF, 0x4F, 0x07), Pen(0xDF, 0x57, 0x07), Pen(0xDF, 0x57, 0x07), |
| Pen(0xD7, 0x5F, 0x07), Pen(0xD7, 0x67, 0x0F), Pen(0xcf, 0x6f, 0x0f), |
| Pen(0xcf, 0x77, 0x0f), Pen(0xcf, 0x7f, 0x0f), Pen(0xCF, 0x87, 0x17), |
| Pen(0xC7, 0x87, 0x17), Pen(0xC7, 0x8F, 0x17), Pen(0xC7, 0x97, 0x1F), |
| Pen(0xBF, 0x9F, 0x1F), Pen(0xBF, 0x9F, 0x1F), Pen(0xBF, 0xA7, 0x27), |
| Pen(0xBF, 0xA7, 0x27), Pen(0xBF, 0xAF, 0x2F), Pen(0xB7, 0xAF, 0x2F), |
| Pen(0xB7, 0xB7, 0x2F), Pen(0xB7, 0xB7, 0x37), Pen(0xCF, 0xCF, 0x6F), |
| Pen(0xDF, 0xDF, 0x9F), Pen(0xEF, 0xEF, 0xC7), Pen(0xFF, 0xFF, 0xFF) |
| }; |
| |
| uint8_t fire[screen_width * screen_height]; |
| uint32_t last_time = 0; |
| int8_t wind = 0; |
| bool enabled = true; |
| |
| uint32_t posAt(uint32_t x, uint32_t y) { |
| return y * screen_width + x; |
| } |
| |
| void init() { |
| blit::set_screen_mode(ScreenMode::hires); |
| |
| screen.pen = Pen(0, 0, 0, 255); |
| screen.clear(); |
| |
| // Initialises the screen |
| for (int x = 0; x < screen_width; x++) { |
| for (int y = 0; y < screen_height; y++) { |
| uint32_t pos = posAt(x, y); |
| fire[pos] = 0; |
| } |
| } |
| |
| // Initialises the bottom line |
| for (int i = 0; i < screen_width; i++) { |
| uint32_t pos = posAt(i, screen_height - 1); |
| fire[pos] = 35; |
| } |
| } |
| |
| void update(uint32_t time) { |
| // Adds wind to east. |
| if (buttons.pressed & Button::DPAD_LEFT) { |
| wind--; |
| } |
| |
| // Adds wind to east. |
| if (buttons.pressed & Button::DPAD_RIGHT) { |
| wind++; |
| } |
| |
| // Toggles fire on and off. |
| if (buttons.pressed & Button::X) { |
| uint8_t fire_index = enabled ? 0 : 35; |
| for (int i = 0; i < screen_width; i++) { |
| uint32_t pos = posAt(i, screen_height - 1); |
| fire[pos] = fire_index; |
| } |
| enabled = !enabled; |
| } |
| } |
| |
| void render(uint32_t time) { |
| if (time - last_time < FPS) { |
| return; |
| } |
| last_time = time; |
| for (int y = 0; y < screen_height; y++) { |
| // Precompute the rows for a small performance gain. |
| int row = y * screen_width; |
| |
| // For each pixel in each row, calculate the colours that will be |
| // rendered on the previous row, on the next call to update. |
| int next_row = y == 0 ? 0 : (y - 1) * screen_width; |
| |
| for (int x = 0; x < screen_width; x++) { |
| // Draw the current pixel. |
| uint8_t color = fire[row + x]; |
| screen.pen = palette[color]; |
| screen.pixel(Point(x, y)); |
| |
| // Update the pixels on the previous row. |
| if (y > 0) { |
| int new_x = x; |
| int rand = std::rand() % 3; |
| new_x = (new_x + rand - 1 + wind); |
| if (new_x >= screen_width) { |
| new_x = new_x - screen_width; |
| } |
| else if (new_x < 0) { |
| new_x = new_x + screen_width; |
| } |
| color = color > 0 ? color - (rand & 1) : 0; |
| fire[next_row + new_x] = color; |
| } |
| } |
| } |
| } |