6ee8869e45ce33b4c4ec555fd3a45970d7a4f380
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / t_items.qc
1 #include "t_items.qh"
2
3 #include "items/_mod.qh"
4
5 #if defined(SVQC)
6
7     #include "../server/bot/api.qh"
8
9     #include <server/mutators/_mod.qh>
10
11     #include "../server/weapons/common.qh"
12     #include "../server/weapons/selection.qh"
13     #include "../server/weapons/weaponsystem.qh"
14
15     #include "constants.qh"
16     #include <common/deathtypes/all.qh>
17     #include <common/notifications/all.qh>
18         #include "triggers/subs.qh"
19     #include "util.qh"
20
21     #include <common/monsters/_mod.qh>
22
23     #include <common/weapons/_all.qh>
24
25     #include "../lib/warpzone/util_server.qh"
26 #elif defined(CSQC)
27         #include "physics/movetypes/movetypes.qh"
28         #include <common/weapons/_all.qh>
29         #include "../lib/csqcmodel/cl_model.qh"
30         #include "../lib/csqcmodel/common.qh"
31 #endif
32
33 REGISTER_NET_LINKED(ENT_CLIENT_ITEM)
34
35 #ifdef CSQC
36 bool autocvar_cl_ghost_items_vehicle = true;
37 .vector item_glowmod;
38 void Item_SetAlpha(entity this)
39 {
40         bool veh_hud = (hud && autocvar_cl_ghost_items_vehicle);
41
42         if(!veh_hud && (this.ItemStatus & ITS_AVAILABLE))
43         {
44                 this.alpha = 1;
45                 this.colormod = '1 1 1';
46                 this.glowmod = this.item_glowmod;
47         }
48         else
49         {
50                 if (autocvar_cl_ghost_items_color)
51                 {
52                         this.alpha = autocvar_cl_ghost_items;
53                         this.colormod = this.glowmod = autocvar_cl_ghost_items_color;
54                 }
55                 else
56                         this.alpha = -1;
57         }
58
59         if(!veh_hud)
60         if(this.ItemStatus & ITS_STAYWEP)
61         {
62                 this.colormod = this.glowmod = autocvar_cl_weapon_stay_color;
63                 this.alpha = autocvar_cl_weapon_stay_alpha;
64         }
65 }
66
67 void ItemDraw(entity this)
68 {
69     if(this.gravity)
70     {
71         Movetype_Physics_MatchServer(this, false);
72         if(IS_ONGROUND(this))
73         { // For some reason avelocity gets set to '0 0 0' here ...
74             this.oldorigin = this.origin;
75             this.gravity = 0;
76
77             if(autocvar_cl_animate_items)
78             { // ... so reset it if animations are requested.
79                 if(this.ItemStatus & ITS_ANIMATE1)
80                     this.avelocity = '0 180 0';
81
82                 if(this.ItemStatus & ITS_ANIMATE2)
83                     this.avelocity = '0 -90 0';
84             }
85
86             // delay is for blocking item's position for a while;
87             // it's a workaround for dropped weapons that receive the position
88             // another time right after they spawn overriding animation position
89             this.onground_time = time + 0.5;
90         }
91     }
92     else if (autocvar_cl_animate_items)
93     {
94         if(this.ItemStatus & ITS_ANIMATE1)
95         {
96             this.angles += this.avelocity * frametime;
97             float fade_in = bound(0, time - this.onground_time, 1);
98             setorigin(this, this.oldorigin + fade_in * ('0 0 10' + '0 0 8' * sin((time - this.onground_time) * 2)));
99         }
100
101         if(this.ItemStatus & ITS_ANIMATE2)
102         {
103             this.angles += this.avelocity * frametime;
104             float fade_in = bound(0, time - this.onground_time, 1);
105             setorigin(this, this.oldorigin + fade_in * ('0 0 8' + '0 0 4' * sin((time - this.onground_time) * 3)));
106         }
107     }
108
109     Item_SetAlpha(this);
110 }
111
112 void ItemDrawSimple(entity this)
113 {
114     if(this.gravity)
115     {
116         Movetype_Physics_MatchServer(this, false);
117
118         if(IS_ONGROUND(this))
119             this.gravity = 0;
120     }
121
122     Item_SetAlpha(this);
123 }
124
125 void Item_PreDraw(entity this)
126 {
127         if(warpzone_warpzones_exist)
128         {
129                 // just incase warpzones were initialized last, reset these
130                 //this.alpha = 1; // alpha is already set by the draw function
131                 this.drawmask = MASK_NORMAL;
132                 return;
133         }
134         float alph;
135         vector org = getpropertyvec(VF_ORIGIN);
136         if(!checkpvs(org, this)) // this makes sense as long as we don't support recursive warpzones
137                 alph = 0;
138         else if(this.fade_start)
139                 alph = bound(0, (this.fade_end - vlen(org - this.origin - 0.5 * (this.mins + this.maxs))) / (this.fade_end - this.fade_start), 1);
140         else
141                 alph = 1;
142         //printf("%v <-> %v\n", view_origin, this.origin + 0.5 * (this.mins + this.maxs));
143         if(!hud && (this.ItemStatus & ITS_AVAILABLE))
144                 this.alpha = alph;
145         if(alph <= 0)
146                 this.drawmask = 0;
147         else
148                 this.drawmask = MASK_NORMAL;
149 }
150
151 void ItemRemove(entity this)
152 {
153         if(this.mdl)
154                 strunzone(this.mdl);
155 }
156
157 NET_HANDLE(ENT_CLIENT_ITEM, bool isnew)
158 {
159     int sf = ReadByte();
160
161     if(sf & ISF_LOCATION)
162     {
163         this.origin_x = ReadCoord();
164         this.origin_y = ReadCoord();
165         this.origin_z = ReadCoord();
166         setorigin(this, this.origin);
167         this.oldorigin = this.origin;
168     }
169
170     if(sf & ISF_ANGLES)
171     {
172         this.angles_x = ReadAngle();
173         this.angles_y = ReadAngle();
174         this.angles_z = ReadAngle();
175     }
176
177     if(sf & ISF_SIZE)
178     {
179         float use_bigsize = ReadByte();
180         setsize(this, '-16 -16 0', (use_bigsize) ? '16 16 48' : '16 16 32');
181     }
182
183     if(sf & ISF_STATUS) // need to read/write status frist so model can handle simple, fb etc.
184     {
185         this.ItemStatus = ReadByte();
186
187         Item_SetAlpha(this);
188
189         if(autocvar_cl_fullbright_items)
190             if(this.ItemStatus & ITS_ALLOWFB)
191                 this.effects |= EF_FULLBRIGHT;
192
193         if(this.ItemStatus & ITS_GLOW)
194         {
195             if(this.ItemStatus & ITS_AVAILABLE)
196                 this.effects |= (EF_ADDITIVE | EF_FULLBRIGHT);
197             else
198                  this.effects &= ~(EF_ADDITIVE | EF_FULLBRIGHT);
199         }
200     }
201
202     if(sf & ISF_MODEL)
203     {
204         this.drawmask  = MASK_NORMAL;
205                 set_movetype(this, MOVETYPE_TOSS);
206                 if (isnew) IL_PUSH(g_drawables, this);
207         this.draw       = ItemDraw;
208         this.solid = SOLID_TRIGGER;
209         //this.flags |= FL_ITEM;
210
211         bool use_bigsize = ReadByte();
212
213         this.fade_end = ReadShort();
214         this.fade_start = ReadShort();
215         if(this.fade_start && !autocvar_cl_items_nofade)
216                 setpredraw(this, Item_PreDraw);
217
218         if(this.mdl)
219             strunzone(this.mdl);
220
221         this.mdl = "";
222         string _fn = ReadString();
223
224         if(autocvar_cl_simple_items && (this.ItemStatus & ITS_ALLOWSI))
225         {
226             string _fn2 = substring(_fn, 0 , strlen(_fn) -4);
227             this.draw = ItemDrawSimple;
228
229             if(fexists(sprintf("%s%s.md3", _fn2, autocvar_cl_simpleitems_postfix)))
230                 this.mdl = strzone(sprintf("%s%s.md3", _fn2, autocvar_cl_simpleitems_postfix));
231             else if(fexists(sprintf("%s%s.dpm", _fn2, autocvar_cl_simpleitems_postfix)))
232                 this.mdl = strzone(sprintf("%s%s.dpm", _fn2, autocvar_cl_simpleitems_postfix));
233             else if(fexists(sprintf("%s%s.iqm", _fn2, autocvar_cl_simpleitems_postfix)))
234                 this.mdl = strzone(sprintf("%s%s.iqm", _fn2, autocvar_cl_simpleitems_postfix));
235             else if(fexists(sprintf("%s%s.mdl", _fn2, autocvar_cl_simpleitems_postfix)))
236                 this.mdl = strzone(sprintf("%s%s.mdl", _fn2, autocvar_cl_simpleitems_postfix));
237             else
238             {
239                 this.draw = ItemDraw;
240                 LOG_TRACE("Simple item requested for ", _fn, " but no model exists for it");
241             }
242         }
243
244         if(this.draw != ItemDrawSimple)
245             this.mdl = strzone(_fn);
246
247
248         if(this.mdl == "")
249             LOG_TRACE("^1WARNING!^7 this.mdl is unset for item ", this.classname, ", tell tZork about this!");
250
251         precache_model(this.mdl);
252         _setmodel(this, this.mdl);
253
254         setsize(this, '-16 -16 0', (use_bigsize) ? '16 16 48' : '16 16 32');
255     }
256
257     if(sf & ISF_COLORMAP)
258     {
259         this.colormap = ReadShort();
260         this.item_glowmod_x = ReadByte() / 255.0;
261         this.item_glowmod_y = ReadByte() / 255.0;
262         this.item_glowmod_z = ReadByte() / 255.0;
263     }
264
265     if(sf & ISF_DROP)
266     {
267         this.gravity = 1;
268         this.pushable = true;
269         //this.angles = '0 0 0';
270         set_movetype(this, MOVETYPE_TOSS);
271         this.velocity_x = ReadCoord();
272         this.velocity_y = ReadCoord();
273         this.velocity_z = ReadCoord();
274         setorigin(this, this.oldorigin);
275
276         if(!this.move_time)
277         {
278             this.move_time = time;
279             this.spawntime = time;
280         }
281         else
282             this.move_time = max(this.move_time, time);
283     }
284
285     if(autocvar_cl_animate_items)
286     {
287         if(this.ItemStatus & ITS_ANIMATE1)
288             this.avelocity = '0 180 0';
289
290         if(this.ItemStatus & ITS_ANIMATE2)
291             this.avelocity = '0 -90 0';
292     }
293
294     this.entremove = ItemRemove;
295
296     return true;
297 }
298
299 #endif
300
301 #ifdef SVQC
302 bool ItemSend(entity this, entity to, int sf)
303 {
304         if(this.gravity)
305                 sf |= ISF_DROP;
306         else
307                 sf &= ~ISF_DROP;
308
309         WriteHeader(MSG_ENTITY, ENT_CLIENT_ITEM);
310         WriteByte(MSG_ENTITY, sf);
311
312         //WriteByte(MSG_ENTITY, this.cnt);
313         if(sf & ISF_LOCATION)
314         {
315                 WriteCoord(MSG_ENTITY, this.origin.x);
316                 WriteCoord(MSG_ENTITY, this.origin.y);
317                 WriteCoord(MSG_ENTITY, this.origin.z);
318         }
319
320         if(sf & ISF_ANGLES)
321         {
322                 WriteAngle(MSG_ENTITY, this.angles_x);
323                 WriteAngle(MSG_ENTITY, this.angles_y);
324                 WriteAngle(MSG_ENTITY, this.angles_z);
325         }
326
327         if(sf & ISF_SIZE)
328         {
329                 Pickup p = this.itemdef;
330                 WriteByte(MSG_ENTITY, p.instanceOfPowerup || p.instanceOfHealth || p.instanceOfArmor);
331         }
332
333         if(sf & ISF_STATUS)
334                 WriteByte(MSG_ENTITY, this.ItemStatus);
335
336         if(sf & ISF_MODEL)
337         {
338                 Pickup p = this.itemdef;
339                 WriteByte(MSG_ENTITY, p.instanceOfPowerup || p.instanceOfHealth || p.instanceOfArmor);
340                 WriteShort(MSG_ENTITY, this.fade_end);
341                 WriteShort(MSG_ENTITY, this.fade_start);
342
343                 if(this.mdl == "")
344                         LOG_TRACE("^1WARNING!^7 this.mdl is unset for item ", this.classname, "expect a crash just about now");
345
346                 WriteString(MSG_ENTITY, this.mdl);
347         }
348
349
350         if(sf & ISF_COLORMAP)
351         {
352                 WriteShort(MSG_ENTITY, this.colormap);
353                 WriteByte(MSG_ENTITY, this.glowmod.x * 255.0);
354         WriteByte(MSG_ENTITY, this.glowmod.y * 255.0);
355         WriteByte(MSG_ENTITY, this.glowmod.z * 255.0);
356         }
357
358         if(sf & ISF_DROP)
359         {
360                 WriteCoord(MSG_ENTITY, this.velocity.x);
361                 WriteCoord(MSG_ENTITY, this.velocity.y);
362                 WriteCoord(MSG_ENTITY, this.velocity.z);
363         }
364
365         return true;
366 }
367
368 void ItemUpdate(entity this)
369 {
370         this.oldorigin = this.origin;
371         this.SendFlags |= ISF_LOCATION;
372 }
373
374 void UpdateItemAfterTeleport(entity this)
375 {
376         if(getSendEntity(this) == ItemSend)
377                 ItemUpdate(this);
378 }
379
380 bool have_pickup_item(entity this)
381 {
382         if(this.itemdef.instanceOfPowerup)
383         {
384                 if(autocvar_g_powerups > 0)
385                         return true;
386                 if(autocvar_g_powerups == 0)
387                         return false;
388         }
389         else
390         {
391                 if(autocvar_g_pickup_items > 0)
392                         return true;
393                 if(autocvar_g_pickup_items == 0)
394                         return false;
395                 if(g_weaponarena)
396                         if(this.weapons || (this.items & IT_AMMO)) // no item or ammo pickups in weaponarena
397                                 return false;
398         }
399         return true;
400 }
401
402 /*
403 float Item_Customize()
404 {
405         if(this.spawnshieldtime)
406                 return true;
407         if(this.weapons & ~other.weapons)
408         {
409                 this.colormod = '0 0 0';
410                 this.glowmod = this.colormod;
411                 this.alpha = 0.5 + 0.5 * g_ghost_items; // halfway more alpha
412                 return true;
413         }
414         else
415         {
416                 if(g_ghost_items)
417                 {
418                         this.colormod = stov(autocvar_g_ghost_items_color);
419                         this.glowmod = this.colormod;
420                         this.alpha = g_ghost_items;
421                         return true;
422                 }
423                 else
424                         return false;
425         }
426 }
427 */
428
429 void Item_Show (entity e, float mode)
430 {
431         e.effects &= ~(EF_ADDITIVE | EF_STARDUST | EF_FULLBRIGHT | EF_NODEPTHTEST);
432         e.ItemStatus &= ~ITS_STAYWEP;
433         entity def = e.itemdef;
434         if (mode > 0)
435         {
436                 // make the item look normal, and be touchable
437                 e.model = e.mdl;
438                 e.solid = SOLID_TRIGGER;
439                 e.spawnshieldtime = 1;
440                 e.ItemStatus |= ITS_AVAILABLE;
441         }
442         else if (mode < 0)
443         {
444                 // hide the item completely
445                 e.model = string_null;
446                 e.solid = SOLID_NOT;
447                 e.spawnshieldtime = 1;
448                 e.ItemStatus &= ~ITS_AVAILABLE;
449         }
450         else {
451         bool nostay = def.instanceOfWeaponPickup ? !!(def.m_weapon.weapons & WEPSET_SUPERWEAPONS) : false // no weapon-stay on superweapons
452                 || e.team // weapon stay isn't supported for teamed weapons
453                 ;
454         if(def.instanceOfWeaponPickup && !nostay && g_weapon_stay)
455         {
456                 // make the item translucent and not touchable
457                 e.model = e.mdl;
458                 e.solid = SOLID_TRIGGER; // can STILL be picked up!
459                 e.effects |= EF_STARDUST;
460                 e.spawnshieldtime = 0; // field indicates whether picking it up may give you anything other than the weapon
461                 e.ItemStatus |= (ITS_AVAILABLE | ITS_STAYWEP);
462         }
463         else
464         {
465                 //setmodel(e, "null");
466                 e.solid = SOLID_NOT;
467                 e.colormod = '0 0 0';
468                 //e.glowmod = e.colormod;
469                 e.spawnshieldtime = 1;
470                 e.ItemStatus &= ~ITS_AVAILABLE;
471         }}
472
473         if (def.m_glow)
474                 e.ItemStatus |= ITS_GLOW;
475
476         if (autocvar_g_nodepthtestitems)
477                 e.effects |= EF_NODEPTHTEST;
478
479
480         if (autocvar_g_fullbrightitems)
481                 e.ItemStatus |= ITS_ALLOWFB;
482
483         if (autocvar_sv_simple_items)
484                 e.ItemStatus |= ITS_ALLOWSI;
485