Crossfire Server, Branch 1.12  R12190
citylife.c
Go to the documentation of this file.
00001 /*****************************************************************************/
00002 /* Template for version 2.0 plugins.                                         */
00003 /* Contact: yann.chachkoff@myrealbox.com                                     */
00004 /*****************************************************************************/
00005 /* That code is placed under the GNU General Public Licence (GPL)            */
00006 /* (C)2001-2005 by Chachkoff Yann (Feel free to deliver your complaints)     */
00007 /*****************************************************************************/
00008 /*  CrossFire, A Multiplayer game for X-windows                              */
00009 /*                                                                           */
00010 /*  Copyright (C) 2000 Mark Wedel                                            */
00011 /*  Copyright (C) 1992 Frank Tore Johansen                                   */
00012 /*                                                                           */
00013 /*  This program is free software; you can redistribute it and/or modify     */
00014 /*  it under the terms of the GNU General Public License as published by     */
00015 /*  the Free Software Foundation; either version 2 of the License, or        */
00016 /*  (at your option) any later version.                                      */
00017 /*                                                                           */
00018 /*  This program is distributed in the hope that it will be useful,          */
00019 /*  but WITHOUT ANY WARRANTY; without even the implied warranty of           */
00020 /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            */
00021 /*  GNU General Public License for more details.                             */
00022 /*                                                                           */
00023 /*  You should have received a copy of the GNU General Public License        */
00024 /*  along with this program; if not, write to the Free Software              */
00025 /*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.                */
00026 /*                                                                           */
00027 /*****************************************************************************/
00028 
00061 #include <citylife.h>
00062 #include <stdarg.h>
00063 #ifndef __CEXTRACT__
00064 #include <citylife_proto.h>
00065 #endif
00066 
00067 CF_PLUGIN int initPlugin(const char *iversion, f_plug_api gethooksptr) {
00068     cf_init_plugin(gethooksptr);
00069 
00070     cf_log(llevDebug, PLUGIN_VERSION " init\n");
00071 
00072     return 0;
00073 }
00074 
00075 CF_PLUGIN void *getPluginProperty(int *type, ...) {
00076     va_list args;
00077     const char *propname;
00078     int size;
00079     char *buf;
00080 
00081     va_start(args, type);
00082     propname = va_arg(args, const char *);
00083 
00084     if (!strcmp(propname, "Identification")) {
00085         buf = va_arg(args, char *);
00086         size = va_arg(args, int);
00087         va_end(args);
00088         snprintf(buf, size, PLUGIN_NAME);
00089         return NULL;
00090     } else if (!strcmp(propname, "FullName")) {
00091         buf = va_arg(args, char *);
00092         size = va_arg(args, int);
00093         va_end(args);
00094         snprintf(buf, size, PLUGIN_VERSION);
00095         return NULL;
00096     }
00097     va_end(args);
00098     return NULL;
00099 }
00100 
00101 CF_PLUGIN int citylife_runPluginCommand(object *op, char *params) {
00102     return -1;
00103 }
00104 
00106 #define FIRST_MOVE_KEY  "citylife_first_move"
00107 
00111 typedef struct {
00112     int x;
00113     int y;
00114 } spawn_point;
00115 
00120 typedef struct {
00121     int sx, sy, ex, ey;
00122 } spawn_zone;
00123 
00127 typedef struct {
00128     const spawn_point *points;                 
00129     int count_points;                          
00130     const spawn_zone *zones;                   
00131     int count_zones;                           
00132     int population;                            
00133     const char *mapname;                       
00134     const char *const *available_archetypes;   
00135     int archetypes_count;                      
00136 } mapzone;
00147 static const spawn_zone scorn_nw_zones[] = {
00148     { 40, 26, 50, 50 }
00149 };
00150 
00152 static const spawn_point scorn_nw_points[] = {
00153     { 41, 37 },
00154     { 48, 35 },
00155     { 49, 40 },
00156     { 47, 22 },
00157     { 49, 37 }
00158 };
00159 
00161 static const spawn_zone scorn_ne_zones[] = {
00162     { 0, 26, 22, 50 }
00163 };
00164 
00166 static const spawn_point scorn_ne_points[] = {
00167     { 15, 42 },
00168     { 9, 35 },
00169     { 15, 29 },
00170     { 1, 25 },
00171     { 1, 29 }
00172 };
00173 
00175 static const spawn_zone scorn_sw_zones[] = {
00176     { 41, 0, 50, 10 }
00177 };
00178 
00180 static const spawn_point scorn_sw_points[] = {
00181     { 41, 2 },
00182     { 46, 8 },
00183     { 42, 8 }
00184 };
00185 
00187 static const spawn_zone scorn_se_zones[] = {
00188     { 0, 0, 13, 10 }
00189 };
00190 
00192 static const spawn_point scorn_se_points[] = {
00193     { 2, 8 },
00194     { 11, 8 },
00195     { 8, 1 },
00196     { 5, 8 }
00197 };
00198 
00200 static const char *const scorn_archs[] = {
00201     "c_man",
00202     "c_woman",
00203     "child",
00204     "farmer",
00205     "fatman",
00206     "fatwoman",
00207     "guard",
00208     "knight",
00209     "man",
00210     "nun",
00211     "sage",
00212     "woman"
00213 };
00221 static const mapzone available_zones[] = {
00222     { scorn_nw_points, 5, scorn_nw_zones, 1, 2, "/world/world_104_115", scorn_archs, 12 },
00223     { scorn_ne_points, 1, scorn_ne_zones, 1, 5, "/world/world_105_115", scorn_archs, 12 },
00224     { scorn_sw_points, 3, scorn_sw_zones, 1, 5, "/world/world_104_116", scorn_archs, 12 },
00225     { scorn_se_points, 1, scorn_se_zones, 1, 5, "/world/world_105_116", scorn_archs, 12 },
00226     { NULL, -1, NULL, -1, 1, "", NULL, 0 },
00227 };
00228 
00237 static const mapzone *get_zone_for_map(mapstruct *map) {
00238     int test;
00239 
00240     for (test = 0; available_zones[test].count_points != -1; test++) {
00241         if (strcmp(available_zones[test].mapname, map->path) == 0)
00242             return &available_zones[test];
00243     }
00244     return NULL;
00245 }
00246 
00254 static object *get_npc(const mapzone *zone) {
00255     int arch = RANDOM()%zone->archetypes_count;
00256     object *npc = cf_create_object_by_name(zone->available_archetypes[arch]);
00257     object *evt;
00258 
00259     if (!npc) {
00260         cf_log(llevError, PLUGIN_NAME ": get_npc() got NULL object for %s!\n", zone->available_archetypes[arch]);
00261         return NULL;
00262     }
00263 
00264     cf_object_set_flag(npc, FLAG_RANDOM_MOVE, 1);
00265     /* Prevent disease spreading in Scorn, mostly rabies. */
00266     cf_object_set_flag(npc, FLAG_UNDEAD, 1);
00267     /* add a key so NPC will not disappear in the house it came from */
00268     cf_object_set_key(npc, FIRST_MOVE_KEY, "1", 1);
00269 
00270     evt = cf_create_object_by_name("event_time");
00271     evt->slaying = cf_add_string(PLUGIN_NAME);
00272     evt->title = cf_add_string(PLUGIN_NAME);
00273     cf_object_insert_object(evt, npc);
00274 
00275     return npc;
00276 }
00277 
00285 static void add_npc_to_zone(const mapzone *zone, mapstruct *map) {
00286     int which;
00287     object *npc = get_npc(zone);
00288 
00289     if (!npc)
00290         return;
00291     which = RANDOM()%zone->count_zones;
00292     if (cf_object_teleport(npc, map, zone->zones[which].sx+RANDOM()%(zone->zones[which].ex-zone->zones[which].sx), zone->zones[which].sy+RANDOM()%(zone->zones[which].ey-zone->zones[which].sy))) {
00293         cf_object_free(npc);
00294     }
00295 }
00296 
00304 static void add_npc_to_point(const mapzone *zone, mapstruct *map) {
00305     int which;
00306     object *npc = get_npc(zone);
00307 
00308     which = RANDOM()%zone->count_points;
00309     if (cf_object_teleport(npc, map, zone->points[which].x, zone->points[which].y)) {
00310         cf_object_free(npc);
00311     }
00312 }
00313 
00319 static void add_npcs_to_map(mapstruct *map) {
00320     int add;
00321     const mapzone *zone = get_zone_for_map(map);
00322 
00323     if (!zone)
00324         return;
00325 
00326     add = 1+RANDOM()%zone->population;
00327     cf_log(llevDebug, PLUGIN_NAME ": adding %d NPC to map %s.\n", add, map->path);
00328 
00329     while (add-- >= 0) {
00330         add_npc_to_zone(zone, map);
00331     }
00332 }
00333 
00337 static void add_npc_to_random_map(void) {
00338     int count, test;
00339     mapstruct *list[50];
00340     int zones[50];
00341     count = 0;
00342 
00343     cf_log(llevDebug, PLUGIN_NAME ": adding NPC to random map.\n");
00344 
00345     for (test = 0; available_zones[test].count_points != -1 && count < 50; test++) {
00346         if ((list[count] = cf_map_has_been_loaded(available_zones[test].mapname)) && (list[count]->in_memory == MAP_IN_MEMORY)) {
00347             zones[count] = test;
00348             count++;
00349         }
00350     }
00351     if (!count)
00352         return;
00353 
00354     test = RANDOM()%count;
00355     add_npc_to_point(&available_zones[zones[test]], list[test]);
00356 }
00357 
00358 CF_PLUGIN void *citylife_globalEventListener(int *type, ...) {
00359     va_list args;
00360     static int rv = 0;
00361     mapstruct *map;
00362     int code;
00363 
00364     va_start(args, type);
00365     code = va_arg(args, int);
00366 
00367     rv = 0;
00368 
00369     switch (code) {
00370     case EVENT_MAPLOAD:
00371         map = va_arg(args, mapstruct *);
00372         add_npcs_to_map(map);
00373         break;
00374 
00375     case EVENT_CLOCK:
00376         if (RANDOM()%40 == 0)
00377             add_npc_to_random_map();
00378     }
00379     va_end(args);
00380 
00381     return &rv;
00382 }
00383 
00384 CF_PLUGIN int postInitPlugin(void) {
00385     cf_log(llevDebug, PLUGIN_VERSION " post init\n");
00386 
00387     /* Pick the global events you want to monitor from this plugin */
00388 
00389 /*
00390     cf_system_register_global_event(EVENT_BORN, PLUGIN_NAME, citylife_globalEventListener);
00391     cf_system_register_global_event(EVENT_CRASH, PLUGIN_NAME, citylife_globalEventListener);
00392     cf_system_register_global_event(EVENT_PLAYER_DEATH, PLUGIN_NAME, citylife_globalEventListener);
00393     cf_system_register_global_event(EVENT_GKILL, PLUGIN_NAME, citylife_globalEventListener);
00394     cf_system_register_global_event(EVENT_LOGIN, PLUGIN_NAME, citylife_globalEventListener);
00395     cf_system_register_global_event(EVENT_LOGOUT, PLUGIN_NAME, citylife_globalEventListener);
00396     cf_system_register_global_event(EVENT_MAPENTER, PLUGIN_NAME, citylife_globalEventListener);
00397     cf_system_register_global_event(EVENT_MAPLEAVE, PLUGIN_NAME, citylife_globalEventListener);
00398     cf_system_register_global_event(EVENT_MAPRESET, PLUGIN_NAME, citylife_globalEventListener);
00399     cf_system_register_global_event(EVENT_REMOVE, PLUGIN_NAME, citylife_globalEventListener);
00400     cf_system_register_global_event(EVENT_SHOUT, PLUGIN_NAME, citylife_globalEventListener);
00401     cf_system_register_global_event(EVENT_TELL, PLUGIN_NAME, citylife_globalEventListener);
00402     cf_system_register_global_event(EVENT_MUZZLE, PLUGIN_NAME, citylife_globalEventListener);
00403     cf_system_register_global_event(EVENT_KICK, PLUGIN_NAME, citylife_globalEventListener);
00404 */
00405     cf_system_register_global_event(EVENT_CLOCK, PLUGIN_NAME, citylife_globalEventListener);
00406     cf_system_register_global_event(EVENT_MAPLOAD, PLUGIN_NAME, citylife_globalEventListener);
00407 /*
00408     cf_system_register_global_event(EVENT_MAPRESET, PLUGIN_NAME, citylife_globalEventListener);
00409 */
00410 
00411     return 0;
00412 }
00413 
00414 CF_PLUGIN void *eventListener(int *type, ...) {
00415     static int rv = 1;
00416     va_list args;
00417     char *buf;
00418     object *ground, *who, *activator, *third, *event;
00419     int fix;
00420     const char *value;
00421 
00422     va_start(args, type);
00423 
00424     who = va_arg(args, object *);
00425     activator = va_arg(args, object *);
00426     third = va_arg(args, object *);
00427     buf = va_arg(args, char *);
00428     fix = va_arg(args, int);
00429     event = va_arg(args, object *);
00430     va_end(args);
00431 
00432     /* should our npc disappear? */
00433     if (RANDOM()%100 < 30) {
00434         for (ground = cf_map_get_object_at(who->map, who->x, who->y); ground; ground = cf_object_get_object_property(ground, CFAPI_OBJECT_PROP_OB_ABOVE)) {
00435             if (ground->type == EXIT) {
00436                 object *inv;
00437 
00438                 value = cf_object_get_key(who, FIRST_MOVE_KEY);
00439                 if (strcmp(value, "1") == 0) {
00440                     cf_object_set_key(who, FIRST_MOVE_KEY, "0", 1);
00441                     break;
00442                 }
00443 
00444                 /* must set inventory as no drop, else it'll just drop on the ground */
00445                 for (inv = cf_object_get_object_property(who, CFAPI_OBJECT_PROP_INVENTORY); inv; inv = cf_object_get_object_property(inv, CFAPI_OBJECT_PROP_OB_BELOW))
00446                     cf_object_set_flag(inv, FLAG_NO_DROP, 1);
00447 
00448                 cf_log(llevDebug, PLUGIN_NAME ": NPC entering building.\n");
00449                 cf_object_remove(who);
00450                 cf_object_free(who);
00451                 return &rv;
00452             }
00453         }
00454     }
00455 
00456     /* we have to move manually, because during the night NPCs don't move. */
00457     cf_object_move(who, 1+RANDOM()%8, NULL);
00458 
00459     return &rv;
00460 }
00461 
00462 CF_PLUGIN int   closePlugin(void) {
00463     cf_log(llevDebug, PLUGIN_VERSION " closing\n");
00464     return 0;
00465 }