Xonotic
subs.qc
Go to the documentation of this file.
1 #include "subs.qh"
2 
3 void SUB_NullThink(entity this) { }
4 
5 void SUB_CalcMoveDone(entity this);
7 
8 #ifdef SVQC
9 spawnfunc(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 #endif
15 
16 /*
17 ==================
18 SUB_Friction
19 
20 Applies some friction to this
21 ==================
22 */
23 .float friction;
24 void SUB_Friction (entity this)
25 {
26  this.nextthink = time;
27  if(IS_ONGROUND(this))
28  this.velocity = this.velocity * (1 - frametime * this.friction);
29 }
30 
31 /*
32 ==================
33 SUB_VanishOrRemove
34 
35 Makes client invisible or removes non-client
36 ==================
37 */
39 {
40  if (IS_CLIENT(ent))
41  {
42  // vanish
43  ent.alpha = -1;
44  ent.effects = 0;
45 #ifdef SVQC
46  ent.glow_size = 0;
47  ent.pflags = 0;
48 #endif
49  }
50  else
51  {
52  // remove
53  delete(ent);
54  }
55 }
56 
58 {
59  if(this.alpha == 0)
60  this.alpha = 1;
62  this.nextthink = time;
63  this.alpha -= frametime * this.fade_rate;
64  if (this.alpha < 0.01)
65  SUB_VanishOrRemove(this);
66  else
67  this.nextthink = time;
68 }
69 
70 /*
71 ==================
72 SUB_SetFade
73 
74 Fade ent out when time >= vanish_time
75 ==================
76 */
77 void SUB_SetFade(entity ent, float vanish_time, float fading_time)
78 {
79  if (fading_time <= 0)
80  fading_time = 0.01;
81  ent.fade_rate = 1/fading_time;
83  ent.nextthink = vanish_time;
84 }
85 
86 /*
87 =============
88 SUB_CalcMove
89 
90 calculate this.velocity and this.nextthink to reach dest from
91 this.origin traveling at speed
92 ===============
93 */
95 {
96  // After moving, set origin to exact final destination
97 
98  setorigin (this, this.finaldest);
99  this.velocity = '0 0 0';
100  this.nextthink = -1;
101  if (this.think1 && this.think1 != SUB_CalcMoveDone)
102  this.think1 (this);
103 }
104 
107 {
108  float traveltime;
109  float phasepos;
110  float nexttick;
111  vector delta;
112  vector delta2;
113  vector veloc;
114  vector angloc;
115  vector nextpos;
116  delta = this.destvec;
117  delta2 = this.destvec2;
118  if(time < this.animstate_endtime)
119  {
120  nexttick = time + PHYS_INPUT_FRAMETIME;
121 
122  traveltime = this.animstate_endtime - this.animstate_starttime;
123  phasepos = (nexttick - this.animstate_starttime) / traveltime; // range: [0, 1]
124  phasepos = cubic_speedfunc(this.platmovetype_start, this.platmovetype_end, phasepos);
125  nextpos = this.origin + (delta * phasepos) + (delta2 * phasepos * phasepos);
126  // derivative: delta + 2 * delta2 * phasepos (e.g. for angle positioning)
127 
128  if(this.owner.platmovetype_turn)
129  {
130  vector destangle;
131  destangle = delta + 2 * delta2 * phasepos;
132  destangle = vectoangles(destangle);
133  destangle_x = -destangle_x; // flip up / down orientation
134 
135  // take the shortest distance for the angles
136  vector v = this.owner.angles;
137  v.x -= 360 * floor((v.x - destangle_x) / 360 + 0.5);
138  v.y -= 360 * floor((v.y - destangle_y) / 360 + 0.5);
139  v.z -= 360 * floor((v.z - destangle_z) / 360 + 0.5);
140  this.owner.angles = v;
141  angloc = destangle - this.owner.angles;
142  angloc = angloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame
143  this.owner.avelocity = angloc;
144  }
145  if(nexttick < this.animstate_endtime)
146  veloc = nextpos - this.owner.origin;
147  else
148  veloc = this.finaldest - this.owner.origin;
149  veloc = veloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame
150 
151  this.owner.velocity = veloc;
152  this.nextthink = nexttick;
153  }
154  else
155  {
156  // derivative: delta + 2 * delta2 (e.g. for angle positioning)
157  entity own = this.owner;
158  setthink(own, this.think1);
159  // set the owner's reference to this entity to NULL
160  own.move_controller = NULL;
161  delete(this);
162  getthink(own)(own);
163  }
164 }
165 
166 void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector control, vector destin)
167 {
168  // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + destin * t * t
169  // 2 * control * t - 2 * control * t * t + destin * t * t
170  // 2 * control * t + (destin - 2 * control) * t * t
171 
172  setorigin(controller, org);
173  control -= org;
174  destin -= org;
175 
176  controller.destvec = 2 * control; // control point
177  controller.destvec2 = destin - 2 * control; // quadratic part required to reach end point
178  // also: initial d/dphasepos origin = 2 * control, final speed = 2 * (destin - control)
179 }
180 
182 {
183  // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + destin * t * t
184  // 2 * control * t - 2 * control * t * t + destin * t * t
185  // 2 * control * t + (destin - 2 * control) * t * t
186 
187  setorigin(controller, org);
188  destin -= org;
189 
190  controller.destvec = destin; // end point
191  controller.destvec2 = '0 0 0';
192 }
193 
194 void SUB_CalcMove_Bezier (entity this, vector tcontrol, vector tdest, float tspeedtype, float tspeed, void(entity this) func)
195 {
196  float traveltime;
197  entity controller;
198 
199  if (!tspeed)
200  objerror (this, "No speed is defined!");
201 
202  this.think1 = func;
203  this.finaldest = tdest;
204  setthink(this, SUB_CalcMoveDone);
205 
206  switch(tspeedtype)
207  {
208  default:
209  case TSPEED_START:
210  traveltime = 2 * vlen(tcontrol - this.origin) / tspeed;
211  break;
212  case TSPEED_END:
213  traveltime = 2 * vlen(tcontrol - tdest) / tspeed;
214  break;
215  case TSPEED_LINEAR:
216  traveltime = vlen(tdest - this.origin) / tspeed;
217  break;
218  case TSPEED_TIME:
219  traveltime = tspeed;
220  break;
221  }
222 
223  if (traveltime < 0.1) // useless anim
224  {
225  this.velocity = '0 0 0';
226  this.nextthink = this.ltime + 0.1;
227  return;
228  }
229 
230  // delete the previous controller, otherwise changing movement midway is glitchy
231  if (this.move_controller != NULL)
232  {
233  delete(this.move_controller);
234  }
235  controller = new_pure(SUB_CalcMove_controller);
236  controller.owner = this;
237  this.move_controller = controller;
238  controller.platmovetype = this.platmovetype;
239  controller.platmovetype_start = this.platmovetype_start;
240  controller.platmovetype_end = this.platmovetype_end;
241  SUB_CalcMove_controller_setbezier(controller, this.origin, tcontrol, tdest);
242  controller.finaldest = (tdest + '0 0 0.125'); // where do we want to end? Offset to overshoot a bit.
243  controller.animstate_starttime = time;
244  controller.animstate_endtime = time + traveltime;
246  controller.think1 = getthink(this);
247 
248  // the thinking is now done by the controller
249  setthink(this, SUB_NullThink); // for PushMove
250  this.nextthink = this.ltime + traveltime;
251 
252  // invoke controller
253  getthink(controller)(controller);
254 }
255 
256 void SUB_CalcMove (entity this, vector tdest, float tspeedtype, float tspeed, void(entity this) func)
257 {
258  vector delta;
259  float traveltime;
260 
261  if (!tspeed)
262  objerror (this, "No speed is defined!");
263 
264  this.think1 = func;
265  this.finaldest = tdest;
266  setthink(this, SUB_CalcMoveDone);
267 
268  if (tdest == this.origin)
269  {
270  this.velocity = '0 0 0';
271  this.nextthink = this.ltime + 0.1;
272  return;
273  }
274 
275  delta = tdest - this.origin;
276 
277  switch(tspeedtype)
278  {
279  default:
280  case TSPEED_START:
281  case TSPEED_END:
282  case TSPEED_LINEAR:
283  traveltime = vlen (delta) / tspeed;
284  break;
285  case TSPEED_TIME:
286  traveltime = tspeed;
287  break;
288  }
289 
290  // Very short animations don't really show off the effect
291  // of controlled animation, so let's just use linear movement.
292  // Alternatively entities can choose to specify non-controlled movement.
293  // The only currently implemented alternative movement is linear (value 1)
294  if (traveltime < 0.15 || (this.platmovetype_start == 1 && this.platmovetype_end == 1)) // is this correct?
295  {
296  this.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
297  this.nextthink = this.ltime + traveltime;
298  return;
299  }
300 
301  // now just run like a bezier curve...
302  SUB_CalcMove_Bezier(this, (this.origin + tdest) * 0.5, tdest, tspeedtype, tspeed, func);
303 }
304 
305 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeedtype, float tspeed, void(entity this) func)
306 {
307  SUB_CalcMove(ent, tdest, tspeedtype, tspeed, func);
308 }
309 
310 /*
311 =============
312 SUB_CalcAngleMove
313 
314 calculate this.avelocity and this.nextthink to reach destangle from
315 this.angles rotating
316 
317 The calling function should make sure this.setthink is valid
318 ===============
319 */
321 {
322  // After rotating, set angle to exact final angle
323  this.angles = this.finalangle;
324  this.avelocity = '0 0 0';
325  this.nextthink = -1;
326  if (this.think1 && this.think1 != SUB_CalcAngleMoveDone) // avoid endless loops
327  this.think1 (this);
328 }
329 
330 // FIXME: I fixed this function only for rotation around the main axes
331 void SUB_CalcAngleMove (entity this, vector destangle, float tspeedtype, float tspeed, void(entity this) func)
332 {
333  if (!tspeed)
334  objerror (this, "No speed is defined!");
335 
336  // take the shortest distance for the angles
337  this.angles_x -= 360 * floor((this.angles_x - destangle_x) / 360 + 0.5);
338  this.angles_y -= 360 * floor((this.angles_y - destangle_y) / 360 + 0.5);
339  this.angles_z -= 360 * floor((this.angles_z - destangle_z) / 360 + 0.5);
340  vector delta = destangle - this.angles;
341  float traveltime;
342 
343  switch(tspeedtype)
344  {
345  default:
346  case TSPEED_START:
347  case TSPEED_END:
348  case TSPEED_LINEAR:
349  traveltime = vlen (delta) / tspeed;
350  break;
351  case TSPEED_TIME:
352  traveltime = tspeed;
353  break;
354  }
355 
356  this.think1 = func;
357  this.finalangle = destangle;
359 
360  if (traveltime < 0.1)
361  {
362  this.avelocity = '0 0 0';
363  this.nextthink = this.ltime + 0.1;
364  return;
365  }
366 
367  this.avelocity = delta * (1 / traveltime);
368  this.nextthink = this.ltime + traveltime;
369 }
370 
371 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeedtype, float tspeed, void(entity this) func)
372 {
373  SUB_CalcAngleMove (ent, destangle, tspeedtype, tspeed, func);
374 }
375 
376 #ifdef SVQC
377 void ApplyMinMaxScaleAngles(entity e)
378 {
379  if(e.angles.x != 0 || e.angles.z != 0 || e.avelocity.x != 0 || e.avelocity.z != 0) // "weird" rotation
380  {
381  e.maxs = '1 1 1' * vlen(
382  '1 0 0' * max(-e.mins.x, e.maxs.x) +
383  '0 1 0' * max(-e.mins.y, e.maxs.y) +
384  '0 0 1' * max(-e.mins.z, e.maxs.z)
385  );
386  e.mins = -e.maxs;
387  }
388  else if(e.angles.y != 0 || e.avelocity.y != 0) // yaw only is a bit better
389  {
390  e.maxs_x = vlen(
391  '1 0 0' * max(-e.mins.x, e.maxs.x) +
392  '0 1 0' * max(-e.mins.y, e.maxs.y)
393  );
394  e.maxs_y = e.maxs.x;
395  e.mins_x = -e.maxs.x;
396  e.mins_y = -e.maxs.x;
397  }
398  if(e.scale)
399  setsize(e, e.mins * e.scale, e.maxs * e.scale);
400  else
401  setsize(e, e.mins, e.maxs);
402 }
403 
404 void SetBrushEntityModel(entity this, bool with_lod)
405 {
406  if(this.model != "")
407  {
408  precache_model(this.model);
409  if(this.mins != '0 0 0' || this.maxs != '0 0 0')
410  {
411  vector mi = this.mins;
412  vector ma = this.maxs;
413  _setmodel(this, this.model); // no precision needed
414  setsize(this, mi, ma);
415  }
416  else
417  _setmodel(this, this.model); // no precision needed
418  if(with_lod)
419  InitializeEntity(this, LODmodel_attach, INITPRIO_FINDTARGET);
420 
421  if(endsWith(this.model, ".obj")) // WORKAROUND: darkplaces currently rotates .obj models on entities incorrectly, we need to add 180 degrees to the Y axis
422  this.angles_y = anglemods(this.angles_y - 180);
423  }
424  setorigin(this, this.origin);
425  ApplyMinMaxScaleAngles(this);
426 }
427 
428 bool LOD_customize(entity this, entity client)
429 {
430  if(autocvar_loddebug)
431  {
432  int d = autocvar_loddebug;
433  if(d == 1)
434  this.modelindex = this.lodmodelindex0;
435  else if(d == 2 || !this.lodmodelindex2)
436  this.modelindex = this.lodmodelindex1;
437  else // if(d == 3)
438  this.modelindex = this.lodmodelindex2;
439  return true;
440  }
441 
442  // TODO csqc network this so it only gets sent once
443  vector near_point = NearestPointOnBox(this, client.origin);
444  if(vdist(near_point - client.origin, <, this.loddistance1))
445  this.modelindex = this.lodmodelindex0;
446  else if(!this.lodmodelindex2 || vdist(near_point - client.origin, <, this.loddistance2))
447  this.modelindex = this.lodmodelindex1;
448  else
449  this.modelindex = this.lodmodelindex2;
450 
451  return true;
452 }
453 
454 void LOD_uncustomize(entity this)
455 {
456  this.modelindex = this.lodmodelindex0;
457 }
458 
459 void LODmodel_attach(entity this)
460 {
461  entity e;
462 
463  if(!this.loddistance1)
464  this.loddistance1 = 1000;
465  if(!this.loddistance2)
466  this.loddistance2 = 2000;
467  this.lodmodelindex0 = this.modelindex;
468 
469  if(this.lodtarget1 != "")
470  {
471  e = find(NULL, targetname, this.lodtarget1);
472  if(e)
473  {
474  this.lodmodel1 = e.model;
475  delete(e);
476  }
477  }
478  if(this.lodtarget2 != "")
479  {
480  e = find(NULL, targetname, this.lodtarget2);
481  if(e)
482  {
483  this.lodmodel2 = e.model;
484  delete(e);
485  }
486  }
487 
488  if(autocvar_loddebug < 0)
489  {
490  this.lodmodel1 = this.lodmodel2 = ""; // don't even initialize
491  }
492 
493  if(this.lodmodel1 != "")
494  {
495  vector mi, ma;
496  mi = this.mins;
497  ma = this.maxs;
498 
499  precache_model(this.lodmodel1);
500  _setmodel(this, this.lodmodel1);
501  this.lodmodelindex1 = this.modelindex;
502 
503  if(this.lodmodel2 != "")
504  {
505  precache_model(this.lodmodel2);
506  _setmodel(this, this.lodmodel2);
507  this.lodmodelindex2 = this.modelindex;
508  }
509 
510  this.modelindex = this.lodmodelindex0;
511  setsize(this, mi, ma);
512  }
513 
514  if(this.lodmodelindex1)
515  if (!getSendEntity(this))
516  SetCustomizer(this, LOD_customize, LOD_uncustomize);
517 }
518 
519 /*
520 ================
521 InitTrigger
522 ================
523 */
524 
525 void SetMovedir(entity this)
526 {
527  if(this.movedir != '0 0 0')
528  this.movedir = normalize(this.movedir);
529  else
530  {
531  makevectors(this.angles);
532  this.movedir = v_forward;
533  }
534 
535  this.angles = '0 0 0';
536 }
537 
538 void InitTrigger(entity this)
539 {
540 // trigger angles are used for one-way touches. An angle of 0 is assumed
541 // to mean no restrictions, so use a yaw of 360 instead.
542  SetMovedir(this);
543  this.solid = SOLID_TRIGGER;
544  SetBrushEntityModel(this, false);
546  this.modelindex = 0;
547  this.model = "";
548 }
549 
550 void InitSolidBSPTrigger(entity this)
551 {
552 // trigger angles are used for one-way touches. An angle of 0 is assumed
553 // to mean no restrictions, so use a yaw of 360 instead.
554  SetMovedir(this);
555  this.solid = SOLID_BSP;
556  SetBrushEntityModel(this, false);
557  set_movetype(this, MOVETYPE_NONE); // why was this PUSH? -div0
558 // this.modelindex = 0;
559  this.model = "";
560 }
561 
562 bool InitMovingBrushTrigger(entity this)
563 {
564 // trigger angles are used for one-way touches. An angle of 0 is assumed
565 // to mean no restrictions, so use a yaw of 360 instead.
566  this.solid = SOLID_BSP;
567  SetBrushEntityModel(this, true);
569  if(this.modelindex == 0)
570  {
571  objerror(this, "InitMovingBrushTrigger: no brushes found!");
572  return false;
573  }
574  return true;
575 }
576 #endif
void SUB_CalcAngleMoveEnt(entity ent, vector destangle, float tspeedtype, float tspeed, void(entity this) func)
Definition: subs.qc:371
float MOVETYPE_NONE
Definition: progsdefs.qc:246
vector destvec2
Definition: subs.qh:36
float alpha
Definition: items.qc:14
void SUB_CalcMove_controller_setlinear(entity controller, vector org, vector destin)
Definition: subs.qc:181
float animstate_endtime
Definition: anim.qh:38
ERASEABLE float anglemods(float v)
Definition: angle.qc:13
#define getthink(e)
vector destvec
Definition: subs.qh:35
#define IS_CLIENT(v)
Definition: utils.qh:13
float modelindex
Definition: csprogsdefs.qc:91
float platmovetype_end
Definition: subs.qh:44
void SUB_VanishOrRemove(entity ent)
Definition: subs.qc:38
fade_rate
Definition: projectile.qh:14
entity() spawn
void SUB_Friction(entity this)
Definition: subs.qc:24
entity move_controller
Definition: subs.qh:68
const int TSPEED_LINEAR
Definition: subs.qh:71
#define IS_ONGROUND(s)
Definition: movetypes.qh:16
vector maxs
Definition: csprogsdefs.qc:113
spawnfunc(info_player_attacker)
Definition: sv_assault.qc:283
origin
Definition: ent_cs.qc:114
string platmovetype
Definition: subs.qh:43
vector avelocity
Definition: csprogsdefs.qc:105
void SUB_CalcAngleMoveDone(entity this)
Definition: subs.qc:320
float ltime
Definition: progsdefs.qc:107
void SUB_CalcMove(entity this, vector tdest, float tspeedtype, float tspeed, void(entity this) func)
Definition: subs.qc:256
entity owner
Definition: main.qh:73
float platmovetype_turn
Definition: subs.qc:105
string model
Definition: csprogsdefs.qc:108
int lodmodelindex0
const int TSPEED_START
Definition: subs.qh:72
vector movedir
Definition: progsdefs.qc:203
vector mins
Definition: csprogsdefs.qc:113
void SUB_SetFade_Think(entity this)
Definition: subs.qc:57
float MOVETYPE_PUSH
Definition: progsdefs.qc:253
const int TSPEED_TIME
Definition: subs.qh:70
void SUB_CalcMoveDone(entity this)
Definition: subs.qc:94
#define NULL
Definition: post.qh:17
float frametime
Definition: csprogsdefs.qc:17
float platmovetype_start
Definition: subs.qh:44
int lodmodelindex2
ERASEABLE float cubic_speedfunc(float startspeedfactor, float endspeedfactor, float spd)
Definition: math.qh:129
float nextthink
Definition: csprogsdefs.qc:121
void SUB_CalcMove_controller_think(entity this)
Definition: subs.qc:106
vector(float skel, float bonenum) _skel_get_boneabs_hidden
void SUB_NullThink(entity this)
Definition: subs.qc:3
vector finalangle
Definition: subs.qh:30
const int TSPEED_END
Definition: subs.qh:73
vector v
Definition: ent_cs.qc:116
const float SOLID_BSP
Definition: csprogsdefs.qc:248
void InitializeEntity(entity e, void(entity this) func, int order)
Definition: world.qc:2146
#define vdist(v, cmp, f)
Vector distance comparison, avoids sqrt()
Definition: vector.qh:8
#define getSendEntity(e)
Definition: self.qh:98
float animstate_starttime
Definition: anim.qh:37
float friction
Definition: subs.qc:23
const float SOLID_TRIGGER
Definition: csprogsdefs.qc:245
int lodmodelindex1
string targetname
Definition: progsdefs.qc:194
#define new_pure(class)
purely logical entities (.origin doesn&#39;t work)
Definition: oo.qh:62
setorigin(ent, v)
#define setthink(e, f)
void SUB_CalcAngleMove(entity this, vector destangle, float tspeedtype, float tspeed, void(entity this) func)
Definition: subs.qc:331
void SUB_SetFade(entity ent, float vanish_time, float fading_time)
Definition: subs.qc:77
vector angles
Definition: csprogsdefs.qc:104
vector finaldest
Definition: subs.qh:30
float time
Definition: csprogsdefs.qc:16
void SUB_CalcMove_controller_setbezier(entity controller, vector org, vector control, vector destin)
Definition: subs.qc:166
vector velocity
Definition: csprogsdefs.qc:103
void SUB_CalcMove_Bezier(entity this, vector tcontrol, vector tdest, float tspeedtype, float tspeed, void(entity this) func)
Definition: subs.qc:194
#define makevectors
Definition: post.qh:21
void set_movetype(entity this, int mt)
void SUB_CalcMoveEnt(entity ent, vector tdest, float tspeedtype, float tspeed, void(entity this) func)
Definition: subs.qc:305
vector v_forward
Definition: csprogsdefs.qc:31
float solid
Definition: csprogsdefs.qc:99
#define endsWith(this, suffix)
Definition: string.qh:226