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 <json-c/json.h>
19 #include <errno.h>
20 #include <limits.h>
21 #include <string.h>
23 #include "isscrolls.h"
25 void
26 cmd_enter_the_fray(char *cmd)
27 {
28 struct character *curchar = get_current_character();
29 char stat[MAX_STAT_LEN];
30 int ival[2] = { -1, -1 };
31 int ret;
33 if (curchar == NULL) {
34 printf("No character loaded. Use 'cd' to load a character\n");
35 return;
36 }
38 ret = get_args_from_cmd(cmd, stat, &ival[1]);
39 if (ret >= 10) {
40 info:
41 printf("\nPlease specify the stat you'd like to use in this move\n\n");
42 printf("heart\t- You are facing off against your foe\n");
43 printf("shadow \t- You are moving into position against or strike without warning\n");
44 printf("wits\t- You are ambushed\n");
45 printf("Example: enterthefray wits\n\n");
46 return;
47 } else if (ret <= -20) {
48 return;
49 }
51 if (strcasecmp(stat, "wits") == 0) {
52 ival[0] = curchar->wits;
53 } else if (strcasecmp(stat, "shadow") == 0) {
54 ival[0] = curchar->shadow;
55 } else if (strcasecmp(stat, "heart") == 0) {
56 ival[0] = curchar->heart;
57 } else
58 goto info;
60 if (curchar->fight_active == 0) {
61 ask_for_fight_difficulty();
62 curchar->fight_active = 1;
63 } else {
64 printf("You are already in a fight\n");
65 return;
66 }
68 ret = action_roll(ival);
69 if (ret == 8) {
70 change_char_value("momentum", INCREASE, 2);
71 set_initiative(1);
72 printf("You have initiative\n");
73 } else if (ret == 4) {
74 printf("You may choose one boost -> Rulebook\n");
75 } else
76 printf("Pay the price -> Rulebook\n");
78 update_prompt();
79 }
81 void
82 cmd_end_the_fight(char *cmd)
83 {
84 struct character *curchar = get_current_character();
85 double dval[2] = { -1.0, -1.0 };
86 int ret;
88 if (curchar == NULL) {
89 printf("No character loaded. Use 'cd' to load a character\n");
90 return;
91 }
93 if (curchar->fight_active == 0) {
94 printf("You are not in a fight. Enter one with enterthefray\n");
95 return;
96 }
98 dval[0] = curchar->fight->progress;
99 dval[1] = get_int_from_cmd(cmd);
100 ret = progress_roll(dval);
101 if (ret == 8) {
102 printf("The foe is no longer in the fight -> Rulebook\n");
103 } else if (ret == 4) {
104 printf("The foe is no longer in the fight, but you must chose one option -> Rulebook\n");
105 } else {
106 printf("You lost the fight. Pay the price -> Rulebook\n");
108 curchar->fight_active = 0;
109 curchar->fight->progress = 0;
110 delete_fight(curchar->id);
111 update_prompt();
114 void
115 cmd_endure_harm(char *cmd)
117 struct character *curchar = get_current_character();
118 int ival[2] = { -1, -1 };
119 int ret, hr, suffer;
121 if (curchar == NULL) {
122 printf("No character loaded. Use 'cd' to load a character\n");
123 return;
126 suffer = 0;
128 /* We are in a fight, so we can suffer harm equal to our foe's rank */
129 if (curchar->fight_active == 1) {
130 hr = curchar->health - curchar->fight->difficulty;
131 suffer = curchar->fight->difficulty;
132 } else {
133 /* We are not in a fight, so the player can specify the amount of
134 * harm to suffer */
135 ival[1] = get_int_from_cmd(cmd);
136 if (ival[1] == -1) {
137 /* We are not in a fight and there is not argument provided */
138 printf("Please specify the amount of harm you want to suffer\n\n");
139 printf("Example: endureharm 2\n");
140 return;
143 hr = curchar->health - ival[1];
144 suffer = ival[1];
145 log_debug("Arg provided %d, hr: %d\n", ival[1], hr);
146 /* Reset ival[1] since we don't need a bonus */
147 ival[1] = -1;
150 if (hr >= 0) {
151 curchar->health -= suffer;
152 printf("You suffer %d harm and your health is down to %d\n",
153 suffer, curchar->health);
154 } else if (hr < 0) {
155 /* Health is 0, so suffer -momentum equal to remaining health */
156 log_debug("hr < 0: %d\n", hr);
157 curchar->health = 0;
158 curchar->momentum -= (hr * (-1));
159 printf("You suffer %d harm and since your health is %d, your "\
160 "momentum is down to %d\n",
161 suffer, curchar->health,
162 curchar->momentum);
165 ival[0] = curchar->iron;
166 if (curchar->heart > curchar->iron) {
167 ival[0] = curchar->heart;
170 ret = action_roll(ival);
171 if (ret == 8) {
172 printf("You need to choose one option -> Rulebook\n");
173 } else if (ret == 4) {
174 printf("You press on\n");
175 } else {
176 change_char_value("momentum", DECREASE, 1);
177 if (curchar->health == 0)
178 printf("Mark either maimed or wounded or on the oracle table -> Rulebook\n");
182 void
183 cmd_strike(char *cmd)
185 struct character *curchar = get_current_character();
186 char stat[MAX_STAT_LEN];
187 int ival[2] = { -1, -1 };
188 int ret;
190 if (curchar == NULL) {
191 printf("No character loaded. Use 'cd' to load a character\n");
192 return;
195 if (curchar->fight_active == 0) {
196 printf("You are not in a fight. Enter one with enterthefray\n");
197 return;
200 ret = get_args_from_cmd(cmd, stat, &ival[1]);
201 if (ret >= 10) {
202 info:
203 printf("Please specify the stat you'd like to use in this move\n\n");
204 printf("iron\t- You attack in close quarters\n");
205 printf("edge\t- You attack at range\n");
206 printf("Example: strike iron\n");
207 return;
208 } else if (ret <= -20)
209 return;
211 if (strcasecmp(stat, "iron") == 0) {
212 ival[0] = curchar->iron;
213 } else if (strcasecmp(stat, "edge") == 0) {
214 ival[0] = curchar->edge;
215 } else
216 goto info;
218 ret = action_roll(ival);
219 if (ret == 8) {
220 printf("You inflict +1 harm and retain initiative\n");
221 set_initiative(1);
222 mark_fight_progress();
223 mark_fight_progress();
224 } else if (ret == 4) {
225 printf("You inflict harm and lose initiative\n");
226 set_initiative(0);
227 mark_fight_progress();
228 } else {
229 printf("Pay the price -> Rulebook\n");
230 set_initiative(0);
231 update_prompt();
235 void
236 cmd_clash(char *cmd)
238 struct character *curchar = get_current_character();
239 char stat[MAX_STAT_LEN];
240 int ival[2] = { -1, -1 };
241 int ret;
243 if (curchar == NULL) {
244 printf("No character loaded. Use 'cd' to load a character\n");
245 return;
248 if (curchar->fight_active == 0) {
249 printf("You are not in a fight. Enter one with enterthefray\n");
250 return;
253 ret = get_args_from_cmd(cmd, stat, &ival[1]);
254 if (ret >= 10) {
255 info:
256 printf("Please specify the stat you'd like to use in this move\n\n");
257 printf("iron\t- You fight in close quarters\n");
258 printf("edge\t- You fight at range\n");
259 printf("Example: clash iron\n");
260 return;
261 } else if (ret <= -20)
262 return;
264 if (strcasecmp(stat, "iron") == 0) {
265 ival[0] = curchar->iron;
266 } else if (strcasecmp(stat, "edge") == 0) {
267 ival[0] = curchar->edge;
268 } else
269 goto info;
271 ret = action_roll(ival);
272 if (ret == 8) {
273 printf("You inflict harm, regain initiative and can choose one option -> Rulebook\n");
274 set_initiative(1);
275 mark_fight_progress();
276 } else if (ret == 4) {
277 printf("You inflict harm and lose initiative. Pay the price -> Rulebook\n");
278 set_initiative(0);
279 mark_fight_progress();
280 } else {
281 printf("Pay the price -> Rulebook\n");
282 set_initiative(0);
283 update_prompt();
287 void
288 cmd_battle(char *cmd)
290 struct character *curchar = get_current_character();
291 char stat[MAX_STAT_LEN];
292 int ival[2] = { -1, -1 };
293 int ret;
295 if (curchar == NULL) {
296 printf("No character loaded. Use 'cd' to load a character\n");
297 return;
300 if (curchar->fight_active == 0) {
301 printf("You are not in a fight. Enter one with enterthefray\n");
302 return;
305 ret = get_args_from_cmd(cmd, stat, &ival[1]);
306 if (ret >= 10) {
307 info:
308 printf("Please specify the stat you'd like to use in this move\n\n");
309 printf("edge\t- Fight at range, or using your speed and the terrain\n");
310 printf("heart\t- Fight depending on your courage, allies, or companions\n");
311 printf("iron\t- Fight in close to overpower your opponents\n");
312 printf("shadow\t- Fight using trickery to befuddle your opponents\n");
313 printf("wits\t- Fight using careful tactics to outsmart your opponents\n\n");
314 printf("Example: battle iron\n");
315 return;
316 } else if (ret <= -20)
317 return;
319 if (strcasecmp(stat, "iron") == 0) {
320 ival[0] = curchar->iron;
321 } else if (strcasecmp(stat, "wits") == 0) {
322 ival[0] = curchar->wits;
323 } else if (strcasecmp(stat, "edge") == 0) {
324 ival[0] = curchar->edge;
325 } else if (strcasecmp(stat, "shadow") == 0) {
326 ival[0] = curchar->shadow;
327 } else if (strcasecmp(stat, "heart") == 0) {
328 ival[0] = curchar->heart;
329 } else
330 goto info;
332 ret = action_roll(ival);
333 if (ret == 8) {
334 change_char_value("momentum", INCREASE, 2);
335 printf("You achieve your objective unconditionally\n");
336 } else if (ret == 4) /* weak hit */
337 printf("You achieve your objective, but not without a cost -> Rulebook\n");
338 else
339 printf("Pay the price -> Rulebook\n");
342 void
343 set_initiative(int what)
345 struct character *curchar = get_current_character();
347 if (curchar == NULL) {
348 log_debug("No character loaded. Cannot set initiative\n");
349 return;
352 if (curchar->fight_active == 0) {
353 printf("You need start a fight before you can mark progress\n");
354 return;
357 if (what == 1)
358 curchar->fight->initiative = 1;
359 else
360 curchar->fight->initiative = 0;
363 void
364 mark_fight_progress()
366 struct character *curchar = get_current_character();
368 if (curchar == NULL) {
369 log_debug("No character loaded. Cannot calculate progress\n");
370 return;
373 if (curchar->fight_active == 0) {
374 printf("You need start a fight before you can mark progress\n");
375 return;
378 switch (curchar->fight->difficulty) {
379 case 1:
380 curchar->fight->progress += 3;
381 break;
382 case 2:
383 curchar->fight->progress += 2;
384 break;
385 case 3:
386 curchar->fight->progress += 1;
387 break;
388 case 4:
389 curchar->fight->progress += 0.5;
390 break;
391 case 5:
392 curchar->fight->progress += 0.25;
393 break;
394 default:
395 curchar->fight->difficulty = 1;
396 log_errx(1, "Unknown difficulty. This should not happen. Set to 1\n");
399 if (curchar->fight->progress > 10) {
400 printf("Your fight is successful. Consider ending it\n");
401 curchar->fight->progress = 10;
404 update_prompt();
407 void
408 save_fight()
410 struct character *curchar = get_current_character();
411 char path[_POSIX_PATH_MAX];
412 json_object *root, *items, *id;
413 int temp_n, i;
415 if (curchar == NULL) {
416 log_debug("No character loaded. No fight to save.\n");
417 return;
420 if (curchar->fight_active == 0) {
421 log_debug("No active fight to save.\n");
422 return;
425 json_object *cobj = json_object_new_object();
426 json_object_object_add(cobj, "id", json_object_new_int(curchar->id));
427 json_object_object_add(cobj, "difficulty", json_object_new_int(curchar->fight->difficulty));
428 json_object_object_add(cobj, "progress", json_object_new_double(curchar->fight->progress));
429 json_object_object_add(cobj, "initiative", json_object_new_int(curchar->fight->initiative));
431 snprintf(path, sizeof(path), "%s/fight.json", get_isscrolls_dir());
432 if ((root = json_object_from_file(path)) == NULL) {
433 log_debug("No fight JSON file found\n");
434 root = json_object_new_object();
435 if (!root)
436 log_errx(1, "Cannot create fight JSON object\n");
438 items = json_object_new_array();
439 json_object_array_add(items, cobj);
440 json_object_object_add(root, "fight", items);
441 } else {
442 /* Get existing character array from JSON */
443 if (!json_object_object_get_ex(root, "fight", &items)) {
444 log_debug("Cannot find a [fight] array in %s. Create one\n", path);
445 items = json_object_new_array();
446 json_object_object_add(root, "fight", items);
449 temp_n = json_object_array_length(items);
450 for (i = 0; i < temp_n; i++) {
451 json_object *temp = json_object_array_get_idx(items, i);
452 json_object_object_get_ex(temp, "id", &id);
453 if (curchar->id == json_object_get_int(id)) {
454 log_debug("Update fight entry for %s\n", curchar->name);
455 json_object_array_del_idx(items, i, 1);
456 json_object_array_add(items, cobj);
457 goto out;
460 log_debug("No fight entry for %s found, adding new one\n", curchar->name);
461 json_object_array_add(items, cobj);
464 out:
465 if (json_object_to_file(path, root))
466 printf("Error saving %s\n", path);
467 else
468 log_debug("Successfully saved %s\n", path);
470 json_object_put(root);
473 void
474 delete_fight(int id)
476 char path[_POSIX_PATH_MAX];
477 json_object *root, *lid;
478 int temp_n, i;
480 snprintf(path, sizeof(path), "%s/fight.json", get_isscrolls_dir());
481 if ((root = json_object_from_file(path)) == NULL) {
482 log_debug("No fight JSON file found\n");
483 return;
486 json_object *fight;
487 if (!json_object_object_get_ex(root, "fight", &fight)) {
488 log_debug("Cannot find a [fight] array in %s\n", path);
489 return;
492 temp_n = json_object_array_length(fight);
493 for (i = 0; i < temp_n; i++) {
494 json_object *temp = json_object_array_get_idx(fight, i);
495 json_object_object_get_ex(temp, "id", &lid);
496 if (id == json_object_get_int(lid)) {
497 json_object_array_del_idx(fight, i, 1);
498 log_debug("Deleted fight entry for %d\n", id);
502 if (json_object_to_file(path, root))
503 printf("Error saving %s\n", path);
504 else
505 log_debug("Successfully saved %s\n", path);
507 json_object_put(root);
510 void
511 load_fight(int id)
513 struct character *curchar = get_current_character();
514 char path[_POSIX_PATH_MAX];
515 json_object *root, *lid;
516 int temp_n, i;
518 if (curchar == NULL) {
519 log_debug("No character loaded\n");
520 return;
523 snprintf(path, sizeof(path), "%s/fight.json", get_isscrolls_dir());
524 if ((root = json_object_from_file(path)) == NULL) {
525 log_debug("No fight JSON file found\n");
526 return;
529 json_object *fight;
530 if (!json_object_object_get_ex(root, "fight", &fight)) {
531 log_debug("Cannot find a [fight] array in %s\n", path);
532 return;
535 temp_n = json_object_array_length(fight);
536 for (i=0; i < temp_n; i++) {
537 json_object *temp = json_object_array_get_idx(fight, i);
538 json_object_object_get_ex(temp, "id", &lid);
539 if (id == json_object_get_int(lid)) {
540 log_debug("Loading fight for id: %d\n", json_object_get_int(lid));
542 json_object *cval;
543 json_object_object_get_ex(temp, "difficulty", &cval);
544 curchar->fight->difficulty = json_object_get_int(cval);
545 json_object_object_get_ex(temp, "progress", &cval);
546 curchar->fight->progress = json_object_get_double(cval);
547 json_object_object_get_ex(temp, "initiative", &cval);
548 curchar->fight->initiative = json_object_get_int(cval);
552 json_object_put(root);
555 void
556 ask_for_fight_difficulty()
558 struct character *curchar = get_current_character();
560 if (curchar == NULL) {
561 log_debug("No character loaded\n");
562 return;
565 printf("Please set a difficulty for your fight\n\n");
566 printf("1\t - Troublesome foe (3 progress per harm)\n");
567 printf("2\t - Dangerous foe (2 progress per harm)\n");
568 printf("3\t - Formidable foe (2 progress per harm)\n");
569 printf("4\t - Extreme foe (2 ticks per harm)\n");
570 printf("5\t - Epic foe (1 tick per harm)\n\n");
572 curchar->fight->difficulty = ask_for_value("Enter a value between 1 and 5: ", 5);