Merge branch 'master' into martin-t/limit
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / mutators / mutator / itemstime / itemstime.qc
1 #include "itemstime.qh"
2
3 REGISTER_MUTATOR(itemstime, true);
4
5 REGISTER_NET_TEMP(itemstime)
6
7 #ifdef SVQC
8 void IT_Write(entity e, int i, float f) {
9     if (!IS_REAL_CLIENT(e)) return;
10     msg_entity = e;
11     WriteHeader(MSG_ONE, itemstime);
12     WriteByte(MSG_ONE, i);
13     WriteFloat(MSG_ONE, f);
14 }
15 #endif
16
17 #ifdef CSQC
18 // reserve one more spot for superweapons time
19 float ItemsTime_time[Items_MAX + 1];
20 float ItemsTime_availableTime[Items_MAX + 1];
21 NET_HANDLE(itemstime, bool isNew)
22 {
23     int i = ReadByte();
24     float f = ReadFloat();
25     return = true;
26     ItemsTime_time[i] = f;
27 }
28 #endif
29
30 #ifdef CSQC
31
32 STATIC_INIT(ItemsTime_Init) {
33         FOREACH(Items, true, {
34                 ItemsTime_time[it.m_id] = -1;
35         });
36         ItemsTime_time[Items_MAX] = -1;
37 }
38
39 int autocvar_hud_panel_itemstime = 2;
40 float autocvar_hud_panel_itemstime_dynamicsize = 1;
41 float autocvar_hud_panel_itemstime_ratio = 2;
42 int autocvar_hud_panel_itemstime_iconalign;
43 bool autocvar_hud_panel_itemstime_progressbar = 0;
44 float autocvar_hud_panel_itemstime_progressbar_maxtime = 30;
45 string autocvar_hud_panel_itemstime_progressbar_name = "progressbar";
46 float autocvar_hud_panel_itemstime_progressbar_reduced;
47 bool autocvar_hud_panel_itemstime_hidespawned = 1;
48 bool autocvar_hud_panel_itemstime_hidebig = false;
49 int autocvar_hud_panel_itemstime_text = 1;
50 #define hud_panel_itemstime_hidebig autocvar_hud_panel_itemstime_hidebig
51 #else
52 #define hud_panel_itemstime_hidebig false
53 #endif
54
55 bool Item_ItemsTime_SpectatorOnly(GameItem it)
56 {
57     return (false
58     || it == ITEM_ArmorMega     || (it == ITEM_ArmorBig && !hud_panel_itemstime_hidebig)
59     || it == ITEM_HealthMega    || (it == ITEM_HealthBig && !hud_panel_itemstime_hidebig)
60     );
61 }
62
63 bool Item_ItemsTime_Allow(GameItem it)
64 {
65     return (false
66     || it.instanceOfPowerup
67     || Item_ItemsTime_SpectatorOnly(it)
68     );
69 }
70
71 #ifdef SVQC
72
73 // reserve one more spot for superweapons time
74 float it_times[Items_MAX + 1];
75
76 STATIC_INIT(ItemsTime_Init) {
77         FOREACH(Items, Item_ItemsTime_Allow(it), {
78                 it_times[it.m_id] = -1;
79         });
80         it_times[Items_MAX] = -1;
81 }
82
83 void Item_ItemsTime_ResetTimes()
84 {
85     FOREACH(Items, Item_ItemsTime_Allow(it), {
86         it_times[it.m_id] = (it_times[it.m_id] == -1) ? -1 : 0;
87     });
88     it_times[Items_MAX] = (it_times[Items_MAX] == -1) ? -1 : 0;
89 }
90
91 void Item_ItemsTime_ResetTimesForPlayer(entity e)
92 {
93     FOREACH(Items, Item_ItemsTime_Allow(it), {
94         IT_Write(e, it.m_id, (it_times[it.m_id] == -1) ? -1 : 0);
95     });
96     IT_Write(e, Items_MAX, (it_times[Items_MAX] == -1) ? -1 : 0);
97 }
98
99 void Item_ItemsTime_SetTimesForPlayer(entity e)
100 {
101     FOREACH(Items, Item_ItemsTime_Allow(it), {
102         IT_Write(e, it.m_id, it_times[it.m_id]);
103     });
104     IT_Write(e, Items_MAX, it_times[Items_MAX]);
105 }
106
107 void Item_ItemsTime_SetTime(entity e, float t)
108 {
109     if (!autocvar_sv_itemstime)
110         return;
111
112     GameItem item = e.itemdef;
113     if (item.instanceOfGameItem)
114     {
115                 if (!item.instanceOfWeaponPickup)
116                         it_times[item.m_id] = t;
117                 else if (STAT(WEAPONS, e) & WEPSET_SUPERWEAPONS)
118                         it_times[Items_MAX] = t;
119     }
120 }
121
122 void Item_ItemsTime_SetTimesForAllPlayers()
123 {
124     FOREACH_CLIENT(IS_REAL_CLIENT(it) && (warmup_stage || !IS_PLAYER(it) || autocvar_sv_itemstime == 2), { Item_ItemsTime_SetTimesForPlayer(it); });
125 }
126
127 float Item_ItemsTime_UpdateTime(entity e, float t)
128 {
129     bool isavailable = (t == 0);
130     IL_EACH(g_items, it != e,
131     {
132         if(!(it.itemdef == e.itemdef || ((STAT(WEAPONS, e) & WEPSET_SUPERWEAPONS) && (STAT(WEAPONS, it) & WEPSET_SUPERWEAPONS))))
133             continue;
134         if (it.scheduledrespawntime <= time)
135             isavailable = true;
136         else if (t == 0 || it.scheduledrespawntime < t)
137             t = it.scheduledrespawntime;
138     });
139     if (isavailable)
140         t = -t; // let know the client there's another available item
141     return t;
142 }
143
144 MUTATOR_HOOKFUNCTION(itemstime, reset_map_global)
145 {
146     Item_ItemsTime_ResetTimes();
147     // ALL the times need to be reset before .reset()ing each item
148     // since Item_Reset schedules respawn of superweapons and powerups
149     IL_EACH(g_items, it.reset,
150     {
151         Item_ItemsTime_SetTime(it, 0);
152     });
153     Item_ItemsTime_SetTimesForAllPlayers();
154 }
155
156 MUTATOR_HOOKFUNCTION(itemstime, MakePlayerObserver)
157 {
158     entity player = M_ARGV(0, entity);
159
160     Item_ItemsTime_SetTimesForPlayer(player);
161 }
162
163 MUTATOR_HOOKFUNCTION(itemstime, ClientConnect, CBC_ORDER_LAST)
164 {
165     entity player = M_ARGV(0, entity);
166
167         if(IS_PLAYER(player))
168         {
169                 // client became player on connection skipping putObserverInServer step
170                 if (IS_REAL_CLIENT(player))
171                 if (warmup_stage || autocvar_sv_itemstime == 2)
172                         Item_ItemsTime_SetTimesForPlayer(player);
173         }
174 }
175
176 MUTATOR_HOOKFUNCTION(itemstime, PlayerSpawn)
177 {
178     if (warmup_stage || autocvar_sv_itemstime == 2) return;
179     entity player = M_ARGV(0, entity);
180
181     Item_ItemsTime_ResetTimesForPlayer(player);
182 }
183
184 #endif
185
186 #ifdef CSQC
187
188 void DrawItemsTimeItem(vector myPos, vector mySize, float ar, string item_icon, float item_time, bool item_available, float item_availableTime)
189 {
190     float t = 0;
191     vector color = '0 0 0';
192     float picalpha;
193
194     if (autocvar_hud_panel_itemstime_hidespawned == 2)
195         picalpha = 1;
196     else if (item_available)
197         picalpha = blink(0.85, 0.15, 5);
198     else
199         picalpha = 0.5;
200     t = floor(item_time - time + 0.999);
201     if (t < 5)
202         color = '0.7 0 0';
203     else if (t < 10)
204         color = '0.7 0.7 0';
205     else
206         color = '1 1 1';
207
208     vector picpos, numpos;
209     if (autocvar_hud_panel_itemstime_iconalign)
210     {
211         numpos = myPos;
212         picpos = myPos + eX * (ar - 1) * mySize_y;
213     }
214     else
215     {
216         numpos = myPos + eX * mySize_y;
217         picpos = myPos;
218     }
219
220     if (t > 0 && autocvar_hud_panel_itemstime_progressbar)
221     {
222         vector p_pos, p_size;
223         if (autocvar_hud_panel_itemstime_progressbar_reduced)
224         {
225             p_pos = numpos;
226             p_size = vec2(((ar - 1)/ar) * mySize.x, mySize.y);
227         }
228         else
229         {
230             p_pos = myPos;
231             p_size = mySize;
232         }
233         HUD_Panel_DrawProgressBar(p_pos, p_size, autocvar_hud_panel_itemstime_progressbar_name, t/autocvar_hud_panel_itemstime_progressbar_maxtime, 0, autocvar_hud_panel_itemstime_iconalign, color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
234     }
235
236     if(autocvar_hud_panel_itemstime_text)
237     {
238         if(t > 0)
239             drawstring_aspect(numpos, ftos(t), vec2(((ar - 1)/ar) * mySize.x, mySize.y), color, panel_fg_alpha, DRAWFLAG_NORMAL);
240         else if(precache_pic("gfx/hud/default/checkmark")) // COMPAT: check if this image exists, as 0.8.1 clients lack it
241             drawpic_aspect_skin(numpos, "checkmark", vec2((ar - 1) * mySize.y, mySize.y), '1 1 1', panel_fg_alpha * picalpha, DRAWFLAG_NORMAL);
242         else // legacy code, if the image is missing just center the icon
243             picpos.x = myPos.x + mySize.x / 2 - mySize.y / 2;
244     }
245     if (item_availableTime)
246         drawpic_aspect_skin_expanding(picpos, item_icon, '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * picalpha, DRAWFLAG_NORMAL, item_availableTime);
247     drawpic_aspect_skin(picpos, item_icon, '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * picalpha, DRAWFLAG_NORMAL);
248 }
249
250 float Item_ItemsTime_GetTime(int item)
251 {
252     if(autocvar__hud_configure)
253     {
254         switch(item)
255         {
256             case ITEM_ArmorMega.m_id: return time + 0;
257             case ITEM_HealthMega.m_id: return time + 8;
258             case ITEM_Strength.m_id: return time + 0;
259             case ITEM_Shield.m_id: return time + 4;
260         }
261
262         return -1; // don't show others
263     }
264     else
265         return ItemsTime_time[item];
266 }
267
268 void HUD_ItemsTime()
269 {
270     if (!autocvar__hud_configure)
271     {
272         if (!(
273             (autocvar_hud_panel_itemstime == 1 && spectatee_status != 0)
274         ||      (autocvar_hud_panel_itemstime == 2 && (spectatee_status != 0 || warmup_stage || STAT(ITEMSTIME) == 2))
275             )) { return; }
276     }
277
278     int count = 0;
279     if (autocvar_hud_panel_itemstime_hidespawned == 1)
280     {
281         FOREACH(Items, Item_ItemsTime_Allow(it), {
282             count += (Item_ItemsTime_GetTime(it.m_id) > time || -Item_ItemsTime_GetTime(it.m_id) > time);
283         });
284         count += (Item_ItemsTime_GetTime(Items_MAX) > time || -Item_ItemsTime_GetTime(Items_MAX) > time);
285     }
286     else if (autocvar_hud_panel_itemstime_hidespawned == 2)
287     {
288         FOREACH(Items, Item_ItemsTime_Allow(it), {
289             count += (Item_ItemsTime_GetTime(it.m_id) > time);
290         });
291         count += (Item_ItemsTime_GetTime(Items_MAX) > time);
292     }
293     else
294     {
295         FOREACH(Items, Item_ItemsTime_Allow(it), {
296             count += (Item_ItemsTime_GetTime(it.m_id) != -1);
297         });
298         count += (Item_ItemsTime_GetTime(Items_MAX) != -1);
299     }
300     if (count == 0)
301         return;
302
303     HUD_Panel_LoadCvars();
304
305     vector pos, mySize;
306     pos = panel_pos;
307     mySize = panel_size;
308
309     if (panel_bg_padding)
310     {
311         pos += '1 1 0' * panel_bg_padding;
312         mySize -= '2 2 0' * panel_bg_padding;
313     }
314
315     float rows, columns;
316     float ar = max(2, autocvar_hud_panel_itemstime_ratio) + 1;
317     rows = HUD_GetRowCount(count, mySize, ar);
318     columns = ceil(count/rows);
319
320     vector itemstime_size = vec2(mySize.x / columns, mySize.y / rows);
321
322     vector offset = '0 0 0';
323     float newSize;
324     if (autocvar_hud_panel_itemstime_dynamicsize)
325     {
326         if (autocvar__hud_configure)
327         if (hud_configure_menu_open != 2)
328             HUD_Panel_DrawBg(); // also draw the bg of the entire panel
329
330         // reduce panel to avoid spacing items
331         if (itemstime_size.x / itemstime_size.y < ar)
332         {
333             newSize = rows * itemstime_size.x / ar;
334             pos.y += (mySize.y - newSize) / 2;
335             mySize.y = newSize;
336             itemstime_size.y = mySize.y / rows;
337         }
338         else
339         {
340             newSize = columns * itemstime_size.y * ar;
341             pos.x += (mySize.x - newSize) / 2;
342             mySize.x = newSize;
343             itemstime_size.x = mySize.x / columns;
344         }
345         panel_pos = pos - '1 1 0' * panel_bg_padding;
346         panel_size = mySize + '2 2 0' * panel_bg_padding;
347     }
348     else
349     {
350         if (itemstime_size.x/itemstime_size.y > ar)
351         {
352             newSize = ar * itemstime_size.y;
353             offset.x = itemstime_size.x - newSize;
354             pos.x += offset.x/2;
355             itemstime_size.x = newSize;
356         }
357         else
358         {
359             newSize = 1/ar * itemstime_size.x;
360             offset.y = itemstime_size.y - newSize;
361             pos.y += offset.y/2;
362             itemstime_size.y = newSize;
363         }
364     }
365
366     HUD_Scale_Enable();
367     HUD_Panel_DrawBg();
368
369     float row = 0, column = 0;
370     bool item_available;
371     int id = 0;
372     string icon = "";
373     FOREACH(Items, Item_ItemsTime_Allow(it) && Item_ItemsTime_GetTime(it.m_id) != -1, {
374         id = it.m_id;
375         icon = it.m_icon;
376
377 LABEL(iteration)
378         float item_time = Item_ItemsTime_GetTime(id);
379         if (item_time < -1)
380         {
381             item_available = true;
382             item_time = -item_time;
383         }
384         else
385             item_available = (item_time <= time);
386
387         if (Item_ItemsTime_GetTime(id) >= 0)
388         {
389             if (time <= Item_ItemsTime_GetTime(id))
390                 ItemsTime_availableTime[id] = 0;
391             else if (ItemsTime_availableTime[id] == 0)
392                 ItemsTime_availableTime[id] = time;
393         }
394         else if (ItemsTime_availableTime[id] == 0)
395             ItemsTime_availableTime[id] = time;
396
397         float f = (time - ItemsTime_availableTime[id]) * 2;
398         f = (f > 1) ? 0 : bound(0, f, 1);
399
400         if (autocvar_hud_panel_itemstime_hidespawned == 1)
401             if (!(Item_ItemsTime_GetTime(id) > time || -Item_ItemsTime_GetTime(id) > time))
402                 continue;
403
404         if (autocvar_hud_panel_itemstime_hidespawned == 2)
405             if (!(Item_ItemsTime_GetTime(id) > time))
406                 continue;
407
408         DrawItemsTimeItem(pos + vec2(column * (itemstime_size.x + offset.x), row * (itemstime_size.y + offset.y)), itemstime_size, ar, icon, item_time, item_available, f);
409         ++row;
410         if (row >= rows)
411         {
412             row = 0;
413             column = column + 1;
414         }
415         if(id == Items_MAX) // can happen only in the last fake iteration
416                 break;
417     });
418     // add another fake iteration for superweapons time
419     if(id < Items_MAX && Item_ItemsTime_GetTime(Items_MAX) != -1)
420     {
421                 id = Items_MAX;
422                 icon = "superweapons";
423                 goto iteration;
424     }
425 }
426
427 #endif