Atrinik Server 2.5
types/spawn_point.c
Go to the documentation of this file.
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 }