13
13
#include <assets_icons.h>
14
14
#include <gui/elements.h>
15
15
16
+ #include <storage/storage.h>
17
+ #include <toolbox/stream/stream.h>
18
+ #include <toolbox/stream/file_stream.h>
19
+ #include <flipper_format/flipper_format.h>
16
20
17
21
/* Magic happens here -- this file is generated by fbt.
18
22
* Just set fap_icon_assets in application.fam and #include {APPID}_icons.h */
@@ -29,7 +33,11 @@ static ImagePosition smoke_position = {.x = 0, .y = 0};
29
33
typedef struct {
30
34
int32_t counter , maxdrags ;
31
35
bool lit , done ;
36
+ uint8_t style ; // 0 = regular, 1 = skinny
32
37
IconAnimation * icon ;
38
+ NotificationApp * notification ;
39
+ uint32_t smoke_end ; // Tracks when to stop smoke
40
+ bool cough_used ; // NEW - tracks if cough was used
33
41
} Cigarette ;
34
42
35
43
static Cigarette cigarette = {.counter = 0 , .maxdrags = 13 , .lit = false, .done = false};
@@ -39,112 +47,153 @@ const NotificationMessage message_delay_15 = {
39
47
.data .delay .length = 15 ,
40
48
};
41
49
42
- static const NotificationSequence vibr1 = {
43
- & message_note_ds4 ,
44
- & message_delay_15 ,
45
- & message_sound_off ,
46
-
47
- & message_note_a5 ,
48
- & message_delay_15 ,
49
- & message_sound_off ,
50
-
50
+ static const NotificationSequence sequence_puff = {
51
51
& message_vibro_on ,
52
- & message_delay_25 ,
52
+ & message_delay_50 ,
53
53
& message_vibro_off ,
54
-
55
54
NULL ,
56
55
};
57
56
58
- // Light up
59
- static const NotificationSequence vibr2 = {
60
- & message_note_ds2 ,
61
- & message_delay_25 ,
62
- & message_sound_off ,
63
-
57
+ static const NotificationSequence sequence_light_up = {
58
+ & message_red_255 ,
59
+ & message_vibro_on ,
60
+ & message_delay_100 ,
61
+ & message_vibro_off ,
62
+ & message_red_0 ,
63
+ NULL ,
64
+ };
65
+ static const NotificationSequence sequence_cough = {
66
+ & message_red_255 ,
64
67
& message_vibro_on ,
65
- & message_delay_15 ,
68
+ & message_delay_100 ,
66
69
& message_vibro_off ,
70
+ & message_red_0 ,
71
+ & message_delay_50 ,
72
+ & message_delay_50 ,
73
+ & message_delay_50 ,
74
+ & message_delay_50 ,
75
+ & message_vibro_on ,
76
+ & message_delay_50 ,
77
+ & message_vibro_off ,
78
+ & message_delay_50 ,
79
+ & message_delay_50 ,
80
+ & message_red_255 ,
81
+ & message_delay_25 ,
82
+ & message_red_0 ,
67
83
68
84
NULL ,
69
85
};
70
86
87
+ typedef struct {
88
+ uint32_t total_smokes ;
89
+ bool visible ;
90
+ Storage * storage ;
91
+ } StatsView ;
92
+
93
+ static StatsView stats_view = {.total_smokes = 0 , .visible = false};
94
+
71
95
static void app_draw_callback (Canvas * canvas , void * ctx ) {
72
96
UNUSED (ctx );
73
-
74
97
canvas_clear (canvas );
75
98
99
+ if (stats_view .visible ) {
100
+ char buffer [64 ];
101
+ // 1 cig in app = 15 IRL cigs (one pack)
102
+ // 14.7% cancer risk after 20 years of 15/day
103
+ float cancer_pct = (stats_view .total_smokes * 15.0f ) / (15 * 365 * 20 ) * 14.7f ;
104
+ if (cancer_pct > 100.0f )
105
+ cancer_pct = 100.0f ;
106
+
107
+ snprintf (buffer , sizeof (buffer ), "darts smoked: %lu" , stats_view .total_smokes );
108
+ canvas_draw_str_aligned (canvas , 64 , 27 , AlignCenter , AlignCenter , buffer );
109
+
110
+ if (cancer_pct >= 100.0f ) {
111
+ canvas_draw_str_aligned (canvas , 64 , 34 , AlignCenter , AlignCenter , "YOU DIED OF CANCER" );
112
+ canvas_draw_str_aligned (canvas , 64 , 44 , AlignCenter , AlignCenter , "RIP BOZO" );
113
+ } else {
114
+ snprintf (buffer , sizeof (buffer ), "progress to cancer: %.1f%%" , (double )cancer_pct );
115
+ canvas_draw_str_aligned (canvas , 64 , 40 , AlignCenter , AlignCenter , buffer );
116
+
117
+ if (cancer_pct >= 50.0f && cancer_pct < 100.0f ) {
118
+ canvas_draw_str_aligned (canvas , 64 , 44 , AlignCenter , AlignCenter , "SEEK HELP IMMEDIATELY" );
119
+ }
120
+ }
121
+
122
+ elements_button_down (canvas , "back" );
123
+ return ;
124
+ }
76
125
77
- if (cigarette .lit == true && cigarette .done == false){
126
+ // Draw smoke only when smoke_end is active
127
+ if (cigarette .smoke_end ) {
128
+ canvas_draw_icon_animation (canvas , smoke_position .x , smoke_position .y , cigarette .icon );
129
+ } else if (cigarette .lit && !cigarette .done ) {
78
130
elements_button_right (canvas , "puff" );
79
131
canvas_draw_icon_animation (canvas , smoke_position .x , smoke_position .y , cigarette .icon );
80
132
}
81
133
82
- if (cigarette .lit == false && cigarette .done == false)
134
+ if (cigarette .lit == false && cigarette .done == false) {
83
135
elements_button_center (canvas , "light up" );
136
+ canvas_draw_str_aligned (canvas , 64 , 15 , AlignCenter , AlignCenter , "< switch style >" );
137
+ }
84
138
85
- if (cigarette .done == true){
86
- elements_button_left (canvas , "another one" );
87
-
139
+ if (cigarette .done == true) {
140
+ elements_button_left (canvas , "another one" );
88
141
}
89
142
90
143
// TODO: open about-view
91
144
//elements_button_down(canvas, "about");
92
145
93
-
94
-
95
- // Cigarette
146
+ // Cigarette icon selection based on style
147
+ const Icon * current_icon = NULL ;
96
148
switch (cigarette .counter ) {
97
149
case 0 :
98
- canvas_draw_icon ( canvas , image_position . x , image_position . y , & I_cigarette0_115x25 ) ;
150
+ current_icon = ( cigarette . style == 0 ) ? & I_cigarette0_115x25 : & I_skinny0_112x20 ;
99
151
break ;
100
152
case 1 :
101
- canvas_draw_icon ( canvas , image_position . x , image_position . y , & I_cigarette1_115x25 ) ;
153
+ current_icon = ( cigarette . style == 0 ) ? & I_cigarette1_115x25 : & I_skinny1_112x20 ;
102
154
break ;
103
155
case 2 :
104
- canvas_draw_icon ( canvas , image_position . x , image_position . y , & I_cigarette2_115x25 ) ;
156
+ current_icon = ( cigarette . style == 0 ) ? & I_cigarette2_115x25 : & I_skinny2_112x20 ;
105
157
break ;
106
158
case 3 :
107
- canvas_draw_icon ( canvas , image_position . x , image_position . y , & I_cigarette3_115x25 ) ;
159
+ current_icon = ( cigarette . style == 0 ) ? & I_cigarette3_115x25 : & I_skinny3_112x20 ;
108
160
break ;
109
161
case 4 :
110
- canvas_draw_icon ( canvas , image_position . x , image_position . y , & I_cigarette4_115x25 ) ;
162
+ current_icon = ( cigarette . style == 0 ) ? & I_cigarette4_115x25 : & I_skinny4_112x20 ;
111
163
break ;
112
164
case 5 :
113
- canvas_draw_icon ( canvas , image_position . x , image_position . y , & I_cigarette5_115x25 ) ;
165
+ current_icon = ( cigarette . style == 0 ) ? & I_cigarette5_115x25 : & I_skinny5_112x20 ;
114
166
break ;
115
167
case 6 :
116
- canvas_draw_icon ( canvas , image_position . x , image_position . y , & I_cigarette6_115x25 ) ;
168
+ current_icon = ( cigarette . style == 0 ) ? & I_cigarette6_115x25 : & I_skinny6_112x20 ;
117
169
break ;
118
170
case 7 :
119
- canvas_draw_icon ( canvas , image_position . x , image_position . y , & I_cigarette7_115x25 ) ;
171
+ current_icon = ( cigarette . style == 0 ) ? & I_cigarette7_115x25 : & I_skinny7_112x20 ;
120
172
break ;
121
173
case 8 :
122
- canvas_draw_icon ( canvas , image_position . x , image_position . y , & I_cigarette8_115x25 ) ;
174
+ current_icon = ( cigarette . style == 0 ) ? & I_cigarette8_115x25 : & I_skinny8_112x20 ;
123
175
break ;
124
176
case 9 :
125
- canvas_draw_icon ( canvas , image_position . x , image_position . y , & I_cigarette9_115x25 ) ;
177
+ current_icon = ( cigarette . style == 0 ) ? & I_cigarette9_115x25 : & I_skinny9_112x20 ;
126
178
break ;
127
179
case 10 :
128
- canvas_draw_icon ( canvas , image_position . x , image_position . y , & I_cigarette10_115x25 ) ;
180
+ current_icon = ( cigarette . style == 0 ) ? & I_cigarette10_115x25 : & I_skinny10_112x20 ;
129
181
break ;
130
182
case 11 :
131
- canvas_draw_icon ( canvas , image_position . x , image_position . y , & I_cigarette11_115x25 ) ;
183
+ current_icon = ( cigarette . style == 0 ) ? & I_cigarette11_115x25 : & I_skinny11_112x20 ;
132
184
break ;
133
185
case 12 :
134
- canvas_draw_icon ( canvas , image_position . x , image_position . y , & I_cigarette12_115x25 ) ;
186
+ current_icon = ( cigarette . style == 0 ) ? & I_cigarette12_115x25 : & I_skinny12_112x20 ;
135
187
break ;
136
188
case 13 :
137
- canvas_draw_icon ( canvas , image_position . x , image_position . y , & I_cigarette13_115x25 ) ;
189
+ current_icon = ( cigarette . style == 0 ) ? & I_cigarette13_115x25 : & I_skinny13_112x20 ;
138
190
cigarette .done = true;
139
191
break ;
140
192
default :
141
- canvas_draw_icon ( canvas , image_position . x , image_position . y , & I_cigarette0_115x25 ) ;
193
+ current_icon = ( cigarette . style == 0 ) ? & I_cigarette13_115x25 : & I_skinny13_112x20 ;
142
194
break ;
143
-
144
195
}
145
-
146
-
147
-
196
+ canvas_draw_icon (canvas , image_position .x , image_position .y , current_icon );
148
197
}
149
198
150
199
static void app_input_callback (InputEvent * input_event , void * ctx ) {
@@ -169,49 +218,96 @@ int32_t cigarette_main(void* p) {
169
218
Gui * gui = furi_record_open (RECORD_GUI );
170
219
gui_add_view_port (gui , view_port , GuiLayerFullscreen );
171
220
172
- NotificationApp * notification = furi_record_open (RECORD_NOTIFICATION );
221
+ cigarette .notification = furi_record_open (RECORD_NOTIFICATION );
222
+
223
+ stats_view .storage = furi_record_open (RECORD_STORAGE );
224
+
225
+ // Load stats
226
+ FlipperFormat * ff_load = flipper_format_file_alloc (stats_view .storage );
227
+ storage_common_mkdir (stats_view .storage , "/ext/apps_data/cigarette" );
228
+ if (flipper_format_file_open_existing (ff_load , "/ext/apps_data/cigarette/stats.txt" )) {
229
+ flipper_format_read_uint32 (ff_load , "total" , & stats_view .total_smokes , 1 );
230
+ }
231
+ flipper_format_free (ff_load );
173
232
174
233
InputEvent event ;
175
234
176
235
bool running = true;
177
236
while (running ) {
237
+ // Check if smoke needs to stop
238
+ if (cigarette .smoke_end && furi_get_tick () >= cigarette .smoke_end ) {
239
+ icon_animation_stop (cigarette .icon );
240
+ cigarette .smoke_end = 0 ;
241
+ }
242
+
178
243
if (furi_message_queue_get (event_queue , & event , 100 ) == FuriStatusOk ) {
179
244
if ((event .type == InputTypePress ) || (event .type == InputTypeRepeat )) {
180
245
switch (event .key ) {
181
246
case InputKeyLeft :
182
- cigarette .counter = 0 ;
183
- cigarette .lit = 0 ;
184
- cigarette .done = false;
185
- smoke_position .x = 0 ;
186
- image_position .x = 12 ;
187
- icon_animation_stop (cigarette .icon );
247
+ if (!stats_view .visible && !cigarette .lit && !cigarette .done ) {
248
+ // Cycle through styles
249
+ cigarette .style = (cigarette .style - 1 + 2 ) % 2 ;
250
+ } else if (cigarette .done ) {
251
+ // Reset positions when getting new cig
252
+ stats_view .total_smokes ++ ;
253
+ cigarette .counter = 0 ;
254
+ cigarette .lit = false;
255
+ cigarette .done = false;
256
+ cigarette .cough_used = false; // RESET FLAG
257
+ smoke_position .x = 0 ; // Reset smoke position
258
+ image_position .x = 12 ; // Reset cig position
259
+ icon_animation_stop (cigarette .icon );
260
+ }
188
261
break ;
189
262
case InputKeyRight :
190
- if (cigarette .counter < cigarette .maxdrags && cigarette .lit == true){
191
- cigarette .counter += 1 ;
192
- smoke_position .x += 5 ;
193
- image_position .x += 5 ;
194
- notification_message (notification , & vibr2 );
263
+ if (!stats_view .visible && !cigarette .lit && !cigarette .done ) {
264
+ cigarette .style = (cigarette .style + 1 ) % 2 ;
265
+ } else if (cigarette .lit ) {
266
+ if (!cigarette .done ) {
267
+ cigarette .counter ++ ;
268
+ if (cigarette .counter > cigarette .maxdrags ) {
269
+ // Secret cough with 1s smoke
270
+ icon_animation_start (cigarette .icon );
271
+ cigarette .smoke_end = furi_get_tick () + 1000 ;
272
+ notification_message (cigarette .notification , & sequence_cough );
273
+ cigarette .counter = cigarette .maxdrags ;
274
+ } else {
275
+ if (cigarette .counter == cigarette .maxdrags ) {
276
+ cigarette .done = true;
277
+ }
278
+ notification_message (cigarette .notification , & sequence_puff );
279
+ smoke_position .x += 5 ;
280
+ image_position .x += 5 ;
281
+ }
282
+ } else {
283
+ if (!cigarette .cough_used ) { // CHECK FLAG
284
+ // Cough puff with reset smoke position
285
+ smoke_position .x = image_position .x - 3 ; // Start from cig tip
286
+ icon_animation_start (cigarette .icon );
287
+ cigarette .smoke_end = furi_get_tick () + 1000 ;
288
+ notification_message (cigarette .notification , & sequence_cough );
289
+ cigarette .cough_used = true; // SET FLAG
290
+ }
291
+ }
195
292
}
196
293
break ;
197
294
case InputKeyUp :
198
- // image_position.y -= 2;
295
+ if (event .type == InputTypePress ) {
296
+ stats_view .visible = !stats_view .visible ;
297
+ }
199
298
break ;
200
299
case InputKeyDown :
201
- // image_position.y += 2;
300
+ if (stats_view .visible ) {
301
+ stats_view .visible = false;
302
+ }
202
303
break ;
203
304
case InputKeyOk :
204
- // Light Cigarette - play lighter animation
205
- if (cigarette .counter == 0 ){
206
- if (cigarette .lit == false){
207
- cigarette .lit = true;
208
- notification_message (notification , & vibr1 );
209
-
210
- }
305
+ if (cigarette .counter == 0 && !cigarette .lit ) {
306
+ cigarette .lit = true;
307
+ notification_message (cigarette .notification , & sequence_light_up );
211
308
icon_animation_start (cigarette .icon );
309
+ notification_message (cigarette .notification , & sequence_blink_red_10 );
212
310
}
213
-
214
-
215
311
break ;
216
312
default :
217
313
running = false;
@@ -223,6 +319,16 @@ int32_t cigarette_main(void* p) {
223
319
}
224
320
225
321
view_port_enabled_set (view_port , false);
322
+
323
+ // Save stats before exit
324
+ FlipperFormat * ff_save = flipper_format_file_alloc (stats_view .storage );
325
+ if (flipper_format_file_open_always (ff_save , "/ext/apps_data/cigarette/stats.txt" )) {
326
+ flipper_format_write_header_cstr (ff_save , "CigaretteStats" , 1 );
327
+ flipper_format_write_uint32 (ff_save , "total" , & stats_view .total_smokes , 1 );
328
+ }
329
+ flipper_format_free (ff_save );
330
+ furi_record_close (RECORD_STORAGE );
331
+
226
332
gui_remove_view_port (gui , view_port );
227
333
view_port_free (view_port );
228
334
furi_message_queue_free (event_queue );
0 commit comments