/* Capcture The Flag A game in which a "flag" object is created next to a specified object. The goal is to tractor this object to one of the specified "goal objects". Usage: [-i ] [-flag_opm_name ] A command "ctf" is provided by this plugin, it allows you to start a new game, end a game, or check the game's status. Type `ctf -help' in game for a list of commands. */ #include #include #include #include #include #include "../include/swserv-plugins.h" #define PROG_NAME "CTF" #define PROG_NAME_FULL "Capture The Flag" #define CMD_NAME "ctf" #define CTF_DEF_FLAG_OPM_NAME "Satelite" #define CTF_DEF_UPDATE_INT 3 /* In seconds */ #define CTF_STATUS_NOT_ACTIVE 0 /* Game not active */ #define CTF_STATUS_ACTIVE 1 #define CTF_STATUS_FINISHED 2 typedef struct { int status; /* One of CTF_STATUS_* */ time_t next_update, /* All these are in seconds */ update_int; char *flag_opm_name; int flag_obj_num, flag_holder_obj_num, /* Object that has the flag */ game_master_obj_num; /* Can be -1 */ int *goal_obj; int ngoal_objs; SWServContext *ctx; } Core; #define CORE(p) ((Core *)(p)) static void CoreDelete(Core *core); static void CTFStartGame( Core *core, const int start_obj_num, const int game_master_obj_num, const int *goal_obj, const int ngoal_objs ); static void CTFEndGame( Core *core, const int goal_obj_num ); static void CTFHandleCommand( Core *core, const int con_num, const int obj_num, const char *arg ); static void CTFManageFlagStatus(Core *core); static void CTFMainIteration(Core *core); #define ATOI(s) (((s) != NULL) ? atoi(s) : 0) #define ATOL(s) (((s) != NULL) ? atol(s) : 0) #define ATOF(s) (((s) != NULL) ? atof(s) : 0.0f) #define STRDUP(s) (((s) != NULL) ? strdup(s) : NULL) #define MAX(a,b) (((a) > (b)) ? (a) : (b)) #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #define CLIP(a,l,h) (MIN(MAX((a),(l)),(h))) #define STRLEN(s) (((s) != NULL) ? (int)strlen(s) : 0) #define STRISEMPTY(s) (((s) != NULL) ? (*(s) == '\0') : 1) #define USAGE_MESG "\ Usage: [-i ] [-flag_opm_name ]" #define STRCASEPFX(_s_,_pfx_) (!strncmp((_s_),(_pfx_),strlen(_pfx_))) #define ISBLANK(_c_) (((_c_) == ' ') || ((_c_) == '\t')) /* * Notifies all connections that are logged in as the specified * object. */ static void CTFNotifyObject( Core *core, const int obj_num, const char *msg ) { SWServContext *ctx = core->ctx; const int m = SWServTotalConnections(ctx); int i; for(i = 0; i < m; i++) { if(obj_num == SWServConGetObjNum(ctx, i)) SWServConNotify(ctx, i, msg); } } /* * Deletes the core. */ static void CoreDelete(Core *core) { SWServContext *ctx; if(core == NULL) return; ctx = core->ctx; SWServObjectRecycle(ctx, core->flag_obj_num); free(core->flag_opm_name); free(core->goal_obj); free(core); } /* * Starts (or restarts) the game. * * Creates a new flag object based on the specified OPM name. * * Moves the flag object to the specified start object. * * Sets the game master object. * * Sets the specified goal object(s). * * Announces the start of the game to all connections. */ static void CTFStartGame( Core *core, const int start_obj_num, const int game_master_obj_num, const int *goal_obj, const int ngoal_objs ) { const char *flag_opm_name; char *s; int obj_num, opm_num; xsw_object_struct *obj, *start_obj, *master_obj; SWServContext *ctx; if(core == NULL) return; ctx = core->ctx; /* Set the game master object */ core->game_master_obj_num = game_master_obj_num; master_obj = SWServObjectGetPointer(ctx, game_master_obj_num); /* Get the start object */ start_obj = SWServObjectGetPointer(ctx, start_obj_num); if(start_obj == NULL) start_obj = master_obj; /* Get the flag OPM name */ flag_opm_name = core->flag_opm_name; if(flag_opm_name == NULL) flag_opm_name = CTF_DEF_FLAG_OPM_NAME; /* Get the flag OPM */ opm_num = SWServOPMGetByName(ctx, flag_opm_name); if(opm_num > -1) { /* Create the flag object */ core->flag_obj_num = obj_num = SWServObjectCreate( ctx, SW_OBJ_TYPE_DYNAMIC ); obj = SWServObjectGetPointer(ctx, obj_num); if(obj != NULL) { /* Model the new flag object with the flag OPM */ SWServOPMModelObject(ctx, opm_num, obj_num); obj->owner = game_master_obj_num; if(start_obj != NULL) { const float offset = MAX( ((float)start_obj->size / 1000.0f), 0.0f ) * 1.10f; obj->sect_x = start_obj->sect_x; obj->sect_y = start_obj->sect_y; obj->sect_z = start_obj->sect_z; obj->x = start_obj->x + offset; obj->y = start_obj->y + offset; obj->z = start_obj->z; obj->permission.uid = 1; /* So it can't be destroyed */ obj->permission.gid = 1; strncpy(obj->name, "Flag", sizeof(obj->name)); obj->name[sizeof(obj->name) - 1] = '\0'; strncpy(obj->empire, start_obj->empire, sizeof(obj->empire)); obj->empire[sizeof(obj->empire) - 1] = '\0'; } } } else { /* No such OPM */ s = (char *)malloc( STRLEN(PROG_NAME) + STRLEN(flag_opm_name) + 80 ); sprintf( s, "%s: %s: Warning: No such OPM.", PROG_NAME, flag_opm_name ); CTFNotifyObject(core, game_master_obj_num, s); free(s); } /* Set the goal objects */ free(core->goal_obj); core->goal_obj = NULL; core->ngoal_objs = 0; if((ngoal_objs > 0) && (goal_obj != NULL)) { core->ngoal_objs = ngoal_objs; core->goal_obj = (int *)malloc(ngoal_objs * sizeof(int)); memcpy(core->goal_obj, goal_obj, ngoal_objs * sizeof(int)); } /* Set the game status to "active" */ core->status = CTF_STATUS_ACTIVE; /* Announce the start of the game to all connections */ obj = SWServObjectGetPointer(ctx, core->flag_obj_num); if(obj != NULL) { s = (char *)malloc( STRLEN(obj->name) + 80 ); sprintf( s, "*** Begin Capture The `%s'! ***", obj->name ); SWServConNotify(ctx, -1, s); free(s); } else { s = (char *)malloc(STRLEN(PROG_NAME_FULL) + 80); sprintf( s, "*** Begin %s! ***", PROG_NAME_FULL ); SWServConNotify(ctx, -1, s); free(s); } } /* * Ends the game. */ static void CTFEndGame( Core *core, const int goal_obj_num ) { char *s; xsw_object_struct *goal_obj, *flag_obj; SWServContext *ctx; if(core == NULL) return; ctx = core->ctx; flag_obj = SWServObjectGetPointer(ctx, core->flag_obj_num); /* Was the flag brought to a goal? */ goal_obj = SWServObjectGetPointer(ctx, goal_obj_num); if((goal_obj != NULL) && (flag_obj != NULL)) { xsw_object_struct *winner_obj = SWServObjectGetPointer( ctx, core->flag_holder_obj_num ); if(winner_obj != NULL) { s = (char *)malloc( STRLEN(flag_obj->name) + STRLEN(goal_obj->empire) + STRLEN(winner_obj->name) + (2 * 80) ); sprintf( s, "*** The `%s' has been brought to the goal of the %s empire by `%s'! ***", flag_obj->name, goal_obj->empire, winner_obj->name ); } else { s = (char *)malloc( STRLEN(flag_obj->name) + STRLEN(goal_obj->empire) + (2 * 80) ); sprintf( s, "*** The `%s' has been brought to the goal of the %s empire! ***", flag_obj->name, goal_obj->empire ); } SWServConNotify(ctx, -1, s); free(s); } else if(flag_obj != NULL) { s = (char *)malloc(STRLEN(flag_obj->name) + 80); sprintf( s, "*** Capture The `%s' Has Ended! ***", flag_obj->name ); SWServConNotify(ctx, -1, s); free(s); } else { SWServConNotify( ctx, -1, "*** " PROG_NAME_FULL " Has Ended! ***" ); } /* Recycle the flag object */ SWServObjectRecycle(ctx, core->flag_obj_num); core->flag_obj_num = -1; flag_obj = NULL; /* Reset the flag holder */ core->flag_holder_obj_num = -1; /* Delete the goal object lists */ free(core->goal_obj); core->goal_obj = NULL; core->ngoal_objs = 0; /* Set the game status to "finished" */ core->status = CTF_STATUS_FINISHED; } /* * Handles the specified command's argument. */ static void CTFHandleCommand( Core *core, const int con_num, const int obj_num, const char *arg ) { SWServContext *ctx; if(core == NULL) return; ctx = core->ctx; /* If no argument is specified then print status */ if(STRISEMPTY(arg)) { char *s; xsw_object_struct *obj; switch(core->status) { case CTF_STATUS_ACTIVE: obj = SWServObjectGetPointer(ctx, core->flag_holder_obj_num); if(obj != NULL) { s = (char *)malloc(STRLEN(obj->name) + 40 + (2 * 80)); sprintf( s, "Status: Active Flag Holder: %s At: %ld,%ld,%ld %.2f,%.2f,%.2f", obj->name, obj->sect_x, obj->sect_y, obj->sect_z, obj->x, obj->y, obj->z ); SWServConNotify(ctx, con_num, s); free(s); } else { s = (char *)malloc(40 + 80); sprintf( s, "Status: Active Flag Holder: %s", "*none*" ); SWServConNotify(ctx, con_num, s); free(s); } if(core->ngoal_objs > 0) { const int m = core->ngoal_objs; int i; SWServConNotify( ctx, con_num, "Goal Objects:" ); for(i = 0; i < m; i++) { obj = SWServObjectGetPointer(ctx, core->goal_obj[i]); if(obj == NULL) continue; s = (char *)malloc(STRLEN(obj->name) + 80); sprintf( s, " %s", obj->name ); SWServConNotify(ctx, con_num, s); free(s); } } break; case CTF_STATUS_FINISHED: SWServConNotify( ctx, con_num, "Status: Finished" ); s = (char *)malloc(STRLEN(CMD_NAME) + 80); sprintf( s, "Type `%s help' for help.", CMD_NAME ); SWServConNotify(ctx, con_num, s); free(s); break; default: SWServConNotify( ctx, con_num, "Status: Not Active" ); s = (char *)malloc(STRLEN(CMD_NAME) + 80); sprintf( s, "Type `%s help' for help.", CMD_NAME ); SWServConNotify(ctx, con_num, s); free(s); break; } return; } /* Help */ if(STRCASEPFX(arg, "help") || STRCASEPFX(arg, "h") || STRCASEPFX(arg, "--help") || STRCASEPFX(arg, "-help") || STRCASEPFX(arg, "--h") || STRCASEPFX(arg, "-h") || STRCASEPFX(arg, "-?") || STRCASEPFX(arg, "?") ) { char *s = (char *)malloc(STRLEN(CMD_NAME) + (2 * 80)); sprintf( s, "Usage: `%s start \ [goal#_obj_num...]", CMD_NAME ); SWServConNotify(ctx, con_num, s); free(s); s = (char *)malloc(STRLEN(CMD_NAME) + (2 * 80)); sprintf( s, "- `%s end'", CMD_NAME ); SWServConNotify(ctx, con_num, s); free(s); s = (char *)malloc(STRLEN(CMD_NAME) + (2 * 80)); sprintf( s, "- `%s flag_opm '", CMD_NAME ); SWServConNotify(ctx, con_num, s); free(s); s = (char *)malloc(STRLEN(CMD_NAME) + (2 * 80)); sprintf( s, "Type `%s' by itself to get the current game status.", CMD_NAME ); SWServConNotify(ctx, con_num, s); free(s); } /* Start */ else if(STRCASEPFX(arg, "start")) { const char *s = arg; int i, start_obj_num; int *goal_obj = NULL; int ngoal_objs = 0; /* Game already in progress? */ if(core->status == CTF_STATUS_ACTIVE) { SWServConNotify( ctx, con_num, "A game is already in progress." ); return; } while((*s != '\0') && !ISBLANK(*s)) s++; while(ISBLANK(*s)) s++; /* Get the start object */ if(*s == '#') s++; start_obj_num = ATOI(s); while((*s != '\0') && !ISBLANK(*s)) s++; while(ISBLANK(*s)) s++; /* Get the goal object(s) */ while(*s != '\0') { i = ngoal_objs; ngoal_objs = i + 1; goal_obj = (int *)realloc( goal_obj, ngoal_objs * sizeof(int) ); if(*s == '#') s++; goal_obj[i] = ATOI(s); while((*s != '\0') && !ISBLANK(*s)) s++; while(ISBLANK(*s)) s++; } /* Start the game */ CTFStartGame( core, start_obj_num, /* Starting object */ obj_num, /* Game master */ goal_obj, ngoal_objs /* Goal objects */ ); free(goal_obj); } /* End */ else if(STRCASEPFX(arg, "end")) { const char *s = arg; /* No game in progress? */ if(core->status != CTF_STATUS_ACTIVE) { SWServConNotify( ctx, con_num, "There is no game currently in progress." ); return; } /* Not the game master? */ if(core->game_master_obj_num != obj_num) { SWServConNotify( ctx, con_num, "You are not the current game master." ); return; } while((*s != '\0') && !ISBLANK(*s)) s++; while(ISBLANK(*s)) s++; /* End the game */ CTFEndGame( core, -1 /* No goal object */ ); } /* Flag OPM */ else if(STRCASEPFX(arg, "flag_opm")) { const char *s = arg; /* Game already in progress? */ if(core->status != CTF_STATUS_NOT_ACTIVE) { SWServConNotify( ctx, con_num, "A game is already in progress." ); return; } while((*s != '\0') && !ISBLANK(*s)) s++; while(ISBLANK(*s)) s++; /* Get flag OPM name */ free(core->flag_opm_name); core->flag_opm_name = STRDUP(s); if(core->flag_opm_name != NULL) { char *s = (char *)malloc( STRLEN(CMD_NAME) + STRLEN(core->flag_opm_name) + 80 ); sprintf( s, "%s: `flag_opm' set to `%s'.", CMD_NAME, core->flag_opm_name ); SWServConNotify(ctx, con_num, s); free(s); } } } /* * Checks if an object has obtained the flag and reports it to * all connections. * * Checks if the flag has been moved to a goal object. */ static void CTFManageFlagStatus(Core *core) { int flag_obj_num; xsw_object_struct *obj, *flag_obj; SWServContext *ctx; if(core == NULL) return; ctx = core->ctx; /* No flag? */ flag_obj_num = core->flag_obj_num; if(flag_obj_num < 0) return; /* Check if the flag has reached a goal object */ flag_obj = SWServObjectGetPointer(ctx, flag_obj_num); if(flag_obj != NULL) { const int m = core->ngoal_objs; int i, goal_obj_num; for(i = 0; i < m; i++) { /* Reached this goal object? */ goal_obj_num = core->goal_obj[i]; if(SWServObjectsInContact( ctx, flag_obj_num, goal_obj_num )) { CTFEndGame( core, goal_obj_num ); return; } } } /* No one has the flag? */ if(core->flag_holder_obj_num < 0) { /* Check if an object has picked up the flag */ const int m = SWServTotalObjects(ctx); int i; for(i = 0; i < m; i++) { obj = SWServObjectGetPointer(ctx, i); if(obj == NULL) continue; /* Check if this object is tractoring the flag object */ if(obj->total_tractored_objects > 0) { const int m = obj->total_tractored_objects; int j; for(j = 0; j < m; j++) { if(obj->tractored_object[j] == flag_obj_num) { /* This object is tractoring the flag * object, update and announce the flag * holder object */ core->flag_holder_obj_num = i; if(flag_obj != NULL) { char *s = (char *)malloc( STRLEN(obj->name) + STRLEN(flag_obj->name) + 80 ); sprintf( s, "*** `%s' has the `%s'! ***", obj->name, flag_obj->name ); SWServConNotify(ctx, -1, s); free(s); } break; } } } /* Got a flag holder object in the above check(s)? */ if(core->flag_holder_obj_num > -1) break; } } else { /* Check if the flag hold still has the flag */ char has_flag = 0; obj = SWServObjectGetPointer(ctx, core->flag_holder_obj_num); if(obj != NULL) { if(obj->total_tractored_objects > 0) { const int m = obj->total_tractored_objects; int i; for(i = 0; i < m; i++) { if(obj->tractored_object[i] == flag_obj_num) { has_flag = 1; break; } } } } /* No longer has flag? */ if(!has_flag) core->flag_holder_obj_num = -1; } } /* * CTF main iteration. */ static void CTFMainIteration(Core *core) { if(core == NULL) return; switch(core->status) { case CTF_STATUS_ACTIVE: CTFManageFlagStatus(core); break; } } /* * SWServ Plugin help commands list callback. */ SWPLUGIN_HELP_COMMANDS_LIST_FUNCRTN SWPLUGIN_HELP_COMMANDS_LIST_FUNC( int *ncommands, SWServContext *ctx ) { static char *help_list[] = { CMD_NAME }; if(ncommands != NULL) *ncommands = sizeof(help_list) / sizeof(char *); return(help_list); } /* * SWServ Plugin command callback. */ SWPLUGIN_COMMAND_FUNCRTN SWPLUGIN_COMMAND_FUNC( const char *cmd, const char *arg, int con_num, int obj_num, int uid, int gid, SWServContext *ctx ) { int status = 0; Core *core = CORE(SWServGetData(ctx)); if((cmd == NULL) || (arg == NULL) || (core == NULL)) return(status); if(!strcmp(cmd, CMD_NAME)) { CTFHandleCommand(core, con_num, obj_num, arg); status = 1; } return(status); } /* * SWServ Plugin destroy notify. */ SWPLUGIN_DESTROY_NOTIFY_FUNCRTN SWPLUGIN_DESTROY_NOTIFY_FUNC( int reason, int destroyed_obj_num, int destroyer_obj_num, int destroyer_obj_owner_num, SWServContext *ctx ) { Core *core = CORE(SWServGetData(ctx)); if((destroyed_obj_num < 0) || (core == NULL)) return; /* Flag got destroyed? */ if(destroyed_obj_num == core->flag_obj_num) { xsw_object_struct *flag_obj = SWServObjectGetPointer( ctx, core->flag_obj_num ), *destroyer_obj_owner = SWServObjectGetPointer( ctx, destroyer_obj_owner_num ); core->flag_obj_num = -1; /* Do we know who destroyed the flag? */ if((flag_obj != NULL) && (destroyer_obj_owner != NULL) ) { /* Announce who destroyed the flag */ char *s = (char *)malloc( STRLEN(destroyer_obj_owner->name) + STRLEN(flag_obj->name) + 80 ); sprintf( s, "*** `%s' destroyed the `%s'! ***", destroyer_obj_owner->name, flag_obj->name ); SWServConNotify(ctx, -1, s); free(s); } CTFEndGame( core, -1 /* Goal object reached */ ); } /* Flag holder got destroyed? */ else if(destroyed_obj_num == core->flag_holder_obj_num) { core->flag_holder_obj_num = -1; /* Do not report anything */ } /* Game master got destroyed? */ else if(destroyed_obj_num == core->game_master_obj_num) { core->game_master_obj_num = -1; CTFEndGame( core, -1 /* Goal object reached */ ); } } /* * SWServ Plugin initialize. */ SWPLUGIN_INIT_FUNCRTN SWPLUGIN_INIT_FUNC( int argc, char **argv, int con_num, SWServContext *ctx ) { int i; const char *arg; Core *core = CORE(calloc(1, sizeof(Core))); if(core == NULL) return(1); core->status = CTF_STATUS_NOT_ACTIVE; core->update_int = CTF_DEF_UPDATE_INT; core->flag_opm_name = STRDUP(CTF_DEF_FLAG_OPM_NAME); core->flag_obj_num = -1; core->flag_holder_obj_num = -1; core->game_master_obj_num = SWServConGetObjNum(ctx, con_num); core->goal_obj = NULL; core->ngoal_objs = 0; core->ctx = ctx; for(i = 0; i < argc; i++) { arg = argv[i]; if(arg == NULL) continue; if(!strcasecmp(arg, "--help") || !strcasecmp(arg, "-help") || !strcasecmp(arg, "--h") || !strcasecmp(arg, "-h") ) { if(con_num > -1) SWServConNotify( ctx, con_num, USAGE_MESG ); else SWServPrint( ctx, USAGE_MESG "\n" ); CoreDelete(core); return(1); } /* Interval */ else if(!strcasecmp(arg, "--interval") || !strcasecmp(arg, "-interval") || !strcasecmp(arg, "--int") || !strcasecmp(arg, "-int") || !strcasecmp(arg, "--i") || !strcasecmp(arg, "-i") ) { i++; arg = (i < argc) ? argv[i] : NULL; if(arg != NULL) { core->update_int = MAX(ATOL(arg), 1l); } } /* Flag OPM */ else if(!strcasecmp(arg, "--flag_opm_name") || !strcasecmp(arg, "-flag_opm_name") || !strcasecmp(arg, "--flag_opm") || !strcasecmp(arg, "-flag_opm") ) { i++; arg = (i < argc) ? argv[i] : NULL; if(arg != NULL) { free(core->flag_opm_name); core->flag_opm_name = STRDUP(arg); } } } /* Schedual initial updates */ core->next_update = SWServCurrentTimeSeconds(ctx); SWServSetData(ctx, core); return(0); } /* * SWServ Plugin manage. */ SWPLUGIN_MANAGE_FUNCRTN SWPLUGIN_MANAGE_FUNC( SWServContext *ctx ) { Core *core = CORE(SWServGetData(ctx)); const time_t cur_sec = SWServCurrentTimeSeconds(ctx); if(core == NULL) return(1); if(core->next_update <= cur_sec) { CTFMainIteration(core); core->next_update = cur_sec + core->update_int; } return(0); } /* * SWServ Plugin shutdown. */ SWPLUGIN_SHUTDOWN_FUNCRTN SWPLUGIN_SHUTDOWN_FUNC( SWServContext *ctx ) { Core *core = CORE(SWServGetData(ctx)); if(core == NULL) return; CoreDelete(core); SWServSetData(ctx, NULL); }