|
Atrinik Server 2.5
|
00001 /************************************************************************ 00002 * Atrinik, a Multiplayer Online Role Playing Game * 00003 * * 00004 * Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team * 00005 * * 00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game) * 00007 * and Crossfire (Multiplayer game for X-windows). * 00008 * * 00009 * This program is free software; you can redistribute it and/or modify * 00010 * it under the terms of the GNU General Public License as published by * 00011 * the Free Software Foundation; either version 2 of the License, or * 00012 * (at your option) any later version. * 00013 * * 00014 * This program is distributed in the hope that it will be useful, * 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00017 * GNU General Public License for more details. * 00018 * * 00019 * You should have received a copy of the GNU General Public License * 00020 * along with this program; if not, write to the Free Software * 00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * 00022 * * 00023 * The author can be reached at admin@atrinik.org * 00024 ************************************************************************/ 00025 00030 #include <global.h> 00031 00038 static object *spawn_monster(object *monster, object *spawn_point_ob, int range) 00039 { 00040 int i; 00041 object *op, *head = NULL, *prev = NULL, *ret = NULL; 00042 archetype *at = monster->arch; 00043 00044 i = find_first_free_spot2(at, spawn_point_ob->map, spawn_point_ob->x, spawn_point_ob->y, 0, range); 00045 00046 if (i == -1) 00047 { 00048 return NULL; 00049 } 00050 00051 while (at) 00052 { 00053 op = get_object(); 00054 00055 /* Copy single/head from spawn inventory */ 00056 if (head == NULL) 00057 { 00058 monster->type = MONSTER; 00059 copy_object(monster, op, 0); 00060 monster->type = SPAWN_POINT_MOB; 00061 ret = op; 00062 } 00063 /* But the tails for multi arch from the clones */ 00064 else 00065 { 00066 copy_object(&at->clone, op, 0); 00067 } 00068 00069 op->x = spawn_point_ob->x + freearr_x[i] + at->clone.x; 00070 op->y = spawn_point_ob->y + freearr_y[i] + at->clone.y; 00071 op->map = spawn_point_ob->map; 00072 00073 if (head) 00074 { 00075 op->head = head; 00076 prev->more = op; 00077 } 00078 00079 if (OBJECT_FREE(op)) 00080 { 00081 return NULL; 00082 } 00083 00084 if (head == NULL) 00085 { 00086 head = op; 00087 } 00088 00089 prev = op; 00090 at = at->more; 00091 } 00092 00093 if (ret && ret->item_condition) 00094 { 00095 int level = MAX(1, MIN(ret->level, MAXLEVEL)), min, max, diff = spawn_point_ob->map->difficulty; 00096 00097 switch (ret->item_condition) 00098 { 00099 case 1: 00100 min = level_color[diff].green; 00101 max = level_color[diff].blue - 1; 00102 break; 00103 00104 case 2: 00105 min = level_color[diff].blue; 00106 max = level_color[diff].yellow - 1; 00107 break; 00108 00109 case 3: 00110 min = level_color[diff].yellow; 00111 max = level_color[diff].orange - 1; 00112 break; 00113 00114 case 4: 00115 min = level_color[diff].orange; 00116 max = level_color[diff].red - 1; 00117 break; 00118 00119 case 5: 00120 min = level_color[diff].red; 00121 max = level_color[diff].purple - 1; 00122 break; 00123 00124 case 6: 00125 min = level_color[diff].purple; 00126 max = min + 1; 00127 break; 00128 00129 default: 00130 min = level; 00131 max = min; 00132 } 00133 00134 ret->level = rndm(MAX(level, MIN(min, MAXLEVEL)), MAX(level, MIN(max, MAXLEVEL))); 00135 } 00136 00137 if (ret->randomitems) 00138 { 00139 create_treasure(ret->randomitems, ret, 0, ret->level ? ret->level : spawn_point_ob->map->difficulty, T_STYLE_UNSET, ART_CHANCE_UNSET, 0, NULL); 00140 } 00141 00142 return ret; 00143 } 00144 00152 static inline int spawn_point_can_spawn(mapstruct *map, object *monster, int darkness) 00153 { 00154 shstr *spawn_time; 00155 00156 if (!map) 00157 { 00158 return 0; 00159 } 00160 00161 /* Check darkness. */ 00162 if (darkness) 00163 { 00164 int map_darkness; 00165 00166 /* Figure out the map's darkness. */ 00167 if (MAP_OUTDOORS(map)) 00168 { 00169 map_darkness = world_darkness; 00170 } 00171 else if (MAP_DARKNESS(map) == -1) 00172 { 00173 map_darkness = MAX_DARKNESS; 00174 } 00175 else 00176 { 00177 map_darkness = MAP_DARKNESS(map); 00178 } 00179 00180 if (darkness < 0) 00181 { 00182 if (map_darkness > -darkness) 00183 { 00184 return 0; 00185 } 00186 } 00187 else 00188 { 00189 if (map_darkness < darkness) 00190 { 00191 return 0; 00192 } 00193 } 00194 } 00195 00196 spawn_time = object_get_value(monster, "spawn_time"); 00197 00198 /* Check if the time is right for the monster to be spawned. */ 00199 if (spawn_time) 00200 { 00201 int hour, minute, hour2, minute2; 00202 00203 if (sscanf(spawn_time, "%d:%d - %d:%d", &hour, &minute, &hour2, &minute2) == 4) 00204 { 00205 timeofday_t tod; 00206 00207 get_tod(&tod); 00208 00209 /* Same day. */ 00210 if (hour <= hour2) 00211 { 00212 if (tod.hour < hour || tod.hour > hour2) 00213 { 00214 return 0; 00215 } 00216 } 00217 /* Overnight. */ 00218 else 00219 { 00220 if (tod.hour < hour && tod.hour > hour2) 00221 { 00222 return 0; 00223 } 00224 } 00225 00226 /* Check minutes. */ 00227 if ((tod.hour == hour && tod.minute < minute) || (tod.hour == hour2 && tod.minute > minute2)) 00228 { 00229 return 0; 00230 } 00231 00232 return 1; 00233 } 00234 else 00235 { 00236 LOG(llevBug, "spawn_point_can_spawn(): Syntax error in spawn_time attribute: %s\n", spawn_time); 00237 } 00238 } 00239 00240 return 1; 00241 } 00242 00256 static void insert_spawn_monster_loot(object *op, object *monster, object *tmp) 00257 { 00258 object *tmp2, *next, *next2, *item; 00259 00260 for (; tmp; tmp = next) 00261 { 00262 next = tmp->below; 00263 00264 if (tmp->type == RANDOM_DROP) 00265 { 00266 if (!tmp->weight_limit || rndm_chance(tmp->weight_limit)) 00267 { 00268 for (tmp2 = tmp->inv; tmp2; tmp2 = next2) 00269 { 00270 next2 = tmp2->below; 00271 00272 if (tmp2->type == RANDOM_DROP) 00273 { 00274 LOG(llevDebug, "RANDOM_DROP not allowed inside another RANDOM_DROP. Monster: >%s< map: %s (x: %d, y: %d)\n", query_name(monster, NULL), op->map ? op->map->path : "null", op->x, op->y); 00275 } 00276 else 00277 { 00278 item = get_object(); 00279 copy_object(tmp2, item, 0); 00280 insert_ob_in_ob(item, monster); 00281 00282 if (tmp2->inv) 00283 { 00284 insert_spawn_monster_loot(op, item, tmp2->inv); 00285 } 00286 } 00287 } 00288 } 00289 } 00290 else 00291 { 00292 item = get_object(); 00293 copy_object(tmp, item, 0); 00294 insert_ob_in_ob(item, monster); 00295 00296 if (tmp->inv) 00297 { 00298 insert_spawn_monster_loot(op, item, tmp->inv); 00299 } 00300 } 00301 } 00302 } 00303 00307 void spawn_point(object *op) 00308 { 00309 int rmt; 00310 object *tmp, *monster, *next; 00311 00312 if (op->enemy) 00313 { 00314 if (OBJECT_VALID(op->enemy, op->enemy_count)) 00315 { 00316 if (!spawn_point_can_spawn(op->map, op->enemy, op->last_eat)) 00317 { 00318 remove_ob(op->enemy); 00319 check_walk_off(op->enemy, NULL, MOVE_APPLY_VANISHED); 00320 } 00321 else 00322 { 00323 return; 00324 } 00325 } 00326 00327 /* Spawn point has nothing spawned */ 00328 op->enemy = NULL; 00329 } 00330 00331 /* A set sp value will override the spawn chance, which is particularly 00332 * useful when saving/loading maps. */ 00333 if (op->stats.sp == -1) 00334 { 00335 if (op->last_grace <= -1) 00336 { 00337 return; 00338 } 00339 00340 if (op->last_grace && !rndm_chance(op->last_grace)) 00341 { 00342 return; 00343 } 00344 00345 op->stats.sp = rndm(1, SPAWN_RANDOM_RANGE) - 1; 00346 } 00347 00348 /* Spawn point without inventory! */ 00349 if (!op->inv) 00350 { 00351 LOG(llevBug, "Spawn point without inventory! --> map %s (x: %d, y: %d)\n", op->map ? (op->map->path ? op->map->path : ">no path<") : ">no map<", op->x, op->y); 00352 remove_ob(op); 00353 check_walk_off(op, NULL, MOVE_APPLY_VANISHED); 00354 return; 00355 } 00356 00357 for (rmt = 0, monster = NULL, tmp = op->inv; tmp; tmp = next) 00358 { 00359 next = tmp->below; 00360 00361 if (tmp->type == BEACON) 00362 { 00363 continue; 00364 } 00365 00366 if (tmp->type != SPAWN_POINT_MOB) 00367 { 00368 LOG(llevBug, "Spawn point in map %s (x: %d, y: %d) with wrong type object (%d) in inv: %s\n", op->map ? op->map->path : "<no map>", op->x, op->y, tmp->type, query_name(tmp, NULL)); 00369 } 00370 else if ((int) tmp->enemy_count <= op->stats.sp && (int) tmp->enemy_count >= rmt) 00371 { 00372 if (!spawn_point_can_spawn(op->map, tmp, tmp->last_eat)) 00373 { 00374 continue; 00375 } 00376 00377 rmt = (int) tmp->enemy_count; 00378 monster = tmp; 00379 } 00380 } 00381 00382 rmt = op->stats.sp; 00383 op->stats.sp = -1; 00384 00385 if (!monster) 00386 { 00387 return; 00388 } 00389 00390 /* Quick save the default monster inventory */ 00391 tmp = monster->inv; 00392 00393 if (!(monster = spawn_monster(monster, op, op->last_heal))) 00394 { 00395 return; 00396 } 00397 00398 /* Setup special monster -> spawn point values */ 00399 op->last_eat = 0; 00400 00401 /* Darkness controlled spawns */ 00402 if (monster->last_eat) 00403 { 00404 op->last_eat = monster->last_eat; 00405 monster->last_eat = 0; 00406 } 00407 00408 insert_spawn_monster_loot(op, monster, tmp); 00409 00410 op->last_sp = rmt; 00411 00412 /* Chain the monster to our spawn point */ 00413 op->enemy = monster; 00414 op->enemy_count = monster->count; 00415 00416 /* Create spawn info */ 00417 tmp = arch_to_object(op->other_arch); 00418 /* Chain spawn point to our monster */ 00419 tmp->owner = op; 00420 tmp->ownercount = op->count; 00421 /* And put it inside the monster */ 00422 insert_ob_in_ob(tmp, monster); 00423 00424 SET_MULTI_FLAG(monster, FLAG_SPAWN_MOB); 00425 fix_monster(monster); 00426 00427 insert_ob_in_map(monster, monster->map, op, 0); 00428 00429 if (QUERY_FLAG(monster, FLAG_ANIMATE)) 00430 { 00431 animate_object(monster, 0); 00432 } 00433 }
1.7.4