File: | src/map.c |
Location: | line 1678, column 32 |
Description: | Null pointer passed as an argument to a 'nonnull' parameter |
1 | /* | |||
2 | * | |||
3 | * Copyright (c) 1994, 2002, 2003 Johannes Prix | |||
4 | * Copyright (c) 1994, 2002 Reinhard Prix | |||
5 | * Copyright (c) 2004-2010 Arthur Huillet | |||
6 | * | |||
7 | * | |||
8 | * This file is part of Freedroid | |||
9 | * | |||
10 | * Freedroid is free software; you can redistribute it and/or modify | |||
11 | * it under the terms of the GNU General Public License as published by | |||
12 | * the Free Software Foundation; either version 2 of the License, or | |||
13 | * (at your option) any later version. | |||
14 | * | |||
15 | * Freedroid is distributed in the hope that it will be useful, | |||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
18 | * GNU General Public License for more details. | |||
19 | * | |||
20 | * You should have received a copy of the GNU General Public License | |||
21 | * along with Freedroid; see the file COPYING. If not, write to the | |||
22 | * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |||
23 | * MA 02111-1307 USA | |||
24 | * | |||
25 | */ | |||
26 | ||||
27 | /** | |||
28 | * This file contains (all?) map-related functions, which also includes | |||
29 | * loading of decks and whole ships, starting the lifts and consoles if | |||
30 | * close to the paradroid, refreshes as well as determining the map brick | |||
31 | * that contains specified coordinates are done in this file. | |||
32 | */ | |||
33 | ||||
34 | #define _map_c | |||
35 | ||||
36 | #include "system.h" | |||
37 | ||||
38 | #include "defs.h" | |||
39 | #include "struct.h" | |||
40 | #include "proto.h" | |||
41 | #include "global.h" | |||
42 | ||||
43 | #include "lvledit/lvledit_actions.h" | |||
44 | #include "lvledit/lvledit_display.h" | |||
45 | #include "map.h" | |||
46 | ||||
47 | #define TELEPORT_PAIR_STRING"teleport pair:" "teleport pair:" | |||
48 | ||||
49 | void GetThisLevelsDroids(char *section_pointer); | |||
50 | ||||
51 | /* | |||
52 | * Initialize a map tile with default values. | |||
53 | */ | |||
54 | void init_map_tile(struct map_tile* tile) | |||
55 | { | |||
56 | int i; | |||
57 | tile->floor_values[0] = ISO_FLOOR_SAND; | |||
58 | for (i = 1; i < MAX_FLOOR_LAYERS2; i++) | |||
59 | tile->floor_values[i] = ISO_FLOOR_EMPTY; | |||
60 | dynarray_init(&tile->glued_obstacles, 0, sizeof(int)); | |||
61 | tile->timestamp = 0; | |||
62 | } | |||
63 | ||||
64 | /** | |||
65 | * This function removes all volatile obstacles from a given level. | |||
66 | * An example of a volatile obstacle is the blood. | |||
67 | * If the blood doesn't vanish, then there will be more and more blood, | |||
68 | * especially after the bots on the level have respawned a few times. | |||
69 | * Therefore we need this function, which will remove all traces of blood | |||
70 | * from a given level. | |||
71 | */ | |||
72 | static void remove_volatile_obstacles(int level_num) | |||
73 | { | |||
74 | int i; | |||
75 | ||||
76 | // We pass through all the obstacles, deleting those | |||
77 | // that are 'blood'. | |||
78 | // | |||
79 | for (i = 0; i < MAX_OBSTACLES_ON_MAP4000; i++) { | |||
80 | int obstacle_type = curShip.AllLevels[level_num]->obstacle_list[i].type; | |||
81 | if (obstacle_type == -1) | |||
82 | continue; | |||
83 | if (get_obstacle_spec(obstacle_type)->flags & IS_VOLATILE) | |||
84 | del_obstacle(&curShip.AllLevels[level_num]->obstacle_list[i]); | |||
85 | } | |||
86 | } | |||
87 | ||||
88 | /** | |||
89 | * This function will make all blood obstacles vanish, all dead bots come | |||
90 | * back to life, and get all bots return to a wandering state. | |||
91 | */ | |||
92 | void respawn_level(int level_num) | |||
93 | { | |||
94 | enemy *erot, *nerot; | |||
95 | ||||
96 | int wp_num = curShip.AllLevels[level_num]->waypoints.size; | |||
97 | char wp_used[wp_num]; // is a waypoint already used ? | |||
98 | memset(wp_used, 0, wp_num); | |||
99 | ||||
100 | // First we remove all the volatile obstacles... | |||
101 | // | |||
102 | remove_volatile_obstacles(level_num); | |||
103 | ||||
104 | // Now we can give new life to dead bots... | |||
105 | // | |||
106 | BROWSE_DEAD_BOTS_SAFE(erot, nerot)for (erot = ({ const typeof( ((typeof(*erot) *)0)->global_list ) *__mptr = ((&dead_bots_head)->next); (typeof(*erot) *)( (char *)__mptr - __builtin_offsetof(typeof(*erot), global_list ) );}), nerot = ({ const typeof( ((typeof(*erot) *)0)->global_list ) *__mptr = (erot->global_list.next); (typeof(*erot) *)( ( char *)__mptr - __builtin_offsetof(typeof(*erot), global_list ) );}); &erot->global_list != (&dead_bots_head); erot = nerot, nerot = ({ const typeof( ((typeof(*nerot) *)0)-> global_list ) *__mptr = (nerot->global_list.next); (typeof (*nerot) *)( (char *)__mptr - __builtin_offsetof(typeof(*nerot ), global_list) );})) { | |||
107 | if (erot->pos.z != level_num || Droidmap[erot->type].is_human || !erot->will_respawn) | |||
108 | continue; | |||
109 | /* Move the bot to the alive list */ | |||
110 | list_move(&(erot->global_list), &alive_bots_head); | |||
111 | /* Reinsert it into the current level list */ | |||
112 | list_add(&(erot->level_list), &level_bots_head[level_num]); | |||
113 | } | |||
114 | ||||
115 | // Finally, we reset the runtime attributes of the bots, place them | |||
116 | // on a waypoint, and ask them to start wandering... | |||
117 | // | |||
118 | BROWSE_LEVEL_BOTS(erot, level_num)for (erot = ({ const typeof( ((typeof(*erot) *)0)->level_list ) *__mptr = ((&level_bots_head[(level_num)])->next); ( typeof(*erot) *)( (char *)__mptr - __builtin_offsetof(typeof( *erot), level_list) );}); &erot->level_list != (&level_bots_head [(level_num)]); erot = ({ const typeof( ((typeof(*erot) *)0)-> level_list ) *__mptr = (erot->level_list.next); (typeof(*erot ) *)( (char *)__mptr - __builtin_offsetof(typeof(*erot), level_list ) );})) { | |||
119 | ||||
120 | // Unconditional reset of the 'transient state' attributes | |||
121 | enemy_reset(erot); | |||
122 | ||||
123 | // Conditional reset of some 'global state' attributes | |||
124 | if (erot->has_been_taken_over) { | |||
125 | erot->faction = FACTION_BOTS; | |||
126 | erot->has_been_taken_over = FALSE(0); | |||
127 | erot->CompletelyFixed = FALSE(0); | |||
128 | erot->follow_tux = FALSE(0); | |||
129 | } | |||
130 | ||||
131 | erot->has_greeted_influencer = FALSE(0); | |||
132 | ||||
133 | if (wp_num) { | |||
134 | // Re-place the bots onto the waypoint system | |||
135 | if (!erot->SpecialForce) { | |||
136 | // Standard bots are randomly placed on one waypoint | |||
137 | int wp = teleport_to_random_waypoint(erot, curShip.AllLevels[level_num], wp_used); | |||
138 | wp_used[wp] = 1; | |||
139 | erot->homewaypoint = erot->lastwaypoint; | |||
140 | erot->combat_state = SELECT_NEW_WAYPOINT; | |||
141 | erot->state_timeout = 0.0; | |||
142 | } else { | |||
143 | if (erot->homewaypoint == -1) { | |||
144 | // If a special force droid has not yet been integrated onto | |||
145 | // the waypoint system, place it near its current position. | |||
146 | int wp = teleport_to_closest_waypoint(erot); | |||
147 | wp_used[wp] = 1; | |||
148 | erot->homewaypoint = erot->lastwaypoint; | |||
149 | erot->combat_state = SELECT_NEW_WAYPOINT; | |||
150 | erot->state_timeout = 0.0; | |||
151 | } else { | |||
152 | // Consider that the nextwaypoint of a special force droid | |||
153 | // is occupied, so that a standard bot will not be placed here | |||
154 | if (erot->nextwaypoint != -1) | |||
155 | wp_used[erot->nextwaypoint] = 1; | |||
156 | } | |||
157 | } | |||
158 | } else { | |||
159 | error_message(__FUNCTION__, "There is no waypoint on level %d - unable to place random bots.", | |||
160 | PLEASE_INFORM, level_num); | |||
161 | } | |||
162 | } | |||
163 | } | |||
164 | ||||
165 | /** | |||
166 | * \brief Get the center coordinates of a given map label. | |||
167 | * In case of fail, a fatal error is thrown. | |||
168 | * \param map_label The name of map label to resolve. | |||
169 | * \return The gps center of the map label. | |||
170 | */ | |||
171 | gps get_map_label_center(const char *map_label) | |||
172 | { | |||
173 | struct map_label *m; | |||
174 | gps position = {0., 0., -1}; | |||
175 | int i; | |||
176 | ||||
177 | for (i = 0; i < curShip.num_levels; i++) { | |||
178 | if (!level_exists(i)) | |||
179 | continue; | |||
180 | ||||
181 | m = get_map_label(curShip.AllLevels[i], map_label); | |||
182 | if (m) { | |||
183 | position.x = m->pos.x + 0.5; | |||
184 | position.y = m->pos.y + 0.5; | |||
185 | position.z = i; | |||
186 | return position; | |||
187 | } | |||
188 | } | |||
189 | ||||
190 | error_message(__FUNCTION__, "\ | |||
191 | Resolving map label \"%s\" failed on the entire world!\n\ | |||
192 | This is a severe error in the game data of FreedroidRPG.", PLEASE_INFORM, map_label); | |||
193 | ||||
194 | return position; | |||
195 | }; | |||
196 | ||||
197 | /** | |||
198 | * Next we extract the level interface data from the human-readable data | |||
199 | * into the level struct, but WITHOUT destroying or damaging the | |||
200 | * human-readable data in the process! | |||
201 | */ | |||
202 | static void decode_interfaces(level *loadlevel, char *DataPointer) | |||
203 | { | |||
204 | char *TempSectionPointer; | |||
205 | char PreservedLetter; | |||
206 | ||||
207 | // We look for the beginning and end of the map statement section | |||
208 | TempSectionPointer = LocateStringInData(DataPointer, MAP_BEGIN_STRING"beginning_of_map"); | |||
209 | ||||
210 | // We add a terminator at the end, but ONLY TEMPORARY. The damage will be restored later! | |||
211 | PreservedLetter = TempSectionPointer[0]; | |||
212 | TempSectionPointer[0] = 0; | |||
213 | ||||
214 | ReadValueFromString(DataPointer, "jump target north: ", "%d", &(loadlevel->jump_target_north), TempSectionPointer); | |||
215 | ReadValueFromString(DataPointer, "jump target south: ", "%d", &(loadlevel->jump_target_south), TempSectionPointer); | |||
216 | ReadValueFromString(DataPointer, "jump target east: ", "%d", &(loadlevel->jump_target_east), TempSectionPointer); | |||
217 | ReadValueFromString(DataPointer, "jump target west: ", "%d", &(loadlevel->jump_target_west), TempSectionPointer); | |||
218 | ||||
219 | TempSectionPointer[0] = PreservedLetter; | |||
220 | ||||
221 | } | |||
222 | ||||
223 | static void decode_dimensions(level *loadlevel, char *DataPointer) | |||
224 | { | |||
225 | ||||
226 | int off = 0; | |||
227 | ||||
228 | /* Read levelnumber */ | |||
229 | char *fp = DataPointer; | |||
230 | fp += strlen(LEVEL_HEADER_LEVELNUMBER"Levelnumber:"); | |||
231 | while (*(fp + off) != '\n') | |||
232 | off++; | |||
233 | fp[off] = 0; | |||
234 | loadlevel->levelnum = atoi(fp); | |||
235 | fp[off] = '\n'; | |||
236 | fp += off + 1; | |||
237 | off = 0; | |||
238 | ||||
239 | /* Read xlen */ | |||
240 | fp += strlen("xlen of this level:"); | |||
241 | while (*(fp + off) != '\n') | |||
242 | off++; | |||
243 | fp[off] = 0; | |||
244 | loadlevel->xlen = atoi(fp); | |||
245 | fp[off] = '\n'; | |||
246 | fp += off + 1; | |||
247 | off = 0; | |||
248 | ||||
249 | /* Read ylen */ | |||
250 | fp += strlen("ylen of this level:"); | |||
251 | while (*(fp + off) != '\n') | |||
252 | off++; | |||
253 | fp[off] = 0; | |||
254 | loadlevel->ylen = atoi(fp); | |||
255 | fp[off] = '\n'; | |||
256 | fp += off + 1; | |||
257 | off = 0; | |||
258 | ||||
259 | /* Read floor_layers */ | |||
260 | fp += strlen("floor layers:"); | |||
261 | while (*(fp + off) != '\n') | |||
262 | off++; | |||
263 | fp[off] = 0; | |||
264 | loadlevel->floor_layers = atoi(fp); | |||
265 | fp[off] = '\n'; | |||
266 | fp += off + 1; | |||
267 | off = 0; | |||
268 | ||||
269 | /* Read lrb */ | |||
270 | fp += strlen("light radius bonus of this level:"); | |||
271 | while (*(fp + off) != '\n') | |||
272 | off++; | |||
273 | fp[off] = 0; | |||
274 | loadlevel->light_bonus = atoi(fp); | |||
275 | fp[off] = '\n'; | |||
276 | fp += off + 1; | |||
277 | off = 0; | |||
278 | ||||
279 | fp += strlen("minimal light on this level:"); | |||
280 | while (*(fp + off) != '\n') | |||
281 | off++; | |||
282 | fp[off] = 0; | |||
283 | loadlevel->minimum_light_value = atoi(fp); | |||
284 | fp[off] = '\n'; | |||
285 | fp += off + 1; | |||
286 | off = 0; | |||
287 | ||||
288 | fp += strlen("infinite_running_on_this_level:"); | |||
289 | while (*(fp + off) != '\n') | |||
290 | off++; | |||
291 | fp[off] = 0; | |||
292 | loadlevel->infinite_running_on_this_level = atoi(fp); | |||
293 | fp[off] = '\n'; | |||
294 | fp += off + 1; | |||
295 | off = 0; | |||
296 | ||||
297 | fp += strlen("random dungeon:"); | |||
298 | while (*(fp + off) != '\n') | |||
299 | off++; | |||
300 | fp[off] = 0; | |||
301 | loadlevel->random_dungeon = atoi(fp); | |||
302 | fp[off] = '\n'; | |||
303 | fp += off + 1; | |||
304 | off = 0; | |||
305 | ||||
306 | if (!strncmp(fp, TELEPORT_PAIR_STRING, strlen(TELEPORT_PAIR_STRING))(__extension__ (__builtin_constant_p (strlen("teleport pair:" )) && ((__builtin_constant_p (fp) && strlen ( fp) < ((size_t) (strlen("teleport pair:")))) || (__builtin_constant_p ("teleport pair:") && strlen ("teleport pair:") < ((size_t) (strlen("teleport pair:"))))) ? __extension__ ({ size_t __s1_len, __s2_len; (__builtin_constant_p (fp) && __builtin_constant_p ("teleport pair:") && (__s1_len = strlen (fp), __s2_len = strlen ("teleport pair:"), (!((size_t)(const void *)((fp) + 1) - (size_t)(const void *)(fp) == 1) || __s1_len >= 4) && (!((size_t)(const void *)(("teleport pair:") + 1) - (size_t) (const void *)("teleport pair:") == 1) || __s2_len >= 4)) ? __builtin_strcmp (fp, "teleport pair:") : (__builtin_constant_p (fp) && ((size_t)(const void *)((fp) + 1) - (size_t) (const void *)(fp) == 1) && (__s1_len = strlen (fp), __s1_len < 4) ? (__builtin_constant_p ("teleport pair:") && ((size_t)(const void *)(("teleport pair:") + 1) - (size_t)(const void *)("teleport pair:") == 1) ? __builtin_strcmp (fp, "teleport pair:" ) : (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) ("teleport pair:"); int __result = (( (const unsigned char *) (const char *) (fp))[0] - __s2[0]); if (__s1_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) (fp))[1] - __s2[1]); if (__s1_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) (fp))[2] - __s2[2]); if (__s1_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (fp))[3] - __s2[3]); } } __result; }))) : ( __builtin_constant_p ("teleport pair:") && ((size_t)( const void *)(("teleport pair:") + 1) - (size_t)(const void * )("teleport pair:") == 1) && (__s2_len = strlen ("teleport pair:" ), __s2_len < 4) ? (__builtin_constant_p (fp) && ( (size_t)(const void *)((fp) + 1) - (size_t)(const void *)(fp) == 1) ? __builtin_strcmp (fp, "teleport pair:") : (- (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) (fp); int __result = (((const unsigned char *) (const char *) ("teleport pair:"))[0] - __s2[0]); if (__s2_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) ("teleport pair:"))[1] - __s2[1]); if (__s2_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) ("teleport pair:"))[2] - __s2[2]); if (__s2_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) ("teleport pair:"))[3] - __s2 [3]); } } __result; })))) : __builtin_strcmp (fp, "teleport pair:" )))); }) : strncmp (fp, "teleport pair:", strlen("teleport pair:" ))))) { | |||
307 | fp += strlen(TELEPORT_PAIR_STRING"teleport pair:"); | |||
308 | while (*(fp + off) != '\n') | |||
309 | off++; | |||
310 | fp[off] = 0; | |||
311 | loadlevel->teleport_pair = atoi(fp); | |||
312 | fp[off] = '\n'; | |||
313 | fp += off + 1; | |||
314 | off = 0; | |||
315 | } else { | |||
316 | loadlevel->teleport_pair = 0; | |||
317 | } | |||
318 | ||||
319 | if (!strncmp(fp, "dungeon generated:", 18)(__extension__ (__builtin_constant_p (18) && ((__builtin_constant_p (fp) && strlen (fp) < ((size_t) (18))) || (__builtin_constant_p ("dungeon generated:") && strlen ("dungeon generated:" ) < ((size_t) (18)))) ? __extension__ ({ size_t __s1_len, __s2_len ; (__builtin_constant_p (fp) && __builtin_constant_p ( "dungeon generated:") && (__s1_len = strlen (fp), __s2_len = strlen ("dungeon generated:"), (!((size_t)(const void *)(( fp) + 1) - (size_t)(const void *)(fp) == 1) || __s1_len >= 4) && (!((size_t)(const void *)(("dungeon generated:" ) + 1) - (size_t)(const void *)("dungeon generated:") == 1) || __s2_len >= 4)) ? __builtin_strcmp (fp, "dungeon generated:" ) : (__builtin_constant_p (fp) && ((size_t)(const void *)((fp) + 1) - (size_t)(const void *)(fp) == 1) && ( __s1_len = strlen (fp), __s1_len < 4) ? (__builtin_constant_p ("dungeon generated:") && ((size_t)(const void *)(("dungeon generated:" ) + 1) - (size_t)(const void *)("dungeon generated:") == 1) ? __builtin_strcmp (fp, "dungeon generated:") : (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) ("dungeon generated:"); int __result = (((const unsigned char *) (const char *) (fp))[0] - __s2[0]); if (__s1_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) (fp))[1] - __s2[1]); if (__s1_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) (fp))[2] - __s2[2]); if (__s1_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (fp ))[3] - __s2[3]); } } __result; }))) : (__builtin_constant_p ( "dungeon generated:") && ((size_t)(const void *)(("dungeon generated:" ) + 1) - (size_t)(const void *)("dungeon generated:") == 1) && (__s2_len = strlen ("dungeon generated:"), __s2_len < 4) ? (__builtin_constant_p (fp) && ((size_t)(const void * )((fp) + 1) - (size_t)(const void *)(fp) == 1) ? __builtin_strcmp (fp, "dungeon generated:") : (- (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) (fp); int __result = (((const unsigned char *) (const char *) ("dungeon generated:" ))[0] - __s2[0]); if (__s2_len > 0 && __result == 0 ) { __result = (((const unsigned char *) (const char *) ("dungeon generated:" ))[1] - __s2[1]); if (__s2_len > 1 && __result == 0 ) { __result = (((const unsigned char *) (const char *) ("dungeon generated:" ))[2] - __s2[2]); if (__s2_len > 2 && __result == 0 ) __result = (((const unsigned char *) (const char *) ("dungeon generated:" ))[3] - __s2[3]); } } __result; })))) : __builtin_strcmp (fp, "dungeon generated:")))); }) : strncmp (fp, "dungeon generated:" , 18)))) { | |||
320 | fp += strlen("dungeon generated:"); | |||
321 | while (*(fp + off) != '\n') | |||
322 | off++; | |||
323 | fp[off] = 0; | |||
324 | loadlevel->dungeon_generated = atoi(fp); | |||
325 | fp[off] = '\n'; | |||
326 | fp += off + 1; | |||
327 | } else { | |||
328 | loadlevel->dungeon_generated = 0; | |||
329 | } | |||
330 | ||||
331 | if (!strncmp(fp, "environmental flags:", 20)(__extension__ (__builtin_constant_p (20) && ((__builtin_constant_p (fp) && strlen (fp) < ((size_t) (20))) || (__builtin_constant_p ("environmental flags:") && strlen ("environmental flags:" ) < ((size_t) (20)))) ? __extension__ ({ size_t __s1_len, __s2_len ; (__builtin_constant_p (fp) && __builtin_constant_p ( "environmental flags:") && (__s1_len = strlen (fp), __s2_len = strlen ("environmental flags:"), (!((size_t)(const void *) ((fp) + 1) - (size_t)(const void *)(fp) == 1) || __s1_len >= 4) && (!((size_t)(const void *)(("environmental flags:" ) + 1) - (size_t)(const void *)("environmental flags:") == 1) || __s2_len >= 4)) ? __builtin_strcmp (fp, "environmental flags:" ) : (__builtin_constant_p (fp) && ((size_t)(const void *)((fp) + 1) - (size_t)(const void *)(fp) == 1) && ( __s1_len = strlen (fp), __s1_len < 4) ? (__builtin_constant_p ("environmental flags:") && ((size_t)(const void *)( ("environmental flags:") + 1) - (size_t)(const void *)("environmental flags:" ) == 1) ? __builtin_strcmp (fp, "environmental flags:") : (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) ("environmental flags:"); int __result = (((const unsigned char *) (const char *) (fp))[0] - __s2[0]); if (__s1_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) (fp))[1] - __s2[1]); if (__s1_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) (fp))[2] - __s2[2]); if (__s1_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (fp ))[3] - __s2[3]); } } __result; }))) : (__builtin_constant_p ( "environmental flags:") && ((size_t)(const void *)(("environmental flags:" ) + 1) - (size_t)(const void *)("environmental flags:") == 1) && (__s2_len = strlen ("environmental flags:"), __s2_len < 4) ? (__builtin_constant_p (fp) && ((size_t)(const void *)((fp) + 1) - (size_t)(const void *)(fp) == 1) ? __builtin_strcmp (fp, "environmental flags:") : (- (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) (fp); int __result = (((const unsigned char *) (const char *) ("environmental flags:" ))[0] - __s2[0]); if (__s2_len > 0 && __result == 0 ) { __result = (((const unsigned char *) (const char *) ("environmental flags:" ))[1] - __s2[1]); if (__s2_len > 1 && __result == 0 ) { __result = (((const unsigned char *) (const char *) ("environmental flags:" ))[2] - __s2[2]); if (__s2_len > 2 && __result == 0 ) __result = (((const unsigned char *) (const char *) ("environmental flags:" ))[3] - __s2[3]); } } __result; })))) : __builtin_strcmp (fp, "environmental flags:")))); }) : strncmp (fp, "environmental flags:" , 20)))) { | |||
332 | fp += strlen("environmental flags:"); | |||
333 | while (*(fp + off) != '\n') | |||
334 | off++; | |||
335 | fp[off] = 0; | |||
336 | loadlevel->flags = atoi(fp); | |||
337 | fp[off] = '\n'; | |||
338 | fp += off + 1; | |||
339 | } else { | |||
340 | loadlevel->flags = 0; | |||
341 | } | |||
342 | ||||
343 | if (!strncmp(fp, "item drop class:", 16)(__extension__ (__builtin_constant_p (16) && ((__builtin_constant_p (fp) && strlen (fp) < ((size_t) (16))) || (__builtin_constant_p ("item drop class:") && strlen ("item drop class:") < ((size_t) (16)))) ? __extension__ ({ size_t __s1_len, __s2_len ; (__builtin_constant_p (fp) && __builtin_constant_p ( "item drop class:") && (__s1_len = strlen (fp), __s2_len = strlen ("item drop class:"), (!((size_t)(const void *)((fp ) + 1) - (size_t)(const void *)(fp) == 1) || __s1_len >= 4 ) && (!((size_t)(const void *)(("item drop class:") + 1) - (size_t)(const void *)("item drop class:") == 1) || __s2_len >= 4)) ? __builtin_strcmp (fp, "item drop class:") : (__builtin_constant_p (fp) && ((size_t)(const void *)((fp) + 1) - (size_t) (const void *)(fp) == 1) && (__s1_len = strlen (fp), __s1_len < 4) ? (__builtin_constant_p ("item drop class:") && ((size_t)(const void *)(("item drop class:") + 1) - (size_t) (const void *)("item drop class:") == 1) ? __builtin_strcmp ( fp, "item drop class:") : (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) ("item drop class:" ); int __result = (((const unsigned char *) (const char *) (fp ))[0] - __s2[0]); if (__s1_len > 0 && __result == 0 ) { __result = (((const unsigned char *) (const char *) (fp)) [1] - __s2[1]); if (__s1_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) (fp))[ 2] - __s2[2]); if (__s1_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (fp))[3] - __s2[3 ]); } } __result; }))) : (__builtin_constant_p ("item drop class:" ) && ((size_t)(const void *)(("item drop class:") + 1 ) - (size_t)(const void *)("item drop class:") == 1) && (__s2_len = strlen ("item drop class:"), __s2_len < 4) ? ( __builtin_constant_p (fp) && ((size_t)(const void *)( (fp) + 1) - (size_t)(const void *)(fp) == 1) ? __builtin_strcmp (fp, "item drop class:") : (- (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) (fp); int __result = (((const unsigned char *) (const char *) ("item drop class:" ))[0] - __s2[0]); if (__s2_len > 0 && __result == 0 ) { __result = (((const unsigned char *) (const char *) ("item drop class:" ))[1] - __s2[1]); if (__s2_len > 1 && __result == 0 ) { __result = (((const unsigned char *) (const char *) ("item drop class:" ))[2] - __s2[2]); if (__s2_len > 2 && __result == 0 ) __result = (((const unsigned char *) (const char *) ("item drop class:" ))[3] - __s2[3]); } } __result; })))) : __builtin_strcmp (fp, "item drop class:")))); }) : strncmp (fp, "item drop class:" , 16)))) { | |||
344 | fp += strlen("item drop class:"); | |||
345 | while (*(fp + off) != '\n') | |||
346 | off++; | |||
347 | fp[off] = 0; | |||
348 | loadlevel->drop_class = atoi(fp); | |||
349 | fp[off] = '\n'; | |||
350 | fp += off + 1; | |||
351 | } else { | |||
352 | loadlevel->drop_class = 0; | |||
353 | } | |||
354 | ||||
355 | if (loadlevel->ylen >= MAX_MAP_LINES100) { | |||
356 | error_message(__FUNCTION__, "\ | |||
357 | A map/level in FreedroidRPG which was supposed to load has more map lines than allowed\n\ | |||
358 | for a map/level as by the constant MAX_MAP_LINES in defs.h.\n\ | |||
359 | Sorry, but unless this constant is raised, FreedroidRPG will refuse to load this map.", PLEASE_INFORM | IS_FATAL); | |||
360 | } | |||
361 | } | |||
362 | ||||
363 | static void decode_random_droids(level *loadlevel, char *data) | |||
364 | { | |||
365 | char *search_ptr; | |||
366 | char *end_ptr; | |||
367 | ||||
368 | #define DROIDS_NUMBER_INDICATION_STRING"number of random droids: " "number of random droids: " | |||
369 | #define ALLOWED_TYPE_INDICATION_STRING"random droid types: " "random droid types: " | |||
370 | ||||
371 | // Read the number of random droids for this level | |||
372 | end_ptr = strstr(data, ALLOWED_TYPE_INDICATION_STRING"random droid types: "); | |||
373 | ReadValueFromString(data, DROIDS_NUMBER_INDICATION_STRING"number of random droids: ", "%d", &loadlevel->random_droids.nr, end_ptr); | |||
374 | ||||
375 | data = strstr(data, ALLOWED_TYPE_INDICATION_STRING"random droid types: "); | |||
376 | ||||
377 | // Now we read in the type(s) of random droids for this level | |||
378 | search_ptr = ReadAndMallocStringFromDataOptional(data, ALLOWED_TYPE_INDICATION_STRING"random droid types: ", "\n"); | |||
379 | if (search_ptr && (loadlevel->random_droids.nr > 0)) { | |||
380 | char *droid_type_ptr = search_ptr; | |||
381 | while (*droid_type_ptr) { | |||
382 | while (*droid_type_ptr && isspace(*droid_type_ptr)((*__ctype_b_loc ())[(int) ((*droid_type_ptr))] & (unsigned short int) _ISspace)) { | |||
383 | droid_type_ptr++; | |||
384 | } | |||
385 | int droid_type_length = 0; | |||
386 | char *ptr = droid_type_ptr; | |||
387 | while (isalnum(*ptr)((*__ctype_b_loc ())[(int) ((*ptr))] & (unsigned short int ) _ISalnum)) { | |||
388 | ptr++; | |||
389 | droid_type_length++; | |||
390 | } | |||
391 | if (!droid_type_length) | |||
392 | break; | |||
393 | ||||
394 | char type_indication_string[droid_type_length + 1]; | |||
395 | strncpy(type_indication_string, droid_type_ptr, droid_type_length)__builtin_strncpy (type_indication_string, droid_type_ptr, droid_type_length ); | |||
396 | type_indication_string[droid_type_length] = 0; | |||
397 | ||||
398 | int droid_type = get_droid_type(type_indication_string); | |||
399 | ||||
400 | loadlevel->random_droids.types[loadlevel->random_droids.types_size++] = droid_type; | |||
401 | ||||
402 | droid_type_ptr += droid_type_length; | |||
403 | if (*droid_type_ptr) | |||
404 | droid_type_ptr++; //skip the comma | |||
405 | } | |||
406 | free(search_ptr); | |||
407 | } | |||
408 | } | |||
409 | ||||
410 | static int decode_header(level *loadlevel, char *data) | |||
411 | { | |||
412 | data = strstr(data, LEVEL_HEADER_LEVELNUMBER"Levelnumber:"); | |||
413 | if (!data) | |||
414 | return 1; | |||
415 | ||||
416 | decode_interfaces(loadlevel, data); | |||
417 | decode_dimensions(loadlevel, data); | |||
418 | decode_random_droids(loadlevel, data); | |||
419 | ||||
420 | // Read the levelname. | |||
421 | loadlevel->Levelname = ReadAndMallocStringFromData(data, LEVEL_NAME_STRING"Name of this level=_\"", "\""); | |||
422 | ||||
423 | loadlevel->Background_Song_Name = ReadAndMallocStringFromData(data, BACKGROUND_SONG_NAME_STRING"BgSong=", "\n"); | |||
424 | ||||
425 | return 0; | |||
426 | } | |||
427 | ||||
428 | /** | |||
429 | * Next we extract the human readable obstacle data into the level struct | |||
430 | * WITHOUT destroying or damaging the human-readable data in the process! | |||
431 | * This is an improved parser that is not quite readable but very performant. | |||
432 | */ | |||
433 | static char *decode_obstacles(level *loadlevel, char *DataPointer) | |||
434 | { | |||
435 | int i; | |||
436 | char *curfield = NULL((void*)0); | |||
437 | char *curfieldend = NULL((void*)0); | |||
438 | char *obstacle_SectionBegin; | |||
439 | float x, y; | |||
440 | int type; | |||
441 | ||||
442 | // First we initialize the obstacles with 'empty' information | |||
443 | // | |||
444 | for (i = 0; i < MAX_OBSTACLES_ON_MAP4000; i++) { | |||
445 | loadlevel->obstacle_list[i].type = -1; | |||
446 | loadlevel->obstacle_list[i].pos.x = -1; | |||
447 | loadlevel->obstacle_list[i].pos.y = -1; | |||
448 | loadlevel->obstacle_list[i].pos.z = loadlevel->levelnum; | |||
449 | loadlevel->obstacle_list[i].timestamp = 0; | |||
450 | loadlevel->obstacle_list[i].frame_index = 0; | |||
451 | } | |||
452 | ||||
453 | if (loadlevel->random_dungeon && !loadlevel->dungeon_generated) | |||
454 | return DataPointer; | |||
455 | ||||
456 | // Now we look for the beginning and end of the obstacle section | |||
457 | // | |||
458 | obstacle_SectionBegin = LocateStringInData(DataPointer, OBSTACLE_DATA_BEGIN_STRING"obsdata") + strlen(OBSTACLE_DATA_BEGIN_STRING"obsdata") + 1; | |||
459 | ||||
460 | // Now we decode all the obstacle information | |||
461 | // | |||
462 | curfield = obstacle_SectionBegin; | |||
463 | while (*curfield != '/') { | |||
464 | //structure of obstacle entry is : // t59 x2.50 y63.50 l-1 d-1 | |||
465 | //we read the type | |||
466 | curfield++; | |||
467 | curfieldend = curfield; | |||
468 | while ((*curfieldend) != ' ') | |||
469 | curfieldend++; | |||
470 | (*curfieldend) = 0; | |||
471 | type = atoi(curfield); | |||
472 | (*curfieldend) = ' '; | |||
473 | ||||
474 | //we read the X position | |||
475 | curfield = curfieldend + 2; | |||
476 | curfieldend += 2; | |||
477 | while ((*curfieldend) != ' ') | |||
478 | curfieldend++; | |||
479 | (*curfieldend) = 0; | |||
480 | x = atof(curfield); | |||
481 | (*curfieldend) = ' '; | |||
482 | ||||
483 | //Y position | |||
484 | curfield = curfieldend + 2; | |||
485 | curfieldend += 2; | |||
486 | while ((*curfieldend) != ' ') | |||
487 | curfieldend++; | |||
488 | (*curfieldend) = 0; | |||
489 | y = atof(curfield); | |||
490 | (*curfieldend) = ' '; | |||
491 | ||||
492 | while ((*curfield) != '\n') | |||
493 | curfield++; | |||
494 | curfield++; | |||
495 | ||||
496 | add_obstacle(loadlevel, x, y, type); | |||
497 | } | |||
498 | ||||
499 | return curfield; | |||
500 | } | |||
501 | ||||
502 | /** | |||
503 | * Next we extract the map labels of this level WITHOUT destroying | |||
504 | * or damaging the data in the process! | |||
505 | */ | |||
506 | static char *decode_map_labels(level *loadlevel, char *data) | |||
507 | { | |||
508 | char *label_name; | |||
509 | int i, x, y; | |||
510 | ||||
511 | // Initialize map labels | |||
512 | dynarray_init(&loadlevel->map_labels, 10, sizeof(struct map_label)); | |||
513 | ||||
514 | if (loadlevel->random_dungeon && !loadlevel->dungeon_generated) | |||
515 | return data; | |||
516 | ||||
517 | // Now we look for the beginning and end of the map labels section | |||
518 | char *map_label_begin = LocateStringInData(data, MAP_LABEL_BEGIN_STRING"plabelinfolvl") + strlen(MAP_LABEL_BEGIN_STRING"plabelinfolvl") + 1; | |||
519 | char *map_label_end = LocateStringInData(map_label_begin, MAP_LABEL_END_STRING"/plabelinfolvl"); | |||
520 | *map_label_end = '\0'; | |||
521 | ||||
522 | // Get the number of map labels in this level | |||
523 | int nb_map_labels_in_level = CountStringOccurences(map_label_begin, LABEL_ITSELF_ANNOUNCE_STRING"name=\""); | |||
524 | DebugPrintf(1, "\nNumber of map labels found in this level : %d.", nb_map_labels_in_level); | |||
525 | ||||
526 | // Now we decode all the map label information | |||
527 | for (i = 0; i < nb_map_labels_in_level ; i++) { | |||
528 | if (i) | |||
529 | map_label_begin = strstr(map_label_begin + 1, X_POSITION_OF_LABEL_STRING"x="); | |||
530 | ||||
531 | // Get the position of the map label | |||
532 | ReadValueFromString(map_label_begin, X_POSITION_OF_LABEL_STRING"x=", "%d", &x, map_label_end); | |||
533 | ReadValueFromString(map_label_begin, Y_POSITION_OF_LABEL_STRING"y=", "%d", &y, map_label_end); | |||
534 | ||||
535 | // Get the name of the map label | |||
536 | label_name = ReadAndMallocStringFromData(map_label_begin, LABEL_ITSELF_ANNOUNCE_STRING"name=\"", "\""); | |||
537 | ||||
538 | // Add the map label on the level | |||
539 | add_map_label(loadlevel, x, y, label_name); | |||
540 | ||||
541 | DebugPrintf(1, "\npos.x=%d pos.y=%d label_name=\"%s\"", x, y, label_name); | |||
542 | } | |||
543 | ||||
544 | *map_label_end = MAP_LABEL_END_STRING"/plabelinfolvl"[0]; | |||
545 | return map_label_end; | |||
546 | } | |||
547 | ||||
548 | static void ReadInOneItem(char *ItemPointer, char *ItemsSectionEnd, item *TargetItem) | |||
549 | { | |||
550 | init_item(TargetItem); | |||
551 | ||||
552 | char *item_id = ReadAndMallocStringFromData(ItemPointer, ITEM_ID_STRING"it: id=\"", "\""); | |||
553 | TargetItem->type = get_item_type_by_id(item_id); | |||
554 | free(item_id); | |||
555 | ||||
556 | ReadValueFromString(ItemPointer, ITEM_POS_X_STRING" X=", "%f", &(TargetItem->pos.x), ItemsSectionEnd); | |||
557 | ReadValueFromString(ItemPointer, ITEM_POS_Y_STRING" Y=", "%f", &(TargetItem->pos.y), ItemsSectionEnd); | |||
558 | ReadValueFromStringWithDefault(ItemPointer, ITEM_ARMOR_CLASS_BASE_STRING" AC=", "%d", "0", &(TargetItem->armor_class), ItemsSectionEnd); | |||
559 | ReadValueFromString(ItemPointer, ITEM_MAX_DURABILITY_STRING" MDur=", "%d", &(TargetItem->max_durability), ItemsSectionEnd); | |||
560 | ReadValueFromString(ItemPointer, ITEM_CUR_DURABILITY_STRING" CDur=", "%f", &(TargetItem->current_durability), ItemsSectionEnd); | |||
561 | ReadValueFromString(ItemPointer, ITEM_AMMO_CLIP_STRING" AClip=", "%d", &(TargetItem->ammo_clip), ItemsSectionEnd); | |||
562 | ReadValueFromString(ItemPointer, ITEM_MULTIPLICITY_STRING" Multi=", "%d", &(TargetItem->multiplicity), ItemsSectionEnd); | |||
563 | ||||
564 | // Read the socket data of the item and calculate bonuses using it. | |||
565 | int i; | |||
566 | int socket_count; | |||
567 | ReadValueFromStringWithDefault(ItemPointer, ITEM_SOCKETS_SIZE_STRING" Sockets=", "%d", "0", &socket_count, ItemsSectionEnd); | |||
568 | for (i = 0; i < socket_count; i++) { | |||
569 | char type_string[32]; | |||
570 | char addon_string[32]; | |||
571 | struct upgrade_socket socket; | |||
572 | sprintf(type_string, "%s%d=", ITEM_SOCKET_TYPE_STRING"SocketType", i); | |||
573 | sprintf(addon_string, "%s%d=", ITEM_SOCKET_ADDON_STRING"SocketAddon", i); | |||
574 | ReadValueFromString(ItemPointer, type_string, "%d", &socket.type, ItemsSectionEnd); | |||
575 | socket.addon = ReadAndMallocStringFromDataOptional(ItemPointer, addon_string, "\""); | |||
576 | create_upgrade_socket(TargetItem, socket.type, socket.addon); | |||
577 | free(socket.addon); | |||
578 | } | |||
579 | calculate_item_bonuses(TargetItem); | |||
580 | ||||
581 | DebugPrintf(1, "\nPosX=%f PosY=%f Item=%d", TargetItem->pos.x, TargetItem->pos.y, TargetItem->type); | |||
582 | ||||
583 | } | |||
584 | ||||
585 | static char *decode_extension_chest(char *ext, void **data) | |||
586 | { | |||
587 | struct dynarray *chest = dynarray_alloc(1, sizeof(item)); | |||
588 | char *item_str, *item_end; | |||
589 | ||||
590 | item_str = ext; | |||
591 | ||||
592 | while (*item_str != '}') { | |||
593 | // Find end of this item (beginning of next item) | |||
594 | item_end = item_str; | |||
595 | while (*item_end != '\n') | |||
596 | item_end++; | |||
597 | while (isspace(*item_end)((*__ctype_b_loc ())[(int) ((*item_end))] & (unsigned short int) _ISspace)) | |||
598 | item_end++; | |||
599 | ||||
600 | // Read the item on this line | |||
601 | item new_item; | |||
602 | ReadInOneItem(item_str, item_end, &new_item); | |||
603 | ||||
604 | // Add the item to the dynarray | |||
605 | dynarray_add(chest, &new_item, sizeof(item)); | |||
606 | ||||
607 | // Move to the next item definition | |||
608 | item_str = item_end; | |||
609 | } | |||
610 | ||||
611 | ||||
612 | *data = chest; | |||
613 | return item_str; | |||
614 | } | |||
615 | ||||
616 | static char *decode_extension_label(char *ext, void **data) | |||
617 | { | |||
618 | char *end = ext; | |||
619 | while (*end != '\n') | |||
620 | end++; | |||
621 | ||||
622 | *end = '\0'; | |||
623 | *data = strdup(ext)(__extension__ (__builtin_constant_p (ext) && ((size_t )(const void *)((ext) + 1) - (size_t)(const void *)(ext) == 1 ) ? (((const char *) (ext))[0] == '\0' ? (char *) calloc ((size_t ) 1, (size_t) 1) : ({ size_t __len = strlen (ext) + 1; char * __retval = (char *) malloc (__len); if (__retval != ((void*)0 )) __retval = (char *) memcpy (__retval, ext, __len); __retval ; })) : __strdup (ext))); | |||
624 | *end = '\n'; | |||
625 | ||||
626 | while (*end != '}') | |||
627 | end++; | |||
628 | ||||
629 | return end; | |||
630 | } | |||
631 | ||||
632 | static char *decode_extension_dialog(char *ext, void **data) | |||
633 | { | |||
634 | // dialog and label extensions are both a string | |||
635 | return decode_extension_label(ext, data); | |||
636 | } | |||
637 | ||||
638 | static char *decode_obstacle_extensions(level *loadlevel, char *data) | |||
639 | { | |||
640 | dynarray_init(&loadlevel->obstacle_extensions, 10, sizeof(struct obstacle_extension)); | |||
641 | ||||
642 | if (loadlevel->random_dungeon && !loadlevel->dungeon_generated) | |||
643 | return data; | |||
644 | ||||
645 | char *ext_begin = LocateStringInData(data, OBSTACLE_EXTENSIONS_BEGIN_STRING"obstacleextensions"); | |||
646 | char *ext_end = LocateStringInData(ext_begin, OBSTACLE_EXTENSIONS_END_STRING"/obstacleextensions"); | |||
647 | *ext_end = '\0'; | |||
648 | ||||
649 | while (1) { | |||
650 | // Look for the next extension | |||
651 | ext_begin = strstr(ext_begin, "idx="); | |||
652 | if (!ext_begin) | |||
653 | break; | |||
654 | ||||
655 | // Read extension information | |||
656 | int index; | |||
657 | int type; | |||
658 | void *ext_data = NULL((void*)0); | |||
659 | sscanf(ext_begin, "idx=%d type=%d", &index, &type); | |||
660 | ||||
661 | // Move to the extension data definition | |||
662 | ext_begin = strstr(ext_begin, "data={\n"); | |||
663 | while (*ext_begin != '\n') | |||
664 | ext_begin++; | |||
665 | while (isspace(*ext_begin)((*__ctype_b_loc ())[(int) ((*ext_begin))] & (unsigned short int) _ISspace)) | |||
666 | ext_begin++; | |||
667 | ||||
668 | // Read the extension data | |||
669 | switch (type) { | |||
670 | case OBSTACLE_EXTENSION_CHEST_ITEMS: | |||
671 | ext_begin = decode_extension_chest(ext_begin, &ext_data); | |||
672 | break; | |||
673 | case OBSTACLE_EXTENSION_LABEL: | |||
674 | ext_begin = decode_extension_label(ext_begin, &ext_data); | |||
675 | break; | |||
676 | case OBSTACLE_EXTENSION_DIALOGFILE: | |||
677 | ext_begin = decode_extension_dialog(ext_begin, &ext_data); | |||
678 | break; | |||
679 | } | |||
680 | ||||
681 | // Add the obstacle extension on the level | |||
682 | add_obstacle_extension(loadlevel, &(loadlevel->obstacle_list[index]), type, ext_data); | |||
683 | } | |||
684 | ||||
685 | *ext_end = OBSTACLE_EXTENSIONS_END_STRING"/obstacleextensions"[0]; | |||
686 | return ext_end; | |||
687 | } | |||
688 | ||||
689 | static char *decode_item_section(level *loadlevel, char *data) | |||
690 | { | |||
691 | int i; | |||
692 | char Preserved_Letter; | |||
693 | int NumberOfItemsInThisLevel; | |||
694 | char *ItemPointer; | |||
695 | char *ItemsSectionBegin; | |||
696 | char *ItemsSectionEnd; | |||
697 | ||||
698 | // First we initialize the items arrays with 'empty' information | |||
699 | // | |||
700 | for (i = 0; i < MAX_ITEMS_PER_LEVEL300; i++) { | |||
701 | init_item(&loadlevel->ItemList[i]); | |||
702 | } | |||
703 | ||||
704 | if (loadlevel->random_dungeon && !loadlevel->dungeon_generated) | |||
705 | return data; | |||
706 | ||||
707 | // We look for the beginning and end of the items section | |||
708 | ItemsSectionBegin = LocateStringInData(data, ITEMS_SECTION_BEGIN_STRING"piteminfolvl"); | |||
709 | ItemsSectionEnd = LocateStringInData(ItemsSectionBegin, ITEMS_SECTION_END_STRING"/piteminfolvl"); | |||
710 | ||||
711 | // We add a terminator at the end of the items section, but ONLY TEMPORARY. | |||
712 | // The damage will be restored later! | |||
713 | Preserved_Letter = ItemsSectionEnd[0]; | |||
714 | ItemsSectionEnd[0] = 0; | |||
715 | NumberOfItemsInThisLevel = CountStringOccurences(ItemsSectionBegin, ITEM_ID_STRING"it: id=\""); | |||
716 | DebugPrintf(1, "\nNumber of items found in this level : %d.", NumberOfItemsInThisLevel); | |||
717 | ||||
718 | // Now we decode all the item information | |||
719 | ItemPointer = ItemsSectionBegin; | |||
720 | char *NextItemPointer; | |||
721 | for (i = 0; i < NumberOfItemsInThisLevel; i++) { | |||
722 | if ((ItemPointer = strstr(ItemPointer + 1, ITEM_ID_STRING"it: id=\""))) { | |||
723 | NextItemPointer = strstr(ItemPointer + 1, ITEM_ID_STRING"it: id=\""); | |||
724 | if (NextItemPointer) | |||
725 | NextItemPointer[0] = 0; | |||
726 | ReadInOneItem(ItemPointer, ItemsSectionEnd, &(loadlevel->ItemList[i])); | |||
727 | loadlevel->ItemList[i].pos.z = loadlevel->levelnum; | |||
728 | if (NextItemPointer) | |||
729 | NextItemPointer[0] = ITEM_ID_STRING"it: id=\""[0]; | |||
730 | } | |||
731 | } | |||
732 | ||||
733 | // Now we repair the damage done to the loaded level data | |||
734 | ItemsSectionEnd[0] = Preserved_Letter; | |||
735 | return ItemsSectionEnd; | |||
736 | } | |||
737 | ||||
738 | static char *decode_map(level *loadlevel, char *data) | |||
739 | { | |||
740 | char *map_begin, *map_end; | |||
741 | char *this_line; | |||
742 | int i; | |||
743 | ||||
744 | if ((map_begin = strstr(data, MAP_BEGIN_STRING"beginning_of_map")) == NULL((void*)0)) | |||
745 | return NULL((void*)0); | |||
746 | map_begin += strlen(MAP_BEGIN_STRING"beginning_of_map") + 1; | |||
747 | ||||
748 | if ((map_end = strstr(data, MAP_END_STRING"/pmapinfolvl")) == NULL((void*)0)) | |||
749 | return NULL((void*)0); | |||
750 | ||||
751 | /* now scan the map */ | |||
752 | unsigned int curlinepos = 0; | |||
753 | this_line = (char *)MyMalloc(4096); | |||
754 | ||||
755 | /* read MapData */ | |||
756 | for (i = 0; i < loadlevel->ylen; i++) { | |||
757 | int col; | |||
758 | int layer; | |||
759 | map_tile *Buffer; | |||
760 | int tmp; | |||
761 | ||||
762 | /* Select the next line */ | |||
763 | unsigned int nlpos = 0; | |||
764 | memset(this_line, 0, 4096); | |||
765 | while (map_begin[curlinepos + nlpos] != '\n') | |||
766 | nlpos++; | |||
767 | memcpy(this_line, map_begin + curlinepos, nlpos); | |||
768 | this_line[nlpos] = '\0'; | |||
769 | nlpos++; | |||
770 | ||||
771 | /* Decode it */ | |||
772 | Buffer = MyMalloc((loadlevel->xlen + 10) * sizeof(map_tile)); | |||
773 | for (col = 0; col < loadlevel->xlen; col++) { | |||
774 | // Make sure that all floor layers are always initialized properly. | |||
775 | init_map_tile(&Buffer[col]); | |||
776 | ||||
777 | for (layer = 0; layer < loadlevel->floor_layers; layer++) { | |||
778 | tmp = strtol(this_line + 4 * (loadlevel->floor_layers * col + layer), NULL((void*)0), 10); | |||
779 | Buffer[col].floor_values[layer] = (Uint16) tmp; | |||
780 | } | |||
781 | } | |||
782 | ||||
783 | // Now the old text pointer can be replaced with a pointer to the | |||
784 | // correctly assembled struct... | |||
785 | // | |||
786 | loadlevel->map[i] = Buffer; | |||
787 | ||||
788 | curlinepos += nlpos; | |||
789 | } | |||
790 | ||||
791 | free(this_line); | |||
792 | return map_end; | |||
793 | } | |||
794 | ||||
795 | static char *decode_waypoints(level *loadlevel, char *data) | |||
796 | { | |||
797 | char *wp_begin, *wp_end; | |||
798 | char *this_line; | |||
799 | int nr, x, y, wp_rnd; | |||
800 | char *pos; | |||
801 | ||||
802 | // Initialize waypoints | |||
803 | dynarray_init(&loadlevel->waypoints, 2, sizeof(struct waypoint)); | |||
804 | ||||
805 | if (loadlevel->random_dungeon && !loadlevel->dungeon_generated) | |||
806 | return data; | |||
807 | ||||
808 | // Find the beginning and end of the waypoint list | |||
809 | if ((wp_begin = strstr(data, WP_BEGIN_STRING"wp")) == NULL((void*)0)) | |||
810 | return NULL((void*)0); | |||
811 | wp_begin += strlen(WP_BEGIN_STRING"wp") + 1; | |||
812 | ||||
813 | if ((wp_end = strstr(data, WP_END_STRING"end_of_level")) == NULL((void*)0)) | |||
814 | return NULL((void*)0); | |||
815 | ||||
816 | int curlinepos = 0; | |||
817 | this_line = (char *)MyMalloc(4096); | |||
818 | ||||
819 | while (1) { | |||
820 | /* Select the next line */ | |||
821 | short int nlpos = 0; | |||
822 | memset(this_line, 0, 4096); | |||
823 | while (wp_begin[curlinepos + nlpos] != '\n') | |||
824 | nlpos++; | |||
825 | memcpy(this_line, wp_begin + curlinepos, nlpos); | |||
826 | this_line[nlpos] = '\0'; | |||
827 | nlpos++; | |||
828 | ||||
829 | curlinepos += nlpos; | |||
830 | ||||
831 | if (!strncmp(this_line, wp_end, strlen(WP_END_STRING))(__extension__ (__builtin_constant_p (strlen("end_of_level")) && ((__builtin_constant_p (this_line) && strlen (this_line) < ((size_t) (strlen("end_of_level")))) || (__builtin_constant_p (wp_end) && strlen (wp_end) < ((size_t) (strlen("end_of_level" ))))) ? __extension__ ({ size_t __s1_len, __s2_len; (__builtin_constant_p (this_line) && __builtin_constant_p (wp_end) && (__s1_len = strlen (this_line), __s2_len = strlen (wp_end), ( !((size_t)(const void *)((this_line) + 1) - (size_t)(const void *)(this_line) == 1) || __s1_len >= 4) && (!((size_t )(const void *)((wp_end) + 1) - (size_t)(const void *)(wp_end ) == 1) || __s2_len >= 4)) ? __builtin_strcmp (this_line, wp_end ) : (__builtin_constant_p (this_line) && ((size_t)(const void *)((this_line) + 1) - (size_t)(const void *)(this_line) == 1) && (__s1_len = strlen (this_line), __s1_len < 4) ? (__builtin_constant_p (wp_end) && ((size_t)(const void *)((wp_end) + 1) - (size_t)(const void *)(wp_end) == 1) ? __builtin_strcmp (this_line, wp_end) : (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) (wp_end); int __result = (((const unsigned char *) (const char *) (this_line))[0] - __s2[0]); if (__s1_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) (this_line))[1] - __s2[1]); if (__s1_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) (this_line))[2] - __s2[2]); if (__s1_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (this_line))[3] - __s2[3]); } } __result; }))) : (__builtin_constant_p (wp_end) && ((size_t)(const void *)((wp_end) + 1) - ( size_t)(const void *)(wp_end) == 1) && (__s2_len = strlen (wp_end), __s2_len < 4) ? (__builtin_constant_p (this_line ) && ((size_t)(const void *)((this_line) + 1) - (size_t )(const void *)(this_line) == 1) ? __builtin_strcmp (this_line , wp_end) : (- (__extension__ ({ const unsigned char *__s2 = ( const unsigned char *) (const char *) (this_line); int __result = (((const unsigned char *) (const char *) (wp_end))[0] - __s2 [0]); if (__s2_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) (wp_end))[1] - __s2 [1]); if (__s2_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) (wp_end))[2] - __s2 [2]); if (__s2_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (wp_end))[3] - __s2 [3]); } } __result; })))) : __builtin_strcmp (this_line, wp_end )))); }) : strncmp (this_line, wp_end, strlen("end_of_level") )))) { | |||
832 | break; | |||
833 | } | |||
834 | wp_rnd = 0; | |||
835 | sscanf(this_line, "Nr.=%d \t x=%d \t y=%d rnd=%d", &nr, &x, &y, &wp_rnd); | |||
836 | ||||
837 | // Create a new waypoint | |||
838 | waypoint new_wp; | |||
839 | new_wp.x = x; | |||
840 | new_wp.y = y; | |||
841 | new_wp.suppress_random_spawn = wp_rnd; | |||
842 | ||||
843 | // Initalize the connections of the new waypoint | |||
844 | dynarray_init(&new_wp.connections, 2, sizeof(int)); | |||
845 | ||||
846 | pos = strstr(this_line, CONNECTION_STRING"c: "); | |||
847 | if (pos == NULL((void*)0)) { | |||
848 | fprintf(stderrstderr, "Unable to find connection string. line is %s, level %i\n", this_line, | |||
849 | loadlevel->levelnum); | |||
850 | } | |||
851 | pos += strlen(CONNECTION_STRING"c: "); // skip connection-string | |||
852 | pos += strspn(pos, WHITE_SPACE)__extension__ ({ char __a0, __a1, __a2; (__builtin_constant_p (" \t") && ((size_t)(const void *)((" \t") + 1) - (size_t )(const void *)(" \t") == 1) ? ((__builtin_constant_p (pos) && ((size_t)(const void *)((pos) + 1) - (size_t)(const void *)( pos) == 1)) ? __builtin_strspn (pos, " \t") : ((__a0 = ((const char *) (" \t"))[0], __a0 == '\0') ? ((void) (pos), (size_t) 0) : ((__a1 = ((const char *) (" \t"))[1], __a1 == '\0') ? __strspn_c1 (pos, __a0) : ((__a2 = ((const char *) (" \t"))[2], __a2 == '\0' ) ? __strspn_c2 (pos, __a0, __a1) : (((const char *) (" \t")) [3] == '\0' ? __strspn_c3 (pos, __a0, __a1, __a2) : __builtin_strspn (pos, " \t")))))) : __builtin_strspn (pos, " \t")); }); // skip initial whitespace | |||
853 | ||||
854 | while (1) { | |||
855 | if (*pos == '\0') | |||
856 | break; | |||
857 | int connection; | |||
858 | int res = sscanf(pos, "%d", &connection); | |||
859 | if ((connection == -1) || (res == 0) || (res == EOF(-1))) | |||
860 | break; | |||
861 | ||||
862 | // Add the connection on this waypoint | |||
863 | dynarray_add(&new_wp.connections, &connection, sizeof(int)); | |||
864 | ||||
865 | pos += strcspn(pos, WHITE_SPACE)__extension__ ({ char __r0, __r1, __r2; (__builtin_constant_p (" \t") && ((size_t)(const void *)((" \t") + 1) - (size_t )(const void *)(" \t") == 1) ? ((__builtin_constant_p (pos) && ((size_t)(const void *)((pos) + 1) - (size_t)(const void *)( pos) == 1)) ? __builtin_strcspn (pos, " \t") : ((__r0 = ((const char *) (" \t"))[0], __r0 == '\0') ? strlen (pos) : ((__r1 = ((const char *) (" \t"))[1], __r1 == '\0') ? __strcspn_c1 (pos , __r0) : ((__r2 = ((const char *) (" \t"))[2], __r2 == '\0') ? __strcspn_c2 (pos, __r0, __r1) : (((const char *) (" \t")) [3] == '\0' ? __strcspn_c3 (pos, __r0, __r1, __r2) : __builtin_strcspn (pos, " \t")))))) : __builtin_strcspn (pos, " \t")); }); // skip last token | |||
866 | pos += strspn(pos, WHITE_SPACE)__extension__ ({ char __a0, __a1, __a2; (__builtin_constant_p (" \t") && ((size_t)(const void *)((" \t") + 1) - (size_t )(const void *)(" \t") == 1) ? ((__builtin_constant_p (pos) && ((size_t)(const void *)((pos) + 1) - (size_t)(const void *)( pos) == 1)) ? __builtin_strspn (pos, " \t") : ((__a0 = ((const char *) (" \t"))[0], __a0 == '\0') ? ((void) (pos), (size_t) 0) : ((__a1 = ((const char *) (" \t"))[1], __a1 == '\0') ? __strspn_c1 (pos, __a0) : ((__a2 = ((const char *) (" \t"))[2], __a2 == '\0' ) ? __strspn_c2 (pos, __a0, __a1) : (((const char *) (" \t")) [3] == '\0' ? __strspn_c3 (pos, __a0, __a1, __a2) : __builtin_strspn (pos, " \t")))))) : __builtin_strspn (pos, " \t")); }); // skip initial whitespace for next one | |||
867 | } | |||
868 | ||||
869 | // Add the waypoint on the level | |||
870 | dynarray_add(&loadlevel->waypoints, &new_wp, sizeof(struct waypoint)); | |||
871 | } | |||
872 | ||||
873 | free(this_line); | |||
874 | return wp_end; | |||
875 | } | |||
876 | ||||
877 | /** | |||
878 | * The smash_obstacle function uses this function as a subfunction to | |||
879 | * check for exploding obstacles glued to one specific map square. Of | |||
880 | * course also the player number (or -1 in case of no check/bullet hit) | |||
881 | * must be supplied so as to be able to suppress hits through walls or | |||
882 | * the like. | |||
883 | */ | |||
884 | static int smash_obstacles_only_on_tile(float x, float y, int level, int map_x, int map_y) | |||
885 | { | |||
886 | Level BoxLevel = curShip.AllLevels[level]; | |||
887 | int i; | |||
888 | int target_idx; | |||
889 | obstacle *target_obstacle; | |||
890 | int smashed_something = FALSE(0); | |||
891 | moderately_finepoint blast_start_pos; | |||
892 | ||||
893 | // First some security checks against touching the outsides of the map... | |||
894 | // | |||
895 | if (!pos_inside_level(map_x, map_y, BoxLevel)) | |||
896 | return (FALSE(0)); | |||
897 | ||||
898 | // We check all the obstacles on this square if they are maybe destructible | |||
899 | // and if they are, we destruct them, haha | |||
900 | // | |||
901 | for (i = 0; i < BoxLevel->map[map_y][map_x].glued_obstacles.size; i++) { | |||
902 | // First we see if there is something glued to this map tile at all. | |||
903 | // | |||
904 | target_idx = ((int *)(BoxLevel->map[map_y][map_x].glued_obstacles.arr))[i]; | |||
905 | ||||
906 | target_obstacle = &(BoxLevel->obstacle_list[target_idx]); | |||
907 | ||||
908 | obstacle_spec *obstacle_spec = get_obstacle_spec(target_obstacle->type); | |||
909 | if (!(obstacle_spec->flags & IS_SMASHABLE)) | |||
910 | continue; | |||
911 | ||||
912 | // Now we check if the item really was close enough to the strike target. | |||
913 | // A range of 0.5 should do. | |||
914 | // | |||
915 | if (fabsf(x - target_obstacle->pos.x) > 0.4) | |||
916 | continue; | |||
917 | if (fabsf(y - target_obstacle->pos.y) > 0.4) | |||
918 | continue; | |||
919 | ||||
920 | colldet_filter filter = FlyableExceptIdPassFilter; | |||
921 | filter.data = &target_idx; | |||
922 | gps smash_pos = { x, y, level }; | |||
923 | gps vsmash_pos; | |||
924 | update_virtual_position(&vsmash_pos, &smash_pos, Me.pos.z); | |||
925 | if (vsmash_pos.x == -1) | |||
926 | continue; | |||
927 | if (!DirectLineColldet(vsmash_pos.x, vsmash_pos.y, Me.pos.x, Me.pos.y, Me.pos.z, &filter)) { | |||
928 | continue; | |||
929 | } | |||
930 | ||||
931 | DebugPrintf(1, "\nObject smashed at: (%f/%f) by hit/explosion at (%f/%f).", | |||
932 | target_obstacle->pos.x, target_obstacle->pos.y, x, y); | |||
933 | ||||
934 | smashed_something = TRUE(1); | |||
935 | ||||
936 | // Since the obstacle is destroyed, we start a blast at it's position. | |||
937 | // But here a WARNING WARNING WARNING! is due! We must not start the | |||
938 | // blast before the obstacle is removed, because the blast will again | |||
939 | // cause this very obstacle removal function, so we need to be careful | |||
940 | // so as not to incite endless recursion. We memorize the position for | |||
941 | // later, then delete the obstacle, then we start the blast. | |||
942 | // | |||
943 | blast_start_pos.x = target_obstacle->pos.x; | |||
944 | blast_start_pos.y = target_obstacle->pos.y; | |||
945 | ||||
946 | int obstacle_drops_treasure | |||
947 | = obstacle_spec->flags & DROPS_RANDOM_TREASURE; | |||
948 | ||||
949 | // Let the automap know that we've updated things | |||
950 | update_obstacle_automap(level, target_obstacle); | |||
951 | ||||
952 | // Now we really smash the obstacle, i.e. we can set it's type to the debris that has | |||
953 | // been configured for this obstacle type. In if there is nothing configured (i.e. -1 set) | |||
954 | // then we'll just delete the obstacle in question entirely. For this we got a standard function to | |||
955 | // safely do it and not make some errors into the glue structure or obstacles lists... | |||
956 | // | |||
957 | if (obstacle_spec->result_type_after_smashing_once == (-1)) { | |||
958 | del_obstacle(target_obstacle); | |||
959 | } else { | |||
960 | target_obstacle->type = obstacle_spec->result_type_after_smashing_once; | |||
961 | } | |||
962 | ||||
963 | // Drop items after destroying the obstacle, in order to avoid collisions | |||
964 | if (obstacle_drops_treasure) | |||
965 | DropRandomItem(level, target_obstacle->pos.x, target_obstacle->pos.y, BoxLevel->drop_class, FALSE(0)); | |||
966 | ||||
967 | // Now that the obstacle is removed AND ONLY NOW that the obstacle is | |||
968 | // removed, we may start a blast at this position. Otherwise we would | |||
969 | // run into trouble, see the warning further above. | |||
970 | StartBlast(blast_start_pos.x, blast_start_pos.y, | |||
971 | level, obstacle_spec->blast_type, 0.0, FACTION_SELF, obstacle_spec->smashed_sound); | |||
972 | } | |||
973 | ||||
974 | return smashed_something; | |||
975 | } | |||
976 | ||||
977 | /** | |||
978 | * When a destructible type of obstacle gets hit, e.g. by a blast | |||
979 | * exploding on the tile or a melee hit on the same floor tile, then some | |||
980 | * of the obstacles (like barrel or crates) might explode, sometimes | |||
981 | * leaving some treasure behind. | |||
982 | * | |||
983 | */ | |||
984 | int smash_obstacle(float x, float y, int level) | |||
985 | { | |||
986 | int map_x, map_y; | |||
987 | int smash_x, smash_y; | |||
988 | int smashed_something = FALSE(0); | |||
989 | ||||
990 | map_x = (int)rintf(x); | |||
991 | map_y = (int)rintf(y); | |||
992 | ||||
993 | for (smash_y = map_y - 1; smash_y < map_y + 2; smash_y++) { | |||
994 | for (smash_x = map_x - 1; smash_x < map_x + 2; smash_x++) { | |||
995 | if (smash_obstacles_only_on_tile(x, y, level, smash_x, smash_y)) | |||
996 | smashed_something = TRUE(1); | |||
997 | } | |||
998 | } | |||
999 | ||||
1000 | return (smashed_something); | |||
1001 | ||||
1002 | } // int smash_obstacle ( float x , float y ); | |||
1003 | ||||
1004 | /** | |||
1005 | * This function returns the map brick code of the tile that occupies the | |||
1006 | * given position in the given layer. | |||
1007 | * Floor layers are indexed from 0 to lvl->floor_layers - 1. The lowest | |||
1008 | * floor layer is #0. Every map has at least one layer. | |||
1009 | */ | |||
1010 | Uint16 get_map_brick(level *lvl, float x, float y, int layer) | |||
1011 | { | |||
1012 | Uint16 BrickWanted; | |||
1013 | int RoundX, RoundY; | |||
1014 | ||||
1015 | gps vpos = { x, y, lvl->levelnum }; | |||
1016 | gps rpos; | |||
1017 | if (!resolve_virtual_position(&rpos, &vpos)) { | |||
1018 | return ISO_FLOOR_EMPTY; | |||
1019 | } | |||
1020 | RoundX = (int)rintf(rpos.x); | |||
1021 | RoundY = (int)rintf(rpos.y); | |||
1022 | ||||
1023 | BrickWanted = curShip.AllLevels[rpos.z]->map[RoundY][RoundX].floor_values[layer]; | |||
1024 | ||||
1025 | if (BrickWanted >= underlay_floor_tiles.size) { | |||
1026 | if (BrickWanted < MAX_UNDERLAY_FLOOR_TILES500 || (BrickWanted - MAX_UNDERLAY_FLOOR_TILES500) >= overlay_floor_tiles.size) { | |||
1027 | error_message(__FUNCTION__, "Level %d at %d %d in %d layer uses an unknown floor tile: %d.", PLEASE_INFORM, | |||
1028 | lvl->levelnum, RoundX, RoundY, layer, BrickWanted); | |||
1029 | return ISO_FLOOR_EMPTY; | |||
1030 | } | |||
1031 | } | |||
1032 | ||||
1033 | return BrickWanted; | |||
1034 | } | |||
1035 | ||||
1036 | /** | |||
1037 | * This functions reads the specification for a level | |||
1038 | * taken from the ship file. | |||
1039 | * | |||
1040 | * @return pointer to the level | |||
1041 | * @param text buffer containing level description | |||
1042 | */ | |||
1043 | static level *decode_level(char **buffer) | |||
1044 | { | |||
1045 | level *loadlevel; | |||
1046 | char *data = *buffer; | |||
1047 | ||||
1048 | loadlevel = (level *)MyMalloc(sizeof(level)); | |||
1049 | ||||
1050 | if (decode_header(loadlevel, data)) { | |||
1051 | error_message(__FUNCTION__, "Unable to decode level header!", PLEASE_INFORM | IS_FATAL); | |||
1052 | } | |||
1053 | ||||
1054 | // The order of sections in the file has to match this. | |||
1055 | data = decode_map(loadlevel, data); | |||
1056 | if (!data) { | |||
1057 | error_message(__FUNCTION__, "Unable to decode the map for level %d", PLEASE_INFORM | IS_FATAL, loadlevel->levelnum); | |||
1058 | } | |||
1059 | data = decode_obstacles(loadlevel, data); | |||
1060 | data = decode_map_labels(loadlevel, data); | |||
1061 | data = decode_item_section(loadlevel, data); | |||
1062 | data = decode_obstacle_extensions(loadlevel, data); | |||
1063 | data = decode_waypoints(loadlevel, data); | |||
1064 | ||||
1065 | // Point the buffer to the end of this level, so the next level can be read | |||
1066 | *buffer = data; | |||
1067 | return loadlevel; | |||
1068 | } | |||
1069 | ||||
1070 | ||||
1071 | /** | |||
1072 | * Call the random dungeon generator on this level if this level is marked | |||
1073 | * as being randomly generated and if we are not in the "leveleditor" mode | |||
1074 | * in which case random dungeons must not be considered as generated (so that | |||
1075 | * they will be exported as being non-generated random levels). | |||
1076 | */ | |||
1077 | static void generate_dungeon_if_needed(level *l) | |||
1078 | { | |||
1079 | if (!l->random_dungeon || l->dungeon_generated) { | |||
1080 | return; | |||
1081 | } | |||
1082 | ||||
1083 | // Generate random dungeon now | |||
1084 | set_dungeon_output(l); | |||
1085 | generate_dungeon(l->xlen, l->ylen, l->random_dungeon, l->teleport_pair); | |||
1086 | l->dungeon_generated = 1; | |||
1087 | } | |||
1088 | ||||
1089 | /** | |||
1090 | * This function loads the data for a whole ship | |||
1091 | * Possible return values are : OK and ERR | |||
1092 | */ | |||
1093 | int LoadShip(char *filename, int compressed) | |||
1094 | { | |||
1095 | char *ShipData = NULL((void*)0); | |||
1096 | FILE *ShipFile; | |||
1097 | int i; | |||
1098 | ||||
1099 | #define END_OF_SHIP_DATA_STRING"*** End of Ship Data ***" "*** End of Ship Data ***" | |||
1100 | ||||
1101 | // Free existing level data | |||
1102 | for (i = 0; i < MAX_LEVELS100; i++) { | |||
1103 | if (level_exists(i)) { | |||
1104 | level *lvl = curShip.AllLevels[i]; | |||
1105 | int row = 0; | |||
1106 | int col = 0; | |||
1107 | ||||
1108 | // Map tiles | |||
1109 | for (row = 0; row < lvl->ylen; row++) { | |||
1110 | if (lvl->map[row]) { | |||
1111 | for (col = 0; col < lvl->xlen; col++) { | |||
1112 | dynarray_free(&lvl->map[row][col].glued_obstacles); | |||
1113 | } | |||
1114 | ||||
1115 | free(curShip.AllLevels[i]->map[row]); | |||
1116 | curShip.AllLevels[i]->map[row] = NULL((void*)0); | |||
1117 | } | |||
1118 | } | |||
1119 | ||||
1120 | // Level strings | |||
1121 | if (lvl->Levelname) { | |||
1122 | free(lvl->Levelname); | |||
1123 | lvl->Levelname = NULL((void*)0); | |||
1124 | } | |||
1125 | ||||
1126 | if (lvl->Background_Song_Name) { | |||
1127 | free(lvl->Background_Song_Name); | |||
1128 | lvl->Background_Song_Name = NULL((void*)0); | |||
1129 | } | |||
1130 | ||||
1131 | // Waypoints | |||
1132 | int w; | |||
1133 | for (w = 0; w < lvl->waypoints.size; w++) { | |||
1134 | struct waypoint *wpts = lvl->waypoints.arr; | |||
1135 | dynarray_free(&wpts[w].connections); | |||
1136 | } | |||
1137 | ||||
1138 | dynarray_free(&lvl->waypoints); | |||
1139 | ||||
1140 | // Obstacle extensions | |||
1141 | free_obstacle_extensions(lvl); | |||
1142 | ||||
1143 | // Map labels | |||
1144 | free_map_labels(lvl); | |||
1145 | ||||
1146 | // Random droids | |||
1147 | lvl->random_droids.types_size = 0; | |||
1148 | ||||
1149 | free(lvl); | |||
1150 | curShip.AllLevels[i] = NULL((void*)0); | |||
1151 | ||||
1152 | } | |||
1153 | } | |||
1154 | ||||
1155 | // Read the whole ship-data to memory | |||
1156 | // | |||
1157 | ShipFile = fopen(filename, "rb"); | |||
1158 | if (!ShipFile) { | |||
1159 | error_message(__FUNCTION__, "Unable to open ship file %s: %s.", PLEASE_INFORM | IS_FATAL, filename, strerror(errno(*__errno_location ()))); | |||
1160 | } | |||
1161 | ||||
1162 | if (compressed) { | |||
1163 | if (inflate_stream(ShipFile, (unsigned char **)&ShipData, NULL((void*)0))) { | |||
1164 | error_message(__FUNCTION__, "Unable to decompress ship file %s.", PLEASE_INFORM | IS_FATAL, filename); | |||
1165 | } | |||
1166 | } else { | |||
1167 | int length = FS_filelength(ShipFile); | |||
1168 | ShipData = malloc(length + 1); | |||
1169 | if (!fread(ShipData, length, 1, ShipFile)) | |||
1170 | error_message(__FUNCTION__, "Reading ship file %s failed with fread().", PLEASE_INFORM | IS_FATAL, filename); | |||
1171 | ShipData[length] = 0; | |||
1172 | } | |||
1173 | ||||
1174 | fclose(ShipFile); | |||
1175 | ||||
1176 | // Read each level | |||
1177 | int done = 0; | |||
1178 | char *pos = ShipData; | |||
1179 | while (!done) { | |||
1180 | level *this_level = decode_level(&pos); | |||
1181 | int this_levelnum = this_level->levelnum; | |||
1182 | ||||
1183 | if (this_levelnum >= MAX_LEVELS100) | |||
1184 | error_message(__FUNCTION__, "One levelnumber in savegame (%d) is bigger than the maximum allowed (%d).", | |||
1185 | PLEASE_INFORM | IS_FATAL, this_levelnum, MAX_LEVELS100 - 1); | |||
1186 | if (level_exists(this_levelnum)) | |||
1187 | error_message(__FUNCTION__, "Two levels with same levelnumber (%d) found in the savegame.", | |||
1188 | PLEASE_INFORM | IS_FATAL, this_levelnum); | |||
1189 | ||||
1190 | curShip.AllLevels[this_levelnum] = this_level; | |||
1191 | curShip.num_levels = this_levelnum + 1; | |||
1192 | ||||
1193 | generate_dungeon_if_needed(this_level); | |||
1194 | ||||
1195 | // Move to the level termination marker | |||
1196 | pos = strstr(pos, LEVEL_END_STRING"end_of_level"); | |||
1197 | pos += strlen(LEVEL_END_STRING"end_of_level") + 1; | |||
1198 | ||||
1199 | // Check if there is another level | |||
1200 | if (!strstr(pos, LEVEL_HEADER_LEVELNUMBER"Levelnumber:")) { | |||
1201 | done = 1; | |||
1202 | } | |||
1203 | } | |||
1204 | ||||
1205 | // Check for consistency of levels | |||
1206 | int check_level = curShip.num_levels; | |||
1207 | while (check_level--) { | |||
1208 | if (!level_exists(check_level)) { | |||
1209 | error_message(__FUNCTION__, "Level number %d should exist but is NULL.", PLEASE_INFORM | IS_FATAL, check_level); | |||
1210 | } | |||
1211 | } | |||
1212 | ||||
1213 | // Now that all the information has been copied, we can free the loaded data | |||
1214 | // again. | |||
1215 | // | |||
1216 | free(ShipData); | |||
1217 | ||||
1218 | // Compute the gps transform acceleration data | |||
1219 | gps_transform_map_dirty_flag = TRUE(1); | |||
1220 | gps_transform_map_init(); | |||
1221 | ||||
1222 | return OK0; | |||
1223 | ||||
1224 | }; // int LoadShip ( ... ) | |||
1225 | ||||
1226 | /** | |||
1227 | * This should write the obstacle information in human-readable form into | |||
1228 | * a buffer. | |||
1229 | */ | |||
1230 | static void encode_obstacles_of_this_level(struct auto_string *shipstr, level *Lev) | |||
1231 | { | |||
1232 | int i; | |||
1233 | autostr_append(shipstr, "%s\n", OBSTACLE_DATA_BEGIN_STRING"obsdata"); | |||
1234 | ||||
1235 | defrag_obstacle_array(Lev); | |||
1236 | ||||
1237 | for (i = 0; i < MAX_OBSTACLES_ON_MAP4000; i++) { | |||
1238 | if (Lev->obstacle_list[i].type == (-1)) | |||
1239 | continue; | |||
1240 | ||||
1241 | autostr_append(shipstr, "%s%d %s%3.2f %s%3.2f\n", OBSTACLE_TYPE_STRING"t", Lev->obstacle_list[i].type, | |||
1242 | OBSTACLE_X_POSITION_STRING"x", Lev->obstacle_list[i].pos.x, OBSTACLE_Y_POSITION_STRING"y", | |||
1243 | Lev->obstacle_list[i].pos.y); | |||
1244 | } | |||
1245 | ||||
1246 | autostr_append(shipstr, "%s\n", OBSTACLE_DATA_END_STRING"/obsdata"); | |||
1247 | } | |||
1248 | ||||
1249 | static void encode_map_labels(struct auto_string *shipstr, level *lvl) | |||
1250 | { | |||
1251 | int i; | |||
1252 | struct map_label *map_label; | |||
1253 | ||||
1254 | autostr_append(shipstr, "%s\n", MAP_LABEL_BEGIN_STRING"plabelinfolvl"); | |||
1255 | ||||
1256 | for (i = 0; i < lvl->map_labels.size; i++) { | |||
1257 | // Get the map label | |||
1258 | map_label = &ACCESS_MAP_LABEL(lvl->map_labels, i)((struct map_label *)(lvl->map_labels.arr))[i]; | |||
1259 | ||||
1260 | // Encode map label | |||
1261 | autostr_append(shipstr, "%s%d %s%d %s%s\"\n", X_POSITION_OF_LABEL_STRING"x=", map_label->pos.x, Y_POSITION_OF_LABEL_STRING"y=", | |||
1262 | map_label->pos.y, LABEL_ITSELF_ANNOUNCE_STRING"name=\"", map_label->label_name); | |||
1263 | } | |||
1264 | ||||
1265 | autostr_append(shipstr, "%s\n", MAP_LABEL_END_STRING"/plabelinfolvl"); | |||
1266 | } | |||
1267 | ||||
1268 | /** | |||
1269 | * | |||
1270 | * | |||
1271 | */ | |||
1272 | static void WriteOutOneItem(struct auto_string *shipstr, item *ItemToWriteOut) | |||
1273 | { | |||
1274 | ||||
1275 | autostr_append(shipstr, "%s%s\" %s%f %s%f ", ITEM_ID_STRING"it: id=\"", ItemMap[ItemToWriteOut->type].id, | |||
1276 | ITEM_POS_X_STRING" X=", ItemToWriteOut->pos.x, ITEM_POS_Y_STRING" Y=", ItemToWriteOut->pos.y); | |||
1277 | ||||
1278 | if (ItemToWriteOut->armor_class) { | |||
1279 | autostr_append(shipstr, "%s%d ", ITEM_ARMOR_CLASS_BASE_STRING" AC=", ItemToWriteOut->armor_class); | |||
1280 | } | |||
1281 | ||||
1282 | autostr_append(shipstr, "%s%d %s%f %s%d %s%d", | |||
1283 | ITEM_MAX_DURABILITY_STRING" MDur=", ItemToWriteOut->max_durability, | |||
1284 | ITEM_CUR_DURABILITY_STRING" CDur=", ItemToWriteOut->current_durability, | |||
1285 | ITEM_AMMO_CLIP_STRING" AClip=", ItemToWriteOut->ammo_clip, | |||
1286 | ITEM_MULTIPLICITY_STRING" Multi=", ItemToWriteOut->multiplicity); | |||
1287 | ||||
1288 | // Write the sockets of the item. The bonuses can be reconstructed from | |||
1289 | // these easily so we don't need to write them at all. | |||
1290 | if (ItemToWriteOut->upgrade_sockets.size) { | |||
1291 | int i; | |||
1292 | autostr_append(shipstr, "%s%d ", ITEM_SOCKETS_SIZE_STRING" Sockets=", ItemToWriteOut->upgrade_sockets.size); | |||
1293 | for (i = 0; i < ItemToWriteOut->upgrade_sockets.size; i++) { | |||
1294 | struct upgrade_socket *socket = &ItemToWriteOut->upgrade_sockets.arr[i]; | |||
1295 | autostr_append(shipstr, "%s%d=%d ", ITEM_SOCKET_TYPE_STRING"SocketType", i, socket->type); | |||
1296 | if (socket->addon) { | |||
1297 | autostr_append(shipstr, "%s%d=%s\" ", ITEM_SOCKET_ADDON_STRING"SocketAddon", i, socket->addon); | |||
1298 | } | |||
1299 | } | |||
1300 | } | |||
1301 | ||||
1302 | autostr_append(shipstr, "\n"); | |||
1303 | } | |||
1304 | ||||
1305 | static void EncodeItemSectionOfThisLevel(struct auto_string *shipstr, level *Lev) | |||
1306 | { | |||
1307 | int i; | |||
1308 | ||||
1309 | autostr_append(shipstr, "%s\n", ITEMS_SECTION_BEGIN_STRING"piteminfolvl"); | |||
1310 | ||||
1311 | // Now we write out the bulk of items infos | |||
1312 | // | |||
1313 | for (i = 0; i < MAX_ITEMS_PER_LEVEL300; i++) { | |||
1314 | if (Lev->ItemList[i].type == (-1)) | |||
1315 | continue; | |||
1316 | ||||
1317 | WriteOutOneItem(shipstr, &(Lev->ItemList[i])); | |||
1318 | ||||
1319 | } | |||
1320 | ||||
1321 | autostr_append(shipstr, "%s\n", ITEMS_SECTION_END_STRING"/piteminfolvl"); | |||
1322 | } | |||
1323 | ||||
1324 | static void encode_extension_chest(struct auto_string *shipstr, struct obstacle_extension *ext) | |||
1325 | { | |||
1326 | int i; | |||
1327 | struct dynarray *da = ext->data; | |||
1328 | ||||
1329 | for (i = 0; i < da->size; i++) { | |||
1330 | item *it = &((item *)da->arr)[i]; | |||
1331 | if (it->type == -1) | |||
1332 | continue; | |||
1333 | ||||
1334 | autostr_append(shipstr, "\t"); | |||
1335 | WriteOutOneItem(shipstr, it); | |||
1336 | } | |||
1337 | } | |||
1338 | ||||
1339 | static void encode_extension_label(struct auto_string *shipstr, struct obstacle_extension *ext) | |||
1340 | { | |||
1341 | const char *label = ext->data; | |||
1342 | ||||
1343 | autostr_append(shipstr, "\t%s\n", label); | |||
1344 | } | |||
1345 | ||||
1346 | static void encode_extension_dialog(struct auto_string *shipstr, struct obstacle_extension *ext) | |||
1347 | { | |||
1348 | // dialog and label extensions are both a string | |||
1349 | encode_extension_label(shipstr, ext); | |||
1350 | } | |||
1351 | ||||
1352 | static void encode_obstacle_extensions(struct auto_string *shipstr, level *l) | |||
1353 | { | |||
1354 | int i; | |||
1355 | autostr_append(shipstr, "%s\n", OBSTACLE_EXTENSIONS_BEGIN_STRING"obstacleextensions"); | |||
1356 | for (i = 0; i < l->obstacle_extensions.size; i++) { | |||
1357 | struct obstacle_extension *ext = &ACCESS_OBSTACLE_EXTENSION(l->obstacle_extensions, i)((struct obstacle_extension *)(l->obstacle_extensions.arr) )[i]; | |||
1358 | ||||
1359 | if (ext->type == 0) | |||
1360 | continue; | |||
1361 | ||||
1362 | autostr_append(shipstr, "idx=%d type=%d data={\n", get_obstacle_index(l, ext->obs), ext->type); | |||
1363 | ||||
1364 | switch ((enum obstacle_extension_type)(ext->type)) { | |||
1365 | case OBSTACLE_EXTENSION_CHEST_ITEMS: | |||
1366 | encode_extension_chest(shipstr, ext); | |||
1367 | break; | |||
1368 | case OBSTACLE_EXTENSION_LABEL: | |||
1369 | encode_extension_label(shipstr, ext); | |||
1370 | break; | |||
1371 | case OBSTACLE_EXTENSION_DIALOGFILE: | |||
1372 | encode_extension_dialog(shipstr, ext); | |||
1373 | break; | |||
1374 | } | |||
1375 | ||||
1376 | autostr_append(shipstr, "}\n"); | |||
1377 | } | |||
1378 | autostr_append(shipstr, "%s\n", OBSTACLE_EXTENSIONS_END_STRING"/obstacleextensions"); | |||
1379 | } | |||
1380 | ||||
1381 | static void encode_waypoints(struct auto_string *shipstr, level *lvl) | |||
1382 | { | |||
1383 | waypoint *wpts = lvl->waypoints.arr; | |||
1384 | int *connections; | |||
1385 | int i, j; | |||
1386 | ||||
1387 | autostr_append(shipstr, "%s\n", WP_BEGIN_STRING"wp"); | |||
1388 | ||||
1389 | for (i = 0; i < lvl->waypoints.size; i++) { | |||
1390 | // Encode the waypoint | |||
1391 | autostr_append(shipstr, "Nr.=%3d x=%4d y=%4d rnd=%1d\t %s", i, wpts[i].x, wpts[i].y, wpts[i].suppress_random_spawn, CONNECTION_STRING"c: "); | |||
1392 | ||||
1393 | // Get the connections of the waypoint | |||
1394 | connections = wpts[i].connections.arr; | |||
1395 | ||||
1396 | for (j = 0; j < wpts[i].connections.size; j++) { | |||
1397 | // Encode the connection of the waypoint | |||
1398 | autostr_append(shipstr, " %3d", connections[j]); | |||
1399 | } | |||
1400 | ||||
1401 | autostr_append(shipstr, "\n"); | |||
1402 | } | |||
1403 | } | |||
1404 | ||||
1405 | /** | |||
1406 | * This function translates map data into human readable map code, that | |||
1407 | * can later be written to the map file on disk. | |||
1408 | */ | |||
1409 | static void TranslateToHumanReadable(struct auto_string *str, map_tile * MapInfo, int LineLength, int layers) | |||
1410 | { | |||
1411 | int col; | |||
1412 | int layer; | |||
1413 | ||||
1414 | for (col = 0; col < LineLength; col++) { | |||
1415 | for (layer = 0; layer < layers; layer++) | |||
1416 | autostr_append(str, "%3d ", MapInfo[col].floor_values[layer]); | |||
1417 | } | |||
1418 | ||||
1419 | autostr_append(str, "\n"); | |||
1420 | } | |||
1421 | ||||
1422 | /** | |||
1423 | * This function generates savable text out of the current level data | |||
1424 | * | |||
1425 | * If reset_random_levels is TRUE, then the random levels are saved | |||
1426 | * "un-generated" (typical usage: levels.dat). | |||
1427 | * | |||
1428 | */ | |||
1429 | static void encode_level_for_saving(struct auto_string *shipstr, level *lvl, int reset_random_levels) | |||
1430 | { | |||
1431 | int i; | |||
1432 | int xlen = lvl->xlen, ylen = lvl->ylen; | |||
1433 | ||||
1434 | // Write level header | |||
1435 | autostr_append(shipstr, "%s %d\n\ | |||
1436 | xlen of this level: %d\n\ | |||
1437 | ylen of this level: %d\n\ | |||
1438 | floor layers: %d\n\ | |||
1439 | light radius bonus of this level: %d\n\ | |||
1440 | minimal light on this level: %d\n\ | |||
1441 | infinite_running_on_this_level: %d\n\ | |||
1442 | random dungeon: %d\n\ | |||
1443 | teleport pair: %d\n\ | |||
1444 | dungeon generated: %d\n\ | |||
1445 | environmental flags: %d\n\ | |||
1446 | item drop class: %d\n\ | |||
1447 | jump target north: %d\n\ | |||
1448 | jump target south: %d\n\ | |||
1449 | jump target east: %d\n\ | |||
1450 | jump target west: %d\n", LEVEL_HEADER_LEVELNUMBER"Levelnumber:", lvl->levelnum, | |||
1451 | lvl->xlen, lvl->ylen, lvl->floor_layers, | |||
1452 | lvl->light_bonus, lvl->minimum_light_value, | |||
1453 | lvl->infinite_running_on_this_level, | |||
1454 | lvl->random_dungeon, | |||
1455 | lvl->teleport_pair, | |||
1456 | (reset_random_levels && lvl->random_dungeon) ? 0 : lvl->dungeon_generated, | |||
1457 | lvl->flags, | |||
1458 | lvl->drop_class, | |||
1459 | lvl->jump_target_north, | |||
1460 | lvl->jump_target_south, | |||
1461 | lvl->jump_target_east, | |||
1462 | lvl->jump_target_west); | |||
1463 | ||||
1464 | autostr_append(shipstr, "number of random droids: %d\n", lvl->random_droids.nr); | |||
1465 | autostr_append(shipstr, "random droid types: "); | |||
1466 | ||||
1467 | for (i = 0; i < lvl->random_droids.types_size; i++) { | |||
1468 | if (i) | |||
1469 | autostr_append(shipstr, ", "); | |||
1470 | autostr_append(shipstr, "%s", Droidmap[lvl->random_droids.types[i]].droidname); | |||
1471 | } | |||
1472 | ||||
1473 | autostr_append(shipstr, "\n%s%s\"\n%s%s\n", LEVEL_NAME_STRING"Name of this level=_\"", lvl->Levelname, | |||
1474 | BACKGROUND_SONG_NAME_STRING"BgSong=", lvl->Background_Song_Name); | |||
1475 | ||||
1476 | autostr_append(shipstr, "%s\n", MAP_BEGIN_STRING"beginning_of_map"); | |||
1477 | ||||
1478 | // Now in the loop each line of map data should be saved as a whole | |||
1479 | for (i = 0; i < ylen; i++) { | |||
1480 | if (!(reset_random_levels && lvl->random_dungeon)) { | |||
1481 | TranslateToHumanReadable(shipstr, lvl->map[i], xlen, lvl->floor_layers); | |||
1482 | } else { | |||
1483 | int j = xlen; | |||
1484 | while (j--) { | |||
1485 | autostr_append(shipstr, " 0 "); | |||
1486 | } | |||
1487 | autostr_append(shipstr, "\n"); | |||
1488 | } | |||
1489 | } | |||
1490 | ||||
1491 | autostr_append(shipstr, "%s\n", MAP_END_STRING"/pmapinfolvl"); | |||
1492 | ||||
1493 | if (!(reset_random_levels && lvl->random_dungeon)) { | |||
1494 | encode_obstacles_of_this_level(shipstr, lvl); | |||
1495 | ||||
1496 | encode_map_labels(shipstr, lvl); | |||
1497 | ||||
1498 | EncodeItemSectionOfThisLevel(shipstr, lvl); | |||
1499 | ||||
1500 | encode_obstacle_extensions(shipstr, lvl); | |||
1501 | ||||
1502 | encode_waypoints(shipstr, lvl); | |||
1503 | } | |||
1504 | ||||
1505 | autostr_append(shipstr, "%s\n----------------------------------------------------------------------\n", | |||
1506 | LEVEL_END_STRING"end_of_level"); | |||
1507 | } | |||
1508 | ||||
1509 | /** | |||
1510 | * This function should save a whole ship to disk to the given filename. | |||
1511 | * It is not only used by the level editor, but also by the function that | |||
1512 | * saves whole games. | |||
1513 | * | |||
1514 | * If reset_random_levels is TRUE, then the random levels are saved | |||
1515 | * "un-generated" (typical usage: levels.dat). | |||
1516 | * @return 0 if OK, 1 on error | |||
1517 | */ | |||
1518 | int SaveShip(const char *filename, int reset_random_levels, int compress) | |||
1519 | { | |||
1520 | int i; | |||
1521 | FILE *ShipFile = NULL((void*)0); | |||
1522 | struct auto_string *shipstr; | |||
1523 | ||||
1524 | // Open the ship file | |||
1525 | if ((ShipFile = fopen(filename, "wb")) == NULL((void*)0)) { | |||
1526 | error_message(__FUNCTION__, "Error opening ship file %s for writing.", NO_REPORT, filename); | |||
1527 | return ERR-1; | |||
1528 | } | |||
1529 | ||||
1530 | shipstr = alloc_autostr(1048576); | |||
1531 | autostr_printf(shipstr, "\n"); | |||
1532 | ||||
1533 | // Save all the levels | |||
1534 | for (i = 0; i < curShip.num_levels; i++) { | |||
1535 | if (level_exists(i)) { | |||
1536 | encode_level_for_saving(shipstr, curShip.AllLevels[i], reset_random_levels); | |||
1537 | } | |||
1538 | } | |||
1539 | ||||
1540 | autostr_append(shipstr, "%s\n\n", END_OF_SHIP_DATA_STRING"*** End of Ship Data ***"); | |||
1541 | ||||
1542 | if (compress) { | |||
1543 | deflate_to_stream((unsigned char *)shipstr->value, shipstr->length, ShipFile); | |||
1544 | } else { | |||
1545 | if (fwrite((unsigned char *)shipstr->value, shipstr->length, 1, ShipFile) != 1) { | |||
1546 | error_message(__FUNCTION__, "Error writing ship file %s.", PLEASE_INFORM, filename); | |||
1547 | fclose(ShipFile); | |||
1548 | free_autostr(shipstr); | |||
1549 | return ERR-1; | |||
1550 | } | |||
1551 | } | |||
1552 | ||||
1553 | if (fclose(ShipFile) == EOF(-1)) { | |||
1554 | error_message(__FUNCTION__, "Closing of ship file failed!", PLEASE_INFORM); | |||
1555 | free_autostr(shipstr); | |||
1556 | return ERR-1; | |||
1557 | } | |||
1558 | ||||
1559 | free_autostr(shipstr); | |||
1560 | return OK0; | |||
1561 | } | |||
1562 | ||||
1563 | int save_special_forces(const char *filename) | |||
1564 | { | |||
1565 | FILE *s_forces_file = NULL((void*)0); | |||
1566 | struct auto_string *s_forces_str; | |||
1567 | level *lvl; | |||
1568 | int i; | |||
1569 | ||||
1570 | if ((s_forces_file = fopen(filename, "wb")) == NULL((void*)0)) { | |||
1571 | error_message(__FUNCTION__, "Error opening Special Forces file %s for writing.", NO_REPORT, filename); | |||
1572 | return ERR-1; | |||
1573 | } | |||
1574 | ||||
1575 | s_forces_str = alloc_autostr(64); | |||
1576 | ||||
1577 | for (i = 0; i < curShip.num_levels; i++) { | |||
1578 | if (!level_exists(i)) | |||
1579 | continue; | |||
1580 | ||||
1581 | lvl = curShip.AllLevels[i]; | |||
1582 | autostr_append(s_forces_str, "** Beginning of new Level **\n"); | |||
1583 | autostr_append(s_forces_str, "Level=%d\n\n", lvl->levelnum); | |||
1584 | ||||
1585 | enemy *en; | |||
1586 | ||||
1587 | list_for_each_entry_reverse(en, &level_bots_head[lvl->levelnum], level_list)for (en = ({ const typeof( ((typeof(*en) *)0)->level_list ) *__mptr = ((&level_bots_head[lvl->levelnum])->prev ); (typeof(*en) *)( (char *)__mptr - __builtin_offsetof(typeof (*en), level_list) );}); &en->level_list != (&level_bots_head [lvl->levelnum]); en = ({ const typeof( ((typeof(*en) *)0) ->level_list ) *__mptr = (en->level_list.prev); (typeof (*en) *)( (char *)__mptr - __builtin_offsetof(typeof(*en), level_list ) );})) { | |||
1588 | if (!en->SpecialForce) | |||
1589 | continue; | |||
1590 | ||||
1591 | autostr_append(s_forces_str, "T=%s: ", Droidmap[en->type].droidname); | |||
1592 | autostr_append(s_forces_str, "PosX=%d PosY=%d ", (int)en->pos.x, (int)en->pos.y); | |||
1593 | autostr_append(s_forces_str, "Faction=\"%s\" ", get_faction_from_id(en->faction)); | |||
1594 | autostr_append(s_forces_str, "UseDialog=\"%s\" ", en->dialog_section_name); | |||
1595 | ||||
1596 | autostr_append(s_forces_str, "ShortLabel=_\"%s\" ", en->short_description_text); | |||
1597 | autostr_append(s_forces_str, "Marker=%d ", en->marker); | |||
1598 | autostr_append(s_forces_str, "RushTux=%d ", en->will_rush_tux); | |||
1599 | ||||
1600 | autostr_append(s_forces_str, "Fixed=%hi ", en->CompletelyFixed); | |||
1601 | autostr_append(s_forces_str, "DropItemId=\"%s\" ", | |||
1602 | (en->on_death_drop_item_code == -1) ? "none" : ItemMap[en->on_death_drop_item_code].id); | |||
1603 | autostr_append(s_forces_str, "MaxDistanceToHome=%hd\n", en->max_distance_to_home); | |||
1604 | } | |||
1605 | ||||
1606 | autostr_append(s_forces_str, "** End of this levels Special Forces data **\n"); | |||
1607 | autostr_append(s_forces_str, "---------------------------------------------------------\n"); | |||
1608 | } | |||
1609 | ||||
1610 | autostr_append(s_forces_str, "*** End of Droid Data ***"); | |||
1611 | ||||
1612 | if (fwrite((unsigned char *)s_forces_str->value, s_forces_str->length, 1, s_forces_file) != 1) { | |||
1613 | error_message(__FUNCTION__, "Error writing SpecialForces file %s.", PLEASE_INFORM, filename); | |||
1614 | fclose(s_forces_file); | |||
1615 | goto out; | |||
1616 | } | |||
1617 | ||||
1618 | if (fclose(s_forces_file) == EOF(-1)) { | |||
1619 | error_message(__FUNCTION__, "Closing of Special Forces file failed!", PLEASE_INFORM); | |||
1620 | goto out; | |||
1621 | } | |||
1622 | ||||
1623 | out: free_autostr(s_forces_str); | |||
1624 | return OK0; | |||
1625 | } | |||
1626 | ||||
1627 | /** | |||
1628 | * This function is used to calculate the number of the droids on the | |||
1629 | * ship, which is a global variable. | |||
1630 | */ | |||
1631 | void CountNumberOfDroidsOnShip(void) | |||
1632 | { | |||
1633 | Number_Of_Droids_On_Ship = 0; | |||
1634 | ||||
1635 | enemy *erot; | |||
1636 | BROWSE_ALIVE_BOTS(erot)for (erot = ({ const typeof( ((typeof(*erot) *)0)->global_list ) *__mptr = ((&alive_bots_head)->next); (typeof(*erot ) *)( (char *)__mptr - __builtin_offsetof(typeof(*erot), global_list ) );}); &erot->global_list != (&alive_bots_head); erot = ({ const typeof( ((typeof(*erot) *)0)->global_list ) *__mptr = (erot->global_list.next); (typeof(*erot) *)( (char *)__mptr - __builtin_offsetof(typeof(*erot), global_list) );})) { | |||
1637 | Number_Of_Droids_On_Ship++; | |||
1638 | } | |||
1639 | ||||
1640 | BROWSE_DEAD_BOTS(erot)for (erot = ({ const typeof( ((typeof(*erot) *)0)->global_list ) *__mptr = ((&dead_bots_head)->next); (typeof(*erot) *)( (char *)__mptr - __builtin_offsetof(typeof(*erot), global_list ) );}); &erot->global_list != (&dead_bots_head); erot = ({ const typeof( ((typeof(*erot) *)0)->global_list ) *__mptr = (erot->global_list.next); (typeof(*erot) *)( (char *)__mptr - __builtin_offsetof(typeof(*erot), global_list) );})) { | |||
1641 | Number_Of_Droids_On_Ship++; | |||
1642 | } | |||
1643 | ||||
1644 | }; // void CountNumberOfDroidsOnShip ( void ) | |||
1645 | ||||
1646 | /* ----------------------------------------------------------------- | |||
1647 | * This function initializes all enemies, which means that enemies are | |||
1648 | * filled in into the enemy list according to the enemies types that | |||
1649 | * are to be found on each deck. | |||
1650 | * ----------------------------------------------------------------- */ | |||
1651 | int GetCrew(char *filename) | |||
1652 | { | |||
1653 | char fpath[PATH_MAX4096]; | |||
1654 | char *MainDroidsFilePointer; | |||
1655 | char *DroidSectionPointer; | |||
1656 | char *EndOfThisDroidSectionPointer; | |||
1657 | ||||
1658 | #define START_OF_DROID_DATA_STRING"*** Beginning of Droid Data ***" "*** Beginning of Droid Data ***" | |||
1659 | #define END_OF_DROID_DATA_STRING"*** End of Droid Data ***" "*** End of Droid Data ***" | |||
1660 | #define DROIDS_LEVEL_DESCRIPTION_START_STRING"** Beginning of new Level **" "** Beginning of new Level **" | |||
1661 | #define DROIDS_LEVEL_DESCRIPTION_END_STRING"** End of this levels Special Forces data **" "** End of this levels Special Forces data **" | |||
1662 | ||||
1663 | //Now its time to start decoding the droids file. | |||
1664 | //For that, we must get it into memory first. | |||
1665 | //The procedure is the same as with LoadShip | |||
1666 | // | |||
1667 | find_file(filename, MAP_DIR, fpath, PLEASE_INFORM | IS_FATAL); | |||
1668 | ||||
1669 | MainDroidsFilePointer = ReadAndMallocAndTerminateFile(fpath, END_OF_DROID_DATA_STRING"*** End of Droid Data ***"); | |||
1670 | ||||
1671 | // The Droid crew file for this map is now completely read into memory | |||
1672 | // It's now time to decode the file and to fill the array of enemys with | |||
1673 | // new droids of the given types. | |||
1674 | // | |||
1675 | enemy_reset_fabric(); | |||
1676 | ||||
1677 | DroidSectionPointer = MainDroidsFilePointer; | |||
1678 | while ((DroidSectionPointer = strstr(DroidSectionPointer, DROIDS_LEVEL_DESCRIPTION_START_STRING"** Beginning of new Level **")) != NULL((void*)0)) { | |||
| ||||
| ||||
1679 | DroidSectionPointer += strlen(DROIDS_LEVEL_DESCRIPTION_START_STRING"** Beginning of new Level **"); | |||
1680 | DebugPrintf(1, "\nFound another levels droids description starting point entry!"); | |||
1681 | EndOfThisDroidSectionPointer = strstr(DroidSectionPointer, DROIDS_LEVEL_DESCRIPTION_END_STRING"** End of this levels Special Forces data **"); | |||
1682 | if (EndOfThisDroidSectionPointer == NULL((void*)0)) { | |||
1683 | error_message(__FUNCTION__, "Unterminated droid section encountered!", PLEASE_INFORM | IS_FATAL); | |||
1684 | } | |||
1685 | // EndOfThisDroidSectionPointer[0]=0; | |||
1686 | GetThisLevelsDroids(DroidSectionPointer); | |||
1687 | DroidSectionPointer = EndOfThisDroidSectionPointer + 2; // Move past the inserted String terminator | |||
1688 | } | |||
1689 | ||||
1690 | free(MainDroidsFilePointer); | |||
1691 | return (OK0); | |||
1692 | ||||
1693 | }; // int GetCrew ( ... ) | |||
1694 | ||||
1695 | /** | |||
1696 | * | |||
1697 | * | |||
1698 | */ | |||
1699 | static void GetThisLevelsSpecialForces(char *search_pointer, int our_level_number, char *lvl_end_location) | |||
1700 | { | |||
1701 | int droid_type; | |||
1702 | #define SPECIAL_FORCE_INDICATION_STRING"T=" "T=" | |||
1703 | ||||
1704 | while ((search_pointer = strstr(search_pointer, SPECIAL_FORCE_INDICATION_STRING"T=")) != NULL((void*)0)) { | |||
1705 | char *special_droid = ReadAndMallocStringFromData(search_pointer, SPECIAL_FORCE_INDICATION_STRING"T=", "\n"); | |||
1706 | char *special_droid_end = special_droid + strlen(special_droid); | |||
1707 | search_pointer += strlen(SPECIAL_FORCE_INDICATION_STRING"T="); | |||
1708 | //identify what model of droid to display: | |||
1709 | char *ptr = special_droid; | |||
1710 | int droid_type_length = 0; | |||
1711 | while (isalnum(*ptr)((*__ctype_b_loc ())[(int) ((*ptr))] & (unsigned short int ) _ISalnum)) { | |||
1712 | ptr++; | |||
1713 | droid_type_length++; | |||
1714 | } | |||
1715 | char type_indication_string[droid_type_length + 1]; | |||
1716 | strncpy(type_indication_string, special_droid, droid_type_length)__builtin_strncpy (type_indication_string, special_droid, droid_type_length ); | |||
1717 | type_indication_string[droid_type_length] = 0; | |||
1718 | ||||
1719 | droid_type = get_droid_type(type_indication_string); | |||
1720 | ||||
1721 | // Create a new enemy, and initialize its 'identity' and 'global state' | |||
1722 | // (the enemy will be fully initialized by respawn_level()) | |||
1723 | ||||
1724 | ||||
1725 | enemy *newen = enemy_new(droid_type); | |||
1726 | ||||
1727 | ||||
1728 | newen->SpecialForce = TRUE(1); | |||
1729 | ||||
1730 | ||||
1731 | char *tmp_sensor_ID = ReadAndMallocStringFromDataOptional(special_droid, "UseSensor=\"","\""); | |||
1732 | ||||
1733 | if (!tmp_sensor_ID) | |||
1734 | newen->sensor_id=Droidmap[newen->type].sensor_id; //Not declared? In this case, use default. | |||
1735 | else | |||
1736 | newen->sensor_id=get_sensor_id_by_name(tmp_sensor_ID); // Otherwise, use the specified sensor! | |||
1737 | ||||
1738 | free(tmp_sensor_ID); | |||
1739 | ||||
1740 | ReadValueFromStringWithDefault(special_droid, "Fixed=", "%hd", "0", &(newen->CompletelyFixed), special_droid_end); | |||
1741 | ReadValueFromStringWithDefault(special_droid, "Marker=", "%d", "0000", &(newen->marker), special_droid_end); | |||
1742 | ReadValueFromStringWithDefault(special_droid, "MaxDistanceToHome=", "%hd", "0", &(newen->max_distance_to_home), | |||
1743 | special_droid_end); | |||
1744 | ||||
1745 | char *faction = ReadAndMallocStringFromData(special_droid, "Faction=\"", "\""); | |||
1746 | newen->faction = get_faction_id(faction); | |||
1747 | free(faction); | |||
1748 | ||||
1749 | char *x, *y; | |||
1750 | x = ReadAndMallocStringFromData(special_droid, "PosX=", " "); | |||
1751 | y = ReadAndMallocStringFromData(special_droid, "PosY=", " "); | |||
1752 | ||||
1753 | newen->pos.x = strtof(x, NULL((void*)0)); | |||
1754 | newen->pos.y = strtof(y, NULL((void*)0)); | |||
1755 | newen->pos.z = our_level_number; | |||
1756 | free(x); | |||
1757 | free(y); | |||
1758 | ||||
1759 | ReadValueFromStringWithDefault(special_droid, "RushTux=", "%hu", "0", &(newen->will_rush_tux), special_droid_end); | |||
1760 | ||||
1761 | newen->dialog_section_name = ReadAndMallocStringFromData(special_droid, "UseDialog=\"", "\""); | |||
1762 | npc_get(newen->dialog_section_name); // Check that we have a valid dialog. | |||
1763 | ||||
1764 | if (newen->short_description_text) | |||
1765 | free(newen->short_description_text); | |||
1766 | ||||
1767 | newen->short_description_text = ReadAndMallocStringFromData(special_droid, "ShortLabel=_\"", "\"");; | |||
1768 | ||||
1769 | char *death_drop; | |||
1770 | death_drop = ReadAndMallocStringFromData(special_droid, "DropItemId=\"", "\""); | |||
1771 | if (strcmp(death_drop, "none")__extension__ ({ size_t __s1_len, __s2_len; (__builtin_constant_p (death_drop) && __builtin_constant_p ("none") && (__s1_len = strlen (death_drop), __s2_len = strlen ("none"), (!((size_t)(const void *)((death_drop) + 1) - (size_t)(const void *)(death_drop) == 1) || __s1_len >= 4) && (! ((size_t)(const void *)(("none") + 1) - (size_t)(const void * )("none") == 1) || __s2_len >= 4)) ? __builtin_strcmp (death_drop , "none") : (__builtin_constant_p (death_drop) && ((size_t )(const void *)((death_drop) + 1) - (size_t)(const void *)(death_drop ) == 1) && (__s1_len = strlen (death_drop), __s1_len < 4) ? (__builtin_constant_p ("none") && ((size_t)(const void *)(("none") + 1) - (size_t)(const void *)("none") == 1) ? __builtin_strcmp (death_drop, "none") : (__extension__ ({ const unsigned char *__s2 = (const unsigned char *) (const char *) ("none"); int __result = (((const unsigned char *) (const char *) (death_drop))[0] - __s2[0]); if (__s1_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) (death_drop))[1] - __s2[1]); if (__s1_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) (death_drop))[2] - __s2[2]); if (__s1_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) (death_drop))[3] - __s2[3]); } } __result; }))) : (__builtin_constant_p ("none") && ((size_t)(const void *)(("none") + 1) - ( size_t)(const void *)("none") == 1) && (__s2_len = strlen ("none"), __s2_len < 4) ? (__builtin_constant_p (death_drop ) && ((size_t)(const void *)((death_drop) + 1) - (size_t )(const void *)(death_drop) == 1) ? __builtin_strcmp (death_drop , "none") : (- (__extension__ ({ const unsigned char *__s2 = ( const unsigned char *) (const char *) (death_drop); int __result = (((const unsigned char *) (const char *) ("none"))[0] - __s2 [0]); if (__s2_len > 0 && __result == 0) { __result = (((const unsigned char *) (const char *) ("none"))[1] - __s2 [1]); if (__s2_len > 1 && __result == 0) { __result = (((const unsigned char *) (const char *) ("none"))[2] - __s2 [2]); if (__s2_len > 2 && __result == 0) __result = (((const unsigned char *) (const char *) ("none"))[3] - __s2 [3]); } } __result; })))) : __builtin_strcmp (death_drop, "none" )))); })) { | |||
1772 | newen->on_death_drop_item_code = get_item_type_by_id(death_drop); | |||
1773 | } else { | |||
1774 | newen->on_death_drop_item_code = -1; | |||
1775 | } | |||
1776 | free(death_drop); | |||
1777 | free(special_droid); | |||
1778 | enemy_insert_into_lists(newen, TRUE(1)); | |||
1779 | } | |||
1780 | ||||
1781 | }; | |||
1782 | ||||
1783 | /** | |||
1784 | * This function receives a pointer to the already read in crew section | |||
1785 | * in a already read in droids file and decodes all the contents of that | |||
1786 | * droid section to fill the AllEnemys array with droid types according | |||
1787 | * to the specifications made in the file. | |||
1788 | */ | |||
1789 | void GetThisLevelsDroids(char *section_pointer) | |||
1790 | { | |||
1791 | int our_level_number; | |||
1792 | char *search_ptr; | |||
1793 | char *lvl_end_location; | |||
1794 | int random_droids; | |||
1795 | int *allowed_type_list; | |||
1796 | level *lvl; | |||
1797 | ||||
1798 | #define DROIDS_LEVEL_INDICATION_STRING"Level=" "Level=" | |||
1799 | #define DROIDS_LEVEL_END_INDICATION_STRING"** End of this levels Special Forces data **" "** End of this levels Special Forces data **" | |||
1800 | ||||
1801 | lvl_end_location = LocateStringInData(section_pointer, DROIDS_LEVEL_END_INDICATION_STRING"** End of this levels Special Forces data **"); | |||
1802 | lvl_end_location[0] = 0; | |||
1803 | ||||
1804 | // Now we read in the level number for this level | |||
1805 | ReadValueFromString(section_pointer, DROIDS_LEVEL_INDICATION_STRING"Level=", "%d", &our_level_number, lvl_end_location); | |||
1806 | ||||
1807 | lvl = curShip.AllLevels[our_level_number]; | |||
1808 | ||||
1809 | // At this point, the List "allowed_type_list" has been filled with the NUMBERS of | |||
1810 | // the allowed types. The number of different allowed types found is also available. | |||
1811 | // That means that now we can add the appropriate droid types into the list of existing | |||
1812 | // droids in that mission. | |||
1813 | ||||
1814 | random_droids = lvl->random_droids.nr; | |||
1815 | allowed_type_list = lvl->random_droids.types; | |||
1816 | ||||
1817 | while (random_droids--) { | |||
1818 | // Create a new enemy, and initialize its 'identity' and 'global state' | |||
1819 | // (the enemy will be fully initialized by respawn_level()) | |||
1820 | enemy *newen = enemy_new(allowed_type_list[MyRandom(lvl->random_droids.types_size - 1)]); | |||
1821 | newen->pos.x = newen->pos.y = -1; | |||
1822 | newen->pos.z = our_level_number; | |||
1823 | newen->on_death_drop_item_code = -1; | |||
1824 | newen->dialog_section_name = strdup("AfterTakeover")(__extension__ (__builtin_constant_p ("AfterTakeover") && ((size_t)(const void *)(("AfterTakeover") + 1) - (size_t)(const void *)("AfterTakeover") == 1) ? (((const char *) ("AfterTakeover" ))[0] == '\0' ? (char *) calloc ((size_t) 1, (size_t) 1) : ({ size_t __len = strlen ("AfterTakeover") + 1; char *__retval = (char *) malloc (__len); if (__retval != ((void*)0)) __retval = (char *) memcpy (__retval, "AfterTakeover", __len); __retval ; })) : __strdup ("AfterTakeover"))); | |||
1825 | newen->faction = FACTION_BOTS; | |||
1826 | ||||
1827 | enemy_insert_into_lists(newen, TRUE(1)); | |||
1828 | } // while (enemy-limit of this level not reached) | |||
1829 | ||||
1830 | search_ptr = section_pointer; | |||
1831 | GetThisLevelsSpecialForces(search_ptr, our_level_number, lvl_end_location); | |||
1832 | ||||
1833 | // End bot's initialization, and put them onto a waypoint. | |||
1834 | respawn_level(our_level_number); | |||
1835 | }; | |||
1836 | ||||
1837 | /** | |||
1838 | * This function determines whether a given object on x/y is visible to | |||
1839 | * the 001 or not (due to some walls or something in between | |||
1840 | * | |||
1841 | * Return values are TRUE or FALSE accordingly | |||
1842 | * | |||
1843 | */ | |||
1844 | int IsVisible(gps *objpos) | |||
1845 | { | |||
1846 | ||||
1847 | // For the purpose of visibility checking, we might as well exclude objects | |||
1848 | // that are too far away to ever be visible and thereby save some checks of | |||
1849 | // longer lines on the map, that wouldn't be necessary or helpful anyway. | |||
1850 | // | |||
1851 | if ((fabsf(Me.pos.x - objpos->x) > FLOOR_TILES_VISIBLE_AROUND_TUX((GameConfig . screen_width >= 1024 ? 13 : GameConfig . screen_width >= 800 ? 9 : 7))) || | |||
1852 | (fabsf(Me.pos.y - objpos->y) > FLOOR_TILES_VISIBLE_AROUND_TUX((GameConfig . screen_width >= 1024 ? 13 : GameConfig . screen_width >= 800 ? 9 : 7)))) | |||
1853 | return (FALSE(0)); | |||
1854 | ||||
1855 | // So if the object in question is close enough to be visible, we'll do the | |||
1856 | // actual check and see if the line of sight is free or blocked, a rather | |||
1857 | // time-consuming and often re-iterated process. (Maybe some do-it-every- | |||
1858 | // -10th-frame-only code could be added here later... and in the meantime | |||
1859 | // old values could be used from a stored flag?! | |||
1860 | // | |||
1861 | return (DirectLineColldet(objpos->x, objpos->y, Me.pos.x, Me.pos.y, objpos->z, &VisiblePassFilter)); | |||
1862 | ||||
1863 | }; // int IsVisible( Point objpos ) | |||
1864 | ||||
1865 | /** | |||
1866 | * | |||
1867 | * | |||
1868 | */ | |||
1869 | inline float translate_pixel_to_map_location(float axis_x, float axis_y, int give_x) | |||
1870 | { | |||
1871 | ||||
1872 | // NOTE: This function does not expect absolute screen coordinates but rather coordinates relative | |||
1873 | // to the center of the screen. | |||
1874 | // | |||
1875 | // That's also why it's 'axis' rather than 'pos' or 'point'. | |||
1876 | // | |||
1877 | // That is because mouse clicks can best be analyzed this way. | |||
1878 | // | |||
1879 | ||||
1880 | if (give_x) { | |||
1881 | return (Me.pos.x + (axis_x / ((float)iso_floor_tile_width)) + (axis_y / ((float)iso_floor_tile_height))); | |||
1882 | } else { | |||
1883 | return (Me.pos.y - (axis_x / ((float)iso_floor_tile_width)) + (axis_y / ((float)iso_floor_tile_height))); | |||
1884 | } | |||
1885 | ||||
1886 | }; // int translate_pixel_to_map_location ( int axis_x , int axis_y , int give_x ) | |||
1887 | ||||
1888 | /** | |||
1889 | * | |||
1890 | * | |||
1891 | */ | |||
1892 | float translate_pixel_to_zoomed_map_location(float axis_x, float axis_y, int give_x) | |||
1893 | { | |||
1894 | float zf = lvledit_zoomfact(); | |||
1895 | if (give_x) { | |||
1896 | return (Me.pos.x + (zf * axis_x / ((float)iso_floor_tile_width)) + (zf * axis_y / ((float)iso_floor_tile_height))); | |||
1897 | // return ( ( axis_x / ISO_WIDTH ) + ( axis_y / ISO_HEIGHT ) ) ; | |||
1898 | } else { | |||
1899 | return (Me.pos.y - (zf * axis_x / ((float)iso_floor_tile_width)) + (zf * axis_y / ((float)iso_floor_tile_height))); | |||
1900 | // return ( - ( axis_x / ISO_WIDTH ) + ( axis_y / ISO_HEIGHT ) ) ; | |||
1901 | } | |||
1902 | ||||
1903 | }; // int translate_pixel_to_zoomed_map_location ( int axis_x , int axis_y , int give_x ) | |||
1904 | ||||
1905 | /** | |||
1906 | * | |||
1907 | * | |||
1908 | */ | |||
1909 | moderately_finepoint translate_point_to_map_location(float axis_x, float axis_y, int zoom_is_on) | |||
1910 | { | |||
1911 | moderately_finepoint position; | |||
1912 | if (zoom_is_on) { | |||
1913 | position.x = translate_pixel_to_zoomed_map_location(axis_x, axis_y, TRUE(1)); | |||
1914 | position.y = translate_pixel_to_zoomed_map_location(axis_x, axis_y, FALSE(0)); | |||
1915 | } else { | |||
1916 | position.x = translate_pixel_to_map_location(axis_x, axis_y, TRUE(1)); | |||
1917 | position.y = translate_pixel_to_map_location(axis_x, axis_y, FALSE(0)); | |||
1918 | } | |||
1919 | return position; | |||
1920 | } | |||
1921 | ||||
1922 | /** | |||
1923 | * This function translates a given map point to screen coordinates. | |||
1924 | * | |||
1925 | * @param x_map_pos X position on map | |||
1926 | * @param y_map_pos Y position on map | |||
1927 | * @param x_res pointer to the int that will hold the x position on screen | |||
1928 | * @param y_res pointer to the y position on screen | |||
1929 | * @param zoom_factor zoom factor in use | |||
1930 | * | |||
1931 | */ | |||
1932 | void translate_map_point_to_screen_pixel_func(float x_map_pos, float y_map_pos, int *x_res, int *y_res) | |||
1933 | { | |||
1934 | float zoom_factor = 1.0; | |||
1935 | ||||
1936 | /* XXX should not check for leveleditor here! */ | |||
1937 | if (game_status == INSIDE_LVLEDITOR && GameConfig.zoom_is_on) { | |||
1938 | zoom_factor = lvledit_zoomfact_inv(); | |||
1939 | } | |||
1940 | #define R ceilf | |||
1941 | #define factX iso_floor_tile_width*0.5*zoom_factor | |||
1942 | #define factY iso_floor_tile_height*0.5*zoom_factor | |||
1943 | if (x_res != NULL((void*)0)) { | |||
1944 | //obstacles oscillent *x_res = UserCenter_x + R( (x_map_pos - Me.pos.x) * factX) + R((Me . pos . y - y_map_pos) * factX); | |||
1945 | //murs tilent pas -- en fait si | |||
1946 | *x_res = UserCenter_x(User_Rect.x+User_Rect.w/2) + R(x_map_pos * factX) - R(y_map_pos * factX) + R(Me.pos.y * factX) - R(factX * Me.pos.x); | |||
1947 | //murs tilent pas ET tux oscille *x_res = UserCenter_x + R( x_map_pos * factX) - R(y_map_pos * factX) + R((Me.pos.y - Me.pos.x) * factX); | |||
1948 | //original "devtrack" - murs tilent pas *x_res = ( UserCenter_x + R ( ( x_map_pos - y_map_pos ) * factX ) + R ( ( Me . pos . y - Me . pos . x ) * factX ) ); | |||
1949 | ||||
1950 | } | |||
1951 | if (y_res != NULL((void*)0)) { | |||
1952 | //*y_res = UserCenter_y + R( (x_map_pos - Me.pos.x)* factY ) + R((y_map_pos - Me . pos . y)* factY); | |||
1953 | *y_res = UserCenter_y(User_Rect.y+User_Rect.h/2) + R(x_map_pos * factY) + R(y_map_pos * factY) - R(Me.pos.x * factY) - R(factY * Me.pos.y); | |||
1954 | //*y_res = UserCenter_y + R( x_map_pos * factY ) + R(y_map_pos * factY) - R((Me.pos.x + Me.pos.y) * factY); | |||
1955 | //*y_res=( UserCenter_y + R ( ( x_map_pos + y_map_pos ) * factY ) - R( ( Me . pos . x + Me . pos . y ) * factY )); | |||
1956 | } | |||
1957 | #undef R | |||
1958 | #undef factX | |||
1959 | #undef factY | |||
1960 | } | |||
1961 | ||||
1962 | #undef _map_c |