1#include "cl_minigames.qh"
2
3// Draw a square in the center of the avaliable area
4void 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' * (panel_bg_border/BORDER_MULTIPLIER));
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
16vector 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
23vector 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
31vector 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
39bool 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
45string minigame_texture_skin(string skinname, string name)
46{
47	return sprintf("gfx/hud/%s/minigames/%s", skinname, name);
48}
49string 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 }
71MINIGAME_SIMPLELINKED_ENTITIES
72#undef MSLE
73#undef FIELD
74
75void minigame_autoclean_entity(entity e)
76{
77	LOG_DEBUG("CL Auto-cleaned: ",ftos(etof(e)), " (",e.classname,")");
78	delete(e);
79}
80
81void HUD_MinigameMenu_CurrentButton();
82bool auto_close_minigamemenu;
83void 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
108void minigame_entremove(entity this)
109{
110	if ( this == active_minigame )
111		deactivate_minigame();
112}
113
114void 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
150void minigame_player_entremove(entity this)
151{
152	if ( this.owner == active_minigame && this.minigame_playerslot == player_localentnum )
153		deactivate_minigame();
154}
155
156string() ReadString_Raw = #366;
157string 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	}
169void 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}
179NET_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
249string minigame_getWrappedLine(float w, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
250{
251	int last_word;
252	string s;
253	int take_until;
254	int skip = 0;
255
256	s = getWrappedLine_remaining;
257
258	if(w <= 0)
259	{
260		getWrappedLine_remaining = string_null;
261		return s; // the line has no size ANYWAY, nothing would be displayed.
262	}
263
264	take_until = textLengthUpToWidth(s, w, theFontSize, tw);
265
266	if ( take_until > strlen(s) )
267		take_until = strlen(s);
268
269	for ( int i = 0; i < take_until; i++ )
270		if ( substring(s,i,1) == "\n" )
271		{
272			take_until = i;
273			skip = 1;
274			break;
275		}
276
277	if ( take_until > 0 || skip > 0 )
278	{
279		if ( skip == 0 && take_until < strlen(s) )
280		{
281			last_word = take_until;
282			while(last_word > 0 && substring(s, last_word, 1) != " ")
283				--last_word;
284
285			if ( last_word != 0 )
286			{
287				take_until = last_word;
288				skip = 1;
289			}
290		}
291
292		getWrappedLine_remaining = substring(s, take_until+skip, strlen(s) - (take_until+skip));
293		if(getWrappedLine_remaining == "")
294			getWrappedLine_remaining = string_null;
295		else if (tw("^7", theFontSize) == 0)
296			getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, take_until)), getWrappedLine_remaining);
297		return substring(s, 0, take_until);
298	}
299	else
300	{
301		getWrappedLine_remaining = string_null;
302		return s;
303	}
304}
305
306vector minigame_drawstring_wrapped( float maxwidth, vector pos, string text,
307	vector fontsize, vector color, float theAlpha, int drawflags, float align )
308{
309	getWrappedLine_remaining = text;
310	vector mypos = pos;
311	while ( getWrappedLine_remaining )
312	{
313		string line = minigame_getWrappedLine(maxwidth,fontsize,stringwidth_nocolors);
314		if ( line == "" )
315			break;
316		mypos_x = pos_x + (maxwidth - stringwidth_nocolors(line, fontsize)) * align;
317		drawstring(mypos, line, fontsize, color, theAlpha, drawflags);
318		mypos_y += fontsize_y;
319	}
320	mypos_x = maxwidth;
321	mypos_y -= pos_y;
322	return mypos;
323}
324
325vector minigame_drawcolorcodedstring_wrapped( float maxwidth, vector pos,
326	string text, vector fontsize, float theAlpha, int drawflags, float align )
327{
328	getWrappedLine_remaining = text;
329	vector mypos = pos;
330	while ( getWrappedLine_remaining )
331	{
332		string line = minigame_getWrappedLine(maxwidth,fontsize,stringwidth_colors);
333		if ( line == "" )
334			break;
335		mypos_x = pos_x + (maxwidth - stringwidth_colors(line, fontsize)) * align;
336		drawcolorcodedstring(mypos, line, fontsize, theAlpha, drawflags);
337		mypos_y += fontsize_y;
338	}
339	mypos_x = maxwidth;
340	mypos_y -= pos_y;
341	return mypos;
342}
343
344void minigame_drawstring_trunc(float maxwidth, vector pos, string text,
345	vector fontsize, vector color, float theAlpha, int drawflags )
346{
347	string line = textShortenToWidth(text,maxwidth,fontsize,stringwidth_nocolors);
348	drawstring(pos, line, fontsize, color, theAlpha, drawflags);
349}
350
351void minigame_drawcolorcodedstring_trunc(float maxwidth, vector pos, string text,
352	vector fontsize, float theAlpha, int drawflags )
353{
354	string line = textShortenToWidth(text,maxwidth,fontsize,stringwidth_colors);
355	drawcolorcodedstring(pos, line, fontsize, theAlpha, drawflags);
356}
357
358void minigame_drawpic_centered( vector pos, string texture, vector sz,
359	vector color, float thealpha, int drawflags )
360{
361	drawpic( pos-sz/2, texture, sz, color, thealpha, drawflags );
362}
363
364// Workaround because otherwise variadic arguments won't work properly
365// It could be a bug in the compiler or in darkplaces
366void minigame_cmd_workaround(float dummy, string...cmdargc)
367{
368	string cmd;
369	cmd = "cmd minigame ";
370	float i;
371	for ( i = 0; i < cmdargc; i++ )
372		cmd = strcat(cmd,...(i,string));
373	localcmd(strcat(cmd,"\n"));
374}
375
376// Prompt the player to play in the current minigame
377// (ie: it's their turn and they should get back to the minigame)
378void minigame_prompt()
379{
380	if ( active_minigame && ! HUD_MinigameMenu_IsOpened() )
381	{
382		HUD_Notify_Push(sprintf("minigames/%s/icon_notif",active_minigame.descriptor.netname),
383			_("It's your turn"), "");
384	}
385}
386
387// handle commands etc.
388REGISTER_MUTATOR(minigames, true);
389
390MUTATOR_HOOKFUNCTION(minigames, HUD_Command)
391{
392	if(MUTATOR_RETURNVALUE) { return false; } // command was already handled
393
394	if(argv(1) == "minigame")
395	{
396		if (HUD_MinigameMenu_IsOpened())
397			HUD_MinigameMenu_Close(NULL, NULL, NULL);
398		else
399			HUD_MinigameMenu_Open();
400		return true;
401	}
402
403	return false;
404}
405