]> git.sur5r.net Git - c128-kasse/blob - src/kasse.c
Format code using clang-format-3.9 (“make format”)
[c128-kasse] / src / kasse.c
1 /*
2  * RGB2R-C128-Kassenprogramm
3  * © 2007-2009 phil_fry, sECuRE, sur5r
4  * See LICENSE for license information
5  *
6  */
7 #define _IS_KASSE
8 #include <stdio.h>
9 #include <conio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <cbm.h>
13
14 #include "general.h"
15 #include "config.h"
16 #include "kasse.h"
17 #include "credit_manager.h"
18 #include "c128time.h"
19 #include "print.h"
20 #include "version.h"
21 // drucker 4 oder 5
22 // graphic 4,0,10
23
24 void print_item(BYTE i) {
25   char profit[10];
26   if (format_euro(profit, sizeof(profit), status.status[i].price) == NULL) {
27     cprintf("Preis %ld konnte nicht umgerechnet werden\r\n",
28             status.status[i].price);
29     exit(1);
30   }
31   textcolor(TC_YELLOW);
32   cprintf("%2d", i);
33   textcolor(TC_LIGHT_GRAY);
34   cprintf(": %-" xstr(MAX_ITEM_NAME_LENGTH) "s \xDD%s, %3dx ",
35           status.status[i].item_name, profit, status.status[i].times_sold);
36 }
37
38 /* Hauptbildschirm ausgeben */
39 static void print_screen(void) {
40   BYTE i = 0;
41   char *time = get_time();
42   char profit[10];
43   clrscr();
44   if (format_euro(profit, 10, money) == NULL) {
45     cprintf("Einnahme %ld konnte nicht umgerechnet werden\r\n", money);
46     exit(1);
47   }
48   textcolor(TC_CYAN);
49   cprintf("C128-Kassenprogramm (phil_fry, sECuRE, sur5r) " GV "\r\n");
50   textcolor(TC_LIGHT_GRAY);
51   cprintf("\r\nUhrzeit:     %s (wird nicht aktualisiert)\r\n"
52           "Eingenommen: %s, Verkauft: %ld Dinge, Drucken: %s\r\n",
53           time, profit, items_sold, (printing == 1 ? "ein" : "aus"));
54   textcolor(TC_LIGHT_GRAY);
55   cprintf("      \xB0"
56           "\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xB2"
57           "\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xB2"
58           "\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xB2"
59           "\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xAE"
60           "\r\n");
61   for (; i < min(status.num_items, 15); ++i) {
62
63     cprintf("      \xDD");
64     print_item(i);
65     cprintf("\xDD");
66
67     /* if we have more than 15 items, use the second column */
68     if ((i + 15) < status.num_items) {
69       print_item(i + 15);
70       cprintf("\xDD");
71     } else {
72       cprintf("              \xDD                \xDD");
73     }
74
75     cprintf("\r\n");
76   }
77   cprintf("      \xAD"
78           "\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xB1"
79           "\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xB1"
80           "\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xB1"
81           "\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xC0\xBD"
82           "\r\n");
83   textcolor(TC_YELLOW);
84   cprintf("   s");
85   textcolor(TC_LIGHT_GRAY);
86   cprintf(") Daten sichern                                  ");
87   textcolor(TC_YELLOW);
88   cprintf("g");
89   textcolor(TC_LIGHT_GRAY);
90   cprintf(") Guthabenverwaltung\r\n");
91   textcolor(TC_YELLOW);
92   cprintf("   z");
93   textcolor(TC_LIGHT_GRAY);
94   cprintf(") Zeit setzen         ");
95   textcolor(TC_YELLOW);
96   cprintf("f");
97   textcolor(TC_LIGHT_GRAY);
98   cprintf(") Freitext verkaufen      ");
99   textcolor(TC_YELLOW);
100   cprintf("q");
101   textcolor(TC_LIGHT_GRAY);
102   cprintf(") Beenden\r\n");
103 }
104
105 /*
106  * Prints a line and logs it to file. Every line can be at max 80 characters.
107  *
108  */
109 static void print_log(char *name, int item_price, int einheiten, char *nickname,
110                       char *rest) {
111   char *time = get_time();
112   char price[10];
113   /* Format:
114      Transaction-ID (Anzahl verkaufter Einträge, inklusive des zu druckenden!)
115      -- 6-stellig
116      Uhrzeit -- 8-stellig
117      Eintragname (= Getränk) -- 9-stellig
118      Preis (in Cents) -- 9-stellig
119      Anzahl -- 2-stellig
120      Nickname (falls es vom Guthaben abgezogen wird) -- 10-stellig
121      restguthaben (9-stellig)
122
123      + 7 leerzeichen
124      --> 48 zeichen
125      */
126   if (format_euro(price, 10, item_price) == NULL) {
127     cprintf("Preis %d konnte nicht umgerechnet werden\r\n", item_price);
128     exit(1);
129   }
130
131   sprintf(print_buffer, "%c[%3u] %s - %-" xstr(
132                             MAX_ITEM_NAME_LENGTH) "s - %s - %s - %d - an %s\r",
133           17, status.transaction_id, time, name, price, rest, einheiten,
134           (*nickname != '\0' ? nickname : "Unbekannt"));
135   status.transaction_id++;
136   print_the_buffer();
137 }
138
139 /* dialog which is called for each bought item */
140 static signed int buy(char *name, unsigned int price) {
141   int negative = 1;
142   char entered[5] = {'1', 0, 0, 0, 0};
143   BYTE i = 0, matches = 0;
144   BYTE c, x, y, nickname_len;
145   int einheiten;
146   char nickname[NICKNAME_MAX_LEN + 1];
147   char rest[11];
148   struct credits_t *credit;
149
150   memset(nickname, '\0', sizeof(nickname));
151   memset(rest, ' ', sizeof(rest));
152   rest[8] = '\0';
153
154   clrscr();
155   cprintf("Wieviel Einheiten \"%s\"? [1] \r\n", name);
156   x = wherex();
157   y = wherey();
158   while (1) {
159     /* Buffer-Ende erreicht? */
160     if (i == 4)
161       break;
162
163     c = cgetc();
164     /* Enter */
165     if (c == PETSCII_CR)
166       break;
167     /* Backspace */
168     if (c == PETSCII_DEL) {
169       if (i == 0)
170         continue;
171       entered[--i] = '\0';
172       cputcxy(x + i, y, ' ');
173       gotoxy(x + i, y);
174       continue;
175     }
176     if (c == 27) {
177       cprintf("Kauf abgebrochen, druecke RETURN...\r\n");
178       get_input();
179       return 1;
180     }
181     if (c == '-' && i == 0) {
182       negative = -1;
183       cputc(c);
184     } else if (c >= PETSCII_0 && c <= PETSCII_9) {
185       entered[i++] = c;
186       cputc(c);
187     }
188
189     /* Ungültige Eingabe (keine Ziffer), einfach ignorieren */
190   }
191   einheiten = atoi(entered) * negative;
192
193   if (einheiten > 100 || einheiten < -100 || einheiten == 0) {
194     cprintf("\r\nEinheit nicht in [-100, 100] oder 0, Abbruch, druecke "
195             "RETURN...\r\n");
196     cgetc();
197     return 1;
198   }
199
200   toggle_videomode();
201   cprintf("\r\n             *** VERKAUF ***\r\n\r\n");
202   cprintf("%dx %s", einheiten, name);
203   toggle_videomode();
204
205   cprintf("\r\nAuf ein Guthaben kaufen? Wenn ja, Nickname eingeben:\r\n");
206   {
207     BYTE i;
208     BYTE x;
209     BYTE y;
210     BYTE matches;
211     char *uniquematch;
212     input_terminator_t terminator;
213     while (1) {
214       terminator = get_input_terminated_by(INPUT_TERMINATOR_RETURN |
215                                                INPUT_TERMINATOR_SPACE,
216                                            nickname, sizeof(nickname));
217
218       /* Clear the screen from any previous completions */
219       x = wherex();
220       y = wherey();
221       for (i = 1; i < 7; i++) {
222         /* "Completion:" is longer than NICKNAME_MAX_LEN */
223         cclearxy(0, y + i, strlen("Completion:"));
224       }
225       gotoxy(x, y);
226
227       if (terminator != INPUT_TERMINATOR_SPACE) {
228         break;
229       }
230
231       matches = 0;
232       uniquematch = NULL;
233       for (i = 0; i < credits.num_items; i++) {
234         if (strncmp(nickname, credits.credits[i].nickname, strlen(nickname)) !=
235             0) {
236           continue;
237         }
238         matches++;
239         if (matches > 1) {
240           break;
241         }
242         uniquematch = credits.credits[i].nickname;
243       }
244       if (matches == 1) {
245         /* Display the rest of the nickname */
246         textcolor(TC_LIGHT_GREEN);
247         cprintf("%s", uniquematch + strlen(nickname));
248         textcolor(TC_LIGHT_GRAY);
249         strcat(nickname, uniquematch + strlen(nickname));
250       } else {
251         /* Multiple nicknames match what was entered so far. Abort and
252          * display all matches, then prompt the user again. */
253         char completion[NICKNAME_MAX_LEN + 1];
254         BYTE len = strlen(nickname);
255         x = wherex();
256         y = wherey();
257         cprintf("\r\nCompletion:\r\n");
258         matches = 0;
259         for (i = 0; i < credits.num_items; i++) {
260           if (strncmp(nickname, credits.credits[i].nickname, len) != 0) {
261             continue;
262           }
263           if (++matches == 5) {
264             cprintf("...\r\n");
265             break;
266           }
267           strcpy(completion, credits.credits[i].nickname);
268           *(completion + len) = '\0';
269           cprintf("%s", completion);
270           textcolor(TC_LIGHT_GREEN);
271           cprintf("%c", *(credits.credits[i].nickname + len));
272           textcolor(TC_LIGHT_GRAY);
273           cprintf("%s\r\n", completion + len + 1);
274         }
275         gotoxy(x, y);
276       }
277     }
278   }
279   if (*nickname != '\0') {
280     toggle_videomode();
281     cprintf(" fuer %s\r\n", nickname);
282     toggle_videomode();
283   }
284
285   if (*nickname != '\0' && *nickname != 32) {
286     nickname_len = strlen(nickname);
287     /* go through credits and remove the amount of money or set nickname
288      * to NULL if no such credit could be found */
289     credit = find_credit(nickname);
290     if (credit != NULL) {
291       while ((signed int)credit->credit < ((signed int)price * einheiten)) {
292         if (format_euro(rest, 10, credit->credit) == NULL) {
293           cprintf("Preis %d konnte nicht umgerechnet werden\r\n",
294                   credit->credit);
295           exit(1);
296         }
297         cprintf(
298             "\r\n%s hat nicht genug Geld (%s). e) einzahlen a) abbruch \r\n",
299             nickname, rest);
300         c = cgetc();
301         if (c == 'e') {
302           deposit_credit(nickname);
303         } else {
304           return 0;
305         }
306       }
307       /* substract money */
308       credit->credit -= (price * einheiten);
309
310       if (format_euro(rest, 10, credit->credit) == NULL) {
311         cprintf("Preis %d konnte nicht umgerechnet werden\r\n", credit->credit);
312         exit(1);
313       }
314
315       textcolor(TC_LIGHT_GREEN);
316       cprintf("\r\nVerbleibendes Guthaben fuer %s: %s. Druecke RETURN...\r\n",
317               nickname, rest);
318       textcolor(TC_LIGHT_GRAY);
319       toggle_videomode();
320       cprintf("\r\nDein Guthaben betraegt noch %s.\r\n", rest);
321       toggle_videomode();
322       get_input();
323       matches++;
324     } else {
325       textcolor(TC_LIGHT_RED);
326       cprintf("\r\nNickname nicht gefunden in der Guthabenverwaltung! Abbruch, "
327               "druecke RETURN...\r\n");
328       textcolor(TC_LIGHT_GRAY);
329       get_input();
330       return 0;
331     }
332   } else {
333     /* Ensure that nickname is NULL if it's empty because it's used in print_log
334      */
335     *nickname = '\0';
336   }
337
338   money += price * einheiten;
339   items_sold += einheiten;
340   if (printing == 1)
341     print_log(name, price, einheiten, nickname, rest);
342
343   return einheiten;
344 }
345
346 void buy_stock(BYTE n) {
347   if (n >= status.num_items || status.status[n].item_name == NULL) {
348     cprintf("FEHLER: Diese Einheit existiert nicht.\r\n");
349     get_input();
350     return;
351   }
352
353   status.status[n].times_sold +=
354       buy(status.status[n].item_name, status.status[n].price);
355 }
356
357 void buy_custom(void) {
358   BYTE c = 0, i = 0;
359   int negative = 1;
360   char entered[5] = {'1', 0, 0, 0, 0};
361   char *input, name[20];
362   int price;
363
364   clrscr();
365   memset(name, '\0', 20);
366   cprintf("\r\nWas soll gekauft werden?\r\n");
367   input = get_input();
368   strncpy(name, input, 20);
369   if (*name == '\0')
370     return;
371
372   cprintf("\r\nWie teuer ist \"%s\" (in cents)?\r\n", name);
373   while (1) {
374     c = cgetc();
375     if (c == 13)
376       break;
377     cputc(c);
378     if (c == 27) {
379       cprintf("Kauf abgebrochen, druecke RETURN...\r\n");
380       get_input();
381       return;
382     } else if (c == '-' && i == 0)
383       negative = -1;
384     else if (c > 47 && c < 58)
385       entered[i++] = c;
386   }
387   price = atoi(entered) * negative;
388
389   cprintf("\r\n");
390
391   buy(name, price);
392 }
393
394 void set_time_interactive(void) {
395   BYTE part[3] = {'0', '0', '\0'};
396   BYTE tp1, tp2, tp3;
397   char *time_input, *time;
398   cprintf("Gib die aktuelle Uhrzeit ein (Format HHMMSS):\r\n");
399   time_input = get_input();
400   part[0] = time_input[0];
401   part[1] = time_input[1];
402   tp1 = atoi(part);
403   part[0] = time_input[2];
404   part[1] = time_input[3];
405   tp2 = atoi(part);
406   part[0] = time_input[4];
407   part[1] = time_input[5];
408   tp3 = atoi(part);
409   set_time(tp1, tp2, tp3);
410
411   time = get_time();
412   cprintf("\r\nZeit gesetzt: %s\r\n", time);
413 }
414
415 int main(void) {
416   char *c;
417   char *time;
418
419   if (VIDEOMODE == 40)
420     toggle_videomode();
421   clrscr();
422
423   /* Allocate logging buffer memory */
424   init_log();
425
426   /* Set time initially, c128 doesn't know it */
427   set_time_interactive();
428
429   POKE(216, 255);
430
431   /* Load configuration */
432   load_config();
433
434   /* Load items (= drinks) */
435   load_items();
436   /* Load credits */
437   load_credits();
438
439   time = get_time();
440   sprintf(print_buffer, "%c----------------------------------------------------"
441                         "----------------------------\r",
442           17);
443   print_the_buffer();
444   sprintf(print_buffer, "%cC128-Kasse Version " GV "\r", 17);
445   print_the_buffer();
446
447   sprintf(print_buffer,
448           "%cKasse gestartet um %s. Nutze logfile log-%u, offset %d.\r", 17,
449           time, log_num, log_heap_offset);
450   print_the_buffer();
451
452   print_header();
453
454   while (1) {
455     print_screen();
456     c = get_input();
457     /* ...display dialogs eventually */
458     if (*c > 47 && *c < 58) {
459       /* if the input starts with a digit, we will interpret it as a number
460        * for the item to be sold */
461       buy_stock(atoi(c));
462       toggle_videomode();
463       clrscr();
464       toggle_videomode();
465     } else if (*c == 'f') {
466       buy_custom();
467       toggle_videomode();
468       clrscr();
469       toggle_videomode();
470     } else if (*c == 's') {
471       save_items();
472       save_credits();
473       log_flush();
474       cprintf("\r\nStatefile/Creditfile/Log gesichert, druecke RETURN...\r\n");
475       get_input();
476     } else if (*c == 'g') {
477       credit_manager();
478     } else if (*c == 'z') {
479       set_time_interactive();
480     } else if (*c == 'q')
481       break;
482   }
483   clrscr();
484   cprintf("\r\nBYEBYE\r\n");
485
486   return 0;
487 }