]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/announcer.qc
bbd8cd885778c4ca6fa2797763a37ae7b0010b95
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / announcer.qc
1 #include "announcer.qh"
2
3 #include <client/hud/panel/centerprint.qh>
4 #include <client/mutators/_mod.qh>
5 #include <common/notifications/all.qh>
6 #include <common/stats.qh>
7 #include <common/mapinfo.qh>
8 #include <common/ent_cs.qh>
9
10 bool announcer_1min;
11 bool announcer_5min;
12 string AnnouncerOption()
13 {
14         string ret = autocvar_cl_announcer;
15         MUTATOR_CALLHOOK(AnnouncerOption, ret);
16         ret = M_ARGV(0, string);
17         return ret;
18 }
19
20 entity announcer_countdown;
21
22 void Announcer_Countdown(entity this)
23 {
24         float starttime = STAT(GAMESTARTTIME);
25         float roundstarttime = STAT(ROUNDSTARTTIME);
26         if(roundstarttime == -1)
27         {
28                 Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_ROUNDSTOP);
29                 delete(this);
30                 announcer_countdown = NULL;
31                 centerprint_ClearTitle();
32                 return;
33         }
34
35         bool inround = (roundstarttime && time >= starttime);
36         float countdown = (inround ? roundstarttime - time : starttime - time);
37         float countdown_rounded = floor(0.5 + countdown);
38
39         if(time >= starttime) centerprint_ClearTitle();
40
41         if(countdown <= 0) // countdown has finished, starttime is now
42         {
43                 Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_BEGIN);
44                 Local_Notification(MSG_MULTI, MULTI_COUNTDOWN_BEGIN);
45                 delete(this);
46                 announcer_countdown = NULL;
47                 centerprint_ClearTitle();
48                 return;
49         }
50         else // countdown is still going
51         {
52                 if(inround)
53                 {
54                         Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_ROUNDSTART, STAT(ROUNDS_PLAYED) + 1, countdown_rounded);
55                         Notification annce_num = Announcer_PickNumber(CNT_ROUNDSTART, countdown_rounded);
56                         if(annce_num != NULL)
57                                 Local_Notification(MSG_ANNCE, annce_num);
58                         this.nextthink = (roundstarttime - (countdown - 1));
59                 }
60                 else
61                 {
62                         Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_GAMESTART, countdown_rounded);
63                         Notification annce_num = Announcer_PickNumber(CNT_GAMESTART, countdown_rounded);
64                         if(!roundstarttime && annce_num != NULL) // Don't announce game start in round based modes
65                                 Local_Notification(MSG_ANNCE, annce_num);
66                         this.nextthink = (starttime - (countdown - 1));
67                 }
68         }
69 }
70
71 /**
72  * Checks whether the server initiated a map restart (stat_game_starttime changed)
73  *
74  * TODO: Use a better solution where a common shared entitiy is used that contains
75  * timelimit, fraglimit and game_starttime! Requires engine changes (remove STAT_TIMELIMIT
76  * and STAT_FRAGLIMIT to be auto-sent)
77  */
78 float previous_game_starttime;
79 void Announcer_Gamestart()
80 {
81         float startTime = STAT(GAMESTARTTIME);
82         float roundstarttime = STAT(ROUNDSTARTTIME);
83         if(roundstarttime > startTime)
84                 startTime = roundstarttime;
85         if(intermission)
86         {
87                 if(announcer_countdown)
88                 {
89                         centerprint_Kill(ORDINAL(CPID_ROUND));
90                         if(announcer_countdown)
91                         {
92                                 delete(announcer_countdown);
93                                 announcer_countdown = NULL;
94                         }
95                 }
96                 return;
97         }
98
99         if(previous_game_starttime != startTime)
100         {
101                 if(time < startTime)
102                 {
103                         if (!announcer_countdown)
104                         {
105                                 announcer_countdown = new(announcer_countdown);
106                                 setthink(announcer_countdown, Announcer_Countdown);
107                         }
108
109                         if(!warmup_stage && time < STAT(GAMESTARTTIME))
110                         {
111                                 if (gametype.m_1v1)
112                                 {
113                                         entity pl1 = players.sort_next;
114                                         entity pl2 = pl1.sort_next;
115                                         string pl1_name = (pl1 && pl1.team != NUM_SPECTATOR ? entcs_GetName(pl1.sv_entnum) : "???");
116                                         string pl2_name = (pl2 && pl2.team != NUM_SPECTATOR ? entcs_GetName(pl2.sv_entnum) : "???");
117
118                                         centerprint_SetTitle(sprintf(_("^BG%s^BG  vs  %s"), pl1_name, pl2_name)); // Show duelers in 1v1 game mode
119                                 }
120                                 else
121                                         centerprint_SetTitle(strcat("^BG", MapInfo_Type_ToText(gametype))); // Show game type as title
122
123                                 if(time + 5.0 < startTime) // if connecting to server while restart was active don't always play prepareforbattle
124                                         Local_Notification(MSG_ANNCE, ANNCE_PREPARE);
125                         }
126
127                         announcer_countdown.nextthink = startTime - floor(startTime - time + 0.5); //synchronize nextthink to startTime
128                 }
129         }
130
131         previous_game_starttime = startTime;
132 }
133
134 #define ANNOUNCER_CHECKMINUTE(minute) MACRO_BEGIN \
135         if(announcer_##minute##min) { \
136                 if(timeleft > minute * 60) \
137                         announcer_##minute##min = false; \
138         } else { \
139                 if(timeleft < minute * 60 && timeleft > minute * 60 - 1) { \
140                         announcer_##minute##min = true; \
141                         Local_Notification(MSG_ANNCE, ANNCE_REMAINING_MIN_##minute); \
142                 } \
143         } \
144 MACRO_END
145
146 void Announcer_Time()
147 {
148         static bool warmup_stage_prev;
149
150         if(intermission)
151                 return;
152
153         if (warmup_stage != warmup_stage_prev)
154         {
155                 announcer_5min = announcer_1min = false;
156                 warmup_stage_prev = warmup_stage;
157                 return;
158         }
159
160         float starttime = STAT(GAMESTARTTIME);
161         if(time < starttime)
162         {
163                 announcer_5min = announcer_1min = false;
164                 return;
165         }
166
167         float timeleft;
168         if(warmup_stage)
169         {
170                 float warmup_timelimit = STAT(WARMUP_TIMELIMIT);
171                 if(warmup_timelimit > 0)
172                         timeleft = max(0, warmup_timelimit - time);
173                 else
174                         timeleft = 0;
175         }
176         else
177                 timeleft = max(0, STAT(TIMELIMIT) * 60 + starttime - time);
178
179         if(autocvar_cl_announcer_maptime >= 2)
180                 ANNOUNCER_CHECKMINUTE(5);
181
182         if((autocvar_cl_announcer_maptime == 1) || (autocvar_cl_announcer_maptime == 3))
183                 ANNOUNCER_CHECKMINUTE(1);
184 }
185
186 void Announcer()
187 {
188         Announcer_Gamestart();
189         Announcer_Time();
190 }