blob: f7b1f7a639ae156ece64525958ac4fa2a68f6687 [file] [log] [blame] [edit]
/*
* 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;
}
}
}
}