blob: 19bfa060c176a61edc205700c8d18f02f6340b57 [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 pallete[] = { // 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;
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) {
if (time - last_time < FPS) {
return;
}
last_time = time;
for (uint32_t src_y = screen_height; src_y > 1; src_y--) {
uint32_t src_row = src_y * screen_width;
for (uint32_t src_x = 0; src_x < screen_width; src_x++) {
int rand = std::rand() % 3;
uint32_t src_index = src_row - src_x;
uint8_t color = fire[src_index];
if (color > 0) {
color = color - (rand & 1);
}
uint32_t dst_x = src_x;
// Avoid propagating fire to the wrong place, when on the edges
if (dst_x == 0) {
dst_x = dst_x + std::max(rand - 1, 0);
} else if (dst_x == screen_width - 1) {
dst_x = dst_x + std::min(rand - 1, 0);
} else {
dst_x = dst_x + rand - 1;
}
uint32_t dst_index = src_row - screen_width - dst_x;
fire[dst_index] = color;
}
}
}
void render(uint32_t time) {
for (uint32_t x = 0; x < screen_width; x++) {
for (uint32_t y = 0; y < screen_height; y++) {
uint32_t pos = posAt(x, y);
screen.pen = pallete[fire[pos]];
screen.pixel(Point(x, y));
}
}
}