//QUAKED trigger_counter (.5 .5 .5) ? NOMESSAGE NOSOUNDS //Acts as an intermediary for an action that takes multiple inputs. Example: a sequence of several buttons to activate a event //-------- KEYS -------- //target : this points to the entity to activate. //targetname : activating trigger points to this. //count : number of actions to count (default 2) //noise_start : sound to play each time a event happens //noise_stop : sound to play at the last event in the count //notsingle : when set to 1, entity will not spawn in Single Player mode //notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes. //notduel : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo) //notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. //notctf : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jaltodo) //-------- SPAWNFLAGS -------- //NOMESSAGE : &1 if not set, it will print "1 more.. " etc when triggered and "sequence complete" when finished. //NOSOUNDS : &2 if not set, it will try to play the noise_start and noise_stop sounds //-------- NOTES -------- //Sounds like this one should be a target and not a trigger, but well... static void trigger_counter_use( edict_t *self, edict_t *other, edict_t *activator ) { if( self->count == 0 ) return; self->count--; if( self->count ) { if( !( self->spawnflags & 1 ) ) G_CenterPrintMsg( activator, "%i more to go...", self->count ); if( !( self->spawnflags & 2 ) ) G_Sound( activator, CHAN_AUTO, self->moveinfo.sound_start, ATTN_NORM ); return; } if( !( self->spawnflags & 1 ) ) G_CenterPrintMsg( activator, "Sequence completed!" ); if( !( self->spawnflags & 2 ) ) G_Sound( activator, CHAN_AUTO, self->moveinfo.sound_end, ATTN_NORM ); self->activator = activator; multi_trigger( self ); }
//========================================== // AITools_ShowPlinks // Draws lines from the current node to it's plinks nodes //========================================== static void AITools_ShowPlinks( edict_t *target ) { static unsigned int debugdrawplinks_timeout; int current_node; int plink_node; int i; nav_ents_t *goalEnt; if( !target || !target->r.client || !target->r.client->level.showPLinks ) return; //do it current_node = AI_FindClosestReachableNode( target->s.origin, target, NODE_DENSITY * 3, NODE_ALL ); // draw the axis where the node is if( nodes[current_node].flags & NODEFLAGS_SERVERLINK ) AITools_DrawAxis( nodes[current_node].origin, COLOR_RGBA( 255, 25, 25, 255 ) ); else AITools_DrawAxis( nodes[current_node].origin, COLOR_RGBA( 210, 250, 250, 255 ) ); //don't draw the links every frame (flood) if( level.time < debugdrawplinks_timeout ) return; debugdrawplinks_timeout = level.time + 4 * game.snapFrameTime; if( nav.editmode || !nav.loaded ) return; FOREACH_GOALENT( goalEnt ) { i = goalEnt->id; if( goalEnt->node == current_node ) { if( !goalEnt->ent->classname ) G_CenterPrintMsg( target, "no classname" ); else G_CenterPrintMsg( target, "%s", goalEnt->ent->classname ); break; } } // no links to draw if( !pLinks[current_node].numLinks ) return; for( i = 0; i < pLinks[current_node].numLinks; i++ ) { plink_node = pLinks[current_node].nodes[i]; if( pLinks[current_node].moveType[i] == LINK_ROCKETJUMP ) AITools_DrawColorLine( nodes[current_node].origin, nodes[plink_node].origin, COLOR_RGBA( 0xff, 0x00, 0x00, 0x80 ), 0 ); else if( pLinks[current_node].moveType[i] == LINK_JUMP ) AITools_DrawColorLine( nodes[current_node].origin, nodes[plink_node].origin, COLOR_RGBA( 0x00, 0x00, 0xff, 0x80 ), 0 ); else AITools_DrawColorLine( nodes[current_node].origin, nodes[plink_node].origin, COLOR_RGBA( 0x00, 0xff, 0x00, 0x80 ), 0 ); } }
/* * G_Teams_ExecuteChallengersQueue */ void G_Teams_ExecuteChallengersQueue( void ) { edict_t *ent; edict_t **challengers; bool restartmatch = false; // Medar fixme: this is only really makes sense, if playerlimit per team is one if( GS_MatchState() == MATCH_STATE_PLAYTIME ) return; if( !GS_HasChallengers() ) return; if( game.realtime < level.spawnedTimeStamp + G_CHALLENGERS_MIN_JOINTEAM_MAPTIME ) { static int time, lasttime; time = (int)( ( G_CHALLENGERS_MIN_JOINTEAM_MAPTIME - ( game.realtime - level.spawnedTimeStamp ) )*0.001 ); if( lasttime && time == lasttime ) return; lasttime = time; if( lasttime ) G_CenterPrintMsg( NULL, "Waiting... %i", lasttime ); else G_CenterPrintMsg( NULL, "" ); return; } // pick players in join order and try to put them in the // game until we get the first refused one. challengers = G_Teams_ChallengersQueue(); if( challengers ) { int i; for( i = 0; challengers[i]; i++ ) { ent = challengers[i]; if( !G_Teams_JoinAnyTeam( ent, true ) ) break; // if we successfully execute the challengers queue during the countdown, revert to warmup if( GS_MatchState() == MATCH_STATE_COUNTDOWN ) { restartmatch = true; } } } if( restartmatch == true ) { G_Match_Autorecord_Cancel(); G_Match_LaunchState( MATCH_STATE_WARMUP ); } }
static void SP_target_print_print( edict_t *self, edict_t *activator ) { if( self->helpmessage && self->mapmessage_index <= MAX_HELPMESSAGES ) { G_SetPlayerHelpMessage( activator, self->mapmessage_index ); return; } G_CenterPrintMsg( activator, self->message ); }
static void G_Gametype_GENERIC_ThinkRules( void ) { if( G_Match_ScorelimitHit() || G_Match_TimelimitHit() || G_Match_SuddenDeathFinished() ) { G_CenterPrintMsg( NULL, "ENDOOOOOOOOOOOOOOOOO saaaaan!!!!!!!!!!\n" ); G_Match_LaunchState( GS_MatchState() + 1 ); } if( GS_MatchState() >= MATCH_STATE_POSTMATCH ) return; }
/* * G_Match_CheckExtendPlayTime */ bool G_Match_CheckExtendPlayTime( void ) { // check for extended time/sudden death if( GS_MatchState() != MATCH_STATE_PLAYTIME ) return false; if( GS_TeamBasedGametype() && !level.forceExit ) { if( G_Match_Tied() ) { GS_GamestatSetFlag( GAMESTAT_FLAG_MATCHEXTENDED, true ); gs.gameState.stats[GAMESTAT_MATCHSTATE] = MATCH_STATE_PLAYTIME; gs.gameState.longstats[GAMELONG_MATCHSTART] = game.serverTime; if( g_match_extendedtime->value ) { if( !GS_MatchExtended() ) // first one G_AnnouncerSound( NULL, trap_SoundIndex( S_ANNOUNCER_OVERTIME_GOING_TO_OVERTIME ), GS_MAX_TEAMS, true, NULL ); else G_AnnouncerSound( NULL, trap_SoundIndex( S_ANNOUNCER_OVERTIME_OVERTIME ), GS_MAX_TEAMS, true, NULL ); G_PrintMsg( NULL, "Match tied. Timelimit extended by %i minutes!\n", g_match_extendedtime->integer ); G_CenterPrintMsg( NULL, "%i MINUTE OVERTIME\n", g_match_extendedtime->integer ); gs.gameState.longstats[GAMELONG_MATCHDURATION] = (unsigned int)( ( fabs( g_match_extendedtime->value ) * 60 ) * 1000 ); } else { G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_OVERTIME_SUDDENDEATH_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); G_PrintMsg( NULL, "Match tied. Sudden death!\n" ); G_CenterPrintMsg( NULL, "SUDDEN DEATH\n" ); gs.gameState.longstats[GAMELONG_MATCHDURATION] = 0; } return true; } } return false; }
static void SP_target_print_print( edict_t *self, edict_t *activator ) { if( self->spawnflags & 8 ) { G_SetPlayerHelpMessage( activator, 0 ); return; } if( self->mapmessage_index && self->mapmessage_index <= MAX_HELPMESSAGES ) { G_SetPlayerHelpMessage( activator, self->mapmessage_index ); } else if( self->message && self->message[0] ) { G_CenterPrintMsg( activator, self->message ); } }
static void SP_target_print_use( edict_t *self, edict_t *other, edict_t *activator ) { int n; edict_t *player; if( activator->r.client && ( self->spawnflags & 4 ) ) { G_CenterPrintMsg( activator, self->message ); return; } // print to team if( activator->r.client && self->spawnflags & 3 ) { edict_t *e; for( e = game.edicts + 1; PLAYERNUM( e ) < gs.maxclients; e++ ) { if( e->r.inuse && e->s.team ) { if( self->spawnflags & 1 && e->s.team == activator->s.team ) G_CenterPrintMsg( e, self->message ); if( self->spawnflags & 2 && e->s.team != activator->s.team ) G_CenterPrintMsg( e, self->message ); } } return; } for( n = 1; n <= gs.maxclients; n++ ) { player = &game.edicts[n]; if( !player->r.inuse ) continue; G_CenterPrintMsg( player, self->message ); } }
/* * G_CheckEvenTeam */ static void G_CheckEvenTeam( void ) { int max = 0; int min = gs.maxclients + 1; int uneven_team = TEAM_SPECTATOR; int i; if( GS_MatchState() >= MATCH_STATE_POSTMATCH ) return; if( !GS_TeamBasedGametype() ) return; if( g_teams_allow_uneven->integer ) return; for( i = TEAM_ALPHA; i < GS_MAX_TEAMS; i++ ) { if( max < teamlist[i].numplayers ) { max = teamlist[i].numplayers; uneven_team = i; } if( min > teamlist[i].numplayers ) min = teamlist[i].numplayers; } if( max - min > 1 ) { for( i = 0; i < teamlist[uneven_team].numplayers; i++ ) { edict_t *e = game.edicts + teamlist[uneven_team].playerIndices[i]; if( !e->r.inuse ) continue; G_CenterPrintMsg( e, "Teams are uneven. Please switch into another team." ); // FIXME: need more suitable message :P G_PrintMsg( e, "%sTeams are uneven. Please switch into another team.\n", S_COLOR_CYAN ); // FIXME: need more suitable message :P } // FIXME: switch team forcibly? } }
void G_Gametype_GENERIC_SetUpMatch( void ) { int i; level.gametype.readyAnnouncementEnabled = false; level.gametype.scoreAnnouncementEnabled = true; level.gametype.countdownEnabled = true; level.gametype.pickableItemsMask = ( level.gametype.spawnableItemsMask|level.gametype.dropableItemsMask ); if( GS_Instagib() ) level.gametype.pickableItemsMask &= ~G_INSTAGIB_NEGATE_ITEMMASK; // clear player stats and scores, team scores and respawn clients in team lists for( i = TEAM_PLAYERS; i < GS_MAX_TEAMS; i++ ) { int j; g_teamlist_t *team = &teamlist[i]; memset( &team->stats, 0, sizeof( team->stats ) ); // respawn all clients inside the playing teams for( j = 0; j < team->numplayers; j++ ) { edict_t *ent = &game.edicts[ team->playerIndices[j] ]; G_ClientClearStats( ent ); G_ClientRespawn( ent, false ); } } // set items to be spawned with a delay G_Items_RespawnByType( IT_ARMOR, ARMOR_RA, 15 ); G_Items_RespawnByType( IT_ARMOR, ARMOR_RA, 15 ); G_Items_RespawnByType( IT_HEALTH, HEALTH_MEGA, 15 ); G_Items_RespawnByType( IT_HEALTH, HEALTH_ULTRA, 15 ); G_Items_RespawnByType( IT_POWERUP, 0, brandom( 20, 40 ) ); G_Match_FreeBodyQueue(); G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_COUNTDOWN_FIGHT_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, false, NULL ); G_CenterPrintMsg( NULL, "FIGHT!" ); }
/* * G_Match_CheckStateAbort */ static void G_Match_CheckStateAbort( void ) { bool any = false; bool enough; if( GS_MatchState() <= MATCH_STATE_NONE || GS_MatchState() >= MATCH_STATE_POSTMATCH || level.gametype.mathAbortDisabled ) { GS_GamestatSetFlag( GAMESTAT_FLAG_WAITING, false ); return; } if( GS_TeamBasedGametype() ) { int team, emptyteams = 0; for( team = TEAM_ALPHA; team < GS_MAX_TEAMS; team++ ) { if( !teamlist[team].numplayers ) emptyteams++; else any = true; } enough = ( emptyteams == 0 ); } else { enough = ( teamlist[TEAM_PLAYERS].numplayers > 1 ); any = ( teamlist[TEAM_PLAYERS].numplayers > 0 ); } // if waiting, turn on match states when enough players joined if( GS_MatchWaiting() && enough ) { GS_GamestatSetFlag( GAMESTAT_FLAG_WAITING, false ); G_UpdatePlayersMatchMsgs(); } // turn off active match states if not enough players left else if( GS_MatchState() == MATCH_STATE_WARMUP && !enough && GS_MatchDuration() ) { GS_GamestatSetFlag( GAMESTAT_FLAG_WAITING, true ); G_UpdatePlayersMatchMsgs(); } else if( GS_MatchState() == MATCH_STATE_COUNTDOWN && !enough ) { if( any ) { G_PrintMsg( NULL, "Not enough players left. Countdown aborted.\n" ); G_CenterPrintMsg( NULL, "COUNTDOWN ABORTED" ); } G_Match_Autorecord_Cancel(); G_Match_LaunchState( MATCH_STATE_WARMUP ); GS_GamestatSetFlag( GAMESTAT_FLAG_WAITING, true ); G_UpdatePlayersMatchMsgs(); } // match running, but not enough players left else if( GS_MatchState() == MATCH_STATE_PLAYTIME && !enough ) { if( any ) { G_PrintMsg( NULL, "Not enough players left. Match aborted.\n" ); G_CenterPrintMsg( NULL, "MATCH ABORTED" ); } G_EndMatch(); } }
/* * G_Match_CheckReadys */ void G_Match_CheckReadys( void ) { edict_t *e; bool allready; int readys, notreadys, teamsready; int team, i; if( GS_MatchState() != MATCH_STATE_WARMUP && GS_MatchState() != MATCH_STATE_COUNTDOWN ) return; if( GS_MatchState() == MATCH_STATE_COUNTDOWN && level.forceStart ) return; // never stop countdown if we have run out of warmup_timelimit teamsready = 0; for( team = TEAM_PLAYERS; team < GS_MAX_TEAMS; team++ ) { readys = notreadys = 0; for( i = 0; i < teamlist[team].numplayers; i++ ) { e = game.edicts + teamlist[team].playerIndices[i]; if( !e->r.inuse ) continue; if( e->s.team == TEAM_SPECTATOR ) //ignore spectators continue; if( level.ready[PLAYERNUM( e )] ) readys++; else notreadys++; } if( !notreadys && readys ) teamsready++; } // everyone has commited if( GS_TeamBasedGametype() ) { if( teamsready == GS_MAX_TEAMS - TEAM_ALPHA ) allready = true; else allready = false; } else { //ffa if( teamsready && teamlist[TEAM_PLAYERS].numplayers > 1 ) allready = true; else allready = false; } if( allready == true && GS_MatchState() != MATCH_STATE_COUNTDOWN ) { G_PrintMsg( NULL, "All players are ready. Match starting!\n" ); G_Match_LaunchState( MATCH_STATE_COUNTDOWN ); } else if( allready == false && GS_MatchState() == MATCH_STATE_COUNTDOWN ) { G_PrintMsg( NULL, "Countdown aborted.\n" ); G_CenterPrintMsg( NULL, "COUNTDOWN ABORTED" ); G_Match_Autorecord_Cancel(); G_Match_LaunchState( MATCH_STATE_WARMUP ); } }
/* * G_ChasePlayer */ void G_ChasePlayer( edict_t *ent, const char *name, bool teamonly, int followmode ) { int i; edict_t *e; gclient_t *client; int targetNum = -1; int oldTarget; bool can_follow = true; char colorlessname[MAX_NAME_BYTES]; client = ent->r.client; oldTarget = client->resp.chase.target; if( teamonly && !client->teamstate.is_coach ) can_follow = false; if( !can_follow && followmode ) { G_PrintMsg( ent, "Chasecam follow mode unavailable\n" ); followmode = false; } if( ent->r.client->resp.chase.followmode && !followmode ) G_PrintMsg( ent, "Disabling chasecam follow mode\n" ); // always disable chasing as a start memset( &client->resp.chase, 0, sizeof( chasecam_t ) ); // locate the requested target if( name && name[0] ) { // find it by player names for( e = game.edicts + 1; PLAYERNUM( e ) < gs.maxclients; e++ ) { if( !G_Chase_IsValidTarget( ent, e, teamonly ) ) continue; Q_strncpyz( colorlessname, COM_RemoveColorTokens( e->r.client->netname ), sizeof(colorlessname) ); if( !Q_stricmp( COM_RemoveColorTokens( name ), colorlessname ) ) { targetNum = PLAYERNUM( e ); break; } } // didn't find it by name, try by numbers if( targetNum == -1 ) { i = atoi( name ); if( i >= 0 && i < gs.maxclients ) { e = game.edicts + 1 + i; if( G_Chase_IsValidTarget( ent, e, teamonly ) ) targetNum = PLAYERNUM( e ); } } if( targetNum == -1 ) G_PrintMsg( ent, "Requested chasecam target is not available\n" ); } // try to reuse old target if we didn't find a valid one if( targetNum == -1 && oldTarget > 0 && oldTarget < gs.maxclients ) { e = game.edicts + 1 + oldTarget; if( G_Chase_IsValidTarget( ent, e, teamonly ) ) targetNum = PLAYERNUM( e ); } // if we still don't have a target, just pick the first valid one if( targetNum == -1 ) { for( e = game.edicts + 1; PLAYERNUM( e ) < gs.maxclients; e++ ) { if( !G_Chase_IsValidTarget( ent, e, teamonly ) ) continue; targetNum = PLAYERNUM( e ); break; } } // make the client a ghost G_GhostClient( ent ); if( targetNum != -1 ) { // we found a target, set up the chasecam client->resp.chase.target = targetNum + 1; client->resp.chase.teamonly = teamonly; client->resp.chase.followmode = followmode; G_Chase_SetChaseActive( ent, true ); } else { // stay as observer if( !teamonly ) ent->movetype = MOVETYPE_NOCLIP; client->level.showscores = false; G_Chase_SetChaseActive( ent, false ); G_CenterPrintMsg( ent, "No one to chase" ); } }
/* * G_Timeout_Update * * Updates the timeout struct and informs clients about the status of the pause */ static void G_Timeout_Update( unsigned int msec ) { static int timeout_printtime = 0; static int timeout_last_endtime = 0; static int countdown_set = 1; if( !GS_MatchPaused() ) return; game.frametime = 0; if( timeout_last_endtime != level.timeout.endtime ) // force print when endtime is changed { timeout_printtime = 0; timeout_last_endtime = level.timeout.endtime; } level.timeout.time += msec; if( level.timeout.endtime && level.timeout.time >= level.timeout.endtime ) { level.timeout.time = 0; level.timeout.caller = -1; GS_GamestatSetFlag( GAMESTAT_FLAG_PAUSED, false ); timeout_printtime = 0; timeout_last_endtime = -1; G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_TIMEOUT_MATCH_RESUMED_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, true, NULL ); G_CenterPrintMsg( NULL, "Match resumed" ); G_PrintMsg( NULL, "Match resumed\n" ); } else if( timeout_printtime == 0 || level.timeout.time - timeout_printtime >= 1000 ) { if( level.timeout.endtime ) { int seconds_left = (int)( ( level.timeout.endtime - level.timeout.time ) / 1000.0 + 0.5 ); if( seconds_left == ( TIMEIN_TIME * 2 ) / 1000 ) { G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_COUNTDOWN_READY_1_to_2, ( rand()&1 )+1 ) ), GS_MAX_TEAMS, false, NULL ); countdown_set = ( rand()&1 )+1; } else if( seconds_left >= 1 && seconds_left <= 3 ) { G_AnnouncerSound( NULL, trap_SoundIndex( va( S_ANNOUNCER_COUNTDOWN_COUNT_1_to_3_SET_1_to_2, seconds_left, countdown_set ) ), GS_MAX_TEAMS, false, NULL ); } if( seconds_left > 1 ) G_CenterPrintFormatMsg( NULL, "Match will resume in %s seconds", va( "%i", seconds_left ), NULL ); else G_CenterPrintMsg( NULL, "Match will resume in 1 second" ); } else { G_CenterPrintMsg( NULL, "Match paused" ); } timeout_printtime = level.timeout.time; } }