diff --git a/applications/plugins/doom/README.md b/applications/plugins/doom/README.md new file mode 100644 index 000000000..d43b7ab1a --- /dev/null +++ b/applications/plugins/doom/README.md @@ -0,0 +1,68 @@ +# 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) diff --git a/applications/plugins/doom/application.fam b/applications/plugins/doom/application.fam new file mode 100644 index 000000000..f0ce5eea4 --- /dev/null +++ b/applications/plugins/doom/application.fam @@ -0,0 +1,15 @@ +App( + appid="game_doom", + name="DOOM", + apptype=FlipperAppType.PLUGIN, + entry_point="doom_app", + cdefines=["APP_DOOM_GAME"], + requires=[ + "gui", + "music_player", + ], + stack_size=4 * 1024, + order=75, + fap_icon="doom_10px.png", + fap_category="Games", +) diff --git a/applications/plugins/doom/assets/door2.png b/applications/plugins/doom/assets/door2.png new file mode 100644 index 000000000..b4b4f0399 Binary files /dev/null and b/applications/plugins/doom/assets/door2.png differ diff --git a/applications/plugins/doom/assets/door_inv.png b/applications/plugins/doom/assets/door_inv.png new file mode 100644 index 000000000..3185f524c Binary files /dev/null and b/applications/plugins/doom/assets/door_inv.png differ diff --git a/applications/plugins/doom/assets/fire_inv.png b/applications/plugins/doom/assets/fire_inv.png new file mode 100644 index 000000000..46af8691b Binary files /dev/null and b/applications/plugins/doom/assets/fire_inv.png differ diff --git a/applications/plugins/doom/assets/fireball_inv.png b/applications/plugins/doom/assets/fireball_inv.png new file mode 100644 index 000000000..b046288f8 Binary files /dev/null and b/applications/plugins/doom/assets/fireball_inv.png differ diff --git a/applications/plugins/doom/assets/fireball_mask_inv.png b/applications/plugins/doom/assets/fireball_mask_inv.png new file mode 100644 index 000000000..548c654b7 Binary files /dev/null and b/applications/plugins/doom/assets/fireball_mask_inv.png differ diff --git a/applications/plugins/doom/assets/gradient_inv.png b/applications/plugins/doom/assets/gradient_inv.png new file mode 100644 index 000000000..78eec8c20 Binary files /dev/null and b/applications/plugins/doom/assets/gradient_inv.png differ diff --git a/applications/plugins/doom/assets/gun_inv.png b/applications/plugins/doom/assets/gun_inv.png new file mode 100644 index 000000000..e2ec05295 Binary files /dev/null and b/applications/plugins/doom/assets/gun_inv.png differ diff --git a/applications/plugins/doom/assets/gun_mask_inv.png b/applications/plugins/doom/assets/gun_mask_inv.png new file mode 100644 index 000000000..2d761a70a Binary files /dev/null and b/applications/plugins/doom/assets/gun_mask_inv.png differ diff --git a/applications/plugins/doom/assets/imp_inv.png b/applications/plugins/doom/assets/imp_inv.png new file mode 100644 index 000000000..4b480f1c5 Binary files /dev/null and b/applications/plugins/doom/assets/imp_inv.png differ diff --git a/applications/plugins/doom/assets/imp_mask_inv.png b/applications/plugins/doom/assets/imp_mask_inv.png new file mode 100644 index 000000000..70e991270 Binary files /dev/null and b/applications/plugins/doom/assets/imp_mask_inv.png differ diff --git a/applications/plugins/doom/assets/intro-screen.png b/applications/plugins/doom/assets/intro-screen.png new file mode 100644 index 000000000..e46d6fdc3 Binary files /dev/null and b/applications/plugins/doom/assets/intro-screen.png differ diff --git a/applications/plugins/doom/assets/item_inv.png b/applications/plugins/doom/assets/item_inv.png new file mode 100644 index 000000000..1d32dbcd8 Binary files /dev/null and b/applications/plugins/doom/assets/item_inv.png differ diff --git a/applications/plugins/doom/assets/item_mask_inv.png b/applications/plugins/doom/assets/item_mask_inv.png new file mode 100644 index 000000000..a0bde9c76 Binary files /dev/null and b/applications/plugins/doom/assets/item_mask_inv.png differ diff --git a/applications/plugins/doom/assets/screenshot-imp2.jpg b/applications/plugins/doom/assets/screenshot-imp2.jpg new file mode 100644 index 000000000..0b4c29c4f Binary files /dev/null and b/applications/plugins/doom/assets/screenshot-imp2.jpg differ diff --git a/applications/plugins/doom/assets/screenshot-intro2.jpg b/applications/plugins/doom/assets/screenshot-intro2.jpg new file mode 100644 index 000000000..21fd72840 Binary files /dev/null and b/applications/plugins/doom/assets/screenshot-intro2.jpg differ diff --git a/applications/plugins/doom/assets/screenshot-medkit2.jpg b/applications/plugins/doom/assets/screenshot-medkit2.jpg new file mode 100644 index 000000000..4ee0269f3 Binary files /dev/null and b/applications/plugins/doom/assets/screenshot-medkit2.jpg differ diff --git a/applications/plugins/doom/assets/screenshot-start2.jpg b/applications/plugins/doom/assets/screenshot-start2.jpg new file mode 100644 index 000000000..6b28ed987 Binary files /dev/null and b/applications/plugins/doom/assets/screenshot-start2.jpg differ diff --git a/applications/plugins/doom/assets/screenshot1.png b/applications/plugins/doom/assets/screenshot1.png new file mode 100644 index 000000000..1ecb073a6 Binary files /dev/null and b/applications/plugins/doom/assets/screenshot1.png differ diff --git a/applications/plugins/doom/assets/screenshot2.png b/applications/plugins/doom/assets/screenshot2.png new file mode 100644 index 000000000..216b699de Binary files /dev/null and b/applications/plugins/doom/assets/screenshot2.png differ diff --git a/applications/plugins/doom/assets/screenshot3.png b/applications/plugins/doom/assets/screenshot3.png new file mode 100644 index 000000000..b5aec03fd Binary files /dev/null and b/applications/plugins/doom/assets/screenshot3.png differ diff --git a/applications/plugins/doom/compiled/assets_icons.c b/applications/plugins/doom/compiled/assets_icons.c new file mode 100644 index 000000000..041b35728 --- /dev/null +++ b/applications/plugins/doom/compiled/assets_icons.c @@ -0,0 +1,403 @@ +#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}; \ No newline at end of file diff --git a/applications/plugins/doom/compiled/assets_icons.h b/applications/plugins/doom/compiled/assets_icons.h new file mode 100644 index 000000000..024aceab1 --- /dev/null +++ b/applications/plugins/doom/compiled/assets_icons.h @@ -0,0 +1,114 @@ +#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 diff --git a/applications/plugins/doom/constants.h b/applications/plugins/doom/constants.h new file mode 100644 index 000000000..4e0f10118 --- /dev/null +++ b/applications/plugins/doom/constants.h @@ -0,0 +1,91 @@ +#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 20 + +// 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 diff --git a/applications/plugins/doom/display.h b/applications/plugins/doom/display.h new file mode 100644 index 000000000..cc9052acc --- /dev/null +++ b/applications/plugins/doom/display.h @@ -0,0 +1,282 @@ +#include +#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; j < h; j++, y++) { + for(int16_t i = 0; i < w; i++) { + if(i & 7) + byte <<= 1; + else + byte = pgm_read_byte(&bitmap[j * byteWidth + i / 8]); + if(byte & 0x80) drawPixel(x + i, y, color, false, canvas); + } + } +} + +void drawVLine(uint8_t x, int8_t start_y, int8_t end_y, uint8_t intensity, Canvas* const canvas) { + UNUSED(intensity); + uint8_t dots = end_y - start_y; + for(int i = 0; i < dots; i++) { + canvas_draw_dot(canvas, x, start_y + i); + } +} + +void setupDisplay(Canvas* canvas) { + memset(zbuffer, 0xff, 128); + display_buf = (uint8_t*)canvas_get_buffer(canvas); + //display_buf = u8g2_GetBufferPtr(&canvas->fb); +} + +void drawBitmap( + int16_t x, + int16_t y, + const Icon* i, + int16_t w, + int16_t h, + uint16_t color, + Canvas* const canvas) { + UNUSED(color); + 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; +} \ No newline at end of file diff --git a/applications/plugins/doom/doom.c b/applications/plugins/doom/doom.c new file mode 100644 index 000000000..424bd0678 --- /dev/null +++ b/applications/plugins/doom/doom.c @@ -0,0 +1,1105 @@ +#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" +#include +#include + +#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; + NotificationApp* notify; +#ifdef SOUND + MusicPlayer* music_instance; + bool intro_sound; +#endif +} PluginState; + +static const NotificationSequence sequence_short_sound = { + &message_note_c5, + &message_delay_50, + &message_sound_off, + NULL, +}; +static const NotificationSequence sequence_long_sound = { + &message_note_c3, + &message_delay_100, + &message_sound_off, + NULL, +}; + +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 >= LEVEL_WIDTH || 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 + notification_message(plugin_state->notify, &sequence_long_sound); + //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 + notification_message(plugin_state->notify, &sequence_long_sound); + //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->notify = furi_record_open(RECORD_NOTIFICATION); + 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; + + notification_message(plugin_state->notify, &sequence_short_sound); + + /*#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"); + furi_record_close(RECORD_NOTIFICATION); + furi_message_queue_free(event_queue); + 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); + + ////////////////////////////////// + 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) { + // press events + if(event.type == EventTypeKey) { + 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 + } + + 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 + goto skipintro; + } + + //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*/ + if(plugin_state->fired) { + plugin_state->fired = false; + } else { + 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; + } + } + } + } + + skipintro: + if(event.type == EventTypeTick) { + doom_game_tick(plugin_state); + } + } +#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_record_close(RECORD_NOTIFICATION); + 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); + free(plugin_state); + return 0; +} diff --git a/applications/plugins/doom/doom_10px.png b/applications/plugins/doom/doom_10px.png new file mode 100644 index 000000000..4abf72cef Binary files /dev/null and b/applications/plugins/doom/doom_10px.png differ diff --git a/applications/plugins/doom/doom_music_player_worker.c b/applications/plugins/doom/doom_music_player_worker.c new file mode 100644 index 000000000..406cf0576 --- /dev/null +++ b/applications/plugins/doom/doom_music_player_worker.c @@ -0,0 +1,499 @@ +#include "doom_music_player_worker.h" + +#include +#include + +#include +#include + +#include + +#define TAG "MusicPlayerWorker" + +#define MUSIC_PLAYER_FILETYPE "Flipper Music Format" +#define MUSIC_PLAYER_VERSION 0 + +#define SEMITONE_PAUSE 0xFF + +#define NOTE_C4 261.63f +#define NOTE_C4_SEMITONE (4.0f * 12.0f) +#define TWO_POW_TWELTH_ROOT 1.059463094359f + +typedef struct { + uint8_t semitone; + uint8_t duration; + uint8_t dots; +} NoteBlock; + +ARRAY_DEF(NoteBlockArray, NoteBlock, M_POD_OPLIST); + +struct MusicPlayerWorker { + FuriThread* thread; + bool should_work; + + MusicPlayerWorkerCallback callback; + void* callback_context; + + float volume; + uint32_t bpm; + uint32_t duration; + uint32_t octave; + NoteBlockArray_t notes; +}; + +static int32_t music_player_worker_thread_callback(void* context) { + furi_assert(context); + MusicPlayerWorker* instance = context; + + NoteBlockArray_it_t it; + NoteBlockArray_it(it, instance->notes); + + while(instance->should_work) { + if(NoteBlockArray_end_p(it)) { + NoteBlockArray_it(it, instance->notes); + furi_delay_ms(10); + } else { + NoteBlock* note_block = NoteBlockArray_ref(it); + + float note_from_a4 = (float)note_block->semitone - NOTE_C4_SEMITONE; + float frequency = NOTE_C4 * powf(TWO_POW_TWELTH_ROOT, note_from_a4); + float duration = + 60.0 * furi_kernel_get_tick_frequency() * 4 / instance->bpm / note_block->duration; + uint32_t dots = note_block->dots; + while(dots > 0) { + duration += duration / 2; + dots--; + } + uint32_t next_tick = furi_get_tick() + duration; + float volume = instance->volume; + + if(instance->callback) { + instance->callback( + note_block->semitone, + note_block->dots, + note_block->duration, + 0.0, + instance->callback_context); + } + + furi_hal_speaker_stop(); + furi_hal_speaker_start(frequency, volume); + while(instance->should_work && furi_get_tick() < next_tick) { + volume *= 0.9945679; + furi_hal_speaker_set_volume(volume); + furi_delay_ms(2); + } + NoteBlockArray_next(it); + } + } + + furi_hal_speaker_stop(); + + return 0; +} + +MusicPlayerWorker* music_player_worker_alloc() { + MusicPlayerWorker* instance = malloc(sizeof(MusicPlayerWorker)); + + NoteBlockArray_init(instance->notes); + + instance->thread = furi_thread_alloc(); + furi_thread_set_name(instance->thread, "MusicPlayerWorker"); + furi_thread_set_stack_size(instance->thread, 1024); + furi_thread_set_context(instance->thread, instance); + furi_thread_set_callback(instance->thread, music_player_worker_thread_callback); + + instance->volume = 1.0f; + + return instance; +} + +void music_player_worker_free(MusicPlayerWorker* instance) { + furi_assert(instance); + furi_thread_free(instance->thread); + NoteBlockArray_clear(instance->notes); + free(instance); +} + +static bool is_digit(const char c) { + return isdigit(c) != 0; +} + +static bool is_letter(const char c) { + return islower(c) != 0 || isupper(c) != 0; +} + +static bool is_space(const char c) { + return c == ' ' || c == '\t'; +} + +static size_t extract_number(const char* string, uint32_t* number) { + size_t ret = 0; + while(is_digit(*string)) { + *number *= 10; + *number += (*string - '0'); + string++; + ret++; + } + return ret; +} + +static size_t extract_dots(const char* string, uint32_t* number) { + size_t ret = 0; + while(*string == '.') { + *number += 1; + string++; + ret++; + } + return ret; +} + +static size_t extract_char(const char* string, char* symbol) { + if(is_letter(*string)) { + *symbol = *string; + return 1; + } else { + return 0; + } +} + +static size_t extract_sharp(const char* string, char* symbol) { + if(*string == '#' || *string == '_') { + *symbol = '#'; + return 1; + } else { + return 0; + } +} + +static size_t skip_till(const char* string, const char symbol) { + size_t ret = 0; + while(*string != '\0' && *string != symbol) { + string++; + ret++; + } + if(*string != symbol) { + ret = 0; + } + return ret; +} + +static bool music_player_worker_add_note( + MusicPlayerWorker* instance, + uint8_t semitone, + uint8_t duration, + uint8_t dots) { + NoteBlock note_block; + + note_block.semitone = semitone; + note_block.duration = duration; + note_block.dots = dots; + + NoteBlockArray_push_back(instance->notes, note_block); + + return true; +} + +static int8_t note_to_semitone(const char note) { + switch(note) { + case 'C': + return 0; + // C# + case 'D': + return 2; + // D# + case 'E': + return 4; + case 'F': + return 5; + // F# + case 'G': + return 7; + // G# + case 'A': + return 9; + // A# + case 'B': + return 11; + default: + return 0; + } +} + +static bool music_player_worker_parse_notes(MusicPlayerWorker* instance, const char* string) { + const char* cursor = string; + bool result = true; + + while(*cursor != '\0') { + if(!is_space(*cursor)) { + uint32_t duration = 0; + char note_char = '\0'; + char sharp_char = '\0'; + uint32_t octave = 0; + uint32_t dots = 0; + + // Parsing + cursor += extract_number(cursor, &duration); + cursor += extract_char(cursor, ¬e_char); + cursor += extract_sharp(cursor, &sharp_char); + cursor += extract_number(cursor, &octave); + cursor += extract_dots(cursor, &dots); + + // Post processing + note_char = toupper(note_char); + if(!duration) { + duration = instance->duration; + } + if(!octave) { + octave = instance->octave; + } + + // Validation + bool is_valid = true; + is_valid &= (duration >= 1 && duration <= 128); + is_valid &= ((note_char >= 'A' && note_char <= 'G') || note_char == 'P'); + is_valid &= (sharp_char == '#' || sharp_char == '\0'); + is_valid &= (octave <= 16); + is_valid &= (dots <= 16); + if(!is_valid) { + FURI_LOG_E( + TAG, + "Invalid note: %u%c%c%u.%u", + duration, + note_char == '\0' ? '_' : note_char, + sharp_char == '\0' ? '_' : sharp_char, + octave, + dots); + result = false; + break; + } + + // Note to semitones + uint8_t semitone = 0; + if(note_char == 'P') { + semitone = SEMITONE_PAUSE; + } else { + semitone += octave * 12; + semitone += note_to_semitone(note_char); + semitone += sharp_char == '#' ? 1 : 0; + } + + if(music_player_worker_add_note(instance, semitone, duration, dots)) { + FURI_LOG_D( + TAG, + "Added note: %c%c%u.%u = %u %u", + note_char == '\0' ? '_' : note_char, + sharp_char == '\0' ? '_' : sharp_char, + octave, + dots, + semitone, + duration); + } else { + FURI_LOG_E( + TAG, + "Invalid note: %c%c%u.%u = %u %u", + note_char == '\0' ? '_' : note_char, + sharp_char == '\0' ? '_' : sharp_char, + octave, + dots, + semitone, + duration); + } + cursor += skip_till(cursor, ','); + } + + if(*cursor != '\0') cursor++; + } + + return result; +} + +bool music_player_worker_load(MusicPlayerWorker* instance, const char* file_path) { + furi_assert(instance); + furi_assert(file_path); + + bool ret = false; + if(strcasestr(file_path, ".fmf")) { + ret = music_player_worker_load_fmf_from_file(instance, file_path); + } else { + ret = music_player_worker_load_rtttl_from_file(instance, file_path); + } + return ret; +} + +bool music_player_worker_load_fmf_from_file(MusicPlayerWorker* instance, const char* file_path) { + furi_assert(instance); + furi_assert(file_path); + + bool result = false; + string_t temp_str; + string_init(temp_str); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + + do { + if(!flipper_format_file_open_existing(file, file_path)) break; + + uint32_t version = 0; + if(!flipper_format_read_header(file, temp_str, &version)) break; + if(string_cmp_str(temp_str, MUSIC_PLAYER_FILETYPE) || (version != MUSIC_PLAYER_VERSION)) { + FURI_LOG_E(TAG, "Incorrect file format or version"); + break; + } + + if(!flipper_format_read_uint32(file, "BPM", &instance->bpm, 1)) { + FURI_LOG_E(TAG, "BPM is missing"); + break; + } + if(!flipper_format_read_uint32(file, "Duration", &instance->duration, 1)) { + FURI_LOG_E(TAG, "Duration is missing"); + break; + } + if(!flipper_format_read_uint32(file, "Octave", &instance->octave, 1)) { + FURI_LOG_E(TAG, "Octave is missing"); + break; + } + + if(!flipper_format_read_string(file, "Notes", temp_str)) { + FURI_LOG_E(TAG, "Notes is missing"); + break; + } + + if(!music_player_worker_parse_notes(instance, string_get_cstr(temp_str))) { + break; + } + + result = true; + } while(false); + + furi_record_close(RECORD_STORAGE); + flipper_format_free(file); + string_clear(temp_str); + + return result; +} + +bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const char* file_path) { + furi_assert(instance); + furi_assert(file_path); + + bool result = false; + string_t content; + string_init(content); + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + + do { + if(!storage_file_open(file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) { + FURI_LOG_E(TAG, "Unable to open file"); + break; + }; + + uint16_t ret = 0; + do { + uint8_t buffer[65] = {0}; + ret = storage_file_read(file, buffer, sizeof(buffer) - 1); + for(size_t i = 0; i < ret; i++) { + string_push_back(content, buffer[i]); + } + } while(ret > 0); + + string_strim(content); + if(!string_size(content)) { + FURI_LOG_E(TAG, "Empty file"); + break; + } + + if(!music_player_worker_load_rtttl_from_string(instance, string_get_cstr(content))) { + FURI_LOG_E(TAG, "Invalid file content"); + break; + } + + result = true; + } while(0); + + storage_file_free(file); + furi_record_close(RECORD_STORAGE); + string_clear(content); + + return result; +} + +bool music_player_worker_load_rtttl_from_string(MusicPlayerWorker* instance, const char* string) { + furi_assert(instance); + + const char* cursor = string; + + // Skip name + cursor += skip_till(cursor, ':'); + if(*cursor != ':') { + return false; + } + + // Duration + cursor += skip_till(cursor, '='); + if(*cursor != '=') { + return false; + } + cursor++; + cursor += extract_number(cursor, &instance->duration); + + // Octave + cursor += skip_till(cursor, '='); + if(*cursor != '=') { + return false; + } + cursor++; + cursor += extract_number(cursor, &instance->octave); + + // BPM + cursor += skip_till(cursor, '='); + if(*cursor != '=') { + return false; + } + cursor++; + cursor += extract_number(cursor, &instance->bpm); + + // Notes + cursor += skip_till(cursor, ':'); + if(*cursor != ':') { + return false; + } + cursor++; + if(!music_player_worker_parse_notes(instance, cursor)) { + return false; + } + + return true; +} + +void music_player_worker_set_callback( + MusicPlayerWorker* instance, + MusicPlayerWorkerCallback callback, + void* context) { + furi_assert(instance); + instance->callback = callback; + instance->callback_context = context; +} + +void music_player_worker_set_volume(MusicPlayerWorker* instance, float volume) { + furi_assert(instance); + instance->volume = volume; +} + +void music_player_worker_start(MusicPlayerWorker* instance) { + furi_assert(instance); + furi_assert(instance->should_work == false); + + instance->should_work = true; + furi_thread_start(instance->thread); +} + +void music_player_worker_stop(MusicPlayerWorker* instance) { + furi_assert(instance); + furi_assert(instance->should_work == true); + + instance->should_work = false; + furi_thread_join(instance->thread); +} diff --git a/applications/plugins/doom/doom_music_player_worker.h b/applications/plugins/doom/doom_music_player_worker.h new file mode 100644 index 000000000..9958a9273 --- /dev/null +++ b/applications/plugins/doom/doom_music_player_worker.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*MusicPlayerWorkerCallback)( + uint8_t semitone, + uint8_t dots, + uint8_t duration, + float position, + void* context); + +typedef struct MusicPlayerWorker MusicPlayerWorker; + +MusicPlayerWorker* music_player_worker_alloc(); + +void music_player_worker_free(MusicPlayerWorker* instance); + +bool music_player_worker_load(MusicPlayerWorker* instance, const char* file_path); + +bool music_player_worker_load_fmf_from_file(MusicPlayerWorker* instance, const char* file_path); + +bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const char* file_path); + +bool music_player_worker_load_rtttl_from_string(MusicPlayerWorker* instance, const char* string); + +void music_player_worker_set_callback( + MusicPlayerWorker* instance, + MusicPlayerWorkerCallback callback, + void* context); + +void music_player_worker_set_volume(MusicPlayerWorker* instance, float volume); + +void music_player_worker_start(MusicPlayerWorker* instance); + +void music_player_worker_stop(MusicPlayerWorker* instance); + +#ifdef __cplusplus +} +#endif diff --git a/applications/plugins/doom/entities.c b/applications/plugins/doom/entities.c new file mode 100644 index 000000000..86c7f6ae1 --- /dev/null +++ b/applications/plugins/doom/entities.c @@ -0,0 +1,42 @@ +#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; +} \ No newline at end of file diff --git a/applications/plugins/doom/entities.h b/applications/plugins/doom/entities.h new file mode 100644 index 000000000..ef5eb1a78 --- /dev/null +++ b/applications/plugins/doom/entities.h @@ -0,0 +1,56 @@ +#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 diff --git a/applications/plugins/doom/level.h b/applications/plugins/doom/level.h new file mode 100644 index 000000000..4d92a1cc6 --- /dev/null +++ b/applications/plugins/doom/level.h @@ -0,0 +1,188 @@ +#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 diff --git a/applications/plugins/doom/sound.h b/applications/plugins/doom/sound.h new file mode 100644 index 000000000..b33ac93c6 --- /dev/null +++ b/applications/plugins/doom/sound.h @@ -0,0 +1,35 @@ +#ifndef sound_h +#define sound_h +#include +#include +#include +#include +#include "doom_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 \ No newline at end of file diff --git a/applications/plugins/doom/types.c b/applications/plugins/doom/types.c new file mode 100644 index 000000000..6b55d56a7 --- /dev/null +++ b/applications/plugins/doom/types.c @@ -0,0 +1,33 @@ +#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; +} diff --git a/applications/plugins/doom/types.h b/applications/plugins/doom/types.h new file mode 100644 index 000000000..b8579f645 --- /dev/null +++ b/applications/plugins/doom/types.h @@ -0,0 +1,36 @@ +#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 diff --git a/applications/plugins/flappy_bird/application.fam b/applications/plugins/flappy_bird/application.fam new file mode 100644 index 000000000..a7b53163c --- /dev/null +++ b/applications/plugins/flappy_bird/application.fam @@ -0,0 +1,12 @@ +App( + appid="game_flappybird", + name="Flappy Bird", + apptype=FlipperAppType.PLUGIN, + entry_point="flappy_game_app", + cdefines=["APP_FLAPPY_GAME"], + requires=["gui"], + stack_size=4 * 1024, + order=100, + fap_icon="flappy_10px.png", + fap_category="Games", +) diff --git a/applications/plugins/flappy_bird/bird.h b/applications/plugins/flappy_bird/bird.h new file mode 100644 index 000000000..8162080be --- /dev/null +++ b/applications/plugins/flappy_bird/bird.h @@ -0,0 +1,54 @@ +#include + +uint8_t bird_array[3][15][11] = { + { + {0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0}, + {0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0}, + {0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0}, + {0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0}, + {0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, + {0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1}, + {1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1}, + {1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1}, + {1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1}, + {1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0}, + {1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0}, + {0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0}, + {0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0}, + {0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0}, + {0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0}, + }, + { + {0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0}, + {0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0}, + {0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, + {0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0}, + {0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0}, + {0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1}, + {1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1}, + {1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1}, + {1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1}, + {1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0}, + {1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0}, + {0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0}, + {0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0}, + {0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0}, + {0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0}, + }, + { + {0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0}, + {0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0}, + {0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0}, + {0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0}, + {0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0}, + {0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1}, + {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}, + {1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1}, + {1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1}, + {1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0}, + {1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0}, + {0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0}, + {0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0}, + {0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0}, + {0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0}, + }}; diff --git a/applications/plugins/flappy_bird/flappy_10px.png b/applications/plugins/flappy_bird/flappy_10px.png new file mode 100644 index 000000000..636f5fc37 Binary files /dev/null and b/applications/plugins/flappy_bird/flappy_10px.png differ diff --git a/applications/plugins/flappy_bird/flappy_bird.c b/applications/plugins/flappy_bird/flappy_bird.c new file mode 100644 index 000000000..1eb6a717e --- /dev/null +++ b/applications/plugins/flappy_bird/flappy_bird.c @@ -0,0 +1,357 @@ +#include +#include +#include +#include + +#include "bird.h" + +#define TAG "Flappy" +#define DEBUG false + +#define FLAPPY_BIRD_HEIGHT 15 +#define FLAPPY_BIRD_WIDTH 10 + +#define FLAPPY_PILAR_MAX 6 +#define FLAPPY_PILAR_DIST 40 + +#define FLAPPY_GAB_HEIGHT 25 +#define FLAPPY_GAB_WIDTH 5 + +#define FLAPPY_GRAVITY_JUMP -1.1 +#define FLAPPY_GRAVITY_TICK 0.15 + +#define FLIPPER_LCD_WIDTH 128 +#define FLIPPER_LCD_HEIGHT 64 + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + int x; + int y; +} POINT; + +typedef struct { + float gravity; + POINT point; +} BIRD; + +typedef struct { + POINT point; + int height; + int visible; + bool passed; +} PILAR; + +typedef enum { + GameStateLife, + GameStateGameOver, +} State; + +typedef struct { + BIRD bird; + int points; + int pilars_count; + PILAR pilars[FLAPPY_PILAR_MAX]; + bool debug; + State state; +} GameState; + +typedef struct { + EventType type; + InputEvent input; +} GameEvent; + +typedef enum { + DirectionUp, + DirectionRight, + DirectionDown, + DirectionLeft, +} Direction; + +static void flappy_game_random_pilar(GameState* const game_state) { + PILAR pilar; + + pilar.passed = false; + pilar.visible = 1; + pilar.height = random() % (FLIPPER_LCD_HEIGHT - FLAPPY_GAB_HEIGHT) + 1; + pilar.point.y = 0; + pilar.point.x = FLIPPER_LCD_WIDTH + FLAPPY_GAB_WIDTH + 1; + + game_state->pilars_count++; + game_state->pilars[game_state->pilars_count % FLAPPY_PILAR_MAX] = pilar; +} + +static void flappy_game_state_init(GameState* const game_state) { + BIRD bird; + bird.gravity = 0.0f; + bird.point.x = 15; + bird.point.y = 32; + + game_state->debug = DEBUG; + game_state->bird = bird; + game_state->pilars_count = 0; + game_state->points = 0; + game_state->state = GameStateLife; + memset(game_state->pilars, 0, sizeof(game_state->pilars)); + + flappy_game_random_pilar(game_state); +} + +// static void flappy_game_reset(GameState* const game_state) { +// FURI_LOG_I(TAG, "Reset Game State\r\n"); // Resetting State +// } + +static void flappy_game_tick(GameState* const game_state) { + if(game_state->state == GameStateLife) { + if(!game_state->debug) { + game_state->bird.gravity += FLAPPY_GRAVITY_TICK; + game_state->bird.point.y += game_state->bird.gravity; + } + + // Checking the location of the last respawned pilar. + PILAR* pilar = &game_state->pilars[game_state->pilars_count % FLAPPY_PILAR_MAX]; + if(pilar->point.x == (FLIPPER_LCD_WIDTH - FLAPPY_PILAR_DIST)) + flappy_game_random_pilar(game_state); + + // Updating the position/status of the pilars (visiblity, posotion, game points) + // | | | | | + // | | | | | + // |__| | |__| + // _____X | X_____ + // | | | | | // [Pos + Width of pilar] >= [Bird Pos] + // |_____| | |_____| + // X <----> | X <-> + // Bird Pos + Lenght of the bird] >= [Pilar] + for(int i = 0; i < FLAPPY_PILAR_MAX; i++) { + PILAR* pilar = &game_state->pilars[i]; + if(pilar != NULL && pilar->visible && game_state->state == GameStateLife) { + pilar->point.x--; + if(game_state->bird.point.x >= pilar->point.x + FLAPPY_GAB_WIDTH && + pilar->passed == false) { + pilar->passed = true; + game_state->points++; + } + if(pilar->point.x < -FLAPPY_GAB_WIDTH) pilar->visible = 0; + + // Checking out of bounds + if(game_state->bird.point.y < 0 - FLAPPY_BIRD_WIDTH || + game_state->bird.point.y > FLIPPER_LCD_HEIGHT) { + game_state->state = GameStateGameOver; + break; + } + + // Bird inbetween pipes + if((game_state->bird.point.x + FLAPPY_BIRD_HEIGHT >= pilar->point.x) && + (game_state->bird.point.x <= pilar->point.x + FLAPPY_GAB_WIDTH)) { + // Bird below Bottom Pipe + if(game_state->bird.point.y + FLAPPY_BIRD_WIDTH - 2 >= + pilar->height + FLAPPY_GAB_HEIGHT) { + game_state->state = GameStateGameOver; + break; + } + + // Bird above Upper Pipe + if(game_state->bird.point.y < pilar->height) { + game_state->state = GameStateGameOver; + break; + } + } + } + } + } +} + +static void flappy_game_flap(GameState* const game_state) { + game_state->bird.gravity = FLAPPY_GRAVITY_JUMP; +} + +static void flappy_game_render_callback(Canvas* const canvas, void* ctx) { + const GameState* game_state = acquire_mutex((ValueMutex*)ctx, 25); + if(game_state == NULL) { + return; + } + + canvas_draw_frame(canvas, 0, 0, 128, 64); + + if(game_state->state == GameStateLife) { + // Pilars + for(int i = 0; i < FLAPPY_PILAR_MAX; i++) { + const PILAR* pilar = &game_state->pilars[i]; + if(pilar != NULL && pilar->visible == 1) { + canvas_draw_frame( + canvas, pilar->point.x, pilar->point.y, FLAPPY_GAB_WIDTH, pilar->height); + + canvas_draw_frame( + canvas, + pilar->point.x, + pilar->point.y + pilar->height + FLAPPY_GAB_HEIGHT, + FLAPPY_GAB_WIDTH, + FLIPPER_LCD_HEIGHT - pilar->height - FLAPPY_GAB_HEIGHT); + } + } + // Flappy + for(int h = 0; h < FLAPPY_BIRD_HEIGHT; h++) { + for(int w = 0; w < FLAPPY_BIRD_WIDTH; w++) { + // Switch animation + int bird = 0; + if(game_state->bird.gravity < -0.5) + bird = 1; + else + bird = 2; + + // Draw bird pixels + if(bird_array[bird][h][w] == 1) { + int x = game_state->bird.point.x + h; + int y = game_state->bird.point.y + w; + + canvas_draw_dot(canvas, x, y); + } + } + } + + // Stats + + canvas_set_font(canvas, FontSecondary); + char buffer[12]; + snprintf(buffer, sizeof(buffer), "Score: %u", game_state->points); + canvas_draw_str_aligned(canvas, 100, 12, AlignCenter, AlignBottom, buffer); + + if(game_state->debug) { + char coordinates[20]; + snprintf(coordinates, sizeof(coordinates), "Y: %u", game_state->bird.point.y); + canvas_draw_str_aligned(canvas, 1, 12, AlignCenter, AlignBottom, coordinates); + } + } + + if(game_state->state == GameStateGameOver) { + // Screen is 128x64 px + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 34, 20, 62, 24); + + canvas_set_color(canvas, ColorBlack); + canvas_draw_frame(canvas, 34, 20, 62, 24); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 37, 31, "Game Over"); + + /*if(game_state->points != 0 && game_state->points % 5 == 0) { + DOLPHIN_DEED(getRandomDeed()); + }*/ + + canvas_set_font(canvas, FontSecondary); + char buffer[12]; + snprintf(buffer, sizeof(buffer), "Score: %u", game_state->points); + canvas_draw_str_aligned(canvas, 64, 41, AlignCenter, AlignBottom, buffer); + } + + release_mutex((ValueMutex*)ctx, game_state); +} + +static void flappy_game_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + GameEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void flappy_game_update_timer_callback(FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + GameEvent event = {.type = EventTypeTick}; + furi_message_queue_put(event_queue, &event, 0); +} + +int32_t flappy_game_app(void* p) { + UNUSED(p); + int32_t return_code = 0; + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(GameEvent)); + + GameState* game_state = malloc(sizeof(GameState)); + flappy_game_state_init(game_state); + + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, game_state, sizeof(GameState))) { + FURI_LOG_E(TAG, "cannot create mutex\r\n"); + return_code = 255; + goto free_and_exit; + } + + // Set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, flappy_game_render_callback, &state_mutex); + view_port_input_callback_set(view_port, flappy_game_input_callback, event_queue); + + FuriTimer* timer = + furi_timer_alloc(flappy_game_update_timer_callback, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 25); + + // Open GUI and register view_port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + GameEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + GameState* game_state = (GameState*)acquire_mutex_block(&state_mutex); + + if(event_status == FuriStatusOk) { + // press events + if(event.type == EventTypeKey) { + if(event.input.type == InputTypePress) { + switch(event.input.key) { + case InputKeyUp: + game_state->bird.point.y--; + break; + case InputKeyDown: + game_state->bird.point.y++; + break; + case InputKeyRight: + game_state->bird.point.x++; + break; + case InputKeyLeft: + game_state->bird.point.x--; + break; + case InputKeyOk: + if(game_state->state == GameStateGameOver) { + flappy_game_state_init(game_state); + } + + if(game_state->state == GameStateLife) { + flappy_game_flap(game_state); + } + + break; + case InputKeyBack: + processing = false; + break; + } + } + } else if(event.type == EventTypeTick) { + flappy_game_tick(game_state); + } + } else { + //FURI_LOG_D(TAG, "osMessageQueue: event timeout"); + // event timeout + } + + view_port_update(view_port); + release_mutex(&state_mutex, game_state); + } + + furi_timer_free(timer); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + view_port_free(view_port); + delete_mutex(&state_mutex); + +free_and_exit: + free(game_state); + furi_message_queue_free(event_queue); + + return return_code; +} diff --git a/applications/plugins/music_player/application.fam b/applications/plugins/music_player/application.fam index 12c7a2604..f0c319e8d 100644 --- a/applications/plugins/music_player/application.fam +++ b/applications/plugins/music_player/application.fam @@ -10,7 +10,7 @@ App( ], provides=["music_player_start"], stack_size=2 * 1024, - order=20, + order=45, fap_icon="../../../assets/icons/Archive/music_10px.png", fap_category="Games", ) diff --git a/applications/plugins/music_player/music_player_worker.h b/applications/plugins/music_player/music_player_worker.h index 3aa99ea37..9958a9273 100644 --- a/applications/plugins/music_player/music_player_worker.h +++ b/applications/plugins/music_player/music_player_worker.h @@ -3,6 +3,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + typedef void (*MusicPlayerWorkerCallback)( uint8_t semitone, uint8_t dots, @@ -34,3 +38,7 @@ void music_player_worker_set_volume(MusicPlayerWorker* instance, float volume); void music_player_worker_start(MusicPlayerWorker* instance); void music_player_worker_stop(MusicPlayerWorker* instance); + +#ifdef __cplusplus +} +#endif diff --git a/applications/plugins/subbrute/scene/subbrute_scene_entrypoint.c b/applications/plugins/subbrute/scene/subbrute_scene_entrypoint.c index 267c1d82f..6f8497b9f 100644 --- a/applications/plugins/subbrute/scene/subbrute_scene_entrypoint.c +++ b/applications/plugins/subbrute/scene/subbrute_scene_entrypoint.c @@ -16,11 +16,11 @@ void subbrute_scene_entrypoint_menu_callback(SubBruteState* context, uint32_t in case SubBruteAttackCAME12bit307: case SubBruteAttackCAME12bit433: case SubBruteAttackCAME12bit868: - if (index == SubBruteAttackCAME12bit307) { + if(index == SubBruteAttackCAME12bit307) { context->frequency = 307800000; - } else if (index == SubBruteAttackCAME12bit433) { + } else if(index == SubBruteAttackCAME12bit433) { context->frequency = 433920000; - } else if (index == SubBruteAttackCAME12bit868) { + } else if(index == SubBruteAttackCAME12bit868) { context->frequency = 868350000; } context->bit = 12; diff --git a/applications/plugins/zombiez/application.fam b/applications/plugins/zombiez/application.fam new file mode 100644 index 000000000..1d46f8bc0 --- /dev/null +++ b/applications/plugins/zombiez/application.fam @@ -0,0 +1,12 @@ +App( + appid="game_zombiez", + name="Zombiez", + apptype=FlipperAppType.PLUGIN, + entry_point="zombiez_game_app", + cdefines=["APP_ZOMBIEZ_GAME"], + requires=["gui"], + stack_size=2 * 1024, + order=280, + fap_icon="zombie_10px.png", + fap_category="Games", +) diff --git a/applications/plugins/zombiez/zombie_10px.png b/applications/plugins/zombiez/zombie_10px.png new file mode 100644 index 000000000..37363ec04 Binary files /dev/null and b/applications/plugins/zombiez/zombie_10px.png differ diff --git a/applications/plugins/zombiez/zombiez.c b/applications/plugins/zombiez/zombiez.c new file mode 100644 index 000000000..b51bdc76c --- /dev/null +++ b/applications/plugins/zombiez/zombiez.c @@ -0,0 +1,400 @@ +#include +#include +#include +#include + +//ORIGINAL REPO: https://github.com/Dooskington/flipperzero-zombiez +//AUTHORS: https://github.com/Dooskington | https://github.com/DevMilanIan + +#include "zombiez.h" + +#define ZOMBIES_MAX 3 +#define ZOMBIES_WIDTH 5 +#define ZOMBIES_HEIGHT 8 +#define PROJECTILES_MAX 10 + +#define MIN_Y 5 +#define MAX_Y 58 +#define WALL_X 16 +#define PLAYER_START_X 8 +#define PLAYER_START_Y (MAX_Y - MIN_Y) / 2 + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +typedef enum { GameStatePlaying, GameStateGameOver } GameState; + +typedef struct { + int x; + int y; +} Point; + +typedef struct { + Point position; + int hp; +} Player; + +typedef struct { + Point position; + int hp; +} Zombie; + +typedef struct { + Point position; +} Projectile; + +typedef struct { + GameState game_state; + Player player; + + size_t zombies_count; + Zombie* zombies[ZOMBIES_MAX]; + + size_t projectiles_count; + Projectile* projectiles[PROJECTILES_MAX]; + + uint16_t score; + bool input_shoot; +} PluginState; + +static void render_callback(Canvas* const canvas, void* ctx) { + const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); + if(plugin_state == NULL) { + return; + } + + canvas_draw_frame(canvas, 0, 0, 128, 64); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned( + canvas, + plugin_state->player.position.x, + plugin_state->player.position.y, + AlignCenter, + AlignCenter, + "@"); + + canvas_draw_line(canvas, WALL_X, 0, WALL_X, 64); + canvas_draw_line(canvas, WALL_X + 2, 4, WALL_X + 2, 59); + + for(int i = 0; i < PROJECTILES_MAX; ++i) { + Projectile* p = plugin_state->projectiles[i]; + if(p != NULL) { + canvas_draw_circle(canvas, p->position.x, p->position.y, 3); + } + } + + for(int i = 0; i < ZOMBIES_MAX; ++i) { + Zombie* z = plugin_state->zombies[i]; + if(z != NULL) { + for(int h = 0; h < ZOMBIES_HEIGHT; h++) { + for(int w = 0; w < ZOMBIES_WIDTH; w++) { + // Switch animation + int zIdx = 0; + if(z->position.x % 2 == 0) { + zIdx = 1; + } + + // Draw zombie pixels + if(zombie_array[zIdx][h][w] == 1) { + int x = z->position.x + w; + int y = z->position.y + h; + + canvas_draw_dot(canvas, x, y); + } + } + } + } + } + + int heart; + if((plugin_state->player.hp - 10) > 5) { // 16, 17, 18, 19, 20 + heart = 0; + } else if((plugin_state->player.hp - 5) > 5) { // 11, 12, 13, 14, 15 + heart = 1; + } else if((plugin_state->player.hp - 3) > 2) { // 6, 7, 8, 9, 10 + heart = 2; + } else if(plugin_state->player.hp > 0) { // 1, 2, 3, 4, 5 + heart = 3; + } else { // 0 + heart = 4; + } + // visual representation of health + for(int h = 0; h < 5; h++) { + for(int w = 0; w < 5; w++) { + if(heart_array[heart][h][w] == 1) { + int x = 124 - w; + int y = 56 + h; + + canvas_draw_dot(canvas, x, y); + } + } + } + + // buffer hp + score + char hpBuffer[8]; + char scoreBuffer[14]; + + if(plugin_state->game_state == GameStatePlaying) { + // display ammo / reload + if(plugin_state->projectiles_count >= PROJECTILES_MAX) { + canvas_draw_str_aligned(canvas, 24, 10, AlignLeft, AlignCenter, "RELOAD"); + } else { + for(uint8_t i = 0; i < (PROJECTILES_MAX - plugin_state->projectiles_count); i++) { + canvas_draw_box(canvas, 24 + (4 * i), 6, 2, 4); + } + } + // display hp + score + snprintf(hpBuffer, sizeof(hpBuffer), "%u", plugin_state->player.hp); + canvas_draw_str_aligned(canvas, 118, 62, AlignRight, AlignBottom, hpBuffer); + + snprintf(scoreBuffer, sizeof(scoreBuffer), "%u", plugin_state->score); + canvas_draw_str_aligned(canvas, 126, 10, AlignRight, AlignBottom, scoreBuffer); + } + // Game Over banner + if(plugin_state->game_state == GameStateGameOver) { + // Screen is 128x64 px + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 34, 20, 62, 24); + + canvas_set_color(canvas, ColorBlack); + canvas_draw_frame(canvas, 34, 20, 62, 24); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 37, 31, "Game Over"); + + canvas_set_font(canvas, FontSecondary); + snprintf(scoreBuffer, sizeof(scoreBuffer), "Score: %u", plugin_state->score); + canvas_draw_str_aligned(canvas, 64, 41, AlignCenter, AlignBottom, scoreBuffer); + } + + //char* info = (char*)malloc(16 * sizeof(char)); + //asprintf(&info, "%d, %d", plugin_state->x, plugin_state->y); + //canvas_draw_str_aligned(canvas, 32, 16, AlignLeft, AlignBottom, info); + //free(info); + + 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, FuriWaitForever); +} + +static void tick(PluginState* const plugin_state) { + if(plugin_state->input_shoot && (plugin_state->projectiles_count < PROJECTILES_MAX)) { + Projectile* p = (Projectile*)malloc(sizeof(Projectile)); + p->position.x = plugin_state->player.position.x; + p->position.y = plugin_state->player.position.y; + + size_t idx = plugin_state->projectiles_count; + plugin_state->projectiles[idx] = p; + plugin_state->projectiles_count += 1; + } + + for(int i = 0; i < ZOMBIES_MAX; ++i) { + if(!plugin_state->zombies[i]) { + Zombie* z = (Zombie*)malloc(sizeof(Zombie)); + //z->hp = 20; + z->position.x = 126; + z->position.y = MIN_Y + (rand() % (MAX_Y - MIN_Y)); + + plugin_state->zombies[i] = z; + plugin_state->zombies_count += 1; + } + } + + for(int i = 0; i < PROJECTILES_MAX; ++i) { + Projectile* p = plugin_state->projectiles[i]; + if(p != NULL) { + p->position.x += 2; + + for(int i = 0; i < ZOMBIES_MAX; ++i) { + Zombie* z = plugin_state->zombies[i]; + if(z != NULL) { + if( // projectile close enough to zombie + (((z->position.x - p->position.x) <= 2) && + ((z->position.y - p->position.y) <= 4)) && + (((p->position.x - z->position.x) <= 2) && + ((p->position.y - z->position.y) <= 6))) { + //z->hp -= 5; + //if(z->hp <= 0) { + plugin_state->zombies_count -= 1; + free(z); + plugin_state->zombies[i] = NULL; + plugin_state->score++; + //if(plugin_state->score % 15 == 0) DOLPHIN_DEED(getRandomDeed()); + //} + } else if(z->position.x <= WALL_X && z->position.x > 0) { // zombie got to the wall + plugin_state->zombies_count -= 1; + free(z); + plugin_state->zombies[i] = NULL; + if(plugin_state->player.hp > 0) { + plugin_state->player.hp--; + } else { + plugin_state->game_state = GameStateGameOver; + } + } else { + if(furi_get_tick() % 2 == 0) z->position.x--; + } + } + } + + if(p->position.x >= 128) { + free(p); + plugin_state->projectiles[i] = NULL; + } + } + } + + plugin_state->input_shoot = false; +} + +static void timer_callback(void* ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + PluginEvent event = {.type = EventTypeTick}; + furi_message_queue_put(event_queue, &event, 0); +} + +static void zombiez_state_init(PluginState* const plugin_state) { + plugin_state->player.position.x = PLAYER_START_X; + plugin_state->player.position.y = PLAYER_START_Y; + plugin_state->player.hp = 20; + + plugin_state->projectiles_count = 0; + plugin_state->zombies_count = 0; + plugin_state->score = 0; + + for(int i = 0; i < PROJECTILES_MAX; i++) { + plugin_state->projectiles[i] = NULL; + } + + for(int i = 0; i < ZOMBIES_MAX; i++) { + plugin_state->zombies[i] = NULL; + } + + plugin_state->game_state = GameStatePlaying; + plugin_state->input_shoot = false; +} + +int32_t zombiez_game_app(void* p) { + UNUSED(p); + uint32_t return_code = 0; + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + + PluginState* plugin_state = malloc(sizeof(PluginState)); + zombiez_state_init(plugin_state); + + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { + FURI_LOG_E("Zombiez", "cannot create mutex\r\n"); + return_code = 255; + goto free_and_exit; + } + + // 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); + + FuriTimer* timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 22); + + // Open GUI and register view_port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + PluginEvent event; + bool isRunning = true; + while(isRunning) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); + PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); + if(event_status == FuriStatusOk) { + if(event.type == EventTypeKey) { + if(event.input.type == InputTypePress) { + switch(event.input.key) { + case InputKeyUp: + if(plugin_state->player.position.y > MIN_Y && + plugin_state->game_state == GameStatePlaying) { + plugin_state->player.position.y--; + } + break; + case InputKeyDown: + if(plugin_state->player.position.y < MAX_Y && + plugin_state->game_state == GameStatePlaying) { + plugin_state->player.position.y++; + } + break; + case InputKeyOk: + if(plugin_state->projectiles_count < PROJECTILES_MAX && + plugin_state->game_state == GameStatePlaying) { + plugin_state->input_shoot = true; + } + break; + case InputKeyBack: + break; + default: + break; + } + } else if( + event.input.type == InputTypeRepeat && + plugin_state->game_state == GameStatePlaying) { + switch(event.input.key) { + case InputKeyUp: + if(plugin_state->player.position.y > (MIN_Y + 1)) { + plugin_state->player.position.y -= 2; + } + break; + case InputKeyDown: + if(plugin_state->player.position.y < (MAX_Y - 1)) { + plugin_state->player.position.y += 2; + } + break; + default: + break; + } + } else if(event.input.type == InputTypeLong) { + if(event.input.key == InputKeyOk) { + if(plugin_state->game_state == GameStateGameOver) { + zombiez_state_init(plugin_state); + } else if(plugin_state->projectiles_count >= PROJECTILES_MAX) { + plugin_state->projectiles_count = 0; + plugin_state->player.hp++; + } + } else if(event.input.key == InputKeyBack) { + isRunning = false; + } + } + } else if(event.type == EventTypeTick) { + tick(plugin_state); + } + } else { + // event timeout + } + + view_port_update(view_port); + release_mutex(&state_mutex, plugin_state); + } + + furi_timer_free(timer); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + view_port_free(view_port); + delete_mutex(&state_mutex); + +free_and_exit: + free(plugin_state); + furi_message_queue_free(event_queue); + + return return_code; +} \ No newline at end of file diff --git a/applications/plugins/zombiez/zombiez.h b/applications/plugins/zombiez/zombiez.h new file mode 100644 index 000000000..eea71d707 --- /dev/null +++ b/applications/plugins/zombiez/zombiez.h @@ -0,0 +1,62 @@ +#include + +uint8_t zombie_array[2][8][5] = { + { + {0, 0, 1, 1, 1}, + {0, 0, 1, 1, 1}, + {1, 1, 1, 1, 1}, + {0, 0, 1, 1, 1}, + {0, 0, 1, 1, 1}, + {0, 0, 1, 1, 1}, + {0, 0, 1, 0, 0}, + {0, 0, 1, 0, 0}, + }, + { + {0, 0, 1, 1, 1}, + {0, 0, 1, 1, 1}, + {1, 1, 1, 1, 1}, + {0, 0, 1, 1, 1}, + {0, 0, 1, 1, 1}, + {0, 0, 1, 1, 1}, + {0, 0, 0, 0, 1}, + {0, 0, 0, 0, 1}, + }, +}; + +uint8_t heart_array[5][5][5] = { + { + {0, 1, 0, 1, 0}, + {1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1}, + {0, 1, 1, 1, 0}, + {0, 0, 1, 0, 0}, + }, + { + {0, 0, 0, 0, 0}, + {1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1}, + {0, 1, 1, 1, 0}, + {0, 0, 1, 0, 0}, + }, + { + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {1, 1, 1, 1, 1}, + {0, 1, 1, 1, 0}, + {0, 0, 1, 0, 0}, + }, + { + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 1, 1, 1, 0}, + {0, 0, 1, 0, 0}, + }, + { + {1, 0, 0, 0, 1}, + {0, 1, 0, 1, 0}, + {0, 0, 1, 0, 0}, + {0, 1, 0, 1, 0}, + {1, 0, 0, 0, 1}, + }, +}; \ No newline at end of file diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index 562f2e183..419b8b567 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -367,6 +367,23 @@ void canvas_draw_glyph(Canvas* canvas, uint8_t x, uint8_t y, uint16_t ch) { u8g2_DrawGlyph(&canvas->fb, x, y, ch); } +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); +} + void canvas_set_bitmap_mode(Canvas* canvas, bool alpha) { u8g2_SetBitmapMode(&canvas->fb, alpha ? 1 : 0); } diff --git a/applications/services/gui/canvas.h b/applications/services/gui/canvas.h index edde10a01..ccb8dc523 100644 --- a/applications/services/gui/canvas.h +++ b/applications/services/gui/canvas.h @@ -358,6 +358,14 @@ void canvas_draw_rbox( uint8_t height, uint8_t radius); +void canvas_draw_icon_bitmap( + Canvas* canvas, + uint8_t x, + uint8_t y, + int16_t w, + int16_t h, + const Icon* icon); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index eb72be9bb..a3f1eaecb 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,2.3,, +Version,+,3.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -540,6 +540,7 @@ Function,+,canvas_draw_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,canvas_draw_glyph,void,"Canvas*, uint8_t, uint8_t, uint16_t" Function,+,canvas_draw_icon,void,"Canvas*, uint8_t, uint8_t, const Icon*" Function,+,canvas_draw_icon_animation,void,"Canvas*, uint8_t, uint8_t, IconAnimation*" +Function,+,canvas_draw_icon_bitmap,void,"Canvas*, uint8_t, uint8_t, int16_t, int16_t, const Icon*" Function,+,canvas_draw_line,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,canvas_draw_rbox,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,canvas_draw_rframe,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" @@ -1479,7 +1480,7 @@ Function,-,isupper,int,int Function,-,isupper_l,int,"int, locale_t" Function,-,isxdigit,int,int Function,-,isxdigit_l,int,"int, locale_t" -Function,-,itoa,char*,"int, char*, int" +Function,+,itoa,char*,"int, char*, int" Function,-,j0,double,double Function,-,j0f,float,float Function,-,j1,double,double diff --git a/site_scons/fbt/appmanifest.py b/site_scons/fbt/appmanifest.py index c0770f7f3..a41717d6a 100644 --- a/site_scons/fbt/appmanifest.py +++ b/site_scons/fbt/appmanifest.py @@ -19,7 +19,6 @@ class FlipperAppType(Enum): STARTUP = "StartupHook" EXTERNAL = "External" METAPACKAGE = "Package" - GAME = "Game" @dataclass