]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/minigames/cl_minigames.qc
a4241ba97b14d4349170db7229cd5700fcf29d21
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / minigames / cl_minigames.qc
1 #include "cl_minigames.qh"
2
3 // Draw a square in the center of the avaliable area
4 void minigame_hud_simpleboard(vector pos, vector mySize, string board_texture)
5 {
6         if(panel.current_panel_bg != "0" && panel.current_panel_bg != "")
7                 draw_BorderPicture(pos - '1 1 0' * panel_bg_border,
8                                         panel.current_panel_bg,
9                                         mySize + '1 1 0' * 2 * panel_bg_border,
10                                         panel_bg_color, panel_bg_alpha,
11                                         '1 1 0' * BORDER_MULTIPLIER * panel_bg_border);
12         drawpic(pos, board_texture, mySize, '1 1 1', panel_bg_alpha, DRAWFLAG_NORMAL);
13 }
14
15 // De-normalize (2D vector) v from relative coordinate inside pos mySize
16 vector minigame_hud_denormalize(vector v, vector pos, vector mySize)
17 {
18         v_x = pos_x + v_x * mySize_x;
19         v_y = pos_y + v_y * mySize_y;
20         return v;
21 }
22 // De-normalize (2D vector) v from relative size inside pos mySize
23 vector minigame_hud_denormalize_size(vector v, vector pos, vector mySize)
24 {
25         v_x = v_x * mySize_x;
26         v_y = v_y * mySize_y;
27         return v;
28 }
29
30 // Normalize (2D vector) v to relative coordinate inside pos mySize
31 vector minigame_hud_normalize(vector v, vector pos, vector mySize)
32 {
33         v_x = ( v_x - pos_x ) / mySize_x;
34         v_y = ( v_y - pos_y ) / mySize_y;
35         return v;
36 }
37
38 // Check if the mouse is inside the given area
39 bool minigame_hud_mouse_in(vector pos, vector sz)
40 {
41         return mousepos_x >= pos_x && mousepos_x < pos_x + sz_x &&
42                mousepos_y >= pos_y && mousepos_y < pos_y + sz_y ;
43 }
44
45 string minigame_texture_skin(string skinname, string name)
46 {
47         return sprintf("gfx/hud/%s/minigames/%s", skinname, name);
48 }
49 string minigame_texture(string name)
50 {
51         string path = minigame_texture_skin(autocvar_menu_skin,name);
52         if ( precache_pic(path) == "" )
53                 path = minigame_texture_skin("default", name);
54         return path;
55 }
56
57 #define FIELD(Flags, Type, Name) MSLE_CLEAN_##Type(this.Name)
58 #define MSLE_CLEAN_String(x) strunzone(x);
59 #define MSLE_CLEAN_Byte(x)
60 #define MSLE_CLEAN_Char(x)
61 #define MSLE_CLEAN_Short(x)
62 #define MSLE_CLEAN_Long(x)
63 #define MSLE_CLEAN_Coord(x)
64 #define MSLE_CLEAN_Angle(x)
65 #define MSLE_CLEAN_Float(x)
66 #define MSLE_CLEAN_Vector(x)
67 #define MSLE_CLEAN_Vector2D(x)
68
69 #define MSLE(Name,Fields) \
70         void msle_entremove_##Name(entity this) { strunzone(this.netname); Fields }
71 MINIGAME_SIMPLELINKED_ENTITIES
72 #undef MSLE
73 #undef FIELD
74
75 void minigame_autoclean_entity(entity e)
76 {
77         LOG_DEBUG("CL Auto-cleaned: ",ftos(etof(e)), " (",e.classname,")");
78         delete(e);
79 }
80
81 void HUD_MinigameMenu_CurrentButton();
82 bool auto_close_minigamemenu;
83 void deactivate_minigame()
84 {
85         if ( !active_minigame )
86                 return;
87
88         active_minigame.minigame_event(active_minigame,"deactivate");
89         entity e = NULL;
90         while( (e = findentity(e, owner, active_minigame)) )
91                 if ( e.minigame_autoclean )
92                 {
93                         minigame_autoclean_entity(e);
94                 }
95
96         minigame_self = NULL;
97         active_minigame = NULL;
98
99         if ( auto_close_minigamemenu )
100         {
101                 HUD_MinigameMenu_Close(NULL, NULL, NULL);
102                 auto_close_minigamemenu = 0;
103         }
104         else
105                 HUD_MinigameMenu_CurrentButton();
106 }
107
108 void minigame_entremove(entity this)
109 {
110         if ( this == active_minigame )
111                 deactivate_minigame();
112 }
113
114 void activate_minigame(entity minigame)
115 {
116         if ( !minigame )
117         {
118                 deactivate_minigame();
119                 return;
120         }
121
122         if ( !minigame.descriptor || minigame.classname != "minigame" )
123         {
124                 LOG_TRACE("Trying to activate unregistered minigame ",minigame.netname," in client");
125                 return;
126         }
127
128         if ( minigame == active_minigame )
129                 return;
130
131         if ( active_minigame )
132         {
133                 deactivate_minigame();
134         }
135
136         if ( minigame_self.owner != minigame )
137                 minigame_self = NULL;
138         active_minigame = minigame;
139         active_minigame.minigame_event(active_minigame,"activate");
140
141         if ( HUD_MinigameMenu_IsOpened() )
142                 HUD_MinigameMenu_CurrentButton();
143         else
144         {
145                 auto_close_minigamemenu = 1;
146                 HUD_MinigameMenu_Open();
147         }
148 }
149
150 void minigame_player_entremove(entity this)
151 {
152         if ( this.owner == active_minigame && this.minigame_playerslot == player_localentnum )
153                 deactivate_minigame();
154 }
155
156 string() ReadString_Raw = #366;
157 string ReadString_Zoned() { return strzone(ReadString_Raw()); }
158 #define ReadString ReadString_Zoned
159 #define FIELD(Flags, Type,Name) if ( sf & (Flags) ) this.Name = Read##Type();
160 #define MSLE(Name,Fields) \
161         else if ( this.classname == #Name ) { \
162                 if ( sf & MINIG_SF_CREATE ) { \
163                         minigame_read_owner(this); \
164                         this.entremove = msle_entremove_##Name; \
165                 } \
166                 minigame_ent = this.owner; \
167                 Fields \
168         }
169 void minigame_read_owner(entity this)
170 {
171         string owner_name = ReadString_Raw();
172         this.owner = NULL;
173         do
174                 this.owner = find(this.owner,netname,owner_name);
175         while ( this.owner && this.owner.classname != "minigame" );
176         if ( !this.owner )
177                 LOG_TRACE("Got a minigame entity without a minigame!");
178 }
179 NET_HANDLE(ENT_CLIENT_MINIGAME, bool isnew)
180 {
181         float sf = ReadByte();
182         if ( sf & MINIG_SF_CREATE )
183         {
184                 this.classname = msle_classname(ReadShort());
185                 this.netname = ReadString_Zoned();
186         }
187
188         entity minigame_ent = NULL;
189
190         if ( this.classname == "minigame" )
191         {
192                 minigame_ent = this;
193
194                 if ( sf & MINIG_SF_CREATE )
195                 {
196                         this.entremove = minigame_entremove;
197                         this.descriptor = minigame_get_descriptor(ReadString_Raw());
198                         if ( !this.descriptor )
199                                 LOG_TRACE("Got a minigame without a client-side descriptor!");
200                         else
201                                 this.minigame_event = this.descriptor.minigame_event;
202                 }
203                 if ( sf & MINIG_SF_UPDATE )
204                         this.minigame_flags = ReadLong();
205         }
206         else if ( this.classname == "minigame_player" )
207         {
208                 float activate = 0;
209                 if ( sf & MINIG_SF_CREATE )
210                 {
211                         this.entremove = minigame_player_entremove;
212                         minigame_read_owner(this);
213                         float ent = ReadLong();
214                         this.minigame_playerslot = ent;
215                         LOG_DEBUG("Player: ",entcs_GetName(ent-1));
216
217                         activate = (ent == player_localnum+1 && this.owner && this.owner != active_minigame);
218
219                 }
220                 minigame_ent = this.owner;
221
222                 if ( sf & MINIG_SF_UPDATE )
223                         this.team = ReadByte();
224
225                 if ( activate )
226                 {
227                         minigame_self = this;
228                         activate_minigame(this.owner);
229                         minigame_self = this; // set it again (needed before, but may also be reset)
230                 }
231         }
232         MINIGAME_SIMPLELINKED_ENTITIES
233
234         if ( minigame_ent )
235                 minigame_ent.minigame_event(minigame_ent,"network_receive",this,sf);
236
237         if ( sf & MINIG_SF_CREATE )
238         {
239                 LOG_DEBUG("CL Reading entity: ",ftos(etof(this)),
240                         " classname:",this.classname," enttype:",ftos(this.enttype) );
241                 LOG_DEBUG(" sf:",ftos(sf)," netname:",this.netname);
242         }
243         return true;
244 }
245 #undef ReadString
246 #undef FIELD
247 #undef MSLE
248
249 void minigame_show_allspecs(vector boardpos, vector boardsize)
250 {
251         string allspecs = "";
252         float allspecs_width = 0;
253         float max_allspecs_width = boardsize.x;
254         float max_current_spec_width = hud_fontsize.x * 5;
255         int allspecs_lines = 2;
256
257         entity e;
258         FOREACH_MINIGAME_ENTITY(e)
259         {
260                 if (allspecs_width >= 0 && e.classname == "minigame_player" && e.team == C4_SPECTATOR_TEAM)
261                 {
262                         string current_spec = ColorTranslateRGB(entcs_GetName(e.minigame_playerslot - 1));
263                         current_spec = textShortenToWidth(current_spec, max_current_spec_width, hud_fontsize, stringwidth_colors);
264                         if (allspecs != "")
265                                 current_spec = strcat(", ", current_spec);
266                         else
267                                 current_spec = current_spec;
268
269                         allspecs_width = stringwidth(allspecs, true, hud_fontsize);
270
271                         float max_width = max_allspecs_width * allspecs_lines - max_current_spec_width;
272                         if (allspecs_width + stringwidth(current_spec, true, hud_fontsize) < max_width)
273                                 allspecs = strcat(allspecs, current_spec);
274                         else
275                         {
276                                 // current_spec doesn't fit in the list
277                                 allspecs = strcat(allspecs, ", ...");
278                                 allspecs_width = -1; // skip remaining spectators
279                         }
280                 }
281         }
282
283         if (allspecs != "")
284         {
285                 vector pos = boardpos;
286                 pos.y -= panel_bg_border + hud_fontsize.y * (1.25 + allspecs_lines + 0.5);
287                 minigame_drawstring_wrapped(max_allspecs_width, pos, _("Spectators:"), hud_fontsize * 1.25, '0.85 0.47 0.42', panel_fg_alpha, DRAWFLAG_NORMAL, 0);
288
289                 pos.y += hud_fontsize.y * 1.25;
290                 minigame_drawcolorcodedstring_wrapped(max_allspecs_width, pos, allspecs, hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL, 0);
291         }
292 }
293
294 string minigame_getWrappedLine(float w, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
295 {
296         string s = getWrappedLine_remaining;
297
298         if(w <= 0)
299         {
300                 getWrappedLine_remaining = string_null;
301                 return s; // the line has no size ANYWAY, nothing would be displayed.
302         }
303
304         int take_until = textLengthUpToWidth(s, w, theFontSize, tw);
305
306         if ( take_until > strlen(s) )
307                 take_until = strlen(s);
308
309         int skip = 0;
310         for ( int i = 0; i < take_until; ++i )
311                 if ( substring(s,i,1) == "\n" )
312                 {
313                         take_until = i;
314                         skip = 1;
315                         break;
316                 }
317
318         if ( take_until > 0 || skip > 0 )
319         {
320                 if ( skip == 0 && take_until < strlen(s) )
321                 {
322                         int last_word = take_until;
323                         while(last_word > 0 && substring(s, last_word, 1) != " ")
324                                 --last_word;
325
326                         if ( last_word != 0 )
327                         {
328                                 take_until = last_word;
329                                 skip = 1;
330                         }
331                 }
332
333                 getWrappedLine_remaining = substring(s, take_until+skip, strlen(s) - (take_until+skip));
334                 if(getWrappedLine_remaining == "")
335                         getWrappedLine_remaining = string_null;
336                 else if (tw("^7", theFontSize) == 0)
337                         getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, take_until)), getWrappedLine_remaining);
338                 return substring(s, 0, take_until);
339         }
340         else
341         {
342                 getWrappedLine_remaining = string_null;
343                 return s;
344         }
345 }
346
347 vector minigame_drawstring_wrapped( float maxwidth, vector pos, string text,
348         vector fontsize, vector color, float theAlpha, int drawflags, float align )
349 {
350         getWrappedLine_remaining = text;
351         vector mypos = pos;
352         while ( getWrappedLine_remaining )
353         {
354                 string line = minigame_getWrappedLine(maxwidth,fontsize,stringwidth_nocolors);
355                 if ( line == "" )
356                         break;
357                 mypos_x = pos_x + (maxwidth - stringwidth_nocolors(line, fontsize)) * align;
358                 drawstring(mypos, line, fontsize, color, theAlpha, drawflags);
359                 mypos_y += fontsize_y;
360         }
361         mypos_x = maxwidth;
362         mypos_y -= pos_y;
363         return mypos;
364 }
365
366 vector minigame_drawcolorcodedstring_wrapped( float maxwidth, vector pos,
367         string text, vector fontsize, float theAlpha, int drawflags, float align )
368 {
369         getWrappedLine_remaining = text;
370         vector mypos = pos;
371         while ( getWrappedLine_remaining )
372         {
373                 string line = minigame_getWrappedLine(maxwidth,fontsize,stringwidth_colors);
374                 if ( line == "" )
375                         break;
376                 mypos_x = pos_x + (maxwidth - stringwidth_colors(line, fontsize)) * align;
377                 drawcolorcodedstring(mypos, line, fontsize, theAlpha, drawflags);
378                 mypos_y += fontsize_y;
379         }
380         mypos_x = maxwidth;
381         mypos_y -= pos_y;
382         return mypos;
383 }
384
385 void minigame_drawstring_trunc(float maxwidth, vector pos, string text,
386         vector fontsize, vector color, float theAlpha, int drawflags )
387 {
388         string line = textShortenToWidth(text,maxwidth,fontsize,stringwidth_nocolors);
389         drawstring(pos, line, fontsize, color, theAlpha, drawflags);
390 }
391
392 void minigame_drawcolorcodedstring_trunc(float maxwidth, vector pos, string text,
393         vector fontsize, float theAlpha, int drawflags )
394 {
395         string line = textShortenToWidth(text,maxwidth,fontsize,stringwidth_colors);
396         drawcolorcodedstring(pos, line, fontsize, theAlpha, drawflags);
397 }
398
399 void minigame_drawpic_centered( vector pos, string texture, vector sz,
400         vector color, float thealpha, int drawflags )
401 {
402         drawpic( pos-sz/2, texture, sz, color, thealpha, drawflags );
403 }
404
405 // Workaround because otherwise variadic arguments won't work properly
406 // It could be a bug in the compiler or in darkplaces
407 void minigame_cmd_workaround(float dummy, string...cmdargc)
408 {
409         string cmd;
410         cmd = "cmd minigame ";
411         float i;
412         for ( i = 0; i < cmdargc; ++i )
413                 cmd = strcat(cmd,...(i,string));
414         localcmd(strcat(cmd,"\n"));
415 }
416
417 // Prompt the player to play in the current minigame
418 // (ie: it's their turn and they should get back to the minigame)
419 void minigame_prompt()
420 {
421         if ( active_minigame && ! HUD_MinigameMenu_IsOpened() )
422         {
423                 HUD_Notify_Push(sprintf("minigames/%s/icon_notif",active_minigame.descriptor.netname),
424                         _("It's your turn"), "");
425         }
426 }
427
428 // handle commands etc.
429 REGISTER_MUTATOR(minigames, true);
430
431 MUTATOR_HOOKFUNCTION(minigames, HUD_Command)
432 {
433         if(MUTATOR_RETURNVALUE) { return false; } // command was already handled
434
435         if(argv(1) == "minigame")
436         {
437                 if (isdemo())
438                         return true; // minigames can't function properly in demo mode
439                 if (HUD_MinigameMenu_IsOpened())
440                         HUD_MinigameMenu_Close(NULL, NULL, NULL);
441                 else
442                         HUD_MinigameMenu_Open();
443                 return true;
444         }
445
446         return false;
447 }