1313#include <assets_icons.h>
1414#include <gui/elements.h>
1515
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>
1620
1721/* Magic happens here -- this file is generated by fbt.
1822 * 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};
2933typedef struct {
3034 int32_t counter , maxdrags ;
3135 bool lit , done ;
36+ uint8_t style ; // 0 = regular, 1 = skinny
3237 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
3341} Cigarette ;
3442
3543static Cigarette cigarette = {.counter = 0 , .maxdrags = 13 , .lit = false, .done = false};
@@ -39,112 +47,153 @@ const NotificationMessage message_delay_15 = {
3947 .data .delay .length = 15 ,
4048};
4149
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 = {
5151 & message_vibro_on ,
52- & message_delay_25 ,
52+ & message_delay_50 ,
5353 & message_vibro_off ,
54-
5554 NULL ,
5655};
5756
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 ,
6467 & message_vibro_on ,
65- & message_delay_15 ,
68+ & message_delay_100 ,
6669 & 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 ,
6783
6884 NULL ,
6985};
7086
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+
7195static void app_draw_callback (Canvas * canvas , void * ctx ) {
7296 UNUSED (ctx );
73-
7497 canvas_clear (canvas );
7598
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+ }
76125
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 ) {
78130 elements_button_right (canvas , "puff" );
79131 canvas_draw_icon_animation (canvas , smoke_position .x , smoke_position .y , cigarette .icon );
80132 }
81133
82- if (cigarette .lit == false && cigarette .done == false)
134+ if (cigarette .lit == false && cigarette .done == false) {
83135 elements_button_center (canvas , "light up" );
136+ canvas_draw_str_aligned (canvas , 64 , 15 , AlignCenter , AlignCenter , "< switch style >" );
137+ }
84138
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" );
88141 }
89142
90143 // TODO: open about-view
91144 //elements_button_down(canvas, "about");
92145
93-
94-
95- // Cigarette
146+ // Cigarette icon selection based on style
147+ const Icon * current_icon = NULL ;
96148 switch (cigarette .counter ) {
97149 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 ;
99151 break ;
100152 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 ;
102154 break ;
103155 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 ;
105157 break ;
106158 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 ;
108160 break ;
109161 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 ;
111163 break ;
112164 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 ;
114166 break ;
115167 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 ;
117169 break ;
118170 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 ;
120172 break ;
121173 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 ;
123175 break ;
124176 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 ;
126178 break ;
127179 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 ;
129181 break ;
130182 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 ;
132184 break ;
133185 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 ;
135187 break ;
136188 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 ;
138190 cigarette .done = true;
139191 break ;
140192 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 ;
142194 break ;
143-
144195 }
145-
146-
147-
196+ canvas_draw_icon (canvas , image_position .x , image_position .y , current_icon );
148197}
149198
150199static void app_input_callback (InputEvent * input_event , void * ctx ) {
@@ -169,49 +218,96 @@ int32_t cigarette_main(void* p) {
169218 Gui * gui = furi_record_open (RECORD_GUI );
170219 gui_add_view_port (gui , view_port , GuiLayerFullscreen );
171220
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 );
173232
174233 InputEvent event ;
175234
176235 bool running = true;
177236 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+
178243 if (furi_message_queue_get (event_queue , & event , 100 ) == FuriStatusOk ) {
179244 if ((event .type == InputTypePress ) || (event .type == InputTypeRepeat )) {
180245 switch (event .key ) {
181246 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+ }
188261 break ;
189262 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+ }
195292 }
196293 break ;
197294 case InputKeyUp :
198- // image_position.y -= 2;
295+ if (event .type == InputTypePress ) {
296+ stats_view .visible = !stats_view .visible ;
297+ }
199298 break ;
200299 case InputKeyDown :
201- // image_position.y += 2;
300+ if (stats_view .visible ) {
301+ stats_view .visible = false;
302+ }
202303 break ;
203304 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 );
211308 icon_animation_start (cigarette .icon );
309+ notification_message (cigarette .notification , & sequence_blink_red_10 );
212310 }
213-
214-
215311 break ;
216312 default :
217313 running = false;
@@ -223,6 +319,16 @@ int32_t cigarette_main(void* p) {
223319 }
224320
225321 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+
226332 gui_remove_view_port (gui , view_port );
227333 view_port_free (view_port );
228334 furi_message_queue_free (event_queue );
0 commit comments