1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-12 12:42:30 +04:00

add sec+ 1.0/2.0 433mhz in add manually, and run fbt fmt

This commit is contained in:
MX
2022-10-14 01:49:32 +03:00
parent dad4772bec
commit cb0d9ec591
4 changed files with 598 additions and 443 deletions

View File

@@ -24,9 +24,11 @@ typedef enum {
SubmenuIndexLinear_300_00, SubmenuIndexLinear_300_00,
SubmenuIndexLiftMaster_315_00, SubmenuIndexLiftMaster_315_00,
SubmenuIndexLiftMaster_390_00, SubmenuIndexLiftMaster_390_00,
SubmenuIndexLiftMaster_433_00,
SubmenuIndexSecPlus_v2_310_00, SubmenuIndexSecPlus_v2_310_00,
SubmenuIndexSecPlus_v2_315_00, SubmenuIndexSecPlus_v2_315_00,
SubmenuIndexSecPlus_v2_390_00, SubmenuIndexSecPlus_v2_390_00,
SubmenuIndexSecPlus_v2_433_00,
//SubGhzCustomEvent //SubGhzCustomEvent
SubGhzCustomEventSceneDeleteSuccess = 100, SubGhzCustomEventSceneDeleteSuccess = 100,

View File

@@ -167,6 +167,12 @@ void subghz_scene_set_type_on_enter(void* context) {
SubmenuIndexLiftMaster_390_00, SubmenuIndexLiftMaster_390_00,
subghz_scene_set_type_submenu_callback, subghz_scene_set_type_submenu_callback,
subghz); subghz);
submenu_add_item(
subghz->submenu,
"Security+1.0 433MHz",
SubmenuIndexLiftMaster_433_00,
subghz_scene_set_type_submenu_callback,
subghz);
submenu_add_item( submenu_add_item(
subghz->submenu, subghz->submenu,
"Security+2.0 310MHz", "Security+2.0 310MHz",
@@ -185,6 +191,12 @@ void subghz_scene_set_type_on_enter(void* context) {
SubmenuIndexSecPlus_v2_390_00, SubmenuIndexSecPlus_v2_390_00,
subghz_scene_set_type_submenu_callback, subghz_scene_set_type_submenu_callback,
subghz); subghz);
submenu_add_item(
subghz->submenu,
"Security+2.0 433MHz",
SubmenuIndexSecPlus_v2_433_00,
subghz_scene_set_type_submenu_callback,
subghz);
submenu_set_selected_item( submenu_set_selected_item(
subghz->submenu, scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSetType)); subghz->submenu, scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneSetType));
@@ -359,6 +371,20 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
generated_protocol = true; generated_protocol = true;
} }
break; break;
case SubmenuIndexLiftMaster_433_00:
while(!subghz_protocol_secplus_v1_check_fixed(key)) {
key = subghz_random_serial();
}
if(subghz_scene_set_type_submenu_gen_data_protocol(
subghz,
SUBGHZ_PROTOCOL_SECPLUS_V1_NAME,
(uint64_t)key << 32 | 0xE6000000,
42,
433920000,
"AM650")) {
generated_protocol = true;
}
break;
case SubmenuIndexSecPlus_v2_310_00: case SubmenuIndexSecPlus_v2_310_00:
subghz->txrx->transmitter = subghz_transmitter_alloc_init( subghz->txrx->transmitter = subghz_transmitter_alloc_init(
subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME); subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME);
@@ -413,6 +439,24 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
} }
subghz_transmitter_free(subghz->txrx->transmitter); subghz_transmitter_free(subghz->txrx->transmitter);
break; break;
case SubmenuIndexSecPlus_v2_433_00:
subghz->txrx->transmitter = subghz_transmitter_alloc_init(
subghz->txrx->environment, SUBGHZ_PROTOCOL_SECPLUS_V2_NAME);
subghz_preset_init(subghz, "AM650", 433920000, NULL, 0);
if(subghz->txrx->transmitter) {
subghz_protocol_secplus_v2_create_data(
subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter),
subghz->txrx->fff_data,
key,
0x68,
0xE500000,
subghz->txrx->preset);
generated_protocol = true;
} else {
generated_protocol = false;
}
subghz_transmitter_free(subghz->txrx->transmitter);
break;
default: default:
return false; return false;
break; break;

View File

@@ -1,48 +1,144 @@
#define tile_0_width 8 #define tile_0_width 8
#define tile_0_height 8 #define tile_0_height 8
static uint8_t tile_0_bits[] = { static uint8_t tile_0_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; 0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
};
#define tile_1_width 8 #define tile_1_width 8
#define tile_1_height 8 #define tile_1_height 8
static uint8_t tile_1_bits[] = { static uint8_t tile_1_bits[] = {
0x00, 0x10, 0x18, 0x10, 0x10, 0x10, 0x10, 0x00, }; 0x00,
0x10,
0x18,
0x10,
0x10,
0x10,
0x10,
0x00,
};
#define tile_2_width 8 #define tile_2_width 8
#define tile_2_height 8 #define tile_2_height 8
static uint8_t tile_2_bits[] = { static uint8_t tile_2_bits[] = {
0x00, 0x1C, 0x20, 0x20, 0x18, 0x04, 0x3C, 0x00, }; 0x00,
0x1C,
0x20,
0x20,
0x18,
0x04,
0x3C,
0x00,
};
#define tile_3_width 8 #define tile_3_width 8
#define tile_3_height 8 #define tile_3_height 8
static uint8_t tile_3_bits[] = { static uint8_t tile_3_bits[] = {
0x00, 0x1C, 0x20, 0x20, 0x18, 0x20, 0x1C, 0x00, }; 0x00,
0x1C,
0x20,
0x20,
0x18,
0x20,
0x1C,
0x00,
};
#define tile_4_width 8 #define tile_4_width 8
#define tile_4_height 8 #define tile_4_height 8
static uint8_t tile_4_bits[] = { static uint8_t tile_4_bits[] = {
0x00, 0x04, 0x14, 0x14, 0x3C, 0x10, 0x10, 0x00, }; 0x00,
0x04,
0x14,
0x14,
0x3C,
0x10,
0x10,
0x00,
};
#define tile_5_width 8 #define tile_5_width 8
#define tile_5_height 8 #define tile_5_height 8
static uint8_t tile_5_bits[] = { static uint8_t tile_5_bits[] = {
0x00, 0x3C, 0x04, 0x1C, 0x20, 0x20, 0x1C, 0x00, }; 0x00,
0x3C,
0x04,
0x1C,
0x20,
0x20,
0x1C,
0x00,
};
#define tile_6_width 8 #define tile_6_width 8
#define tile_6_height 8 #define tile_6_height 8
static uint8_t tile_6_bits[] = { static uint8_t tile_6_bits[] = {
0x00, 0x18, 0x24, 0x04, 0x1C, 0x24, 0x18, 0x00, }; 0x00,
0x18,
0x24,
0x04,
0x1C,
0x24,
0x18,
0x00,
};
#define tile_7_width 8 #define tile_7_width 8
#define tile_7_height 8 #define tile_7_height 8
static uint8_t tile_7_bits[] = { static uint8_t tile_7_bits[] = {
0x00, 0x3C, 0x20, 0x20, 0x10, 0x08, 0x08, 0x00, }; 0x00,
0x3C,
0x20,
0x20,
0x10,
0x08,
0x08,
0x00,
};
#define tile_8_width 8 #define tile_8_width 8
#define tile_8_height 8 #define tile_8_height 8
static uint8_t tile_8_bits[] = { static uint8_t tile_8_bits[] = {
0x00, 0x18, 0x24, 0x18, 0x24, 0x24, 0x18, 0x00, }; 0x00,
0x18,
0x24,
0x18,
0x24,
0x24,
0x18,
0x00,
};
#define tile_flag_width 8 #define tile_flag_width 8
#define tile_flag_height 8 #define tile_flag_height 8
static uint8_t tile_flag_bits[] = { static uint8_t tile_flag_bits[] = {
0xFF, 0x81, 0xB9, 0x89, 0x89, 0x9D, 0x81, 0xFF, }; 0xFF,
0x81,
0xB9,
0x89,
0x89,
0x9D,
0x81,
0xFF,
};
#define tile_mine_width 8 #define tile_mine_width 8
#define tile_mine_height 8 #define tile_mine_height 8
static uint8_t tile_mine_bits[] = { static uint8_t tile_mine_bits[] = {
0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, }; 0x55,
0xAA,
0x55,
0xAA,
0x55,
0xAA,
0x55,
0xAA,
};
#define tile_uncleared_width 8 #define tile_uncleared_width 8
#define tile_uncleared_height 8 #define tile_uncleared_height 8
static uint8_t tile_uncleared_bits[] = { static uint8_t tile_uncleared_bits[] = {
0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF, }; 0xFF,
0x81,
0x81,
0x81,
0x81,
0x81,
0x81,
0xFF,
};

View File

@@ -28,18 +28,18 @@ typedef struct {
} PluginEvent; } PluginEvent;
typedef enum { typedef enum {
TileType0, // this HAS to be in order, for hint assignment to be ez pz TileType0, // this HAS to be in order, for hint assignment to be ez pz
TileType1, TileType1,
TileType2, TileType2,
TileType3, TileType3,
TileType4, TileType4,
TileType5, TileType5,
TileType6, TileType6,
TileType7, TileType7,
TileType8, TileType8,
TileTypeUncleared, TileTypeUncleared,
TileTypeFlag, TileTypeFlag,
TileTypeMine TileTypeMine
} TileType; } TileType;
typedef enum { typedef enum {
@@ -48,23 +48,23 @@ typedef enum {
} Field; } Field;
typedef struct { typedef struct {
Field minefield[PLAYFIELD_WIDTH][PLAYFIELD_HEIGHT]; Field minefield[PLAYFIELD_WIDTH][PLAYFIELD_HEIGHT];
TileType playfield[PLAYFIELD_WIDTH][PLAYFIELD_HEIGHT]; TileType playfield[PLAYFIELD_WIDTH][PLAYFIELD_HEIGHT];
FuriTimer* timer; FuriTimer* timer;
int cursor_x; int cursor_x;
int cursor_y; int cursor_y;
int mines_left; int mines_left;
int fields_cleared; int fields_cleared;
int flags_set; int flags_set;
bool game_started; bool game_started;
uint32_t game_started_tick; uint32_t game_started_tick;
} Minesweeper; } Minesweeper;
static void timer_callback(void* ctx) { static void timer_callback(void* ctx) {
UNUSED(ctx); UNUSED(ctx);
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
notification_message(notification, &sequence_reset_vibro); notification_message(notification, &sequence_reset_vibro);
furi_record_close(RECORD_NOTIFICATION); furi_record_close(RECORD_NOTIFICATION);
} }
static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) {
@@ -76,8 +76,8 @@ static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queu
static void render_callback(Canvas* const canvas, void* ctx) { static void render_callback(Canvas* const canvas, void* ctx) {
const Minesweeper* minesweeper_state = acquire_mutex((ValueMutex*)ctx, 25); const Minesweeper* minesweeper_state = acquire_mutex((ValueMutex*)ctx, 25);
if (minesweeper_state == NULL) { if(minesweeper_state == NULL) {
return; return;
} }
FuriString* mineStr; FuriString* mineStr;
FuriString* timeStr; FuriString* timeStr;
@@ -90,134 +90,134 @@ static void render_callback(Canvas* const canvas, void* ctx) {
int seconds = 0; int seconds = 0;
int minutes = 0; int minutes = 0;
if (minesweeper_state->game_started) { if(minesweeper_state->game_started) {
uint32_t ticks_elapsed = furi_get_tick() - minesweeper_state->game_started_tick; uint32_t ticks_elapsed = furi_get_tick() - minesweeper_state->game_started_tick;
seconds = (int) ticks_elapsed / furi_kernel_get_tick_frequency(); seconds = (int)ticks_elapsed / furi_kernel_get_tick_frequency();
minutes = (int) seconds / 60; minutes = (int)seconds / 60;
seconds = seconds % 60; seconds = seconds % 60;
} }
furi_string_printf(timeStr, "%01d:%02d", minutes, seconds); furi_string_printf(timeStr, "%01d:%02d", minutes, seconds);
canvas_draw_str_aligned(canvas, 128, 0, AlignRight, AlignTop, furi_string_get_cstr(timeStr)); canvas_draw_str_aligned(canvas, 128, 0, AlignRight, AlignTop, furi_string_get_cstr(timeStr));
for (int y = 0; y < PLAYFIELD_HEIGHT; y++) { for(int y = 0; y < PLAYFIELD_HEIGHT; y++) {
for (int x = 0; x < PLAYFIELD_WIDTH; x++) { for(int x = 0; x < PLAYFIELD_WIDTH; x++) {
if ( x == minesweeper_state->cursor_x && y == minesweeper_state->cursor_y) { if(x == minesweeper_state->cursor_x && y == minesweeper_state->cursor_y) {
canvas_invert_color(canvas); canvas_invert_color(canvas);
}
switch(minesweeper_state->playfield[x][y]) {
case TileType0:
canvas_draw_xbm(
canvas,
x * TILE_HEIGHT, // x
8 + (y * TILE_WIDTH), // y
TILE_WIDTH,
TILE_HEIGHT,
tile_0_bits);
break;
case TileType1:
canvas_draw_xbm(
canvas,
x * TILE_HEIGHT, // x
8 + (y * TILE_WIDTH), // y
TILE_WIDTH,
TILE_HEIGHT,
tile_1_bits);
break;
case TileType2:
canvas_draw_xbm(
canvas,
x * TILE_HEIGHT, // x
8 + (y * TILE_WIDTH), // y
TILE_WIDTH,
TILE_HEIGHT,
tile_2_bits);
break;
case TileType3:
canvas_draw_xbm(
canvas,
x * TILE_HEIGHT, // x
8 + (y * TILE_WIDTH), // y
TILE_WIDTH,
TILE_HEIGHT,
tile_3_bits);
break;
case TileType4:
canvas_draw_xbm(
canvas,
x * TILE_HEIGHT, // x
8 + (y * TILE_WIDTH), // y
TILE_WIDTH,
TILE_HEIGHT,
tile_4_bits);
break;
case TileType5:
canvas_draw_xbm(
canvas,
x * TILE_HEIGHT, // x
8 + (y * TILE_WIDTH), // y
TILE_WIDTH,
TILE_HEIGHT,
tile_5_bits);
break;
case TileType6:
canvas_draw_xbm(
canvas,
x * TILE_HEIGHT, // x
8 + (y * TILE_WIDTH), // y
TILE_WIDTH,
TILE_HEIGHT,
tile_6_bits);
break;
case TileType7:
canvas_draw_xbm(
canvas,
x * TILE_HEIGHT, // x
8 + (y * TILE_WIDTH), // y
TILE_WIDTH,
TILE_HEIGHT,
tile_7_bits);
break;
case TileType8:
canvas_draw_xbm(
canvas,
x * TILE_HEIGHT, // x
8 + (y * TILE_WIDTH), // y
TILE_WIDTH,
TILE_HEIGHT,
tile_8_bits);
break;
case TileTypeFlag:
canvas_draw_xbm(
canvas,
x * TILE_HEIGHT, // x
8 + (y * TILE_WIDTH), // y
TILE_WIDTH,
TILE_HEIGHT,
tile_flag_bits);
break;
case TileTypeUncleared:
canvas_draw_xbm(
canvas,
x * TILE_HEIGHT, // x
8 + (y * TILE_WIDTH), // y
TILE_WIDTH,
TILE_HEIGHT,
tile_uncleared_bits);
break;
case TileTypeMine:
canvas_draw_xbm(
canvas,
x * TILE_HEIGHT, // x
8 + (y * TILE_WIDTH), // y
TILE_WIDTH,
TILE_HEIGHT,
tile_mine_bits);
break;
}
if(x == minesweeper_state->cursor_x && y == minesweeper_state->cursor_y) {
canvas_invert_color(canvas);
}
} }
switch (minesweeper_state->playfield[x][y]) {
case TileType0:
canvas_draw_xbm(
canvas,
x*TILE_HEIGHT, // x
8 + (y * TILE_WIDTH), // y
TILE_WIDTH,
TILE_HEIGHT,
tile_0_bits);
break;
case TileType1:
canvas_draw_xbm(
canvas,
x*TILE_HEIGHT, // x
8 + (y * TILE_WIDTH), // y
TILE_WIDTH,
TILE_HEIGHT,
tile_1_bits);
break;
case TileType2:
canvas_draw_xbm(
canvas,
x*TILE_HEIGHT, // x
8 + (y * TILE_WIDTH), // y
TILE_WIDTH,
TILE_HEIGHT,
tile_2_bits);
break;
case TileType3:
canvas_draw_xbm(
canvas,
x*TILE_HEIGHT, // x
8 + (y * TILE_WIDTH), // y
TILE_WIDTH,
TILE_HEIGHT,
tile_3_bits);
break;
case TileType4:
canvas_draw_xbm(
canvas,
x*TILE_HEIGHT, // x
8 + (y * TILE_WIDTH), // y
TILE_WIDTH,
TILE_HEIGHT,
tile_4_bits);
break;
case TileType5:
canvas_draw_xbm(
canvas,
x*TILE_HEIGHT, // x
8 + (y * TILE_WIDTH), // y
TILE_WIDTH,
TILE_HEIGHT,
tile_5_bits);
break;
case TileType6:
canvas_draw_xbm(
canvas,
x*TILE_HEIGHT, // x
8 + (y * TILE_WIDTH), // y
TILE_WIDTH,
TILE_HEIGHT,
tile_6_bits);
break;
case TileType7:
canvas_draw_xbm(
canvas,
x*TILE_HEIGHT, // x
8 + (y * TILE_WIDTH), // y
TILE_WIDTH,
TILE_HEIGHT,
tile_7_bits);
break;
case TileType8:
canvas_draw_xbm(
canvas,
x*TILE_HEIGHT, // x
8 + (y * TILE_WIDTH), // y
TILE_WIDTH,
TILE_HEIGHT,
tile_8_bits);
break;
case TileTypeFlag:
canvas_draw_xbm(
canvas,
x*TILE_HEIGHT, // x
8 + (y * TILE_WIDTH), // y
TILE_WIDTH,
TILE_HEIGHT,
tile_flag_bits);
break;
case TileTypeUncleared:
canvas_draw_xbm(
canvas,
x*TILE_HEIGHT, // x
8 + (y * TILE_WIDTH), // y
TILE_WIDTH,
TILE_HEIGHT,
tile_uncleared_bits);
break;
case TileTypeMine:
canvas_draw_xbm(
canvas,
x*TILE_HEIGHT, // x
8 + (y * TILE_WIDTH), // y
TILE_WIDTH,
TILE_HEIGHT,
tile_mine_bits);
break;
}
if ( x == minesweeper_state->cursor_x && y == minesweeper_state->cursor_y) {
canvas_invert_color(canvas);
}
}
} }
furi_string_free(mineStr); furi_string_free(mineStr);
@@ -226,336 +226,349 @@ static void render_callback(Canvas* const canvas, void* ctx) {
} }
static void setup_playfield(Minesweeper* minesweeper_state) { static void setup_playfield(Minesweeper* minesweeper_state) {
int mines_left = MINECOUNT; int mines_left = MINECOUNT;
for (int y = 0; y < PLAYFIELD_HEIGHT; y++) { for(int y = 0; y < PLAYFIELD_HEIGHT; y++) {
for (int x = 0; x < PLAYFIELD_WIDTH; x++){ for(int x = 0; x < PLAYFIELD_WIDTH; x++) {
minesweeper_state->minefield[x][y] = FieldEmpty; minesweeper_state->minefield[x][y] = FieldEmpty;
minesweeper_state->playfield[x][y] = TileTypeUncleared; minesweeper_state->playfield[x][y] = TileTypeUncleared;
}
} }
} while(mines_left > 0) {
while(mines_left > 0) { int rand_x = rand() % PLAYFIELD_WIDTH;
int rand_x = rand() % PLAYFIELD_WIDTH; int rand_y = rand() % PLAYFIELD_HEIGHT;
int rand_y = rand() % PLAYFIELD_HEIGHT; // make sure first guess isn't a mine
// make sure first guess isn't a mine if(minesweeper_state->minefield[rand_x][rand_y] == FieldEmpty &&
if (minesweeper_state->minefield[rand_x][rand_y] == FieldEmpty && (minesweeper_state->cursor_x != rand_x && minesweeper_state->cursor_y != rand_y)) {
(minesweeper_state->cursor_x != rand_x && minesweeper_state->cursor_y != rand_y )) { minesweeper_state->minefield[rand_x][rand_y] = FieldMine;
minesweeper_state->minefield[rand_x][rand_y] = FieldMine; mines_left--;
mines_left--; }
} }
} minesweeper_state->mines_left = MINECOUNT;
minesweeper_state->mines_left = MINECOUNT; minesweeper_state->fields_cleared = 0;
minesweeper_state->fields_cleared = 0; minesweeper_state->flags_set = 0;
minesweeper_state->flags_set = 0; minesweeper_state->game_started_tick = furi_get_tick();
minesweeper_state->game_started_tick = furi_get_tick(); minesweeper_state->game_started = false;
minesweeper_state->game_started = false;
} }
static void place_flag(Minesweeper* minesweeper_state) { static void place_flag(Minesweeper* minesweeper_state) {
if (minesweeper_state->playfield[minesweeper_state->cursor_x][minesweeper_state->cursor_y] == TileTypeUncleared) { if(minesweeper_state->playfield[minesweeper_state->cursor_x][minesweeper_state->cursor_y] ==
minesweeper_state->playfield[minesweeper_state->cursor_x][minesweeper_state->cursor_y] = TileTypeFlag; TileTypeUncleared) {
minesweeper_state->flags_set++; minesweeper_state->playfield[minesweeper_state->cursor_x][minesweeper_state->cursor_y] =
} else if (minesweeper_state->playfield[minesweeper_state->cursor_x][minesweeper_state->cursor_y] == TileTypeFlag) { TileTypeFlag;
minesweeper_state->playfield[minesweeper_state->cursor_x][minesweeper_state->cursor_y] = TileTypeUncleared; minesweeper_state->flags_set++;
minesweeper_state->flags_set--; } else if(
} minesweeper_state->playfield[minesweeper_state->cursor_x][minesweeper_state->cursor_y] ==
TileTypeFlag) {
minesweeper_state->playfield[minesweeper_state->cursor_x][minesweeper_state->cursor_y] =
TileTypeUncleared;
minesweeper_state->flags_set--;
}
} }
static bool game_lost(Minesweeper* minesweeper_state) { static bool game_lost(Minesweeper* minesweeper_state) {
// returns true if the player wants to restart, otherwise false // returns true if the player wants to restart, otherwise false
DialogsApp *dialogs = furi_record_open(RECORD_DIALOGS); DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
DialogMessage* message = dialog_message_alloc(); DialogMessage* message = dialog_message_alloc();
const char* header_text = "Game Over"; const char* header_text = "Game Over";
const char* message_text = "You hit a mine!"; const char* message_text = "You hit a mine!";
dialog_message_set_header(message, header_text, 64, 3, AlignCenter, AlignTop); dialog_message_set_header(message, header_text, 64, 3, AlignCenter, AlignTop);
dialog_message_set_text(message, message_text, 64, 32, AlignCenter, AlignCenter); dialog_message_set_text(message, message_text, 64, 32, AlignCenter, AlignCenter);
dialog_message_set_buttons(message, NULL, "Play again", NULL); dialog_message_set_buttons(message, NULL, "Play again", NULL);
dialog_message_set_icon(message, NULL, 0, 10); dialog_message_set_icon(message, NULL, 0, 10);
NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION); NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION);
notification_message(notifications, &sequence_set_vibro_on); notification_message(notifications, &sequence_set_vibro_on);
furi_record_close(RECORD_NOTIFICATION); furi_record_close(RECORD_NOTIFICATION);
furi_timer_start(minesweeper_state->timer, (uint32_t) furi_kernel_get_tick_frequency() * 0.2); furi_timer_start(minesweeper_state->timer, (uint32_t)furi_kernel_get_tick_frequency() * 0.2);
DialogMessageButton choice = dialog_message_show(dialogs, message); DialogMessageButton choice = dialog_message_show(dialogs, message);
dialog_message_free(message); dialog_message_free(message);
furi_record_close(RECORD_DIALOGS); furi_record_close(RECORD_DIALOGS);
return choice == DialogMessageButtonCenter; return choice == DialogMessageButtonCenter;
} }
static bool game_won(Minesweeper* minesweeper_state) { static bool game_won(Minesweeper* minesweeper_state) {
DialogsApp *dialogs = furi_record_open(RECORD_DIALOGS); DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
FuriString* tempStr; FuriString* tempStr;
tempStr = furi_string_alloc(); tempStr = furi_string_alloc();
int seconds = 0; int seconds = 0;
int minutes = 0; int minutes = 0;
uint32_t ticks_elapsed = furi_get_tick() - minesweeper_state->game_started_tick; uint32_t ticks_elapsed = furi_get_tick() - minesweeper_state->game_started_tick;
seconds = (int) ticks_elapsed / furi_kernel_get_tick_frequency(); seconds = (int)ticks_elapsed / furi_kernel_get_tick_frequency();
minutes = (int) seconds / 60; minutes = (int)seconds / 60;
seconds = seconds % 60; seconds = seconds % 60;
DialogMessage* message = dialog_message_alloc(); DialogMessage* message = dialog_message_alloc();
const char* header_text = "Game won!"; const char* header_text = "Game won!";
furi_string_cat_printf(tempStr, "Minefield cleared in %01d:%02d", minutes, seconds); furi_string_cat_printf(tempStr, "Minefield cleared in %01d:%02d", minutes, seconds);
dialog_message_set_header(message, header_text, 64, 3, AlignCenter, AlignTop); dialog_message_set_header(message, header_text, 64, 3, AlignCenter, AlignTop);
dialog_message_set_text(message, furi_string_get_cstr(tempStr), 64, 32, AlignCenter, AlignCenter); dialog_message_set_text(
dialog_message_set_buttons(message, NULL, "Play again", NULL); message, furi_string_get_cstr(tempStr), 64, 32, AlignCenter, AlignCenter);
dialog_message_set_icon(message, NULL, 72, 17); dialog_message_set_buttons(message, NULL, "Play again", NULL);
dialog_message_set_icon(message, NULL, 72, 17);
DialogMessageButton choice = dialog_message_show(dialogs, message); DialogMessageButton choice = dialog_message_show(dialogs, message);
dialog_message_free(message); dialog_message_free(message);
furi_string_free(tempStr); furi_string_free(tempStr);
furi_record_close(RECORD_DIALOGS); furi_record_close(RECORD_DIALOGS);
return choice == DialogMessageButtonCenter; return choice == DialogMessageButtonCenter;
} }
// returns false if the move loses the game - otherwise true // returns false if the move loses the game - otherwise true
static bool play_move(Minesweeper* minesweeper_state, int cursor_x, int cursor_y) { static bool play_move(Minesweeper* minesweeper_state, int cursor_x, int cursor_y) {
if (minesweeper_state->playfield[cursor_x][cursor_y] == TileTypeFlag) { if(minesweeper_state->playfield[cursor_x][cursor_y] == TileTypeFlag) {
// we're on a flagged field, do nothing // we're on a flagged field, do nothing
return true; return true;
}
if (minesweeper_state->minefield[cursor_x][cursor_y] == FieldMine) {
// player loses - draw mine
minesweeper_state->playfield[cursor_x][cursor_y] = TileTypeMine;
return false;
}
if (minesweeper_state->playfield[cursor_x][cursor_y] >= TileType1 && minesweeper_state->playfield[cursor_x][cursor_y] <= TileType8) {
// click on a cleared cell with a number
// count the flags around
int flags = 0;
for (int y = cursor_y-1; y <= cursor_y+1; y++) {
for (int x = cursor_x-1; x <= cursor_x+1; x++) {
if ( x == cursor_x && y == cursor_y ) {
// we're on the cell the user selected, so ignore.
continue;
}
// make sure we don't go OOB
if ( x >= 0 && x < PLAYFIELD_WIDTH && y >= 0 && y < PLAYFIELD_HEIGHT) {
if (minesweeper_state->playfield[x][y] == TileTypeFlag) {
flags ++;
}
}
}
} }
int mines = minesweeper_state->playfield[cursor_x][cursor_y]; // ¯\_(ツ)_/¯ if(minesweeper_state->minefield[cursor_x][cursor_y] == FieldMine) {
if (flags == mines) { // player loses - draw mine
// auto uncover all non-flags around (to win faster ;) minesweeper_state->playfield[cursor_x][cursor_y] = TileTypeMine;
for (int auto_y = cursor_y-1; auto_y <= cursor_y+1; auto_y++) { return false;
for (int auto_x = cursor_x-1; auto_x <= cursor_x+1; auto_x++) { }
if ( auto_x == cursor_x && auto_y == cursor_y ) {
continue; if(minesweeper_state->playfield[cursor_x][cursor_y] >= TileType1 &&
} minesweeper_state->playfield[cursor_x][cursor_y] <= TileType8) {
if ( auto_x >= 0 && auto_x < PLAYFIELD_WIDTH && auto_y >= 0 && auto_y < PLAYFIELD_HEIGHT) { // click on a cleared cell with a number
if (minesweeper_state->playfield[auto_x][auto_y] == TileTypeUncleared) { // count the flags around
if(!play_move(minesweeper_state, auto_x, auto_y)) { int flags = 0;
// flags were wrong, we got a mine! for(int y = cursor_y - 1; y <= cursor_y + 1; y++) {
return false; for(int x = cursor_x - 1; x <= cursor_x + 1; x++) {
} if(x == cursor_x && y == cursor_y) {
// we're on the cell the user selected, so ignore.
continue;
}
// make sure we don't go OOB
if(x >= 0 && x < PLAYFIELD_WIDTH && y >= 0 && y < PLAYFIELD_HEIGHT) {
if(minesweeper_state->playfield[x][y] == TileTypeFlag) {
flags++;
}
}
} }
}
} }
} int mines = minesweeper_state->playfield[cursor_x][cursor_y]; // ¯\_(ツ)_/¯
// we're done without hitting a mine - so return if(flags == mines) {
return true; // auto uncover all non-flags around (to win faster ;)
for(int auto_y = cursor_y - 1; auto_y <= cursor_y + 1; auto_y++) {
for(int auto_x = cursor_x - 1; auto_x <= cursor_x + 1; auto_x++) {
if(auto_x == cursor_x && auto_y == cursor_y) {
continue;
}
if(auto_x >= 0 && auto_x < PLAYFIELD_WIDTH && auto_y >= 0 &&
auto_y < PLAYFIELD_HEIGHT) {
if(minesweeper_state->playfield[auto_x][auto_y] == TileTypeUncleared) {
if(!play_move(minesweeper_state, auto_x, auto_y)) {
// flags were wrong, we got a mine!
return false;
}
}
}
}
}
// we're done without hitting a mine - so return
return true;
}
} }
}
// calculate number of surrounding mines. // calculate number of surrounding mines.
int hint = 0; int hint = 0;
for (int y = cursor_y-1; y <= cursor_y+1; y++) { for(int y = cursor_y - 1; y <= cursor_y + 1; y++) {
for (int x = cursor_x-1; x <= cursor_x+1; x++) { for(int x = cursor_x - 1; x <= cursor_x + 1; x++) {
if ( x == cursor_x && y == cursor_y ) { if(x == cursor_x && y == cursor_y) {
// we're on the cell the user selected, so ignore. // we're on the cell the user selected, so ignore.
continue; continue;
} }
// make sure we don't go OOB // make sure we don't go OOB
if ( x >= 0 && x < PLAYFIELD_WIDTH && y >= 0 && y < PLAYFIELD_HEIGHT) { if(x >= 0 && x < PLAYFIELD_WIDTH && y >= 0 && y < PLAYFIELD_HEIGHT) {
if(minesweeper_state->minefield[x][y] == FieldMine) { if(minesweeper_state->minefield[x][y] == FieldMine) {
hint ++; hint++;
}
}
} }
}
} }
} // 〜( ̄▽ ̄〜) don't judge me (〜 ̄▽ ̄)〜
// 〜( ̄▽ ̄〜) don't judge me (〜 ̄▽ ̄)〜 minesweeper_state->playfield[cursor_x][cursor_y] = hint;
minesweeper_state->playfield[cursor_x][cursor_y] = hint; minesweeper_state->fields_cleared++;
minesweeper_state->fields_cleared++; FURI_LOG_D("Minesweeper", "Setting %d,%d to %d", cursor_x, cursor_y, hint);
FURI_LOG_D("Minesweeper", "Setting %d,%d to %d", cursor_x, cursor_y, hint); if(hint == 0) {
if (hint == 0) { // the field is "empty"
// the field is "empty" // auto open surrounding fields.
// auto open surrounding fields. for(int auto_y = cursor_y - 1; auto_y <= cursor_y + 1; auto_y++) {
for (int auto_y = cursor_y-1; auto_y <= cursor_y+1; auto_y++) { for(int auto_x = cursor_x - 1; auto_x <= cursor_x + 1; auto_x++) {
for (int auto_x = cursor_x-1; auto_x <= cursor_x+1; auto_x++) { if(auto_x == cursor_x && auto_y == cursor_y) {
if ( auto_x == cursor_x && auto_y == cursor_y ) { continue;
continue; }
if(auto_x >= 0 && auto_x < PLAYFIELD_WIDTH && auto_y >= 0 &&
auto_y < PLAYFIELD_HEIGHT) {
if(minesweeper_state->playfield[auto_x][auto_y] == TileTypeUncleared) {
play_move(minesweeper_state, auto_x, auto_y);
}
}
}
} }
if ( auto_x >= 0 && auto_x < PLAYFIELD_WIDTH && auto_y >= 0 && auto_y < PLAYFIELD_HEIGHT) {
if (minesweeper_state->playfield[auto_x][auto_y] == TileTypeUncleared) {
play_move(minesweeper_state, auto_x, auto_y);
}
}
}
} }
} return true;
return true;
} }
static void minesweeper_state_init(Minesweeper* const minesweeper_state) { static void minesweeper_state_init(Minesweeper* const minesweeper_state) {
minesweeper_state->cursor_x = minesweeper_state->cursor_y = 0; minesweeper_state->cursor_x = minesweeper_state->cursor_y = 0;
minesweeper_state->game_started = false; minesweeper_state->game_started = false;
for (int y = 0; y < PLAYFIELD_HEIGHT; y++) { for(int y = 0; y < PLAYFIELD_HEIGHT; y++) {
for (int x = 0; x < PLAYFIELD_WIDTH; x++){ for(int x = 0; x < PLAYFIELD_WIDTH; x++) {
minesweeper_state->playfield[x][y] = TileTypeUncleared; minesweeper_state->playfield[x][y] = TileTypeUncleared;
} }
} }
} }
int32_t minesweeper_app(void* p) { int32_t minesweeper_app(void* p) {
UNUSED(p); UNUSED(p);
DialogsApp *dialogs = furi_record_open(RECORD_DIALOGS); DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
DialogMessage* message = dialog_message_alloc(); DialogMessage* message = dialog_message_alloc();
const char* header_text = "Minesweeper"; const char* header_text = "Minesweeper";
const char* message_text = "Hold OK pressed to toggle flags.\ngithub.com/panki27"; const char* message_text = "Hold OK pressed to toggle flags.\ngithub.com/panki27";
dialog_message_set_header(message, header_text, 64, 3, AlignCenter, AlignTop); dialog_message_set_header(message, header_text, 64, 3, AlignCenter, AlignTop);
dialog_message_set_text(message, message_text, 64, 32, AlignCenter, AlignCenter); dialog_message_set_text(message, message_text, 64, 32, AlignCenter, AlignCenter);
dialog_message_set_buttons(message, NULL, "Play", NULL); dialog_message_set_buttons(message, NULL, "Play", NULL);
dialog_message_set_icon(message, NULL, 0, 10); dialog_message_set_icon(message, NULL, 0, 10);
dialog_message_show(dialogs, message); dialog_message_show(dialogs, message);
dialog_message_free(message); dialog_message_free(message);
furi_record_close(RECORD_DIALOGS); furi_record_close(RECORD_DIALOGS);
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
Minesweeper* minesweeper_state = malloc(sizeof(Minesweeper)); Minesweeper* minesweeper_state = malloc(sizeof(Minesweeper));
// setup // setup
minesweeper_state_init(minesweeper_state); minesweeper_state_init(minesweeper_state);
ValueMutex state_mutex; ValueMutex state_mutex;
if (!init_mutex(&state_mutex, minesweeper_state, sizeof(minesweeper_state))) { if(!init_mutex(&state_mutex, minesweeper_state, sizeof(minesweeper_state))) {
FURI_LOG_E("Minesweeper", "cannot create mutex\r\n"); FURI_LOG_E("Minesweeper", "cannot create mutex\r\n");
free(minesweeper_state); free(minesweeper_state);
return 255; return 255;
}
// BEGIN IMPLEMENTATION
// 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);
minesweeper_state->timer = furi_timer_alloc(timer_callback, FuriTimerTypeOnce, &state_mutex);
// Open GUI and register view_port
Gui* gui = furi_record_open("gui");
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
PluginEvent event;
for (bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
Minesweeper* minesweeper_state = (Minesweeper*)acquire_mutex_block(&state_mutex);
if(event_status == FuriStatusOk) {
// press events
if(event.type == EventTypeKey) {
if(event.input.type == InputTypeShort) {
switch(event.input.key) {
case InputKeyUp:
minesweeper_state->cursor_y--;
if(minesweeper_state->cursor_y < 0) {
minesweeper_state->cursor_y = 0;
}
break;
case InputKeyDown:
minesweeper_state->cursor_y++;
if(minesweeper_state->cursor_y >= PLAYFIELD_HEIGHT) {
minesweeper_state->cursor_y = PLAYFIELD_HEIGHT-1;
}
break;
case InputKeyRight:
minesweeper_state->cursor_x++;
if(minesweeper_state->cursor_x >= PLAYFIELD_WIDTH) {
minesweeper_state->cursor_x = PLAYFIELD_WIDTH-1;
}
break;
case InputKeyLeft:
minesweeper_state->cursor_x--;
if(minesweeper_state->cursor_x < 0) {
minesweeper_state->cursor_x = 0;
}
break;
case InputKeyOk:
if (!minesweeper_state->game_started) {
setup_playfield(minesweeper_state);
minesweeper_state->game_started = true;
}
if (!play_move(minesweeper_state, minesweeper_state->cursor_x, minesweeper_state->cursor_y)) {
// ooops. looks like we hit a mine!
if (game_lost(minesweeper_state)) {
// player wants to restart.
setup_playfield(minesweeper_state);
} else {
// player wants to exit :(
processing = false;
}
} else {
// check win condition.
if (minesweeper_state->fields_cleared == (PLAYFIELD_HEIGHT*PLAYFIELD_WIDTH) - MINECOUNT){
if (game_won(minesweeper_state)) {
//player wants to restart
setup_playfield(minesweeper_state);
} else {
processing = false;
}
}
}
break;
case InputKeyBack:
// Exit the plugin
processing = false;
break;
}
} else if (event.input.type == InputTypeLong) {
// hold events
FURI_LOG_D("Minesweeper", "Got a long press!");
switch(event.input.key) {
case InputKeyUp:
case InputKeyDown:
case InputKeyRight:
case InputKeyLeft:
break;
case InputKeyOk:
FURI_LOG_D("Minesweeper", "Toggling flag");
place_flag(minesweeper_state);
break;
case InputKeyBack:
processing = false;
break;
}
}
}
} else {
// event timeout
;
} }
view_port_update(view_port); // BEGIN IMPLEMENTATION
release_mutex(&state_mutex, minesweeper_state);
}
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);
delete_mutex(&state_mutex);
furi_timer_free(minesweeper_state->timer);
free(minesweeper_state);
return 0; // 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);
minesweeper_state->timer = furi_timer_alloc(timer_callback, FuriTimerTypeOnce, &state_mutex);
// Open GUI and register view_port
Gui* gui = furi_record_open("gui");
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
PluginEvent event;
for(bool processing = true; processing;) {
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
Minesweeper* minesweeper_state = (Minesweeper*)acquire_mutex_block(&state_mutex);
if(event_status == FuriStatusOk) {
// press events
if(event.type == EventTypeKey) {
if(event.input.type == InputTypeShort) {
switch(event.input.key) {
case InputKeyUp:
minesweeper_state->cursor_y--;
if(minesweeper_state->cursor_y < 0) {
minesweeper_state->cursor_y = 0;
}
break;
case InputKeyDown:
minesweeper_state->cursor_y++;
if(minesweeper_state->cursor_y >= PLAYFIELD_HEIGHT) {
minesweeper_state->cursor_y = PLAYFIELD_HEIGHT - 1;
}
break;
case InputKeyRight:
minesweeper_state->cursor_x++;
if(minesweeper_state->cursor_x >= PLAYFIELD_WIDTH) {
minesweeper_state->cursor_x = PLAYFIELD_WIDTH - 1;
}
break;
case InputKeyLeft:
minesweeper_state->cursor_x--;
if(minesweeper_state->cursor_x < 0) {
minesweeper_state->cursor_x = 0;
}
break;
case InputKeyOk:
if(!minesweeper_state->game_started) {
setup_playfield(minesweeper_state);
minesweeper_state->game_started = true;
}
if(!play_move(
minesweeper_state,
minesweeper_state->cursor_x,
minesweeper_state->cursor_y)) {
// ooops. looks like we hit a mine!
if(game_lost(minesweeper_state)) {
// player wants to restart.
setup_playfield(minesweeper_state);
} else {
// player wants to exit :(
processing = false;
}
} else {
// check win condition.
if(minesweeper_state->fields_cleared ==
(PLAYFIELD_HEIGHT * PLAYFIELD_WIDTH) - MINECOUNT) {
if(game_won(minesweeper_state)) {
//player wants to restart
setup_playfield(minesweeper_state);
} else {
processing = false;
}
}
}
break;
case InputKeyBack:
// Exit the plugin
processing = false;
break;
}
} else if(event.input.type == InputTypeLong) {
// hold events
FURI_LOG_D("Minesweeper", "Got a long press!");
switch(event.input.key) {
case InputKeyUp:
case InputKeyDown:
case InputKeyRight:
case InputKeyLeft:
break;
case InputKeyOk:
FURI_LOG_D("Minesweeper", "Toggling flag");
place_flag(minesweeper_state);
break;
case InputKeyBack:
processing = false;
break;
}
}
}
} else {
// event timeout
;
}
view_port_update(view_port);
release_mutex(&state_mutex, minesweeper_state);
}
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);
delete_mutex(&state_mutex);
furi_timer_free(minesweeper_state->timer);
free(minesweeper_state);
return 0;
} }