1 /* src/server/thing_actions.c
3 * This file is part of PlomRogue. PlomRogue is licensed under the GPL version 3
4 * or any later version. For details on its copyright, license, and warranties,
5 * see the file NOTICE in the root directory of the PlomRogue source package.
8 #include "thing_actions.h"
9 #include <stddef.h> /* NULL */
10 #include <stdint.h> /* uint8_t, INT16_MIN, INT16_MAX */
11 #include <stdio.h> /* sprintf() */
12 #include <stdlib.h> /* free() */
13 #include <string.h> /* strlen() */
14 #include "../common/rexit.h" /* exit_err(), exit_trouble() */
15 #include "../common/try_malloc.h" /* try_malloc() */
16 #include "../common/yx_uint8.h" /* yx_uint8 */
17 #include "field_of_view.h" /* build_fov_map() */
18 #include "hardcoded_strings.h" /* s */
19 #include "things.h" /* Thing, ThingType, get_player(), free_things_in_memory(),
20 * own_thing(), set_thing_position(), get_thing_type(),
22 #include "map.h" /* mv_yx_in_dir_legal() */
23 #include "rrand.h" /* rrand() */
24 #include "run.h" /* send_to_outfile() */
25 #include "world.h" /* global world */
29 /* Send "text" as log message to server out file. */
30 static void update_log(char * text);
32 /* Decrement "t"'s lifepoints, and if to zero, kill it with log update. */
33 static void decrement_lifepoints(struct Thing * t);
35 /* One actor "wounds" another actor, decrementing his lifepoints. */
36 static void actor_hits_actor(struct Thing * hitter, struct Thing * hitted);
38 /* Bonus stuff to actor_*() to happen if actor==player. Mostly writing of log
39 * messages; _pick and _drop also decrement world.inventory_sel by 1 if >0.
40 * (match_dir() is just a little helper to playerbonus_move().)
42 static void playerbonus_wait();
43 static uint8_t match_dir(char d, char ** dsc_d, char match, char * dsc_match);
44 static void playerbonus_move(char d, uint8_t passable);
45 static void playerbonus_drop(uint8_t owns_none);
46 static void playerbonus_pick(uint8_t picked);
47 static void playerbonus_use(uint8_t no_thing, uint8_t wrong_thing);
51 static void update_log(char * text)
53 send_to_outfile("LOG ", 0);
54 send_to_outfile(text, 0);
55 send_to_outfile("\n", 1);
60 static void decrement_lifepoints(struct Thing * t)
62 struct Thing * player = get_player();
64 if (0 == t->lifepoints)
66 t->type = get_thing_type(t->type)->corpse_id;
69 update_log("You die.");
70 memset(t->fov_map, ' ', world.map.length * world.map.length);
79 free(t->mem_depth_map);
80 t->mem_depth_map = NULL;
81 free_things_in_memory(t->t_mem);
84 update_log("It dies.");
90 static void actor_hits_actor(struct Thing * hitter, struct Thing * hitted)
92 struct ThingType * tt_hitter = get_thing_type(hitter->type);
93 struct ThingType * tt_hitted = get_thing_type(hitted->type);
94 struct Thing * player = get_player();
96 char * msg2 = "wound";
100 msg1 = tt_hitter->name;
103 if (player != hitted)
105 msg3 = tt_hitted->name;
107 uint8_t len = strlen(msg1) + 1 + strlen(msg2) + 1 + strlen(msg3) + 2;
108 char * msg = try_malloc(len, __func__);
109 int test = sprintf(msg, "%s %s %s.", msg1, msg2, msg3);
110 exit_trouble(test < 0, __func__, s[S_FCN_SPRINTF]);
113 decrement_lifepoints(hitted);
118 static void playerbonus_wait()
120 update_log("You wait.");
125 static uint8_t match_dir(char d, char ** dsc_d, char match, char * dsc_match)
137 static void playerbonus_move(char d, uint8_t passable)
139 char * dsc_dir = "north-east";
140 if ( match_dir(d, &dsc_dir, 'd', "east")
141 || match_dir(d, &dsc_dir, 'c', "south-east")
142 || match_dir(d, &dsc_dir, 'x', "south-west")
143 || match_dir(d, &dsc_dir, 's', "west")
144 || match_dir(d, &dsc_dir, 'w', "north-west"))
148 char * dsc_move = "You move ";
151 dsc_move = "You fail to move ";
153 char * msg = try_malloc(strlen(dsc_move) + strlen (dsc_dir) + 2, __func__);
154 int test = sprintf(msg, "%s%s.", dsc_move, dsc_dir);
155 exit_trouble(test < 0, __func__, s[S_FCN_SPRINTF]);
162 static void playerbonus_drop(uint8_t owns_none)
166 update_log("You try to drop an object, but you own none.");
169 update_log("You drop an object.");
174 static void playerbonus_pick(uint8_t picked)
178 update_log("You pick up an object.");
181 update_log("You try to pick up an object, but there is none.");
186 static void playerbonus_use(uint8_t no_thing, uint8_t wrong_thing)
190 update_log("You try to use an object, but you own none.");
193 else if (wrong_thing)
195 update_log("You try to use this object, but fail.");
198 update_log("You consume this object.");
203 extern void actor_wait(struct Thing * t)
205 if (t == get_player())
213 extern void actor_move(struct Thing * t)
216 struct Thing * other_t;
217 struct yx_uint8 target = t->pos;
218 uint8_t legal_move = mv_yx_in_dir_legal(d, &target);
219 mv_yx_in_dir_legal(0, NULL);
220 uint8_t passable = 0;
223 passable = '.' == world.map.cells[target.y*world.map.length + target.x];
224 for (other_t = world.things; other_t != 0; other_t = other_t->next)
226 if (0 == other_t->lifepoints || other_t == t)
230 if (target.y == other_t->pos.y && target.x == other_t->pos.x)
232 actor_hits_actor(t, other_t);
239 set_thing_position(t, target);
242 if (t == get_player())
244 playerbonus_move(d, passable);
250 extern void actor_drop(struct Thing * t)
252 uint8_t owns_none = (!t->owns);
255 uint8_t select = t->arg;
256 struct Thing * owned = t->owns;
258 for (; i != select; i++, owned = owned->next);
259 own_thing(&world.things, &t->owns, owned->id);
261 if (t == get_player())
263 playerbonus_drop(owns_none);
269 extern void actor_pick(struct Thing * t)
271 struct Thing * picked = NULL;
273 uint8_t highest_id = 0;
274 for (t_i = world.things; t_i; t_i = t_i->next)
276 if (t_i != t && t_i->pos.y == t->pos.y && t_i->pos.x == t->pos.x)
278 if (t_i->id >= highest_id) /* With several Things to pick, */
279 { /* pick the one with the highest ID. */
280 highest_id = t_i->id;
287 own_thing(&t->owns, &world.things, picked->id);
288 set_thing_position(picked, t->pos);
290 if (t == get_player())
292 playerbonus_pick(!(!picked));
298 extern void actor_use(struct Thing * t)
300 uint8_t wrong_thing = 1;
301 uint8_t no_thing = (!t->owns);
304 uint8_t select = t->arg;
306 struct Thing * selected = t->owns;
307 for (; i != select; i++, selected = selected->next);
308 struct ThingType * tt = get_thing_type(selected->type);
312 struct Thing * next = selected->next;
318 for (i = 0; i != select; i++, selected = selected->next);
319 selected->next = next;
325 t->satiation = t->satiation + tt->consumable > INT16_MAX ?
326 INT16_MAX : t->satiation + tt->consumable;
329 if (t == get_player())
331 playerbonus_use(no_thing, wrong_thing);
337 extern void try_healing(struct Thing * t)
339 struct ThingType * tt = get_thing_type(t->type);
340 if ( t->satiation > 0 && t->lifepoints < tt->lifepoints
341 && 0 == (rrand() % 31)
342 && get_thing_action_id_by_name(s[S_CMD_WAIT]) == t->command)
345 t->satiation = t->satiation - 32;
346 if (get_player() == t)
348 update_log("You heal.");
352 char * msg_part = " heals.";
353 uint8_t len = strlen(tt->name) + strlen(msg_part) + 1;
354 char * msg = try_malloc(len, __func__);
355 int test = sprintf(msg, "%s%s", tt->name, msg_part);
356 exit_trouble(test < 0, __func__, s[S_FCN_SPRINTF]);
365 extern void hunger(struct Thing * t)
367 if (t->satiation > INT16_MIN)
371 struct ThingType * tt = get_thing_type(t->type);
372 uint16_t testbase = t->satiation < 0 ? -(t->satiation) : t->satiation;
373 exit_err(!(tt->lifepoints), "A thing that should not hunger is hungering.");
374 uint16_t endurance = INT16_MAX / tt->lifepoints;
375 if ((testbase / endurance) / ((rrand() % endurance) + 1))
377 if (get_player() == t)
379 update_log("You suffer from hunger.");
383 char * msg_part = " suffers from hunger.";
384 uint8_t len = strlen(tt->name) + strlen(msg_part) + 1;
385 char * msg = try_malloc(len, __func__);
386 int test = sprintf(msg, "%s%s", tt->name, msg_part);
387 exit_trouble(test < 0, __func__, s[S_FCN_SPRINTF]);
391 decrement_lifepoints(t);