1 | // Copyright (c) Athena Dev Teams - Licensed under GNU GPL |
---|
2 | // For more information, see LICENCE in the main folder |
---|
3 | |
---|
4 | #include "../common/cbasetypes.h" |
---|
5 | #include "../common/core.h" |
---|
6 | #include "../common/timer.h" |
---|
7 | #include "../common/grfio.h" |
---|
8 | #include "../common/malloc.h" |
---|
9 | #include "../common/socket.h" // WFIFO*() |
---|
10 | #include "../common/showmsg.h" |
---|
11 | #include "../common/version.h" |
---|
12 | #include "../common/nullpo.h" |
---|
13 | #include "../common/strlib.h" |
---|
14 | #include "../common/utils.h" |
---|
15 | |
---|
16 | #include "map.h" |
---|
17 | #include "path.h" |
---|
18 | #include "chrif.h" |
---|
19 | #include "clif.h" |
---|
20 | #include "intif.h" |
---|
21 | #include "npc.h" |
---|
22 | #include "pc.h" |
---|
23 | #include "status.h" |
---|
24 | #include "mob.h" |
---|
25 | #include "npc.h" // npc_setcells(), npc_unsetcells() |
---|
26 | #include "chat.h" |
---|
27 | #include "itemdb.h" |
---|
28 | #include "storage.h" |
---|
29 | #include "skill.h" |
---|
30 | #include "trade.h" |
---|
31 | #include "party.h" |
---|
32 | #include "unit.h" |
---|
33 | #include "battle.h" |
---|
34 | #include "script.h" |
---|
35 | #include "guild.h" |
---|
36 | #include "pet.h" |
---|
37 | #include "mercenary.h" |
---|
38 | #include "atcommand.h" |
---|
39 | #include "charcommand.h" |
---|
40 | #include "log.h" |
---|
41 | #include "irc.h" |
---|
42 | #ifndef TXT_ONLY |
---|
43 | #include "mail.h" |
---|
44 | #endif |
---|
45 | #include <stdio.h> |
---|
46 | #include <stdlib.h> |
---|
47 | #include <string.h> |
---|
48 | #include <stdarg.h> |
---|
49 | #include <math.h> |
---|
50 | #ifndef _WIN32 |
---|
51 | #include <unistd.h> |
---|
52 | #endif |
---|
53 | |
---|
54 | #ifndef TXT_ONLY |
---|
55 | char default_codepage[32] = ""; |
---|
56 | |
---|
57 | int map_server_port = 3306; |
---|
58 | char map_server_ip[32] = "127.0.0.1"; |
---|
59 | char map_server_id[32] = "ragnarok"; |
---|
60 | char map_server_pw[32] = "ragnarok"; |
---|
61 | char map_server_db[32] = "ragnarok"; |
---|
62 | Sql* mmysql_handle; |
---|
63 | |
---|
64 | int db_use_sqldbs = 0; |
---|
65 | char item_db_db[32] = "item_db"; |
---|
66 | char item_db2_db[32] = "item_db2"; |
---|
67 | char mob_db_db[32] = "mob_db"; |
---|
68 | char mob_db2_db[32] = "mob_db2"; |
---|
69 | |
---|
70 | char char_db[32] = "char"; |
---|
71 | |
---|
72 | // log database |
---|
73 | char log_db_ip[32] = "127.0.0.1"; |
---|
74 | int log_db_port = 3306; |
---|
75 | char log_db_id[32] = "ragnarok"; |
---|
76 | char log_db_pw[32] = "ragnarok"; |
---|
77 | char log_db[32] = "log"; |
---|
78 | Sql* logmysql_handle; |
---|
79 | |
---|
80 | #endif /* not TXT_ONLY */ |
---|
81 | |
---|
82 | int lowest_gm_level = 1; |
---|
83 | |
---|
84 | // This param using for sending mainchat |
---|
85 | // messages like whispers to this nick. [LuzZza] |
---|
86 | char main_chat_nick[16] = "Main"; |
---|
87 | |
---|
88 | char *INTER_CONF_NAME; |
---|
89 | char *LOG_CONF_NAME; |
---|
90 | char *MAP_CONF_NAME; |
---|
91 | char *BATTLE_CONF_FILENAME; |
---|
92 | char *ATCOMMAND_CONF_FILENAME; |
---|
93 | char *CHARCOMMAND_CONF_FILENAME; |
---|
94 | char *SCRIPT_CONF_NAME; |
---|
95 | char *MSG_CONF_NAME; |
---|
96 | char *GRF_PATH_FILENAME; |
---|
97 | |
---|
98 | // ÉÍ staticÅ?JÉ?ßé |
---|
99 | static DBMap* id_db=NULL; // int id -> struct block_list* |
---|
100 | static DBMap* pc_db=NULL; // int id -> struct map_session_data* |
---|
101 | static DBMap* mobid_db=NULL; // int id -> struct mob_data* |
---|
102 | static DBMap* map_db=NULL; // unsigned int mapindex -> struct map_data* |
---|
103 | static DBMap* nick_db=NULL; // int char_id -> struct charid2nick* (requested names of offline characters) |
---|
104 | static DBMap* charid_db=NULL; // int char_id -> struct map_session_data* |
---|
105 | |
---|
106 | static int map_users=0; |
---|
107 | static struct block_list *objects[MAX_FLOORITEM]; |
---|
108 | static int first_free_object_id=0,last_object_id=0; |
---|
109 | |
---|
110 | #define block_free_max 1048576 |
---|
111 | struct block_list *block_free[block_free_max]; |
---|
112 | static int block_free_count = 0, block_free_lock = 0; |
---|
113 | |
---|
114 | #define BL_LIST_MAX 1048576 |
---|
115 | static struct block_list *bl_list[BL_LIST_MAX]; |
---|
116 | static int bl_list_count = 0; |
---|
117 | |
---|
118 | struct map_data map[MAX_MAP_PER_SERVER]; |
---|
119 | int map_num = 0; |
---|
120 | |
---|
121 | int map_port=0; |
---|
122 | |
---|
123 | int autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; |
---|
124 | int minsave_interval = 100; |
---|
125 | int save_settings = 0xFFFF; |
---|
126 | int agit_flag = 0; |
---|
127 | int night_flag = 0; // 0=day, 1=night [Yor] |
---|
128 | |
---|
129 | struct charid_request { |
---|
130 | struct charid_request* next; |
---|
131 | int charid;// who want to be notified of the nick |
---|
132 | }; |
---|
133 | struct charid2nick { |
---|
134 | char nick[NAME_LENGTH]; |
---|
135 | struct charid_request* requests;// requests of notification on this nick |
---|
136 | }; |
---|
137 | |
---|
138 | // This is the main header found at the very beginning of the map cache |
---|
139 | struct map_cache_main_header { |
---|
140 | unsigned long file_size; |
---|
141 | unsigned short map_count; |
---|
142 | }; |
---|
143 | |
---|
144 | // This is the header appended before every compressed map cells info in the map cache |
---|
145 | struct map_cache_map_info { |
---|
146 | char name[MAP_NAME_LENGTH]; |
---|
147 | short xs; |
---|
148 | short ys; |
---|
149 | long len; |
---|
150 | }; |
---|
151 | |
---|
152 | char map_cache_file[256]="db/map_cache.dat"; |
---|
153 | char db_path[256] = "db"; |
---|
154 | char motd_txt[256] = "conf/motd.txt"; |
---|
155 | char help_txt[256] = "conf/help.txt"; |
---|
156 | char help2_txt[256] = "conf/help2.txt"; |
---|
157 | char charhelp_txt[256] = "conf/charhelp.txt"; |
---|
158 | |
---|
159 | char wisp_server_name[NAME_LENGTH] = "Server"; // can be modified in char-server configuration file |
---|
160 | |
---|
161 | int console = 0; |
---|
162 | int enable_spy = 0; //To enable/disable @spy commands, which consume too much cpu time when sending packets. [Skotlex] |
---|
163 | int enable_grf = 0; //To enable/disable reading maps from GRF files, bypassing mapcache [blackhole89] |
---|
164 | |
---|
165 | /*========================================== |
---|
166 | * server player count (of all mapservers) |
---|
167 | *------------------------------------------*/ |
---|
168 | void map_setusers(int users) |
---|
169 | { |
---|
170 | map_users = users; |
---|
171 | } |
---|
172 | |
---|
173 | int map_getusers(void) |
---|
174 | { |
---|
175 | return map_users; |
---|
176 | } |
---|
177 | |
---|
178 | /*========================================== |
---|
179 | * server player count (this mapserver only) |
---|
180 | *------------------------------------------*/ |
---|
181 | int map_usercount(void) |
---|
182 | { |
---|
183 | /* |
---|
184 | int count = 0; |
---|
185 | struct s_mapiterator* iter = mapit_getallusers(); |
---|
186 | for( mapit_first(iter); mapit_exists(iter); mapit_next(iter) ) |
---|
187 | count++; |
---|
188 | mapit_free(iter); |
---|
189 | return count; |
---|
190 | */ |
---|
191 | // since pc_db now only holds fully authed players, this approach is equivalent: |
---|
192 | return pc_db->size(pc_db); |
---|
193 | } |
---|
194 | |
---|
195 | // |
---|
196 | // blockíÌÀS«mÛ? |
---|
197 | // |
---|
198 | |
---|
199 | /*========================================== |
---|
200 | * blockðfree·éÆ«freeÌ?íèÉÄÔ |
---|
201 | * bN³êĢ鯫Íobt@Éœßé |
---|
202 | *------------------------------------------*/ |
---|
203 | int map_freeblock (struct block_list *bl) |
---|
204 | { |
---|
205 | nullpo_retr(block_free_lock, bl); |
---|
206 | if (block_free_lock == 0 || block_free_count >= block_free_max) |
---|
207 | { |
---|
208 | aFree(bl); |
---|
209 | bl = NULL; |
---|
210 | if (block_free_count >= block_free_max) |
---|
211 | ShowWarning("map_freeblock: too many free block! %d %d\n", block_free_count, block_free_lock); |
---|
212 | } else |
---|
213 | block_free[block_free_count++] = bl; |
---|
214 | |
---|
215 | return block_free_lock; |
---|
216 | } |
---|
217 | /*========================================== |
---|
218 | * blockÌfreeðêsIÉÖ~·é |
---|
219 | *------------------------------------------*/ |
---|
220 | int map_freeblock_lock (void) |
---|
221 | { |
---|
222 | return ++block_free_lock; |
---|
223 | } |
---|
224 | |
---|
225 | /*========================================== |
---|
226 | * blockÌfreeÌbNðð·é |
---|
227 | * ±ÌÆ«AbNª®SÉÈÈéÆ |
---|
228 | * obt@ÉœÜÁÄ¢œblockðSí |
---|
229 | *------------------------------------------*/ |
---|
230 | int map_freeblock_unlock (void) |
---|
231 | { |
---|
232 | if ((--block_free_lock) == 0) { |
---|
233 | int i; |
---|
234 | for (i = 0; i < block_free_count; i++) |
---|
235 | { |
---|
236 | aFree(block_free[i]); |
---|
237 | block_free[i] = NULL; |
---|
238 | } |
---|
239 | block_free_count = 0; |
---|
240 | } else if (block_free_lock < 0) { |
---|
241 | ShowError("map_freeblock_unlock: lock count < 0 !\n"); |
---|
242 | block_free_lock = 0; |
---|
243 | } |
---|
244 | |
---|
245 | return block_free_lock; |
---|
246 | } |
---|
247 | |
---|
248 | // map_freeblock_lock() ðÄñÅ map_freeblock_unlock() ðÄÎÈ¢ |
---|
249 | // Öª ÁœÌÅAèúIÉblock_free_lockðZbg·éæ€É·éB |
---|
250 | // ±ÌÖÍAdo_timer() Ìgbvx©çÄÎêéÌÅA |
---|
251 | // block_free_lock ðŒÚ¢¶ÁÄàx᳢͞B |
---|
252 | |
---|
253 | int map_freeblock_timer(int tid, unsigned int tick, int id, intptr data) |
---|
254 | { |
---|
255 | if (block_free_lock > 0) { |
---|
256 | ShowError("map_freeblock_timer: block_free_lock(%d) is invalid.\n", block_free_lock); |
---|
257 | block_free_lock = 1; |
---|
258 | map_freeblock_unlock(); |
---|
259 | } |
---|
260 | |
---|
261 | return 0; |
---|
262 | } |
---|
263 | |
---|
264 | // |
---|
265 | // block»? |
---|
266 | // |
---|
267 | /*========================================== |
---|
268 | * map[]Ìblock_list©ç?ªÁÄ¢éêÉ |
---|
269 | * bl->prevÉbl_headÌAhXðüêÄš |
---|
270 | *------------------------------------------*/ |
---|
271 | static struct block_list bl_head; |
---|
272 | |
---|
273 | #ifdef CELL_NOSTACK |
---|
274 | /*========================================== |
---|
275 | * These pair of functions update the counter of how many objects |
---|
276 | * lie on a tile. |
---|
277 | *------------------------------------------*/ |
---|
278 | void map_addblcell(struct block_list *bl) |
---|
279 | { |
---|
280 | if( bl->m<0 || bl->x<0 || bl->x>=map[bl->m].xs || bl->y<0 || bl->y>=map[bl->m].ys || !(bl->type&BL_CHAR) ) |
---|
281 | return; |
---|
282 | map[bl->m].cell[bl->x+bl->y*map[bl->m].xs].cell_bl++; |
---|
283 | return; |
---|
284 | } |
---|
285 | |
---|
286 | void map_delblcell(struct block_list *bl) |
---|
287 | { |
---|
288 | if( bl->m <0 || bl->x<0 || bl->x>=map[bl->m].xs || bl->y<0 || bl->y>=map[bl->m].ys || !(bl->type&BL_CHAR) ) |
---|
289 | return; |
---|
290 | map[bl->m].cell[bl->x+bl->y*map[bl->m].xs].cell_bl--; |
---|
291 | } |
---|
292 | #endif |
---|
293 | |
---|
294 | /*========================================== |
---|
295 | * Adds a block to the map. |
---|
296 | * Returns 0 on success, 1 on failure (illegal coordinates). |
---|
297 | *------------------------------------------*/ |
---|
298 | int map_addblock(struct block_list* bl) |
---|
299 | { |
---|
300 | int m, x, y, pos; |
---|
301 | |
---|
302 | nullpo_retr(0, bl); |
---|
303 | |
---|
304 | if (bl->prev != NULL) { |
---|
305 | ShowError("map_addblock: bl->prev != NULL\n"); |
---|
306 | return 1; |
---|
307 | } |
---|
308 | |
---|
309 | m = bl->m; |
---|
310 | x = bl->x; |
---|
311 | y = bl->y; |
---|
312 | if( m < 0 || m >= map_num ) |
---|
313 | { |
---|
314 | ShowError("map_addblock: invalid map id (%d), only %d are loaded.\n", m, map_num); |
---|
315 | return 1; |
---|
316 | } |
---|
317 | if( x < 0 || x >= map[m].xs || y < 0 || y >= map[m].ys ) |
---|
318 | { |
---|
319 | ShowError("map_addblock: out-of-bounds coordinates (\"%s\",%d,%d), map is %dx%d\n", map[m].name, x, y, map[m].xs, map[m].ys); |
---|
320 | return 1; |
---|
321 | } |
---|
322 | |
---|
323 | pos = x/BLOCK_SIZE+(y/BLOCK_SIZE)*map[m].bxs; |
---|
324 | |
---|
325 | if (bl->type == BL_MOB) { |
---|
326 | bl->next = map[m].block_mob[pos]; |
---|
327 | bl->prev = &bl_head; |
---|
328 | if (bl->next) bl->next->prev = bl; |
---|
329 | map[m].block_mob[pos] = bl; |
---|
330 | } else { |
---|
331 | bl->next = map[m].block[pos]; |
---|
332 | bl->prev = &bl_head; |
---|
333 | if (bl->next) bl->next->prev = bl; |
---|
334 | map[m].block[pos] = bl; |
---|
335 | } |
---|
336 | |
---|
337 | #ifdef CELL_NOSTACK |
---|
338 | map_addblcell(bl); |
---|
339 | #endif |
---|
340 | |
---|
341 | return 0; |
---|
342 | } |
---|
343 | |
---|
344 | /*========================================== |
---|
345 | * Removes a block from the map. |
---|
346 | *------------------------------------------*/ |
---|
347 | int map_delblock(struct block_list* bl) |
---|
348 | { |
---|
349 | int pos; |
---|
350 | nullpo_retr(0, bl); |
---|
351 | |
---|
352 | // ?Éblocklist©ç?¯Ä¢é |
---|
353 | if (bl->prev == NULL) { |
---|
354 | if (bl->next != NULL) { |
---|
355 | // prevªNULLÅnextªNULLÅÈ¢ÌÍLÁÄÍÈçÈ¢ |
---|
356 | ShowError("map_delblock error : bl->next!=NULL\n"); |
---|
357 | } |
---|
358 | return 0; |
---|
359 | } |
---|
360 | |
---|
361 | #ifdef CELL_NOSTACK |
---|
362 | map_delblcell(bl); |
---|
363 | #endif |
---|
364 | |
---|
365 | pos = bl->x/BLOCK_SIZE+(bl->y/BLOCK_SIZE)*map[bl->m].bxs; |
---|
366 | |
---|
367 | if (bl->next) |
---|
368 | bl->next->prev = bl->prev; |
---|
369 | if (bl->prev == &bl_head) { |
---|
370 | // Xg̪ÈÌÅAmap[]Ìblock_listðXV·é |
---|
371 | if (bl->type == BL_MOB) { |
---|
372 | map[bl->m].block_mob[pos] = bl->next; |
---|
373 | } else { |
---|
374 | map[bl->m].block[pos] = bl->next; |
---|
375 | } |
---|
376 | } else { |
---|
377 | bl->prev->next = bl->next; |
---|
378 | } |
---|
379 | bl->next = NULL; |
---|
380 | bl->prev = NULL; |
---|
381 | |
---|
382 | return 0; |
---|
383 | } |
---|
384 | |
---|
385 | /*========================================== |
---|
386 | * Moves a block a x/y target position. [Skotlex] |
---|
387 | * Pass flag as 1 to prevent doing skill_unit_move checks |
---|
388 | * (which are executed by default on BL_CHAR types) |
---|
389 | *------------------------------------------*/ |
---|
390 | int map_moveblock(struct block_list *bl, int x1, int y1, unsigned int tick) |
---|
391 | { |
---|
392 | int x0 = bl->x, y0 = bl->y; |
---|
393 | struct status_change *sc = NULL; |
---|
394 | int moveblock = ( x0/BLOCK_SIZE != x1/BLOCK_SIZE || y0/BLOCK_SIZE != y1/BLOCK_SIZE); |
---|
395 | |
---|
396 | if (!bl->prev) { |
---|
397 | //Block not in map, just update coordinates, but do naught else. |
---|
398 | bl->x = x1; |
---|
399 | bl->y = y1; |
---|
400 | return 0; |
---|
401 | } |
---|
402 | |
---|
403 | //TODO: Perhaps some outs of bounds checking should be placed here? |
---|
404 | if (bl->type&BL_CHAR) { |
---|
405 | skill_unit_move(bl,tick,2); |
---|
406 | sc = status_get_sc(bl); |
---|
407 | if (sc && sc->count) { |
---|
408 | if (sc->data[SC_CLOSECONFINE]) |
---|
409 | status_change_end(bl, SC_CLOSECONFINE, -1); |
---|
410 | if (sc->data[SC_CLOSECONFINE2]) |
---|
411 | status_change_end(bl, SC_CLOSECONFINE2, -1); |
---|
412 | // if (sc->data[SC_BLADESTOP]) //Won't stop when you are knocked away, go figure... |
---|
413 | // status_change_end(bl, SC_BLADESTOP, -1); |
---|
414 | if (sc->data[SC_BASILICA]) |
---|
415 | status_change_end(bl, SC_BASILICA, -1); |
---|
416 | if (sc->data[SC_TATAMIGAESHI]) |
---|
417 | status_change_end(bl, SC_TATAMIGAESHI, -1); |
---|
418 | if (sc->data[SC_MAGICROD]) |
---|
419 | status_change_end(bl, SC_MAGICROD, -1); |
---|
420 | } |
---|
421 | } else |
---|
422 | if (bl->type == BL_NPC) |
---|
423 | npc_unsetcells((TBL_NPC*)bl); |
---|
424 | |
---|
425 | if (moveblock) map_delblock(bl); |
---|
426 | #ifdef CELL_NOSTACK |
---|
427 | else map_delblcell(bl); |
---|
428 | #endif |
---|
429 | bl->x = x1; |
---|
430 | bl->y = y1; |
---|
431 | if (moveblock) map_addblock(bl); |
---|
432 | #ifdef CELL_NOSTACK |
---|
433 | else map_addblcell(bl); |
---|
434 | #endif |
---|
435 | |
---|
436 | if (bl->type&BL_CHAR) { |
---|
437 | skill_unit_move(bl,tick,3); |
---|
438 | if (sc) { |
---|
439 | if (sc->count) { |
---|
440 | if (sc->data[SC_CLOAKING]) |
---|
441 | skill_check_cloaking(bl, sc->data[SC_CLOAKING]); |
---|
442 | if (sc->data[SC_DANCING]) |
---|
443 | skill_unit_move_unit_group((struct skill_unit_group *)sc->data[SC_DANCING]->val2, bl->m, x1-x0, y1-y0); |
---|
444 | if (sc->data[SC_WARM]) |
---|
445 | skill_unit_move_unit_group((struct skill_unit_group *)sc->data[SC_WARM]->val4, bl->m, x1-x0, y1-y0); |
---|
446 | } |
---|
447 | } |
---|
448 | } else |
---|
449 | if (bl->type == BL_NPC) |
---|
450 | npc_setcells((TBL_NPC*)bl); |
---|
451 | |
---|
452 | return 0; |
---|
453 | } |
---|
454 | |
---|
455 | /*========================================== |
---|
456 | * Counts specified number of objects on given cell. |
---|
457 | *------------------------------------------*/ |
---|
458 | int map_count_oncell(int m, int x, int y, int type) |
---|
459 | { |
---|
460 | int bx,by; |
---|
461 | struct block_list *bl; |
---|
462 | int count = 0; |
---|
463 | |
---|
464 | if (x < 0 || y < 0 || (x >= map[m].xs) || (y >= map[m].ys)) |
---|
465 | return 0; |
---|
466 | |
---|
467 | bx = x/BLOCK_SIZE; |
---|
468 | by = y/BLOCK_SIZE; |
---|
469 | |
---|
470 | if (type&~BL_MOB) |
---|
471 | for( bl = map[m].block[bx+by*map[m].bxs] ; bl != NULL ; bl = bl->next ) |
---|
472 | if(bl->x == x && bl->y == y && bl->type&type) |
---|
473 | count++; |
---|
474 | |
---|
475 | if (type&BL_MOB) |
---|
476 | for( bl = map[m].block_mob[bx+by*map[m].bxs] ; bl != NULL ; bl = bl->next ) |
---|
477 | if(bl->x == x && bl->y == y) |
---|
478 | count++; |
---|
479 | |
---|
480 | return count; |
---|
481 | } |
---|
482 | /* |
---|
483 | * «»«EŸªÎõÌôøªËÌžªÄª±ª¿«¹««Eæ«Ë«Ã«ÈªòÚ÷ª¹ |
---|
484 | */ |
---|
485 | struct skill_unit* map_find_skill_unit_oncell(struct block_list* target,int x,int y,int skill_id,struct skill_unit* out_unit) |
---|
486 | { |
---|
487 | int m,bx,by; |
---|
488 | struct block_list *bl; |
---|
489 | struct skill_unit *unit; |
---|
490 | m = target->m; |
---|
491 | |
---|
492 | if (x < 0 || y < 0 || (x >= map[m].xs) || (y >= map[m].ys)) |
---|
493 | return NULL; |
---|
494 | |
---|
495 | bx = x/BLOCK_SIZE; |
---|
496 | by = y/BLOCK_SIZE; |
---|
497 | |
---|
498 | for( bl = map[m].block[bx+by*map[m].bxs] ; bl != NULL ; bl = bl->next ) |
---|
499 | { |
---|
500 | if (bl->x != x || bl->y != y || bl->type != BL_SKILL) |
---|
501 | continue; |
---|
502 | |
---|
503 | unit = (struct skill_unit *) bl; |
---|
504 | if( unit == out_unit || !unit->alive || !unit->group || unit->group->skill_id != skill_id ) |
---|
505 | continue; |
---|
506 | if( battle_check_target(&unit->bl,target,unit->group->target_flag) > 0 ) |
---|
507 | return unit; |
---|
508 | } |
---|
509 | return NULL; |
---|
510 | } |
---|
511 | |
---|
512 | /*========================================== |
---|
513 | * Adapted from foreachinarea for an easier invocation. [Skotlex] |
---|
514 | *------------------------------------------*/ |
---|
515 | int map_foreachinrange(int (*func)(struct block_list*,va_list), struct block_list* center, int range, int type, ...) |
---|
516 | { |
---|
517 | va_list ap; |
---|
518 | int bx,by,m; |
---|
519 | int returnCount =0; //total sum of returned values of func() [Skotlex] |
---|
520 | struct block_list *bl; |
---|
521 | int blockcount=bl_list_count,i; |
---|
522 | int x0,x1,y0,y1; |
---|
523 | va_start(ap,type); |
---|
524 | |
---|
525 | m = center->m; |
---|
526 | x0 = max(center->x-range, 0); |
---|
527 | y0 = max(center->y-range, 0); |
---|
528 | x1 = min(center->x+range, map[m].xs-1); |
---|
529 | y1 = min(center->y+range, map[m].ys-1); |
---|
530 | |
---|
531 | if (type&~BL_MOB) |
---|
532 | for (by = y0 / BLOCK_SIZE; by <= y1 / BLOCK_SIZE; by++) { |
---|
533 | for(bx = x0 / BLOCK_SIZE; bx <= x1 / BLOCK_SIZE; bx++) { |
---|
534 | for( bl = map[m].block[bx+by*map[m].bxs] ; bl != NULL ; bl = bl->next ) |
---|
535 | { |
---|
536 | if( bl->type&type |
---|
537 | && bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1 |
---|
538 | #ifdef CIRCULAR_AREA |
---|
539 | && check_distance_bl(center, bl, range) |
---|
540 | #endif |
---|
541 | && bl_list_count<BL_LIST_MAX) |
---|
542 | bl_list[bl_list_count++]=bl; |
---|
543 | } |
---|
544 | } |
---|
545 | } |
---|
546 | if(type&BL_MOB) |
---|
547 | for(by=y0/BLOCK_SIZE;by<=y1/BLOCK_SIZE;by++){ |
---|
548 | for(bx=x0/BLOCK_SIZE;bx<=x1/BLOCK_SIZE;bx++){ |
---|
549 | for( bl = map[m].block_mob[bx+by*map[m].bxs] ; bl != NULL ; bl = bl->next ) |
---|
550 | { |
---|
551 | if( bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1 |
---|
552 | #ifdef CIRCULAR_AREA |
---|
553 | && check_distance_bl(center, bl, range) |
---|
554 | #endif |
---|
555 | && bl_list_count<BL_LIST_MAX) |
---|
556 | bl_list[bl_list_count++]=bl; |
---|
557 | } |
---|
558 | } |
---|
559 | } |
---|
560 | |
---|
561 | if(bl_list_count>=BL_LIST_MAX) |
---|
562 | ShowWarning("map_foreachinrange: block count too many!\n"); |
---|
563 | |
---|
564 | map_freeblock_lock(); // ©çÌðúðÖ~·é |
---|
565 | |
---|
566 | for(i=blockcount;i<bl_list_count;i++) |
---|
567 | if(bl_list[i]->prev) // L?©Ç€©`FbN |
---|
568 | returnCount += func(bl_list[i],ap); |
---|
569 | |
---|
570 | map_freeblock_unlock(); // ðúð·é |
---|
571 | |
---|
572 | va_end(ap); |
---|
573 | bl_list_count = blockcount; |
---|
574 | return returnCount; //[Skotlex] |
---|
575 | } |
---|
576 | |
---|
577 | /*========================================== |
---|
578 | * Same as foreachinrange, but there must be a shoot-able range between center and target to be counted in. [Skotlex] |
---|
579 | *------------------------------------------*/ |
---|
580 | int map_foreachinshootrange(int (*func)(struct block_list*,va_list),struct block_list* center, int range, int type,...) |
---|
581 | { |
---|
582 | va_list ap; |
---|
583 | int bx,by,m; |
---|
584 | int returnCount =0; //total sum of returned values of func() [Skotlex] |
---|
585 | struct block_list *bl; |
---|
586 | int blockcount=bl_list_count,i; |
---|
587 | int x0,x1,y0,y1; |
---|
588 | |
---|
589 | m = center->m; |
---|
590 | if (m < 0) |
---|
591 | return 0; |
---|
592 | |
---|
593 | va_start(ap,type); |
---|
594 | |
---|
595 | x0 = max(center->x-range, 0); |
---|
596 | y0 = max(center->y-range, 0); |
---|
597 | x1 = min(center->x+range, map[m].xs-1); |
---|
598 | y1 = min(center->y+range, map[m].ys-1); |
---|
599 | |
---|
600 | if (type&~BL_MOB) |
---|
601 | for(by = y0 / BLOCK_SIZE; by <= y1 / BLOCK_SIZE; by++) { |
---|
602 | for(bx = x0 / BLOCK_SIZE; bx <= x1 / BLOCK_SIZE; bx++) { |
---|
603 | for( bl = map[m].block[bx+by*map[m].bxs] ; bl != NULL ; bl = bl->next ) |
---|
604 | { |
---|
605 | if( bl->type&type |
---|
606 | && bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1 |
---|
607 | #ifdef CIRCULAR_AREA |
---|
608 | && check_distance_bl(center, bl, range) |
---|
609 | #endif |
---|
610 | && path_search_long(NULL,center->m,center->x,center->y,bl->x,bl->y,CELL_CHKWALL) |
---|
611 | && bl_list_count<BL_LIST_MAX) |
---|
612 | bl_list[bl_list_count++]=bl; |
---|
613 | } |
---|
614 | } |
---|
615 | } |
---|
616 | if(type&BL_MOB) |
---|
617 | for(by=y0/BLOCK_SIZE;by<=y1/BLOCK_SIZE;by++){ |
---|
618 | for(bx=x0/BLOCK_SIZE;bx<=x1/BLOCK_SIZE;bx++){ |
---|
619 | for( bl = map[m].block_mob[bx+by*map[m].bxs] ; bl != NULL ; bl = bl->next ) |
---|
620 | { |
---|
621 | if( bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1 |
---|
622 | #ifdef CIRCULAR_AREA |
---|
623 | && check_distance_bl(center, bl, range) |
---|
624 | #endif |
---|
625 | && path_search_long(NULL,center->m,center->x,center->y,bl->x,bl->y,CELL_CHKWALL) |
---|
626 | && bl_list_count<BL_LIST_MAX) |
---|
627 | bl_list[bl_list_count++]=bl; |
---|
628 | } |
---|
629 | } |
---|
630 | } |
---|
631 | |
---|
632 | if(bl_list_count>=BL_LIST_MAX) |
---|
633 | ShowWarning("map_foreachinrange: block count too many!\n"); |
---|
634 | |
---|
635 | map_freeblock_lock(); // ©çÌðúðÖ~·é |
---|
636 | |
---|
637 | for(i=blockcount;i<bl_list_count;i++) |
---|
638 | if(bl_list[i]->prev) // L?©Ç€©`FbN |
---|
639 | returnCount += func(bl_list[i],ap); |
---|
640 | |
---|
641 | map_freeblock_unlock(); // ðúð·é |
---|
642 | |
---|
643 | va_end(ap); |
---|
644 | bl_list_count = blockcount; |
---|
645 | return returnCount; //[Skotlex] |
---|
646 | } |
---|
647 | |
---|
648 | /*========================================== |
---|
649 | * map m (x0,y0)-(x1,y1)?ÌSobjÉ?µÄ |
---|
650 | * funcðÄÔ |
---|
651 | * type!=0 Èç»ÌíÞÌÝ |
---|
652 | *------------------------------------------*/ |
---|
653 | int map_foreachinarea(int (*func)(struct block_list*,va_list), int m, int x0, int y0, int x1, int y1, int type, ...) |
---|
654 | { |
---|
655 | va_list ap; |
---|
656 | int bx,by; |
---|
657 | int returnCount =0; //total sum of returned values of func() [Skotlex] |
---|
658 | struct block_list *bl; |
---|
659 | int blockcount=bl_list_count,i; |
---|
660 | |
---|
661 | if (m < 0) |
---|
662 | return 0; |
---|
663 | va_start(ap,type); |
---|
664 | if (x1 < x0) |
---|
665 | { //Swap range |
---|
666 | bx = x0; |
---|
667 | x0 = x1; |
---|
668 | x1 = bx; |
---|
669 | } |
---|
670 | if (y1 < y0) |
---|
671 | { |
---|
672 | bx = y0; |
---|
673 | y0 = y1; |
---|
674 | y1 = bx; |
---|
675 | } |
---|
676 | if (x0 < 0) x0 = 0; |
---|
677 | if (y0 < 0) y0 = 0; |
---|
678 | if (x1 >= map[m].xs) x1 = map[m].xs-1; |
---|
679 | if (y1 >= map[m].ys) y1 = map[m].ys-1; |
---|
680 | |
---|
681 | if (type&~BL_MOB) |
---|
682 | for(by = y0 / BLOCK_SIZE; by <= y1 / BLOCK_SIZE; by++) |
---|
683 | for(bx = x0 / BLOCK_SIZE; bx <= x1 / BLOCK_SIZE; bx++) |
---|
684 | for( bl = map[m].block[bx+by*map[m].bxs] ; bl != NULL ; bl = bl->next ) |
---|
685 | if(bl->type&type && bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1 && bl_list_count<BL_LIST_MAX) |
---|
686 | bl_list[bl_list_count++]=bl; |
---|
687 | |
---|
688 | if(type&BL_MOB) |
---|
689 | for(by=y0/BLOCK_SIZE;by<=y1/BLOCK_SIZE;by++) |
---|
690 | for(bx=x0/BLOCK_SIZE;bx<=x1/BLOCK_SIZE;bx++) |
---|
691 | for( bl = map[m].block_mob[bx+by*map[m].bxs] ; bl != NULL ; bl = bl->next ) |
---|
692 | if(bl->x>=x0 && bl->x<=x1 && bl->y>=y0 && bl->y<=y1 && bl_list_count<BL_LIST_MAX) |
---|
693 | bl_list[bl_list_count++]=bl; |
---|
694 | |
---|
695 | if(bl_list_count>=BL_LIST_MAX) |
---|
696 | ShowWarning("map_foreachinarea: block count too many!\n"); |
---|
697 | |
---|
698 | map_freeblock_lock(); // ©çÌðúðÖ~·é |
---|
699 | |
---|
700 | for(i=blockcount;i<bl_list_count;i++) |
---|
701 | if(bl_list[i]->prev) // L?©Ç€©`FbN |
---|
702 | returnCount += func(bl_list[i],ap); |
---|
703 | |
---|
704 | map_freeblock_unlock(); // ðúð·é |
---|
705 | |
---|
706 | va_end(ap); |
---|
707 | bl_list_count = blockcount; |
---|
708 | return returnCount; //[Skotlex] |
---|
709 | } |
---|
710 | |
---|
711 | /*========================================== |
---|
712 | * é`(x0,y0)-(x1,y1)ª(dx,dy)Ú®µœbÌ |
---|
713 | * ÌæOÉÈéÌæ(é`©L`)?ÌobjÉ |
---|
714 | * ?µÄfuncðÄÔ |
---|
715 | * |
---|
716 | * dx,dyÍ-1,0,1ÌÝÆ·éiÇñÈlÅࢢÁÛ¢Hj |
---|
717 | *------------------------------------------*/ |
---|
718 | int map_foreachinmovearea(int (*func)(struct block_list*,va_list), struct block_list* center, int range, int dx, int dy, int type, ...) |
---|
719 | { |
---|
720 | int bx,by,m; |
---|
721 | int returnCount =0; //total sum of returned values of func() [Skotlex] |
---|
722 | struct block_list *bl; |
---|
723 | va_list ap; |
---|
724 | int blockcount=bl_list_count,i; |
---|
725 | int x0, x1, y0, y1; |
---|
726 | |
---|
727 | if (!range) return 0; |
---|
728 | if (!dx && !dy) return 0; //No movement. |
---|
729 | va_start(ap,type); |
---|
730 | m = center->m; |
---|
731 | |
---|
732 | x0 = center->x-range; |
---|
733 | x1 = center->x+range; |
---|
734 | y0 = center->y-range; |
---|
735 | y1 = center->y+range; |
---|
736 | |
---|
737 | if (x1 < x0) |
---|
738 | { //Swap range |
---|
739 | bx = x0; |
---|
740 | x0 = x1; |
---|
741 | x1 = bx; |
---|
742 | } |
---|
743 | if (y1 < y0) |
---|
744 | { |
---|
745 | bx = y0; |
---|
746 | y0 = y1; |
---|
747 | y1 = bx; |
---|
748 | } |
---|
749 | if(dx==0 || dy==0){ |
---|
750 | //Movement along one axis only. |
---|
751 | if(dx==0){ |
---|
752 | if(dy<0) //Moving south |
---|
753 | y0=y1+dy+1; |
---|
754 | else //North |
---|
755 | y1=y0+dy-1; |
---|
756 | } else { //dy == 0 |
---|
757 | if(dx<0) //West |
---|
758 | x0=x1+dx+1; |
---|
759 | else //East |
---|
760 | x1=x0+dx-1; |
---|
761 | } |
---|
762 | if(x0<0) x0=0; |
---|
763 | if(y0<0) y0=0; |
---|
764 | if(x1>=map[m].xs) x1=map[m].xs-1; |
---|
765 | if(y1>=map[m].ys) y1=map[m].ys-1; |
---|
766 | for(by=y0/BLOCK_SIZE;by<=y1/BLOCK_SIZE;by++){ |
---|
767 | for(bx=x0/BLOCK_SIZE;bx<=x1/BLOCK_SIZE;bx++){ |
---|
768 | if (type&~BL_MOB) { |
---|
769 | for( bl = map[m].block[bx+by*map[m].bxs] ; bl != NULL ; bl = bl->next ) |
---|
770 | { |
---|
771 | if(bl->type&type && |
---|
772 | bl->x>=x0 && bl->x<=x1 && |
---|
773 | bl->y>=y0 && bl->y<=y1 && |
---|
774 | bl_list_count<BL_LIST_MAX) |
---|
775 | bl_list[bl_list_count++]=bl; |
---|
776 | } |
---|
777 | } |
---|
778 | if (type&BL_MOB) { |
---|
779 | for( bl = map[m].block_mob[bx+by*map[m].bxs] ; bl != NULL ; bl = bl->next ) |
---|
780 | { |
---|
781 | if(bl->x>=x0 && bl->x<=x1 && |
---|
782 | bl->y>=y0 && bl->y<=y1 && |
---|
783 | bl_list_count<BL_LIST_MAX) |
---|
784 | bl_list[bl_list_count++]=bl; |
---|
785 | } |
---|
786 | } |
---|
787 | } |
---|
788 | } |
---|
789 | }else{ |
---|
790 | // Diagonal movement |
---|
791 | if(x0<0) x0=0; |
---|
792 | if(y0<0) y0=0; |
---|
793 | if(x1>=map[m].xs) x1=map[m].xs-1; |
---|
794 | if(y1>=map[m].ys) y1=map[m].ys-1; |
---|
795 | for(by=y0/BLOCK_SIZE;by<=y1/BLOCK_SIZE;by++){ |
---|
796 | for(bx=x0/BLOCK_SIZE;bx<=x1/BLOCK_SIZE;bx++){ |
---|
797 | if (type & ~BL_MOB) { |
---|
798 | for( bl = map[m].block[bx+by*map[m].bxs] ; bl != NULL ; bl = bl->next ) |
---|
799 | { |
---|
800 | if( bl->type&type && |
---|
801 | bl->x>=x0 && bl->x<=x1 && |
---|
802 | bl->y>=y0 && bl->y<=y1 && |
---|
803 | bl_list_count<BL_LIST_MAX ) |
---|
804 | if((dx>0 && bl->x<x0+dx) || |
---|
805 | (dx<0 && bl->x>x1+dx) || |
---|
806 | (dy>0 && bl->y<y0+dy) || |
---|
807 | (dy<0 && bl->y>y1+dy)) |
---|
808 | bl_list[bl_list_count++]=bl; |
---|
809 | } |
---|
810 | } |
---|
811 | if (type & BL_MOB) { |
---|
812 | for( bl = map[m].block_mob[bx+by*map[m].bxs] ; bl != NULL ; bl = bl->next ) |
---|
813 | { |
---|
814 | if( bl->x>=x0 && bl->x<=x1 && |
---|
815 | bl->y>=y0 && bl->y<=y1 && |
---|
816 | bl_list_count<BL_LIST_MAX) |
---|
817 | if((dx>0 && bl->x<x0+dx) || |
---|
818 | (dx<0 && bl->x>x1+dx) || |
---|
819 | (dy>0 && bl->y<y0+dy) || |
---|
820 | (dy<0 && bl->y>y1+dy)) |
---|
821 | bl_list[bl_list_count++]=bl; |
---|
822 | } |
---|
823 | } |
---|
824 | } |
---|
825 | } |
---|
826 | |
---|
827 | } |
---|
828 | |
---|
829 | if(bl_list_count>=BL_LIST_MAX) |
---|
830 | ShowWarning("map_foreachinmovearea: block count too many!\n"); |
---|
831 | |
---|
832 | map_freeblock_lock(); // ©çÌðúðÖ~·é |
---|
833 | |
---|
834 | for(i=blockcount;i<bl_list_count;i++) |
---|
835 | if(bl_list[i]->prev) |
---|
836 | returnCount += func(bl_list[i],ap); |
---|
837 | |
---|
838 | map_freeblock_unlock(); // ðúð·é |
---|
839 | |
---|
840 | va_end(ap); |
---|
841 | bl_list_count = blockcount; |
---|
842 | return returnCount; |
---|
843 | } |
---|
844 | |
---|
845 | // -- moonsoul (added map_foreachincell which is a rework of map_foreachinarea but |
---|
846 | // which only checks the exact single x/y passed to it rather than an |
---|
847 | // area radius - may be more useful in some instances) |
---|
848 | // |
---|
849 | int map_foreachincell(int (*func)(struct block_list*,va_list), int m, int x, int y, int type, ...) |
---|
850 | { |
---|
851 | int bx,by; |
---|
852 | int returnCount =0; //total sum of returned values of func() [Skotlex] |
---|
853 | struct block_list *bl; |
---|
854 | va_list ap; |
---|
855 | int blockcount=bl_list_count,i; |
---|
856 | |
---|
857 | if (x < 0 || y < 0 || x >= map[m].xs || y >= map[m].ys) return 0; |
---|
858 | |
---|
859 | va_start(ap,type); |
---|
860 | |
---|
861 | by=y/BLOCK_SIZE; |
---|
862 | bx=x/BLOCK_SIZE; |
---|
863 | |
---|
864 | if(type&~BL_MOB) |
---|
865 | for( bl = map[m].block[bx+by*map[m].bxs] ; bl != NULL ; bl = bl->next ) |
---|
866 | if(bl->type&type && bl->x==x && bl->y==y && bl_list_count<BL_LIST_MAX) |
---|
867 | bl_list[bl_list_count++]=bl; |
---|
868 | |
---|
869 | if(type&BL_MOB) |
---|
870 | for( bl = map[m].block_mob[bx+by*map[m].bxs] ; bl != NULL ; bl = bl->next ) |
---|
871 | if(bl->x==x && bl->y==y && bl_list_count<BL_LIST_MAX) |
---|
872 | bl_list[bl_list_count++]=bl; |
---|
873 | |
---|
874 | if(bl_list_count>=BL_LIST_MAX) |
---|
875 | ShowWarning("map_foreachincell: block count too many!\n"); |
---|
876 | |
---|
877 | map_freeblock_lock(); // ©çÌðúðÖ~·é |
---|
878 | |
---|
879 | for(i=blockcount;i<bl_list_count;i++) |
---|
880 | if(bl_list[i]->prev) // L?©Ç€©`FbN |
---|
881 | returnCount += func(bl_list[i],ap); |
---|
882 | |
---|
883 | map_freeblock_unlock(); // ðúð·é |
---|
884 | |
---|
885 | va_end(ap); |
---|
886 | bl_list_count = blockcount; |
---|
887 | return returnCount; |
---|
888 | } |
---|
889 | |
---|
890 | /*============================================================ |
---|
891 | * For checking a path between two points (x0, y0) and (x1, y1) |
---|
892 | *------------------------------------------------------------*/ |
---|
893 | int map_foreachinpath(int (*func)(struct block_list*,va_list),int m,int x0,int y0,int x1,int y1,int range,int length, int type,...) |
---|
894 | { |
---|
895 | int returnCount =0; //total sum of returned values of func() [Skotlex] |
---|
896 | ////////////////////////////////////////////////////////////// |
---|
897 | // |
---|
898 | // sharp shooting 3 [Skotlex] |
---|
899 | // |
---|
900 | ////////////////////////////////////////////////////////////// |
---|
901 | // problem: |
---|
902 | // Same as Sharp Shooting 1. Hits all targets within range of |
---|
903 | // the line. |
---|
904 | // (t1,t2 t3 and t4 get hit) |
---|
905 | // |
---|
906 | // target 1 |
---|
907 | // x t4 |
---|
908 | // t2 |
---|
909 | // t3 x |
---|
910 | // x |
---|
911 | // S |
---|
912 | ////////////////////////////////////////////////////////////// |
---|
913 | // Methodology: |
---|
914 | // My trigonometrics and math are a little rusty... so the approach I am writing |
---|
915 | // here is basicly do a double for to check for all targets in the square that |
---|
916 | // contains the initial and final positions (area range increased to match the |
---|
917 | // radius given), then for each object to test, calculate the distance to the |
---|
918 | // path and include it if the range fits and the target is in the line (0<k<1, |
---|
919 | // as they call it). |
---|
920 | // The implementation I took as reference is found at |
---|
921 | // http://astronomy.swin.edu.au/~pbourke/geometry/pointline/ |
---|
922 | // (they have a link to a C implementation, too) |
---|
923 | // This approach is a lot like #2 commented on this function, which I have no |
---|
924 | // idea why it was commented. I won't use doubles/floats, but pure int math for |
---|
925 | // speed purposes. The range considered is always the same no matter how |
---|
926 | // close/far the target is because that's how SharpShooting works currently in |
---|
927 | // kRO. |
---|
928 | |
---|
929 | //Generic map_foreach* variables. |
---|
930 | va_list ap; |
---|
931 | int i, blockcount = bl_list_count; |
---|
932 | struct block_list *bl; |
---|
933 | int bx, by; |
---|
934 | //method specific variables |
---|
935 | int magnitude2, len_limit; //The square of the magnitude |
---|
936 | int k, xi, yi, xu, yu; |
---|
937 | int mx0 = x0, mx1 = x1, my0 = y0, my1 = y1; |
---|
938 | |
---|
939 | //Avoid needless calculations by not getting the sqrt right away. |
---|
940 | #define MAGNITUDE2(x0, y0, x1, y1) (((x1)-(x0))*((x1)-(x0)) + ((y1)-(y0))*((y1)-(y0))) |
---|
941 | |
---|
942 | if (m < 0) |
---|
943 | return 0; |
---|
944 | |
---|
945 | va_start(ap,type); |
---|
946 | |
---|
947 | len_limit = magnitude2 = MAGNITUDE2(x0,y0, x1,y1); |
---|
948 | if (magnitude2 < 1) //Same begin and ending point, can't trace path. |
---|
949 | return 0; |
---|
950 | |
---|
951 | if (length) |
---|
952 | { //Adjust final position to fit in the given area. |
---|
953 | //TODO: Find an alternate method which does not requires a square root calculation. |
---|
954 | k = (int)sqrt((float)magnitude2); |
---|
955 | mx1 = x0 + (x1 - x0)*length/k; |
---|
956 | my1 = y0 + (y1 - y0)*length/k; |
---|
957 | len_limit = MAGNITUDE2(x0,y0, mx1,my1); |
---|
958 | } |
---|
959 | //Expand target area to cover range. |
---|
960 | if (mx0 > mx1) |
---|
961 | { |
---|
962 | mx0+=range; |
---|
963 | mx1-=range; |
---|
964 | } else { |
---|
965 | mx0-=range; |
---|
966 | mx1+=range; |
---|
967 | } |
---|
968 | if (my0 > my1) |
---|
969 | { |
---|
970 | my0+=range; |
---|
971 | my1-=range; |
---|
972 | } else { |
---|
973 | my0-=range; |
---|
974 | my1+=range; |
---|
975 | } |
---|
976 | |
---|
977 | //The two fors assume mx0 < mx1 && my0 < my1 |
---|
978 | if (mx0 > mx1) |
---|
979 | { |
---|
980 | k = mx1; |
---|
981 | mx1 = mx0; |
---|
982 | mx0 = k; |
---|
983 | } |
---|
984 | if (my0 > my1) |
---|
985 | { |
---|
986 | k = my1; |
---|
987 | my1 = my0; |
---|
988 | my0 = k; |
---|
989 | } |
---|
990 | |
---|
991 | if (mx0 < 0) mx0 = 0; |
---|
992 | if (my0 < 0) my0 = 0; |
---|
993 | if (mx1 >= map[m].xs) mx1 = map[m].xs-1; |
---|
994 | if (my1 >= map[m].ys) my1 = map[m].ys-1; |
---|
995 | |
---|
996 | range*=range<<8; //Values are shifted later on for higher precision using int math. |
---|
997 | |
---|
998 | if (type & ~BL_MOB) |
---|
999 | for (by = my0 / BLOCK_SIZE; by <= my1 / BLOCK_SIZE; by++) { |
---|
1000 | for(bx=mx0/BLOCK_SIZE;bx<=mx1/BLOCK_SIZE;bx++){ |
---|
1001 | for( bl = map[m].block[bx+by*map[m].bxs] ; bl != NULL ; bl = bl->next ) |
---|
1002 | { |
---|
1003 | if(bl->prev && bl->type&type && bl_list_count<BL_LIST_MAX) |
---|
1004 | { |
---|
1005 | xi = bl->x; |
---|
1006 | yi = bl->y; |
---|
1007 | |
---|
1008 | k = (xi-x0)*(x1-x0) + (yi-y0)*(y1-y0); |
---|
1009 | if (k < 0 || k > len_limit) //Since more skills use this, check for ending point as well. |
---|
1010 | continue; |
---|
1011 | |
---|
1012 | if (k > magnitude2 && !path_search_long(NULL,m,x0,y0,xi,yi,CELL_CHKWALL)) |
---|
1013 | continue; //Targets beyond the initial ending point need the wall check. |
---|
1014 | |
---|
1015 | //All these shifts are to increase the precision of the intersection point and distance considering how it's |
---|
1016 | //int math. |
---|
1017 | k = (k<<4)/magnitude2; //k will be between 1~16 instead of 0~1 |
---|
1018 | xi<<=4; |
---|
1019 | yi<<=4; |
---|
1020 | xu= (x0<<4) +k*(x1-x0); |
---|
1021 | yu= (y0<<4) +k*(y1-y0); |
---|
1022 | k = MAGNITUDE2(xi, yi, xu, yu); |
---|
1023 | |
---|
1024 | //If all dot coordinates were <<4 the square of the magnitude is <<8 |
---|
1025 | if (k > range) |
---|
1026 | continue; |
---|
1027 | |
---|
1028 | bl_list[bl_list_count++]=bl; |
---|
1029 | } |
---|
1030 | } |
---|
1031 | } |
---|
1032 | } |
---|
1033 | |
---|
1034 | if(type&BL_MOB) |
---|
1035 | for(by=my0/BLOCK_SIZE;by<=my1/BLOCK_SIZE;by++){ |
---|
1036 | for(bx=mx0/BLOCK_SIZE;bx<=mx1/BLOCK_SIZE;bx++){ |
---|
1037 | for( bl = map[m].block_mob[bx+by*map[m].bxs] ; bl != NULL ; bl = bl->next ) |
---|
1038 | { |
---|
1039 | if(bl->prev && bl_list_count<BL_LIST_MAX) |
---|
1040 | { |
---|
1041 | xi = bl->x; |
---|
1042 | yi = bl->y; |
---|
1043 | k = (xi-x0)*(x1-x0) + (yi-y0)*(y1-y0); |
---|
1044 | if (k < 0 || k > len_limit) |
---|
1045 | continue; |
---|
1046 | |
---|
1047 | if (k > magnitude2 && !path_search_long(NULL,m,x0,y0,xi,yi,CELL_CHKWALL)) |
---|
1048 | continue; //Targets beyond the initial ending point need the wall check. |
---|
1049 | |
---|
1050 | k = (k<<4)/magnitude2; //k will be between 1~16 instead of 0~1 |
---|
1051 | xi<<=4; |
---|
1052 | yi<<=4; |
---|
1053 | xu= (x0<<4) +k*(x1-x0); |
---|
1054 | yu= (y0<<4) +k*(y1-y0); |
---|
1055 | k = MAGNITUDE2(xi, yi, xu, yu); |
---|
1056 | |
---|
1057 | //If all dot coordinates were <<4 the square of the magnitude is <<8 |
---|
1058 | if (k > range) |
---|
1059 | continue; |
---|
1060 | |
---|
1061 | bl_list[bl_list_count++]=bl; |
---|
1062 | } |
---|
1063 | } |
---|
1064 | } |
---|
1065 | } |
---|
1066 | |
---|
1067 | if(bl_list_count>=BL_LIST_MAX) |
---|
1068 | ShowWarning("map_foreachinpath: block count too many!\n"); |
---|
1069 | |
---|
1070 | map_freeblock_lock(); |
---|
1071 | |
---|
1072 | for(i=blockcount;i<bl_list_count;i++) |
---|
1073 | if(bl_list[i]->prev) //This check is done in case some object gets killed due to further skill processing. |
---|
1074 | returnCount += func(bl_list[i],ap); |
---|
1075 | |
---|
1076 | map_freeblock_unlock(); |
---|
1077 | |
---|
1078 | va_end(ap); |
---|
1079 | bl_list_count = blockcount; |
---|
1080 | return returnCount; //[Skotlex] |
---|
1081 | |
---|
1082 | } |
---|
1083 | |
---|
1084 | // Copy of map_foreachincell, but applied to the whole map. [Skotlex] |
---|
1085 | int map_foreachinmap(int (*func)(struct block_list*,va_list), int m, int type,...) |
---|
1086 | { |
---|
1087 | int b, bsize; |
---|
1088 | int returnCount =0; //total sum of returned values of func() [Skotlex] |
---|
1089 | struct block_list *bl; |
---|
1090 | va_list ap; |
---|
1091 | int blockcount=bl_list_count,i; |
---|
1092 | |
---|
1093 | va_start(ap,type); |
---|
1094 | |
---|
1095 | bsize = map[m].bxs * map[m].bys; |
---|
1096 | |
---|
1097 | if(type&~BL_MOB) |
---|
1098 | for(b=0;b<bsize;b++) |
---|
1099 | for( bl = map[m].block[b] ; bl != NULL ; bl = bl->next ) |
---|
1100 | if(bl->type&type && bl_list_count<BL_LIST_MAX) |
---|
1101 | bl_list[bl_list_count++]=bl; |
---|
1102 | |
---|
1103 | if(type&BL_MOB) |
---|
1104 | for(b=0;b<bsize;b++) |
---|
1105 | for( bl = map[m].block_mob[b] ; bl != NULL ; bl = bl->next ) |
---|
1106 | if(bl_list_count<BL_LIST_MAX) |
---|
1107 | bl_list[bl_list_count++]=bl; |
---|
1108 | |
---|
1109 | if(bl_list_count>=BL_LIST_MAX) |
---|
1110 | ShowWarning("map_foreachinmap: block count too many!\n"); |
---|
1111 | |
---|
1112 | map_freeblock_lock(); // ©çÌðúðÖ~·é |
---|
1113 | |
---|
1114 | for(i=blockcount;i<bl_list_count;i++) |
---|
1115 | if(bl_list[i]->prev) // L?©Ç€©`FbN |
---|
1116 | returnCount += func(bl_list[i],ap); |
---|
1117 | |
---|
1118 | map_freeblock_unlock(); // ðúð·é |
---|
1119 | |
---|
1120 | va_end(ap); |
---|
1121 | bl_list_count = blockcount; |
---|
1122 | return returnCount; |
---|
1123 | } |
---|
1124 | |
---|
1125 | /*========================================== |
---|
1126 | * °ACeâGtFNgpÌêObjè?Ä |
---|
1127 | * object[]ÖÌÛ¶Æid_dbo?ÜÅ |
---|
1128 | * |
---|
1129 | * bl->idà±ÌÅÝèµÄâè³¢? |
---|
1130 | *------------------------------------------*/ |
---|
1131 | int map_addobject(struct block_list *bl) |
---|
1132 | { |
---|
1133 | int i; |
---|
1134 | if( bl == NULL ){ |
---|
1135 | ShowWarning("map_addobject nullpo?\n"); |
---|
1136 | return 0; |
---|
1137 | } |
---|
1138 | if(first_free_object_id<2 || first_free_object_id>=MAX_FLOORITEM) |
---|
1139 | first_free_object_id=2; |
---|
1140 | for(i=first_free_object_id;i<MAX_FLOORITEM && objects[i];i++); |
---|
1141 | if(i>=MAX_FLOORITEM){ |
---|
1142 | ShowWarning("no free object id\n"); |
---|
1143 | return 0; |
---|
1144 | } |
---|
1145 | first_free_object_id=i; |
---|
1146 | if(last_object_id<i) |
---|
1147 | last_object_id=i; |
---|
1148 | objects[i]=bl; |
---|
1149 | idb_put(id_db,i,bl); |
---|
1150 | return i; |
---|
1151 | } |
---|
1152 | |
---|
1153 | /*========================================== |
---|
1154 | * êObjectÌðú |
---|
1155 | * map_delobjectÌfreeµÈ¢o?W |
---|
1156 | *------------------------------------------*/ |
---|
1157 | int map_delobjectnofree(int id) |
---|
1158 | { |
---|
1159 | if( id < 0 || id >= MAX_FLOORITEM ) |
---|
1160 | { |
---|
1161 | ShowError("map_delobjectnofree: invalid object id '%d'!\n", id); |
---|
1162 | return 0; |
---|
1163 | } |
---|
1164 | |
---|
1165 | if(objects[id]==NULL) |
---|
1166 | return 0; |
---|
1167 | |
---|
1168 | map_delblock(objects[id]); |
---|
1169 | idb_remove(id_db,id); |
---|
1170 | objects[id]=NULL; |
---|
1171 | |
---|
1172 | if(first_free_object_id>id) |
---|
1173 | first_free_object_id=id; |
---|
1174 | |
---|
1175 | while(last_object_id>2 && objects[last_object_id]==NULL) |
---|
1176 | last_object_id--; |
---|
1177 | |
---|
1178 | return 0; |
---|
1179 | } |
---|
1180 | |
---|
1181 | /*========================================== |
---|
1182 | * êObjectÌðú |
---|
1183 | * block_list©çÌíAid_db©çÌí |
---|
1184 | * object dataÌfreeAobject[]ÖÌNULLãü |
---|
1185 | * |
---|
1186 | * addÆÌ??«ª³¢Ìª?ÉÈé |
---|
1187 | *------------------------------------------*/ |
---|
1188 | int map_delobject(int id) |
---|
1189 | { |
---|
1190 | struct block_list* bl; |
---|
1191 | |
---|
1192 | if( id < 0 || id >= MAX_FLOORITEM ) |
---|
1193 | { |
---|
1194 | ShowError("map_delobject: invalid object id '%d'!\n", id); |
---|
1195 | return 0; |
---|
1196 | } |
---|
1197 | |
---|
1198 | if(objects[id]==NULL) |
---|
1199 | return 0; |
---|
1200 | |
---|
1201 | bl = objects[id]; |
---|
1202 | map_delobjectnofree(id); |
---|
1203 | map_freeblock(bl); |
---|
1204 | |
---|
1205 | return 0; |
---|
1206 | } |
---|
1207 | |
---|
1208 | /*========================================== |
---|
1209 | * SêObjèÉfuncðÄÔ |
---|
1210 | * |
---|
1211 | *------------------------------------------*/ |
---|
1212 | void map_foreachobject(int (*func)(struct block_list*,va_list),int type,...) |
---|
1213 | { |
---|
1214 | int i; |
---|
1215 | int blockcount=bl_list_count; |
---|
1216 | va_list ap; |
---|
1217 | |
---|
1218 | va_start(ap,type); |
---|
1219 | |
---|
1220 | for(i=2;i<=last_object_id;i++){ |
---|
1221 | if(objects[i]){ |
---|
1222 | if(!(objects[i]->type==type)) // Fixed [Lance] |
---|
1223 | continue; |
---|
1224 | if(bl_list_count>=BL_LIST_MAX) { |
---|
1225 | ShowWarning("map_foreachobject: too many blocks !\n"); |
---|
1226 | break; |
---|
1227 | } |
---|
1228 | bl_list[bl_list_count++]=objects[i]; |
---|
1229 | } |
---|
1230 | } |
---|
1231 | |
---|
1232 | map_freeblock_lock(); |
---|
1233 | |
---|
1234 | for(i=blockcount;i<bl_list_count;i++) |
---|
1235 | if( bl_list[i]->prev || bl_list[i]->next ) |
---|
1236 | func(bl_list[i],ap); |
---|
1237 | |
---|
1238 | map_freeblock_unlock(); |
---|
1239 | |
---|
1240 | va_end(ap); |
---|
1241 | bl_list_count = blockcount; |
---|
1242 | } |
---|
1243 | |
---|
1244 | /*========================================== |
---|
1245 | * °ACeðÁ· |
---|
1246 | * |
---|
1247 | * data==0ÌbÍtimerÅÁŠœê * data!=0ÌbÍE€ÅÁŠœbƵĮì |
---|
1248 | * |
---|
1249 | * ãÒÍAmap_clearflooritem(id)Ö |
---|
1250 | * map.h?Å#defineµÄ é |
---|
1251 | *------------------------------------------*/ |
---|
1252 | int map_clearflooritem_timer(int tid, unsigned int tick, int id, intptr data) |
---|
1253 | { |
---|
1254 | struct flooritem_data *fitem=NULL; |
---|
1255 | |
---|
1256 | fitem = (struct flooritem_data *)objects[id]; |
---|
1257 | if(fitem==NULL || fitem->bl.type!=BL_ITEM || (!data && fitem->cleartimer != tid)){ |
---|
1258 | ShowError("map_clearflooritem_timer : error\n"); |
---|
1259 | return 1; |
---|
1260 | } |
---|
1261 | if(data) |
---|
1262 | delete_timer(fitem->cleartimer,map_clearflooritem_timer); |
---|
1263 | else if(fitem->item_data.card[0] == CARD0_PET) |
---|
1264 | intif_delete_petdata( MakeDWord(fitem->item_data.card[1],fitem->item_data.card[2]) ); |
---|
1265 | clif_clearflooritem(fitem,0); |
---|
1266 | map_delobject(fitem->bl.id); |
---|
1267 | |
---|
1268 | return 0; |
---|
1269 | } |
---|
1270 | |
---|
1271 | /*========================================== |
---|
1272 | * (m,x,y) locates a random available free cell around the given coordinates |
---|
1273 | * to place an BL_ITEM object. Scan area is 9x9, returns 1 on success. |
---|
1274 | * x and y are modified with the target cell when successful. |
---|
1275 | *------------------------------------------*/ |
---|
1276 | int map_searchrandfreecell(int m,int *x,int *y,int stack) |
---|
1277 | { |
---|
1278 | int free_cell,i,j; |
---|
1279 | int free_cells[9][2]; |
---|
1280 | |
---|
1281 | for(free_cell=0,i=-1;i<=1;i++){ |
---|
1282 | if(i+*y<0 || i+*y>=map[m].ys) |
---|
1283 | continue; |
---|
1284 | for(j=-1;j<=1;j++){ |
---|
1285 | if(j+*x<0 || j+*x>=map[m].xs) |
---|
1286 | continue; |
---|
1287 | if(map_getcell(m,j+*x,i+*y,CELL_CHKNOPASS)) |
---|
1288 | continue; |
---|
1289 | //Avoid item stacking to prevent against exploits. [Skotlex] |
---|
1290 | if(stack && map_count_oncell(m,j+*x,i+*y, BL_ITEM) > stack) |
---|
1291 | continue; |
---|
1292 | free_cells[free_cell][0] = j+*x; |
---|
1293 | free_cells[free_cell++][1] = i+*y; |
---|
1294 | } |
---|
1295 | } |
---|
1296 | if(free_cell==0) |
---|
1297 | return 0; |
---|
1298 | free_cell = rand()%free_cell; |
---|
1299 | *x = free_cells[free_cell][0]; |
---|
1300 | *y = free_cells[free_cell][1]; |
---|
1301 | return 1; |
---|
1302 | } |
---|
1303 | |
---|
1304 | |
---|
1305 | static int map_count_sub(struct block_list *bl,va_list ap) |
---|
1306 | { |
---|
1307 | return 1; |
---|
1308 | } |
---|
1309 | |
---|
1310 | /*========================================== |
---|
1311 | * Locates a random spare cell around the object given, using range as max |
---|
1312 | * distance from that spot. Used for warping functions. Use range < 0 for |
---|
1313 | * whole map range. |
---|
1314 | * Returns 1 on success. when it fails and src is available, x/y are set to src's |
---|
1315 | * src can be null as long as flag&1 |
---|
1316 | * when ~flag&1, m is not needed. |
---|
1317 | * Flag values: |
---|
1318 | * &1 = random cell must be around given m,x,y, not around src |
---|
1319 | * &2 = the target should be able to walk to the target tile. |
---|
1320 | * &4 = there shouldn't be any players around the target tile (use the no_spawn_on_player setting) |
---|
1321 | *------------------------------------------*/ |
---|
1322 | int map_search_freecell(struct block_list *src, int m, short *x,short *y, int rx, int ry, int flag) |
---|
1323 | { |
---|
1324 | int tries, spawn=0; |
---|
1325 | int bx, by; |
---|
1326 | int rx2 = 2*rx+1; |
---|
1327 | int ry2 = 2*ry+1; |
---|
1328 | |
---|
1329 | if (!src && (!(flag&1) || flag&2)) |
---|
1330 | { |
---|
1331 | ShowDebug("map_search_freecell: Incorrect usage! When src is NULL, flag has to be &1 and can't have &2\n"); |
---|
1332 | return 0; |
---|
1333 | } |
---|
1334 | |
---|
1335 | if (flag&1) { |
---|
1336 | bx = *x; |
---|
1337 | by = *y; |
---|
1338 | } else { |
---|
1339 | bx = src->x; |
---|
1340 | by = src->y; |
---|
1341 | m = src->m; |
---|
1342 | } |
---|
1343 | if (!rx && !ry) { |
---|
1344 | //No range? Return the target cell then.... |
---|
1345 | *x = bx; |
---|
1346 | *y = by; |
---|
1347 | return map_getcell(m,*x,*y,CELL_CHKREACH); |
---|
1348 | } |
---|
1349 | |
---|
1350 | if (rx >= 0 && ry >= 0) { |
---|
1351 | tries = rx2*ry2; |
---|
1352 | if (tries > 100) tries = 100; |
---|
1353 | } else { |
---|
1354 | tries = map[m].xs*map[m].ys; |
---|
1355 | if (tries > 500) tries = 500; |
---|
1356 | } |
---|
1357 | |
---|
1358 | while(tries--) { |
---|
1359 | *x = (rx >= 0)?(rand()%rx2-rx+bx):(rand()%(map[m].xs-2)+1); |
---|
1360 | *y = (ry >= 0)?(rand()%ry2-ry+by):(rand()%(map[m].ys-2)+1); |
---|
1361 | |
---|
1362 | if (*x == bx && *y == by) |
---|
1363 | continue; //Avoid picking the same target tile. |
---|
1364 | |
---|
1365 | if (map_getcell(m,*x,*y,CELL_CHKREACH)) |
---|
1366 | { |
---|
1367 | if(flag&2 && !unit_can_reach_pos(src, *x, *y, 1)) |
---|
1368 | continue; |
---|
1369 | if(flag&4) { |
---|
1370 | if (spawn >= 100) return 0; //Limit of retries reached. |
---|
1371 | if (spawn++ < battle_config.no_spawn_on_player && |
---|
1372 | map_foreachinarea(map_count_sub, m, |
---|
1373 | *x-AREA_SIZE, *y-AREA_SIZE, |
---|
1374 | *x+AREA_SIZE, *y+AREA_SIZE, BL_PC) |
---|
1375 | ) |
---|
1376 | continue; |
---|
1377 | } |
---|
1378 | return 1; |
---|
1379 | } |
---|
1380 | } |
---|
1381 | *x = bx; |
---|
1382 | *y = by; |
---|
1383 | return 0; |
---|
1384 | } |
---|
1385 | |
---|
1386 | /*========================================== |
---|
1387 | * (m,x,y)ðSÉ3x3È?ɰACeÝu |
---|
1388 | * |
---|
1389 | * item_dataÍamountÈOðcopy·é |
---|
1390 | * type flag: &1 MVP item. &2 do stacking check. |
---|
1391 | *------------------------------------------*/ |
---|
1392 | int map_addflooritem(struct item *item_data,int amount,int m,int x,int y,int first_charid,int second_charid,int third_charid,int flags) |
---|
1393 | { |
---|
1394 | int r; |
---|
1395 | struct flooritem_data *fitem=NULL; |
---|
1396 | |
---|
1397 | nullpo_retr(0, item_data); |
---|
1398 | |
---|
1399 | if(!map_searchrandfreecell(m,&x,&y,flags&2?1:0)) |
---|
1400 | return 0; |
---|
1401 | r=rand(); |
---|
1402 | |
---|
1403 | CREATE(fitem, struct flooritem_data, 1); |
---|
1404 | fitem->bl.type=BL_ITEM; |
---|
1405 | fitem->bl.prev = fitem->bl.next = NULL; |
---|
1406 | fitem->bl.m=m; |
---|
1407 | fitem->bl.x=x; |
---|
1408 | fitem->bl.y=y; |
---|
1409 | fitem->bl.id = map_addobject(&fitem->bl); |
---|
1410 | if(fitem->bl.id==0){ |
---|
1411 | aFree(fitem); |
---|
1412 | return 0; |
---|
1413 | } |
---|
1414 | |
---|
1415 | fitem->first_get_charid = first_charid; |
---|
1416 | fitem->first_get_tick = gettick() + (flags&1 ? battle_config.mvp_item_first_get_time : battle_config.item_first_get_time); |
---|
1417 | fitem->second_get_charid = second_charid; |
---|
1418 | fitem->second_get_tick = fitem->first_get_tick + (flags&1 ? battle_config.mvp_item_second_get_time : battle_config.item_second_get_time); |
---|
1419 | fitem->third_get_charid = third_charid; |
---|
1420 | fitem->third_get_tick = fitem->second_get_tick + (flags&1 ? battle_config.mvp_item_third_get_time : battle_config.item_third_get_time); |
---|
1421 | |
---|
1422 | memcpy(&fitem->item_data,item_data,sizeof(*item_data)); |
---|
1423 | fitem->item_data.amount=amount; |
---|
1424 | fitem->subx=(r&3)*3+3; |
---|
1425 | fitem->suby=((r>>2)&3)*3+3; |
---|
1426 | fitem->cleartimer=add_timer(gettick()+battle_config.flooritem_lifetime,map_clearflooritem_timer,fitem->bl.id,0); |
---|
1427 | |
---|
1428 | map_addblock(&fitem->bl); |
---|
1429 | clif_dropflooritem(fitem); |
---|
1430 | |
---|
1431 | return fitem->bl.id; |
---|
1432 | } |
---|
1433 | |
---|
1434 | static void* create_charid2nick(DBKey key, va_list args) |
---|
1435 | { |
---|
1436 | struct charid2nick *p; |
---|
1437 | CREATE(p, struct charid2nick, 1); |
---|
1438 | return p; |
---|
1439 | } |
---|
1440 | |
---|
1441 | /// Adds(or replaces) the nick of charid to nick_db and fullfils pending requests. |
---|
1442 | /// Does nothing if the character is online. |
---|
1443 | void map_addnickdb(int charid, const char* nick) |
---|
1444 | { |
---|
1445 | struct charid2nick* p; |
---|
1446 | struct charid_request* req; |
---|
1447 | struct map_session_data* sd; |
---|
1448 | |
---|
1449 | if( map_charid2sd(charid) ) |
---|
1450 | return;// already online |
---|
1451 | |
---|
1452 | p = (struct charid2nick*)idb_ensure(nick_db, charid, create_charid2nick); |
---|
1453 | safestrncpy(p->nick, nick, sizeof(p->nick)); |
---|
1454 | |
---|
1455 | while( p->requests ) |
---|
1456 | { |
---|
1457 | req = p->requests; |
---|
1458 | p->requests = req->next; |
---|
1459 | sd = map_charid2sd(req->charid); |
---|
1460 | if( sd ) |
---|
1461 | clif_solved_charname(sd->fd, charid, p->nick); |
---|
1462 | aFree(req); |
---|
1463 | } |
---|
1464 | } |
---|
1465 | |
---|
1466 | /// Removes the nick of charid from nick_db. |
---|
1467 | /// Sends name to all pending requests on charid. |
---|
1468 | void map_delnickdb(int charid, const char* name) |
---|
1469 | { |
---|
1470 | struct charid2nick* p; |
---|
1471 | struct charid_request* req; |
---|
1472 | struct map_session_data* sd; |
---|
1473 | |
---|
1474 | p = (struct charid2nick*)idb_remove(nick_db, charid); |
---|
1475 | if( p == NULL ) |
---|
1476 | return; |
---|
1477 | |
---|
1478 | while( p->requests ) |
---|
1479 | { |
---|
1480 | req = p->requests; |
---|
1481 | p->requests = req->next; |
---|
1482 | sd = map_charid2sd(req->charid); |
---|
1483 | if( sd ) |
---|
1484 | clif_solved_charname(sd->fd, charid, name); |
---|
1485 | aFree(req); |
---|
1486 | } |
---|
1487 | aFree(p); |
---|
1488 | } |
---|
1489 | |
---|
1490 | /// Notifies sd of the nick of charid. |
---|
1491 | /// Uses the name in the character if online. |
---|
1492 | /// Uses the name in nick_db if offline. |
---|
1493 | void map_reqnickdb(struct map_session_data * sd, int charid) |
---|
1494 | { |
---|
1495 | struct charid2nick* p; |
---|
1496 | struct charid_request* req; |
---|
1497 | struct map_session_data* tsd; |
---|
1498 | |
---|
1499 | nullpo_retv(sd); |
---|
1500 | |
---|
1501 | tsd = map_charid2sd(charid); |
---|
1502 | if( tsd ) |
---|
1503 | { |
---|
1504 | clif_solved_charname(sd->fd, charid, tsd->status.name); |
---|
1505 | return; |
---|
1506 | } |
---|
1507 | |
---|
1508 | p = (struct charid2nick*)idb_ensure(nick_db, charid, create_charid2nick); |
---|
1509 | if( *p->nick ) |
---|
1510 | { |
---|
1511 | clif_solved_charname(sd->fd, charid, p->nick); |
---|
1512 | return; |
---|
1513 | } |
---|
1514 | // not in cache, request it |
---|
1515 | CREATE(req, struct charid_request, 1); |
---|
1516 | req->next = p->requests; |
---|
1517 | p->requests = req; |
---|
1518 | chrif_searchcharid(charid); |
---|
1519 | } |
---|
1520 | |
---|
1521 | /*========================================== |
---|
1522 | * id_dbÖblðÇÁ |
---|
1523 | *------------------------------------------*/ |
---|
1524 | void map_addiddb(struct block_list *bl) |
---|
1525 | { |
---|
1526 | nullpo_retv(bl); |
---|
1527 | |
---|
1528 | if (bl->type == BL_PC) |
---|
1529 | { |
---|
1530 | TBL_PC* sd = (TBL_PC*)bl; |
---|
1531 | idb_put(pc_db,sd->bl.id,sd); |
---|
1532 | idb_put(charid_db,sd->status.char_id,sd); |
---|
1533 | } else if (bl->type == BL_MOB) |
---|
1534 | idb_put(mobid_db,bl->id,bl); |
---|
1535 | |
---|
1536 | idb_put(id_db,bl->id,bl); |
---|
1537 | } |
---|
1538 | |
---|
1539 | /*========================================== |
---|
1540 | * id_db©çblðí |
---|
1541 | *------------------------------------------*/ |
---|
1542 | void map_deliddb(struct block_list *bl) |
---|
1543 | { |
---|
1544 | nullpo_retv(bl); |
---|
1545 | |
---|
1546 | if (bl->type == BL_PC) |
---|
1547 | { |
---|
1548 | TBL_PC* sd = (TBL_PC*)bl; |
---|
1549 | idb_remove(pc_db,sd->bl.id); |
---|
1550 | idb_remove(charid_db,sd->status.char_id); |
---|
1551 | } else if (bl->type == BL_MOB) |
---|
1552 | idb_remove(mobid_db,bl->id); |
---|
1553 | idb_remove(id_db,bl->id); |
---|
1554 | } |
---|
1555 | |
---|
1556 | /*========================================== |
---|
1557 | * Standard call when a player connection is closed. |
---|
1558 | *------------------------------------------*/ |
---|
1559 | int map_quit(struct map_session_data *sd) |
---|
1560 | { |
---|
1561 | if(!sd->state.active) { //Removing a player that is not active. |
---|
1562 | struct auth_node *node = chrif_search(sd->status.account_id); |
---|
1563 | if (node && node->char_id == sd->status.char_id && |
---|
1564 | node->state != ST_LOGOUT) |
---|
1565 | //Except when logging out, clear the auth-connect data immediately. |
---|
1566 | chrif_auth_delete(node->account_id, node->char_id, node->state); |
---|
1567 | //Non-active players should not have loaded any data yet (or it was cleared already) so no additional cleanups are needed. |
---|
1568 | return 0; |
---|
1569 | } |
---|
1570 | |
---|
1571 | if (sd->npc_timer_id != -1) //Cancel the event timer. |
---|
1572 | npc_timerevent_quit(sd); |
---|
1573 | |
---|
1574 | npc_script_event(sd, NPCE_LOGOUT); |
---|
1575 | |
---|
1576 | //Unit_free handles clearing the player related data, |
---|
1577 | //map_quit handles extra specific data which is related to quitting normally |
---|
1578 | //(changing map-servers invokes unit_free but bypasses map_quit) |
---|
1579 | if(sd->sc.count) { |
---|
1580 | //Status that are not saved... |
---|
1581 | if(sd->sc.data[SC_AUTOTRADE]) |
---|
1582 | status_change_end(&sd->bl,SC_AUTOTRADE,-1); |
---|
1583 | if(sd->sc.data[SC_SPURT]) |
---|
1584 | status_change_end(&sd->bl,SC_SPURT,-1); |
---|
1585 | if(sd->sc.data[SC_BERSERK]) |
---|
1586 | status_change_end(&sd->bl,SC_BERSERK,-1); |
---|
1587 | if(sd->sc.data[SC_TRICKDEAD]) |
---|
1588 | status_change_end(&sd->bl,SC_TRICKDEAD,-1); |
---|
1589 | if(sd->sc.data[SC_GUILDAURA]) |
---|
1590 | status_change_end(&sd->bl,SC_GUILDAURA,-1); |
---|
1591 | if(sd->sc.data[SC_ENDURE] && sd->sc.data[SC_ENDURE]->val4) |
---|
1592 | status_change_end(&sd->bl,SC_ENDURE,-1); //No need to save infinite endure. |
---|
1593 | if (battle_config.debuff_on_logout&1) { |
---|
1594 | if(sd->sc.data[SC_ORCISH]) |
---|
1595 | status_change_end(&sd->bl,SC_ORCISH,-1); |
---|
1596 | if(sd->sc.data[SC_STRIPWEAPON]) |
---|
1597 | status_change_end(&sd->bl,SC_STRIPWEAPON,-1); |
---|
1598 | if(sd->sc.data[SC_STRIPARMOR]) |
---|
1599 | status_change_end(&sd->bl,SC_STRIPARMOR,-1); |
---|
1600 | if(sd->sc.data[SC_STRIPSHIELD]) |
---|
1601 | status_change_end(&sd->bl,SC_STRIPSHIELD,-1); |
---|
1602 | if(sd->sc.data[SC_STRIPHELM]) |
---|
1603 | status_change_end(&sd->bl,SC_STRIPHELM,-1); |
---|
1604 | if(sd->sc.data[SC_EXTREMITYFIST]) |
---|
1605 | status_change_end(&sd->bl,SC_EXTREMITYFIST,-1); |
---|
1606 | if(sd->sc.data[SC_EXPLOSIONSPIRITS]) |
---|
1607 | status_change_end(&sd->bl,SC_EXPLOSIONSPIRITS,-1); |
---|
1608 | if(sd->sc.data[SC_REGENERATION] && sd->sc.data[SC_REGENERATION]->val4) |
---|
1609 | status_change_end(&sd->bl,SC_REGENERATION,-1); |
---|
1610 | //TO-DO Probably there are way more NPC_type negative status that are removed |
---|
1611 | if(sd->sc.data[SC_CHANGEUNDEAD]) |
---|
1612 | status_change_end(&sd->bl,SC_CHANGEUNDEAD,-1); |
---|
1613 | } |
---|
1614 | if (battle_config.debuff_on_logout&2) |
---|
1615 | { |
---|
1616 | if(sd->sc.data[SC_MAXIMIZEPOWER]) |
---|
1617 | status_change_end(&sd->bl,SC_MAXIMIZEPOWER,-1); |
---|
1618 | if(sd->sc.data[SC_MAXOVERTHRUST]) |
---|
1619 | status_change_end(&sd->bl,SC_MAXOVERTHRUST,-1); |
---|
1620 | if(sd->sc.data[SC_STEELBODY]) |
---|
1621 | status_change_end(&sd->bl,SC_STEELBODY,-1); |
---|
1622 | } |
---|
1623 | } |
---|
1624 | |
---|
1625 | // Return loot to owner |
---|
1626 | if( sd->pd ) pet_lootitem_drop(sd->pd, sd); |
---|
1627 | |
---|
1628 | unit_remove_map_pc(sd,3); |
---|
1629 | pc_makesavestatus(sd); |
---|
1630 | pc_clean_skilltree(sd); |
---|
1631 | chrif_save(sd,1); |
---|
1632 | unit_free_pc(sd); |
---|
1633 | return 0; |
---|
1634 | } |
---|
1635 | |
---|
1636 | /*========================================== |
---|
1637 | * idÔ?ÌPCðT·BȯêÎNULL |
---|
1638 | *------------------------------------------*/ |
---|
1639 | struct map_session_data * map_id2sd(int id) |
---|
1640 | { |
---|
1641 | if (id <= 0) return NULL; |
---|
1642 | return (struct map_session_data*)idb_get(pc_db,id); |
---|
1643 | } |
---|
1644 | |
---|
1645 | struct mob_data * map_id2md(int id) |
---|
1646 | { |
---|
1647 | if (id <= 0) return NULL; |
---|
1648 | return (struct mob_data*)idb_get(mobid_db,id); |
---|
1649 | } |
---|
1650 | |
---|
1651 | struct npc_data * map_id2nd(int id) |
---|
1652 | {// just a id2bl lookup because there's no npc_db |
---|
1653 | if (id <= 0) return NULL; |
---|
1654 | return (struct npc_data*)map_id2bl(id); |
---|
1655 | } |
---|
1656 | |
---|
1657 | /// Returns the nick of the target charid or NULL if unknown (requests the nick to the char server). |
---|
1658 | const char* map_charid2nick(int charid) |
---|
1659 | { |
---|
1660 | struct charid2nick *p; |
---|
1661 | struct map_session_data* sd; |
---|
1662 | |
---|
1663 | sd = map_charid2sd(charid); |
---|
1664 | if( sd ) |
---|
1665 | return sd->status.name;// character is online, return it's name |
---|
1666 | |
---|
1667 | p = (struct charid2nick*)idb_ensure(nick_db, charid, create_charid2nick); |
---|
1668 | if( *p->nick ) |
---|
1669 | return p->nick;// name in nick_db |
---|
1670 | |
---|
1671 | chrif_searchcharid(charid);// request the name |
---|
1672 | return NULL; |
---|
1673 | } |
---|
1674 | |
---|
1675 | /// Returns the struct map_session_data of the charid or NULL if the char is not online. |
---|
1676 | struct map_session_data* map_charid2sd(int charid) |
---|
1677 | { |
---|
1678 | return (struct map_session_data*)idb_get(charid_db, charid); |
---|
1679 | } |
---|
1680 | |
---|
1681 | /*========================================== |
---|
1682 | * Search session data from a nick name |
---|
1683 | * (without sensitive case if necessary) |
---|
1684 | * return map_session_data pointer or NULL |
---|
1685 | *------------------------------------------*/ |
---|
1686 | struct map_session_data * map_nick2sd(const char *nick) |
---|
1687 | { |
---|
1688 | struct map_session_data* sd; |
---|
1689 | struct map_session_data* found_sd; |
---|
1690 | struct s_mapiterator* iter; |
---|
1691 | size_t nicklen; |
---|
1692 | int qty = 0; |
---|
1693 | |
---|
1694 | if( nick == NULL ) |
---|
1695 | return NULL; |
---|
1696 | |
---|
1697 | nicklen = strlen(nick); |
---|
1698 | iter = mapit_getallusers(); |
---|
1699 | |
---|
1700 | found_sd = NULL; |
---|
1701 | for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) ) |
---|
1702 | { |
---|
1703 | if( battle_config.partial_name_scan ) |
---|
1704 | {// partial name search |
---|
1705 | if( strnicmp(sd->status.name, nick, nicklen) == 0 ) |
---|
1706 | { |
---|
1707 | found_sd = sd; |
---|
1708 | |
---|
1709 | if( strcmp(sd->status.name, nick) == 0 ) |
---|
1710 | {// Perfect Match |
---|
1711 | qty = 1; |
---|
1712 | break; |
---|
1713 | } |
---|
1714 | |
---|
1715 | qty++; |
---|
1716 | } |
---|
1717 | } |
---|
1718 | else if( strcasecmp(sd->status.name, nick) == 0 ) |
---|
1719 | {// exact search only |
---|
1720 | found_sd = sd; |
---|
1721 | break; |
---|
1722 | } |
---|
1723 | } |
---|
1724 | mapit_free(iter); |
---|
1725 | |
---|
1726 | if( battle_config.partial_name_scan && qty != 1 ) |
---|
1727 | found_sd = NULL; |
---|
1728 | |
---|
1729 | return found_sd; |
---|
1730 | } |
---|
1731 | |
---|
1732 | /*========================================== |
---|
1733 | * idÔ?ÌšðT· |
---|
1734 | * êObjectÌêÍzñðøÌÝ |
---|
1735 | *------------------------------------------*/ |
---|
1736 | struct block_list * map_id2bl(int id) |
---|
1737 | { |
---|
1738 | struct block_list *bl; |
---|
1739 | if(id >= 0 && id < ARRAYLENGTH(objects)) |
---|
1740 | bl = objects[id]; |
---|
1741 | else |
---|
1742 | bl = (struct block_list*)idb_get(id_db,id); |
---|
1743 | |
---|
1744 | return bl; |
---|
1745 | } |
---|
1746 | |
---|
1747 | /// Applies func to all the players in the db. |
---|
1748 | /// Stops iterating if func returns -1. |
---|
1749 | void map_foreachpc(int (*func)(struct map_session_data* sd, va_list args), ...) |
---|
1750 | { |
---|
1751 | DBIterator* iter; |
---|
1752 | struct map_session_data* sd; |
---|
1753 | |
---|
1754 | iter = db_iterator(pc_db); |
---|
1755 | for( sd = (struct map_session_data*)iter->first(iter,NULL); iter->exists(iter); sd = (struct map_session_data*)iter->next(iter,NULL) ) |
---|
1756 | { |
---|
1757 | va_list args; |
---|
1758 | int ret; |
---|
1759 | |
---|
1760 | va_start(args, func); |
---|
1761 | ret = func(sd, args); |
---|
1762 | va_end(args); |
---|
1763 | if( ret == -1 ) |
---|
1764 | break;// stop iterating |
---|
1765 | } |
---|
1766 | dbi_destroy(iter); |
---|
1767 | } |
---|
1768 | |
---|
1769 | /// Applies func to all the mobs in the db. |
---|
1770 | /// Stops iterating if func returns -1. |
---|
1771 | void map_foreachmob(int (*func)(struct mob_data* md, va_list args), ...) |
---|
1772 | { |
---|
1773 | DBIterator* iter; |
---|
1774 | struct mob_data* md; |
---|
1775 | |
---|
1776 | iter = db_iterator(mobid_db); |
---|
1777 | for( md = (struct mob_data*)dbi_first(iter); dbi_exists(iter); md = (struct mob_data*)dbi_next(iter) ) |
---|
1778 | { |
---|
1779 | va_list args; |
---|
1780 | int ret; |
---|
1781 | |
---|
1782 | va_start(args, func); |
---|
1783 | ret = func(md, args); |
---|
1784 | va_end(args); |
---|
1785 | if( ret == -1 ) |
---|
1786 | break;// stop iterating |
---|
1787 | } |
---|
1788 | dbi_destroy(iter); |
---|
1789 | } |
---|
1790 | |
---|
1791 | /// Applies func to all the npcs in the db. |
---|
1792 | /// Stops iterating if func returns -1. |
---|
1793 | void map_foreachnpc(int (*func)(struct npc_data* nd, va_list args), ...) |
---|
1794 | { |
---|
1795 | DBIterator* iter; |
---|
1796 | struct block_list* bl; |
---|
1797 | |
---|
1798 | iter = db_iterator(id_db); |
---|
1799 | for( bl = (struct block_list*)dbi_first(iter); dbi_exists(iter); bl = (struct block_list*)dbi_next(iter) ) |
---|
1800 | { |
---|
1801 | if( bl->type == BL_NPC ) |
---|
1802 | { |
---|
1803 | struct npc_data* nd = (struct npc_data*)bl; |
---|
1804 | va_list args; |
---|
1805 | int ret; |
---|
1806 | |
---|
1807 | va_start(args, func); |
---|
1808 | ret = func(nd, args); |
---|
1809 | va_end(args); |
---|
1810 | if( ret == -1 ) |
---|
1811 | break;// stop iterating |
---|
1812 | } |
---|
1813 | } |
---|
1814 | dbi_destroy(iter); |
---|
1815 | } |
---|
1816 | |
---|
1817 | /// Applies func to everything in the db. |
---|
1818 | /// Stops iterating if func returns -1. |
---|
1819 | void map_foreachiddb(int (*func)(struct block_list* bl, va_list args), ...) |
---|
1820 | { |
---|
1821 | DBIterator* iter; |
---|
1822 | struct block_list* bl; |
---|
1823 | |
---|
1824 | iter = db_iterator(id_db); |
---|
1825 | for( bl = (struct block_list*)dbi_first(iter); dbi_exists(iter); bl = (struct block_list*)dbi_next(iter) ) |
---|
1826 | { |
---|
1827 | va_list args; |
---|
1828 | int ret; |
---|
1829 | |
---|
1830 | va_start(args, func); |
---|
1831 | ret = func(bl, args); |
---|
1832 | va_end(args); |
---|
1833 | if( ret == -1 ) |
---|
1834 | break;// stop iterating |
---|
1835 | } |
---|
1836 | dbi_destroy(iter); |
---|
1837 | } |
---|
1838 | |
---|
1839 | /// Iterator. |
---|
1840 | /// Can filter by bl type. |
---|
1841 | struct s_mapiterator |
---|
1842 | { |
---|
1843 | enum e_mapitflags flags;// flags for special behaviour |
---|
1844 | enum bl_type types;// what bl types to return |
---|
1845 | DBIterator* dbi;// database iterator |
---|
1846 | }; |
---|
1847 | |
---|
1848 | /// Returns true if the block_list matches the description in the iterator. |
---|
1849 | /// |
---|
1850 | /// @param _mapit_ Iterator |
---|
1851 | /// @param _bl_ block_list |
---|
1852 | /// @return true if it matches |
---|
1853 | #define MAPIT_MATCHES(_mapit_,_bl_) \ |
---|
1854 | ( \ |
---|
1855 | ( (_bl_)->type & (_mapit_)->types /* type matches */ ) \ |
---|
1856 | ) |
---|
1857 | |
---|
1858 | /// Allocates a new iterator. |
---|
1859 | /// Returns the new iterator. |
---|
1860 | /// types can represent several BL's as a bit field. |
---|
1861 | /// TODO should this be expanded to allow filtering of map/guild/party/chat/cell/area/...? |
---|
1862 | /// |
---|
1863 | /// @param flags Flags of the iterator |
---|
1864 | /// @param type Target types |
---|
1865 | /// @return Iterator |
---|
1866 | struct s_mapiterator* mapit_alloc(enum e_mapitflags flags, enum bl_type types) |
---|
1867 | { |
---|
1868 | struct s_mapiterator* mapit; |
---|
1869 | |
---|
1870 | CREATE(mapit, struct s_mapiterator, 1); |
---|
1871 | mapit->flags = flags; |
---|
1872 | mapit->types = types; |
---|
1873 | if( types == BL_PC ) mapit->dbi = db_iterator(pc_db); |
---|
1874 | else if( types == BL_MOB ) mapit->dbi = db_iterator(mobid_db); |
---|
1875 | else mapit->dbi = db_iterator(id_db); |
---|
1876 | return mapit; |
---|
1877 | } |
---|
1878 | |
---|
1879 | /// Frees the iterator. |
---|
1880 | /// |
---|
1881 | /// @param mapit Iterator |
---|
1882 | void mapit_free(struct s_mapiterator* mapit) |
---|
1883 | { |
---|
1884 | nullpo_retv(mapit); |
---|
1885 | |
---|
1886 | dbi_destroy(mapit->dbi); |
---|
1887 | aFree(mapit); |
---|
1888 | } |
---|
1889 | |
---|
1890 | /// Returns the first block_list that matches the description. |
---|
1891 | /// Returns NULL if not found. |
---|
1892 | /// |
---|
1893 | /// @param mapit Iterator |
---|
1894 | /// @return first block_list or NULL |
---|
1895 | struct block_list* mapit_first(struct s_mapiterator* mapit) |
---|
1896 | { |
---|
1897 | struct block_list* bl; |
---|
1898 | |
---|
1899 | nullpo_retr(NULL,mapit); |
---|
1900 | |
---|
1901 | for( bl = (struct block_list*)dbi_first(mapit->dbi); bl != NULL; bl = (struct block_list*)dbi_next(mapit->dbi) ) |
---|
1902 | { |
---|
1903 | if( MAPIT_MATCHES(mapit,bl) ) |
---|
1904 | break;// found match |
---|
1905 | } |
---|
1906 | return bl; |
---|
1907 | } |
---|
1908 | |
---|
1909 | /// Returns the last block_list that matches the description. |
---|
1910 | /// Returns NULL if not found. |
---|
1911 | /// |
---|
1912 | /// @param mapit Iterator |
---|
1913 | /// @return last block_list or NULL |
---|
1914 | struct block_list* mapit_last(struct s_mapiterator* mapit) |
---|
1915 | { |
---|
1916 | struct block_list* bl; |
---|
1917 | |
---|
1918 | nullpo_retr(NULL,mapit); |
---|
1919 | |
---|
1920 | for( bl = (struct block_list*)dbi_last(mapit->dbi); bl != NULL; bl = (struct block_list*)dbi_prev(mapit->dbi) ) |
---|
1921 | { |
---|
1922 | if( MAPIT_MATCHES(mapit,bl) ) |
---|
1923 | break;// found match |
---|
1924 | } |
---|
1925 | return bl; |
---|
1926 | } |
---|
1927 | |
---|
1928 | /// Returns the next block_list that matches the description. |
---|
1929 | /// Returns NULL if not found. |
---|
1930 | /// |
---|
1931 | /// @param mapit Iterator |
---|
1932 | /// @return next block_list or NULL |
---|
1933 | struct block_list* mapit_next(struct s_mapiterator* mapit) |
---|
1934 | { |
---|
1935 | struct block_list* bl; |
---|
1936 | |
---|
1937 | nullpo_retr(NULL,mapit); |
---|
1938 | |
---|
1939 | for( ; ; ) |
---|
1940 | { |
---|
1941 | bl = (struct block_list*)dbi_next(mapit->dbi); |
---|
1942 | if( bl == NULL ) |
---|
1943 | break;// end |
---|
1944 | if( MAPIT_MATCHES(mapit,bl) ) |
---|
1945 | break;// found a match |
---|
1946 | // try next |
---|
1947 | } |
---|
1948 | return bl; |
---|
1949 | } |
---|
1950 | |
---|
1951 | /// Returns the previous block_list that matches the description. |
---|
1952 | /// Returns NULL if not found. |
---|
1953 | /// |
---|
1954 | /// @param mapit Iterator |
---|
1955 | /// @return previous block_list or NULL |
---|
1956 | struct block_list* mapit_prev(struct s_mapiterator* mapit) |
---|
1957 | { |
---|
1958 | struct block_list* bl; |
---|
1959 | |
---|
1960 | nullpo_retr(NULL,mapit); |
---|
1961 | |
---|
1962 | for( ; ; ) |
---|
1963 | { |
---|
1964 | bl = (struct block_list*)dbi_prev(mapit->dbi); |
---|
1965 | if( bl == NULL ) |
---|
1966 | break;// end |
---|
1967 | if( MAPIT_MATCHES(mapit,bl) ) |
---|
1968 | break;// found a match |
---|
1969 | // try prev |
---|
1970 | } |
---|
1971 | return bl; |
---|
1972 | } |
---|
1973 | |
---|
1974 | /// Returns true if the current block_list exists in the database. |
---|
1975 | /// |
---|
1976 | /// @param mapit Iterator |
---|
1977 | /// @return true if it exists |
---|
1978 | bool mapit_exists(struct s_mapiterator* mapit) |
---|
1979 | { |
---|
1980 | nullpo_retr(false,mapit); |
---|
1981 | |
---|
1982 | return dbi_exists(mapit->dbi); |
---|
1983 | } |
---|
1984 | |
---|
1985 | /*========================================== |
---|
1986 | * map.npcÖÇÁ (warpÌÌæ¿ÌÝ) |
---|
1987 | *------------------------------------------*/ |
---|
1988 | bool map_addnpc(int m,struct npc_data *nd) |
---|
1989 | { |
---|
1990 | nullpo_retr(0, nd); |
---|
1991 | |
---|
1992 | if( m < 0 || m >= map_num ) |
---|
1993 | return false; |
---|
1994 | |
---|
1995 | if( map[m].npc_num == MAX_NPC_PER_MAP ) |
---|
1996 | { |
---|
1997 | ShowWarning("too many NPCs in one map %s\n",map[m].name); |
---|
1998 | return false; |
---|
1999 | } |
---|
2000 | |
---|
2001 | map[m].npc[map[m].npc_num]=nd; |
---|
2002 | map[m].npc_num++; |
---|
2003 | idb_put(id_db,nd->bl.id,nd); |
---|
2004 | return true; |
---|
2005 | } |
---|
2006 | |
---|
2007 | /*========================================= |
---|
2008 | * Dynamic Mobs [Wizputer] |
---|
2009 | *-----------------------------------------*/ |
---|
2010 | // Stores the spawn data entry in the mob list. |
---|
2011 | // Returns the index of successful, or -1 if the list was full. |
---|
2012 | int map_addmobtolist(unsigned short m, struct spawn_data *spawn) |
---|
2013 | { |
---|
2014 | size_t i; |
---|
2015 | ARR_FIND( 0, MAX_MOB_LIST_PER_MAP, i, map[m].moblist[i] == NULL ); |
---|
2016 | if( i < MAX_MOB_LIST_PER_MAP ) |
---|
2017 | { |
---|
2018 | map[m].moblist[i] = spawn; |
---|
2019 | return i; |
---|
2020 | } |
---|
2021 | return -1; |
---|
2022 | } |
---|
2023 | |
---|
2024 | void map_spawnmobs(int m) |
---|
2025 | { |
---|
2026 | int i, k=0; |
---|
2027 | if (map[m].mob_delete_timer != -1) |
---|
2028 | { //Mobs have not been removed yet [Skotlex] |
---|
2029 | delete_timer(map[m].mob_delete_timer, map_removemobs_timer); |
---|
2030 | map[m].mob_delete_timer = -1; |
---|
2031 | return; |
---|
2032 | } |
---|
2033 | for(i=0; i<MAX_MOB_LIST_PER_MAP; i++) |
---|
2034 | if(map[m].moblist[i]!=NULL) |
---|
2035 | { |
---|
2036 | k+=map[m].moblist[i]->num; |
---|
2037 | npc_parse_mob2(map[m].moblist[i]); |
---|
2038 | } |
---|
2039 | |
---|
2040 | if (battle_config.etc_log && k > 0) |
---|
2041 | { |
---|
2042 | ShowStatus("Map %s: Spawned '"CL_WHITE"%d"CL_RESET"' mobs.\n",map[m].name, k); |
---|
2043 | } |
---|
2044 | } |
---|
2045 | |
---|
2046 | int map_removemobs_sub(struct block_list *bl, va_list ap) |
---|
2047 | { |
---|
2048 | struct mob_data *md = (struct mob_data *)bl; |
---|
2049 | nullpo_retr(0, md); |
---|
2050 | |
---|
2051 | //When not to remove: |
---|
2052 | //Mob doesn't respawn and is not a slave |
---|
2053 | if( !md->spawn && !md->master_id ) |
---|
2054 | return 0; |
---|
2055 | //Mob respawn data is not in cache |
---|
2056 | if( md->spawn && !md->spawn->state.dynamic ) |
---|
2057 | return 0; |
---|
2058 | //Mob is damaged and mob_remove_damaged is off |
---|
2059 | if( !battle_config.mob_remove_damaged && md->status.hp < md->status.max_hp ) |
---|
2060 | return 0; |
---|
2061 | |
---|
2062 | unit_free(&md->bl,0); |
---|
2063 | |
---|
2064 | return 1; |
---|
2065 | } |
---|
2066 | |
---|
2067 | int map_removemobs_timer(int tid, unsigned int tick, int id, intptr data) |
---|
2068 | { |
---|
2069 | int count; |
---|
2070 | const int m = id; |
---|
2071 | |
---|
2072 | if (m < 0 || m >= MAX_MAP_PER_SERVER) |
---|
2073 | { //Incorrect map id! |
---|
2074 | ShowError("map_removemobs_timer error: timer %d points to invalid map %d\n",tid, m); |
---|
2075 | return 0; |
---|
2076 | } |
---|
2077 | if (map[m].mob_delete_timer != tid) |
---|
2078 | { //Incorrect timer call! |
---|
2079 | ShowError("map_removemobs_timer mismatch: %d != %d (map %s)\n",map[m].mob_delete_timer, tid, map[m].name); |
---|
2080 | return 0; |
---|
2081 | } |
---|
2082 | map[m].mob_delete_timer = -1; |
---|
2083 | if (map[m].users > 0) //Map not empty! |
---|
2084 | return 1; |
---|
2085 | |
---|
2086 | count = map_foreachinmap(map_removemobs_sub, m, BL_MOB); |
---|
2087 | |
---|
2088 | if (battle_config.etc_log && count > 0) |
---|
2089 | ShowStatus("Map %s: Removed '"CL_WHITE"%d"CL_RESET"' mobs.\n",map[m].name, count); |
---|
2090 | |
---|
2091 | return 1; |
---|
2092 | } |
---|
2093 | |
---|
2094 | void map_removemobs(int m) |
---|
2095 | { |
---|
2096 | if (map[m].mob_delete_timer != -1) |
---|
2097 | return; //Mobs are already scheduled for removal |
---|
2098 | |
---|
2099 | map[m].mob_delete_timer = add_timer(gettick()+battle_config.mob_remove_delay, map_removemobs_timer, m, 0); |
---|
2100 | } |
---|
2101 | |
---|
2102 | /*========================================== |
---|
2103 | * mapŒ©çmapÔ?Ö?· |
---|
2104 | *------------------------------------------*/ |
---|
2105 | int map_mapname2mapid(const char* name) |
---|
2106 | { |
---|
2107 | unsigned short map_index; |
---|
2108 | map_index = mapindex_name2id(name); |
---|
2109 | if (!map_index) |
---|
2110 | return -1; |
---|
2111 | return map_mapindex2mapid(map_index); |
---|
2112 | } |
---|
2113 | |
---|
2114 | /*========================================== |
---|
2115 | * Returns the map of the given mapindex. [Skotlex] |
---|
2116 | *------------------------------------------*/ |
---|
2117 | int map_mapindex2mapid(unsigned short mapindex) |
---|
2118 | { |
---|
2119 | struct map_data *md=NULL; |
---|
2120 | |
---|
2121 | if (!mapindex) |
---|
2122 | return -1; |
---|
2123 | |
---|
2124 | md = (struct map_data*)uidb_get(map_db,(unsigned int)mapindex); |
---|
2125 | if(md==NULL || md->cell==NULL) |
---|
2126 | return -1; |
---|
2127 | return md->m; |
---|
2128 | } |
---|
2129 | |
---|
2130 | /*========================================== |
---|
2131 | * ŒImapŒ©çip,port?· |
---|
2132 | *------------------------------------------*/ |
---|
2133 | int map_mapname2ipport(unsigned short name, uint32* ip, uint16* port) |
---|
2134 | { |
---|
2135 | struct map_data_other_server *mdos=NULL; |
---|
2136 | |
---|
2137 | mdos = (struct map_data_other_server*)uidb_get(map_db,(unsigned int)name); |
---|
2138 | if(mdos==NULL || mdos->cell) //If gat isn't null, this is a local map. |
---|
2139 | return -1; |
---|
2140 | *ip=mdos->ip; |
---|
2141 | *port=mdos->port; |
---|
2142 | return 0; |
---|
2143 | } |
---|
2144 | |
---|
2145 | /*========================================== |
---|
2146 | * Checks if both dirs point in the same direction. |
---|
2147 | *------------------------------------------*/ |
---|
2148 | int map_check_dir(int s_dir,int t_dir) |
---|
2149 | { |
---|
2150 | if(s_dir == t_dir) |
---|
2151 | return 0; |
---|
2152 | switch(s_dir) { |
---|
2153 | case 0: if(t_dir == 7 || t_dir == 1 || t_dir == 0) return 0; break; |
---|
2154 | case 1: if(t_dir == 0 || t_dir == 2 || t_dir == 1) return 0; break; |
---|
2155 | case 2: if(t_dir == 1 || t_dir == 3 || t_dir == 2) return 0; break; |
---|
2156 | case 3: if(t_dir == 2 || t_dir == 4 || t_dir == 3) return 0; break; |
---|
2157 | case 4: if(t_dir == 3 || t_dir == 5 || t_dir == 4) return 0; break; |
---|
2158 | case 5: if(t_dir == 4 || t_dir == 6 || t_dir == 5) return 0; break; |
---|
2159 | case 6: if(t_dir == 5 || t_dir == 7 || t_dir == 6) return 0; break; |
---|
2160 | case 7: if(t_dir == 6 || t_dir == 0 || t_dir == 7) return 0; break; |
---|
2161 | } |
---|
2162 | return 1; |
---|
2163 | } |
---|
2164 | |
---|
2165 | /*========================================== |
---|
2166 | * Returns the direction of the given cell, relative to 'src' |
---|
2167 | *------------------------------------------*/ |
---|
2168 | uint8 map_calc_dir(struct block_list* src, int x, int y) |
---|
2169 | { |
---|
2170 | unsigned char dir = 0; |
---|
2171 | int dx, dy; |
---|
2172 | |
---|
2173 | nullpo_retr(0, src); |
---|
2174 | |
---|
2175 | dx = x-src->x; |
---|
2176 | dy = y-src->y; |
---|
2177 | if( dx == 0 && dy == 0 ) |
---|
2178 | { // both are standing on the same spot |
---|
2179 | //dir = 6; // aegis-style, makes knockback default to the left |
---|
2180 | dir = unit_getdir(src); // athena-style, makes knockback default to behind 'src' |
---|
2181 | } |
---|
2182 | else if( dx >= 0 && dy >=0 ) |
---|
2183 | { // upper-right |
---|
2184 | if( dx*2 <= dy ) dir = 0; // up |
---|
2185 | else if( dx > dy*2 ) dir = 6; // right |
---|
2186 | else dir = 7; // up-right |
---|
2187 | } |
---|
2188 | else if( dx >= 0 && dy <= 0 ) |
---|
2189 | { // lower-right |
---|
2190 | if( dx*2 <= -dy ) dir = 4; // down |
---|
2191 | else if( dx > -dy*2 ) dir = 6; // right |
---|
2192 | else dir = 5; // down-right |
---|
2193 | } |
---|
2194 | else if( dx <= 0 && dy <= 0 ) |
---|
2195 | { // lower-left |
---|
2196 | if( dx*2 >= dy ) dir = 4; // down |
---|
2197 | else if( dx < dy*2 ) dir = 2; // left |
---|
2198 | else dir = 3; // down-left |
---|
2199 | } |
---|
2200 | else |
---|
2201 | { // upper-left |
---|
2202 | if( -dx*2 <= dy ) dir = 0; // up |
---|
2203 | else if( -dx > dy*2 ) dir = 2; // left |
---|
2204 | else dir = 1; // up-left |
---|
2205 | |
---|
2206 | } |
---|
2207 | return dir; |
---|
2208 | } |
---|
2209 | |
---|
2210 | /*========================================== |
---|
2211 | * Randomizes target cell x,y to a random walkable cell that |
---|
2212 | * has the same distance from object as given coordinates do. [Skotlex] |
---|
2213 | *------------------------------------------*/ |
---|
2214 | int map_random_dir(struct block_list *bl, short *x, short *y) |
---|
2215 | { |
---|
2216 | short xi = *x-bl->x; |
---|
2217 | short yi = *y-bl->y; |
---|
2218 | short i=0, j; |
---|
2219 | int dist2 = xi*xi + yi*yi; |
---|
2220 | short dist = (short)sqrt((float)dist2); |
---|
2221 | short segment; |
---|
2222 | |
---|
2223 | if (dist < 1) dist =1; |
---|
2224 | |
---|
2225 | do { |
---|
2226 | j = rand()%8; //Pick a random direction |
---|
2227 | segment = 1+(rand()%dist); //Pick a random interval from the whole vector in that direction |
---|
2228 | xi = bl->x + segment*dirx[j]; |
---|
2229 | segment = (short)sqrt((float)(dist2 - segment*segment)); //The complement of the previously picked segment |
---|
2230 | yi = bl->y + segment*diry[j]; |
---|
2231 | } while ( |
---|
2232 | (map_getcell(bl->m,xi,yi,CELL_CHKNOPASS) || !path_search(NULL,bl->m,bl->x,bl->y,xi,yi,1,CELL_CHKNOREACH)) |
---|
2233 | && (++i)<100 ); |
---|
2234 | |
---|
2235 | if (i < 100) { |
---|
2236 | *x = xi; |
---|
2237 | *y = yi; |
---|
2238 | return 1; |
---|
2239 | } |
---|
2240 | return 0; |
---|
2241 | } |
---|
2242 | |
---|
2243 | // gatn |
---|
2244 | static struct mapcell map_gat2cell(int gat) |
---|
2245 | { |
---|
2246 | struct mapcell cell; |
---|
2247 | memset(&cell, 0, sizeof(cell)); |
---|
2248 | |
---|
2249 | switch( gat ) |
---|
2250 | { |
---|
2251 | case 0: cell.walkable = 1; cell.shootable = 1; cell.water = 0; break; // walkable ground |
---|
2252 | case 1: cell.walkable = 0; cell.shootable = 0; cell.water = 0; break; // non-walkable ground |
---|
2253 | case 2: cell.walkable = 1; cell.shootable = 1; cell.water = 0; break; // ??? |
---|
2254 | case 3: cell.walkable = 1; cell.shootable = 1; cell.water = 1; break; // walkable water |
---|
2255 | case 4: cell.walkable = 1; cell.shootable = 1; cell.water = 0; break; // ??? |
---|
2256 | case 5: cell.walkable = 0; cell.shootable = 1; cell.water = 0; break; // gap (snipable) |
---|
2257 | case 6: cell.walkable = 1; cell.shootable = 1; cell.water = 0; break; // ??? |
---|
2258 | default: |
---|
2259 | ShowWarning("map_gat2cell: unrecognized gat type '%d'\n", gat); |
---|
2260 | break; |
---|
2261 | } |
---|
2262 | |
---|
2263 | return cell; |
---|
2264 | } |
---|
2265 | |
---|
2266 | static int map_cell2gat(struct mapcell cell) |
---|
2267 | { |
---|
2268 | if( cell.walkable == 1 && cell.shootable == 1 && cell.water == 0 ) return 0; |
---|
2269 | if( cell.walkable == 0 && cell.shootable == 0 && cell.water == 0 ) return 1; |
---|
2270 | if( cell.walkable == 1 && cell.shootable == 1 && cell.water == 1 ) return 3; |
---|
2271 | if( cell.walkable == 0 && cell.shootable == 1 && cell.water == 0 ) return 5; |
---|
2272 | |
---|
2273 | ShowWarning("map_cell2gat: cell has no matching gat type\n"); |
---|
2274 | return 1; // default to 'wall' |
---|
2275 | } |
---|
2276 | |
---|
2277 | /*========================================== |
---|
2278 | * (m,x,y)ÌóÔð²×é |
---|
2279 | *------------------------------------------*/ |
---|
2280 | int map_getcell(int m,int x,int y,cell_chk cellchk) |
---|
2281 | { |
---|
2282 | return (m < 0 || m >= MAX_MAP_PER_SERVER) ? 0 : map_getcellp(&map[m],x,y,cellchk); |
---|
2283 | } |
---|
2284 | |
---|
2285 | int map_getcellp(struct map_data* m,int x,int y,cell_chk cellchk) |
---|
2286 | { |
---|
2287 | struct mapcell cell; |
---|
2288 | |
---|
2289 | nullpo_ret(m); |
---|
2290 | |
---|
2291 | //NOTE: this intentionally overrides the last row and column |
---|
2292 | if(x<0 || x>=m->xs-1 || y<0 || y>=m->ys-1) |
---|
2293 | return( cellchk == CELL_CHKNOPASS ); |
---|
2294 | |
---|
2295 | cell = m->cell[x + y*m->xs]; |
---|
2296 | |
---|
2297 | switch(cellchk) |
---|
2298 | { |
---|
2299 | // gat type retrieval |
---|
2300 | case CELL_GETTYPE: |
---|
2301 | return map_cell2gat(cell); |
---|
2302 | |
---|
2303 | // base gat type checks |
---|
2304 | case CELL_CHKWALL: |
---|
2305 | return (!cell.walkable && !cell.shootable); |
---|
2306 | //return (map_cell2gat(cell) == 1); |
---|
2307 | case CELL_CHKWATER: |
---|
2308 | return (cell.water); |
---|
2309 | //return (map_cell2gat(cell) == 3); |
---|
2310 | case CELL_CHKCLIFF: |
---|
2311 | return (!cell.walkable && cell.shootable); |
---|
2312 | //return (map_cell2gat(cell) == 5); |
---|
2313 | |
---|
2314 | // base cell type checks |
---|
2315 | case CELL_CHKNPC: |
---|
2316 | return (cell.npc); |
---|
2317 | case CELL_CHKBASILICA: |
---|
2318 | return (cell.basilica); |
---|
2319 | case CELL_CHKLANDPROTECTOR: |
---|
2320 | return (cell.landprotector); |
---|
2321 | case CELL_CHKNOVENDING: |
---|
2322 | return (cell.novending); |
---|
2323 | case CELL_CHKNOCHAT: |
---|
2324 | return (cell.nochat); |
---|
2325 | |
---|
2326 | // special checks |
---|
2327 | case CELL_CHKPASS: |
---|
2328 | #ifdef CELL_NOSTACK |
---|
2329 | if (cell.cell_bl >= battle_config.cell_stack_limit) return 0; |
---|
2330 | #endif |
---|
2331 | case CELL_CHKREACH: |
---|
2332 | return (cell.walkable); |
---|
2333 | |
---|
2334 | case CELL_CHKNOPASS: |
---|
2335 | #ifdef CELL_NOSTACK |
---|
2336 | if (cell.cell_bl >= battle_config.cell_stack_limit) return 1; |
---|
2337 | #endif |
---|
2338 | case CELL_CHKNOREACH: |
---|
2339 | return (!cell.walkable); |
---|
2340 | |
---|
2341 | case CELL_CHKSTACK: |
---|
2342 | #ifdef CELL_NOSTACK |
---|
2343 | return (cell.cell_bl >= battle_config.cell_stack_limit); |
---|
2344 | #else |
---|
2345 | return 0; |
---|
2346 | #endif |
---|
2347 | |
---|
2348 | default: |
---|
2349 | return 0; |
---|
2350 | } |
---|
2351 | } |
---|
2352 | |
---|
2353 | /*========================================== |
---|
2354 | * Change the type/flags of a map cell |
---|
2355 | * 'cell' - which flag to modify |
---|
2356 | * 'flag' - true = on, false = off |
---|
2357 | *------------------------------------------*/ |
---|
2358 | void map_setcell(int m, int x, int y, cell_t cell, bool flag) |
---|
2359 | { |
---|
2360 | int j; |
---|
2361 | |
---|
2362 | if( m < 0 || m >= map_num || x < 0 || x >= map[m].xs || y < 0 || y >= map[m].ys ) |
---|
2363 | return; |
---|
2364 | |
---|
2365 | j = x + y*map[m].xs; |
---|
2366 | |
---|
2367 | switch( cell ) { |
---|
2368 | case CELL_WALKABLE: map[m].cell[j].walkable = flag; break; |
---|
2369 | case CELL_SHOOTABLE: map[m].cell[j].shootable = flag; break; |
---|
2370 | case CELL_WATER: map[m].cell[j].water = flag; break; |
---|
2371 | |
---|
2372 | case CELL_NPC: map[m].cell[j].npc = flag; break; |
---|
2373 | case CELL_BASILICA: map[m].cell[j].basilica = flag; break; |
---|
2374 | case CELL_LANDPROTECTOR: map[m].cell[j].landprotector = flag; break; |
---|
2375 | case CELL_NOVENDING: map[m].cell[j].novending = flag; break; |
---|
2376 | case CELL_NOCHAT: map[m].cell[j].nochat = flag; break; |
---|
2377 | default: |
---|
2378 | ShowWarning("map_setcell: invalid cell type '%d'\n", (int)cell); |
---|
2379 | break; |
---|
2380 | } |
---|
2381 | } |
---|
2382 | |
---|
2383 | void map_setgatcell(int m, int x, int y, int gat) |
---|
2384 | { |
---|
2385 | int j; |
---|
2386 | struct mapcell cell; |
---|
2387 | |
---|
2388 | if( m < 0 || m >= map_num || x < 0 || x >= map[m].xs || y < 0 || y >= map[m].ys ) |
---|
2389 | return; |
---|
2390 | |
---|
2391 | j = x + y*map[m].xs; |
---|
2392 | |
---|
2393 | cell = map_gat2cell(gat); |
---|
2394 | map[m].cell[j].walkable = cell.walkable; |
---|
2395 | map[m].cell[j].shootable = cell.shootable; |
---|
2396 | map[m].cell[j].water = cell.water; |
---|
2397 | } |
---|
2398 | |
---|
2399 | static void* create_map_data_other_server(DBKey key, va_list args) |
---|
2400 | { |
---|
2401 | struct map_data_other_server *mdos; |
---|
2402 | unsigned short mapindex = (unsigned short)key.ui; |
---|
2403 | mdos=(struct map_data_other_server *)aCalloc(1,sizeof(struct map_data_other_server)); |
---|
2404 | mdos->index = mapindex; |
---|
2405 | memcpy(mdos->name, mapindex_id2name(mapindex), MAP_NAME_LENGTH); |
---|
2406 | return mdos; |
---|
2407 | } |
---|
2408 | |
---|
2409 | /*========================================== |
---|
2410 | * ŒIÇÌ}bvðdbÉÇÁ |
---|
2411 | *------------------------------------------*/ |
---|
2412 | int map_setipport(unsigned short mapindex, uint32 ip, uint16 port) |
---|
2413 | { |
---|
2414 | struct map_data_other_server *mdos=NULL; |
---|
2415 | |
---|
2416 | mdos=(struct map_data_other_server *)uidb_ensure(map_db,(unsigned int)mapindex, create_map_data_other_server); |
---|
2417 | |
---|
2418 | if(mdos->cell) //Local map,Do nothing. Give priority to our own local maps over ones from another server. [Skotlex] |
---|
2419 | return 0; |
---|
2420 | if(ip == clif_getip() && port == clif_getport()) { |
---|
2421 | //That's odd, we received info that we are the ones with this map, but... we don't have it. |
---|
2422 | ShowFatalError("map_setipport : received info that this map-server SHOULD have map '%s', but it is not loaded.\n",mapindex_id2name(mapindex)); |
---|
2423 | exit(EXIT_FAILURE); |
---|
2424 | } |
---|
2425 | mdos->ip = ip; |
---|
2426 | mdos->port = port; |
---|
2427 | return 1; |
---|
2428 | } |
---|
2429 | |
---|
2430 | /*========================================== |
---|
2431 | * ŒIÇÌ}bvðSÄí |
---|
2432 | *------------------------------------------*/ |
---|
2433 | int map_eraseallipport_sub(DBKey key,void *data,va_list va) |
---|
2434 | { |
---|
2435 | struct map_data_other_server *mdos = (struct map_data_other_server*)data; |
---|
2436 | if(mdos->cell == NULL) { |
---|
2437 | db_remove(map_db,key); |
---|
2438 | aFree(mdos); |
---|
2439 | } |
---|
2440 | return 0; |
---|
2441 | } |
---|
2442 | |
---|
2443 | int map_eraseallipport(void) |
---|
2444 | { |
---|
2445 | map_db->foreach(map_db,map_eraseallipport_sub); |
---|
2446 | return 1; |
---|
2447 | } |
---|
2448 | |
---|
2449 | /*========================================== |
---|
2450 | * ŒIÇÌ}bvðdb©çí |
---|
2451 | *------------------------------------------*/ |
---|
2452 | int map_eraseipport(unsigned short mapindex, uint32 ip, uint16 port) |
---|
2453 | { |
---|
2454 | struct map_data_other_server *mdos; |
---|
2455 | |
---|
2456 | mdos = (struct map_data_other_server*)uidb_get(map_db,(unsigned int)mapindex); |
---|
2457 | if(!mdos || mdos->cell) //Map either does not exists or is a local map. |
---|
2458 | return 0; |
---|
2459 | |
---|
2460 | if(mdos->ip==ip && mdos->port == port) { |
---|
2461 | uidb_remove(map_db,(unsigned int)mapindex); |
---|
2462 | aFree(mdos); |
---|
2463 | return 1; |
---|
2464 | } |
---|
2465 | return 0; |
---|
2466 | } |
---|
2467 | |
---|
2468 | /*========================================== |
---|
2469 | * Map cache reading |
---|
2470 | *==========================================*/ |
---|
2471 | int map_readfromcache(struct map_data *m, FILE *fp) |
---|
2472 | { |
---|
2473 | struct map_cache_main_header header; |
---|
2474 | struct map_cache_map_info info; |
---|
2475 | int i; |
---|
2476 | |
---|
2477 | if( !fp ) |
---|
2478 | return 0; |
---|
2479 | |
---|
2480 | fseek(fp, 0, SEEK_SET); |
---|
2481 | fread(&header, sizeof(struct map_cache_main_header), 1, fp); |
---|
2482 | |
---|
2483 | for( i = 0; i < header.map_count; ++i ) |
---|
2484 | { |
---|
2485 | fread(&info, sizeof(struct map_cache_map_info), 1, fp); |
---|
2486 | |
---|
2487 | if( strcmp(m->name, info.name) == 0 ) |
---|
2488 | break; // Map found |
---|
2489 | |
---|
2490 | // Map not found, jump to the beginning of the next map info header |
---|
2491 | fseek(fp, info.len, SEEK_CUR); |
---|
2492 | } |
---|
2493 | |
---|
2494 | if( i < header.map_count ) |
---|
2495 | { |
---|
2496 | unsigned char *buf, *buf2; |
---|
2497 | unsigned long size, xy; |
---|
2498 | |
---|
2499 | m->xs = info.xs; |
---|
2500 | m->ys = info.ys; |
---|
2501 | size = info.xs*info.ys; |
---|
2502 | |
---|
2503 | buf = (unsigned char*)aMalloc(info.len); // temp buffer to read the zipped map |
---|
2504 | buf2 = (unsigned char*)aMalloc(size); // temp buffer to unpack the data |
---|
2505 | CREATE(m->cell, struct mapcell, size); |
---|
2506 | |
---|
2507 | fread(buf, info.len, 1, fp); |
---|
2508 | decode_zip(buf2, &size, buf, info.len); // Unzip the map from the buffer |
---|
2509 | |
---|
2510 | for( xy = 0; xy < size; ++xy ) |
---|
2511 | m->cell[xy] = map_gat2cell(buf2[xy]); |
---|
2512 | |
---|
2513 | aFree(buf); |
---|
2514 | aFree(buf2); |
---|
2515 | return 1; |
---|
2516 | } |
---|
2517 | |
---|
2518 | return 0; |
---|
2519 | } |
---|
2520 | |
---|
2521 | int map_addmap(char* mapname) |
---|
2522 | { |
---|
2523 | if (strcmpi(mapname,"clear")==0) { |
---|
2524 | map_num=0; |
---|
2525 | return 0; |
---|
2526 | } |
---|
2527 | |
---|
2528 | if (map_num >= MAX_MAP_PER_SERVER - 1) { |
---|
2529 | ShowError("Could not add map '"CL_WHITE"%s"CL_RESET"', the limit of maps has been reached.\n",mapname); |
---|
2530 | return 1; |
---|
2531 | } |
---|
2532 | |
---|
2533 | mapindex_getmapname(mapname, map[map_num].name); |
---|
2534 | map_num++; |
---|
2535 | return 0; |
---|
2536 | } |
---|
2537 | |
---|
2538 | static void map_delmapid(int id) |
---|
2539 | { |
---|
2540 | ShowNotice("Removing map [ %s ] from maplist"CL_CLL"\n",map[id].name); |
---|
2541 | memmove(map+id, map+id+1, sizeof(map[0])*(map_num-id-1)); |
---|
2542 | map_num--; |
---|
2543 | } |
---|
2544 | |
---|
2545 | int map_delmap(char* mapname) |
---|
2546 | { |
---|
2547 | int i; |
---|
2548 | char map_name[MAP_NAME_LENGTH]; |
---|
2549 | |
---|
2550 | if (strcmpi(mapname, "all") == 0) { |
---|
2551 | map_num = 0; |
---|
2552 | return 0; |
---|
2553 | } |
---|
2554 | |
---|
2555 | mapindex_getmapname(mapname, map_name); |
---|
2556 | for(i = 0; i < map_num; i++) { |
---|
2557 | if (strcmp(map[i].name, map_name) == 0) { |
---|
2558 | map_delmapid(i); |
---|
2559 | return 1; |
---|
2560 | } |
---|
2561 | } |
---|
2562 | return 0; |
---|
2563 | } |
---|
2564 | |
---|
2565 | #define NO_WATER 1000000 |
---|
2566 | |
---|
2567 | /* |
---|
2568 | * Reads from the .rsw for each map |
---|
2569 | * Returns water height (or NO_WATER if file doesn't exist) or other error is encountered. |
---|
2570 | * Assumed path for file is data/mapname.rsw |
---|
2571 | * Credits to LittleWolf |
---|
2572 | */ |
---|
2573 | int map_waterheight(char* mapname) |
---|
2574 | { |
---|
2575 | char fn[256]; |
---|
2576 | char *rsw, *found; |
---|
2577 | |
---|
2578 | //Look up for the rsw |
---|
2579 | sprintf(fn, "data\\%s.rsw", mapname); |
---|
2580 | |
---|
2581 | found = grfio_find_file(fn); |
---|
2582 | if (found) strcpy(fn, found); // replace with real name |
---|
2583 | |
---|
2584 | // read & convert fn |
---|
2585 | rsw = (char *) grfio_read (fn); |
---|
2586 | if (rsw) |
---|
2587 | { //Load water height from file |
---|
2588 | int wh = (int) *(float*)(rsw+166); |
---|
2589 | aFree(rsw); |
---|
2590 | return wh; |
---|
2591 | } |
---|
2592 | ShowWarning("Failed to find water level for (%s)\n", mapname, fn); |
---|
2593 | return NO_WATER; |
---|
2594 | } |
---|
2595 | |
---|
2596 | /*================================== |
---|
2597 | * .GAT format |
---|
2598 | *----------------------------------*/ |
---|
2599 | int map_readgat (struct map_data* m) |
---|
2600 | { |
---|
2601 | char filename[256]; |
---|
2602 | uint8* gat; |
---|
2603 | int water_height; |
---|
2604 | size_t xy, off, num_cells; |
---|
2605 | |
---|
2606 | sprintf(filename, "data\\%s.gat", m->name); |
---|
2607 | |
---|
2608 | gat = (uint8 *) grfio_read(filename); |
---|
2609 | if (gat == NULL) |
---|
2610 | return 0; |
---|
2611 | |
---|
2612 | m->xs = *(int32*)(gat+6); |
---|
2613 | m->ys = *(int32*)(gat+10); |
---|
2614 | num_cells = m->xs * m->ys; |
---|
2615 | CREATE(m->cell, struct mapcell, num_cells); |
---|
2616 | |
---|
2617 | water_height = map_waterheight(m->name); |
---|
2618 | |
---|
2619 | // Set cell properties |
---|
2620 | off = 14; |
---|
2621 | for( xy = 0; xy < num_cells; ++xy ) |
---|
2622 | { |
---|
2623 | // read cell data |
---|
2624 | float height = *(float*)( gat + off ); |
---|
2625 | uint32 type = *(uint32*)( gat + off + 16 ); |
---|
2626 | off += 20; |
---|
2627 | |
---|
2628 | if( type == 0 && water_height != NO_WATER && height > water_height ) |
---|
2629 | type = 3; // Cell is 0 (walkable) but under water level, set to 3 (walkable water) |
---|
2630 | |
---|
2631 | m->cell[xy] = map_gat2cell(type); |
---|
2632 | } |
---|
2633 | |
---|
2634 | aFree(gat); |
---|
2635 | |
---|
2636 | return 1; |
---|
2637 | } |
---|
2638 | |
---|
2639 | /*====================================== |
---|
2640 | * Initiate maps loading stage |
---|
2641 | *--------------------------------------*/ |
---|
2642 | int map_readallmaps (void) |
---|
2643 | { |
---|
2644 | int i; |
---|
2645 | FILE* fp=NULL; |
---|
2646 | int maps_removed = 0; |
---|
2647 | |
---|
2648 | if( enable_grf ) |
---|
2649 | ShowStatus("Loading maps (using GRF files)...\n"); |
---|
2650 | else |
---|
2651 | { |
---|
2652 | ShowStatus("Loading maps (using %s as map cache)...\n", map_cache_file); |
---|
2653 | if( (fp = fopen(map_cache_file, "rb")) == NULL ) |
---|
2654 | { |
---|
2655 | ShowFatalError("Unable to open map cache file "CL_WHITE"%s"CL_RESET"\n", map_cache_file); |
---|
2656 | exit(EXIT_FAILURE); //No use launching server if maps can't be read. |
---|
2657 | } |
---|
2658 | } |
---|
2659 | |
---|
2660 | for(i = 0; i < map_num; i++) |
---|
2661 | { |
---|
2662 | size_t size; |
---|
2663 | |
---|
2664 | // show progress |
---|
2665 | ShowStatus("Loading maps [%i/%i]: %s"CL_CLL"\r", i, map_num, map[i].name); |
---|
2666 | |
---|
2667 | // try to load the map |
---|
2668 | if( ! |
---|
2669 | (enable_grf? |
---|
2670 | map_readgat(&map[i]) |
---|
2671 | :map_readfromcache(&map[i], fp)) |
---|
2672 | ) { |
---|
2673 | map_delmapid(i); |
---|
2674 | maps_removed++; |
---|
2675 | i--; |
---|
2676 | continue; |
---|
2677 | } |
---|
2678 | |
---|
2679 | map[i].index = mapindex_name2id(map[i].name); |
---|
2680 | |
---|
2681 | if (uidb_get(map_db,(unsigned int)map[i].index) != NULL) |
---|
2682 | { |
---|
2683 | ShowWarning("Map %s already loaded!"CL_CLL"\n", map[i].name); |
---|
2684 | if (map[i].cell) { |
---|
2685 | aFree(map[i].cell); |
---|
2686 | map[i].cell = NULL; |
---|
2687 | } |
---|
2688 | map_delmapid(i); |
---|
2689 | maps_removed++; |
---|
2690 | i--; |
---|
2691 | continue; |
---|
2692 | } |
---|
2693 | |
---|
2694 | uidb_put(map_db, (unsigned int)map[i].index, &map[i]); |
---|
2695 | |
---|
2696 | map[i].m = i; |
---|
2697 | memset(map[i].moblist, 0, sizeof(map[i].moblist)); //Initialize moblist [Skotlex] |
---|
2698 | map[i].mob_delete_timer = -1; //Initialize timer [Skotlex] |
---|
2699 | if(battle_config.pk_mode) |
---|
2700 | map[i].flag.pvp = 1; // make all maps pvp for pk_mode [Valaris] |
---|
2701 | |
---|
2702 | map[i].bxs = (map[i].xs + BLOCK_SIZE - 1) / BLOCK_SIZE; |
---|
2703 | map[i].bys = (map[i].ys + BLOCK_SIZE - 1) / BLOCK_SIZE; |
---|
2704 | |
---|
2705 | // default experience multiplicators |
---|
2706 | map[i].jexp = 100; |
---|
2707 | map[i].bexp = 100; |
---|
2708 | |
---|
2709 | size = map[i].bxs * map[i].bys * sizeof(struct block_list*); |
---|
2710 | map[i].block = (struct block_list**)aCalloc(size, 1); |
---|
2711 | map[i].block_mob = (struct block_list**)aCalloc(size, 1); |
---|
2712 | } |
---|
2713 | |
---|
2714 | if( !enable_grf ) |
---|
2715 | fclose(fp); |
---|
2716 | |
---|
2717 | // finished map loading |
---|
2718 | ShowInfo("Successfully loaded '"CL_WHITE"%d"CL_RESET"' maps."CL_CLL"\n",map_num); |
---|
2719 | |
---|
2720 | if (maps_removed) |
---|
2721 | ShowNotice("Maps removed: '"CL_WHITE"%d"CL_RESET"'\n",maps_removed); |
---|
2722 | |
---|
2723 | return 0; |
---|
2724 | } |
---|
2725 | |
---|
2726 | //////////////////////////////////////////////////////////////////////// |
---|
2727 | static int map_ip_set = 0; |
---|
2728 | static int char_ip_set = 0; |
---|
2729 | |
---|
2730 | /*========================================== |
---|
2731 | * Console Command Parser [Wizputer] |
---|
2732 | *------------------------------------------*/ |
---|
2733 | int parse_console(char* buf) |
---|
2734 | { |
---|
2735 | char type[64]; |
---|
2736 | char command[64]; |
---|
2737 | char map[64]; |
---|
2738 | int x = 0; |
---|
2739 | int y = 0; |
---|
2740 | int m; |
---|
2741 | int n; |
---|
2742 | struct map_session_data sd; |
---|
2743 | |
---|
2744 | memset(&sd, 0, sizeof(struct map_session_data)); |
---|
2745 | strcpy(sd.status.name, "console"); |
---|
2746 | |
---|
2747 | if( (n=sscanf(buf, "%[^:]:%[^:]:%99s %d %d[^\n]",type,command,map,&x,&y)) < 5 ) |
---|
2748 | if( (n=sscanf(buf, "%[^:]:%[^\n]",type,command)) < 2 ) |
---|
2749 | n = sscanf(buf,"%[^\n]",type); |
---|
2750 | |
---|
2751 | if( n == 5 ) { |
---|
2752 | m = map_mapname2mapid(map); |
---|
2753 | if( m < 0 ){ |
---|
2754 | ShowWarning("Console: Unknown map\n"); |
---|
2755 | return 0; |
---|
2756 | } |
---|
2757 | sd.bl.m = m; |
---|
2758 | map_search_freecell(&sd.bl, m, &sd.bl.x, &sd.bl.y, -1, -1, 0); |
---|
2759 | if( x > 0 ) |
---|
2760 | sd.bl.x = x; |
---|
2761 | if( y > 0 ) |
---|
2762 | sd.bl.y = y; |
---|
2763 | } else { |
---|
2764 | map[0] = '\0'; |
---|
2765 | if( n < 2 ) command[0] = '\0'; |
---|
2766 | if( n < 1 ) type[0] = '\0'; |
---|
2767 | } |
---|
2768 | |
---|
2769 | ShowInfo("Type of command: '%s' || Command: '%s' || Map: '%s' Coords: %d %d\n", type, command, map, x, y); |
---|
2770 | |
---|
2771 | if( n == 5 && strcmpi("admin",type) == 0 ){ |
---|
2772 | if( !is_atcommand_sub(sd.fd,&sd,command,99) ) |
---|
2773 | ShowInfo("Console: not atcommand\n"); |
---|
2774 | } else if( n == 2 && strcmpi("server",type) == 0 ){ |
---|
2775 | if( strcmpi("shutdown",command) == 0 || |
---|
2776 | strcmpi("exit",command) == 0 || |
---|
2777 | strcmpi("quit",command) == 0 ){ |
---|
2778 | runflag = 0; |
---|
2779 | } |
---|
2780 | } else if( strcmpi("help",type) == 0 ){ |
---|
2781 | ShowNotice("To use GM commands:\n"); |
---|
2782 | ShowInfo("admin:<gm command>:<map of \"gm\"> <x> <y>\n"); |
---|
2783 | ShowInfo("You can use any GM command that doesn't require the GM.\n"); |
---|
2784 | ShowInfo("No using @item or @warp however you can use @charwarp\n"); |
---|
2785 | ShowInfo("The <map of \"gm\"> <x> <y> is for commands that need coords of the GM\n"); |
---|
2786 | ShowInfo("IE: @spawn\n"); |
---|
2787 | ShowInfo("To shutdown the server:\n"); |
---|
2788 | ShowInfo("server:shutdown\n"); |
---|
2789 | } |
---|
2790 | |
---|
2791 | return 0; |
---|
2792 | } |
---|
2793 | |
---|
2794 | /*========================================== |
---|
2795 | * Ýèt@Cð?Ý?Þ |
---|
2796 | *------------------------------------------*/ |
---|
2797 | int map_config_read(char *cfgName) |
---|
2798 | { |
---|
2799 | char line[1024], w1[1024], w2[1024], *ptr; |
---|
2800 | FILE *fp; |
---|
2801 | |
---|
2802 | fp = fopen(cfgName,"r"); |
---|
2803 | if (fp == NULL) { |
---|
2804 | ShowError("Map configuration file not found at: %s\n", cfgName); |
---|
2805 | return 1; |
---|
2806 | } |
---|
2807 | while(fgets(line, sizeof(line), fp)) |
---|
2808 | { |
---|
2809 | if (line[0] == '/' && line[1] == '/') |
---|
2810 | continue; |
---|
2811 | |
---|
2812 | if ((ptr = strstr(line, "//")) != NULL) |
---|
2813 | *ptr = '\n'; //Strip comments |
---|
2814 | |
---|
2815 | if (sscanf(line, "%[^:]: %[^\t\r\n]", w1, w2) == 2) { |
---|
2816 | //Strip trailing spaces |
---|
2817 | ptr = w2 + strlen(w2); |
---|
2818 | while (--ptr >= w2 && *ptr == ' '); |
---|
2819 | ptr++; |
---|
2820 | *ptr = '\0'; |
---|
2821 | |
---|
2822 | if(strcmpi(w1,"timestamp_format")==0){ |
---|
2823 | strncpy(timestamp_format, w2, 20); |
---|
2824 | } else if(strcmpi(w1,"stdout_with_ansisequence")==0){ |
---|
2825 | stdout_with_ansisequence = config_switch(w2); |
---|
2826 | } else if(strcmpi(w1,"console_silent")==0){ |
---|
2827 | ShowInfo("Console Silent Setting: %d\n", atoi(w2)); |
---|
2828 | msg_silent = atoi(w2); |
---|
2829 | } else if (strcmpi(w1, "userid")==0){ |
---|
2830 | chrif_setuserid(w2); |
---|
2831 | } else if (strcmpi(w1, "passwd") == 0) { |
---|
2832 | chrif_setpasswd(w2); |
---|
2833 | } else if (strcmpi(w1, "char_ip") == 0) { |
---|
2834 | char_ip_set = chrif_setip(w2); |
---|
2835 | } else if (strcmpi(w1, "char_port") == 0) { |
---|
2836 | chrif_setport(atoi(w2)); |
---|
2837 | } else if (strcmpi(w1, "map_ip") == 0) { |
---|
2838 | map_ip_set = clif_setip(w2); |
---|
2839 | } else if (strcmpi(w1, "bind_ip") == 0) { |
---|
2840 | clif_setbindip(w2); |
---|
2841 | } else if (strcmpi(w1, "map_port") == 0) { |
---|
2842 | clif_setport(atoi(w2)); |
---|
2843 | map_port = (atoi(w2)); |
---|
2844 | } else if (strcmpi(w1, "map") == 0) { |
---|
2845 | map_addmap(w2); |
---|
2846 | } else if (strcmpi(w1, "delmap") == 0) { |
---|
2847 | map_delmap(w2); |
---|
2848 | } else if (strcmpi(w1, "npc") == 0) { |
---|
2849 | npc_addsrcfile(w2); |
---|
2850 | } else if (strcmpi(w1, "delnpc") == 0) { |
---|
2851 | npc_delsrcfile(w2); |
---|
2852 | } else if (strcmpi(w1, "autosave_time") == 0) { |
---|
2853 | autosave_interval = atoi(w2); |
---|
2854 | if (autosave_interval < 1) //Revert to default saving. |
---|
2855 | autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; |
---|
2856 | else |
---|
2857 | autosave_interval *= 1000; //Pass from sec to ms |
---|
2858 | } else if (strcmpi(w1, "minsave_time") == 0) { |
---|
2859 | minsave_interval= atoi(w2); |
---|
2860 | if (minsave_interval < 1) |
---|
2861 | minsave_interval = 1; |
---|
2862 | } else if (strcmpi(w1, "save_settings") == 0) { |
---|
2863 | save_settings = atoi(w2); |
---|
2864 | } else if (strcmpi(w1, "motd_txt") == 0) { |
---|
2865 | strcpy(motd_txt, w2); |
---|
2866 | } else if (strcmpi(w1, "help_txt") == 0) { |
---|
2867 | strcpy(help_txt, w2); |
---|
2868 | } else if (strcmpi(w1, "help2_txt") == 0) { |
---|
2869 | strcpy(help2_txt, w2); |
---|
2870 | } else if (strcmpi(w1, "charhelp_txt") == 0) { |
---|
2871 | strcpy(charhelp_txt, w2); |
---|
2872 | } else if (strcmpi(w1, "mapreg_txt") == 0) { |
---|
2873 | strcpy(mapreg_txt, w2); |
---|
2874 | } else if(strcmpi(w1,"map_cache_file") == 0) { |
---|
2875 | strncpy(map_cache_file,w2,255); |
---|
2876 | } else if(strcmpi(w1,"db_path") == 0) { |
---|
2877 | strncpy(db_path,w2,255); |
---|
2878 | } else if (strcmpi(w1, "console") == 0) { |
---|
2879 | console = config_switch(w2); |
---|
2880 | if (console) |
---|
2881 | ShowNotice("Console Commands are enabled.\n"); |
---|
2882 | } else if (strcmpi(w1, "enable_spy") == 0) { |
---|
2883 | enable_spy = config_switch(w2); |
---|
2884 | } else if (strcmpi(w1, "use_grf") == 0) { |
---|
2885 | enable_grf = config_switch(w2); |
---|
2886 | } else if (strcmpi(w1, "import") == 0) { |
---|
2887 | map_config_read(w2); |
---|
2888 | } else |
---|
2889 | ShowWarning("Unknown setting '%s' in file %s\n", w1, cfgName); |
---|
2890 | } |
---|
2891 | } |
---|
2892 | fclose(fp); |
---|
2893 | |
---|
2894 | return 0; |
---|
2895 | } |
---|
2896 | |
---|
2897 | int inter_config_read(char *cfgName) |
---|
2898 | { |
---|
2899 | int i; |
---|
2900 | char line[1024],w1[1024],w2[1024]; |
---|
2901 | FILE *fp; |
---|
2902 | |
---|
2903 | fp=fopen(cfgName,"r"); |
---|
2904 | if(fp==NULL){ |
---|
2905 | ShowError("File not found: '%s'.\n",cfgName); |
---|
2906 | return 1; |
---|
2907 | } |
---|
2908 | while(fgets(line, sizeof(line), fp)) |
---|
2909 | { |
---|
2910 | if(line[0] == '/' && line[1] == '/') |
---|
2911 | continue; |
---|
2912 | i=sscanf(line,"%[^:]: %[^\r\n]",w1,w2); |
---|
2913 | if(i!=2) |
---|
2914 | continue; |
---|
2915 | if(strcmpi(w1,"party_share_level")==0){ |
---|
2916 | party_share_level = config_switch(w2); |
---|
2917 | } else if(strcmpi(w1,"lowest_gm_level")==0){ |
---|
2918 | lowest_gm_level = atoi(w2); |
---|
2919 | |
---|
2920 | /* Main chat nick [LuzZza] */ |
---|
2921 | } else if(strcmpi(w1, "main_chat_nick")==0){ |
---|
2922 | strcpy(main_chat_nick, w2); |
---|
2923 | |
---|
2924 | #ifndef TXT_ONLY |
---|
2925 | } else if(strcmpi(w1,"item_db_db")==0){ |
---|
2926 | strcpy(item_db_db,w2); |
---|
2927 | } else if(strcmpi(w1,"mob_db_db")==0){ |
---|
2928 | strcpy(mob_db_db,w2); |
---|
2929 | } else if(strcmpi(w1,"item_db2_db")==0){ |
---|
2930 | strcpy(item_db2_db,w2); |
---|
2931 | } else if(strcmpi(w1,"mob_db2_db")==0){ |
---|
2932 | strcpy(mob_db2_db,w2); |
---|
2933 | } else if (strcmpi(w1, "char_db") == 0) { |
---|
2934 | strcpy(char_db, w2); |
---|
2935 | //Map Server SQL DB |
---|
2936 | } else if(strcmpi(w1,"map_server_ip")==0){ |
---|
2937 | strcpy(map_server_ip, w2); |
---|
2938 | } else if(strcmpi(w1,"map_server_port")==0){ |
---|
2939 | map_server_port=atoi(w2); |
---|
2940 | } else if(strcmpi(w1,"map_server_id")==0){ |
---|
2941 | strcpy(map_server_id, w2); |
---|
2942 | } else if(strcmpi(w1,"map_server_pw")==0){ |
---|
2943 | strcpy(map_server_pw, w2); |
---|
2944 | } else if(strcmpi(w1,"map_server_db")==0){ |
---|
2945 | strcpy(map_server_db, w2); |
---|
2946 | } else if(strcmpi(w1,"default_codepage")==0){ |
---|
2947 | strcpy(default_codepage, w2); |
---|
2948 | } else if(strcmpi(w1,"use_sql_db")==0){ |
---|
2949 | db_use_sqldbs = config_switch(w2); |
---|
2950 | ShowStatus ("Using SQL dbs: %s\n",w2); |
---|
2951 | } else if(strcmpi(w1,"log_db")==0) { |
---|
2952 | strcpy(log_db, w2); |
---|
2953 | } else if(strcmpi(w1,"log_db_ip")==0) { |
---|
2954 | strcpy(log_db_ip, w2); |
---|
2955 | } else if(strcmpi(w1,"log_db")==0) { |
---|
2956 | strcpy(log_db, w2); |
---|
2957 | } else if(strcmpi(w1,"log_db_id")==0) { |
---|
2958 | strcpy(log_db_id, w2); |
---|
2959 | } else if(strcmpi(w1,"log_db_pw")==0) { |
---|
2960 | strcpy(log_db_pw, w2); |
---|
2961 | } else if(strcmpi(w1,"log_db_port")==0) { |
---|
2962 | log_db_port = atoi(w2); |
---|
2963 | #endif |
---|
2964 | //support the import command, just like any other config |
---|
2965 | } else if(strcmpi(w1,"import")==0){ |
---|
2966 | inter_config_read(w2); |
---|
2967 | } |
---|
2968 | } |
---|
2969 | fclose(fp); |
---|
2970 | |
---|
2971 | return 0; |
---|
2972 | } |
---|
2973 | |
---|
2974 | #ifndef TXT_ONLY |
---|
2975 | /*======================================= |
---|
2976 | * MySQL Init |
---|
2977 | *---------------------------------------*/ |
---|
2978 | int map_sql_init(void) |
---|
2979 | { |
---|
2980 | // main db connection |
---|
2981 | mmysql_handle = Sql_Malloc(); |
---|
2982 | |
---|
2983 | ShowInfo("Connecting to the Map DB Server....\n"); |
---|
2984 | if( SQL_ERROR == Sql_Connect(mmysql_handle, map_server_id, map_server_pw, map_server_ip, map_server_port, map_server_db) ) |
---|
2985 | exit(EXIT_FAILURE); |
---|
2986 | ShowStatus("connect success! (Map Server Connection)\n"); |
---|
2987 | |
---|
2988 | if( strlen(default_codepage) > 0 ) |
---|
2989 | if ( SQL_ERROR == Sql_SetEncoding(mmysql_handle, default_codepage) ) |
---|
2990 | Sql_ShowDebug(mmysql_handle); |
---|
2991 | |
---|
2992 | return 0; |
---|
2993 | } |
---|
2994 | |
---|
2995 | int map_sql_close(void) |
---|
2996 | { |
---|
2997 | ShowStatus("Close Map DB Connection....\n"); |
---|
2998 | Sql_Free(mmysql_handle); |
---|
2999 | mmysql_handle = NULL; |
---|
3000 | |
---|
3001 | if (log_config.sql_logs) |
---|
3002 | { |
---|
3003 | ShowStatus("Close Log DB Connection....\n"); |
---|
3004 | Sql_Free(logmysql_handle); |
---|
3005 | logmysql_handle = NULL; |
---|
3006 | } |
---|
3007 | |
---|
3008 | return 0; |
---|
3009 | } |
---|
3010 | |
---|
3011 | int log_sql_init(void) |
---|
3012 | { |
---|
3013 | // log db connection |
---|
3014 | logmysql_handle = Sql_Malloc(); |
---|
3015 | |
---|
3016 | ShowInfo(""CL_WHITE"[SQL]"CL_RESET": Connecting to the Log Database "CL_WHITE"%s"CL_RESET" At "CL_WHITE"%s"CL_RESET"...\n",log_db,log_db_ip); |
---|
3017 | if ( SQL_ERROR == Sql_Connect(logmysql_handle, log_db_id, log_db_pw, log_db_ip, log_db_port, log_db) ) |
---|
3018 | exit(EXIT_FAILURE); |
---|
3019 | ShowStatus(""CL_WHITE"[SQL]"CL_RESET": Successfully '"CL_GREEN"connected"CL_RESET"' to Database '"CL_WHITE"%s"CL_RESET"'.\n", log_db); |
---|
3020 | |
---|
3021 | if( strlen(default_codepage) > 0 ) |
---|
3022 | if ( SQL_ERROR == Sql_SetEncoding(logmysql_handle, default_codepage) ) |
---|
3023 | Sql_ShowDebug(logmysql_handle); |
---|
3024 | |
---|
3025 | return 0; |
---|
3026 | } |
---|
3027 | |
---|
3028 | /*============================================= |
---|
3029 | * Does a mysql_ping to all connection handles |
---|
3030 | *---------------------------------------------*/ |
---|
3031 | int map_sql_ping(int tid, unsigned int tick, int id, intptr data) |
---|
3032 | { |
---|
3033 | ShowInfo("Pinging SQL server to keep connection alive...\n"); |
---|
3034 | Sql_Ping(mmysql_handle); |
---|
3035 | if (log_config.sql_logs) |
---|
3036 | Sql_Ping(logmysql_handle); |
---|
3037 | return 0; |
---|
3038 | } |
---|
3039 | |
---|
3040 | int sql_ping_init(void) |
---|
3041 | { |
---|
3042 | uint32 connection_timeout, connection_ping_interval; |
---|
3043 | |
---|
3044 | // set a default value |
---|
3045 | connection_timeout = 28800; // 8 hours |
---|
3046 | |
---|
3047 | // ask the mysql server for the timeout value |
---|
3048 | Sql_GetTimeout(mmysql_handle, &connection_timeout); |
---|
3049 | if (connection_timeout < 60) |
---|
3050 | connection_timeout = 60; |
---|
3051 | |
---|
3052 | // establish keepalive |
---|
3053 | connection_ping_interval = connection_timeout - 30; // 30-second reserve |
---|
3054 | add_timer_func_list(map_sql_ping, "map_sql_ping"); |
---|
3055 | add_timer_interval(gettick() + connection_ping_interval*1000, map_sql_ping, 0, 0, connection_ping_interval*1000); |
---|
3056 | |
---|
3057 | return 0; |
---|
3058 | } |
---|
3059 | #endif /* not TXT_ONLY */ |
---|
3060 | |
---|
3061 | int map_db_final(DBKey k,void *d,va_list ap) |
---|
3062 | { |
---|
3063 | struct map_data_other_server *mdos = (struct map_data_other_server*)d; |
---|
3064 | if(mdos && mdos->cell == NULL) |
---|
3065 | aFree(mdos); |
---|
3066 | return 0; |
---|
3067 | } |
---|
3068 | |
---|
3069 | int nick_db_final(DBKey key, void *data, va_list args) |
---|
3070 | { |
---|
3071 | struct charid2nick* p = (struct charid2nick*)data; |
---|
3072 | struct charid_request* req; |
---|
3073 | |
---|
3074 | if( p == NULL ) |
---|
3075 | return 0; |
---|
3076 | while( p->requests ) |
---|
3077 | { |
---|
3078 | req = p->requests; |
---|
3079 | p->requests = req->next; |
---|
3080 | aFree(req); |
---|
3081 | } |
---|
3082 | aFree(p); |
---|
3083 | return 0; |
---|
3084 | } |
---|
3085 | |
---|
3086 | int cleanup_sub(struct block_list *bl, va_list ap) |
---|
3087 | { |
---|
3088 | nullpo_retr(0, bl); |
---|
3089 | |
---|
3090 | switch(bl->type) { |
---|
3091 | case BL_PC: |
---|
3092 | map_quit((struct map_session_data *) bl); |
---|
3093 | break; |
---|
3094 | case BL_NPC: |
---|
3095 | npc_unload((struct npc_data *)bl); |
---|
3096 | break; |
---|
3097 | case BL_MOB: |
---|
3098 | unit_free(bl,0); |
---|
3099 | break; |
---|
3100 | case BL_PET: |
---|
3101 | //There is no need for this, the pet is removed together with the player. [Skotlex] |
---|
3102 | break; |
---|
3103 | case BL_ITEM: |
---|
3104 | map_clearflooritem(bl->id); |
---|
3105 | break; |
---|
3106 | case BL_SKILL: |
---|
3107 | skill_delunit((struct skill_unit *) bl); |
---|
3108 | break; |
---|
3109 | } |
---|
3110 | |
---|
3111 | return 1; |
---|
3112 | } |
---|
3113 | |
---|
3114 | static int cleanup_db_sub(DBKey key,void *data,va_list va) |
---|
3115 | { |
---|
3116 | return cleanup_sub((struct block_list*)data, NULL); |
---|
3117 | } |
---|
3118 | |
---|
3119 | /*========================================== |
---|
3120 | * mapII¹E |
---|
3121 | *------------------------------------------*/ |
---|
3122 | void do_final(void) |
---|
3123 | { |
---|
3124 | int i, j; |
---|
3125 | struct map_session_data* sd; |
---|
3126 | struct s_mapiterator* iter; |
---|
3127 | |
---|
3128 | ShowStatus("Terminating...\n"); |
---|
3129 | |
---|
3130 | for (i = 0; i < map_num; i++) |
---|
3131 | if (map[i].m >= 0) |
---|
3132 | map_foreachinmap(cleanup_sub, i, BL_ALL); |
---|
3133 | |
---|
3134 | //Scan any remaining players (between maps?) to kick them out. [Skotlex] |
---|
3135 | iter = mapit_getallusers(); |
---|
3136 | for( sd = (TBL_PC*)mapit_first(iter); mapit_exists(iter); sd = (TBL_PC*)mapit_next(iter) ) |
---|
3137 | map_quit(sd); |
---|
3138 | mapit_free(iter); |
---|
3139 | |
---|
3140 | id_db->foreach(id_db,cleanup_db_sub); |
---|
3141 | chrif_char_reset_offline(); |
---|
3142 | chrif_flush_fifo(); |
---|
3143 | |
---|
3144 | do_final_atcommand(); |
---|
3145 | do_final_battle(); |
---|
3146 | do_final_chrif(); |
---|
3147 | do_final_npc(); |
---|
3148 | do_final_script(); |
---|
3149 | do_final_itemdb(); |
---|
3150 | do_final_storage(); |
---|
3151 | do_final_guild(); |
---|
3152 | do_final_party(); |
---|
3153 | do_final_pc(); |
---|
3154 | do_final_pet(); |
---|
3155 | do_final_mob(); |
---|
3156 | do_final_msg(); |
---|
3157 | do_final_skill(); |
---|
3158 | do_final_status(); |
---|
3159 | do_final_unit(); |
---|
3160 | if(use_irc) |
---|
3161 | do_final_irc(); |
---|
3162 | |
---|
3163 | map_db->destroy(map_db, map_db_final); |
---|
3164 | |
---|
3165 | for (i=0; i<map_num; i++) { |
---|
3166 | if(map[i].cell) aFree(map[i].cell); |
---|
3167 | if(map[i].block) aFree(map[i].block); |
---|
3168 | if(map[i].block_mob) aFree(map[i].block_mob); |
---|
3169 | if(battle_config.dynamic_mobs) { //Dynamic mobs flag by [random] |
---|
3170 | for (j=0; j<MAX_MOB_LIST_PER_MAP; j++) |
---|
3171 | if (map[i].moblist[j]) aFree(map[i].moblist[j]); |
---|
3172 | } |
---|
3173 | } |
---|
3174 | |
---|
3175 | mapindex_final(); |
---|
3176 | if(enable_grf) |
---|
3177 | grfio_final(); |
---|
3178 | |
---|
3179 | id_db->destroy(id_db, NULL); |
---|
3180 | pc_db->destroy(pc_db, NULL); |
---|
3181 | mobid_db->destroy(mobid_db, NULL); |
---|
3182 | nick_db->destroy(nick_db, nick_db_final); |
---|
3183 | charid_db->destroy(charid_db, NULL); |
---|
3184 | |
---|
3185 | #ifndef TXT_ONLY |
---|
3186 | map_sql_close(); |
---|
3187 | #endif /* not TXT_ONLY */ |
---|
3188 | ShowStatus("Successfully terminated.\n"); |
---|
3189 | } |
---|
3190 | |
---|
3191 | static int map_abort_sub(struct map_session_data* sd, va_list ap) |
---|
3192 | { |
---|
3193 | chrif_save(sd,1); |
---|
3194 | return 1; |
---|
3195 | } |
---|
3196 | |
---|
3197 | |
---|
3198 | //------------------------------ |
---|
3199 | // Function called when the server |
---|
3200 | // has received a crash signal. |
---|
3201 | //------------------------------ |
---|
3202 | void do_abort(void) |
---|
3203 | { |
---|
3204 | static int run = 0; |
---|
3205 | //Save all characters and then flush the inter-connection. |
---|
3206 | if (run) { |
---|
3207 | ShowFatalError("Server has crashed while trying to save characters. Character data can't be saved!\n"); |
---|
3208 | return; |
---|
3209 | } |
---|
3210 | run = 1; |
---|
3211 | if (!chrif_isconnected()) |
---|
3212 | { |
---|
3213 | if (pc_db->size(pc_db)) |
---|
3214 | ShowFatalError("Server has crashed without a connection to the char-server, %u characters can't be saved!\n", pc_db->size(pc_db)); |
---|
3215 | return; |
---|
3216 | } |
---|
3217 | ShowError("Server received crash signal! Attempting to save all online characters!\n"); |
---|
3218 | map_foreachpc(map_abort_sub); |
---|
3219 | chrif_flush_fifo(); |
---|
3220 | } |
---|
3221 | |
---|
3222 | /*====================================================== |
---|
3223 | * Map-Server Version Screen [MC Cameri] |
---|
3224 | *------------------------------------------------------*/ |
---|
3225 | void map_helpscreen(int flag) |
---|
3226 | { |
---|
3227 | puts("Usage: map-server [options]"); |
---|
3228 | puts("Options:"); |
---|
3229 | puts(CL_WHITE" Commands\t\t\tDescription"CL_RESET); |
---|
3230 | puts("-----------------------------------------------------------------------------"); |
---|
3231 | puts(" --help, --h, --?, /? Displays this help screen"); |
---|
3232 | puts(" --map-config <file> Load map-server configuration from <file>"); |
---|
3233 | puts(" --battle-config <file> Load battle configuration from <file>"); |
---|
3234 | puts(" --atcommand-config <file> Load atcommand configuration from <file>"); |
---|
3235 | puts(" --charcommand-config <file> Load charcommand configuration from <file>"); |
---|
3236 | puts(" --script-config <file> Load script configuration from <file>"); |
---|
3237 | puts(" --msg-config <file> Load message configuration from <file>"); |
---|
3238 | puts(" --grf-path-file <file> Load grf path file configuration from <file>"); |
---|
3239 | puts(" --sql-config <file> Load inter-server configuration from <file>"); |
---|
3240 | puts(" (SQL Only)"); |
---|
3241 | puts(" --log-config <file> Load logging configuration from <file>"); |
---|
3242 | puts(" (SQL Only)"); |
---|
3243 | puts(" --version, --v, -v, /v Displays the server's version"); |
---|
3244 | puts("\n"); |
---|
3245 | if (flag) exit(EXIT_FAILURE); |
---|
3246 | } |
---|
3247 | |
---|
3248 | /*====================================================== |
---|
3249 | * Map-Server Version Screen [MC Cameri] |
---|
3250 | *------------------------------------------------------*/ |
---|
3251 | void map_versionscreen(int flag) |
---|
3252 | { |
---|
3253 | ShowInfo("CL_WHITE" "eAthena version %d.%02d.%02d, Athena Mod version %d" CL_RESET"\n", |
---|
3254 | ATHENA_MAJOR_VERSION, ATHENA_MINOR_VERSION, ATHENA_REVISION, |
---|
3255 | ATHENA_MOD_VERSION); |
---|
3256 | ShowInfo(CL_GREEN "Website/Forum:" CL_RESET "\thttp://eathena.deltaanime.net/"); |
---|
3257 | ShowInfo(CL_GREEN "IRC Channel:" CL_RESET "\tirc://irc.deltaanime.net/#athena"); |
---|
3258 | ShowInfo("\nOpen " CL_WHITE "readme.html" CL_RESET " for more information."); |
---|
3259 | if (ATHENA_RELEASE_FLAG) ShowNotice("This version is not for release.\n"); |
---|
3260 | if (flag) exit(EXIT_FAILURE); |
---|
3261 | } |
---|
3262 | |
---|
3263 | /*====================================================== |
---|
3264 | * Map-Server Init and Command-line Arguments [Valaris] |
---|
3265 | *------------------------------------------------------*/ |
---|
3266 | void set_server_type(void) |
---|
3267 | { |
---|
3268 | SERVER_TYPE = ATHENA_SERVER_MAP; |
---|
3269 | } |
---|
3270 | |
---|
3271 | int do_init(int argc, char *argv[]) |
---|
3272 | { |
---|
3273 | int i; |
---|
3274 | |
---|
3275 | #ifdef GCOLLECT |
---|
3276 | GC_enable_incremental(); |
---|
3277 | #endif |
---|
3278 | |
---|
3279 | INTER_CONF_NAME="conf/inter_athena.conf"; |
---|
3280 | LOG_CONF_NAME="conf/log_athena.conf"; |
---|
3281 | MAP_CONF_NAME = "conf/map_athena.conf"; |
---|
3282 | BATTLE_CONF_FILENAME = "conf/battle_athena.conf"; |
---|
3283 | ATCOMMAND_CONF_FILENAME = "conf/atcommand_athena.conf"; |
---|
3284 | CHARCOMMAND_CONF_FILENAME = "conf/charcommand_athena.conf"; |
---|
3285 | SCRIPT_CONF_NAME = "conf/script_athena.conf"; |
---|
3286 | MSG_CONF_NAME = "conf/msg_athena.conf"; |
---|
3287 | GRF_PATH_FILENAME = "conf/grf-files.txt"; |
---|
3288 | |
---|
3289 | srand(gettick()); |
---|
3290 | |
---|
3291 | for (i = 1; i < argc ; i++) { |
---|
3292 | if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "--h") == 0 || strcmp(argv[i], "--?") == 0 || strcmp(argv[i], "/?") == 0) |
---|
3293 | map_helpscreen(1); |
---|
3294 | else if (strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "--v") == 0 || strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "/v") == 0) |
---|
3295 | map_versionscreen(1); |
---|
3296 | else if (strcmp(argv[i], "--map_config") == 0 || strcmp(argv[i], "--map-config") == 0) |
---|
3297 | MAP_CONF_NAME=argv[i+1]; |
---|
3298 | else if (strcmp(argv[i],"--battle_config") == 0 || strcmp(argv[i],"--battle-config") == 0) |
---|
3299 | BATTLE_CONF_FILENAME = argv[i+1]; |
---|
3300 | else if (strcmp(argv[i],"--atcommand_config") == 0 || strcmp(argv[i],"--atcommand-config") == 0) |
---|
3301 | ATCOMMAND_CONF_FILENAME = argv[i+1]; |
---|
3302 | else if (strcmp(argv[i],"--charcommand_config") == 0 || strcmp(argv[i],"--charcommand-config") == 0) |
---|
3303 | CHARCOMMAND_CONF_FILENAME = argv[i+1]; |
---|
3304 | else if (strcmp(argv[i],"--script_config") == 0 || strcmp(argv[i],"--script-config") == 0) |
---|
3305 | SCRIPT_CONF_NAME = argv[i+1]; |
---|
3306 | else if (strcmp(argv[i],"--msg_config") == 0 || strcmp(argv[i],"--msg-config") == 0) |
---|
3307 | MSG_CONF_NAME = argv[i+1]; |
---|
3308 | else if (strcmp(argv[i],"--grf_path_file") == 0 || strcmp(argv[i],"--grf-path-file") == 0) |
---|
3309 | GRF_PATH_FILENAME = argv[i+1]; |
---|
3310 | #ifndef TXT_ONLY |
---|
3311 | else if (strcmp(argv[i],"--inter_config") == 0 || strcmp(argv[i],"--inter-config") == 0) |
---|
3312 | INTER_CONF_NAME = argv[i+1]; |
---|
3313 | #endif |
---|
3314 | else if (strcmp(argv[i],"--log_config") == 0 || strcmp(argv[i],"--log-config") == 0) |
---|
3315 | LOG_CONF_NAME = argv[i+1]; |
---|
3316 | else if (strcmp(argv[i],"--run_once") == 0) // close the map-server as soon as its done.. for testing [Celest] |
---|
3317 | runflag = 0; |
---|
3318 | } |
---|
3319 | |
---|
3320 | map_config_read(MAP_CONF_NAME); |
---|
3321 | irc_read_conf(IRC_CONF); // [Zido] |
---|
3322 | chrif_checkdefaultlogin(); |
---|
3323 | |
---|
3324 | if (!map_ip_set || !char_ip_set) { |
---|
3325 | char ip_str[16]; |
---|
3326 | ip2str(addr_[0], ip_str); |
---|
3327 | |
---|
3328 | ShowError("\nNot all IP addresses in map_athena.conf configured, autodetecting...\n"); |
---|
3329 | |
---|
3330 | if (naddr_ == 0) |
---|
3331 | ShowError("Unable to determine your IP address...\n"); |
---|
3332 | else if (naddr_ > 1) |
---|
3333 | ShowNotice("Multiple interfaces detected...\n"); |
---|
3334 | |
---|
3335 | ShowInfo("Defaulting to %s as our IP address\n", ip_str); |
---|
3336 | |
---|
3337 | if (!map_ip_set) |
---|
3338 | clif_setip(ip_str); |
---|
3339 | if (!char_ip_set) |
---|
3340 | chrif_setip(ip_str); |
---|
3341 | } |
---|
3342 | |
---|
3343 | battle_config_read(BATTLE_CONF_FILENAME); |
---|
3344 | msg_config_read(MSG_CONF_NAME); |
---|
3345 | atcommand_config_read(ATCOMMAND_CONF_FILENAME); |
---|
3346 | charcommand_config_read(CHARCOMMAND_CONF_FILENAME); |
---|
3347 | script_config_read(SCRIPT_CONF_NAME); |
---|
3348 | inter_config_read(INTER_CONF_NAME); |
---|
3349 | log_config_read(LOG_CONF_NAME); |
---|
3350 | |
---|
3351 | id_db = idb_alloc(DB_OPT_BASE); |
---|
3352 | pc_db = idb_alloc(DB_OPT_BASE); //Added for reliable map_id2sd() use. [Skotlex] |
---|
3353 | mobid_db = idb_alloc(DB_OPT_BASE); //Added to lower the load of the lazy mob ai. [Skotlex] |
---|
3354 | map_db = uidb_alloc(DB_OPT_BASE); |
---|
3355 | nick_db = idb_alloc(DB_OPT_BASE); |
---|
3356 | charid_db = idb_alloc(DB_OPT_BASE); |
---|
3357 | #ifndef TXT_ONLY |
---|
3358 | map_sql_init(); |
---|
3359 | #endif /* not TXT_ONLY */ |
---|
3360 | |
---|
3361 | mapindex_init(); |
---|
3362 | if(enable_grf) |
---|
3363 | grfio_init(GRF_PATH_FILENAME); |
---|
3364 | |
---|
3365 | map_readallmaps(); |
---|
3366 | |
---|
3367 | add_timer_func_list(map_freeblock_timer, "map_freeblock_timer"); |
---|
3368 | add_timer_func_list(map_clearflooritem_timer, "map_clearflooritem_timer"); |
---|
3369 | add_timer_func_list(map_removemobs_timer, "map_removemobs_timer"); |
---|
3370 | add_timer_interval(gettick()+1000, map_freeblock_timer, 0, 0, 60*1000); |
---|
3371 | |
---|
3372 | if(use_irc) |
---|
3373 | do_init_irc(); |
---|
3374 | do_init_atcommand(); |
---|
3375 | do_init_battle(); |
---|
3376 | do_init_chrif(); |
---|
3377 | do_init_clif(); |
---|
3378 | do_init_script(); |
---|
3379 | do_init_itemdb(); |
---|
3380 | do_init_skill(); |
---|
3381 | do_init_mob(); |
---|
3382 | do_init_pc(); |
---|
3383 | do_init_status(); |
---|
3384 | do_init_party(); |
---|
3385 | do_init_guild(); |
---|
3386 | do_init_storage(); |
---|
3387 | do_init_pet(); |
---|
3388 | do_init_merc(); //[orn] |
---|
3389 | do_init_npc(); |
---|
3390 | do_init_unit(); |
---|
3391 | #ifndef TXT_ONLY /* mail system [Valaris] */ |
---|
3392 | if (log_config.sql_logs) |
---|
3393 | log_sql_init(); |
---|
3394 | |
---|
3395 | sql_ping_init(); |
---|
3396 | #endif /* not TXT_ONLY */ |
---|
3397 | |
---|
3398 | npc_event_do_oninit(); // npcÌOnInitCxg?s |
---|
3399 | |
---|
3400 | if( console ) |
---|
3401 | { |
---|
3402 | //##TODO invoke a CONSOLE_START plugin event |
---|
3403 | } |
---|
3404 | |
---|
3405 | if (battle_config.pk_mode == 1) |
---|
3406 | ShowNotice("Server is running on '"CL_WHITE"PK Mode"CL_RESET"'.\n"); |
---|
3407 | |
---|
3408 | ShowStatus("Server is '"CL_GREEN"ready"CL_RESET"' and listening on port '"CL_WHITE"%d"CL_RESET"'.\n\n", map_port); |
---|
3409 | |
---|
3410 | return 0; |
---|
3411 | } |
---|