Atrinik Server 2.5
plugins/plugin_arena/plugin_arena.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 
00086 #include <global.h>
00087 #include <stdarg.h>
00088 
00090 #define PLUGIN_NAME "Arena"
00091 
00093 #define PLUGIN_VERSION "Arena plugin 1.0"
00094 
00096 typedef struct arena_map_players
00097 {
00099     object *op;
00100 
00102     struct arena_map_players *next;
00103 } arena_map_players;
00104 
00106 typedef struct arena_maps_struct
00107 {
00109     char path[HUGE_BUF];
00110 
00112     int players;
00113 
00115     int parties;
00116 
00118     arena_map_players *player_list;
00119 
00121     int max_players;
00122 
00124     int max_parties;
00125 
00127     uint32 flags;
00128 
00130     char message_arena_full[MAX_BUF];
00131 
00133     char message_arena_party[MAX_BUF];
00134 
00136     struct arena_maps_struct *next;
00137 } arena_maps_struct;
00138 
00144 #define ARENA_FLAG_NONE             0
00145 
00146 #define ARENA_FLAG_PARTY            1
00147 
00148 #define ARENA_FLAG_PARTY_PLAYERS    2
00149 
00152 arena_maps_struct *arena_maps;
00153 
00155 struct plugin_hooklist *hooks;
00156 
00157 #undef LOG
00158 #define LOG hooks->LOG
00159 
00160 MODULEAPI void initPlugin(struct plugin_hooklist *hooklist)
00161 {
00162     hooks = hooklist;
00163 
00164     LOG(llevDebug, "Arena: Atrinik Arena Plugin loading...\n");
00165     LOG(llevDebug, "Arena:  [Done]\n");
00166 }
00167 
00168 MODULEAPI void closePlugin()
00169 {
00170     LOG(llevDebug, "Arena: Arena Plugin closing.\n");
00171 }
00172 
00173 MODULEAPI void *getPluginProperty(int *type, ...)
00174 {
00175     va_list args;
00176     const char *propname;
00177     int size;
00178     char *buf;
00179 
00180     va_start(args, type);
00181     propname = va_arg(args, const char *);
00182 
00183     if (!strcmp(propname, "Identification"))
00184     {
00185         buf = va_arg(args, char *);
00186         size = va_arg(args, int);
00187         va_end(args);
00188         snprintf(buf, size, PLUGIN_NAME);
00189         return NULL;
00190     }
00191     else if (!strcmp(propname, "FullName"))
00192     {
00193         buf = va_arg(args, char *);
00194         size = va_arg(args, int);
00195         va_end(args);
00196         snprintf(buf, size, PLUGIN_VERSION);
00197         return NULL;
00198     }
00199 
00200     va_end(args);
00201     return NULL;
00202 }
00203 
00204 MODULEAPI void postinitPlugin()
00205 {
00206     LOG(llevDebug, "Arena: Start postinitPlugin.\n");
00207 
00208     hooks->register_global_event(PLUGIN_NAME, GEVENT_LOGOUT);
00209 }
00210 
00216 static int check_arena_player(object *op, arena_map_players *player_list)
00217 {
00218     arena_map_players *player_list_tmp;
00219 
00220     /* Go through the list of players. */
00221     for (player_list_tmp = player_list; player_list_tmp; player_list_tmp = player_list_tmp->next)
00222     {
00223         if (player_list_tmp->op == op)
00224         {
00225             return 1;
00226         }
00227     }
00228 
00229     return 0;
00230 }
00231 
00236 static void remove_arena_player(object *op, arena_map_players **player_list)
00237 {
00238     arena_map_players *currP, *prevP = NULL;
00239 
00240     for (currP = *player_list; currP; prevP = currP, currP = currP->next)
00241     {
00242         if (currP->op == op)
00243         {
00244             if (!prevP)
00245             {
00246                 *player_list = currP->next;
00247             }
00248             else
00249             {
00250                 prevP->next = currP->next;
00251             }
00252 
00253             free(currP);
00254             break;
00255         }
00256     }
00257 }
00258 
00263 static void arena_map_parse_line(arena_maps_struct *arena_map, const char *line)
00264 {
00265     /* Maximum number of players */
00266     if (strncmp(line, "max_players ", 12) == 0)
00267     {
00268         arena_map->max_players = atoi(line + 12);
00269     }
00270     /* Maximum number of parties */
00271     else if (strncmp(line, "max_parties ", 12) == 0)
00272     {
00273         arena_map->max_parties = atoi(line + 12);
00274     }
00275     /* Whether to allow arena party mode */
00276     else if (strncmp(line, "party ", 6) == 0)
00277     {
00278         line += 6;
00279 
00280         if (!strcmp(line, "true") || *line == '1')
00281         {
00282             arena_map->flags |= ARENA_FLAG_PARTY;
00283         }
00284     }
00285     /* Or even party players? */
00286     else if (strncmp(line, "party_players ", 14) == 0)
00287     {
00288         line += 14;
00289 
00290         if (!strcmp(line, "true") || *line == '1')
00291         {
00292             arena_map->flags |= ARENA_FLAG_PARTY_PLAYERS;
00293         }
00294     }
00295     /* Message for when the arena is full */
00296     else if (strncmp(line, "message_full ", 13) == 0)
00297     {
00298         strncpy(arena_map->message_arena_full, line + 13, sizeof(arena_map->message_arena_full) - 1);
00299     }
00300     /* Message when you need to join a party to enter */
00301     else if (strncmp(line, "message_party ", 14) == 0)
00302     {
00303         strncpy(arena_map->message_arena_party, line + 13, sizeof(arena_map->message_arena_party) - 1);
00304     }
00305 }
00306 
00312 static void arena_map_parse_script(const char *arena_script, object *exit_ob, arena_maps_struct *arena_map)
00313 {
00314     FILE *fh;
00315     char buf[MAX_BUF], tmp_path[HUGE_BUF];
00316     char *arena_script_path;
00317 
00318     /* Normalize the path to the script, allowing relative paths */
00319     hooks->normalize_path(exit_ob->map->path, arena_script, tmp_path);
00320 
00321     /* Create path name to the script in maps directory */
00322     arena_script_path = hooks->create_pathname(tmp_path);
00323 
00324     /* Initialize defaults */
00325     arena_map->max_players = 0;
00326     arena_map->max_parties = 0;
00327     arena_map->players = 0;
00328     arena_map->parties = 0;
00329     arena_map->flags = ARENA_FLAG_NONE;
00330     strncpy(arena_map->message_arena_full, "Sorry, this arena seems to be full.", sizeof(arena_map->message_arena_full) - 1);
00331     strncpy(arena_map->message_arena_party, "You must be in a party in order to enter this arena.", sizeof(arena_map->message_arena_party) - 1);
00332 
00333     fh = fopen(arena_script_path, "r");
00334 
00335     if (!fh)
00336     {
00337         LOG(llevBug, "Arena: Could not open arena script: %s\n", arena_script_path);
00338         return;
00339     }
00340 
00341     while (fgets(buf, sizeof(buf), fh))
00342     {
00343         /* Ignore comments and empty lines */
00344         if (*buf == '#' || *buf == '\n')
00345         {
00346             continue;
00347         }
00348 
00349         /* Remove newline and parse the line */
00350         buf[strlen(buf) - 1] = '\0';
00351         arena_map_parse_line(arena_map, buf);
00352     }
00353 
00354     fclose(fh);
00355 }
00356 
00363 static int arena_full(arena_maps_struct *arena_map)
00364 {
00365     /* Simple case: The map has nothing to do with parties. */
00366     if (!(arena_map->flags & ARENA_FLAG_PARTY) && !(arena_map->flags & ARENA_FLAG_PARTY_PLAYERS) && arena_map->players == arena_map->max_players)
00367     {
00368         return 1;
00369     }
00370     /* Otherwise a party map. */
00371     else if (arena_map->flags & ARENA_FLAG_PARTY)
00372     {
00373         /* If this is party players arena, count in players. */
00374         if (arena_map->flags & ARENA_FLAG_PARTY_PLAYERS && arena_map->players == arena_map->max_players)
00375         {
00376             return 1;
00377         }
00378 
00379         /* Always check for maximum parties, even if this is party players arena. */
00380         if (arena_map->parties == arena_map->max_parties)
00381         {
00382             return 1;
00383         }
00384     }
00385 
00386     return 0;
00387 }
00388 
00395 int arena_enter(object *who, object *exit_ob, const char *arena_script)
00396 {
00397     char tmp_path[HUGE_BUF];
00398     arena_maps_struct *arena_maps_tmp;
00399 
00400     /* The exit must have a path */
00401     if (!exit_ob->slaying)
00402     {
00403         return 0;
00404     }
00405 
00406     /* Normalize the map's path */
00407     hooks->normalize_path(exit_ob->map->path, EXIT_PATH(exit_ob), tmp_path);
00408 
00409     /* Go through the list of arenas */
00410     for (arena_maps_tmp = arena_maps; arena_maps_tmp; arena_maps_tmp = arena_maps_tmp->next)
00411     {
00412         /* If the exit's path matches this arena */
00413         if (!strcmp(arena_maps_tmp->path, tmp_path))
00414         {
00415             /* If the arena is full, show a message to the player */
00416             if (arena_full(arena_maps_tmp))
00417             {
00418                 hooks->new_draw_info(0, COLOR_WHITE, who, arena_maps_tmp->message_arena_full);
00419                 return 1;
00420             }
00421             /* Not full but it's party arena and the player is not in a party? */
00422             else if (arena_maps_tmp->flags & ARENA_FLAG_PARTY && !CONTR(who)->party)
00423             {
00424                 hooks->new_draw_info(0, COLOR_WHITE, who, arena_maps_tmp->message_arena_party);
00425                 return 1;
00426             }
00427             /* Add the player to the list of players and increase the number of players/parties */
00428             else
00429             {
00430                 arena_map_players *player_list_tmp = (arena_map_players *) malloc(sizeof(arena_map_players));
00431 
00432                 /* For party arenas, also increase the parties count */
00433                 if (arena_maps_tmp->flags & ARENA_FLAG_PARTY)
00434                 {
00435                     arena_map_players *player_list_party;
00436                     int new_party = 1;
00437 
00438                     /* Loop through the player list */
00439                     for (player_list_party = arena_maps_tmp->player_list; player_list_party; player_list_party = player_list_party->next)
00440                     {
00441                         /* If we found a match for this party number, do not increase the count */
00442                         if (CONTR(who)->party && CONTR(who)->party == CONTR(player_list_party->op)->party)
00443                         {
00444                             new_party = 0;
00445                             break;
00446                         }
00447                     }
00448 
00449                     /* Increase the count, if this is a new party in the arena */
00450                     if (new_party)
00451                     {
00452                         arena_maps_tmp->parties++;
00453                     }
00454                 }
00455 
00456                 /* Increase the number of players */
00457                 arena_maps_tmp->players++;
00458 
00459                 player_list_tmp->next = arena_maps_tmp->player_list;
00460                 arena_maps_tmp->player_list = player_list_tmp;
00461 
00462                 /* Store the player object */
00463                 player_list_tmp->op = who;
00464                 return 0;
00465             }
00466         }
00467     }
00468 
00469     /* If we are here, the arena doesn't have an entry in the linked list -- create it */
00470     arena_maps_tmp = (arena_maps_struct *) malloc(sizeof(arena_maps_struct));
00471     strncpy(arena_maps_tmp->path, tmp_path, sizeof(arena_maps_tmp->path) - 1);
00472 
00473     /* Parse script options */
00474     arena_map_parse_script(arena_script, exit_ob, arena_maps_tmp);
00475 
00476     /* If this arena is full, show a message and return */
00477     if (arena_full(arena_maps_tmp))
00478     {
00479         hooks->new_draw_info(0, COLOR_WHITE, who, arena_maps_tmp->message_arena_full);
00480         free(arena_maps_tmp);
00481         return 1;
00482     }
00483     /* Otherwise if not full and the player is not in party */
00484     else if (arena_maps_tmp->flags & ARENA_FLAG_PARTY && !CONTR(who)->party)
00485     {
00486         hooks->new_draw_info(0, COLOR_WHITE, who, arena_maps_tmp->message_arena_party);
00487         free(arena_maps_tmp);
00488         return 1;
00489     }
00490 
00491     /* Add this player to the player count */
00492     arena_maps_tmp->players++;
00493 
00494     /* Add to the party count, if this is party arena */
00495     if (arena_maps_tmp->flags & ARENA_FLAG_PARTY)
00496     {
00497         arena_maps_tmp->parties++;
00498     }
00499 
00500     /* Make a list of players in this arena */
00501     arena_maps_tmp->player_list = (arena_map_players *) malloc(sizeof(arena_map_players));
00502 
00503     /* Store the player */
00504     arena_maps_tmp->player_list->op = who;
00505 
00506     arena_maps_tmp->player_list->next = NULL;
00507 
00508     arena_maps_tmp->next = arena_maps;
00509     arena_maps = arena_maps_tmp;
00510 
00511     return 0;
00512 }
00513 
00519 int arena_sign(object *who, const char *path)
00520 {
00521     arena_maps_struct *arena_maps_tmp;
00522 
00523     /* Sanity check */
00524     if (!path || path[0] == '\0')
00525     {
00526         return 1;
00527     }
00528 
00529     for (arena_maps_tmp = arena_maps; arena_maps_tmp; arena_maps_tmp = arena_maps_tmp->next)
00530     {
00531         /* If the path matches */
00532         if (!strcmp(arena_maps_tmp->path, path) && arena_maps_tmp->player_list)
00533         {
00534             arena_map_players *player_list_tmp;
00535 
00536             hooks->new_draw_info(0, COLOR_YELLOW, who, "This arena has the following players in:\n");
00537 
00538             /* Now go through the list of players in this arena */
00539             for (player_list_tmp = arena_maps_tmp->player_list; player_list_tmp; player_list_tmp = player_list_tmp->next)
00540             {
00541                 hooks->new_draw_info_format(0, COLOR_YELLOW, who, "%s (level %d)", player_list_tmp->op->name, player_list_tmp->op->level);
00542             }
00543 
00544             if (!(arena_maps_tmp->flags & ARENA_FLAG_PARTY) || (arena_maps_tmp->flags & ARENA_FLAG_PARTY && arena_maps_tmp->flags & ARENA_FLAG_PARTY_PLAYERS))
00545             {
00546                 hooks->new_draw_info_format(0, COLOR_YELLOW, who, "\nTotal players: %d\nMaximum players:  %d", arena_maps_tmp->players, arena_maps_tmp->max_players);
00547             }
00548 
00549             if (arena_maps_tmp->flags & ARENA_FLAG_PARTY)
00550             {
00551                 hooks->new_draw_info_format(0, COLOR_YELLOW, who, "\nTotal parties: %d\nMaximum parties:  %d", arena_maps_tmp->parties, arena_maps_tmp->max_parties);
00552             }
00553 
00554             return 1;
00555         }
00556     }
00557 
00558     hooks->new_draw_info(0, COLOR_YELLOW, who, "This arena is currently empty.");
00559     return 1;
00560 }
00561 
00565 static int arena_event(object *who, object *exit_ob, const char *event_options, const char *arena_script)
00566 {
00567     /* If the first 5 characters are "sign|", this is an arena sign */
00568     if (event_options && !strncmp(event_options, "sign|", 5))
00569     {
00570         event_options += 5;
00571         return arena_sign(who, event_options);
00572     }
00573     /* Otherwise arena entrance */
00574     else
00575     {
00576         return arena_enter(who, exit_ob, arena_script);
00577     }
00578 }
00579 
00585 static int arena_leave(object *who)
00586 {
00587     arena_maps_struct *arena_maps_tmp;
00588 
00589     /* Sanity checks */
00590     if (!who || !who->map || !who->map->path || !who->name)
00591     {
00592         return 0;
00593     }
00594 
00595     /* Go through the list of arenas */
00596     for (arena_maps_tmp = arena_maps; arena_maps_tmp; arena_maps_tmp = arena_maps_tmp->next)
00597     {
00598         /* If it matches, and the player really is in the arena */
00599         if (!strcmp(arena_maps_tmp->path, who->map->path) && check_arena_player(who, arena_maps_tmp->player_list))
00600         {
00601             /* If this is party arena, we will want to see if we have to decrease the parties count */
00602             if (arena_maps_tmp->flags & ARENA_FLAG_PARTY)
00603             {
00604                 arena_map_players *player_list_party;
00605                 int do_remove = 1;
00606 
00607                 /* Loop through the player list for this map */
00608                 for (player_list_party = arena_maps_tmp->player_list; player_list_party; player_list_party = player_list_party->next)
00609                 {
00610                     /* If the party number matches, we're not going to remove this party */
00611                     if (player_list_party->op != who && CONTR(who)->party && CONTR(who)->party == CONTR(player_list_party->op)->party)
00612                     {
00613                         do_remove = 0;
00614                         break;
00615                     }
00616                 }
00617 
00618                 /* Removing the party? Then decrease the count. */
00619                 if (do_remove)
00620                 {
00621                     arena_maps_tmp->parties--;
00622                 }
00623             }
00624 
00625             /* Decrease the count of players */
00626             arena_maps_tmp->players--;
00627 
00628             /* Remove the player from this the arena's player list */
00629             remove_arena_player(who, &arena_maps_tmp->player_list);
00630             return 0;
00631         }
00632     }
00633 
00634     return 0;
00635 }
00636 
00637 MODULEAPI void *triggerEvent(int *type, ...)
00638 {
00639     object *activator, *who, *other, *event;
00640     va_list args;
00641     int eventcode, event_type;
00642     static int result = 0;
00643 
00644     va_start(args, type);
00645     event_type = va_arg(args, int);
00646     eventcode = va_arg(args, int);
00647     LOG(llevDebug, "Arena: triggerEvent(): eventcode %d\n", eventcode);
00648 
00649     activator = va_arg(args, object *);
00650 
00651     if (event_type == PLUGIN_EVENT_NORMAL)
00652     {
00653         switch (eventcode)
00654         {
00655             case EVENT_APPLY:
00656             case EVENT_TRIGGER:
00657             {
00658                 char *text, *script, *options;
00659                 int parm1, parm2, parm3, parm4;
00660 
00661                 who = va_arg(args, object *);
00662                 other = va_arg(args, object *);
00663                 event = va_arg(args, object *);
00664                 text = va_arg(args, char *);
00665                 parm1 = va_arg(args, int);
00666                 parm2 = va_arg(args, int);
00667                 parm3 = va_arg(args, int);
00668                 parm4 = va_arg(args, int);
00669                 script = va_arg(args, char *);
00670                 options = va_arg(args, char *);
00671 
00672                 (void) other;
00673                 (void) event;
00674                 (void) text;
00675                 (void) parm1;
00676                 (void) parm2;
00677                 (void) parm3;
00678                 (void) parm4;
00679 
00680                 result = arena_event(activator, who, options, script);
00681                 break;
00682             }
00683         }
00684     }
00685     else if (event_type == PLUGIN_EVENT_MAP)
00686     {
00687         switch (eventcode)
00688         {
00689             case MEVENT_LEAVE:
00690                 result = arena_leave(activator);
00691                 break;
00692         }
00693     }
00694     else if (event_type == PLUGIN_EVENT_GLOBAL)
00695     {
00696         switch (eventcode)
00697         {
00698             case GEVENT_PLAYER_DEATH:
00699             case GEVENT_LOGOUT:
00700                 result = arena_leave(activator);
00701                 break;
00702         }
00703     }
00704 
00705     va_end(args);
00706     return &result;
00707 }
00708