home · contact · privacy
674ff6229e06328cee8bdb999036c2fcc172ffee
[plomrogue] / src / server / god_commands.c
1 /* src/server/god_commands.c */
2
3 #include "god_commands.h"
4 #include <stddef.h> /* NULL */
5 #include <stdint.h> /* uint8_t */
6 #include <stdlib.h> /* atoi(), free() */
7 #include <string.h> /* strcmp() */
8 #include <unistd.h> /* F_OK, access(), unlink() */
9 #include "../common/parse_file.h" /* err_line(), parse_val(), parsetest_int() */
10 #include "../common/rexit.h" /* exit_trouble() */
11 #include "cleanup.h" /* unset_cleanup_flag() */
12 #include "field_of_view.h" /* build_fov_map() */
13 #include "hardcoded_strings.h" /* s */
14 #include "init.h" /* remake_world() */
15 #include "map.h" /* remake_map() */
16 #include "thing_actions.h" /* ThingAction, actor_wait(), actor_move(),
17                             * actor_use(), actor_pickup(), actor_drop()
18                             */
19 #include "things.h" /* Thing, ThingType, add_thing(), get_thing(), own_thing(),
20                      * free_things()
21                      */
22 #include "world.h" /* world */
23
24
25
26 /* Parse/apply god command in "tok0"/tok1" to manipulate a ThingType*/
27 static uint8_t parse_thingtype_manipulation(char * tok0, char * tok1);
28
29 /* If "name" fits "ta"->name, set "ta"->func to "func". (Derives ThingAction
30  * .func from .name for set_members().
31  */
32 static uint8_t try_func_name(struct ThingAction * ta, char * name,
33                              void (* func) (struct Thing *));
34
35 /* Parse/apply god command in "tok0"/"tok1" to manipulate a ThingAction. */
36 static uint8_t parse_thingaction_manipulation(char * tok0, char * tok1);
37
38 /* Parse/apply god command in "tok0"/"tok1" oo setting "t"'s thing type. */
39 static uint8_t parse_thing_type(char * tok0, char * tok1, struct Thing * t);
40
41 /* Parse/apply god command in "tok0"/"tok1" on setting up thing "t". */
42 static uint8_t parse_thing_command(char * tok0, char * tok1, struct Thing * t);
43
44 /* Parse/apply god command in "tok0"/"tok1" on positioning a thing "t". */
45 static uint8_t parse_position(char* tok0, char * tok1, struct Thing * t);
46
47 /* Parse/apply god command in "tok0"/"tok1" on "t" owning another thing. */
48 static uint8_t parse_carry(char * tok0, char * tok1, struct Thing * t);
49
50 /* Parse/apply god command in "tok0"/"tok1" to manipulate a Thing. */
51 static uint8_t parse_thing_manipulation(char * tok0, char * tok1);
52
53 /* Performs parse_world_active()'s world activation legality tests. */
54 static uint8_t world_may_be_set_active();
55
56 /* Parse/apply god command in "tok0"/"tok1" on toggling world.exists. Unset if
57  * argument is 0 and unlink worldstate file, but only set it on positive
58  * argument if it is not already set and a thing action of name S_CMD_WAIT, a
59  * player thing and a map are defined. On setting it, rebuild all FOVs.
60  */
61 static uint8_t parse_world_active(char * tok0, char * tok1);
62
63 /* Parse/apply god command in "tok0"/"tok1" to reset world.map.length. On
64  * re-set, set world.exists to 0, remove all things and free world.map.cells
65  */
66 static uint8_t set_map_length(char * tok0, char * tok1);
67
68
69
70 static uint8_t parse_thingtype_manipulation(char * tok0, char * tok1)
71 {
72     static struct ThingType * tt = NULL;
73     if (!tt &&
74         (   !strcmp(tok0, s[S_CMD_TT_CONSUM]) || !strcmp(tok0, s[S_CMD_TT_SYMB])
75          || !strcmp(tok0, s[S_CMD_TT_STARTN]) || !strcmp(tok0, s[S_CMD_TT_NAME])
76          || !strcmp(tok0, s[S_CMD_TT_CORPS]) || !strcmp(tok0, s[S_CMD_TT_HP])))
77     {
78         err_line(1, "No thing type defined to manipulate yet.");
79         return 1;
80     }
81     uint8_t id;
82     if (   parse_val(tok0,tok1,s[S_CMD_TT_CONSUM],'8',(char *) &tt->consumable)
83         || parse_val(tok0,tok1,s[S_CMD_TT_HP],'8',(char *) &tt->lifepoints)
84         || parse_val(tok0,tok1,s[S_CMD_TT_STARTN],'8',(char *) &tt->start_n)
85         || parse_val(tok0,tok1,s[S_CMD_TT_SYMB],'c',(char *) &tt->char_on_map)
86         || parse_val(tok0,tok1,s[S_CMD_TT_NAME],'s',(char *) &tt->name));
87     else if (parse_val(tok0, tok1, s[S_CMD_TT_CORPS],'8',(char *)&id))
88     {
89         if (!get_thing_type(id))
90         {
91             err_line(1, "Corpse ID belongs to no known thing type.");
92             return 1;
93         }
94         tt->corpse_id = id;
95     }
96     else if (parse_val(tok0, tok1, s[S_CMD_THINGTYPE], '8', (char *) &id))
97     {
98         tt = get_thing_type(id);
99         if (!tt)
100         {
101             tt = add_thing_type(id);
102         }
103     }
104     else
105     {
106         return 0;
107     }
108     return 1;
109 }
110
111
112
113 static uint8_t try_func_name(struct ThingAction * ta, char * name,
114                              void (* func) (struct Thing *))
115 {
116     if (0 == strcmp(ta->name, name))
117     {
118         ta->func = func;
119         return 1;
120     }
121     return 0;
122 }
123
124
125
126 static uint8_t parse_thingaction_manipulation(char * tok0, char * tok1)
127 {
128     static struct ThingAction * ta = NULL;
129     if (!ta &&
130         (!strcmp(tok0, s[S_CMD_TA_EFFORT]) || !strcmp(tok0, s[S_CMD_TA_NAME])))
131     {
132         err_line(1, "No thing action defined to manipulate yet.");
133         return 1;
134     }
135     uint8_t id;
136     if      (parse_val(tok0, tok1, s[S_CMD_TA_EFFORT],'8',(char *)&ta->effort));
137     else if (parse_val(tok0, tok1, s[S_CMD_TA_NAME], 's', (char *)&ta->name))
138     {
139         if (!(   try_func_name(ta, s[S_CMD_MOVE], actor_move)
140               || try_func_name(ta, s[S_CMD_PICKUP], actor_pick)
141               || try_func_name(ta, s[S_CMD_WAIT], actor_wait)
142               || try_func_name(ta, s[S_CMD_DROP], actor_drop)
143               || try_func_name(ta, s[S_CMD_USE], actor_use)))
144         {
145             err_line(1, "Invalid action function name.");
146             return 1;
147         }         /* Legal worlds have at least one thing action for waiting. */
148         if (world.exists)
149         {
150             world.exists = 0 != get_thing_action_id_by_name(s[S_CMD_WAIT]);
151         }
152     }
153     else if (parse_val(tok0, tok1, s[S_CMD_THINGACTION], '8', (char *) &id))
154     {
155         ta = get_thing_action(id);
156         if (!ta)
157         {
158             ta = add_thing_action(id);
159         }
160     }
161     else
162     {
163         return 0;
164     }
165     return 1;
166 }
167
168
169
170 static uint8_t parse_thing_type(char * tok0, char * tok1, struct Thing * t)
171 {
172     uint8_t type;
173     if (parse_val(tok0, tok1, s[S_CMD_T_TYPE], '8', (char *) &type))
174     {
175         struct ThingType * tt = get_thing_type(type);
176         if (!err_line(!tt, "Thing type does not exist."))
177         {
178             t->type = type;
179         }
180         return 1;
181     }
182     return 0;
183 }
184
185
186
187 static uint8_t parse_thing_command(char * tok0, char * tok1, struct Thing * t)
188 {
189     uint8_t command;
190     if (parse_val(tok0, tok1, s[S_CMD_T_COMMAND], '8', (char *) &command))
191     {
192         if (!command)
193         {
194             t->command = command;
195             return 1;
196         }
197         struct ThingAction * ta = world.thing_actions;
198         for (; ta && command != ta->id; ta = ta->next);
199         if (!err_line(!ta, "Thing action does not exist."))
200         {
201             t->command = command;
202         }
203         return 1;
204     }
205     return 0;
206 }
207
208
209
210 static uint8_t parse_position(char* tok0, char * tok1, struct Thing * t)
211 {
212     char axis = 0;
213     if      (!strcmp(tok0, s[S_CMD_T_POSY]))
214     {
215         axis = 'y';
216     }
217     else if (!strcmp(tok0, s[S_CMD_T_POSX]))
218     {
219         axis = 'x';
220     }
221     if (axis && !parsetest_int(tok1, '8'))
222     {
223         uint8_t length = atoi(tok1);
224         char * err = "Position is outside of map.";
225         if (!err_line(length >= world.map.length, err))
226         {
227             if      ('y' == axis)
228             {
229                 t->pos.y = length;
230             }
231             else if ('x' == axis)
232             {
233                 t->pos.x = length;
234             }
235             free(t->fov_map);
236             if (world.exists && t->lifepoints)
237             {
238                 t->fov_map = build_fov_map(t);
239             }
240         }
241         return 1;
242     }
243     return 0;
244 }
245
246
247
248 static uint8_t parse_carry(char * tok0, char * tok1, struct Thing * t)
249 {
250     uint8_t id;
251     if (parse_val(tok0, tok1, s[S_CMD_T_CARRIES], '8', (char *) &id))
252     {
253         if (!err_line(id == t->id, "Thing cannot carry itself."))
254         {
255             struct Thing * o = get_thing(world.things, id, 0);
256             if (!err_line(!o, "Thing not available for carrying."))
257             {
258                 own_thing(&(t->owns), &world.things, id);
259                 o->pos = t->pos;
260             }
261         }
262         return 1;
263     }
264     return 0;
265 }
266
267
268
269 static uint8_t parse_thing_manipulation(char * tok0, char * tok1)
270 {
271     static struct Thing * t = NULL;
272     if (!t &&
273         (   !strcmp(tok0, s[S_CMD_T_PROGRESS]) || !strcmp(tok0, s[S_CMD_T_TYPE])
274          || !strcmp(tok0, s[S_CMD_T_CARRIES]) || !strcmp(tok0, s[S_CMD_T_POSY])
275          || !strcmp(tok0, s[S_CMD_T_POSY]) || !strcmp(tok0, s[S_CMD_T_ARGUMENT])
276          || !strcmp(tok0, s[S_CMD_T_HP]) || !strcmp(tok0, s[S_CMD_T_COMMAND])))
277     {
278         err_line(1, "No thing defined to manipulate yet.");
279         return 1;
280     }
281     uint8_t id;
282     if (    parse_thing_type(tok0, tok1, t)
283         || parse_thing_command(tok0, tok1, t)
284         || parse_val(tok0,tok1, s[S_CMD_T_ARGUMENT], '8', (char *)&t->arg)
285         || parse_val(tok0,tok1, s[S_CMD_T_PROGRESS], '8', (char *)&t->progress)
286         || parse_val(tok0,tok1, s[S_CMD_T_HP], '8', (char *) &t->lifepoints)
287         || parse_position(tok0, tok1, t)
288         || parse_carry(tok0, tok1, t));
289     else if (parse_val(tok0, tok1, s[S_CMD_THING], 'i', (char *) &id))
290     {
291         t = get_thing(world.things, id, 1);
292         char * err = "No thing type found to initialize new thing.";
293         if (!t && !err_line(NULL == world.thing_types, err))
294         {
295             t = add_thing(id, world.thing_types->id, 0, 0);
296             if (world.exists && t->lifepoints)
297             {
298                 t->fov_map = build_fov_map(t);
299             }
300         }
301     }
302     else
303     {
304         return 0;
305     }
306     return 1;
307 }
308
309
310
311 static uint8_t world_may_be_set_active()
312 {
313     if (!get_thing_action_id_by_name(s[S_CMD_WAIT]))
314     {
315         err_line(1, "No thing action of name 'wait' found.");
316         return 0;
317     }
318     if (!get_player())
319     {
320         err_line(1, "No un-owned player thing (of id=0) found.");
321         return 0;
322     }
323     if (!world.map.cells)
324     {
325         err_line(1, "No map found.");
326         return 0;
327     }
328     return 1;
329 }
330
331
332
333 static uint8_t parse_world_active(char * tok0, char * tok1)
334 {
335     if (!strcmp(tok0, s[S_CMD_WORLD_ACTIVE]) && !parsetest_int(tok1, '8'))
336     {
337         if (!parsetest_int(tok1, '8'))
338         {
339             uint8_t argument = atoi(tok1);
340             if (!argument)
341             {
342                 if (!access(s[S_PATH_WORLDSTATE], F_OK))
343                 {
344                     int test = unlink(s[S_PATH_WORLDSTATE]);
345                     exit_trouble(-1 == test, __func__, "unlink");
346                 }
347                 world.exists = 0;
348             }
349             else if (world.exists)
350             {
351                 err_line(1, "World already active.");
352             }
353             else if (world_may_be_set_active())
354             {
355                 struct Thing * ti;
356                 for (ti = world.things; ti; ti = ti->next)
357                 {
358                     if (ti->lifepoints)
359                     {
360                         if (ti->fov_map)
361                         {
362                             free(ti->fov_map);
363                         }
364                         ti->fov_map = build_fov_map(ti);
365                     }
366                 }
367                 world.exists = 1;
368             }
369             return 1;
370         }
371     }
372     return 0;
373 }
374
375
376
377 static uint8_t set_map_length(char * tok0, char * tok1)
378 {
379     if (!strcmp(tok0, s[S_CMD_MAPLENGTH]) && !parsetest_int(tok1, 'u'))
380     {
381         uint16_t argument = atoi(tok1);
382         if (argument < 1 || argument > 256)
383         {
384             err_line(1, "Value must be >= 1 and <= 256.");
385             return 1;
386         }
387         world.exists = 0;
388         free_things(world.things);
389         free(world.map.cells);
390         world.map.cells = NULL;    /* Since remake_map() runs free() on this. */
391         world.map.length = argument;
392         return 1;
393     }
394     return 0;
395 }
396
397
398
399 extern uint8_t parse_god_command_1arg(char * tok0, char * tok1)
400 {
401     if (   parse_thingtype_manipulation(tok0, tok1)
402         || parse_thingaction_manipulation(tok0, tok1)
403         || parse_thing_manipulation(tok0, tok1)
404         || set_map_length(tok0,tok1)
405         || parse_val(tok0,tok1,s[S_CMD_SEED_RAND],'U', (char *)&world.seed)
406         || parse_val(tok0,tok1,s[S_CMD_TURN],'u',(char *)&world.turn)
407         || parse_val(tok0,tok1,s[S_CMD_PLAYTYPE],'8',(char *)&world.player_type)
408         || parse_world_active(tok0, tok1));
409     else if (parse_val(tok0,tok1,s[S_CMD_SEED_MAP],'U',(char *)&world.seed_map))
410
411     {
412         remake_map();
413     }
414     else if (parse_val(tok0, tok1, s[S_CMD_MAKE_WORLD],'U',(char *)&world.seed))
415     {
416         uint8_t test = remake_world();
417         err_line(1 == test, "No player type with start number of >0 defined.");
418         err_line(2 == test, "No thing action with name 'wait' defined.");
419     }
420     else
421     {
422         return 0;
423     }
424     return 1;
425 }