Blob


1 /*
2 * Copyright (c) 2021 Matthias Schmidt <xhr@giessen.ccc.de>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
17 #include <errno.h>
18 #include <limits.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <stdlib.h>
24 #include "isscrolls.h"
26 #define MAXTOKENS 3
28 static const char *odds[] = {
29 "Almost certain",
30 "Likely",
31 "50/50",
32 "Unlikely",
33 "Small chance",
34 NULL
35 };
37 void
38 cmd_gather_information(char *cmd)
39 {
40 struct character *curchar = get_current_character();
41 int ival[2] = { -1, -1 };
42 int ret;
44 if (curchar == NULL) {
45 printf("No character loaded. Use 'cd' to load a character\n");
46 return;
47 }
49 ival[0] = curchar->wits;
50 ival[1] = get_int_from_cmd(cmd);
52 ret = action_roll(ival);
53 if (ret == 8) { /* strong hit */
54 change_char_value("momentum", INCREASE, 2);
55 } else if (ret == 4) { /* weak hit */
56 change_char_value("momentum", INCREASE, 1);
57 } else
58 printf("Pay the price -> Rulebook\n");
59 }
61 void
62 cmd_sojourn(char *cmd)
63 {
64 struct character *curchar = get_current_character();
65 int ival[2] = { -1, -1 };
66 int ret;
68 if (curchar == NULL) {
69 printf("No character loaded. Use 'cd' to load a character\n");
70 return;
71 }
73 ival[0] = curchar->heart;
74 ival[1] = get_int_from_cmd(cmd);
76 ret = action_roll(ival);
77 if (ret == 8) { /* strong hit */
78 printf("You may choose two optionsresults -> Rulebook\n");
79 } else if (ret == 4) { /* weak hit */
80 printf("You may choose one result -> Rulebook\n");
81 } else
82 printf("Pay the price -> Rulebook\n");
83 }
85 void
86 cmd_draw_the_circle(char *cmd)
87 {
88 struct character *curchar = get_current_character();
89 int ival[2] = { -1, -1 };
90 int ret;
92 if (curchar == NULL) {
93 printf("No character loaded. Use 'cd' to load a character\n");
94 return;
95 }
97 ival[0] = curchar->heart;
98 ival[1] = get_int_from_cmd(cmd);
100 ret = action_roll(ival);
101 if (ret == 8) {
102 change_char_value("momentum", INCREASE, 1);
103 printf("You may choose even more boasts -> Rulebook\n");
104 } else if (ret == 4) {
105 printf("You may choose one boasts -> Rulebook\n");
106 } else
107 printf("Pay the price -> Rulebook\n");
110 void
111 cmd_swear_an_iron_vow(char *cmd)
113 struct character *curchar = get_current_character();
114 int ival[2] = { -1, -1 };
115 int ret;
117 if (curchar == NULL) {
118 printf("No character loaded. Use 'cd' to load a character\n");
119 return;
122 ival[0] = curchar->heart;
123 ival[1] = get_int_from_cmd(cmd);
125 ret = action_roll(ival);
126 if (ret == 8) {
127 change_char_value("momentum", INCREASE, 2);
128 printf("You are emboldened and know what you must do next\n");
129 } else if (ret == 4) {
130 change_char_value("momentum", INCREASE, 1);
131 printf("You are determined but begin your quest with questions\n");
132 } else
133 printf("You face a significant obstacle -> Rulebook\n");
136 void
137 cmd_forge_a_bond(char *cmd)
139 struct character *curchar = get_current_character();
140 int ival[2] = { -1, -1 };
141 int ret;
143 if (curchar == NULL) {
144 printf("No character loaded. Use 'cd' to load a character\n");
145 return;
148 ival[0] = curchar->heart;
149 ival[1] = get_int_from_cmd(cmd);
151 ret = action_roll(ival);
152 if (ret == 8) {
153 printf("You forge a bind and choose one option -> Rulebook\n");
154 curchar->bonds += 0.25;
155 } else if (ret == 4) {
156 printf("They ask something from you first -> Rulebook\n");
157 } else
158 printf("You are refused. Pay the price -> Rulebook\n");
161 void
162 cmd_test_your_bond(char *cmd)
164 struct character *curchar = get_current_character();
165 int ival[2] = { -1, -1 };
166 int ret;
168 if (curchar == NULL) {
169 printf("No character loaded. Use 'cd' to load a character\n");
170 return;
173 if (curchar->bonds == 0) {
174 printf("You have no bonds forged. Please do so first\n");
175 return;
178 ival[0] = curchar->heart;
179 ival[1] = get_int_from_cmd(cmd);
181 ret = action_roll(ival);
182 if (ret == 8) {
183 printf("You may choose one boost -> Rulebook\n");
184 } else if (ret == 4) {
185 printf("Your bond is fragile -> Rulebook\n");
186 } else {
187 printf("Your bond is cleared. Pay the price -> Rulebook\n");
188 curchar->bonds -= 0.25;
192 void
193 cmd_endure_stress(char *cmd)
195 struct character *curchar = get_current_character();
196 int ival[2] = { -1, -1 };
197 int ret, hr;
199 if (curchar == NULL) {
200 printf("No character loaded. Use 'cd' to load a character\n");
201 return;
204 ival[1] = get_int_from_cmd(cmd);
205 if (ival[1] == -1) {
206 printf("Please provide a number as argument\n\n");
207 printf("The number is the amount of stress you suffer\n");
208 printf("Example: endurestress 2\n");
209 return;
212 hr = curchar->spirit - ival[1];
213 if (hr >= 0) {
214 curchar->spirit -= ival[1];
215 printf("You suffer -%d spirit and it is down to %d\n",
216 ival[1], curchar->spirit);
217 } else if (hr < 0) {
218 /* Spirit is 0, so suffer -momentum equal to remaining health */
219 log_debug("hr < 0: %d\n", hr);
220 curchar->spirit = 0;
221 curchar->momentum -= (hr * (-1));
222 printf("You suffer -%d spirt and since your spirit is 0, your "\
223 "momentum is down to %d\n", ival[1], curchar->momentum);
226 /* Reset ival[1] since we need no bonus for the roll */
227 ival[1] = -1;
228 ival[0] = curchar->heart;
229 if (curchar->spirit > curchar->heart) {
230 ival[0] = curchar->spirit;
233 ret = action_roll(ival);
234 if (ret == 8) {
235 printf("You need to choose one option -> Rulebook\n");
236 } else if (ret == 4) {
237 printf("You press on\n");
238 } else {
239 change_char_value("momentum", DECREASE, 1);
240 if (curchar->health == 0)
241 printf("Mark either shaken or corrupted or roll on the oracle table -> Rulebook\n");
245 void
246 cmd_face_death(char *cmd)
248 struct character *curchar = get_current_character();
249 int ival[2] = { -1, -1 };
250 int ret;
252 if (curchar == NULL) {
253 printf("No character loaded. Use 'cd' to load a character\n");
254 return;
257 ival[0] = curchar->heart;
258 ival[1] = get_int_from_cmd(cmd);
260 ret = action_roll(ival);
261 if (ret == 8) {
262 printf("Death rejects you.\n");
263 } else if (ret == 4) {
264 printf("Your must choose one option -> Rulebook\n");
265 } else {
266 printf("You are dead\n");
267 curchar->dead = 1;
271 void
272 cmd_heal(char *who)
274 struct character *curchar = get_current_character();
275 int ival[2] = { -1, -1 };
276 int ret;
278 if (curchar == NULL) {
279 printf("No character loaded. Use 'cd' to load a character\n");
280 return;
283 if (strlen(who) == 0) {
284 info:
285 printf("Please specify who to heal\n\n");
286 printf("me\t- heal yourself (roll against Iron or Wits (whatever is lower))\n");
287 printf("others\t- heal others (roll against Wits)\n\n");
288 printf("Example: heal me\n");
289 return;
292 if (strcasecmp(who, "me") == 0) {
293 if (curchar->iron < curchar->wits)
294 ival[0] = curchar->iron;
295 else if (curchar->iron > curchar->wits)
296 ival[0] = curchar->wits;
297 else
298 ival[0] = curchar->wits;
299 } else if (strcasecmp(who, "others") == 0) {
300 ival[0] = curchar->wits;
301 } else
302 goto info;
304 ret = action_roll(ival);
305 if (ret == 8) { /* strong hit */
306 change_char_value("health", INCREASE, 2);
307 } else if (ret == 4) { /* weak hit */
308 change_char_value("health", INCREASE, 1);
309 change_char_value("momentum", DECREASE, 1);
310 } else
311 printf("Pay the price -> Rulebook\n");
314 void
315 cmd_roll_action_dice(char *cmd)
317 char *ep;
318 char *tokens[MAXTOKENS];
319 char *p, *last;
320 int i = 0;
321 long lval[2];
322 int ival[2] = { -1, -1 };
324 if (cmd == NULL || strlen(cmd) == 0) {
325 printf("Please provide at least one attribute value\n\n");
326 printf("> action <attribute value> [bonus value]\n\n");
327 printf("Examples:\n");
328 printf("> action 3\t- Add 3 to the D6 die\n");
329 printf("> action 4 1\t- Add 4 and additionally 1 to the D6 die\n\n");
331 return;
334 log_debug("cmd: %s\n", cmd);
336 /* Parse the argument line into 2 tokens, separated by space */
337 for ((p = strtok_r(cmd, " ", &last)); p;
338 (p = strtok_r(NULL, " ", &last))) {
339 if (i < MAXTOKENS - 1)
340 tokens[i++] = p;
342 tokens[i] = NULL;
344 for (i=0; tokens[i] != NULL; i++) {
345 log_debug("token %d: %s\n", i, tokens[i]);
347 errno = 0;
348 lval[i] = strtol(tokens[i], &ep, 10);
349 if (cmd[0] == '\0' || *ep != '\0') {
350 printf("Please provide a number as argument\n");
351 return;
353 if ((errno == ERANGE || lval[i] <= 0 || lval[i] > 10)) {
354 printf("Please provide a number between 1 and 10\n");
355 return;
357 ival[i] = lval[i];
360 action_roll(ival);
363 void
364 cmd_resupply(char *cmd)
366 struct character *curchar = get_current_character();
367 int ival[2] = { -1, -1 };
368 int ret;
370 if (curchar == NULL) {
371 printf("No character loaded. Use 'cd' to load a character\n");
372 return;
375 ival[0] = curchar->wits;
376 ival[1] = get_int_from_cmd(cmd);
378 ret = action_roll(ival);
379 if (ret == 8) { /* strong hit */
380 change_char_value("momentum", INCREASE, 2);
381 } else if (ret == 4) { /* weak hit */
382 printf("Take up to +2 supply, but suffer -1 momentum for each\n");
383 } else
384 printf("Pay the price -> Rulebook\n");
388 void
389 cmd_make_camp(char *cmd)
391 struct character *curchar = get_current_character();
392 int ival[2] = { -1, -1 };
393 int ret;
395 if (curchar == NULL) {
396 printf("No character loaded. Use 'cd' to load a character\n");
397 return;
400 ival[0] = curchar->supply;
401 ival[1] = get_int_from_cmd(cmd);
403 ret = action_roll(ival);
404 if (ret == 8)
405 printf("Choose two options-> Rulebook\n");
406 else if (ret == 4)
407 printf("Choose one option-> Rulebook\n");
408 else
409 printf("Pay the price -> Rulebook\n");
412 void
413 cmd_face_danger(char *stat)
415 struct character *curchar = get_current_character();
416 int ival[2] = { -1, -1 };
417 int ret;
419 if (curchar == NULL) {
420 printf("No character loaded. Use 'cd' to load a character\n");
421 return;
424 if (strlen(stat) == 0) {
425 info:
426 printf("Please specify the stat you'd like to use in this move\n\n");
427 printf("edge\t- Act with speed, agility, or precision\n");
428 printf("heart\t- Act with charm, loyalty, or courage\n");
429 printf("iron\t- Act with aggressive action, forceful defense, strength\n");
430 printf("shadow\t- Act with deception, stealth, or trickery\n");
431 printf("wits\t- Act with expertise, insight, or observation\n\n");
432 printf("Example: facedanger iron\n");
433 return;
436 if (strcasecmp(stat, "iron") == 0) {
437 ival[0] = curchar->iron;
438 } else if (strcasecmp(stat, "wits") == 0) {
439 ival[0] = curchar->wits;
440 } else if (strcasecmp(stat, "edge") == 0) {
441 ival[0] = curchar->edge;
442 } else if (strcasecmp(stat, "shadow") == 0) {
443 ival[0] = curchar->shadow;
444 } else if (strcasecmp(stat, "heart") == 0) {
445 ival[0] = curchar->heart;
446 } else
447 goto info;
449 ret = action_roll(ival);
450 if (ret == 8) /* strong hit */
451 change_char_value("momentum", INCREASE, 1);
452 else if (ret == 4) /* weak hit */
453 printf("Face a troublesome cost -> Rulebook\n");
454 else
455 printf("Pay the price -> Rulebook\n");
458 void
459 cmd_compel(char *cmd)
461 struct character *curchar = get_current_character();
462 char stat[MAX_STAT_LEN];
463 int ival[2] = { -1, -1 };
464 int ret;
466 if (curchar == NULL) {
467 printf("No character loaded. Use 'cd' to load a character\n");
468 return;
471 ret = get_args_from_cmd(cmd, stat, &ival[1]);
472 if (ret >= 10) {
473 info:
474 printf("Please specify the stat you'd like to use in this move\n\n");
475 printf("heart\t- You charm, pacify, barter, or convince\n");
476 printf("iron\t- You threaten or incite\n");
477 printf("shadow\t- You lie or swindle\n");
478 printf("Example: compel iron\n");
479 return;
480 } else if (ret <= -20)
481 return;
483 if (strcasecmp(stat, "iron") == 0) {
484 ival[0] = curchar->iron;
485 } else if (strcasecmp(stat, "shadow") == 0) {
486 ival[0] = curchar->shadow;
487 } else if (strcasecmp(stat, "heart") == 0) {
488 ival[0] = curchar->heart;
489 } else
490 goto info;
492 ret = action_roll(ival);
493 if (ret == 8) {
494 change_char_value("momentum", INCREASE, 1);
495 printf("You might get +1 for your next move -> Rulebook\n");
496 } else if (ret == 4) {
497 change_char_value("momentum", INCREASE, 1);
498 printf("You might be asked for something in return -> Rulebook\n");
499 } else
500 printf("Pay the price -> Rulebook\n");
503 void
504 cmd_secure_an_advantage(char *cmd)
506 struct character *curchar = get_current_character();
507 char stat[MAX_STAT_LEN];
508 int ival[2] = { -1, -1 };
509 int ret;
511 if (curchar == NULL) {
512 printf("No character loaded. Use 'cd' to load a character\n");
513 return;
516 ret = get_args_from_cmd(cmd, stat, &ival[1]);
517 if (ret >= 10) {
518 info:
519 printf("Please specify the stat you'd like to use in this move\n\n");
520 printf("edge\t- Act with speed, agility, or precision\n");
521 printf("heart\t- Act with charm, loyalty, or courage\n");
522 printf("iron\t- Act with aggressive action, forceful defense, strength\n");
523 printf("shadow\t- Act with deception, stealth, or trickery\n");
524 printf("wits\t- Act with expertise, insight, or observation\n\n");
525 printf("Example: secureanadvantage iron\n");
526 return;
527 } else if (ret <= -20)
528 return;
530 if (strcasecmp(stat, "iron") == 0) {
531 ival[0] = curchar->iron;
532 } else if (strcasecmp(stat, "wits") == 0) {
533 ival[0] = curchar->wits;
534 } else if (strcasecmp(stat, "edge") == 0) {
535 ival[0] = curchar->edge;
536 } else if (strcasecmp(stat, "shadow") == 0) {
537 ival[0] = curchar->shadow;
538 } else if (strcasecmp(stat, "heart") == 0) {
539 ival[0] = curchar->heart;
540 } else
541 goto info;
543 ret = action_roll(ival);
544 if (ret == 8)
545 printf("Gain an advantage -> Rulebook\n");
546 else if (ret == 4)
547 change_char_value("momentum", INCREASE, 1);
548 else
549 printf("Pay the price -> Rulebook\n");
552 void
553 cmd_roll_challenge_die(__attribute__((unused)) char *unused)
555 printf("%ld\n", roll_challenge_die());
558 void
559 cmd_roll_oracle_die(__attribute__((unused)) char *unused)
561 printf("%ld\n", roll_oracle_die());
564 long
565 roll_action_die()
567 long ret = random() % 6;
569 return ret == 0 ? 6 : ret;
572 long
573 roll_challenge_die()
575 return random() % 10;
578 long
579 roll_oracle_die()
581 return random() % 100;
584 void
585 cmd_yes_or_no(char *args)
587 int num = atoi(args);
588 int i;
590 log_debug("Argument %d\n", num);
591 if (num <= 0 || num > 5) {
592 printf("Provide a number between 1-5 as argument, i.e. yesorno 2\n\n");
593 for (i=0; odds[i] != NULL; i++)
594 printf("%d - %s\n", i+1, odds[i]);
596 return;
599 yes_or_no(num);
602 void
603 yes_or_no(int num)
605 long a1, c1, c2;
607 a1 = roll_challenge_die();
608 c2 = roll_challenge_die();
609 c1 = (a1 * 10) + c2;
611 if (a1 == c2)
612 printf("W10: match %ld -> ", a1);
613 else {
614 printf("W10: %ld, %ld -> ", a1, c2);
617 if (num == 1 && c1 >= 11)
618 printf("yes\n");
619 else if (num == 2 && c1 >= 26)
620 printf("yes\n");
621 else if (num == 3 && c1 >= 51)
622 printf("yes\n");
623 else if (num == 4 && c1 >= 76)
624 printf("yes\n");
625 else if (num == 5 && c1 >= 91)
626 printf("yes\n");
627 else
628 printf("no\n");
631 int
632 action_roll(int args[2])
634 long c1, c2, a1, b;
635 int ret = 0;
637 log_debug("Action args: %d, %d\n", args[0], args[1]);
639 if (args[0] == -1) {
640 log_errx(1, "No attribute value provided. This should not happen!");
643 a1 = b = roll_action_die();
644 /* Add attribute and maybe a bonus value */
645 b += args[0];
646 if (args[1] != -1)
647 b += args[1];
649 if (args[1] == -1)
650 printf("D6: %ld+%d=%ld ", a1, args[0], b);
651 else
652 printf("D6: %ld+%d+%d=%ld ", a1, args[0], args[1], b);
654 c1 = roll_challenge_die();
655 c2 = roll_challenge_die();
656 c1 = (c1 == 0 ? 10 : c1);
657 c2 = (c2 == 0 ? 10 : c2);
659 if (c1 == c2) {
660 printf("D10: %ld match -> ", c1);
661 } else {
662 printf("D10: %ld, %ld -> ", c1, c2);
665 if (b <= c1 && b <= c2) {
666 pm(RED, "miss\n");
667 ret = 2;
668 } else if (b <= c1 || b <= c2) {
669 pm(YELLOW, "weak hit\n");
670 ret = 4;
671 } else if (b > c1 && b > c2) {
672 pm(GREEN, "strong hit\n");
673 ret = 8;
676 return ret;
679 int
680 progress_roll(double args[2])
682 double b;
683 long c1, c2;
684 int ret = 0;
686 if (args[0] == -1) {
687 log_errx(1, "No attribute value provided. This should not happen!");
690 c1 = roll_challenge_die();
691 c2 = roll_challenge_die();
692 c1 = (c1 == 0 ? 10 : c1);
693 c2 = (c2 == 0 ? 10 : c2);
695 log_debug("args[0] %.2lf args[1] %.2lf\n", args[0], args[1]);
697 b = args[0];
698 if (args[1] != -1)
699 b += args[1];
701 if (c1 == c2) {
702 printf("D10: %ld match vs ", c1);
703 } else {
704 printf("D10: %ld, %ld vs ", c1, c2);
707 printf("Progress: %.2lf -> ", b);
709 if (b <= c1 && b <= c2) {
710 pm(RED, "miss\n");
711 ret = 2;
712 } else if (b <= c1 || b <= c2) {
713 pm(YELLOW, "weak hit\n");
714 ret = 4;
715 } else if (b > c1 && b > c2) {
716 pm(GREEN, "strong hit\n");
717 ret = 8;
720 return ret;
723 int
724 get_int_from_cmd(const char *cmd)
726 char *ep;
727 long lval;
728 int ival = -1;
730 if (strlen(cmd) > 0) {
731 errno = 0;
732 lval = strtol(cmd, &ep, 10);
733 if (cmd[0] == '\0' || *ep != '\0') {
734 printf("Please provide a number as argument\n");
735 return ival;
737 if ((errno == ERANGE || lval <= 0 || lval > 10)) {
738 printf("Please provide a number between 1 and 10\n");
739 return ival;
742 ival = lval;
743 log_debug("Arg provided %d\n", ival);
746 return ival;
749 int
750 get_args_from_cmd(char *cmd, char *stat, int *ival)
752 char *tokens[MAXTOKENS];
753 char *ep;
754 char *p, *last;
755 int i = 0;
756 long lval = -1;
758 /* Parse the argument line into max tokens, separated by space */
759 for ((p = strtok_r(cmd, " ", &last)); p;
760 (p = strtok_r(NULL, " ", &last))) {
761 if (i < MAXTOKENS - 1)
762 tokens[i++] = p;
764 tokens[i] = NULL;
766 /* First token is a stat */
767 i = 0;
768 if (tokens[i] == NULL)
769 return 10;
770 else if (strlen(tokens[i]) == 0)
771 return 11;
773 log_debug("stat token: %s\n", tokens[i]);
774 snprintf(stat, MAX_STAT_LEN, "%s", tokens[i]);
776 /* Second token is a bonus value*/
777 errno = 0;
778 i++;
779 /* It's OK to have no bonus, so the token can be NULL and we can return */
780 if (tokens[i] == NULL)
781 return 0;
782 else
783 log_debug("bonus token: %s\n", tokens[i]);
785 lval = strtol(tokens[i], &ep, 10);
786 if (cmd[0] == '\0' || *ep != '\0') {
787 printf("Please provide a number as argument\n");
788 return -20;
790 if ((errno == ERANGE || lval <= 0 || lval > 10)) {
791 printf("Please provide a number between 1 and 10\n");
792 return -22;
794 *ival = lval;
796 return 0;