]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/items/items.qc
Reorganise item code so that VM-specific code is in its correct directories and not...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / items / items.qc
1 #include "items.qh"
2
3 #include <common/items/_mod.qh>
4
5 #include <client/main.qh>
6 #include <common/physics/movetypes/movetypes.qh>
7 #include <common/weapons/_all.qh>
8 #include <lib/csqcmodel/cl_model.qh>
9 #include <lib/csqcmodel/common.qh>
10 #include <lib/warpzone/common.qh>
11
12 bool autocvar_cl_ghost_items_vehicle = true;
13 .vector item_glowmod;
14 .bool item_simple; // probably not really needed, but better safe than sorry
15 .float alpha;
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         this.origin = ReadVector();
139         setorigin(this, this.origin);
140         this.oldorigin = this.origin;
141     }
142
143     if(sf & ISF_ANGLES)
144     {
145         this.angles = ReadAngleVector();
146     }
147
148     if(sf & ISF_SIZE)
149     {
150         setsize(this, '-16 -16 0', '16 16 48');
151     }
152
153     if(sf & ISF_STATUS) // need to read/write status first so model can handle simple, fb etc.
154     {
155         this.ItemStatus = ReadByte();
156
157         Item_SetAlpha(this);
158
159         if(this.ItemStatus & ITS_ALLOWFB)
160             this.effects |= EF_FULLBRIGHT;
161         else
162             this.effects &= ~EF_FULLBRIGHT;
163
164         if(this.ItemStatus & ITS_GLOW)
165         {
166             if(this.ItemStatus & ITS_AVAILABLE)
167                 this.effects |= (EF_ADDITIVE | EF_FULLBRIGHT);
168             else
169                 this.effects &= ~(EF_ADDITIVE | EF_FULLBRIGHT);
170         }
171     }
172
173     if(sf & ISF_MODEL)
174     {
175         this.drawmask  = MASK_NORMAL;
176                 set_movetype(this, MOVETYPE_TOSS);
177                 if (isnew) IL_PUSH(g_drawables, this);
178         this.draw       = ItemDraw;
179         this.solid = SOLID_TRIGGER;
180         //this.flags |= FL_ITEM;
181
182         this.fade_end = ReadShort();
183         this.fade_start = ReadShort();
184         if(!warpzone_warpzones_exist && this.fade_start && !autocvar_cl_items_nofade)
185                 setpredraw(this, Item_PreDraw);
186
187                 strfree(this.mdl);
188
189         string _fn = ReadString();
190         this.item_simple = false; // reset it!
191
192         if(autocvar_cl_simple_items && (this.ItemStatus & ITS_ALLOWSI))
193         {
194             string _fn2 = substring(_fn, 0 , strlen(_fn) -4);
195             this.item_simple = true;
196
197                         #define extensions(x) \
198                                 x(md3) \
199                                 x(dpm) \
200                                 x(iqm) \
201                                 x(mdl) \
202                                 /**/
203                         #define tryext(ext) { \
204                                 string s = strcat(_fn2, autocvar_cl_simpleitems_postfix, "." #ext); \
205                                 string cached = HM_gets(ENT_CLIENT_ITEM_simple, s); \
206                                 if (cached == "") { \
207                                         HM_sets(ENT_CLIENT_ITEM_simple, s, cached = fexists(s) ? "1" : "0"); \
208                                 } \
209                                 if (cached != "0") { \
210                                         strcpy(this.mdl, s); \
211                                         break; \
212                                 } \
213                         }
214                         do {
215                                 extensions(tryext);
216                                 this.item_simple = false;
217                 LOG_TRACEF("Simple item requested for %s but no model exists for it", _fn);
218                         } while (0);
219                         #undef tryext
220                         #undef extensions
221         }
222
223         if(!this.item_simple)
224             strcpy(this.mdl, _fn);
225
226         if(this.mdl == "")
227             LOG_WARNF("this.mdl is unset for item %s", this.classname);
228
229         precache_model(this.mdl);
230         _setmodel(this, this.mdl);
231
232         setsize(this, '-16 -16 0', '16 16 48');
233     }
234
235     if(sf & ISF_COLORMAP)
236     {
237         this.colormap = ReadShort();
238         this.item_glowmod_x = ReadByte() / 255.0;
239         this.item_glowmod_y = ReadByte() / 255.0;
240         this.item_glowmod_z = ReadByte() / 255.0;
241     }
242
243     if(sf & ISF_DROP)
244     {
245         this.gravity = 1;
246         this.pushable = true;
247         //this.angles = '0 0 0';
248         set_movetype(this, MOVETYPE_TOSS);
249         this.velocity = ReadVector();
250         setorigin(this, this.oldorigin);
251
252         if(!this.move_time)
253         {
254             this.move_time = time;
255             this.spawntime = time;
256         }
257         else
258             this.move_time = max(this.move_time, time);
259     }
260
261     if(autocvar_cl_animate_items)
262     {
263         if(this.ItemStatus & ITS_ANIMATE1)
264             this.avelocity = '0 180 0';
265
266         if(this.ItemStatus & ITS_ANIMATE2)
267             this.avelocity = '0 -90 0';
268     }
269
270     this.entremove = ItemRemove;
271
272     return true;
273 }