]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/items/items.qc
Use correct pickup item bboxes on clients
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / items / items.qc
1 #include "items.qh"
2
3 #include <client/main.qh>
4 #include <common/items/_mod.qh>
5 #include <common/physics/movetypes/movetypes.qh>
6 #include <common/weapons/_all.qh>
7 #include <lib/csqcmodel/cl_model.qh>
8 #include <lib/csqcmodel/common.qh>
9 #include <lib/warpzone/common.qh>
10
11 bool autocvar_cl_ghost_items_vehicle = true;
12 .vector item_glowmod;
13 .bool item_simple; // probably not really needed, but better safe than sorry
14 .float alpha;
15 .bool pushable;
16 void Item_SetAlpha(entity this)
17 {
18         bool veh_hud = (hud && autocvar_cl_ghost_items_vehicle);
19
20         if(!veh_hud && (this.ItemStatus & ITS_AVAILABLE))
21         {
22                 this.alpha = 1;
23                 this.colormod = '1 1 1';
24                 this.glowmod = this.item_glowmod;
25         }
26         else
27         {
28                 this.alpha = autocvar_cl_ghost_items;
29                 this.colormod = this.glowmod = autocvar_cl_ghost_items_color;
30         }
31
32         if((!veh_hud) && (this.ItemStatus & ITS_STAYWEP))
33         {
34                 this.colormod = this.glowmod = autocvar_cl_weapon_stay_color;
35                 this.alpha = autocvar_cl_weapon_stay_alpha;
36         }
37
38         this.drawmask = ((this.alpha <= 0) ? 0 : MASK_NORMAL);
39 }
40
41 void ItemDraw(entity this)
42 {
43     if(this.gravity)
44     {
45         Movetype_Physics_MatchServer(this, false);
46         if(IS_ONGROUND(this))
47         { // For some reason avelocity gets set to '0 0 0' here ...
48             this.oldorigin = this.origin;
49             this.gravity = 0;
50
51             if(autocvar_cl_animate_items)
52             { // ... so reset it if animations are requested.
53                 if(this.ItemStatus & ITS_ANIMATE1)
54                     this.avelocity = '0 180 0';
55
56                 if(this.ItemStatus & ITS_ANIMATE2)
57                     this.avelocity = '0 -90 0';
58             }
59
60             // delay is for blocking item's position for a while;
61             // it's a workaround for dropped weapons that receive the position
62             // another time right after they spawn overriding animation position
63             this.onground_time = time + 0.5;
64         }
65     }
66     else if (autocvar_cl_animate_items && !this.item_simple) // no bobbing applied to simple items, for consistency's sake (no visual difference between ammo and weapons)
67     {
68         if(this.ItemStatus & ITS_ANIMATE1)
69         {
70                 this.angles += this.avelocity * frametime;
71             float fade_in = bound(0, time - this.onground_time, 1);
72             setorigin(this, this.oldorigin + fade_in * ('0 0 10' + '0 0 8' * sin((time - this.onground_time) * 2)));
73         }
74
75         if(this.ItemStatus & ITS_ANIMATE2)
76         {
77                 this.angles += this.avelocity * frametime;
78             float fade_in = bound(0, time - this.onground_time, 1);
79             setorigin(this, this.oldorigin + fade_in * ('0 0 8' + '0 0 4' * sin((time - this.onground_time) * 3)));
80         }
81     }
82
83     Item_SetAlpha(this);
84 }
85
86 void Item_PreDraw(entity this)
87 {
88         if(warpzone_warpzones_exist)
89         {
90                 setpredraw(this, func_null); // no need to keep running this
91                 return;
92         }
93         float alph;
94         vector org = getpropertyvec(VF_ORIGIN);
95         //if(!checkpvs(org, this)) // this makes sense as long as we don't support recursive warpzones
96                 //alph = 0; // this shouldn't be needed, since items behind walls are culled anyway
97         if(this.fade_start)
98         {
99                 if(vdist(org - this.origin, >, this.fade_end))
100                         alph = 0; // save on some processing
101                 else if(vdist(org - this.origin, <, this.fade_start))
102                         alph = 1; // more processing saved
103                 else
104                         alph = bound(0, (this.fade_end - vlen(org - this.origin - 0.5 * (this.mins + this.maxs))) / (this.fade_end - this.fade_start), 1);
105         }
106         else
107                 alph = 1;
108         //printf("%v <-> %v\n", view_origin, this.origin + 0.5 * (this.mins + this.maxs));
109         if(!hud && (this.ItemStatus & ITS_AVAILABLE))
110                 this.alpha = alph;
111         if(alph <= 0)
112                 this.drawmask = 0;
113         //else
114                 //this.drawmask = MASK_NORMAL; // reset by the setalpha function
115 }
116
117 void ItemRemove(entity this)
118 {
119         strfree(this.mdl);
120 }
121
122 HashMap ENT_CLIENT_ITEM_simple;
123 STATIC_INIT(ENT_CLIENT_ITEM_simple)
124 {
125         HM_NEW(ENT_CLIENT_ITEM_simple);
126 }
127 SHUTDOWN(ENT_CLIENT_ITEM_simple)
128 {
129         HM_DELETE(ENT_CLIENT_ITEM_simple);
130 }
131
132 NET_HANDLE(ENT_CLIENT_ITEM, bool isnew)
133 {
134     int sf = ReadByte();
135
136     if(sf & ISF_LOCATION)
137     {
138         vector org = ReadVector();
139         setorigin(this, org);
140         this.oldorigin = org;
141     }
142
143     if(sf & ISF_ANGLES)
144     {
145         this.angles = ReadAngleVector();
146     }
147
148     if(sf & ISF_STATUS) // need to read/write status first so model can handle simple, fb etc.
149     {
150         this.ItemStatus = ReadByte();
151
152         Item_SetAlpha(this);
153
154         if(this.ItemStatus & ITS_ALLOWFB)
155             this.effects |= EF_FULLBRIGHT;
156         else
157             this.effects &= ~EF_FULLBRIGHT;
158
159         if(this.ItemStatus & ITS_GLOW)
160         {
161             if(this.ItemStatus & ITS_AVAILABLE)
162                 this.effects |= (EF_ADDITIVE | EF_FULLBRIGHT);
163             else
164                 this.effects &= ~(EF_ADDITIVE | EF_FULLBRIGHT);
165         }
166     }
167
168     if(sf & ISF_MODEL)
169     {
170         this.drawmask  = MASK_NORMAL;
171                 set_movetype(this, MOVETYPE_TOSS);
172                 if (isnew) IL_PUSH(g_drawables, this);
173         this.draw       = ItemDraw;
174         this.solid = SOLID_TRIGGER;
175         //this.flags |= FL_ITEM;
176
177         this.fade_end = ReadShort();
178         this.fade_start = ReadShort();
179         if(!warpzone_warpzones_exist && this.fade_start && !autocvar_cl_items_nofade)
180                 setpredraw(this, Item_PreDraw);
181
182                 strfree(this.mdl);
183
184         string _fn = ReadString();
185         this.item_simple = false; // reset it!
186
187         if(autocvar_cl_simple_items && (this.ItemStatus & ITS_ALLOWSI))
188         {
189             string _fn2 = substring(_fn, 0 , strlen(_fn) -4);
190             this.item_simple = true;
191
192                         #define extensions(x) \
193                                 x(md3) \
194                                 x(dpm) \
195                                 x(iqm) \
196                                 x(mdl) \
197                                 /**/
198                         #define tryext(ext) { \
199                                 string s = strcat(_fn2, autocvar_cl_simpleitems_postfix, "." #ext); \
200                                 string cached = HM_gets(ENT_CLIENT_ITEM_simple, s); \
201                                 if (cached == "") { \
202                                         HM_sets(ENT_CLIENT_ITEM_simple, s, cached = fexists(s) ? "1" : "0"); \
203                                 } \
204                                 if (cached != "0") { \
205                                         strcpy(this.mdl, s); \
206                                         break; \
207                                 } \
208                         }
209                         do {
210                                 extensions(tryext);
211                                 this.item_simple = false;
212                 LOG_TRACEF("Simple item requested for %s but no model exists for it", _fn);
213                         } while (0);
214                         #undef tryext
215                         #undef extensions
216         }
217
218         if(!this.item_simple)
219             strcpy(this.mdl, _fn);
220
221         if(this.mdl == "")
222             LOG_WARNF("this.mdl is unset for item %s", this.classname);
223
224         precache_model(this.mdl);
225         _setmodel(this, this.mdl);
226
227         this.skin = ReadByte();
228     }
229
230         if(sf & ISF_SIZE && sf & ISF_SIZE2) // Default
231                 setsize(this, ITEM_D_MINS, ITEM_D_MAXS);
232         else if(sf & ISF_SIZE && !(sf & ISF_SIZE2)) // Small
233                 setsize(this, ITEM_S_MINS, ITEM_S_MAXS);
234         else if(!(sf & ISF_SIZE) && sf & ISF_SIZE2) // Large
235                 setsize(this, ITEM_D_MINS, ITEM_L_MAXS);
236
237     if(sf & ISF_COLORMAP)
238     {
239         this.colormap = ReadShort();
240         this.item_glowmod_x = ReadByte() / 255.0;
241         this.item_glowmod_y = ReadByte() / 255.0;
242         this.item_glowmod_z = ReadByte() / 255.0;
243     }
244
245     if(sf & ISF_DROP)
246     {
247         this.gravity = 1;
248         this.pushable = true;
249         //this.angles = '0 0 0';
250         set_movetype(this, MOVETYPE_TOSS);
251         this.velocity = ReadVector();
252         setorigin(this, this.oldorigin);
253
254         if(!this.move_time)
255         {
256             this.move_time = time;
257             this.spawntime = time;
258         }
259         else
260             this.move_time = max(this.move_time, time);
261     }
262
263     if(autocvar_cl_animate_items)
264     {
265         if(this.ItemStatus & ITS_ANIMATE1)
266             this.avelocity = '0 180 0';
267
268         if(this.ItemStatus & ITS_ANIMATE2)
269             this.avelocity = '0 -90 0';
270     }
271
272     this.entremove = ItemRemove;
273
274     return true;
275 }