Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
items.qc
Go to the documentation of this file.
1#include "items.qh"
2
3#include <client/main.qh>
10
13.float alpha;
15.float anim_start_time; // reusing for bob waveform synchronisation
16.vector angles_held; // reusing for (re)storing original angles
17.float wait, delay, pointtime; // reusing for despawn effects
18.vector m_mins, m_maxs; // reusing for storing standard bbox (same purpose as in SVQC itemdef)
19
29
30void ItemSetModel(entity this, bool wantsimple)
31{
32 if(wantsimple)
33 {
34 string _fn2 = substring(this.mdl, 0 , strlen(this.mdl) -4);
35 #define extensions(x) \
36 x(iqm) \
37 x(dpm) \
38 x(md3) \
39 x(mdl) \
40 /**/
41 #define tryext(ext) { \
42 string s = strcat(_fn2, autocvar_cl_simpleitems_postfix, "." #ext); \
43 string cached = HM_gets(ENT_CLIENT_ITEM_simple, s); \
44 if (cached == "") { \
45 HM_sets(ENT_CLIENT_ITEM_simple, s, cached = fexists(s) ? "1" : "0"); \
46 } \
47 if (cached != "0") { \
48 this.model = s; \
49 this.item_simple = 1; \
50 break; \
51 } \
52 }
53 do {
55 this.model = this.mdl; // fall back to 3d model
56 this.item_simple = -1; // don't retry every frame
57 LOG_TRACEF("Simple item requested for %s but no model exists for it", this.mdl);
58 } while (0);
59 #undef tryext
60 #undef extensions
61 }
62 else
63 {
64 this.model = this.mdl;
65 this.item_simple = 0;
66 }
67
68 // this.model is an engine string so it doesn't need to be zoned and can't be unzoned
69 if(this.model == "")
70 LOG_WARNF("this.model is unset for item %s", this.classname);
71 precache_model(this.model);
72 _setmodel(this, this.model);
73 setsize(this, this.m_mins, this.m_maxs);
74}
75
76void ItemDraw(entity this)
77{
78 bool wantsimple = autocvar_cl_simple_items && (this.ItemStatus & ITS_ALLOWSI);
79 if(wantsimple != this.item_simple && this.item_simple != -1)
80 ItemSetModel(this, wantsimple);
81
82 // no bobbing applied to simple items, for consistency's sake (no visual difference between ammo and weapons)
83 bool animate = (autocvar_cl_items_animate & 1) && this.item_simple <= 0 && (this.ItemStatus & (ITS_ANIMATE1 | ITS_ANIMATE2));
84
85 // rotation must be set before running physics
86 if(!animate)
87 {
88 this.avelocity_y = 0;
89 this.angles = this.angles_held; // restore angles sent from server
90 }
91 else if(!this.avelocity_y) // unset by MOVETYPE_TOSS or animation was disabled previously
92 {
93 if(this.ItemStatus & ITS_ANIMATE1)
94 this.avelocity_y = 180;
95 else if(this.ItemStatus & ITS_ANIMATE2)
96 this.avelocity_y = -90;
97 }
98
99 // CSQC physics OR bobbing (both would look weird)
100 float bobheight = 0; // reset bob offset if animations are disabled
101 if(this.move_movetype && (!IS_ONGROUND(this) || this.velocity != '0 0 0'))
102 {
103 // this isn't equivalent to player prediction but allows smooth motion with very low ISF_LOCATION rate
104 // which requires running this even if the item is just outside visible range (it could be moving into range)
105 if(animate)
106 bobheight = this.origin_z - this.oldorigin_z;
108 this.oldorigin = this.origin; // update real (SVQC equivalent) origin
109 if(animate)
110 {
111 if(bobheight)
112 {
113 this.anim_start_time += frametime; // bobbing is paused this frame
114 this.oldorigin_z -= bobheight; // restore bob offset (CSQC physics uses the offset bbox)
115 }
116 else
117 {
118 this.anim_start_time = time; // starting our bob animation from NOW
119 if(this.ItemStatus & ITS_ANIMATE1)
120 bobheight = 10; // height of wave at 0 time
121 else if(this.ItemStatus & ITS_ANIMATE2)
122 bobheight = 8; // height of wave at 0 time
123 }
124 }
125 }
126 else if(animate)
127 {
128 this.angles += this.avelocity * frametime; // MOVETYPE_TOSS does this while it's moving
129
130 if(this.ItemStatus & ITS_ANIMATE1)
131 bobheight = 10 + 8 * sin((time - this.anim_start_time) * 2);
132 else if(this.ItemStatus & ITS_ANIMATE2)
133 bobheight = 8 + 4 * sin((time - this.anim_start_time) * 3);
134 }
135
136 // apply new bob offset
137 if (bobheight != this.origin_z - this.oldorigin_z)
138 {
139 this.origin_z = this.oldorigin_z + bobheight;
140 this.mins_z = this.m_mins.z - bobheight; // don't want the absmin and absmax to bob
141 this.maxs_z = this.m_maxs.z - bobheight;
142 }
143
144 // set alpha based on distance
145 this.alpha = 1;
146 this.drawmask = 0;
148 {
149 vector org = getpropertyvec(VF_ORIGIN);
150 if(vdist(org - this.origin, >, this.fade_end))
151 this.alpha = 0; // save on some processing
152 else if(autocvar_cl_items_fadedist > 0)
153 {
155 if(vdist(org - this.origin, >, this.fade_start))
156 this.alpha = bound(0, (this.fade_end - vlen(org - this.origin - 0.5 * (this.mins + this.maxs))) / (this.fade_end - this.fade_start), 1);
157 }
158 }
159
160 if(!this.alpha)
161 return;
162
163 // modify alpha based on availability and vehicle hud
164 if(this.ItemStatus & ITS_AVAILABLE)
165 {
166 if(hud) // apparently this means we're in a vehicle lol
167 {
170 }
171 else if(this.ItemStatus & ITS_STAYWEP)
172 {
175 }
176 else
177 {
178 this.colormod = '1 1 1';
179 this.glowmod = this.item_glowmod;
180 }
181 }
182 else
183 {
186 }
187
188 if(!this.alpha)
189 return;
190
191 // loot item despawn effects
192 if(this.ItemStatus & ITS_EXPIRING)
193 {
194 if(!this.wait) // when receiving the first message with ITS_EXPIRING set
195 {
196 this.wait = time + IT_DESPAWNFX_TIME; // it will despawn then
197 this.delay = 0.25;
198 }
199
201 this.alpha *= (this.wait - time) / IT_DESPAWNFX_TIME;
202
203 if((autocvar_cl_items_animate & 4) && time >= this.pointtime)
204 {
205 pointparticles(EFFECT_ITEM_DESPAWN, this.origin + '0 0 16', '0 0 0', 1);
206 if (this.delay > 0.0625)
207 this.delay *= 0.5;
208 this.pointtime = time + this.delay;
209 }
210 }
211
212 this.drawmask = this.alpha <= 0 ? 0 : MASK_NORMAL;
213}
214
216{
217 strfree(this.mdl);
218}
219
220NET_HANDLE(ENT_CLIENT_ITEM, bool isnew)
221{
222 int sf = ReadByte();
223
224 if(sf & ISF_LOCATION)
225 {
226 float bobheight = this.origin_z - this.oldorigin_z;
227 this.origin = this.oldorigin = ReadVector();
228 this.origin_z += bobheight; // restore animation offset (SVQC physics is unaware of CSQC bbox offset)
229 setorigin(this, this.origin); // link
230 }
231
232 if(sf & ISF_ANGLES)
233 {
234 this.angles = this.angles_held = ReadAngleVector();
235 }
236
237 if(sf & ISF_STATUS) // need to read/write status first so model can handle simple, fb etc.
238 {
239 int prevItemStatus = this.ItemStatus;
240 this.ItemStatus = ReadByte();
241
242 if(this.ItemStatus & ITS_ALLOWFB)
243 this.effects |= EF_FULLBRIGHT;
244 else
245 this.effects &= ~EF_FULLBRIGHT;
246
247 if(this.ItemStatus & ITS_AVAILABLE)
248 {
249 if(this.solid != SOLID_TRIGGER)
250 {
251 this.solid = SOLID_TRIGGER;
252 setorigin(this, this.origin); // link it to the area grid
253 }
254
255 if(this.ItemStatus & ITS_GLOW)
257 if(!(prevItemStatus & ITS_AVAILABLE) && this.alpha && !isnew)
258 pointparticles(EFFECT_ITEM_RESPAWN, (this.absmin + this.absmax) * 0.5, '0 0 0', 1);
259 }
260 else
261 {
262 if(this.solid != SOLID_NOT)
263 {
264 this.solid = SOLID_NOT;
265 setorigin(this, this.origin); // optimisation: unlink it from the area grid
266 }
267
268 if(this.ItemStatus & ITS_GLOW)
269 this.effects &= ~(EF_ADDITIVE | EF_FULLBRIGHT);
270 if((prevItemStatus & ITS_AVAILABLE) && this.alpha)
271 pointparticles(EFFECT_ITEM_PICKUP, (this.absmin + this.absmax) * 0.5, '0 0 0', 1);
272 }
273 }
274
275 if(sf & (ISF_SIZE | ISF_SIZE2)) // always true when it's spawned (in CSQC's perspective)
276 {
277 if(isnew)
278 {
279 IL_PUSH(g_drawables, this);
280 this.draw = ItemDraw;
281 this.flags |= FL_ITEM;
282 this.entremove = ItemRemove;
283 }
284
285 if(sf & ISF_SIZE && !(sf & ISF_SIZE2)) // Small
286 {
287 this.m_mins = ITEM_S_MINS;
288 this.m_maxs = ITEM_S_MAXS;
289 }
290 else if(!(sf & ISF_SIZE) && sf & ISF_SIZE2) // Large
291 {
292 this.m_mins = ITEM_D_MINS;
293 this.m_maxs = ITEM_L_MAXS;
294 }
295 else // Default
296 {
297 this.m_mins = ITEM_D_MINS;
298 this.m_maxs = ITEM_D_MAXS;
299 }
300
301 this.fade_end = ReadShort();
302
303 strcpy(this.mdl, ReadString());
304 this.item_simple = -2;
305
306 this.skin = ReadByte();
307 }
308
309 if(sf & ISF_COLORMAP)
310 {
311 this.colormap = ReadShort();
312 this.item_glowmod_x = ReadByte() / 255.0;
313 this.item_glowmod_y = ReadByte() / 255.0;
314 this.item_glowmod_z = ReadByte() / 255.0;
315 }
316
317 if(sf & ISF_DROP)
318 {
319 this.gravity = 1;
320 this.pushable = true;
322 this.velocity = ReadVector();
323 }
324 else if (this.gravity) // caution: kludge FIXME (with sv_legacy_bbox_expand)
325 {
326 // workaround for prediction errors caused by bbox discrepancy between SVQC and CSQC
327 this.gravity = 0; // don't do this kludge again
328 this.pushable = false; // no fun allowed
329 set_movetype(this, MOVETYPE_NONE); // disable physics
330 this.velocity = '0 0 0'; // disable it more
331 SET_ONGROUND(this); // extra overkill
332 }
333
334 if(sf & ISF_REMOVEFX && !(sf & (ISF_SIZE | ISF_SIZE2))) // TODO !isnew isn't reliable for this... are we double sending initialisations?
335 {
336 // no longer available to pick up, about to be removed
337 if (this.drawmask) // this.alpha > 0
338 pointparticles(EFFECT_ITEM_PICKUP, (this.absmin + this.absmax) * 0.5, '0 0 0', 1);
339 // removing now causes CSQC_Ent_Remove() to spam
340 this.drawmask = 0;
341 IL_REMOVE(g_drawables, this);
342 this.solid = SOLID_NOT;
343 }
344
345 return true;
346}
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
#define ReadString
vector colormod
Definition powerups.qc:21
#define tryext(ext)
void ItemRemove(entity this)
Definition items.qc:215
vector item_glowmod
Definition items.qc:11
vector m_mins
Definition items.qc:18
vector angles_held
Definition items.qc:16
bool pushable
Definition items.qc:14
vector m_maxs
Definition items.qc:18
float anim_start_time
Definition items.qc:15
int item_simple
Definition items.qc:12
float alpha
Definition items.qc:13
float delay
Definition items.qc:17
float pointtime
Definition items.qc:17
HashMap ENT_CLIENT_ITEM_simple
Definition items.qc:20
void ItemSetModel(entity this, bool wantsimple)
Definition items.qc:30
void ItemDraw(entity this)
Definition items.qc:76
float wait
Definition items.qc:17
#define extensions(x)
float gravity
Definition items.qh:17
float autocvar_cl_items_animate
Definition items.qh:5
float autocvar_cl_items_vehicle_alpha
Definition items.qh:7
float autocvar_cl_weapon_stay_alpha
Definition items.qh:12
float autocvar_cl_items_fadedist
Definition items.qh:6
vector autocvar_cl_items_vehicle_color
Definition items.qh:8
vector autocvar_cl_ghost_items_color
Definition items.qh:10
float autocvar_cl_ghost_items
Definition items.qh:9
float autocvar_cl_simple_items
Definition items.qh:13
vector autocvar_cl_weapon_stay_color
Definition items.qh:11
IntrusiveList g_drawables
Definition main.qh:91
int hud
Definition main.qh:173
float fade_start
Definition item.qh:86
float fade_end
Definition item.qh:87
const int ITS_STAYWEP
Definition item.qh:62
const int ISF_COLORMAP
Definition item.qh:53
const int ITS_ALLOWSI
Definition item.qh:67
const int ITS_EXPIRING
Definition item.qh:69
const int ITS_ALLOWFB
Definition item.qh:66
const int ITS_GLOW
Definition item.qh:68
string mdl
Definition item.qh:89
const int ITS_AVAILABLE
Definition item.qh:65
const int ISF_SIZE2
Definition item.qh:51
const vector ITEM_S_MAXS
Definition item.qh:81
const vector ITEM_D_MAXS
Definition item.qh:83
const vector ITEM_S_MINS
Definition item.qh:80
int ItemStatus
Definition item.qh:61
const vector ITEM_L_MAXS
Definition item.qh:84
const int ITS_ANIMATE1
Definition item.qh:63
const int ISF_STATUS
Definition item.qh:52
const vector ITEM_D_MINS
Definition item.qh:82
const int ISF_REMOVEFX
Definition item.qh:49
const int ISF_ANGLES
Definition item.qh:55
const float IT_DESPAWNFX_TIME
Definition item.qh:72
const int ITS_ANIMATE2
Definition item.qh:64
const int ISF_SIZE
Definition item.qh:56
const int ISF_LOCATION
Definition item.qh:50
const int ISF_DROP
Definition item.qh:54
const int FL_ITEM
Definition constants.qh:77
string classname
float flags
float drawmask
const float SOLID_TRIGGER
const float VF_ORIGIN
vector avelocity
float frametime
const float MASK_NORMAL
vector mins
vector velocity
const float EF_ADDITIVE
const float EF_FULLBRIGHT
const float SOLID_NOT
float effects
float skin
float time
vector maxs
float colormap
vector absmax
vector origin
vector oldorigin
vector absmin
vector glowmod
#define strlen
#define pointparticles(effect, org, vel, howmany)
Definition effect.qh:7
ent angles
Definition ent_cs.qc:121
model
Definition ent_cs.qc:139
solid
Definition ent_cs.qc:165
ERASEABLE void IL_REMOVE(IntrusiveList this, entity it)
Remove any element, anywhere in the list.
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
#define NET_HANDLE(id, param)
Definition net.qh:15
#define ReadVector()
Definition net.qh:367
#define ReadAngleVector()
Definition net.qh:369
int ReadByte()
float warpzone_warpzones_exist
Definition common.qh:9
#define LOG_WARNF(...)
Definition log.qh:62
#define LOG_TRACEF(...)
Definition log.qh:77
#define HM_NEW(this)
Definition map.qh:29
int HashMap
Definition map.qh:22
#define HM_DELETE(this)
Definition map.qh:88
float bound(float min, float value, float max)
string substring(string s, float start, float length)
float vlen(vector v)
float sin(float f)
float max(float f,...)
void set_movetype(entity this, int mt)
Definition movetypes.qc:4
void Movetype_Physics_NoMatchTicrate(entity this, float movedt, bool isclient)
Definition movetypes.qc:779
const int MOVETYPE_NONE
Definition movetypes.qh:129
#define SET_ONGROUND(s)
Definition movetypes.qh:17
float move_movetype
Definition movetypes.qh:76
const int MOVETYPE_TOSS
Definition movetypes.qh:135
#define IS_ONGROUND(s)
Definition movetypes.qh:16
vector
Definition self.qh:92
vector org
Definition self.qh:92
#define SHUTDOWN(func)
before shutdown
Definition static.qh:49
#define STATIC_INIT(func)
during worldspawn
Definition static.qh:32
#define strfree(this)
Definition string.qh:59
#define strcpy(this, s)
Definition string.qh:52
#define vdist(v, cmp, f)
Vector distance comparison, avoids sqrt()
Definition vector.qh:8