Repository: p4nic4ttack/doom-flipper-zero Branch: main Commit: 6cd5da460b93 Files: 13 Total size: 101.4 KB Directory structure: gitextract_bi69kcir/ ├── README.md ├── application.fam ├── compiled/ │ ├── assets_icons.c │ └── assets_icons.h ├── constants.h ├── display.h ├── doom.c ├── entities.c ├── entities.h ├── level.h ├── sound.h ├── types.c └── types.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: README.md ================================================ # Doom Flipper Zero edition
## Will it run Doom? As tradition goes, Doom is being ported to almost every possible embedded electronic device. Therefore I did an attempt to come up with something close to Doom and still compatible on the Flipper Zero's hardware.
This is not the actual Doom game but a port made from yet another Doom port to the Arduino Nano (https://github.com/daveruiz/doom-nano/). This port is basically a raycasting engine, using Doom sprites.
This version is very basic and might be improved over time. ## How to install on Flipper Zero During the porting process, minor changes were made to the workings (and build options) of the current firmware. These changes are documented here and are necessary in order to get a working firmware build that contains this Doom port. ### Modifying the firmware & build options * In the `sites/cc.scons` add the following values to the `CCFLAGS` section: ``` ... "-Wno-unused-parameter", "-Wno-type-limits", "-Wno-unused-variable", ... ``` * In `applications/gui/canvas_i.h` comment out the following line:
`uint8_t* canvas_get_buffer(Canvas* canvas);` --> `//uint8_t* canvas_get_buffer(Canvas* canvas);` * In `applications/gui/canvas.h` add the following lines: ``` uint8_t* canvas_get_buffer(Canvas* canvas); void canvas_draw_icon_bitmap(Canvas* canvas, uint8_t x, uint8_t y, int16_t w, int16_t h, const Icon* icon); ``` * In `applications/gui/canvas.c` add the following function: ``` void canvas_draw_icon_bitmap(Canvas* canvas, uint8_t x, uint8_t y, int16_t w, int16_t h, const Icon* icon){ furi_assert(canvas); furi_assert(icon); x += canvas->offset_x; y += canvas->offset_y; uint8_t* icon_data = NULL; furi_hal_compress_icon_decode(icon_get_data(icon), &icon_data); u8g2_DrawXBM(&canvas->fb, x, y, w, h, icon_data); } ``` ### Installing the plugin in the firmware * Make a folder called Doom in the applications folder. Add all the source files (also the compiled folder and it's files) in the Doom folder. * Make the `applications/meta/application.fam` look like the following: ``` App( appid="basic_plugins", name="Basic applications for plug-in menu", apptype=FlipperAppType.METAPACKAGE, provides=[ ... "doom_game", ... ], ) ``` If all went well the only thing left to do is building the firmware and installing it to the Flipper. ## Screenshots ![Intro screen](assets/screenshot-intro2.jpg) ![Start screen](assets/screenshot-start2.jpg) ![Imp](assets/screenshot-imp2.jpg) ![Medkit](assets/screenshot-medkit2.jpg) ================================================ FILE: application.fam ================================================ App( appid="doom_game", name="Doom", apptype=FlipperAppType.PLUGIN, entry_point="doom_app", cdefines=["APP_DOOM_GAME"], requires=["gui"], stack_size=1 * 1024, icon="A_Plugins_14", order=30, ) ================================================ FILE: compiled/assets_icons.c ================================================ #include "assets_icons.h" #include // Inverted icons const uint8_t _I_fire_inv_0[] = {0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x30,0x00,0x40,0xee,0x00,0x80,0xe6,0x00,0x80,0xa7,0x01,0x80,0xc7,0x03,0x40,0x45,0x03,0xe0,0x41,0x07,0xf8,0x82,0x9f,0xb9,0x01,0x3e,0x7c,0x00,0x7a,0x6e,0x00,0x56,0x1c,0x00,0x6c,0xf4,0x01,0x3a,0x6c,0x00,0x7e,0xfc,0x00,0x1a,0x08,0x00,0x3c,0x11,0x00,0x00,0x00,0x00,0x00,}; const uint8_t* const _I_fire_inv[] = {_I_fire_inv_0}; const uint8_t _I_gun_inv_0[] = {0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x1b,0x00,0x00,0x80,0x23,0x00,0x00,0x40,0x20,0x00,0x00,0x40,0x40,0x00,0x00,0x40,0x57,0x00,0x00,0x20,0x8b,0x00,0x00,0x90,0x11,0x01,0x00,0x98,0x00,0x00,0x00,0xb0,0x43,0x01,0x00,0x94,0x81,0x03,0x00,0xd0,0x45,0x04,0x00,0x8c,0x02,0x02,0x00,0xc4,0x00,0x03,0x00,0xc8,0x00,0x02,0x00,0x4e,0x40,0x00,0x00,0x92,0x00,0x02,0x80,0x07,0x15,0x04,0xe0,0x8f,0x00,0x0c,0xd0,0x9d,0x07,0x17,0xe0,0x3a,0xc0,0x3f,0xe0,0xf7,0xff,0x77,0xe0,0xae,0xfe,0x4b,0xd8,0xdd,0xff,0x4d,0x88,0xea,0xbe,0x26,0x4c,0xf5,0xff,0x17,0xc8,0xfa,0xae,0x0b,0xcc,0xff,0xdf,0x19,0xe8,0xeb,0xa7,0x00,0xd8,0xf1,0x4d,0x0c,0xc0,0xbe,0x1a,0x08,0xf6,0xfd,0x37,0x04,}; const uint8_t* const _I_gun_inv[] = {_I_gun_inv_0}; const uint8_t _I_gun_mask_inv_0[] = {0x01,0x00,0x53,0x00,0x00,0x0c,0x38,0x04,0x38,0x09,0xf8,0x0c,0x78,0x17,0xf0,0x18,0xf8,0x00,0x67,0xff,0x01,0xaf,0xc3,0xff,0x01,0x80,0x7e,0x3f,0xf0,0x38,0x07,0xf0,0x0e,0x40,0x31,0x03,0x8f,0xfb,0xff,0x07,0x01,0x94,0x3c,0x0e,0xc0,0x35,0xff,0x85,0xc8,0x06,0x30,0x7e,0x00,0x0c,0x61,0xe2,0xe1,0xff,0xc7,0xc5,0xc3,0xff,0x9f,0x80,0xca,0xfe,0x03,0x2f,0xf8,0x0c,0xc6,0xc2,0x03,0x4b,0xf8,0xa8,0x42,0xe2,0x03,0x28,0xf8,0x1e,0x80,0x68,0x1e,0x28,0x78,}; const uint8_t* const _I_gun_mask_inv[] = {_I_gun_mask_inv_0}; const uint8_t _I_logo_inv_0[] = {0x01,0x00,0x71,0x01,0x00,0x44,0x0a,0x21,0x00,0xc0,0x48,0x20,0x91,0x0c,0x05,0x20,0x14,0x30,0x80,0x71,0x80,0x8e,0x03,0x00,0x85,0x40,0x22,0x98,0x8a,0x00,0x20,0x60,0xa0,0x83,0xa8,0x50,0x20,0xd8,0x00,0x41,0xcd,0x01,0x03,0x82,0xc3,0xc3,0x61,0xf0,0xa8,0xf0,0x44,0x6c,0x14,0x68,0x26,0x16,0x3b,0x0a,0x89,0xcd,0x24,0x14,0x0a,0x96,0x32,0x1b,0x11,0x85,0xc4,0x62,0x92,0x00,0x88,0xe1,0x30,0xb1,0x18,0x54,0x46,0x6d,0x28,0xa0,0x70,0x82,0x23,0x51,0xa5,0x54,0x62,0x90,0x1b,0x05,0x0b,0x19,0x15,0x89,0x86,0x86,0x69,0x42,0xb0,0xf5,0xb2,0x9a,0x58,0xac,0xae,0xaf,0x35,0x85,0x50,0xb8,0xda,0x69,0x6d,0x6e,0x95,0x9b,0x9b,0x50,0xac,0x9c,0xac,0x6e,0x36,0x37,0x2b,0xad,0xed,0xa2,0x41,0xa1,0xd7,0x6b,0x42,0x23,0x3f,0xdb,0x75,0xad,0x5d,0x6c,0x2c,0x37,0xbf,0x5b,0x0d,0x0e,0x5a,0xc5,0xca,0xdb,0xef,0x6b,0xf6,0xea,0xff,0xde,0x6d,0x4d,0xfb,0x75,0xe5,0xfb,0xfd,0xde,0xfe,0x6d,0xe7,0xb9,0x7b,0xba,0xff,0xdb,0xfd,0xaf,0xbf,0x77,0xc7,0xd9,0xf3,0x5f,0x79,0xed,0x3c,0x10,0x3f,0x79,0x7d,0xbf,0x9f,0xed,0xff,0xf7,0xe7,0x5f,0xff,0xbd,0xae,0xbd,0xd6,0xfe,0xdf,0x62,0x34,0xbf,0xdf,0xde,0x77,0x6b,0xe9,0x7f,0xbf,0xf5,0x38,0x77,0xe7,0xef,0x7b,0xb9,0xf4,0xbf,0x7f,0xde,0xbd,0xb8,0x34,0x3f,0xbb,0x5f,0xbb,0xff,0xff,0xbf,0xda,0x80,0x63,0xfb,0xa1,0xfe,0xc1,0x05,0xdf,0x11,0x8f,0xce,0xdd,0xf2,0xff,0xff,0x40,0xe3,0xff,0xf3,0xfd,0xe9,0x42,0xb1,0xfc,0x84,0xc4,0x32,0x3f,0xbb,0xf0,0x10,0x7e,0x60,0x82,0xfe,0xdc,0xe8,0xc1,0x11,0x07,0x97,0xff,0xfd,0x7f,0xbf,0x82,0x07,0x8f,0xff,0xf8,0x82,0x06,0xef,0x7e,0x04,0x0e,0x0f,0xff,0xe0,0x9f,0xfe,0x04,0x67,0x01,0x9f,0x60,0x21,0xff,0x0d,0xf8,0x68,0xa0,0x11,0xcc,0x04,0x1f,0xc1,0xbf,0x0d,0x0c,0xfe,0x01,0x08,0x80,0x40,0xb8,0x1f,0xc0,0x88,0xc7,0xe0,0x00,0x03,0xe4,0x5f,0xff,0xf0,0xf0,0x42,0x70,0x03,0x43,0x03,0x04,0x28,0x68,0x60,0x20,0x87,0x03,0xa4,0x03,0xc1,0x7f,0x1a,0x08,0x45,0x86,0x8b,0x00,0xc0,0x5f,0xe1,0xc0,0x85,0x80,0x5f,0xe0,0x80,0x86,0xfe,0x01,0xda,0x01,0x78,0x04,0x3c,0xc0,}; const uint8_t* const _I_logo_inv[] = {_I_logo_inv_0}; const Icon I_fire_inv = {.width=24,.height=20,.frame_count=1,.frame_rate=0,.frames=_I_fire_inv}; const Icon I_gun_inv = {.width=32,.height=32,.frame_count=1,.frame_rate=0,.frames=_I_gun_inv}; const Icon I_gun_mask_inv = {.width=32,.height=32,.frame_count=1,.frame_rate=0,.frames=_I_gun_mask_inv}; const Icon I_logo_inv = {.width=72,.height=47,.frame_count=1,.frame_rate=0,.frames=_I_logo_inv}; const uint8_t space[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; const uint8_t zero[] = {0x00, 0x60, 0x90, 0x90, 0x90, 0x60}; const uint8_t one[] = {0x00, 0x20, 0x20, 0x20, 0x20, 0x70}; const uint8_t two[] ={0x00, 0x60, 0x90, 0x20, 0x40, 0xf0}; const uint8_t three[] = {0x00, 0x60, 0x90, 0x20, 0x90, 0x60}; const uint8_t four[] = {0x00, 0x90, 0x90, 0xf0, 0x10, 0x10}; const uint8_t five[] = {0x00, 0xf0, 0x80, 0xe0, 0x10, 0xe0}; const uint8_t six[] = {0x00, 0x60, 0x80, 0xe0, 0x90, 0x60}; const uint8_t seven[] = {0x00, 0xf0, 0x10, 0x10, 0x10, 0x10}; const uint8_t eight[] = {0x00, 0x60, 0x90, 0x60, 0x90, 0x60}; const uint8_t nine[] = {0x00, 0x60, 0x90, 0x70, 0x10, 0x60}; const uint8_t A[] = {0x00, 0x60, 0x90, 0xf0, 0x90, 0x90}; const uint8_t B[] = {0x00, 0xe0, 0x90, 0xe0, 0x90, 0xe0}; const uint8_t C[] = {0x00, 0x60, 0x90, 0x80, 0x90, 0x60}; const uint8_t D[] = {0x00, 0xe0, 0x90, 0x90, 0x90, 0xe0}; const uint8_t E[] = {0x00, 0xf0, 0x80, 0xe0, 0x80, 0xf0}; const uint8_t F[] = {0x00, 0xf0, 0x80, 0xe0, 0x80, 0x80}; const uint8_t G[] = {0x00, 0x60, 0x80, 0x80, 0x90, 0x60}; const uint8_t H[] = {0x00, 0x90, 0x90, 0xf0, 0x90, 0x90}; const uint8_t I[] = {0x00, 0x20, 0x20, 0x20, 0x20, 0x20}; const uint8_t J[] = {0x00, 0x10, 0x10, 0x10, 0x90, 0x60}; const uint8_t K[] = {0x00, 0x90, 0xa0, 0xc0, 0xa0, 0x90}; const uint8_t L[] = {0x00, 0x80, 0x80, 0x80, 0x80, 0xf0}; const uint8_t M[] = {0x00, 0x90, 0xf0, 0x90, 0x90, 0x90}; const uint8_t N[] = {0x00, 0x90, 0xd0, 0xb0, 0x90, 0x90}; const uint8_t O[] = {0x00, 0x60, 0x90, 0x90, 0x90, 0x60}; const uint8_t P[] = {0x00, 0xe0, 0x90, 0xe0, 0x80, 0x80}; const uint8_t Q[] = {0x00, 0x60, 0x90, 0x90, 0xb0, 0x70}; const uint8_t R[] = {0x00, 0xe0, 0x90, 0xe0, 0x90, 0x90}; const uint8_t S[] = {0x00, 0x60, 0x80, 0x60, 0x10, 0xe0}; const uint8_t T[] = {0x00, 0xe0, 0x40, 0x40, 0x40, 0x40}; const uint8_t U[] = {0x00, 0x90, 0x90, 0x90, 0x90, 0x60}; const uint8_t V[] = {0x00, 0x90, 0x90, 0x90, 0x60, 0x60}; const uint8_t W[] = {0x00, 0x90, 0x90, 0x90, 0xf0, 0x90}; const uint8_t X[] = {0x00, 0x90, 0x90, 0x60, 0x90, 0x90}; const uint8_t Y[] = {0x00, 0x90, 0x90, 0x60, 0x60, 0x60}; const uint8_t Z[] = {0x00, 0xf0, 0x10, 0x60, 0x80, 0xf0}; const uint8_t dot[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x40}; const uint8_t comma[] = {0x00, 0x00, 0x00, 0x00, 0x20, 0x40}; const uint8_t dash[] = {0x00, 0x00, 0x00, 0x60, 0x00, 0x00}; const uint8_t underscore[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0xf0}; const uint8_t bracket_open[] = {0x00, 0x20, 0x40, 0x40, 0x40, 0x20}; const uint8_t bracket_close[] = {0x00, 0x40, 0x20, 0x20, 0x20, 0x40}; const uint8_t cross_left[] = {0x10, 0x10, 0x70, 0x70, 0x10, 0x10}; const uint8_t cross_right[] = {0x80, 0x80, 0xe0, 0xe0, 0x80, 0x80}; const uint8_t pacman_left[] = {0x00, 0x30, 0x50, 0x70, 0x70, 0x00}; const uint8_t pacman_right[] = {0x00, 0xc0, 0x60, 0xe0, 0xe0, 0xe0}; const uint8_t box[] = {0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x00}; const uint8_t *char_arr[48] = {space,zero,one,two,three,four,five,six,seven,eight,nine,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,dot,comma,dash,underscore,bracket_open,bracket_close,cross_left,cross_right,pacman_left,pacman_right,box}; const uint8_t gradient[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x00, 0x00, 0x22, 0x22, 0x00, 0x00, 0x8a, 0x8a, 0x00, 0x00, 0x22, 0x22, 0x00, 0x00, 0xaa, 0xaa, 0x10, 0x10, 0xaa, 0xaa, 0x00, 0x00, 0xaa, 0xaa, 0x01, 0x01, 0xaa, 0xaa, 0x44, 0x44, 0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa, 0x44, 0x44, 0xaa, 0xaa, 0x15, 0x55, 0xaa, 0xaa, 0x55, 0x55, 0xaa, 0xaa, 0x55, 0x55, 0xbb, 0xbb, 0x55, 0x55, 0xaa, 0xea, 0x55, 0x55, 0xbb, 0xbb, 0x55, 0x55, 0xff, 0xff, 0x55, 0x55, 0xfb, 0xfb, 0x55, 0x55, 0xff, 0xff, 0x55, 0x55, 0xbb, 0xbf, 0x57, 0x57, 0xff, 0xff, 0xdd, 0xdd, 0xff, 0xff, 0x77, 0x75, 0xff, 0xff, 0xdd, 0xdd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; //const uint8_t gun[] = {0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0x27, 0xff, 0xff, 0xfe, 0x3b, 0xff, 0xff, 0xfd, 0xfb, 0xff, 0xff, 0xfd, 0xfd, 0xff, 0xff, 0xfd, 0x15, 0xff, 0xff, 0xfb, 0x2e, 0xff, 0xff, 0xf6, 0x77, 0x7f, 0xff, 0xe6, 0xff, 0xff, 0xff, 0xf2, 0x3d, 0x7f, 0xff, 0xd6, 0x7e, 0x3f, 0xff, 0xf4, 0x5d, 0xdf, 0xff, 0xce, 0xbf, 0xbf, 0xff, 0xdc, 0xff, 0x3f, 0xff, 0xec, 0xff, 0xbf, 0xff, 0x8d, 0xfd, 0xff, 0xff, 0xb6, 0xff, 0xbf, 0xfe, 0x1f, 0x57, 0xdf, 0xf8, 0x0e, 0xff, 0xcf, 0xf4, 0x46, 0x1f, 0x17, 0xf8, 0xa3, 0xfc, 0x03, 0xf8, 0x10, 0x00, 0x11, 0xf8, 0x8a, 0x80, 0x2d, 0xe4, 0x44, 0x00, 0x4d, 0xee, 0xa8, 0x82, 0x9b, 0xcd, 0x50, 0x00, 0x17, 0xec, 0xa0, 0x8a, 0x2f, 0xcc, 0x00, 0x04, 0x67, 0xe8, 0x28, 0x1a, 0xff, 0xe4, 0x70, 0x4d, 0xcf, 0xfc, 0x82, 0xa7, 0xef, 0x90, 0x40, 0x13, 0xdf}; // const uint8_t gun_mask[] = {0xff, 0xff, 0x8f, 0xff, 0xff, 0xfe, 0x03, 0xff, 0xff, 0xfc, 0x01, 0xff, 0xff, 0xf8, 0x01, 0xff, 0xff, 0xf8, 0x00, 0xff, 0xff, 0xf8, 0x00, 0xff, 0xff, 0xf0, 0x00, 0x7f, 0xff, 0xe0, 0x00, 0x3f, 0xff, 0xc0, 0x00, 0x7f, 0xff, 0xc0, 0x00, 0x3f, 0xff, 0x80, 0x00, 0x1f, 0xff, 0x80, 0x00, 0x0f, 0xff, 0x80, 0x00, 0x1f, 0xff, 0x80, 0x00, 0x1f, 0xff, 0x80, 0x00, 0x1f, 0xff, 0x00, 0x00, 0x3f, 0xff, 0x00, 0x00, 0x1f, 0xfc, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0f}; const uint8_t gun[] = {0x00, 0x00, 0x20, 0x00,0x00, 0x00, 0xd8, 0x00,0x00, 0x01, 0xc4, 0x00,0x00, 0x02, 0x04, 0x00,0x00, 0x02, 0x02, 0x00,0x00, 0x02, 0xea, 0x00,0x00, 0x04, 0xd1, 0x00,0x00, 0x09, 0x88, 0x80,0x00, 0x19, 0x00, 0x00,0x00, 0x0d, 0xc2, 0x80,0x00, 0x29, 0x81, 0xc0,0x00, 0x0b, 0xa2, 0x20,0x00, 0x31, 0x40, 0x40,0x00, 0x23, 0x00, 0xc0,0x00, 0x13, 0x00, 0x40,0x00, 0x72, 0x02, 0x00,0x00, 0x49, 0x00, 0x40,0x01, 0xe0, 0xa8, 0x20,0x07, 0xf1, 0x00, 0x30,0x0b, 0xb9, 0xe0, 0xe8,0x07, 0x5c, 0x03, 0xfc,0x07, 0xef, 0xff, 0xee,0x07, 0x75, 0x7f, 0xd2,0x1b, 0xbb, 0xff, 0xb2,0x11, 0x57, 0x7d, 0x64,0x32, 0xaf, 0xff, 0xe8,0x13, 0x5f, 0x75, 0xd0,0x33, 0xff, 0xfb, 0x98,0x17, 0xd7, 0xe5, 0x00,0x1b, 0x8f, 0xb2, 0x30,0x03, 0x7d, 0x58, 0x10,0x6f, 0xbf, 0xec, 0x20}; const uint8_t gun_mask[] = {0x00, 0x00, 0x70, 0x00,0x00, 0x01, 0xfc, 0x00,0x00, 0x03, 0xfe, 0x00,0x00, 0x07, 0xfe, 0x00,0x00, 0x07, 0xff, 0x00,0x00, 0x07, 0xff, 0x00,0x00, 0x0f, 0xff, 0x80,0x00, 0x1f, 0xff, 0xc0,0x00, 0x3f, 0xff, 0x80,0x00, 0x3f, 0xff, 0xc0,0x00, 0x7f, 0xff, 0xe0,0x00, 0x7f, 0xff, 0xf0,0x00, 0x7f, 0xff, 0xe0,0x00, 0x7f, 0xff, 0xe0,0x00, 0x7f, 0xff, 0xe0,0x00, 0xff, 0xff, 0xc0,0x00, 0xff, 0xff, 0xe0,0x03, 0xff, 0xff, 0xf0,0x0f, 0xff, 0xff, 0xf8,0x1f, 0xff, 0xff, 0xfc,0x1f, 0xff, 0xff, 0xfe,0x1f, 0xff, 0xff, 0xff,0x1f, 0xff, 0xff, 0xff,0x3f, 0xff, 0xff, 0xff,0x3f, 0xff, 0xff, 0xfe,0x7f, 0xff, 0xff, 0xfc,0x7f, 0xff, 0xff, 0xf8,0x7f, 0xff, 0xff, 0xfc,0x7f, 0xff, 0xff, 0xf8,0x7f, 0xff, 0xff, 0xf8,0x7f, 0xff, 0xff, 0xf8,0xff, 0xff, 0xff, 0xf0}; const uint8_t imp_inv[] = {0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x01, 0x80, 0x00,0x00, 0x02, 0x80, 0x00,0x00, 0x07, 0x40, 0x00,0x00, 0x02, 0x80, 0x00,0x00, 0x01, 0x00, 0x00,0x01, 0x0f, 0xb3, 0x00,0x00, 0xd0, 0x4e, 0x00,0x00, 0x79, 0x8c, 0x00,0x00, 0x1c, 0x19, 0x00,0x01, 0x8a, 0x20, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x03, 0x00,0x02, 0x00, 0x00, 0x00,0x03, 0x02, 0x00, 0x00,0x00, 0x00, 0x00, 0x40,0x02, 0x08, 0x00, 0x80,0x00, 0x00, 0x01, 0x00,0x01, 0x8e, 0x30, 0x00,0x00, 0x04, 0x10, 0x00,0x00, 0x0c, 0x20, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x10, 0x00,0x00, 0x06, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x20, 0x00,0x00, 0x01, 0x00, 0x00,0x00, 0x02, 0x20, 0x00,0x00, 0x05, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x01, 0x80, 0x00,0x00, 0x02, 0x40, 0x00,0x00, 0x03, 0xe0, 0x00,0x00, 0x04, 0x00, 0x00,0x00, 0x01, 0xa1, 0x80,0x01, 0x80, 0x13, 0x00,0x00, 0xf3, 0x8a, 0x00,0x00, 0x09, 0x94, 0x00,0x00, 0x88, 0x38, 0x80,0x00, 0x00, 0x00, 0x00,0x00, 0x02, 0x23, 0x00,0x00, 0x00, 0x00, 0x40,0x01, 0x80, 0x00, 0x80,0x00, 0x00, 0x01, 0x00,0x00, 0xe2, 0x80, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x0c, 0x20, 0x00,0x00, 0x04, 0x30, 0x00,0x00, 0x02, 0x20, 0x00,0x00, 0x00, 0x40, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x01, 0x00, 0x00,0x00, 0x02, 0x20, 0x00,0x00, 0x06, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x02, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0xa0, 0x00,0x00, 0x00, 0x48, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x04, 0x00,0x00, 0x00, 0x0a, 0x00,0x00, 0x00, 0x1f, 0x00,0x00, 0x02, 0x2a, 0x80,0x00, 0x01, 0x05, 0x00,0x00, 0x01, 0xae, 0x20,0x00, 0x01, 0x24, 0x40,0x00, 0x02, 0xac, 0x80,0x00, 0x02, 0x86, 0x00,0x00, 0x03, 0x20, 0x20,0x00, 0x04, 0x30, 0x40,0x00, 0x0c, 0x00, 0x00,0x00, 0x00, 0x00, 0x40,0x00, 0x00, 0x20, 0x20,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x08, 0x20,0x00, 0x01, 0x00, 0x00,0x00, 0x02, 0x1a, 0x00,0x00, 0x00, 0x1c, 0x00,0x00, 0x00, 0x38, 0x00,0x00, 0x04, 0x00, 0x00,0x00, 0x02, 0x98, 0x00,0x00, 0x00, 0x18, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x36, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x20, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x04, 0x00,0x00, 0x00, 0x0a, 0x00,0x00, 0x00, 0x08, 0x40,0x00, 0x00, 0x00, 0x80,0x00, 0x01, 0xd6, 0x80,0x00, 0x02, 0xbf, 0x80,0x00, 0x06, 0x61, 0xa0,0x00, 0x0c, 0xe8, 0x80,0x00, 0x0c, 0x10, 0x00,0x00, 0x1a, 0x22, 0x00,0x00, 0x12, 0x40, 0x00,0x00, 0x06, 0x0c, 0x00,0x00, 0x04, 0x0d, 0x00,0x00, 0x3a, 0x03, 0x00,0x00, 0x10, 0x02, 0x00,0x00, 0x60, 0x0a, 0x00,0x00, 0x50, 0x04, 0x00,0x00, 0x20, 0x03, 0x00,0x00, 0x00, 0x04, 0x00,0x00, 0x20, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x20, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x40, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x02, 0x24, 0x00,0x00, 0x01, 0x08, 0x00,0x00, 0x01, 0x18, 0x00,0x00, 0x01, 0x41, 0x40,0x02, 0x33, 0xb6, 0x80,0x01, 0x9c, 0x04, 0x00,0x08, 0xfa, 0x02, 0x08,0x05, 0x00, 0x01, 0x0c,0x27, 0x83, 0xa2, 0x2a,0x00, 0x04, 0x00, 0x00,0x02, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00};//{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x07, 0x40, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x0f, 0xb3, 0x00, 0x00, 0xd0, 0x4e, 0x00, 0x00, 0x79, 0x8c, 0x00, 0x00, 0x1c, 0x19, 0x00, 0x01, 0x8a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x02, 0x08, 0x00, 0x80, 0x00, 0x00, 0x01, 0x00, 0x01, 0x8e, 0x30, 0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x0c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x20, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; const uint8_t imp_mask_inv[] = {0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x03, 0xc0, 0x00,0x00, 0x03, 0xc0, 0x00,0x00, 0x07, 0xe0, 0x00,0x00, 0x07, 0xe0, 0x00,0x00, 0x03, 0xe0, 0x00,0x01, 0x07, 0xf1, 0x80,0x00, 0xdf, 0xfe, 0x00,0x00, 0x3f, 0xfe, 0x00,0x00, 0x7f, 0xff, 0x00,0x01, 0xff, 0xff, 0x80,0x00, 0xff, 0xff, 0x80,0x01, 0xff, 0xff, 0x80,0x03, 0xcf, 0xf1, 0xc0,0x01, 0xc7, 0xf1, 0xc0,0x01, 0x87, 0xf1, 0xc0,0x03, 0x0f, 0xf9, 0x80,0x03, 0x0f, 0xfb, 0x80,0x01, 0x8f, 0xff, 0x80,0x03, 0x9f, 0x79, 0x00,0x00, 0x1f, 0x7c, 0x00,0x00, 0x0f, 0x78, 0x00,0x00, 0x0f, 0x78, 0x00,0x00, 0x07, 0x30, 0x00,0x00, 0x07, 0x38, 0x00,0x00, 0x07, 0x30, 0x00,0x00, 0x07, 0x30, 0x00,0x00, 0x03, 0x78, 0x00,0x00, 0x07, 0x30, 0x00,0x00, 0x0f, 0x80, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x03, 0xc0, 0x00,0x00, 0x07, 0xc0, 0x00,0x00, 0x07, 0xe0, 0x00,0x00, 0x07, 0xc0, 0x00,0x01, 0x07, 0xe1, 0x00,0x00, 0x8f, 0xfa, 0x00,0x00, 0xff, 0xfe, 0x00,0x00, 0x3f, 0xfe, 0x00,0x01, 0x7f, 0xff, 0x80,0x00, 0xff, 0xff, 0x00,0x01, 0xff, 0xff, 0x80,0x03, 0xcf, 0xfb, 0xc0,0x03, 0x87, 0xf1, 0xc0,0x03, 0xcf, 0xf3, 0xc0,0x01, 0xcf, 0xf1, 0x80,0x00, 0xcf, 0xf1, 0x00,0x00, 0x0f, 0xfb, 0x80,0x00, 0x1e, 0x78, 0x00,0x00, 0x0e, 0x78, 0x00,0x00, 0x1e, 0x78, 0x00,0x00, 0x0f, 0x70, 0x00,0x00, 0x0f, 0x78, 0x00,0x00, 0x07, 0x70, 0x00,0x00, 0x07, 0x70, 0x00,0x00, 0x07, 0x38, 0x00,0x00, 0x03, 0x30, 0x00,0x00, 0x03, 0x20, 0x00,0x00, 0x07, 0x30, 0x00,0x00, 0x05, 0x70, 0x00,0x00, 0x00, 0x78, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x0e, 0x00,0x00, 0x00, 0x1f, 0x00,0x00, 0x00, 0x1f, 0x00,0x00, 0x03, 0x3f, 0x80,0x00, 0x01, 0x3f, 0x00,0x00, 0x01, 0xff, 0x30,0x00, 0x03, 0xff, 0xc0,0x00, 0x03, 0xff, 0xc0,0x00, 0x03, 0xff, 0x80,0x00, 0x07, 0xff, 0xe0,0x00, 0x07, 0xff, 0xc0,0x00, 0x05, 0xff, 0xe0,0x00, 0x00, 0xfc, 0xe0,0x00, 0x01, 0xfc, 0xe0,0x00, 0x01, 0xfc, 0x70,0x00, 0x03, 0xfc, 0x38,0x00, 0x03, 0xfe, 0x70,0x00, 0x07, 0xfc, 0x00,0x00, 0x07, 0x9e, 0x00,0x00, 0x0f, 0xbc, 0x00,0x00, 0x0f, 0x3e, 0x00,0x00, 0x07, 0x9c, 0x00,0x00, 0x03, 0x9c, 0x00,0x00, 0x03, 0xb8, 0x00,0x00, 0x03, 0x98, 0x00,0x00, 0x01, 0x98, 0x00,0x00, 0x02, 0x1c, 0x00,0x00, 0x00, 0x36, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x20, 0x00,0x00, 0x00, 0x38, 0x00,0x00, 0x00, 0x1f, 0x00,0x00, 0x00, 0x1f, 0x40,0x00, 0x00, 0x3e, 0x80,0x00, 0x01, 0xff, 0x80,0x00, 0x03, 0xff, 0x80,0x00, 0x07, 0xff, 0xe0,0x00, 0x0e, 0xff, 0xc0,0x00, 0x0c, 0xff, 0x80,0x00, 0x1f, 0xfe, 0x00,0x00, 0x13, 0xfc, 0x00,0x00, 0x07, 0xfe, 0x00,0x00, 0x1f, 0xff, 0x00,0x00, 0x3f, 0x9f, 0x00,0x00, 0x3e, 0x0f, 0x00,0x00, 0x7c, 0x0f, 0x00,0x00, 0x78, 0x0f, 0x00,0x00, 0x78, 0x07, 0x80,0x00, 0x78, 0x07, 0x40,0x00, 0x38, 0x07, 0x80,0x00, 0x30, 0x07, 0x00,0x00, 0x30, 0x01, 0x00,0x01, 0xf0, 0x00, 0x00,0x01, 0xb0, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x01, 0x1c, 0x00,0x00, 0x01, 0x3e, 0x00,0x00, 0x03, 0xff, 0x00,0x00, 0x0f, 0xff, 0xe0,0x01, 0x3f, 0xff, 0xc0,0x01, 0xff, 0xff, 0xc0,0x19, 0xff, 0xff, 0xe8,0x7f, 0xff, 0xff, 0xfe,0x3f, 0xff, 0xff, 0xfe,0x1f, 0xc2, 0x07, 0xe0,0x1f, 0x00, 0x01, 0xe0,0x0e, 0x00, 0x00, 0x40,0x00, 0x00, 0x00, 0x00,};//{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00, 0x03, 0xe0, 0x00, 0x01, 0x07, 0xf1, 0x80, 0x00, 0xdf, 0xfe, 0x00, 0x00, 0x3f, 0xfe, 0x00, 0x00, 0x7f, 0xff, 0x00, 0x01, 0xff, 0xff, 0x80, 0x00, 0xff, 0xff, 0x80, 0x01, 0xff, 0xff, 0x80, 0x03, 0xcf, 0xf1, 0xc0, 0x01, 0xc7, 0xf1, 0xc0, 0x01, 0x87, 0xf1, 0xc0, 0x03, 0x0f, 0xf9, 0x80, 0x03, 0x0f, 0xfb, 0x80, 0x01, 0x8f, 0xff, 0x80, 0x03, 0x9f, 0x79, 0x00, 0x00, 0x1f, 0x7c, 0x00, 0x00, 0x0f, 0x78, 0x00, 0x00, 0x0f, 0x78, 0x00, 0x00, 0x07, 0x30, 0x00, 0x00, 0x07, 0x38, 0x00, 0x00, 0x07, 0x30, 0x00, 0x00, 0x07, 0x30, 0x00, 0x00, 0x03, 0x78, 0x00, 0x00, 0x07, 0x30, 0x00, 0x00, 0x0f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00}; const uint8_t fireball[] = {0x00, 0x00,0x01, 0x40,0x0a, 0xb0,0x0e, 0xd0,0x00, 0x68,0x53, 0xb4,0x0f, 0x48,0x27, 0x78,0x17, 0xa8,0x27, 0xf0,0x21, 0xd6,0x02, 0xf8,0x20, 0x48,0x06, 0x20,0x01, 0x00,0x00, 0x00}; const uint8_t fireball_mask[] = {0x1f, 0x40,0x0f, 0xf0,0x3f, 0xf8,0x1f, 0xfc,0x7f, 0xfd,0x7f, 0xfc,0x7f, 0xfd,0xff, 0xfe,0xff, 0xff,0xff, 0xff,0xff, 0xfe,0xff, 0xfe,0x3f, 0xfe,0x17, 0xf8,0x07, 0xf4,0x01, 0xe0}; const uint8_t item[] = {0x1f, 0xf8,0x3f, 0xfc,0x7f, 0xfe,0x7f, 0xfe,0x77, 0xee,0x3f, 0xfc,0x5f, 0xfa,0x2f, 0xf6,0x53, 0xcc,0x3e, 0x7e,0x5e, 0x7c,0x38, 0x1e,0x58, 0x1c,0x3e, 0x7e,0x5e, 0x7e,0x2e, 0xfc,0x00, 0x00,0x00, 0x00,0x00, 0x00,0x00, 0x00,0x00, 0x00,0x0f, 0xfc,0x17, 0xfc,0x22, 0x6c,0x36, 0x44,0x3f, 0xfc,0x1f, 0xfc,0x2b, 0xfc,0x05, 0x54,0x02, 0xa8,0x00, 0x00,0x00, 0x00}; const uint8_t item_mask[] = {0x1f, 0xf8,0x3f, 0xfc,0x7f, 0xfe,0x7f, 0xfe,0x7f, 0xfe,0x7f, 0xfe,0x7f, 0xfe,0x7f, 0xfe,0x7f, 0xfe,0x7f, 0xfe,0x7f, 0xfe,0x7f, 0xfe,0x7f, 0xfe,0x7f, 0xfe,0x7f, 0xfe,0x3f, 0xfc,0x00, 0x00,0x00, 0x00,0x00, 0x00,0x00, 0x00,0x00, 0x00,0x0f, 0xfc,0x1f, 0xfc,0x3f, 0xfc,0x3f, 0xfc,0x3f, 0xfc,0x3f, 0xfc,0x3f, 0xfc,0x07, 0xfc,0x03, 0xf8,0x00, 0x00,0x00, 0x00}; //const uint8_t door[] = {0xff, 0xff, 0xff, 0xff,0xb2, 0xbd, 0xcd, 0x5b,0x9a, 0xf4, 0x6d, 0x71,0xff, 0xff, 0xff, 0xff,0x00, 0x00, 0x00, 0x00,0xbf, 0xff, 0xff, 0xfd,0x3f, 0x00, 0xfe, 0xfc,0x3e, 0x00, 0xc6, 0xfc,0xbc, 0xaa, 0xfe, 0xbd,0x39, 0x54, 0xc6, 0xbc,0x32, 0x8e, 0xfe, 0xac,0xb5, 0xfe, 0xc6, 0xad,0x3f, 0xe0, 0xfe, 0xac,0x31, 0xe0, 0xc6, 0xac,0xb3, 0xf4, 0xfe, 0xad,0x3f, 0xe8, 0xc6, 0xac,0x3c, 0xf4, 0xd6, 0xac,0xb8, 0xff, 0xfe, 0xad,0x34, 0xc7, 0xfe, 0xfc,0x38, 0xd6, 0x0e, 0x0c,0xb0, 0xd6, 0x4e, 0x0d,0x3f, 0xd6, 0xaf, 0x5c,0x30, 0x47, 0xff, 0xac,0xb7, 0x57, 0xff, 0xfd,0x3f, 0xc6, 0x0e, 0x0c,0x35, 0x56, 0x40, 0x4c,0xb5, 0x46, 0xaa, 0xad,0x35, 0x56, 0x55, 0x4c,0xff, 0xff, 0xff, 0xff,0xb0, 0x1f, 0xf8, 0x0d,0xd9, 0x30, 0x0c, 0x9b,0xff, 0xe0, 0x07, 0xff}; const uint8_t door[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x07, 0xe1, 0x8c, 0x00, 0x04, 0x00, 0x7c, 0x03, 0x18, 0x60, 0x08, 0x00, 0x3e, 0x0f, 0xf7, 0xdf, 0x00, 0x1f, 0x00, 0xfe, 0x0f, 0xbe, 0xf8, 0x3e, 0x00, 0x3f, 0x1f, 0xff, 0xdf, 0x00, 0x1f, 0x81, 0xff, 0x0f, 0xff, 0xf8, 0x7e, 0x00, 0x3f, 0x8f, 0xff, 0xdf, 0x00, 0xff, 0xf9, 0xff, 0x1f, 0xff, 0xf8, 0xff, 0x80, 0x3f, 0xc7, 0xff, 0xcc, 0x07, 0xff, 0xfc, 0xff, 0x1f, 0xff, 0xe3, 0xff, 0x80, 0x3f, 0xc7, 0xff, 0xc0, 0x07, 0xff, 0xfc, 0x7f, 0x0f, 0xfe, 0x03, 0xff, 0xc0, 0x3f, 0xc3, 0xf7, 0xc0, 0x07, 0xdf, 0xf8, 0x3e, 0x0f, 0xbe, 0x01, 0xff, 0x80, 0x1f, 0x80, 0xe3, 0x80, 0x07, 0x8f, 0xf8, 0x1e, 0x07, 0x1c, 0x01, 0xff, 0x80, 0x3f, 0xc1, 0xff, 0xc0, 0x0f, 0xff, 0xfc, 0x3f, 0x0f, 0xbe, 0x03, 0xff, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0x80, 0x00, 0x7f, 0xff, 0xff, 0xc0, 0x00, 0x00, 0xe0, 0x00, 0x1f, 0xf0, 0xff, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xc0, 0x00, 0x00, 0xe0, 0x00, 0x0f, 0xf0, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x01, 0xe0, 0x00, 0x0f, 0xf0, 0xff, 0x00, 0x01, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x01, 0xf0, 0x00, 0x0f, 0xf0, 0xff, 0x00, 0x03, 0xff, 0xff, 0xff, 0xe0, 0x7f, 0x81, 0xf0, 0x00, 0x0f, 0xf0, 0xff, 0x00, 0x07, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xc1, 0xf0, 0x00, 0x0f, 0xf0, 0xff, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xe1, 0xf0, 0x00, 0x0f, 0xf0, 0xff, 0x00, 0x1f, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xc1, 0xf3, 0x00, 0x0f, 0xf0, 0xff, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xe0, 0xff, 0x81, 0xff, 0xc0, 0x0f, 0xf0, 0xff, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xe0, 0x3f, 0x01, 0xff, 0xc0, 0x0f, 0xf0, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xc1, 0xff, 0xe0, 0x0f, 0xf0, 0xff, 0x01, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xc1, 0xff, 0xe0, 0x0f, 0xf0, 0xff, 0x03, 0xff, 0xff, 0xff, 0xff, 0xe1, 0xff, 0xe1, 0xff, 0xe0, 0x0f, 0xf0, 0xff, 0x07, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xc1, 0xff, 0xf0, 0x0f, 0xf0, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x8f, 0xe0, 0xff, 0x81, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0x1f, 0xff, 0xff, 0xfe, 0x07, 0xe0, 0x7f, 0x81, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xff, 0xff, 0xfc, 0x07, 0xe0, 0xff, 0xc1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xff, 0x8f, 0xfc, 0x03, 0xe0, 0xff, 0xc1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xff, 0x07, 0xfc, 0x07, 0xe0, 0xff, 0xc1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xfe, 0x00, 0x7f, 0xff, 0xe0, 0xff, 0xc1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x0f, 0x9c, 0x00, 0x3f, 0xff, 0xe0, 0xff, 0xc1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x07, 0xfc, 0x00, 0x7f, 0xff, 0xe0, 0xff, 0xc1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x07, 0xfc, 0x00, 0x7f, 0xff, 0xe0, 0xff, 0xc1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x0f, 0xfc, 0x00, 0x7f, 0xff, 0xe0, 0xff, 0xc1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x0f, 0xfc, 0x00, 0x7f, 0xff, 0xe0, 0xff, 0xc1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xfe, 0x00, 0x7f, 0xff, 0xe0, 0xff, 0xc1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xfc, 0x00, 0x3f, 0xff, 0xe0, 0xff, 0xc1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xfc, 0x00, 0x3f, 0xff, 0xe0, 0xff, 0xc1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xf0, 0x00, 0x1f, 0xff, 0xe0, 0x7f, 0x81, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x0f, 0xf0, 0x00, 0x1f, 0xff, 0xe0, 0xff, 0x81, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x07, 0xe0, 0x00, 0x3f, 0xff, 0xe0, 0xff, 0xc1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xe1, 0xff, 0xe1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x00, 0x1f, 0x80, 0x3f, 0xff, 0xe1, 0xff, 0xe1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x00, 0x3f, 0xc0, 0x1f, 0xff, 0xe1, 0xff, 0xe1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x00, 0x7f, 0xc0, 0x0f, 0xff, 0xe1, 0xff, 0xe1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x00, 0xff, 0xc0, 0x07, 0xff, 0xe1, 0xff, 0xe1, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x01, 0xff, 0xc0, 0x03, 0x8f, 0xc0, 0xc1, 0x81, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x03, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x07, 0xff, 0xc0, 0xff, 0x80, 0x00, 0x00, 0x01, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0x0f, 0xff, 0xc1, 0xff, 0x80, 0x00, 0x00, 0x01, 0xf3, 0x8e, 0x0f, 0xf0, 0xff, 0x0f, 0xff, 0xc3, 0xff, 0xc0, 0x00, 0x00, 0x01, 0xff, 0x9c, 0x0f, 0xf0, 0xff, 0x0f, 0xff, 0xc3, 0xff, 0xc0, 0xff, 0xfc, 0x01, 0xff, 0xfe, 0x0f, 0xf0, 0xff, 0x0f, 0xff, 0xc3, 0xff, 0xc1, 0xff, 0xfe, 0x01, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0x07, 0xff, 0xc3, 0xff, 0xc3, 0xff, 0xff, 0x01, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x0f, 0xff, 0xc3, 0xff, 0xc3, 0xff, 0xff, 0x01, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x0f, 0xff, 0xc3, 0xff, 0xc3, 0xff, 0xff, 0x01, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x0f, 0xff, 0xc3, 0xff, 0xc3, 0xff, 0xff, 0x01, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x0f, 0xff, 0xc3, 0xff, 0xc3, 0xff, 0xff, 0x01, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0x0f, 0xff, 0xc3, 0xff, 0xc3, 0xff, 0xff, 0x00, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0x0f, 0xff, 0xc3, 0xff, 0xc3, 0xff, 0xff, 0x00, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0x07, 0xff, 0xe3, 0xff, 0xc3, 0xff, 0xff, 0x00, 0x3f, 0xfe, 0x0f, 0xf0, 0xff, 0x07, 0xff, 0xf3, 0xff, 0xc1, 0xef, 0xfe, 0x00, 0x3f, 0xfe, 0x0f, 0xf0, 0xff, 0x0f, 0xff, 0xff, 0xff, 0xc0, 0x82, 0x00, 0x00, 0x1f, 0xff, 0x0f, 0xf0, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x07, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x07, 0xff, 0x0f, 0xf0, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x03, 0x8e, 0x0f, 0xf0, 0xff, 0x1f, 0xc1, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x03, 0x88, 0x0f, 0xf0, 0xff, 0x0f, 0x80, 0xff, 0xff, 0xc1, 0xff, 0xfc, 0x00, 0xff, 0xfe, 0x0f, 0xf0, 0xff, 0x06, 0x00, 0x73, 0xff, 0xc3, 0xff, 0xfe, 0x01, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0x00, 0x00, 0x03, 0xff, 0xc3, 0xff, 0xff, 0x83, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x07, 0x0c, 0x73, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x0f, 0xfe, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xf0, 0xff, 0x1f, 0xff, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8f, 0xf0, 0xfe, 0x1f, 0xff, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x87, 0xf0, 0xfe, 0x1f, 0xfe, 0xfb, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xfc, 0x0f, 0x9e, 0x73, 0xff, 0x81, 0xf9, 0xf7, 0xe7, 0x9c, 0xff, 0x03, 0xf0, 0xfc, 0x07, 0xfe, 0xfb, 0xc0, 0x00, 0xf0, 0x00, 0x6f, 0xbe, 0xfe, 0x03, 0xf0, 0x3c, 0x07, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xfe, 0x03, 0xc0, 0x1c, 0x0f, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0x03, 0x80, 0x1e, 0x0f, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0x07, 0x80, 0x3f, 0x0f, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xf0, 0x7f, 0xff, 0xff, 0x0f, 0xc0, 0x1f, 0x8f, 0xff, 0xff, 0xe7, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0x1f, 0x80, 0x1f, 0xc7, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x3f, 0x80, 0x07, 0xc3, 0xff, 0xff, 0x9f, 0xff, 0xff, 0xff, 0x9f, 0xff, 0xfc, 0x3e, 0x00, 0x07, 0xc1, 0xfe, 0xff, 0x3f, 0xff, 0xff, 0xff, 0xcf, 0xf7, 0xf8, 0x3e, 0x00, 0x01, 0x00, 0xfc, 0x7e, 0x7f, 0xff, 0xff, 0xff, 0xe7, 0xe3, 0xf0, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00}; ================================================ FILE: compiled/assets_icons.h ================================================ #pragma once #include #ifndef _sprites_h #define _sprites_h #define bmp_font_width 24 // in bytes #define bmp_font_height 6 #define bmp_font_width_pxs 192 #define bmp_font_height_pxs 48 #define CHAR_MAP " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ.,-_(){}[]#" #define CHAR_WIDTH 4 #define CHAR_HEIGHT 6 #define BMP_GUN_WIDTH 32 #define BMP_GUN_HEIGHT 32 #define BMP_FIRE_WIDTH 24 #define BMP_FIRE_HEIGHT 20 #define BMP_IMP_WIDTH 32 #define BMP_IMP_HEIGHT 32 #define BMP_IMP_COUNT 5 #define BMP_FIREBALL_WIDTH 16 #define BMP_FIREBALL_HEIGHT 16 #define BMP_DOOR_WIDTH 100 #define BMP_DOOR_HEIGHT 100 #define BMP_ITEMS_WIDTH 16 #define BMP_ITEMS_HEIGHT 16 #define BMP_ITEMS_COUNT 2 #define BMP_LOGO_WIDTH 72 #define BMP_LOGO_HEIGHT 47 #define GRADIENT_WIDTH 2 #define GRADIENT_HEIGHT 8 #define GRADIENT_COUNT 8 #define GRADIENT_WHITE 7 #define GRADIENT_BLACK 0 // Inverted icons extern const Icon I_fire_inv; extern const Icon I_gun_inv; extern const Icon I_gun_mask_inv; extern const Icon I_logo_inv; // Fonts extern const uint8_t zero[]; extern const uint8_t one[]; extern const uint8_t two[]; extern const uint8_t three[]; extern const uint8_t four[]; extern const uint8_t five[]; extern const uint8_t six[]; extern const uint8_t seven[]; extern const uint8_t eight[]; extern const uint8_t nine[]; extern const uint8_t A[]; extern const uint8_t B[]; extern const uint8_t C[]; extern const uint8_t D[]; extern const uint8_t E[]; extern const uint8_t F[]; extern const uint8_t G[]; extern const uint8_t H[]; extern const uint8_t I[]; extern const uint8_t J[]; extern const uint8_t K[]; extern const uint8_t L[]; extern const uint8_t M[]; extern const uint8_t N[]; extern const uint8_t O[]; extern const uint8_t P[]; extern const uint8_t Q[]; extern const uint8_t R[]; extern const uint8_t S[]; extern const uint8_t T[]; extern const uint8_t U[]; extern const uint8_t V[]; extern const uint8_t W[]; extern const uint8_t X[]; extern const uint8_t Y[]; extern const uint8_t Z[]; extern const uint8_t dot[]; extern const uint8_t comma[]; extern const uint8_t dash[]; extern const uint8_t underscore[]; extern const uint8_t bracket_open[]; extern const uint8_t bracket_close[]; extern const uint8_t cross_left[]; extern const uint8_t cross_right[]; extern const uint8_t pacman_left[]; extern const uint8_t pacman_right[]; extern const uint8_t box[]; extern const uint8_t *char_arr[48]; extern const uint8_t gradient[]; //extern const uint8_t gun[] //extern const uint8_t gun_mask[] extern const uint8_t gun[]; extern const uint8_t gun_mask[]; extern const uint8_t imp_inv[]; extern const uint8_t imp_mask_inv[]; extern const uint8_t fireball[]; extern const uint8_t fireball_mask[]; extern const uint8_t item[]; extern const uint8_t item_mask[]; extern const uint8_t door[]; #endif ================================================ FILE: constants.h ================================================ #ifndef _constants_h #define _constants_h #define PB_CONSTEXPR constexpr #define PI 3.14159265358979323846 // Key pinout #define USE_INPUT_PULLUP #define K_LEFT 6 #define K_RIGHT 7 #define K_UP 8 #define K_DOWN 3 #define K_FIRE 10 // SNES Controller // uncomment following line to enable snes controller support // #define SNES_CONTROLLER const uint8_t DATA_CLOCK = 11; const uint8_t DATA_LATCH = 12; const uint8_t DATA_SERIAL = 13; // Sound const uint8_t SOUND_PIN = 9; // do not change, belongs to used timer // GFX settings #define OPTIMIZE_SSD1306 // Optimizations for SSD1366 displays #define FRAME_TIME 66.666666 // Desired time per frame in ms (66.666666 is ~15 fps) #define RES_DIVIDER 2 // Higher values will result in lower horizontal resolution when rasterize and lower process and memory usage // Lower will require more process and memory, but looks nicer #define Z_RES_DIVIDER 2 // Zbuffer resolution divider. We sacrifice resolution to save memory #define DISTANCE_MULTIPLIER 20 // Distances are stored as uint8_t, multiplying the distance we can obtain more precision taking care // of keep numbers inside the type range. Max is 256 / MAX_RENDER_DEPTH #define MAX_RENDER_DEPTH 12 #define MAX_SPRITE_DEPTH 8 #define ZBUFFER_SIZE SCREEN_WIDTH / Z_RES_DIVIDER // Level #define LEVEL_WIDTH_BASE 6 #define LEVEL_WIDTH (1 << LEVEL_WIDTH_BASE) #define LEVEL_HEIGHT 57 #define LEVEL_SIZE LEVEL_WIDTH / 2 * LEVEL_HEIGHT // scenes #define INTRO 0 #define GAME_PLAY 1 // Game #define GUN_TARGET_POS 18 #define GUN_SHOT_POS GUN_TARGET_POS + 4 #define ROT_SPEED .12 #define MOV_SPEED .2 #define MOV_SPEED_INV 5 // 1 / MOV_SPEED #define JOGGING_SPEED .005 #define ENEMY_SPEED .02 #define FIREBALL_SPEED .2 #define FIREBALL_ANGLES 45 // Num of angles per PI #define MAX_ENTITIES 10 // Max num of active entities #define MAX_STATIC_ENTITIES 28 // Max num of entities in sleep mode #define MAX_ENTITY_DISTANCE 200 // * DISTANCE_MULTIPLIER #define MAX_ENEMY_VIEW 80 // * DISTANCE_MULTIPLIER #define ITEM_COLLIDER_DIST 6 // * DISTANCE_MULTIPLIER #define ENEMY_COLLIDER_DIST 4 // * DISTANCE_MULTIPLIER #define FIREBALL_COLLIDER_DIST 2 // * DISTANCE_MULTIPLIER #define ENEMY_MELEE_DIST 6 // * DISTANCE_MULTIPLIER #define WALL_COLLIDER_DIST .2 #define ENEMY_MELEE_DAMAGE 8 #define ENEMY_FIREBALL_DAMAGE 20 #define GUN_MAX_DAMAGE 15 // display const uint8_t SCREEN_WIDTH = 128; const uint8_t SCREEN_HEIGHT = 64; const uint8_t HALF_WIDTH = SCREEN_WIDTH/2; const uint8_t RENDER_HEIGHT = 56; // raycaster working height (the rest is for the hud) const uint8_t HALF_HEIGHT = SCREEN_HEIGHT/2; #endif ================================================ FILE: display.h ================================================ #include #include #include #include "constants.h" #include "compiled/assets_icons.h" #define CHECK_BIT(var,pos) ((var) & (1<<(pos))) static const uint8_t bit_mask[8] = { 128, 64, 32, 16, 8, 4, 2, 1 }; #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) #define read_bit(b, n) b & pgm_read_byte(bit_mask + n) ? 1 : 0 //#define read_bit(byte, index) (((unsigned)(byte) >> (index)) & 1) void drawVLine(uint8_t x, int8_t start_y, int8_t end_y, uint8_t intensity, Canvas* const canvas); void drawPixel(int8_t x, int8_t y, bool color, bool raycasterViewport, Canvas* const canvas); void drawSprite(int8_t x, int8_t y, const uint8_t *bitmap, const uint8_t *bitmap_mask, int16_t w, int16_t h, uint8_t sprite, double distance, Canvas* const canvas); void drawBitmap(int16_t x, int16_t y, const Icon *i, int16_t w, int16_t h, uint16_t color, Canvas* const canvas); void drawTextSpace(int8_t x, int8_t y, char *txt, uint8_t space, Canvas* const canvas); void drawChar(int8_t x, int8_t y, char ch, Canvas* const canvas); void clearRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, Canvas* const canvas); void drawGun(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, Canvas* const canvas); void drawRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, Canvas* const canvas); void drawText(uint8_t x, uint8_t y, uint8_t num, Canvas* const canvas); void fadeScreen(uint8_t intensity, bool color, Canvas* const canvas); bool getGradientPixel(uint8_t x, uint8_t y, uint8_t i); double getActualFps(); void fps(); void setupDisplay(Canvas* canvas); uint8_t reverse_bits(uint8_t num); // FPS control double delta = 1; uint32_t lastFrameTime = 0; uint8_t zbuffer[128]; /// 128 = screen width & REMOVE WHEN DISPLAY.H IMPLEMENTED uint8_t *display_buf = NULL; void drawGun(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color, Canvas* const canvas){ int16_t byteWidth = (w + 7) / 8; uint8_t byte = 0; for(int16_t j=0; jfb); } void drawBitmap(int16_t x, int16_t y, const Icon *i, int16_t w, int16_t h, uint16_t color, Canvas* const canvas){ canvas_draw_icon_bitmap(canvas, x, y, w, h, i); } void drawText(uint8_t x, uint8_t y, uint8_t num, Canvas* const canvas){ char buf[4]; itoa(num, buf, 10); drawTextSpace(x,y,buf,1,canvas); } void drawTextSpace(int8_t x, int8_t y, char *txt, uint8_t space, Canvas* const canvas){ uint8_t pos = x; uint8_t i = 0; char ch; while ((ch = txt[i]) != '\0') { drawChar(pos, y, ch, canvas); i++; pos += CHAR_WIDTH + space; // shortcut on end of screen if (pos > SCREEN_WIDTH) return; } } // Custom drawBitmap method with scale support, mask, zindex and pattern filling void drawSprite(int8_t x, int8_t y, const uint8_t *bitmap, const uint8_t *bitmap_mask, int16_t w, int16_t h, uint8_t sprite, double distance, Canvas* const canvas){ uint8_t tw = (double) w / distance; uint8_t th = (double) h / distance; uint8_t byte_width = w / 8; uint8_t pixel_size = fmax(1, (double)1.0 / (double)distance); uint16_t sprite_offset = byte_width * h * sprite; bool pixel; bool maskPixel; // Don't draw the whole sprite if the anchor is hidden by z buffer // Not checked per pixel for performance reasons if (zbuffer[(int)(fmin(fmax(x, 0), ZBUFFER_SIZE - 1) / Z_RES_DIVIDER)] < distance * DISTANCE_MULTIPLIER) { return; } for (uint8_t ty = 0; ty < th; ty += pixel_size) { // Don't draw out of screen if (y + ty < 0 || y + ty >= RENDER_HEIGHT) { continue; } uint8_t sy = ty * distance; // The y from the sprite for (uint8_t tx = 0; tx < tw; tx += pixel_size) { uint8_t sx = tx * distance; // The x from the sprite uint16_t byte_offset = sprite_offset + sy * byte_width + sx / 8; // Don't draw out of screen if (x + tx < 0 || x + tx >= SCREEN_WIDTH) { continue; } maskPixel = read_bit(pgm_read_byte(bitmap_mask + byte_offset), sx % 8); if (maskPixel) { pixel = read_bit(pgm_read_byte(bitmap + byte_offset), sx % 8); for (uint8_t ox = 0; ox < pixel_size; ox++) { for (uint8_t oy = 0; oy < pixel_size; oy++) { if(bitmap == imp_inv) drawPixel(x + tx + ox, y + ty + oy, 1, true, canvas); else drawPixel(x + tx + ox, y + ty + oy, pixel, true, canvas); } } } } } } void drawPixel(int8_t x, int8_t y, bool color, bool raycasterViewport, Canvas* const canvas) { if (x < 0 || x >= SCREEN_WIDTH || y < 0 || y >= (raycasterViewport ? RENDER_HEIGHT : SCREEN_HEIGHT)){ return; } if(color) canvas_draw_dot(canvas, x, y); else{ canvas_invert_color(canvas); canvas_draw_dot(canvas, x, y); canvas_invert_color(canvas); } } void drawChar(int8_t x, int8_t y, char ch, Canvas* const canvas){ uint8_t lsb; uint8_t c = 0; while (CHAR_MAP[c] != ch && CHAR_MAP[c] != '\0') c++; for(uint8_t i = 0; i < 6; i++){ //lsb = (char_arr[c][i] >> 4); lsb = reverse_bits(char_arr[c][i]); for (uint8_t n = 0; n < 4; n++){ if(CHECK_BIT(lsb, n)){ drawPixel(x+n, y+i, true, false, canvas); } } } } void clearRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, Canvas* const canvas){ canvas_invert_color(canvas); for(int i = 0; i < w; i++){ for(int j = 0; j < h; j++){ canvas_draw_dot(canvas, x+i, y+j); } } canvas_invert_color(canvas); } void drawRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, Canvas* const canvas){ for(int i = 0; i < w; i++){ for(int j = 0; j < h; j++){ canvas_draw_dot(canvas, x+i, y+j); } } } bool getGradientPixel(uint8_t x, uint8_t y, uint8_t i) { if (i == 0) return 0; if (i >= GRADIENT_COUNT - 1) return 1; uint8_t index = fmax(0, fmin(GRADIENT_COUNT - 1, i)) * GRADIENT_WIDTH * GRADIENT_HEIGHT // gradient index + y * GRADIENT_WIDTH % (GRADIENT_WIDTH * GRADIENT_HEIGHT) // y byte offset + x / GRADIENT_HEIGHT % GRADIENT_WIDTH; // x byte offset //uint8_t *gradient_data = NULL; //furi_hal_compress_icon_decode(icon_get_data(&I_gradient_inv), &gradient_data); // return the bit based on x return read_bit(pgm_read_byte(gradient + index), x % 8); } void fadeScreen(uint8_t intensity, bool color, Canvas* const canvas) { for (uint8_t x = 0; x < SCREEN_WIDTH; x++) { for (uint8_t y = 0; y < SCREEN_HEIGHT; y++) { if (getGradientPixel(x, y, intensity)) drawPixel(x, y, color, false, canvas); } } } // Adds a delay to limit play to specified fps // Calculates also delta to keep movement consistent in lower framerates void fps() { while (furi_get_tick() - lastFrameTime < FRAME_TIME); delta = (double)(furi_get_tick() - lastFrameTime) / (double)FRAME_TIME; lastFrameTime = furi_get_tick(); } double getActualFps() { return 1000 / ((double)FRAME_TIME * (double)delta); } uint8_t reverse_bits(uint8_t num) { unsigned int NO_OF_BITS = sizeof(num) * 8; uint8_t reverse_num = 0; uint8_t i; for (i = 0; i < NO_OF_BITS; i++) { if ((num & (1 << i))) reverse_num |= 1 << ((NO_OF_BITS - 1) - i); } return reverse_num; } ================================================ FILE: doom.c ================================================ #include #include #include #include #include #include #include "sound.h" #include "display.h" #include "compiled/assets_icons.h" #include "constants.h" #include "entities.h" #include "types.h" #include "level.h" #define SOUND // Useful macros #define swap(a, b) do { typeof(a) temp = a; a = b; b = temp; } while (0) #define sign(a, b) (double) (a > b ? 1 : (b > a ? -1 : 0)) #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) typedef enum { EventTypeTick, EventTypeKey, } EventType; typedef struct { EventType type; InputEvent input; } PluginEvent; typedef struct { Player player; Entity entity[MAX_ENTITIES]; StaticEntity static_entity[MAX_STATIC_ENTITIES]; uint8_t num_entities; uint8_t num_static_entities; uint8_t scene; uint8_t gun_pos; double jogging; double view_height; bool init; bool up; bool down; bool left; bool right; bool fired; bool gun_fired; double rot_speed; double old_dir_x; double old_plane_x; #ifdef SOUND MusicPlayer* music_instance; bool intro_sound; #endif } PluginState; Coords translateIntoView(Coords *pos, PluginState* const plugin_state); void updateHud(Canvas* const canvas, PluginState* const plugin_state); // general bool invert_screen = false; uint8_t flash_screen = 0; // game // player and entities uint8_t getBlockAt(const uint8_t level[], uint8_t x, uint8_t y) { if (x < 0 || x >= LEVEL_WIDTH || y < 0 || y >= LEVEL_HEIGHT) { return E_FLOOR; } // y is read in inverse order return pgm_read_byte(level + (((LEVEL_HEIGHT - 1 - y) * LEVEL_WIDTH + x) / 2)) >> (!(x % 2) * 4) // displace part of wanted bits & 0b1111; // mask wanted bits } // Finds the player in the map void initializeLevel(const uint8_t level[], PluginState* const plugin_state) { for (uint8_t y = LEVEL_HEIGHT - 1; y > 0; y--) { for (uint8_t x = 0; x < LEVEL_WIDTH; x++) { uint8_t block = getBlockAt(level, x, y); if (block == E_PLAYER) { plugin_state->player = create_player(x, y); return; } // todo create other static entities } } } bool isSpawned(UID uid, PluginState* const plugin_state) { for (uint8_t i = 0; i < plugin_state->num_entities; i++) { if (plugin_state->entity[i].uid == uid) return true; } return false; } bool isStatic(UID uid, PluginState* const plugin_state) { for (uint8_t i = 0; i < plugin_state->num_static_entities; i++) { if (plugin_state->static_entity[i].uid == uid) return true; } return false; } void spawnEntity(uint8_t type, uint8_t x, uint8_t y, PluginState* const plugin_state) { // Limit the number of spawned entities if (plugin_state->num_entities >= MAX_ENTITIES) { return; } // todo: read static entity status switch (type) { case E_ENEMY: plugin_state->entity[plugin_state->num_entities] = create_enemy(x, y); plugin_state->num_entities++; break; case E_KEY: plugin_state->entity[plugin_state->num_entities] = create_key(x, y); plugin_state->num_entities++; break; case E_MEDIKIT: plugin_state->entity[plugin_state->num_entities] = create_medikit(x, y); plugin_state->num_entities++; break; } } void spawnFireball(double x, double y, PluginState* const plugin_state) { // Limit the number of spawned entities if (plugin_state->num_entities >= MAX_ENTITIES) { return; } UID uid = create_uid(E_FIREBALL, x, y); // Remove if already exists, don't throw anything. Not the best, but shouldn't happen too often if (isSpawned(uid, plugin_state)) return; // Calculate direction. 32 angles int16_t dir = FIREBALL_ANGLES + atan2(y - plugin_state->player.pos.y, x - plugin_state->player.pos.x) / (double)PI * FIREBALL_ANGLES; if (dir < 0) dir += FIREBALL_ANGLES * 2; plugin_state->entity[plugin_state->num_entities] = create_fireball(x, y, dir); plugin_state->num_entities++; } void removeEntity(UID uid, PluginState* const plugin_state) { uint8_t i = 0; bool found = false; while (i < plugin_state->num_entities) { if (!found && plugin_state->entity[i].uid == uid) { // todo: doze it found = true; plugin_state->num_entities--; } // displace entities if (found) { plugin_state->entity[i] = plugin_state->entity[i + 1]; } i++; } } void removeStaticEntity(UID uid, PluginState* const plugin_state) { uint8_t i = 0; bool found = false; while (i < plugin_state->num_static_entities) { if (!found && plugin_state->static_entity[i].uid == uid) { found = true; plugin_state->num_static_entities--; } // displace entities if (found) { plugin_state->static_entity[i] = plugin_state->static_entity[i + 1]; } i++; } } UID detectCollision(const uint8_t level[], Coords *pos, double relative_x, double relative_y, bool only_walls, PluginState* const plugin_state) { // Wall collision uint8_t round_x = (int)pos->x + (int)relative_x; uint8_t round_y = (int)pos->y + (int)relative_y; uint8_t block = getBlockAt(level, round_x, round_y); if (block == E_WALL) { //playSound(hit_wall_snd, HIT_WALL_SND_LEN); return create_uid(block, round_x, round_y); } if (only_walls) { return UID_null; } // Entity collision for (uint8_t i=0; i < plugin_state->num_entities; i++) { // Don't collide with itself if (&(plugin_state->entity[i].pos) == pos) { continue; } uint8_t type = uid_get_type(plugin_state->entity[i].uid); // Only ALIVE enemy collision if (type != E_ENEMY || plugin_state->entity[i].state == S_DEAD || plugin_state->entity[i].state == S_HIDDEN) { continue; } Coords new_coords = { plugin_state->entity[i].pos.x - relative_x, plugin_state->entity[i].pos.y - relative_y }; uint8_t distance = coords_distance(pos, &new_coords); // Check distance and if it's getting closer if (distance < ENEMY_COLLIDER_DIST && distance < plugin_state->entity[i].distance) { return plugin_state->entity[i].uid; } } return UID_null; } // Shoot void fire(PluginState* const plugin_state) { //playSound(shoot_snd, SHOOT_SND_LEN); for (uint8_t i = 0; i < plugin_state->num_entities; i++) { // Shoot only ALIVE enemies if (uid_get_type(plugin_state->entity[i].uid) != E_ENEMY || plugin_state->entity[i].state == S_DEAD || plugin_state->entity[i].state == S_HIDDEN) { continue; } Coords transform = translateIntoView(&(plugin_state->entity[i].pos), plugin_state); if (fabs(transform.x) < 20 && transform.y > 0) { uint8_t damage = (double) fmin(GUN_MAX_DAMAGE, GUN_MAX_DAMAGE / (fabs(transform.x) * plugin_state->entity[i].distance) / 5); if (damage > 0) { plugin_state->entity[i].health = fmax(0, plugin_state->entity[i].health - damage); plugin_state->entity[i].state = S_HIT; plugin_state->entity[i].timer = 4; } } } } UID updatePosition(const uint8_t level[], Coords *pos, double relative_x, double relative_y, bool only_walls, PluginState* const plugin_state) { UID collide_x = detectCollision(level, pos, relative_x, 0, only_walls, plugin_state); UID collide_y = detectCollision(level, pos, 0, relative_y, only_walls, plugin_state); if (!collide_x) pos->x += relative_x; if (!collide_y) pos->y += relative_y; return collide_x || collide_y || UID_null; } void updateEntities(const uint8_t level[], Canvas* const canvas, PluginState* const plugin_state) { uint8_t i = 0; while (i < plugin_state->num_entities) { // update distance plugin_state->entity[i].distance = coords_distance(&(plugin_state->player.pos), &(plugin_state->entity[i].pos)); // Run the timer. Works with actual frames. // Todo: use delta here. But needs double type and more memory if (plugin_state->entity[i].timer > 0) plugin_state->entity[i].timer--; // too far away. put it in doze mode if (plugin_state->entity[i].distance > MAX_ENTITY_DISTANCE) { removeEntity(plugin_state->entity[i].uid, plugin_state); // don't increase 'i', since current one has been removed continue; } // bypass render if hidden if (plugin_state->entity[i].state == S_HIDDEN) { i++; continue; } uint8_t type = uid_get_type(plugin_state->entity[i].uid); switch (type) { case E_ENEMY: { // Enemy "IA" if (plugin_state->entity[i].health == 0) { if (plugin_state->entity[i].state != S_DEAD) { plugin_state->entity[i].state = S_DEAD; plugin_state->entity[i].timer = 6; } } else if (plugin_state->entity[i].state == S_HIT) { if (plugin_state->entity[i].timer == 0) { // Back to alert state plugin_state->entity[i].state = S_ALERT; plugin_state->entity[i].timer = 40; // delay next fireball thrown } } else if (plugin_state->entity[i].state == S_FIRING) { if (plugin_state->entity[i].timer == 0) { // Back to alert state plugin_state->entity[i].state = S_ALERT; plugin_state->entity[i].timer = 40; // delay next fireball throwm } } else { // ALERT STATE if (plugin_state->entity[i].distance > ENEMY_MELEE_DIST && plugin_state->entity[i].distance < MAX_ENEMY_VIEW) { if (plugin_state->entity[i].state != S_ALERT) { plugin_state->entity[i].state = S_ALERT; plugin_state->entity[i].timer = 20; // used to throw fireballs } else { if (plugin_state->entity[i].timer == 0) { // Throw a fireball spawnFireball(plugin_state->entity[i].pos.x, plugin_state->entity[i].pos.y, plugin_state); plugin_state->entity[i].state = S_FIRING; plugin_state->entity[i].timer = 6; } else { // move towards to the player. updatePosition( level, &(plugin_state->entity[i].pos), sign(plugin_state->player.pos.x, plugin_state->entity[i].pos.x) * (double)ENEMY_SPEED * 1, // NOT SURE (delta) sign(plugin_state->player.pos.y, plugin_state->entity[i].pos.y) * (double)ENEMY_SPEED * 1, // NOT SURE (delta) true, plugin_state ); } } } else if (plugin_state->entity[i].distance <= ENEMY_MELEE_DIST) { if (plugin_state->entity[i].state != S_MELEE) { // Preparing the melee attack plugin_state->entity[i].state = S_MELEE; plugin_state->entity[i].timer = 10; } else if (plugin_state->entity[i].timer == 0) { // Melee attack plugin_state->player.health = fmax(0, plugin_state->player.health - ENEMY_MELEE_DAMAGE); plugin_state->entity[i].timer = 14; flash_screen = 1; updateHud(canvas, plugin_state); } } else { // stand plugin_state->entity[i].state = S_STAND; } } break; } case E_FIREBALL: { if (plugin_state->entity[i].distance < FIREBALL_COLLIDER_DIST) { // Hit the player and disappear plugin_state->player.health = fmax(0, plugin_state->player.health - ENEMY_FIREBALL_DAMAGE); flash_screen = 1; updateHud(canvas, plugin_state); removeEntity(plugin_state->entity[i].uid, plugin_state); continue; // continue in the loop } else { // Move. Only collide with walls. // Note: using health to store the angle of the movement UID collided = updatePosition( level, &(plugin_state->entity[i].pos), cos((double) plugin_state->entity[i].health / FIREBALL_ANGLES * (double)PI) * (double)FIREBALL_SPEED, sin((double) plugin_state->entity[i].health / FIREBALL_ANGLES * (double)PI) * (double)FIREBALL_SPEED, true, plugin_state ); if (collided) { removeEntity(plugin_state->entity[i].uid, plugin_state); continue; // continue in the entity check loop } } break; } case E_MEDIKIT: { if (plugin_state->entity[i].distance < ITEM_COLLIDER_DIST) { // pickup //playSound(medkit_snd, MEDKIT_SND_LEN); plugin_state->entity[i].state = S_HIDDEN; plugin_state->player.health = fmin(100, plugin_state->player.health + 50); updateHud(canvas, plugin_state); flash_screen = 1; } break; } case E_KEY: { if (plugin_state->entity[i].distance < ITEM_COLLIDER_DIST) { // pickup //playSound(get_key_snd, GET_KEY_SND_LEN); plugin_state->entity[i].state = S_HIDDEN; plugin_state->player.keys++; updateHud(canvas, plugin_state); flash_screen = 1; } break; } } i++; } } // The map raycaster. Based on https://lodev.org/cgtutor/raycasting.html void renderMap(const uint8_t level[], double view_height, Canvas* const canvas, PluginState* const plugin_state) { UID last_uid = 0; // NOT SURE ? for (uint8_t x = 0; x < SCREEN_WIDTH; x += RES_DIVIDER) { double camera_x = 2 * (double) x / SCREEN_WIDTH - 1; double ray_x = plugin_state->player.dir.x + plugin_state->player.plane.x * camera_x; double ray_y = plugin_state->player.dir.y + plugin_state->player.plane.y * camera_x; uint8_t map_x = (uint8_t)plugin_state->player.pos.x; uint8_t map_y = (uint8_t)plugin_state->player.pos.y; Coords map_coords = { plugin_state->player.pos.x, plugin_state->player.pos.y }; double delta_x = fabs(1 / ray_x); double delta_y = fabs(1 / ray_y); int8_t step_x; int8_t step_y; double side_x; double side_y; if (ray_x < 0) { step_x = -1; side_x = (plugin_state->player.pos.x - map_x) * delta_x; } else { step_x = 1; side_x = (map_x + (double)1.0 - plugin_state->player.pos.x) * delta_x; } if (ray_y < 0) { step_y = -1; side_y = (plugin_state->player.pos.y - map_y) * delta_y; } else { step_y = 1; side_y = (map_y + (double)1.0 - plugin_state->player.pos.y) * delta_y; } // Wall detection uint8_t depth = 0; bool hit = 0; bool side; while (!hit && depth < MAX_RENDER_DEPTH) { if (side_x < side_y) { side_x += delta_x; map_x += step_x; side = 0; } else { side_y += delta_y; map_y += step_y; side = 1; } uint8_t block = getBlockAt(level, map_x, map_y); if (block == E_WALL) { hit = 1; } else { // Spawning entities here, as soon they are visible for the // player. Not the best place, but would be a very performance // cost scan for them in another loop if (block == E_ENEMY || (block & 0b00001000) /* all collectable items */) { // Check that it's close to the player if (coords_distance(&(plugin_state->player.pos), &map_coords) < MAX_ENTITY_DISTANCE) { UID uid = create_uid(block, map_x, map_y); if (last_uid != uid && !isSpawned(uid, plugin_state)) { spawnEntity(block, map_x, map_y, plugin_state); last_uid = uid; } } } } depth++; } if (hit) { double distance; if (side == 0) { distance = fmax(1, (map_x - plugin_state->player.pos.x + (1 - step_x) / 2) / ray_x); } else { distance = fmax(1, (map_y - plugin_state->player.pos.y + (1 - step_y) / 2) / ray_y); } // store zbuffer value for the column zbuffer[x / Z_RES_DIVIDER] = fmin(distance * DISTANCE_MULTIPLIER, 255); // rendered line height uint8_t line_height = RENDER_HEIGHT / distance; drawVLine( x, view_height / distance - line_height / 2 + RENDER_HEIGHT / 2, view_height / distance + line_height / 2 + RENDER_HEIGHT / 2, GRADIENT_COUNT - (int)distance / MAX_RENDER_DEPTH * GRADIENT_COUNT - side * 2, canvas ); } } } // Sort entities from far to close uint8_t sortEntities(PluginState* const plugin_state) { uint8_t gap = plugin_state->num_entities; bool swapped = false; while (gap > 1 || swapped) { //shrink factor 1.3 gap = (gap * 10) / 13; if (gap == 9 || gap == 10) gap = 11; if (gap < 1) gap = 1; swapped = false; for (uint8_t i = 0; i < plugin_state->num_entities - gap; i++) { uint8_t j = i + gap; if (plugin_state->entity[i].distance < plugin_state->entity[j].distance) { swap(plugin_state->entity[i], plugin_state->entity[j]); swapped = true; } } } return swapped; } Coords translateIntoView(Coords *pos, PluginState* const plugin_state) { //translate sprite position to relative to camera double sprite_x = pos->x - plugin_state->player.pos.x; double sprite_y = pos->y - plugin_state->player.pos.y; //required for correct matrix multiplication double inv_det = ((double)1.0 / ((double)plugin_state->player.plane.x * (double)plugin_state->player.dir.y - (double)plugin_state->player.dir.x * (double)plugin_state->player.plane.y)); double transform_x = inv_det * (plugin_state->player.dir.y * sprite_x - plugin_state->player.dir.x * sprite_y); double transform_y = inv_det * (- plugin_state->player.plane.y * sprite_x + plugin_state->player.plane.x * sprite_y); // Z in screen Coords res = {transform_x, transform_y}; return res; } void renderEntities(double view_height, Canvas* const canvas, PluginState* const plugin_state) { sortEntities(plugin_state); for (uint8_t i = 0; i < plugin_state->num_entities; i++) { if (plugin_state->entity[i].state == S_HIDDEN) continue; Coords transform = translateIntoView(&(plugin_state->entity[i].pos), plugin_state); // don´t render if behind the player or too far away if (transform.y <= (double)0.1 || transform.y > MAX_SPRITE_DEPTH) { continue; } int16_t sprite_screen_x = HALF_WIDTH * ((double)1.0 + transform.x / transform.y); int8_t sprite_screen_y = RENDER_HEIGHT / 2 + view_height / transform.y; uint8_t type = uid_get_type(plugin_state->entity[i].uid); // don´t try to render if outside of screen // doing this pre-shortcut due int16 -> int8 conversion makes out-of-screen // values fit into the screen space if (sprite_screen_x < - HALF_WIDTH || sprite_screen_x > SCREEN_WIDTH + HALF_WIDTH) { continue; } switch (type) { case E_ENEMY: { uint8_t sprite; if (plugin_state->entity[i].state == S_ALERT) { // walking sprite = ((int)furi_get_tick() / 500) % 2; } else if (plugin_state->entity[i].state == S_FIRING) { // fireball sprite = 2; } else if (plugin_state->entity[i].state == S_HIT) { // hit sprite = 3; } else if (plugin_state->entity[i].state == S_MELEE) { // melee atack sprite = plugin_state->entity[i].timer > 10 ? 2 : 1; } else if (plugin_state->entity[i].state == S_DEAD) { // dying sprite = plugin_state->entity[i].timer > 0 ? 3 : 4; } else { // stand sprite = 0; } drawSprite( sprite_screen_x - BMP_IMP_WIDTH * (double).5 / transform.y, sprite_screen_y - 8 / transform.y, imp_inv, imp_mask_inv, BMP_IMP_WIDTH, BMP_IMP_HEIGHT, sprite, transform.y, canvas ); break; } case E_FIREBALL: { drawSprite( sprite_screen_x - BMP_FIREBALL_WIDTH / 2 / transform.y, sprite_screen_y - BMP_FIREBALL_HEIGHT / 2 / transform.y, fireball, fireball_mask, BMP_FIREBALL_WIDTH, BMP_FIREBALL_HEIGHT, 0, transform.y, canvas ); break; } case E_MEDIKIT: { drawSprite( sprite_screen_x - BMP_ITEMS_WIDTH / 2 / transform.y, sprite_screen_y + 5 / transform.y, item, item_mask, BMP_ITEMS_WIDTH, BMP_ITEMS_HEIGHT, 0, transform.y, canvas ); break; } case E_KEY: { drawSprite( sprite_screen_x - BMP_ITEMS_WIDTH / 2 / transform.y, sprite_screen_y + 5 / transform.y, item, item_mask, BMP_ITEMS_WIDTH, BMP_ITEMS_HEIGHT, 1, transform.y, canvas ); break; } } } } void renderGun(uint8_t gun_pos, double amount_jogging, Canvas* const canvas) { // jogging char x = 48 + sin((double) furi_get_tick() * (double)JOGGING_SPEED) * 10 * amount_jogging; char y = RENDER_HEIGHT - gun_pos + fabs(cos((double) furi_get_tick() * (double)JOGGING_SPEED)) * 8 * amount_jogging; if (gun_pos > GUN_SHOT_POS - 2) { // Gun fire drawBitmap(x + 6, y - 11, &I_fire_inv, BMP_FIRE_WIDTH, BMP_FIRE_HEIGHT, 1, canvas); } // Don't draw over the hud! uint8_t clip_height = fmax(0, fmin(y + BMP_GUN_HEIGHT, RENDER_HEIGHT) - y); // Draw the gun (black mask + actual sprite). drawBitmap(x, y, &I_gun_mask_inv, BMP_GUN_WIDTH, clip_height, 0, canvas); drawBitmap(x, y, &I_gun_inv, BMP_GUN_WIDTH, clip_height, 1, canvas); //drawGun(x,y,gun_mask, BMP_GUN_WIDTH, clip_height, 0, canvas); //drawGun(x,y,gun, BMP_GUN_WIDTH, clip_height, 1, canvas); } // Only needed first time void renderHud(Canvas* const canvas, PluginState* plugin_state) { drawTextSpace(2, 58, "{}", 0, canvas); // Health symbol drawTextSpace(40, 58, "[]", 0, canvas); // Keys symbol updateHud(canvas, plugin_state); } // Render values for the HUD void updateHud(Canvas* const canvas, PluginState* plugin_state) { clearRect(12, 58, 15, 6, canvas); clearRect(50, 58, 15, 6, canvas); drawText(12, 58, plugin_state->player.health, canvas); drawText(50, 58, plugin_state->player.keys, canvas); } // Debug stats void renderStats(Canvas* const canvas, PluginState* plugin_state) { clearRect(58, 58, 70, 6, canvas); drawText(114, 58, (int)getActualFps(), canvas); drawText(82, 58, plugin_state->num_entities, canvas); // drawText(94, 58, freeMemory()); } // Intro screen void loopIntro(Canvas* const canvas) { canvas_draw_icon(canvas, (SCREEN_WIDTH - BMP_LOGO_WIDTH) / 2, (SCREEN_HEIGHT - BMP_LOGO_HEIGHT) / 3, &I_logo_inv); drawTextSpace(SCREEN_WIDTH / 2 - 25, SCREEN_HEIGHT * .8, "PRESS FIRE", 1, canvas); } static void render_callback(Canvas* const canvas, void* ctx) { PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); if(plugin_state == NULL) { return; } if(plugin_state->init) setupDisplay(canvas); canvas_set_font(canvas, FontPrimary); switch(plugin_state->scene){ case INTRO:{ loopIntro(canvas); break; } case GAME_PLAY:{ updateEntities(sto_level_1, canvas, plugin_state); renderGun(plugin_state->gun_pos,plugin_state->jogging, canvas); renderMap(sto_level_1, plugin_state->view_height, canvas, plugin_state); renderEntities(plugin_state->view_height, canvas, plugin_state); renderHud(canvas, plugin_state); updateHud(canvas, plugin_state); renderStats(canvas, plugin_state); break; } } release_mutex((ValueMutex*)ctx, plugin_state); } static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { furi_assert(event_queue); PluginEvent event = {.type = EventTypeKey, .input = *input_event}; furi_message_queue_put(event_queue, &event, 0); } static void doom_state_init(PluginState* const plugin_state) { plugin_state->num_entities = 0; plugin_state->num_static_entities = 0; plugin_state->scene = INTRO; plugin_state->gun_pos = 0; plugin_state->view_height = 0; plugin_state->init = true; plugin_state->up = false; plugin_state->down = false; plugin_state->left = false; plugin_state->right = false; plugin_state->fired = false; plugin_state->gun_fired = false; #ifdef SOUND plugin_state->music_instance = malloc(sizeof(MusicPlayer)); plugin_state->music_instance->model = malloc(sizeof(MusicPlayerModel)); memset(plugin_state->music_instance->model->duration_history, 0xff, MUSIC_PLAYER_SEMITONE_HISTORY_SIZE); memset(plugin_state->music_instance->model->semitone_history, 0xff, MUSIC_PLAYER_SEMITONE_HISTORY_SIZE); plugin_state->music_instance->model->volume = 2; plugin_state->music_instance->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal); //plugin_state->music_instance->view_port = view_port_alloc(); plugin_state->music_instance->worker = music_player_worker_alloc(); //music_player_worker_set_volume(plugin_state->music_instance->worker, 0.75); music_player_worker_set_volume(plugin_state->music_instance->worker, MUSIC_PLAYER_VOLUMES[plugin_state->music_instance->model->volume]); plugin_state->intro_sound = true; //init_sound(plugin_state->music_instance); #endif } static void doom_game_update_timer_callback(FuriMessageQueue* event_queue) { furi_assert(event_queue); PluginEvent event = {.type = EventTypeTick}; furi_message_queue_put(event_queue, &event, 0); } static void doom_game_tick(PluginState* const plugin_state){ if(plugin_state->scene == GAME_PLAY){ fps(); memset(display_buf, 0, SCREEN_WIDTH * (RENDER_HEIGHT / 8)); //player is alive if(plugin_state->player.health > 0){ if(plugin_state->up){ plugin_state->player.velocity += ((double)MOV_SPEED - plugin_state->player.velocity) * (double).4; plugin_state->jogging = fabs(plugin_state->player.velocity) * MOV_SPEED_INV; //plugin_state->up = false; }else if(plugin_state->down){ plugin_state->player.velocity += (- (double)MOV_SPEED - plugin_state->player.velocity) * (double).4; plugin_state->jogging = fabs(plugin_state->player.velocity) * MOV_SPEED_INV; //plugin_state->down = false; }else { plugin_state->player.velocity *= (double).5; plugin_state->jogging = fabs(plugin_state->player.velocity) * MOV_SPEED_INV; } if(plugin_state->right){ plugin_state->rot_speed = (double)ROT_SPEED * delta; plugin_state->old_dir_x = plugin_state->player.dir.x; plugin_state->player.dir.x = plugin_state->player.dir.x * cos(-(plugin_state->rot_speed)) - plugin_state->player.dir.y * sin(-(plugin_state->rot_speed)); plugin_state->player.dir.y = plugin_state->old_dir_x * sin(-(plugin_state->rot_speed)) + plugin_state->player.dir.y * cos(-(plugin_state->rot_speed)); plugin_state->old_plane_x = plugin_state->player.plane.x; plugin_state->player.plane.x = plugin_state->player.plane.x * cos(-(plugin_state->rot_speed)) - plugin_state->player.plane.y * sin(-(plugin_state->rot_speed)); plugin_state->player.plane.y = plugin_state->old_plane_x * sin(-(plugin_state->rot_speed)) + plugin_state->player.plane.y * cos(-(plugin_state->rot_speed)); //plugin_state->right = false; }else if(plugin_state->left){ plugin_state->rot_speed = (double)ROT_SPEED * delta; plugin_state->old_dir_x = plugin_state->player.dir.x; plugin_state->player.dir.x = plugin_state->player.dir.x * cos(plugin_state->rot_speed) - plugin_state->player.dir.y * sin(plugin_state->rot_speed); plugin_state->player.dir.y = plugin_state->old_dir_x * sin(plugin_state->rot_speed) + plugin_state->player.dir.y * cos(plugin_state->rot_speed); plugin_state->old_plane_x = plugin_state->player.plane.x; plugin_state->player.plane.x = plugin_state->player.plane.x * cos(plugin_state->rot_speed) - plugin_state->player.plane.y * sin(plugin_state->rot_speed); plugin_state->player.plane.y = plugin_state->old_plane_x * sin(plugin_state->rot_speed) + plugin_state->player.plane.y * cos(plugin_state->rot_speed); //plugin_state->left = false; } plugin_state->view_height = fabs(sin((double) furi_get_tick() * (double)JOGGING_SPEED)) * 6 * plugin_state->jogging; if (plugin_state->gun_pos > GUN_TARGET_POS) { // Right after fire plugin_state->gun_pos -= 1; }else if(plugin_state->gun_pos < GUN_TARGET_POS){ plugin_state->gun_pos += 2; }else if(!plugin_state->gun_fired && plugin_state->fired){ //furi_hal_speaker_start(20480 / 10, 0.45f); /*#ifdef SOUND music_player_worker_start(plugin_state->music_instance->worker); #endif*/ plugin_state->gun_pos = GUN_SHOT_POS; plugin_state->gun_fired = true; plugin_state->fired = false; fire(plugin_state); }else if(plugin_state->gun_fired && !plugin_state->fired){ //furi_hal_speaker_stop(); plugin_state->gun_fired = false; /*#ifdef SOUND music_player_worker_stop(plugin_state->music_instance->worker); #endif*/ } }else{ // Player is dead if(plugin_state->view_height > -10) plugin_state->view_height--; if(plugin_state->gun_pos > 1) plugin_state->gun_pos -=2; } if(fabs(plugin_state->player.velocity) > (double)0.003){ updatePosition(sto_level_1, &(plugin_state->player.pos), plugin_state->player.dir.x * plugin_state->player.velocity * delta, plugin_state->player.dir.y * plugin_state->player.velocity * delta, false, plugin_state); }else{ plugin_state->player.velocity = 0; } } } int32_t doom_app() { FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); PluginState* plugin_state = malloc(sizeof(PluginState)); doom_state_init(plugin_state); ValueMutex state_mutex; if (!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { FURI_LOG_E("Doom_game", "cannot create mutex\r\n"); free(plugin_state); return 255; } FuriTimer* timer = furi_timer_alloc(doom_game_update_timer_callback, FuriTimerTypePeriodic, event_queue); furi_timer_start(timer, furi_kernel_get_tick_frequency()/ 12); // Set system callbacks ViewPort* view_port = view_port_alloc(); view_port_draw_callback_set(view_port, render_callback, &state_mutex); view_port_input_callback_set(view_port, input_callback, event_queue); // Open GUI and register view_port Gui* gui = furi_record_open("gui"); gui_add_view_port(gui, view_port, GuiLayerFullscreen); // GAMEPLAY VARS bool gun_fired = false; //double jogging; uint8_t fade = GRADIENT_COUNT - 1; ////////////////////////////////// if(display_buf != NULL) plugin_state->init = false; PluginEvent event; #ifdef SOUND music_player_worker_load_rtttl_from_string(plugin_state->music_instance->worker, dsintro); music_player_worker_start(plugin_state->music_instance->worker); #endif for(bool processing = true; processing;) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); #ifdef SOUND furi_check(furi_mutex_acquire(plugin_state->music_instance->model_mutex, FuriWaitForever) == FuriStatusOk); #endif if(event_status == FuriStatusOk) { if(event.type == EventTypeTick){ doom_game_tick(plugin_state); } plugin_state->fired = false; // press events if(event.type == EventTypeKey) { if(event.input.type == InputTypePress) { if(plugin_state->scene == INTRO && event.input.key == InputKeyOk){ plugin_state->scene = GAME_PLAY; initializeLevel(sto_level_1, plugin_state); #ifdef SOUND furi_mutex_release(plugin_state->music_instance->model_mutex); music_player_worker_stop(plugin_state->music_instance->worker); plugin_state->intro_sound = false; #endif } //While playing game if(plugin_state->scene == GAME_PLAY){ // If the player is alive if (plugin_state->player.health > 0) { //Player speed if(event.input.key == InputKeyUp){ plugin_state->up = true; }else if (event.input.key == InputKeyDown) { plugin_state->down = true; } // Player rotation if(event.input.key == InputKeyRight){ plugin_state->right = true; }else if(event.input.key == InputKeyLeft){ plugin_state->left = true; } if(event.input.key == InputKeyOk){ /*#ifdef SOUND music_player_worker_load_rtttl_from_string(plugin_state->music_instance->worker, dspistol); #endif*/ plugin_state->fired = true; } }else{ // Player is dead if(event.input.key == InputKeyOk)plugin_state->scene = INTRO; } } } if(event.input.type == InputTypeRelease){ if (plugin_state->player.health > 0) { //Player speed if(event.input.key == InputKeyUp){ plugin_state->up = false; }else if (event.input.key == InputKeyDown) { plugin_state->down = false; } // Player rotation if(event.input.key == InputKeyRight){ plugin_state->right = false; }else if(event.input.key == InputKeyLeft){ plugin_state->left = false; } } } if(event.input.key == InputKeyBack){ processing = false; #ifdef SOUND if(plugin_state->intro_sound){ furi_mutex_release(plugin_state->music_instance->model_mutex); music_player_worker_stop(plugin_state->music_instance->worker); } #endif } } } else { FURI_LOG_D("Doom_game", "osMessageQueue: event timeout"); // event timeout } #ifdef SOUND furi_mutex_release(plugin_state->music_instance->model_mutex); #endif view_port_update(view_port); release_mutex(&state_mutex, plugin_state); } #ifdef SOUND music_player_worker_free(plugin_state->music_instance->worker); furi_mutex_free(plugin_state->music_instance->model_mutex); free(plugin_state->music_instance->model); free(plugin_state->music_instance); #endif furi_timer_free(timer); view_port_enabled_set(view_port, false); gui_remove_view_port(gui, view_port); furi_record_close("gui"); view_port_free(view_port); furi_message_queue_free(event_queue); return 0; } ================================================ FILE: entities.c ================================================ #include "entities.h" //extern "C" /*Player create_player(double x, double y){ return {create_coords((double) x + (double)0.5, (double) y + (double)0.5), create_coords(1, 0), create_coords(0, -0.66), 0, 100, 0}; }*/ Player create_player(double x, double y){ Player p; p.pos = create_coords((double) x + (double)0.5, (double) y + (double)0.5); p.dir = create_coords(1, 0); p.plane = create_coords(0, -0.66); p.velocity = 0; p.health = 100; p.keys = 0; return p;//{create_coords((double) x + (double)0.5, (double) y + (double)0.5), create_coords(1, 0), create_coords(0, -0.66), 0, 100, 0}; } //extern "C" Entity create_entity(uint8_t type, uint8_t x, uint8_t y, uint8_t initialState, uint8_t initialHealth) { UID uid = create_uid(type, x, y); Coords pos = create_coords((double) x + (double).5, (double) y + (double).5); Entity new_entity;// = { uid, pos, initialState, initialHealth, 0, 0 }; new_entity.uid = uid; new_entity.pos = pos; new_entity.state = initialState; new_entity.health = initialHealth; new_entity.distance = 0; new_entity.timer = 0; return new_entity; } //extern "C" StaticEntity crate_static_entity(UID uid, uint8_t x, uint8_t y, bool active) { StaticEntity ent; ent.uid = uid; ent.x = x; ent.y = y; ent.active = active; return ent; } ================================================ FILE: entities.h ================================================ #ifndef _entities_h #define _entities_h #include #include #include "types.h" // Shortcuts //#define create_player(x, y) {create_coords((double) x + (double)0.5, (double) y + (double)0.5), create_coords(1, 0), create_coords(0, -0.66), 0, 100} #define create_enemy(x, y) create_entity(E_ENEMY, x, y, S_STAND, 50) #define create_medikit(x, y) create_entity(E_MEDIKIT, x, y, S_STAND, 0) #define create_key(x, y) create_entity(E_KEY, x, y, S_STAND, 0) #define create_fireball(x, y, dir) create_entity(E_FIREBALL, x, y, S_STAND, dir) #define create_door(x, y) create_entity(E_DOOR, x, y, S_STAND, 0) // entity statuses #define S_STAND 0 #define S_ALERT 1 #define S_FIRING 2 #define S_MELEE 3 #define S_HIT 4 #define S_DEAD 5 #define S_HIDDEN 6 #define S_OPEN 7 #define S_CLOSE 8 typedef struct Player { Coords pos; Coords dir; Coords plane; double velocity; uint8_t health; uint8_t keys; } Player; typedef struct Entity { UID uid; Coords pos; uint8_t state; uint8_t health; // angle for fireballs uint8_t distance; uint8_t timer; } Entity; typedef struct StaticEntity { UID uid; uint8_t x; uint8_t y; bool active; } StaticEntity; Entity create_entity(uint8_t type, uint8_t x, uint8_t y, uint8_t initialState, uint8_t initialHealth); StaticEntity create_static_entity(UID uid, uint8_t x, uint8_t y, bool active); Player create_player(double x, double y); #endif ================================================ FILE: level.h ================================================ #ifndef _level_h #define _level_h #include "constants.h" /* Based on E1M1 from Wolfenstein 3D ################################################################ #############################...........######################## ######....###################........E..######################## ######....########..........#...........#...#################### ######.....#######..........L.....E.......M.#################### ######.....#######..........#...........#...#################### ##################...########...........######################## ######.........###...########...........######################## ######.........###...#############D############################# ######.........#......E##########...############################ ######....E....D...E...##########...############################ ######.........#.......##########...############################ ######....E....##################...############################ #...##.........##################...############################ #.K.######D######################...############################ #...#####...###############...#E.....K########################## ##D######...###############..####...############################ #...#####...###############..####...############################ #...#...#...###############..####...############################ #...D...#...#####################...############################ #...#...#...#####################...############################ #...######D#######################L############################# #.E.##.........#################.....#################........## #...##.........############...............############........## #...##...E.....############...............############........## #....#.........############...E.......E....#.........#........## #....L....K....############................D....E....D....E...## #....#.........############................#.........#........## #...##.....E...############...............####....####........## #...##.........############...............#####..#####.....M..## #...##.........#################.....##########..#####........## #...######L#######################D############..############### #...#####...#####################...###########..############### #E.E#####...#####################...###########..############### #...#...#...#####################.E.###########..############### #...D.M.#...#####################...###########..############### #...#...#...#####################...###########..###.#.#.#.##### #...#####...#####################...###########...#.........#### #...#####...#####################...###########...D....E..K.#### #................##......########...###########...#.........#### #....E........E...L...E...X######...################.#.#.#.##### #................##......########...############################ #################################...############################ #############..#..#..#############L############################# ###########....#..#.########....#...#....####################### #############.....##########.P..D...D....####################### ############################....#...#....####################### ##############..#################...############################ ##############..############....#...#....####################### ############################....D...D....####################### ############################....#...#....####################### #################################...############################ ############################.............####################### ############################..........EK.####################### ############################.............####################### ################################################################ */ /* Same map above built from some regexp replacements using the legend above. Using this way lets me use only 4 bit to store each block */ const uint8_t sto_level_1[LEVEL_SIZE] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x02, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x20, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x90, 0xFF, 0xFF, 0xFF, 0x4F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xF2, 0x00, 0x00, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4F, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x40, 0x00, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0x4F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x5F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x20, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0xFF, 0x00, 0x02, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x05, 0x00, 0x00, 0x90, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x00, 0x20, 0x00, 0xFF, 0xF0, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0xFF, 0x00, 0x00, 0x02, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0x00, 0x00, 0x08, 0x00, 0xFF, 0xF0, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0x5F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF2, 0x02, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x20, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x40, 0x80, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x40, 0x00, 0x02, 0x00, 0x90, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xF0, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x50, 0x00, 0x20, 0x00, 0x7F, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xF0, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0x00, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x5F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x0F, 0x00, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x40, 0x00, 0x40, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xF0, 0x00, 0xF0, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, }; #endif ================================================ FILE: sound.h ================================================ #ifndef sound_h #define sound_h #include #include #include #include #include "../music_player/music_player_worker.h" //static const char dspistol[] = "AnyConv:d=,o=,b=120:408,40p,40p,40p,40p,405,40p,40p,40p,405,30p.,30p.,30p.,13p"; static const char dsintro[] = "Doom:d=32,o=4,b=56:f,f,f5,f,f,d#5,f,f,c#5,f,f,b,f,f,c5,c#5,f,f,f5,f,f,d#5,f,f,c#5,f,f,8b.,f,f,f5,f,f,d#5,f,f,c#5,f,f,b,f,f,c5,c#5,f,f,f5,f,f,d#5,f,f,c#5,f,f,8b.,a#,a#,a#5,a#,a#,g#5,a#,a#,f#5,a#,a#,e5,a#,a#,f5,f#5,a#,a#,a#5,a#,a#,g#5,a#,a#,f#5,a#,a#,8e5"; //static const char dsgetpow[] = "dsgetpow:d=,o=,b=120:407,40p,30.6,407,40p,406,40p,407,40p,40p,407,30p.,407"; //static const char dsnoway[] = "dsnoway:d=,o=,b=120:407,30.4"; #define MUSIC_PLAYER_SEMITONE_HISTORY_SIZE 4 static const float MUSIC_PLAYER_VOLUMES[] = {0, .25, .5, .75, 1}; typedef struct { uint8_t semitone_history[MUSIC_PLAYER_SEMITONE_HISTORY_SIZE]; uint8_t duration_history[MUSIC_PLAYER_SEMITONE_HISTORY_SIZE]; uint8_t volume; uint8_t semitone; uint8_t dots; uint8_t duration; float position; } MusicPlayerModel; typedef struct { MusicPlayerModel* model; MusicPlayerWorker* worker; FuriMutex** model_mutex; } MusicPlayer; #endif ================================================ FILE: types.c ================================================ #include "types.h" /*template inline T sq(T value) { return value * value; }*/ double sq(double val){ return val * val; } //extern "C" Coords create_coords(double x, double y) { Coords cord; cord.x = x; cord.y = y; return cord; } //extern "C" uint8_t coords_distance(Coords* a, Coords* b) { return sqrt(sq(a->x - b->x) + sq(a->y - b->y)) * 20; } //extern "C" UID create_uid(uint8_t type, uint8_t x, uint8_t y) { return ((y << 6) | x) << 4 | type; } //extern "C" uint8_t uid_get_type(UID uid) { return uid & 0x0F; } ================================================ FILE: types.h ================================================ #ifndef _types_h #define _types_h #include #include //#include "constants.h" #define UID_null 0 // Entity types (legend applies to level.h) #define E_FLOOR 0x0 // . (also null) #define E_WALL 0xF // # #define E_PLAYER 0x1 // P #define E_ENEMY 0x2 // E #define E_DOOR 0x4 // D #define E_LOCKEDDOOR 0x5 // L #define E_EXIT 0x7 // X // collectable entities >= 0x8 #define E_MEDIKIT 0x8 // M #define E_KEY 0x9 // K #define E_FIREBALL 0xA // not in map typedef uint16_t UID; typedef uint8_t EType; typedef struct Coords { double x; double y; }Coords; UID create_uid(EType type, uint8_t x, uint8_t y); EType uid_get_type(UID uid); Coords create_coords(double x, double y); uint8_t coords_distance(Coords* a, Coords* b); #endif