home · contact · privacy
Added diagonal movement, with a 1.4 penalty.
[plomrogue] / src / server / run.c
1 /* src/server/run.c */
2
3 #include "run.h"
4 #include <stddef.h> /* NULL */
5 #include <stdint.h> /* uint8_t, uint16_t, uint32_t */
6 #include <stdio.h> /* FILE, sprintf() */
7 #include <stdlib.h> /* free() */
8 #include <string.h> /* strlen(), strncmp(), atoi() */
9 #include <unistd.h> /* access() */
10 #include "../common/readwrite.h" /* try_fopen(), try_fcose(), try_fwrite(),
11                                   * try_fgets(), try_fclose_unlink_rename(),
12                                   * textfile_sizes(), try_fputc()
13                                   */
14 #include "../common/rexit.h" /* exit_trouble() */
15 #include "ai.h" /* ai() */
16 #include "init.h" /* remake_world() */
17 #include "io.h" /* io_round() */
18 #include "map_object_actions.h" /* get_moa_id_by_name() */
19 #include "map_objects.h" /* struct MapObj, get_player() */
20 #include "world.h" /* global world */
21
22
23
24 /* Run the game world and its inhabitants (and their actions) until the player
25  * avatar is free to receive new commands (or is dead).
26  */
27 static void turn_over();
28
29 /* Helper to turn_over() to determine whether a map object's action effort has
30  * reached its end. The simplicity of just comparing map_object->progress to
31  * moa->effort is suspended for actor movement, for in this case the effort
32  * depends on the diagonal movement penalty expressed in the ratio of
33  * world.map.dist_diagonal / world.map.dist_orthogonal. (Movement being diagonal
34  * or orthogonal is determined by the ->arg char encoding an even or un-even
35  * number digit).
36  */
37 static uint8_t is_effort_finished(struct MapObjAct * moa,
38                                   struct MapObj * map_object);
39
40 /* If "msg"'s first part matches "command_name", set player's MapObj's .command
41  * to the command's id and its .arg to a numerical value following in the latter
42  * part of "msg" (if no digits are found, use 0); then finish player's turn and
43  * turn game over to the NPCs via turn_over(); then return 1. Else, return 0.
44  */
45 static uint8_t apply_player_command(char * msg, char * command_name);
46
47
48
49 static void turn_over()
50 {
51     struct MapObj * player = get_player();
52     struct MapObj * map_object = player;
53     uint16_t start_turn = world.turn;
54     while (    0 < player->lifepoints
55            || (0 == player->lifepoints && start_turn == world.turn))
56     {
57         if (NULL == map_object)
58         {
59             world.turn++;
60             map_object = world.map_objs;
61         }
62         if (0 < map_object->lifepoints)
63         {
64             if (0 == map_object->command)
65             {
66                 if (map_object == player)
67                 {
68                     break;
69                 }
70                 ai(map_object);
71             }
72             map_object->progress++;
73             struct MapObjAct * moa = world.map_obj_acts;
74             while (moa->id != map_object->command)
75             {
76                 moa = moa->next;
77             }
78             if (is_effort_finished(moa, map_object))
79             {
80                 moa->func(map_object);
81                 map_object->command = 0;
82                 map_object->progress = 0;
83             }
84         }
85         map_object = map_object->next;
86     }
87 }
88
89
90
91 static uint8_t is_effort_finished(struct MapObjAct * moa,
92                                   struct MapObj * map_object)
93 {
94     if (moa->func != actor_move)
95     {
96         if (map_object->progress == moa->effort)
97         {
98             return 1;
99         }
100     }
101     else if (strchr("8624", map_object->arg))
102     {
103         if (map_object->progress == moa->effort)
104         {
105             return 1;
106         }
107     }
108     else if (strchr("1379", map_object->arg))
109     {
110         uint16_t diagonal_effort =   (moa->effort * world.map.dist_diagonal)
111                                    / world.map.dist_orthogonal;
112         if (map_object->progress == diagonal_effort)
113         {
114             return 1;
115         }
116     }
117     return 0;
118 }
119
120
121
122 static uint8_t apply_player_command(char * msg, char * command_name)
123 {
124     if (!strncmp(msg, command_name, strlen(command_name)))
125     {
126         struct MapObj * player = get_player();
127         player->arg = atoi(&(msg[strlen(command_name)]));
128         player->command = get_moa_id_by_name(command_name);
129         turn_over();
130         return 1;
131     }
132     return 0;
133 }
134
135
136
137 extern void obey_msg(char * msg, uint8_t do_record)
138 {
139     char * f_name = "obey_msg()";
140     if (   apply_player_command(msg, "wait")   /* TODO: Check for non-error   */
141         || apply_player_command(msg, "move")   /* return value of a modified  */
142         || apply_player_command(msg, "pick_up")/* get_moa_id_by_name(); if id */
143         || apply_player_command(msg, "drop")   /* found, execute on it what's */
144         || apply_player_command(msg, "use"));  /* in apply_player_command().  */
145     else
146     {
147         char * seed_command = "seed";
148         if (!strncmp(msg, seed_command, strlen(seed_command)))
149         {
150             remake_world(atoi(&(msg[strlen(seed_command)])));
151         }
152     }
153     if (do_record)
154     {
155         char path_tmp[strlen(world.path_record) + strlen(world.tmp_suffix) + 1];
156         sprintf(path_tmp, "%s%s", world.path_record, world.tmp_suffix);
157         FILE * file_tmp  = try_fopen(path_tmp, "w", f_name);
158         if (!access(world.path_record, F_OK))
159         {
160             FILE * file_read = try_fopen(world.path_record, "r", f_name);
161             uint32_t linemax = textfile_sizes(file_read, NULL);
162             char line[linemax + 1];
163             while (try_fgets(line, linemax + 1, file_read, f_name))
164             {
165                 try_fwrite(line, strlen(line), 1, file_tmp, f_name);
166             }
167             try_fclose(file_read, f_name);
168         }
169         try_fwrite(msg, strlen(msg), 1, file_tmp, f_name);
170         try_fputc('\n', file_tmp, f_name);
171         try_fclose_unlink_rename(file_tmp, path_tmp, world.path_record, f_name);
172     }
173 }
174
175
176
177 extern uint8_t io_loop()
178 {
179     char * f_name = "io_loop()";
180     while (1)
181     {
182         char * msg = io_round();
183         if (NULL == msg)
184         {
185             continue;
186         }
187         if (world.is_verbose)
188         {
189             exit_trouble(-1 == printf("Input: %s\n", msg), f_name, "printf()");
190         }
191         if (!strcmp("QUIT", msg))
192         {
193             free(msg);
194             return 1;
195         }
196         if (world.replay)
197         {
198             free(msg);
199             return 0;
200         }
201         obey_msg(msg, 1);
202         free(msg);
203     }
204 }