1#include "g_subs.qh"
2
3#include "antilag.qh"
4#include "command/common.qh"
5#include "../common/state.qh"
6#include "../lib/warpzone/common.qh"
7#include "../common/triggers/subs.qh"
8
9spawnfunc(info_null)
10{
11	delete(this);
12	// if anything breaks, tell the mapper to fix his map! info_null is meant to remove itself immediately.
13}
14
15/*
16==================
17main
18
19unused but required by the engine
20==================
21*/
22void main ()
23{
24
25}
26
27// Misc
28
29/*
30==================
31traceline_antilag
32
33A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack
34Additionally it moves players back into the past before the trace and restores them afterward.
35==================
36*/
37void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag, float wz)
38{
39	// check whether antilagged traces are enabled
40	if (lag < 0.001)
41		lag = 0;
42	if (!IS_REAL_CLIENT(forent))
43		lag = 0; // only antilag for clients
44
45	// change shooter to SOLID_BBOX so the shot can hit corpses
46	int oldsolid = source.dphitcontentsmask;
47	if(source)
48		source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
49
50	if (lag)
51	{
52		// take players back into the past
53		FOREACH_CLIENT(IS_PLAYER(it) && it != forent, antilag_takeback(it, CS(it), time - lag));
54		IL_EACH(g_monsters, it != forent,
55		{
56			antilag_takeback(it, it, time - lag);
57		});
58	}
59
60	// do the trace
61	if(wz)
62		WarpZone_TraceBox (v1, mi, ma, v2, nomonst, forent);
63	else
64		tracebox (v1, mi, ma, v2, nomonst, forent);
65
66	// restore players to current positions
67	if (lag)
68	{
69		FOREACH_CLIENT(IS_PLAYER(it) && it != forent, antilag_restore(it, CS(it)));
70		IL_EACH(g_monsters, it != forent,
71		{
72			antilag_restore(it, it);
73		});
74	}
75
76	// restore shooter solid type
77	if(source)
78		source.dphitcontentsmask = oldsolid;
79}
80void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
81{
82	tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, false);
83}
84void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
85{
86	if (autocvar_g_antilag != 2 || source.cvar_cl_noantilag)
87		lag = 0;
88	traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
89}
90void tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
91{
92	if (autocvar_g_antilag != 2 || source.cvar_cl_noantilag)
93		lag = 0;
94	tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, false);
95}
96void WarpZone_traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
97{
98	tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, true);
99}
100void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
101{
102	if (autocvar_g_antilag != 2 || source.cvar_cl_noantilag)
103		lag = 0;
104	WarpZone_traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
105}
106void WarpZone_tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
107{
108	if (autocvar_g_antilag != 2 || source.cvar_cl_noantilag)
109		lag = 0;
110	tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, true);
111}
112
113float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent, float stopatentity, entity ignorestopatentity) // returns the number of traces done, for benchmarking
114{
115	vector pos, dir, t;
116	float nudge;
117	entity stopentity;
118
119	//nudge = 2 * cvar("collision_impactnudge"); // why not?
120	nudge = 0.5;
121
122	dir = normalize(v2 - v1);
123
124	pos = v1 + dir * nudge;
125
126	float c;
127	c = 0;
128
129	for (;;)
130	{
131		if(pos * dir >= v2 * dir)
132		{
133			// went too far
134			trace_fraction = 1;
135			trace_endpos = v2;
136			return c;
137		}
138
139		tracebox(pos, mi, ma, v2, nomonsters, forent);
140		++c;
141
142		if(c == 50)
143		{
144			LOG_TRACE("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2));
145			LOG_TRACE("  Nudging gets us nowhere at ", vtos(pos));
146			LOG_TRACE("  trace_endpos is ", vtos(trace_endpos));
147			LOG_TRACE("  trace distance is ", ftos(vlen(pos - trace_endpos)));
148		}
149
150		stopentity = trace_ent;
151
152		if(trace_startsolid)
153		{
154			// we started inside solid.
155			// then trace from endpos to pos
156			t = trace_endpos;
157			tracebox(t, mi, ma, pos, nomonsters, forent);
158			++c;
159			if(trace_startsolid)
160			{
161				// t is still inside solid? bad
162				// force advance, then, and retry
163				pos = t + dir * nudge;
164
165				// but if we hit an entity, stop RIGHT before it
166				if(stopatentity && stopentity && stopentity != ignorestopatentity)
167				{
168					trace_ent = stopentity;
169					trace_endpos = t;
170					trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
171					return c;
172				}
173			}
174			else
175			{
176				// we actually LEFT solid!
177				trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
178				return c;
179			}
180		}
181		else
182		{
183			// pos is outside solid?!? but why?!? never mind, just return it.
184			trace_endpos = pos;
185			trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
186			return c;
187		}
188	}
189}
190
191void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent, float stopatentity, entity ignorestopatentity)
192{
193	tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent, stopatentity, ignorestopatentity);
194}
195
196/*
197==================
198findbetterlocation
199
200Returns a point at least 12 units away from walls
201(useful for explosion animations, although the blast is performed where it really happened)
202Ripped from DPMod
203==================
204*/
205vector findbetterlocation (vector org, float mindist)
206{
207	vector	loc;
208	vector vec;
209	float c, h;
210
211	vec = mindist * '1 0 0';
212	c = 0;
213	while (c < 6)
214	{
215		traceline (org, org + vec, true, NULL);
216		vec = vec * -1;
217		if (trace_fraction < 1)
218		{
219			loc = trace_endpos;
220			traceline (loc, loc + vec, true, NULL);
221			if (trace_fraction >= 1)
222				org = loc + vec;
223		}
224		if (c & 1)
225		{
226			h = vec.y;
227			vec.y = vec.x;
228			vec.x = vec.z;
229			vec.z = h;
230		}
231		c = c + 1;
232	}
233
234	return org;
235}
236
237bool LOD_customize(entity this, entity client)
238{
239	if(autocvar_loddebug)
240	{
241		int d = autocvar_loddebug;
242		if(d == 1)
243			this.modelindex = this.lodmodelindex0;
244		else if(d == 2 || !this.lodmodelindex2)
245			this.modelindex = this.lodmodelindex1;
246		else // if(d == 3)
247			this.modelindex = this.lodmodelindex2;
248		return true;
249	}
250
251	// TODO csqc network this so it only gets sent once
252	vector near_point = NearestPointOnBox(this, client.origin);
253	if(vdist(near_point - client.origin, <, this.loddistance1))
254		this.modelindex = this.lodmodelindex0;
255	else if(!this.lodmodelindex2 || vdist(near_point - client.origin, <, this.loddistance2))
256		this.modelindex = this.lodmodelindex1;
257	else
258		this.modelindex = this.lodmodelindex2;
259
260	return true;
261}
262
263void LOD_uncustomize(entity this)
264{
265	this.modelindex = this.lodmodelindex0;
266}
267
268void LODmodel_attach(entity this)
269{
270	entity e;
271
272	if(!this.loddistance1)
273		this.loddistance1 = 1000;
274	if(!this.loddistance2)
275		this.loddistance2 = 2000;
276	this.lodmodelindex0 = this.modelindex;
277
278	if(this.lodtarget1 != "")
279	{
280		e = find(NULL, targetname, this.lodtarget1);
281		if(e)
282		{
283			this.lodmodel1 = e.model;
284			delete(e);
285		}
286	}
287	if(this.lodtarget2 != "")
288	{
289		e = find(NULL, targetname, this.lodtarget2);
290		if(e)
291		{
292			this.lodmodel2 = e.model;
293			delete(e);
294		}
295	}
296
297	if(autocvar_loddebug < 0)
298	{
299		this.lodmodel1 = this.lodmodel2 = ""; // don't even initialize
300	}
301
302	if(this.lodmodel1 != "")
303	{
304		vector mi, ma;
305		mi = this.mins;
306		ma = this.maxs;
307
308		precache_model(this.lodmodel1);
309		_setmodel(this, this.lodmodel1);
310		this.lodmodelindex1 = this.modelindex;
311
312		if(this.lodmodel2 != "")
313		{
314			precache_model(this.lodmodel2);
315			_setmodel(this, this.lodmodel2);
316			this.lodmodelindex2 = this.modelindex;
317		}
318
319		this.modelindex = this.lodmodelindex0;
320		setsize(this, mi, ma);
321	}
322
323	if(this.lodmodelindex1)
324		if (!getSendEntity(this))
325			SetCustomizer(this, LOD_customize, LOD_uncustomize);
326}
327
328void ApplyMinMaxScaleAngles(entity e)
329{
330	if(e.angles.x != 0 || e.angles.z != 0 || e.avelocity.x != 0 || e.avelocity.z != 0) // "weird" rotation
331	{
332		e.maxs = '1 1 1' * vlen(
333			'1 0 0' * max(-e.mins.x, e.maxs.x) +
334			'0 1 0' * max(-e.mins.y, e.maxs.y) +
335			'0 0 1' * max(-e.mins.z, e.maxs.z)
336		);
337		e.mins = -e.maxs;
338	}
339	else if(e.angles.y != 0 || e.avelocity.y != 0) // yaw only is a bit better
340	{
341		e.maxs_x = vlen(
342			'1 0 0' * max(-e.mins.x, e.maxs.x) +
343			'0 1 0' * max(-e.mins.y, e.maxs.y)
344		);
345		e.maxs_y = e.maxs.x;
346		e.mins_x = -e.maxs.x;
347		e.mins_y = -e.maxs.x;
348	}
349	if(e.scale)
350		setsize(e, e.mins * e.scale, e.maxs * e.scale);
351	else
352		setsize(e, e.mins, e.maxs);
353}
354
355void SetBrushEntityModel(entity this)
356{
357 	if(this.model != "")
358 	{
359 		precache_model(this.model);
360		if(this.mins != '0 0 0' || this.maxs != '0 0 0')
361		{
362			vector mi = this.mins;
363			vector ma = this.maxs;
364			_setmodel(this, this.model); // no precision needed
365			setsize(this, mi, ma);
366		}
367		else
368			_setmodel(this, this.model); // no precision needed
369		InitializeEntity(this, LODmodel_attach, INITPRIO_FINDTARGET);
370 	}
371	setorigin(this, this.origin);
372	ApplyMinMaxScaleAngles(this);
373}
374
375void SetBrushEntityModelNoLOD(entity this)
376{
377 	if(this.model != "")
378 	{
379 		precache_model(this.model);
380		if(this.mins != '0 0 0' || this.maxs != '0 0 0')
381		{
382			vector mi = this.mins;
383			vector ma = this.maxs;
384			_setmodel(this, this.model); // no precision needed
385			setsize(this, mi, ma);
386		}
387		else
388			_setmodel(this, this.model); // no precision needed
389 	}
390	setorigin(this, this.origin);
391	ApplyMinMaxScaleAngles(this);
392}
393
394/*
395================
396InitTrigger
397================
398*/
399
400void SetMovedir(entity this)
401{
402	if(this.movedir != '0 0 0')
403		this.movedir = normalize(this.movedir);
404	else
405	{
406		makevectors(this.angles);
407		this.movedir = v_forward;
408	}
409
410	this.angles = '0 0 0';
411}
412
413void InitTrigger(entity this)
414{
415// trigger angles are used for one-way touches.  An angle of 0 is assumed
416// to mean no restrictions, so use a yaw of 360 instead.
417	SetMovedir(this);
418	this.solid = SOLID_TRIGGER;
419	SetBrushEntityModel(this);
420	set_movetype(this, MOVETYPE_NONE);
421	this.modelindex = 0;
422	this.model = "";
423}
424
425void InitSolidBSPTrigger(entity this)
426{
427// trigger angles are used for one-way touches.  An angle of 0 is assumed
428// to mean no restrictions, so use a yaw of 360 instead.
429	SetMovedir(this);
430	this.solid = SOLID_BSP;
431	SetBrushEntityModel(this);
432	set_movetype(this, MOVETYPE_NONE); // why was this PUSH? -div0
433//	this.modelindex = 0;
434	this.model = "";
435}
436
437bool InitMovingBrushTrigger(entity this)
438{
439// trigger angles are used for one-way touches.  An angle of 0 is assumed
440// to mean no restrictions, so use a yaw of 360 instead.
441	this.solid = SOLID_BSP;
442	SetBrushEntityModel(this);
443	set_movetype(this, MOVETYPE_PUSH);
444	if(this.modelindex == 0)
445	{
446		objerror(this, "InitMovingBrushTrigger: no brushes found!");
447		return false;
448	}
449	return true;
450}
451