Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
announcer.qc
Go to the documentation of this file.
1#include "announcer.qh"
2
3#include <client/draw.qh>
8#include <common/stats.qh>
9#include <common/mapinfo.qh>
10#include <common/ent_cs.qh>
11
15{
16 string ret = autocvar_cl_announcer;
18 ret = M_ARGV(0, string);
19 return ret;
20}
21
23
30{
32
33 entity pl1 = players.sort_next;
34 entity pl2 = pl1.sort_next;
35 string pl1_name = (pl1 && pl1.team != NUM_SPECTATOR ? entcs_GetName(pl1.sv_entnum) : "???");
36 string pl2_name = (pl2 && pl2.team != NUM_SPECTATOR ? entcs_GetName(pl2.sv_entnum) : "???");
37
38 if(pl1_name == prev_pl1_name && pl2_name == prev_pl2_name)
39 return; // Players haven't changed, stop here
40
41 strcpy(prev_pl1_name, pl1_name);
42 strcpy(prev_pl2_name, pl2_name);
43
44 // There are new duelers, update title
45 centerprint_SetDuelTitle(pl1_name, pl2_name);
46}
47
54
59{
60 float starttime = STAT(GAMESTARTTIME);
61 float roundstarttime = STAT(ROUNDSTARTTIME);
62 if(roundstarttime == -1)
63 {
64 Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_ROUNDSTOP);
65 delete(this);
68 return;
69 }
70
71 bool inround = (roundstarttime && time >= starttime);
72 float countdown = (inround ? roundstarttime - time : starttime - time);
73 float countdown_rounded = floor(0.5 + countdown);
74
75 if (starttime != prev_starttime || roundstarttime != prev_roundstarttime || prev_inround != inround)
76 this.skin = 0; // restart centerprint countdown
77
78 if(countdown <= 0) // countdown has finished, starttime is now
79 {
80 Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_BEGIN);
81 Local_Notification(MSG_MULTI, COUNTDOWN_BEGIN);
82 delete(this);
85 return;
86 }
87 else // countdown is still going
88 {
89 if(inround)
90 {
91 if(!prev_inround) Announcer_ClearTitle(); // clear title if we just started the match
92 if (!this.skin) // first tic
93 Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_ROUNDSTART, STAT(ROUNDS_PLAYED) + 1, countdown_rounded);
94 Notification annce_num = Announcer_PickNumber(CNT_ROUNDSTART, countdown_rounded);
95 if(annce_num != NULL)
96 Local_Notification(MSG_ANNCE, annce_num);
97 this.nextthink = (roundstarttime - (countdown - 1));
98 }
99 else
100 {
101 if (!this.skin) // first tic
102 Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_GAMESTART, countdown_rounded);
103 Notification annce_num = Announcer_PickNumber(CNT_GAMESTART, countdown_rounded);
104 if(!roundstarttime && annce_num != NULL) // Don't announce game start in round based modes
105 Local_Notification(MSG_ANNCE, annce_num);
106 this.nextthink = (starttime - (countdown - 1));
107 }
108 // Don't call centerprint countdown in the remaining tics, it will continue automatically.
109 // It's an optimization but also fixes ^COUNT shown in the last tic because of high slowmo values (15+).
110 // Hopefully it fixes ^COUNT occasionally shown in online servers, probably due to lags.
111 this.skin = 1; // recycled field
112 }
113
114 prev_inround = inround;
115 prev_starttime = starttime;
116 prev_roundstarttime = roundstarttime;
117}
118
128{
129 float startTime = STAT(GAMESTARTTIME);
130 float roundstarttime = STAT(ROUNDSTARTTIME);
131 if(time > startTime && roundstarttime > startTime)
132 startTime = roundstarttime;
134 {
136 {
139 delete(announcer_countdown);
141 }
142 return;
143 }
144
145 if(announcer_countdown && gametype.m_1v1)
147
148 if(previous_game_starttime != startTime)
149 {
150 if(time < startTime)
151 {
153 {
156 }
157
158 if(!warmup_stage && time < STAT(GAMESTARTTIME))
159 {
160 if (gametype.m_1v1)
162 else
163 centerprint_SetTitle(strcat("^BG", MapInfo_Type_ToText(gametype))); // Show gametype as title
164
165 if(time + 5.0 < startTime) // if connecting to server while restart was active don't always play prepareforbattle
166 Local_Notification(MSG_ANNCE, ANNCE_PREPARE);
167 }
168
169 announcer_countdown.nextthink = startTime - floor(startTime - time + 0.5); //synchronize nextthink to startTime
170 }
171 }
172
173 previous_game_starttime = startTime;
174}
175
176#define ANNOUNCER_CHECKMINUTE(minute) MACRO_BEGIN \
177 if(announcer_##minute##min) { \
178 if(timeleft > minute * 60) \
179 announcer_##minute##min = false; \
180 } else { \
181 if(timeleft < minute * 60 && timeleft > minute * 60 - 1) { \
182 announcer_##minute##min = true; \
183 Local_Notification(MSG_ANNCE, ANNCE_REMAINING_MIN_##minute); \
184 } \
185 } \
186MACRO_END
187
189{
190 static bool warmup_stage_prev;
191
192 if(intermission)
193 return;
194
195 if (warmup_stage != warmup_stage_prev)
196 {
198 warmup_stage_prev = warmup_stage;
199 return;
200 }
201
202 float starttime = STAT(GAMESTARTTIME);
203 if(time < starttime)
204 {
206 return;
207 }
208
209 float timeleft;
210 if(warmup_stage)
211 {
212 float warmup_timelimit = STAT(WARMUP_TIMELIMIT);
213 if(warmup_timelimit > 0)
214 timeleft = max(0, warmup_timelimit + starttime - time);
215 else
216 timeleft = 0;
217 }
218 else
219 timeleft = max(0, STAT(TIMELIMIT) * 60 + starttime - time);
220
223
226}
227
229{
230 // announcer code sets gametype name as centerprint title
231 if(!gametype)
232 return;
235}
float prev_roundstarttime
Definition announcer.qc:57
void Announcer_Time()
Definition announcer.qc:188
entity announcer_countdown
Definition announcer.qc:22
void Announcer_Duel()
Definition announcer.qc:29
string prev_pl1_name
Displays duel title; updates it if the players in-game have changed.
Definition announcer.qc:27
void Announcer_Gamestart()
Definition announcer.qc:127
bool announcer_1min
Definition announcer.qc:12
string AnnouncerOption()
Definition announcer.qc:14
void Announcer_Countdown(entity this)
Definition announcer.qc:58
bool prev_inround
Definition announcer.qc:55
float prev_starttime
Definition announcer.qc:56
void Announcer()
Definition announcer.qc:228
string prev_pl2_name
Definition announcer.qc:28
void Announcer_ClearTitle()
Definition announcer.qc:48
float previous_game_starttime
Checks whether the server initiated a map restart (stat_game_starttime changed)
Definition announcer.qc:126
bool announcer_5min
Definition announcer.qc:13
#define ANNOUNCER_CHECKMINUTE(minute)
Definition announcer.qc:176
string autocvar_cl_announcer
Definition announcer.qh:3
float autocvar_cl_announcer_maptime
Definition announcer.qh:5
#define MUTATOR_CALLHOOK(id,...)
Definition base.qh:143
void centerprint_ClearTitle()
void centerprint_SetTitle(string title)
void centerprint_SetDuelTitle(string left, string right)
void centerprint_Kill(int id)
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
entity players
Definition main.qh:57
bool warmup_stage
Definition main.qh:120
entity gametype
Definition main.qh:43
#define M_ARGV(x, type)
Definition events.qh:17
Notification Announcer_PickNumber(int type, int num)
Definition util.qc:1827
const int CNT_ROUNDSTART
Definition util.qh:256
const int CNT_GAMESTART
Definition util.qh:252
float skin
float time
float nextthink
float intermission
string entcs_GetName(int i)
Definition ent_cs.qh:151
#define ORDINAL(it)
Definition enumclass.qh:25
#define STAT(...)
Definition stats.qh:82
string MapInfo_Type_ToText(Gametype t)
Definition mapinfo.qc:661
float floor(float f)
float max(float f,...)
CPID_ROUND
Definition all.inc:561
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
void Local_Notification(MSG net_type, Notification net_name,...count)
Definition all.qc:1236
entity Notification
always last
Definition all.qh:81
#define new_pure(class)
purely logical entities (not linked to the area grid)
Definition oo.qh:67
#define NULL
Definition post.qh:14
void Scoreboard_UpdatePlayerTeams()
#define setthink(e, f)
#define strfree(this)
Definition string.qh:59
#define strcpy(this, s)
Definition string.qh:52
const int NUM_SPECTATOR
Definition teams.qh:23