Quake II RTX doxygen  1.0 dev
g_func.c
Go to the documentation of this file.
1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18 #include "g_local.h"
19 
20 /*
21 =========================================================
22 
23  PLATS
24 
25  movement options:
26 
27  linear
28  smooth start, hard stop
29  smooth start, smooth stop
30 
31  start
32  end
33  acceleration
34  speed
35  deceleration
36  begin sound
37  end sound
38  target fired when reaching end
39  wait at end
40 
41  object characteristics that use move segments
42  ---------------------------------------------
43  movetype_push, or movetype_stop
44  action when touched
45  action when blocked
46  action when used
47  disabled?
48  auto trigger spawning
49 
50 
51 =========================================================
52 */
53 
54 #define PLAT_LOW_TRIGGER 1
55 
56 #define STATE_TOP 0
57 #define STATE_BOTTOM 1
58 #define STATE_UP 2
59 #define STATE_DOWN 3
60 
61 #define DOOR_START_OPEN 1
62 #define DOOR_REVERSE 2
63 #define DOOR_CRUSHER 4
64 #define DOOR_NOMONSTER 8
65 #define DOOR_TOGGLE 32
66 #define DOOR_X_AXIS 64
67 #define DOOR_Y_AXIS 128
68 
69 
70 //
71 // Support routines for movement (changes in origin using velocity)
72 //
73 
74 void Move_Done(edict_t *ent)
75 {
76  VectorClear(ent->velocity);
77  ent->moveinfo.endfunc(ent);
78 }
79 
80 void Move_Final(edict_t *ent)
81 {
82  if (ent->moveinfo.remaining_distance == 0) {
83  Move_Done(ent);
84  return;
85  }
86 
87  VectorScale(ent->moveinfo.dir, ent->moveinfo.remaining_distance / FRAMETIME, ent->velocity);
88 
89  ent->think = Move_Done;
90  ent->nextthink = level.time + FRAMETIME;
91 }
92 
93 void Move_Begin(edict_t *ent)
94 {
95  float frames;
96 
97  if ((ent->moveinfo.speed * FRAMETIME) >= ent->moveinfo.remaining_distance) {
98  Move_Final(ent);
99  return;
100  }
101  VectorScale(ent->moveinfo.dir, ent->moveinfo.speed, ent->velocity);
102  frames = floor((ent->moveinfo.remaining_distance / ent->moveinfo.speed) / FRAMETIME);
103  ent->moveinfo.remaining_distance -= frames * ent->moveinfo.speed * FRAMETIME;
104  ent->nextthink = level.time + (frames * FRAMETIME);
105  ent->think = Move_Final;
106 }
107 
108 void Think_AccelMove(edict_t *ent);
109 
110 void Move_Calc(edict_t *ent, vec3_t dest, void(*func)(edict_t*))
111 {
112  VectorClear(ent->velocity);
113  VectorSubtract(dest, ent->s.origin, ent->moveinfo.dir);
114  ent->moveinfo.remaining_distance = VectorNormalize(ent->moveinfo.dir);
115  ent->moveinfo.endfunc = func;
116 
117  if (ent->moveinfo.speed == ent->moveinfo.accel && ent->moveinfo.speed == ent->moveinfo.decel) {
118  if (level.current_entity == ((ent->flags & FL_TEAMSLAVE) ? ent->teammaster : ent)) {
119  Move_Begin(ent);
120  } else {
121  ent->nextthink = level.time + FRAMETIME;
122  ent->think = Move_Begin;
123  }
124  } else {
125  // accelerative
126  ent->moveinfo.current_speed = 0;
127  ent->think = Think_AccelMove;
128  ent->nextthink = level.time + FRAMETIME;
129  }
130 }
131 
132 
133 //
134 // Support routines for angular movement (changes in angle using avelocity)
135 //
136 
137 void AngleMove_Done(edict_t *ent)
138 {
139  VectorClear(ent->avelocity);
140  ent->moveinfo.endfunc(ent);
141 }
142 
143 void AngleMove_Final(edict_t *ent)
144 {
145  vec3_t move;
146 
147  if (ent->moveinfo.state == STATE_UP)
148  VectorSubtract(ent->moveinfo.end_angles, ent->s.angles, move);
149  else
150  VectorSubtract(ent->moveinfo.start_angles, ent->s.angles, move);
151 
152  if (VectorCompare(move, vec3_origin)) {
153  AngleMove_Done(ent);
154  return;
155  }
156 
157  VectorScale(move, 1.0 / FRAMETIME, ent->avelocity);
158 
159  ent->think = AngleMove_Done;
160  ent->nextthink = level.time + FRAMETIME;
161 }
162 
163 void AngleMove_Begin(edict_t *ent)
164 {
165  vec3_t destdelta;
166  float len;
167  float traveltime;
168  float frames;
169 
170  // set destdelta to the vector needed to move
171  if (ent->moveinfo.state == STATE_UP)
172  VectorSubtract(ent->moveinfo.end_angles, ent->s.angles, destdelta);
173  else
174  VectorSubtract(ent->moveinfo.start_angles, ent->s.angles, destdelta);
175 
176  // calculate length of vector
177  len = VectorLength(destdelta);
178 
179  // divide by speed to get time to reach dest
180  traveltime = len / ent->moveinfo.speed;
181 
182  if (traveltime < FRAMETIME) {
183  AngleMove_Final(ent);
184  return;
185  }
186 
187  frames = floor(traveltime / FRAMETIME);
188 
189  // scale the destdelta vector by the time spent traveling to get velocity
190  VectorScale(destdelta, 1.0 / traveltime, ent->avelocity);
191 
192  // set nextthink to trigger a think when dest is reached
193  ent->nextthink = level.time + frames * FRAMETIME;
194  ent->think = AngleMove_Final;
195 }
196 
197 void AngleMove_Calc(edict_t *ent, void(*func)(edict_t*))
198 {
199  VectorClear(ent->avelocity);
200  ent->moveinfo.endfunc = func;
201  if (level.current_entity == ((ent->flags & FL_TEAMSLAVE) ? ent->teammaster : ent)) {
202  AngleMove_Begin(ent);
203  } else {
204  ent->nextthink = level.time + FRAMETIME;
205  ent->think = AngleMove_Begin;
206  }
207 }
208 
209 
210 /*
211 ==============
212 Think_AccelMove
213 
214 The team has completed a frame of movement, so
215 change the speed for the next frame
216 ==============
217 */
218 #define AccelerationDistance(target, rate) (target * ((target / rate) + 1) / 2)
219 
221 {
222  float accel_dist;
223  float decel_dist;
224 
225  moveinfo->move_speed = moveinfo->speed;
226 
227  if (moveinfo->remaining_distance < moveinfo->accel) {
228  moveinfo->current_speed = moveinfo->remaining_distance;
229  return;
230  }
231 
232  accel_dist = AccelerationDistance(moveinfo->speed, moveinfo->accel);
233  decel_dist = AccelerationDistance(moveinfo->speed, moveinfo->decel);
234 
235  if ((moveinfo->remaining_distance - accel_dist - decel_dist) < 0) {
236  float f;
237 
238  f = (moveinfo->accel + moveinfo->decel) / (moveinfo->accel * moveinfo->decel);
239  moveinfo->move_speed = (-2 + sqrt(4 - 4 * f * (-2 * moveinfo->remaining_distance))) / (2 * f);
240  decel_dist = AccelerationDistance(moveinfo->move_speed, moveinfo->decel);
241  }
242 
243  moveinfo->decel_distance = decel_dist;
244 }
245 
247 {
248  // are we decelerating?
249  if (moveinfo->remaining_distance <= moveinfo->decel_distance) {
250  if (moveinfo->remaining_distance < moveinfo->decel_distance) {
251  if (moveinfo->next_speed) {
252  moveinfo->current_speed = moveinfo->next_speed;
253  moveinfo->next_speed = 0;
254  return;
255  }
256  if (moveinfo->current_speed > moveinfo->decel)
257  moveinfo->current_speed -= moveinfo->decel;
258  }
259  return;
260  }
261 
262  // are we at full speed and need to start decelerating during this move?
263  if (moveinfo->current_speed == moveinfo->move_speed)
264  if ((moveinfo->remaining_distance - moveinfo->current_speed) < moveinfo->decel_distance) {
265  float p1_distance;
266  float p2_distance;
267  float distance;
268 
269  p1_distance = moveinfo->remaining_distance - moveinfo->decel_distance;
270  p2_distance = moveinfo->move_speed * (1.0 - (p1_distance / moveinfo->move_speed));
271  distance = p1_distance + p2_distance;
272  moveinfo->current_speed = moveinfo->move_speed;
273  moveinfo->next_speed = moveinfo->move_speed - moveinfo->decel * (p2_distance / distance);
274  return;
275  }
276 
277  // are we accelerating?
278  if (moveinfo->current_speed < moveinfo->speed) {
279  float old_speed;
280  float p1_distance;
281  float p1_speed;
282  float p2_distance;
283  float distance;
284 
285  old_speed = moveinfo->current_speed;
286 
287  // figure simple acceleration up to move_speed
288  moveinfo->current_speed += moveinfo->accel;
289  if (moveinfo->current_speed > moveinfo->speed)
290  moveinfo->current_speed = moveinfo->speed;
291 
292  // are we accelerating throughout this entire move?
293  if ((moveinfo->remaining_distance - moveinfo->current_speed) >= moveinfo->decel_distance)
294  return;
295 
296  // during this move we will accelrate from current_speed to move_speed
297  // and cross over the decel_distance; figure the average speed for the
298  // entire move
299  p1_distance = moveinfo->remaining_distance - moveinfo->decel_distance;
300  p1_speed = (old_speed + moveinfo->move_speed) / 2.0;
301  p2_distance = moveinfo->move_speed * (1.0 - (p1_distance / p1_speed));
302  distance = p1_distance + p2_distance;
303  moveinfo->current_speed = (p1_speed * (p1_distance / distance)) + (moveinfo->move_speed * (p2_distance / distance));
304  moveinfo->next_speed = moveinfo->move_speed - moveinfo->decel * (p2_distance / distance);
305  return;
306  }
307 
308  // we are at constant velocity (move_speed)
309  return;
310 }
311 
312 void Think_AccelMove(edict_t *ent)
313 {
314  ent->moveinfo.remaining_distance -= ent->moveinfo.current_speed;
315 
316  if (ent->moveinfo.current_speed == 0) // starting or blocked
317  plat_CalcAcceleratedMove(&ent->moveinfo);
318 
319  plat_Accelerate(&ent->moveinfo);
320 
321  // will the entire move complete on next frame?
322  if (ent->moveinfo.remaining_distance <= ent->moveinfo.current_speed) {
323  Move_Final(ent);
324  return;
325  }
326 
327  VectorScale(ent->moveinfo.dir, ent->moveinfo.current_speed * 10, ent->velocity);
328  ent->nextthink = level.time + FRAMETIME;
329  ent->think = Think_AccelMove;
330 }
331 
332 
333 void plat_go_down(edict_t *ent);
334 
335 void plat_hit_top(edict_t *ent)
336 {
337  if (!(ent->flags & FL_TEAMSLAVE)) {
338  if (ent->moveinfo.sound_end)
339  gi.sound(ent, CHAN_NO_PHS_ADD + CHAN_VOICE, ent->moveinfo.sound_end, 1, ATTN_STATIC, 0);
340  ent->s.sound = 0;
341  }
342  ent->moveinfo.state = STATE_TOP;
343 
344  ent->think = plat_go_down;
345  ent->nextthink = level.time + 3;
346 }
347 
348 void plat_hit_bottom(edict_t *ent)
349 {
350  if (!(ent->flags & FL_TEAMSLAVE)) {
351  if (ent->moveinfo.sound_end)
352  gi.sound(ent, CHAN_NO_PHS_ADD + CHAN_VOICE, ent->moveinfo.sound_end, 1, ATTN_STATIC, 0);
353  ent->s.sound = 0;
354  }
355  ent->moveinfo.state = STATE_BOTTOM;
356 }
357 
358 void plat_go_down(edict_t *ent)
359 {
360  if (!(ent->flags & FL_TEAMSLAVE)) {
361  if (ent->moveinfo.sound_start)
362  gi.sound(ent, CHAN_NO_PHS_ADD + CHAN_VOICE, ent->moveinfo.sound_start, 1, ATTN_STATIC, 0);
363  ent->s.sound = ent->moveinfo.sound_middle;
364  }
365  ent->moveinfo.state = STATE_DOWN;
366  Move_Calc(ent, ent->moveinfo.end_origin, plat_hit_bottom);
367 }
368 
369 void plat_go_up(edict_t *ent)
370 {
371  if (!(ent->flags & FL_TEAMSLAVE)) {
372  if (ent->moveinfo.sound_start)
373  gi.sound(ent, CHAN_NO_PHS_ADD + CHAN_VOICE, ent->moveinfo.sound_start, 1, ATTN_STATIC, 0);
374  ent->s.sound = ent->moveinfo.sound_middle;
375  }
376  ent->moveinfo.state = STATE_UP;
377  Move_Calc(ent, ent->moveinfo.start_origin, plat_hit_top);
378 }
379 
380 void plat_blocked(edict_t *self, edict_t *other)
381 {
382  if (!(other->svflags & SVF_MONSTER) && (!other->client)) {
383  // give it a chance to go away on it's own terms (like gibs)
384  T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH);
385  // if it's still there, nuke it
386  if (other)
388  return;
389  }
390 
391  T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
392 
393  if (self->moveinfo.state == STATE_UP)
394  plat_go_down(self);
395  else if (self->moveinfo.state == STATE_DOWN)
396  plat_go_up(self);
397 }
398 
399 
400 void Use_Plat(edict_t *ent, edict_t *other, edict_t *activator)
401 {
402  if (ent->think)
403  return; // already down
404  plat_go_down(ent);
405 }
406 
407 
408 void Touch_Plat_Center(edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
409 {
410  if (!other->client)
411  return;
412 
413  if (other->health <= 0)
414  return;
415 
416  ent = ent->enemy; // now point at the plat, not the trigger
417  if (ent->moveinfo.state == STATE_BOTTOM)
418  plat_go_up(ent);
419  else if (ent->moveinfo.state == STATE_TOP)
420  ent->nextthink = level.time + 1; // the player is still on the plat, so delay going down
421 }
422 
423 void plat_spawn_inside_trigger(edict_t *ent)
424 {
425  edict_t *trigger;
426  vec3_t tmin, tmax;
427 
428 //
429 // middle trigger
430 //
431  trigger = G_Spawn();
432  trigger->touch = Touch_Plat_Center;
433  trigger->movetype = MOVETYPE_NONE;
434  trigger->solid = SOLID_TRIGGER;
435  trigger->enemy = ent;
436 
437  tmin[0] = ent->mins[0] + 25;
438  tmin[1] = ent->mins[1] + 25;
439  tmin[2] = ent->mins[2];
440 
441  tmax[0] = ent->maxs[0] - 25;
442  tmax[1] = ent->maxs[1] - 25;
443  tmax[2] = ent->maxs[2] + 8;
444 
445  tmin[2] = tmax[2] - (ent->pos1[2] - ent->pos2[2] + st.lip);
446 
447  if (ent->spawnflags & PLAT_LOW_TRIGGER)
448  tmax[2] = tmin[2] + 8;
449 
450  if (tmax[0] - tmin[0] <= 0) {
451  tmin[0] = (ent->mins[0] + ent->maxs[0]) * 0.5;
452  tmax[0] = tmin[0] + 1;
453  }
454  if (tmax[1] - tmin[1] <= 0) {
455  tmin[1] = (ent->mins[1] + ent->maxs[1]) * 0.5;
456  tmax[1] = tmin[1] + 1;
457  }
458 
459  VectorCopy(tmin, trigger->mins);
460  VectorCopy(tmax, trigger->maxs);
461 
462  gi.linkentity(trigger);
463 }
464 
465 
466 /*QUAKED func_plat (0 .5 .8) ? PLAT_LOW_TRIGGER
467 speed default 150
468 
469 Plats are always drawn in the extended position, so they will light correctly.
470 
471 If the plat is the target of another trigger or button, it will start out disabled in the extended position until it is trigger, when it will lower and become a normal plat.
472 
473 "speed" overrides default 200.
474 "accel" overrides default 500
475 "lip" overrides default 8 pixel lip
476 
477 If the "height" key is set, that will determine the amount the plat moves, instead of being implicitly determoveinfoned by the model's height.
478 
479 Set "sounds" to one of the following:
480 1) base fast
481 2) chain slow
482 */
483 void SP_func_plat(edict_t *ent)
484 {
485  VectorClear(ent->s.angles);
486  ent->solid = SOLID_BSP;
487  ent->movetype = MOVETYPE_PUSH;
488 
489  gi.setmodel(ent, ent->model);
490 
491  ent->blocked = plat_blocked;
492 
493  if (!ent->speed)
494  ent->speed = 20;
495  else
496  ent->speed *= 0.1;
497 
498  if (!ent->accel)
499  ent->accel = 5;
500  else
501  ent->accel *= 0.1;
502 
503  if (!ent->decel)
504  ent->decel = 5;
505  else
506  ent->decel *= 0.1;
507 
508  if (!ent->dmg)
509  ent->dmg = 2;
510 
511  if (!st.lip)
512  st.lip = 8;
513 
514  // pos1 is the top position, pos2 is the bottom
515  VectorCopy(ent->s.origin, ent->pos1);
516  VectorCopy(ent->s.origin, ent->pos2);
517  if (st.height)
518  ent->pos2[2] -= st.height;
519  else
520  ent->pos2[2] -= (ent->maxs[2] - ent->mins[2]) - st.lip;
521 
522  ent->use = Use_Plat;
523 
524  plat_spawn_inside_trigger(ent); // the "start moving" trigger
525 
526  if (ent->targetname) {
527  ent->moveinfo.state = STATE_UP;
528  } else {
529  VectorCopy(ent->pos2, ent->s.origin);
530  gi.linkentity(ent);
531  ent->moveinfo.state = STATE_BOTTOM;
532  }
533 
534  ent->moveinfo.speed = ent->speed;
535  ent->moveinfo.accel = ent->accel;
536  ent->moveinfo.decel = ent->decel;
537  ent->moveinfo.wait = ent->wait;
538  VectorCopy(ent->pos1, ent->moveinfo.start_origin);
539  VectorCopy(ent->s.angles, ent->moveinfo.start_angles);
540  VectorCopy(ent->pos2, ent->moveinfo.end_origin);
541  VectorCopy(ent->s.angles, ent->moveinfo.end_angles);
542 
543  ent->moveinfo.sound_start = gi.soundindex("plats/pt1_strt.wav");
544  ent->moveinfo.sound_middle = gi.soundindex("plats/pt1_mid.wav");
545  ent->moveinfo.sound_end = gi.soundindex("plats/pt1_end.wav");
546 }
547 
548 //====================================================================
549 
550 /*QUAKED func_rotating (0 .5 .8) ? START_ON REVERSE X_AXIS Y_AXIS TOUCH_PAIN STOP ANIMATED ANIMATED_FAST
551 You need to have an origin brush as part of this entity. The center of that brush will be
552 the point around which it is rotated. It will rotate around the Z axis by default. You can
553 check either the X_AXIS or Y_AXIS box to change that.
554 
555 "speed" determines how fast it moves; default value is 100.
556 "dmg" damage to inflict when blocked (2 default)
557 
558 REVERSE will cause the it to rotate in the opposite direction.
559 STOP mean it will stop moving instead of pushing entities
560 */
561 
562 void rotating_blocked(edict_t *self, edict_t *other)
563 {
564  T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
565 }
566 
567 void rotating_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
568 {
569  if (self->avelocity[0] || self->avelocity[1] || self->avelocity[2])
570  T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
571 }
572 
573 void rotating_use(edict_t *self, edict_t *other, edict_t *activator)
574 {
575  if (!VectorCompare(self->avelocity, vec3_origin)) {
576  self->s.sound = 0;
577  VectorClear(self->avelocity);
578  self->touch = NULL;
579  } else {
580  self->s.sound = self->moveinfo.sound_middle;
581  VectorScale(self->movedir, self->speed, self->avelocity);
582  if (self->spawnflags & 16)
583  self->touch = rotating_touch;
584  }
585 }
586 
587 void SP_func_rotating(edict_t *ent)
588 {
589  ent->solid = SOLID_BSP;
590  if (ent->spawnflags & 32)
591  ent->movetype = MOVETYPE_STOP;
592  else
593  ent->movetype = MOVETYPE_PUSH;
594 
595  // set the axis of rotation
596  VectorClear(ent->movedir);
597  if (ent->spawnflags & 4)
598  ent->movedir[2] = 1.0;
599  else if (ent->spawnflags & 8)
600  ent->movedir[0] = 1.0;
601  else // Z_AXIS
602  ent->movedir[1] = 1.0;
603 
604  // check for reverse rotation
605  if (ent->spawnflags & 2)
606  VectorNegate(ent->movedir, ent->movedir);
607 
608  if (!ent->speed)
609  ent->speed = 100;
610  if (!ent->dmg)
611  ent->dmg = 2;
612 
613 // ent->moveinfo.sound_middle = "doors/hydro1.wav";
614 
615  ent->use = rotating_use;
616  if (ent->dmg)
617  ent->blocked = rotating_blocked;
618 
619  if (ent->spawnflags & 1)
620  ent->use(ent, NULL, NULL);
621 
622  if (ent->spawnflags & 64)
623  ent->s.effects |= EF_ANIM_ALL;
624  if (ent->spawnflags & 128)
625  ent->s.effects |= EF_ANIM_ALLFAST;
626 
627  gi.setmodel(ent, ent->model);
628  gi.linkentity(ent);
629 }
630 
631 /*
632 ======================================================================
633 
634 BUTTONS
635 
636 ======================================================================
637 */
638 
639 /*QUAKED func_button (0 .5 .8) ?
640 When a button is touched, it moves some distance in the direction of it's angle, triggers all of it's targets, waits some time, then returns to it's original position where it can be triggered again.
641 
642 "angle" determines the opening direction
643 "target" all entities with a matching targetname will be used
644 "speed" override the default 40 speed
645 "wait" override the default 1 second wait (-1 = never return)
646 "lip" override the default 4 pixel lip remaining at end of move
647 "health" if set, the button must be killed instead of touched
648 "sounds"
649 1) silent
650 2) steam metal
651 3) wooden clunk
652 4) metallic click
653 5) in-out
654 */
655 
656 void button_done(edict_t *self)
657 {
658  self->moveinfo.state = STATE_BOTTOM;
659  self->s.effects &= ~EF_ANIM23;
660  self->s.effects |= EF_ANIM01;
661 }
662 
663 void button_return(edict_t *self)
664 {
665  self->moveinfo.state = STATE_DOWN;
666 
667  Move_Calc(self, self->moveinfo.start_origin, button_done);
668 
669  self->s.frame = 0;
670 
671  if (self->health)
672  self->takedamage = DAMAGE_YES;
673 }
674 
675 void button_wait(edict_t *self)
676 {
677  self->moveinfo.state = STATE_TOP;
678  self->s.effects &= ~EF_ANIM01;
679  self->s.effects |= EF_ANIM23;
680 
681  G_UseTargets(self, self->activator);
682  self->s.frame = 1;
683  if (self->moveinfo.wait >= 0) {
684  self->nextthink = level.time + self->moveinfo.wait;
685  self->think = button_return;
686  }
687 }
688 
689 void button_fire(edict_t *self)
690 {
691  if (self->moveinfo.state == STATE_UP || self->moveinfo.state == STATE_TOP)
692  return;
693 
694  self->moveinfo.state = STATE_UP;
695  if (self->moveinfo.sound_start && !(self->flags & FL_TEAMSLAVE))
696  gi.sound(self, CHAN_NO_PHS_ADD + CHAN_VOICE, self->moveinfo.sound_start, 1, ATTN_STATIC, 0);
697  Move_Calc(self, self->moveinfo.end_origin, button_wait);
698 }
699 
700 void button_use(edict_t *self, edict_t *other, edict_t *activator)
701 {
702  self->activator = activator;
703  button_fire(self);
704 }
705 
706 void button_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
707 {
708  if (!other->client)
709  return;
710 
711  if (other->health <= 0)
712  return;
713 
714  self->activator = other;
715  button_fire(self);
716 }
717 
718 void button_killed(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
719 {
720  self->activator = attacker;
721  self->health = self->max_health;
722  self->takedamage = DAMAGE_NO;
723  button_fire(self);
724 }
725 
726 void SP_func_button(edict_t *ent)
727 {
728  vec3_t abs_movedir;
729  float dist;
730 
731  G_SetMovedir(ent->s.angles, ent->movedir);
732  ent->movetype = MOVETYPE_STOP;
733  ent->solid = SOLID_BSP;
734  gi.setmodel(ent, ent->model);
735 
736  if (ent->sounds != 1)
737  ent->moveinfo.sound_start = gi.soundindex("switches/butn2.wav");
738 
739  if (!ent->speed)
740  ent->speed = 40;
741  if (!ent->accel)
742  ent->accel = ent->speed;
743  if (!ent->decel)
744  ent->decel = ent->speed;
745 
746  if (!ent->wait)
747  ent->wait = 3;
748  if (!st.lip)
749  st.lip = 4;
750 
751  VectorCopy(ent->s.origin, ent->pos1);
752  abs_movedir[0] = fabs(ent->movedir[0]);
753  abs_movedir[1] = fabs(ent->movedir[1]);
754  abs_movedir[2] = fabs(ent->movedir[2]);
755  dist = abs_movedir[0] * ent->size[0] + abs_movedir[1] * ent->size[1] + abs_movedir[2] * ent->size[2] - st.lip;
756  VectorMA(ent->pos1, dist, ent->movedir, ent->pos2);
757 
758  ent->use = button_use;
759  ent->s.effects |= EF_ANIM01;
760 
761  if (ent->health) {
762  ent->max_health = ent->health;
763  ent->die = button_killed;
764  ent->takedamage = DAMAGE_YES;
765  } else if (! ent->targetname)
766  ent->touch = button_touch;
767 
768  ent->moveinfo.state = STATE_BOTTOM;
769 
770  ent->moveinfo.speed = ent->speed;
771  ent->moveinfo.accel = ent->accel;
772  ent->moveinfo.decel = ent->decel;
773  ent->moveinfo.wait = ent->wait;
774  VectorCopy(ent->pos1, ent->moveinfo.start_origin);
775  VectorCopy(ent->s.angles, ent->moveinfo.start_angles);
776  VectorCopy(ent->pos2, ent->moveinfo.end_origin);
777  VectorCopy(ent->s.angles, ent->moveinfo.end_angles);
778 
779  gi.linkentity(ent);
780 }
781 
782 /*
783 ======================================================================
784 
785 DOORS
786 
787  spawn a trigger surrounding the entire team unless it is
788  already targeted by another
789 
790 ======================================================================
791 */
792 
793 /*QUAKED func_door (0 .5 .8) ? START_OPEN x CRUSHER NOMONSTER ANIMATED TOGGLE ANIMATED_FAST
794 TOGGLE wait in both the start and end states for a trigger event.
795 START_OPEN the door to moves to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not useful for touch or takedamage doors).
796 NOMONSTER monsters will not trigger this door
797 
798 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
799 "angle" determines the opening direction
800 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
801 "health" if set, door must be shot open
802 "speed" movement speed (100 default)
803 "wait" wait before returning (3 default, -1 = never return)
804 "lip" lip remaining at end of move (8 default)
805 "dmg" damage to inflict when blocked (2 default)
806 "sounds"
807 1) silent
808 2) light
809 3) medium
810 4) heavy
811 */
812 
813 void door_use_areaportals(edict_t *self, qboolean open)
814 {
815  edict_t *t = NULL;
816 
817  if (!self->target)
818  return;
819 
820  while ((t = G_Find(t, FOFS(targetname), self->target))) {
821  if (Q_stricmp(t->classname, "func_areaportal") == 0) {
822  gi.SetAreaPortalState(t->style, open);
823  }
824  }
825 }
826 
827 void door_go_down(edict_t *self);
828 
829 void door_hit_top(edict_t *self)
830 {
831  if (!(self->flags & FL_TEAMSLAVE)) {
832  if (self->moveinfo.sound_end)
833  gi.sound(self, CHAN_NO_PHS_ADD + CHAN_VOICE, self->moveinfo.sound_end, 1, ATTN_STATIC, 0);
834  self->s.sound = 0;
835  }
836  self->moveinfo.state = STATE_TOP;
837  if (self->spawnflags & DOOR_TOGGLE)
838  return;
839  if (self->moveinfo.wait >= 0) {
840  self->think = door_go_down;
841  self->nextthink = level.time + self->moveinfo.wait;
842  }
843 }
844 
845 void door_hit_bottom(edict_t *self)
846 {
847  if (!(self->flags & FL_TEAMSLAVE)) {
848  if (self->moveinfo.sound_end)
849  gi.sound(self, CHAN_NO_PHS_ADD + CHAN_VOICE, self->moveinfo.sound_end, 1, ATTN_STATIC, 0);
850  self->s.sound = 0;
851  }
852  self->moveinfo.state = STATE_BOTTOM;
853  door_use_areaportals(self, qfalse);
854 }
855 
856 void door_go_down(edict_t *self)
857 {
858  if (!(self->flags & FL_TEAMSLAVE)) {
859  if (self->moveinfo.sound_start)
860  gi.sound(self, CHAN_NO_PHS_ADD + CHAN_VOICE, self->moveinfo.sound_start, 1, ATTN_STATIC, 0);
861  self->s.sound = self->moveinfo.sound_middle;
862  }
863  if (self->max_health) {
864  self->takedamage = DAMAGE_YES;
865  self->health = self->max_health;
866  }
867 
868  self->moveinfo.state = STATE_DOWN;
869  if (strcmp(self->classname, "func_door") == 0)
870  Move_Calc(self, self->moveinfo.start_origin, door_hit_bottom);
871  else if (strcmp(self->classname, "func_door_rotating") == 0)
873 }
874 
875 void door_go_up(edict_t *self, edict_t *activator)
876 {
877  if (self->moveinfo.state == STATE_UP)
878  return; // already going up
879 
880  if (self->moveinfo.state == STATE_TOP) {
881  // reset top wait time
882  if (self->moveinfo.wait >= 0)
883  self->nextthink = level.time + self->moveinfo.wait;
884  return;
885  }
886 
887  if (!(self->flags & FL_TEAMSLAVE)) {
888  if (self->moveinfo.sound_start)
889  gi.sound(self, CHAN_NO_PHS_ADD + CHAN_VOICE, self->moveinfo.sound_start, 1, ATTN_STATIC, 0);
890  self->s.sound = self->moveinfo.sound_middle;
891  }
892  self->moveinfo.state = STATE_UP;
893  if (strcmp(self->classname, "func_door") == 0)
894  Move_Calc(self, self->moveinfo.end_origin, door_hit_top);
895  else if (strcmp(self->classname, "func_door_rotating") == 0)
897 
898  G_UseTargets(self, activator);
899  door_use_areaportals(self, qtrue);
900 }
901 
902 void door_use(edict_t *self, edict_t *other, edict_t *activator)
903 {
904  edict_t *ent;
905 
906  if (self->flags & FL_TEAMSLAVE)
907  return;
908 
909  if (self->spawnflags & DOOR_TOGGLE) {
910  if (self->moveinfo.state == STATE_UP || self->moveinfo.state == STATE_TOP) {
911  // trigger all paired doors
912  for (ent = self ; ent ; ent = ent->teamchain) {
913  ent->message = NULL;
914  ent->touch = NULL;
915  door_go_down(ent);
916  }
917  return;
918  }
919  }
920 
921  // trigger all paired doors
922  for (ent = self ; ent ; ent = ent->teamchain) {
923  ent->message = NULL;
924  ent->touch = NULL;
925  door_go_up(ent, activator);
926  }
927 }
928 
929 void Touch_DoorTrigger(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
930 {
931  if (other->health <= 0)
932  return;
933 
934  if (!(other->svflags & SVF_MONSTER) && (!other->client))
935  return;
936 
937  if ((self->owner->spawnflags & DOOR_NOMONSTER) && (other->svflags & SVF_MONSTER))
938  return;
939 
940  if (level.time < self->touch_debounce_time)
941  return;
942  self->touch_debounce_time = level.time + 1.0;
943 
944  door_use(self->owner, other, other);
945 }
946 
947 void Think_CalcMoveSpeed(edict_t *self)
948 {
949  edict_t *ent;
950  float min;
951  float time;
952  float newspeed;
953  float ratio;
954  float dist;
955 
956  if (self->flags & FL_TEAMSLAVE)
957  return; // only the team master does this
958 
959  // find the smallest distance any member of the team will be moving
960  min = fabs(self->moveinfo.distance);
961  for (ent = self->teamchain; ent; ent = ent->teamchain) {
962  dist = fabs(ent->moveinfo.distance);
963  if (dist < min)
964  min = dist;
965  }
966 
967  time = min / self->moveinfo.speed;
968 
969  // adjust speeds so they will all complete at the same time
970  for (ent = self; ent; ent = ent->teamchain) {
971  newspeed = fabs(ent->moveinfo.distance) / time;
972  ratio = newspeed / ent->moveinfo.speed;
973  if (ent->moveinfo.accel == ent->moveinfo.speed)
974  ent->moveinfo.accel = newspeed;
975  else
976  ent->moveinfo.accel *= ratio;
977  if (ent->moveinfo.decel == ent->moveinfo.speed)
978  ent->moveinfo.decel = newspeed;
979  else
980  ent->moveinfo.decel *= ratio;
981  ent->moveinfo.speed = newspeed;
982  }
983 }
984 
985 void Think_SpawnDoorTrigger(edict_t *ent)
986 {
987  edict_t *other;
988  vec3_t mins, maxs;
989 
990  if (ent->flags & FL_TEAMSLAVE)
991  return; // only the team leader spawns a trigger
992 
993  VectorCopy(ent->absmin, mins);
994  VectorCopy(ent->absmax, maxs);
995 
996  for (other = ent->teamchain ; other ; other = other->teamchain) {
997  AddPointToBounds(other->absmin, mins, maxs);
998  AddPointToBounds(other->absmax, mins, maxs);
999  }
1000 
1001  // expand
1002  mins[0] -= 60;
1003  mins[1] -= 60;
1004  maxs[0] += 60;
1005  maxs[1] += 60;
1006 
1007  other = G_Spawn();
1008  VectorCopy(mins, other->mins);
1009  VectorCopy(maxs, other->maxs);
1010  other->owner = ent;
1011  other->solid = SOLID_TRIGGER;
1012  other->movetype = MOVETYPE_NONE;
1013  other->touch = Touch_DoorTrigger;
1014  gi.linkentity(other);
1015 
1016  if (ent->spawnflags & DOOR_START_OPEN)
1017  door_use_areaportals(ent, qtrue);
1018 
1019  Think_CalcMoveSpeed(ent);
1020 }
1021 
1022 void door_blocked(edict_t *self, edict_t *other)
1023 {
1024  edict_t *ent;
1025 
1026  if (!(other->svflags & SVF_MONSTER) && (!other->client)) {
1027  // give it a chance to go away on it's own terms (like gibs)
1028  T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH);
1029  // if it's still there, nuke it
1030  if (other)
1032  return;
1033  }
1034 
1035  T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
1036 
1037  if (self->spawnflags & DOOR_CRUSHER)
1038  return;
1039 
1040 
1041 // if a door has a negative wait, it would never come back if blocked,
1042 // so let it just squash the object to death real fast
1043  if (self->moveinfo.wait >= 0) {
1044  if (self->moveinfo.state == STATE_DOWN) {
1045  for (ent = self->teammaster ; ent ; ent = ent->teamchain)
1046  door_go_up(ent, ent->activator);
1047  } else {
1048  for (ent = self->teammaster ; ent ; ent = ent->teamchain)
1049  door_go_down(ent);
1050  }
1051  }
1052 }
1053 
1054 void door_killed(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1055 {
1056  edict_t *ent;
1057 
1058  for (ent = self->teammaster ; ent ; ent = ent->teamchain) {
1059  ent->health = ent->max_health;
1060  ent->takedamage = DAMAGE_NO;
1061  }
1062  door_use(self->teammaster, attacker, attacker);
1063 }
1064 
1065 void door_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
1066 {
1067  if (!other->client)
1068  return;
1069 
1070  if (level.time < self->touch_debounce_time)
1071  return;
1072  self->touch_debounce_time = level.time + 5.0;
1073 
1074  gi.centerprintf(other, "%s", self->message);
1075  gi.sound(other, CHAN_AUTO, gi.soundindex("misc/talk1.wav"), 1, ATTN_NORM, 0);
1076 }
1077 
1078 void SP_func_door(edict_t *ent)
1079 {
1080  vec3_t abs_movedir;
1081 
1082  if (ent->sounds != 1) {
1083  ent->moveinfo.sound_start = gi.soundindex("doors/dr1_strt.wav");
1084  ent->moveinfo.sound_middle = gi.soundindex("doors/dr1_mid.wav");
1085  ent->moveinfo.sound_end = gi.soundindex("doors/dr1_end.wav");
1086  }
1087 
1088  G_SetMovedir(ent->s.angles, ent->movedir);
1089  ent->movetype = MOVETYPE_PUSH;
1090  ent->solid = SOLID_BSP;
1091  gi.setmodel(ent, ent->model);
1092 
1093  ent->blocked = door_blocked;
1094  ent->use = door_use;
1095 
1096  if (!ent->speed)
1097  ent->speed = 100;
1098  if (deathmatch->value)
1099  ent->speed *= 2;
1100 
1101  if (!ent->accel)
1102  ent->accel = ent->speed;
1103  if (!ent->decel)
1104  ent->decel = ent->speed;
1105 
1106  if (!ent->wait)
1107  ent->wait = 3;
1108  if (!st.lip)
1109  st.lip = 8;
1110  if (!ent->dmg)
1111  ent->dmg = 2;
1112 
1113  // calculate second position
1114  VectorCopy(ent->s.origin, ent->pos1);
1115  abs_movedir[0] = fabs(ent->movedir[0]);
1116  abs_movedir[1] = fabs(ent->movedir[1]);
1117  abs_movedir[2] = fabs(ent->movedir[2]);
1118  ent->moveinfo.distance = abs_movedir[0] * ent->size[0] + abs_movedir[1] * ent->size[1] + abs_movedir[2] * ent->size[2] - st.lip;
1119  VectorMA(ent->pos1, ent->moveinfo.distance, ent->movedir, ent->pos2);
1120 
1121  // if it starts open, switch the positions
1122  if (ent->spawnflags & DOOR_START_OPEN) {
1123  VectorCopy(ent->pos2, ent->s.origin);
1124  VectorCopy(ent->pos1, ent->pos2);
1125  VectorCopy(ent->s.origin, ent->pos1);
1126  }
1127 
1128  ent->moveinfo.state = STATE_BOTTOM;
1129 
1130  if (ent->health) {
1131  ent->takedamage = DAMAGE_YES;
1132  ent->die = door_killed;
1133  ent->max_health = ent->health;
1134  } else if (ent->targetname && ent->message) {
1135  gi.soundindex("misc/talk.wav");
1136  ent->touch = door_touch;
1137  }
1138 
1139  ent->moveinfo.speed = ent->speed;
1140  ent->moveinfo.accel = ent->accel;
1141  ent->moveinfo.decel = ent->decel;
1142  ent->moveinfo.wait = ent->wait;
1143  VectorCopy(ent->pos1, ent->moveinfo.start_origin);
1144  VectorCopy(ent->s.angles, ent->moveinfo.start_angles);
1145  VectorCopy(ent->pos2, ent->moveinfo.end_origin);
1146  VectorCopy(ent->s.angles, ent->moveinfo.end_angles);
1147 
1148  if (ent->spawnflags & 16)
1149  ent->s.effects |= EF_ANIM_ALL;
1150  if (ent->spawnflags & 64)
1151  ent->s.effects |= EF_ANIM_ALLFAST;
1152 
1153  // to simplify logic elsewhere, make non-teamed doors into a team of one
1154  if (!ent->team)
1155  ent->teammaster = ent;
1156 
1157  gi.linkentity(ent);
1158 
1159  ent->nextthink = level.time + FRAMETIME;
1160  if (ent->health || ent->targetname)
1161  ent->think = Think_CalcMoveSpeed;
1162  else
1163  ent->think = Think_SpawnDoorTrigger;
1164 }
1165 
1166 
1167 /*QUAKED func_door_rotating (0 .5 .8) ? START_OPEN REVERSE CRUSHER NOMONSTER ANIMATED TOGGLE X_AXIS Y_AXIS
1168 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1169 
1170 START_OPEN the door to moves to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not useful for touch or takedamage doors).
1171 NOMONSTER monsters will not trigger this door
1172 
1173 You need to have an origin brush as part of this entity. The center of that brush will be
1174 the point around which it is rotated. It will rotate around the Z axis by default. You can
1175 check either the X_AXIS or Y_AXIS box to change that.
1176 
1177 "distance" is how many degrees the door will be rotated.
1178 "speed" determines how fast the door moves; default value is 100.
1179 
1180 REVERSE will cause the door to rotate in the opposite direction.
1181 
1182 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1183 "angle" determines the opening direction
1184 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1185 "health" if set, door must be shot open
1186 "speed" movement speed (100 default)
1187 "wait" wait before returning (3 default, -1 = never return)
1188 "dmg" damage to inflict when blocked (2 default)
1189 "sounds"
1190 1) silent
1191 2) light
1192 3) medium
1193 4) heavy
1194 */
1195 
1196 void SP_func_door_rotating(edict_t *ent)
1197 {
1198  VectorClear(ent->s.angles);
1199 
1200  // set the axis of rotation
1201  VectorClear(ent->movedir);
1202  if (ent->spawnflags & DOOR_X_AXIS)
1203  ent->movedir[2] = 1.0;
1204  else if (ent->spawnflags & DOOR_Y_AXIS)
1205  ent->movedir[0] = 1.0;
1206  else // Z_AXIS
1207  ent->movedir[1] = 1.0;
1208 
1209  // check for reverse rotation
1210  if (ent->spawnflags & DOOR_REVERSE)
1211  VectorNegate(ent->movedir, ent->movedir);
1212 
1213  if (!st.distance) {
1214  gi.dprintf("%s at %s with no distance set\n", ent->classname, vtos(ent->s.origin));
1215  st.distance = 90;
1216  }
1217 
1218  VectorCopy(ent->s.angles, ent->pos1);
1219  VectorMA(ent->s.angles, st.distance, ent->movedir, ent->pos2);
1220  ent->moveinfo.distance = st.distance;
1221 
1222  ent->movetype = MOVETYPE_PUSH;
1223  ent->solid = SOLID_BSP;
1224  gi.setmodel(ent, ent->model);
1225 
1226  ent->blocked = door_blocked;
1227  ent->use = door_use;
1228 
1229  if (!ent->speed)
1230  ent->speed = 100;
1231  if (!ent->accel)
1232  ent->accel = ent->speed;
1233  if (!ent->decel)
1234  ent->decel = ent->speed;
1235 
1236  if (!ent->wait)
1237  ent->wait = 3;
1238  if (!ent->dmg)
1239  ent->dmg = 2;
1240 
1241  if (ent->sounds != 1) {
1242  ent->moveinfo.sound_start = gi.soundindex("doors/dr1_strt.wav");
1243  ent->moveinfo.sound_middle = gi.soundindex("doors/dr1_mid.wav");
1244  ent->moveinfo.sound_end = gi.soundindex("doors/dr1_end.wav");
1245  }
1246 
1247  // if it starts open, switch the positions
1248  if (ent->spawnflags & DOOR_START_OPEN) {
1249  VectorCopy(ent->pos2, ent->s.angles);
1250  VectorCopy(ent->pos1, ent->pos2);
1251  VectorCopy(ent->s.angles, ent->pos1);
1252  VectorNegate(ent->movedir, ent->movedir);
1253  }
1254 
1255  if (ent->health) {
1256  ent->takedamage = DAMAGE_YES;
1257  ent->die = door_killed;
1258  ent->max_health = ent->health;
1259  }
1260 
1261  if (ent->targetname && ent->message) {
1262  gi.soundindex("misc/talk.wav");
1263  ent->touch = door_touch;
1264  }
1265 
1266  ent->moveinfo.state = STATE_BOTTOM;
1267  ent->moveinfo.speed = ent->speed;
1268  ent->moveinfo.accel = ent->accel;
1269  ent->moveinfo.decel = ent->decel;
1270  ent->moveinfo.wait = ent->wait;
1271  VectorCopy(ent->s.origin, ent->moveinfo.start_origin);
1272  VectorCopy(ent->pos1, ent->moveinfo.start_angles);
1273  VectorCopy(ent->s.origin, ent->moveinfo.end_origin);
1274  VectorCopy(ent->pos2, ent->moveinfo.end_angles);
1275 
1276  if (ent->spawnflags & 16)
1277  ent->s.effects |= EF_ANIM_ALL;
1278 
1279  // to simplify logic elsewhere, make non-teamed doors into a team of one
1280  if (!ent->team)
1281  ent->teammaster = ent;
1282 
1283  gi.linkentity(ent);
1284 
1285  ent->nextthink = level.time + FRAMETIME;
1286  if (ent->health || ent->targetname)
1287  ent->think = Think_CalcMoveSpeed;
1288  else
1289  ent->think = Think_SpawnDoorTrigger;
1290 }
1291 
1292 
1293 /*QUAKED func_water (0 .5 .8) ? START_OPEN
1294 func_water is a moveable water brush. It must be targeted to operate. Use a non-water texture at your own risk.
1295 
1296 START_OPEN causes the water to move to its destination when spawned and operate in reverse.
1297 
1298 "angle" determines the opening direction (up or down only)
1299 "speed" movement speed (25 default)
1300 "wait" wait before returning (-1 default, -1 = TOGGLE)
1301 "lip" lip remaining at end of move (0 default)
1302 "sounds" (yes, these need to be changed)
1303 0) no sound
1304 1) water
1305 2) lava
1306 */
1307 
1308 void SP_func_water(edict_t *self)
1309 {
1310  vec3_t abs_movedir;
1311 
1312  G_SetMovedir(self->s.angles, self->movedir);
1313  self->movetype = MOVETYPE_PUSH;
1314  self->solid = SOLID_BSP;
1315  gi.setmodel(self, self->model);
1316 
1317  switch (self->sounds) {
1318  default:
1319  break;
1320 
1321  case 1: // water
1322  self->moveinfo.sound_start = gi.soundindex("world/mov_watr.wav");
1323  self->moveinfo.sound_end = gi.soundindex("world/stp_watr.wav");
1324  break;
1325 
1326  case 2: // lava
1327  self->moveinfo.sound_start = gi.soundindex("world/mov_watr.wav");
1328  self->moveinfo.sound_end = gi.soundindex("world/stp_watr.wav");
1329  break;
1330  }
1331 
1332  // calculate second position
1333  VectorCopy(self->s.origin, self->pos1);
1334  abs_movedir[0] = fabs(self->movedir[0]);
1335  abs_movedir[1] = fabs(self->movedir[1]);
1336  abs_movedir[2] = fabs(self->movedir[2]);
1337  self->moveinfo.distance = abs_movedir[0] * self->size[0] + abs_movedir[1] * self->size[1] + abs_movedir[2] * self->size[2] - st.lip;
1338  VectorMA(self->pos1, self->moveinfo.distance, self->movedir, self->pos2);
1339 
1340  // if it starts open, switch the positions
1341  if (self->spawnflags & DOOR_START_OPEN) {
1342  VectorCopy(self->pos2, self->s.origin);
1343  VectorCopy(self->pos1, self->pos2);
1344  VectorCopy(self->s.origin, self->pos1);
1345  }
1346 
1347  VectorCopy(self->pos1, self->moveinfo.start_origin);
1348  VectorCopy(self->s.angles, self->moveinfo.start_angles);
1349  VectorCopy(self->pos2, self->moveinfo.end_origin);
1350  VectorCopy(self->s.angles, self->moveinfo.end_angles);
1351 
1352  self->moveinfo.state = STATE_BOTTOM;
1353 
1354  if (!self->speed)
1355  self->speed = 25;
1356  self->moveinfo.accel = self->moveinfo.decel = self->moveinfo.speed = self->speed;
1357 
1358  if (!self->wait)
1359  self->wait = -1;
1360  self->moveinfo.wait = self->wait;
1361 
1362  self->use = door_use;
1363 
1364  if (self->wait == -1)
1365  self->spawnflags |= DOOR_TOGGLE;
1366 
1367  self->classname = "func_door";
1368 
1369  gi.linkentity(self);
1370 }
1371 
1372 
1373 #define TRAIN_START_ON 1
1374 #define TRAIN_TOGGLE 2
1375 #define TRAIN_BLOCK_STOPS 4
1376 
1377 /*QUAKED func_train (0 .5 .8) ? START_ON TOGGLE BLOCK_STOPS
1378 Trains are moving platforms that players can ride.
1379 The targets origin specifies the min point of the train at each corner.
1380 The train spawns at the first target it is pointing at.
1381 If the train is the target of a button or trigger, it will not begin moving until activated.
1382 speed default 100
1383 dmg default 2
1384 noise looping sound to play when the train is in motion
1385 
1386 */
1387 void train_next(edict_t *self);
1388 
1389 void train_blocked(edict_t *self, edict_t *other)
1390 {
1391  if (!(other->svflags & SVF_MONSTER) && (!other->client)) {
1392  // give it a chance to go away on it's own terms (like gibs)
1393  T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH);
1394  // if it's still there, nuke it
1395  if (other)
1397  return;
1398  }
1399 
1400  if (level.time < self->touch_debounce_time)
1401  return;
1402 
1403  if (!self->dmg)
1404  return;
1405  self->touch_debounce_time = level.time + 0.5;
1406  T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
1407 }
1408 
1409 void train_wait(edict_t *self)
1410 {
1411  if (self->target_ent->pathtarget) {
1412  char *savetarget;
1413  edict_t *ent;
1414 
1415  ent = self->target_ent;
1416  savetarget = ent->target;
1417  ent->target = ent->pathtarget;
1418  G_UseTargets(ent, self->activator);
1419  ent->target = savetarget;
1420 
1421  // make sure we didn't get killed by a killtarget
1422  if (!self->inuse)
1423  return;
1424  }
1425 
1426  if (self->moveinfo.wait) {
1427  if (self->moveinfo.wait > 0) {
1428  self->nextthink = level.time + self->moveinfo.wait;
1429  self->think = train_next;
1430  } else if (self->spawnflags & TRAIN_TOGGLE) { // && wait < 0
1431  train_next(self);
1432  self->spawnflags &= ~TRAIN_START_ON;
1433  VectorClear(self->velocity);
1434  self->nextthink = 0;
1435  }
1436 
1437  if (!(self->flags & FL_TEAMSLAVE)) {
1438  if (self->moveinfo.sound_end)
1439  gi.sound(self, CHAN_NO_PHS_ADD + CHAN_VOICE, self->moveinfo.sound_end, 1, ATTN_STATIC, 0);
1440  self->s.sound = 0;
1441  }
1442  } else {
1443  train_next(self);
1444  }
1445 
1446 }
1447 
1448 void train_next(edict_t *self)
1449 {
1450  edict_t *ent;
1451  vec3_t dest;
1452  qboolean first;
1453 
1454  first = qtrue;
1455 again:
1456  if (!self->target) {
1457 // gi.dprintf ("train_next: no next target\n");
1458  return;
1459  }
1460 
1461  ent = G_PickTarget(self->target);
1462  if (!ent) {
1463  gi.dprintf("train_next: bad target %s\n", self->target);
1464  return;
1465  }
1466 
1467  self->target = ent->target;
1468 
1469  // check for a teleport path_corner
1470  if (ent->spawnflags & 1) {
1471  if (!first) {
1472  gi.dprintf("connected teleport path_corners, see %s at %s\n", ent->classname, vtos(ent->s.origin));
1473  return;
1474  }
1475  first = qfalse;
1476  VectorSubtract(ent->s.origin, self->mins, self->s.origin);
1477  VectorCopy(self->s.origin, self->s.old_origin);
1478  self->s.event = EV_OTHER_TELEPORT;
1479  gi.linkentity(self);
1480  goto again;
1481  }
1482 
1483  self->moveinfo.wait = ent->wait;
1484  self->target_ent = ent;
1485 
1486  if (!(self->flags & FL_TEAMSLAVE)) {
1487  if (self->moveinfo.sound_start)
1488  gi.sound(self, CHAN_NO_PHS_ADD + CHAN_VOICE, self->moveinfo.sound_start, 1, ATTN_STATIC, 0);
1489  self->s.sound = self->moveinfo.sound_middle;
1490  }
1491 
1492  VectorSubtract(ent->s.origin, self->mins, dest);
1493  self->moveinfo.state = STATE_TOP;
1494  VectorCopy(self->s.origin, self->moveinfo.start_origin);
1495  VectorCopy(dest, self->moveinfo.end_origin);
1496  Move_Calc(self, dest, train_wait);
1497  self->spawnflags |= TRAIN_START_ON;
1498 }
1499 
1500 void train_resume(edict_t *self)
1501 {
1502  edict_t *ent;
1503  vec3_t dest;
1504 
1505  ent = self->target_ent;
1506 
1507  VectorSubtract(ent->s.origin, self->mins, dest);
1508  self->moveinfo.state = STATE_TOP;
1509  VectorCopy(self->s.origin, self->moveinfo.start_origin);
1510  VectorCopy(dest, self->moveinfo.end_origin);
1511  Move_Calc(self, dest, train_wait);
1512  self->spawnflags |= TRAIN_START_ON;
1513 }
1514 
1515 void func_train_find(edict_t *self)
1516 {
1517  edict_t *ent;
1518 
1519  if (!self->target) {
1520  gi.dprintf("train_find: no target\n");
1521  return;
1522  }
1523  ent = G_PickTarget(self->target);
1524  if (!ent) {
1525  gi.dprintf("train_find: target %s not found\n", self->target);
1526  return;
1527  }
1528  self->target = ent->target;
1529 
1530  VectorSubtract(ent->s.origin, self->mins, self->s.origin);
1531  gi.linkentity(self);
1532 
1533  // if not triggered, start immediately
1534  if (!self->targetname)
1535  self->spawnflags |= TRAIN_START_ON;
1536 
1537  if (self->spawnflags & TRAIN_START_ON) {
1538  self->nextthink = level.time + FRAMETIME;
1539  self->think = train_next;
1540  self->activator = self;
1541  }
1542 }
1543 
1544 void train_use(edict_t *self, edict_t *other, edict_t *activator)
1545 {
1546  self->activator = activator;
1547 
1548  if (self->spawnflags & TRAIN_START_ON) {
1549  if (!(self->spawnflags & TRAIN_TOGGLE))
1550  return;
1551  self->spawnflags &= ~TRAIN_START_ON;
1552  VectorClear(self->velocity);
1553  self->nextthink = 0;
1554  } else {
1555  if (self->target_ent)
1556  train_resume(self);
1557  else
1558  train_next(self);
1559  }
1560 }
1561 
1562 void SP_func_train(edict_t *self)
1563 {
1564  self->movetype = MOVETYPE_PUSH;
1565 
1566  VectorClear(self->s.angles);
1567  self->blocked = train_blocked;
1568  if (self->spawnflags & TRAIN_BLOCK_STOPS)
1569  self->dmg = 0;
1570  else {
1571  if (!self->dmg)
1572  self->dmg = 100;
1573  }
1574  self->solid = SOLID_BSP;
1575  gi.setmodel(self, self->model);
1576 
1577  if (st.noise)
1578  self->moveinfo.sound_middle = gi.soundindex(st.noise);
1579 
1580  if (!self->speed)
1581  self->speed = 100;
1582 
1583  self->moveinfo.speed = self->speed;
1584  self->moveinfo.accel = self->moveinfo.decel = self->moveinfo.speed;
1585 
1586  self->use = train_use;
1587 
1588  gi.linkentity(self);
1589 
1590  if (self->target) {
1591  // start trains on the second frame, to make sure their targets have had
1592  // a chance to spawn
1593  self->nextthink = level.time + FRAMETIME;
1594  self->think = func_train_find;
1595  } else {
1596  gi.dprintf("func_train without a target at %s\n", vtos(self->absmin));
1597  }
1598 }
1599 
1600 
1601 /*QUAKED trigger_elevator (0.3 0.1 0.6) (-8 -8 -8) (8 8 8)
1602 */
1603 void trigger_elevator_use(edict_t *self, edict_t *other, edict_t *activator)
1604 {
1605  edict_t *target;
1606 
1607  if (self->movetarget->nextthink) {
1608 // gi.dprintf("elevator busy\n");
1609  return;
1610  }
1611 
1612  if (!other->pathtarget) {
1613  gi.dprintf("elevator used with no pathtarget\n");
1614  return;
1615  }
1616 
1617  target = G_PickTarget(other->pathtarget);
1618  if (!target) {
1619  gi.dprintf("elevator used with bad pathtarget: %s\n", other->pathtarget);
1620  return;
1621  }
1622 
1623  self->movetarget->target_ent = target;
1624  train_resume(self->movetarget);
1625 }
1626 
1627 void trigger_elevator_init(edict_t *self)
1628 {
1629  if (!self->target) {
1630  gi.dprintf("trigger_elevator has no target\n");
1631  return;
1632  }
1633  self->movetarget = G_PickTarget(self->target);
1634  if (!self->movetarget) {
1635  gi.dprintf("trigger_elevator unable to find target %s\n", self->target);
1636  return;
1637  }
1638  if (strcmp(self->movetarget->classname, "func_train") != 0) {
1639  gi.dprintf("trigger_elevator target %s is not a train\n", self->target);
1640  return;
1641  }
1642 
1643  self->use = trigger_elevator_use;
1644  self->svflags = SVF_NOCLIENT;
1645 
1646 }
1647 
1648 void SP_trigger_elevator(edict_t *self)
1649 {
1650  self->think = trigger_elevator_init;
1651  self->nextthink = level.time + FRAMETIME;
1652 }
1653 
1654 
1655 /*QUAKED func_timer (0.3 0.1 0.6) (-8 -8 -8) (8 8 8) START_ON
1656 "wait" base time between triggering all targets, default is 1
1657 "random" wait variance, default is 0
1658 
1659 so, the basic time between firing is a random time between
1660 (wait - random) and (wait + random)
1661 
1662 "delay" delay before first firing when turned on, default is 0
1663 
1664 "pausetime" additional delay used only the very first time
1665  and only if spawned with START_ON
1666 
1667 These can used but not touched.
1668 */
1669 void func_timer_think(edict_t *self)
1670 {
1671  G_UseTargets(self, self->activator);
1672  self->nextthink = level.time + self->wait + crandom() * self->random;
1673 }
1674 
1675 void func_timer_use(edict_t *self, edict_t *other, edict_t *activator)
1676 {
1677  self->activator = activator;
1678 
1679  // if on, turn it off
1680  if (self->nextthink) {
1681  self->nextthink = 0;
1682  return;
1683  }
1684 
1685  // turn it on
1686  if (self->delay)
1687  self->nextthink = level.time + self->delay;
1688  else
1689  func_timer_think(self);
1690 }
1691 
1692 void SP_func_timer(edict_t *self)
1693 {
1694  if (!self->wait)
1695  self->wait = 1.0;
1696 
1697  self->use = func_timer_use;
1698  self->think = func_timer_think;
1699 
1700  if (self->random >= self->wait) {
1701  self->random = self->wait - FRAMETIME;
1702  gi.dprintf("func_timer at %s has random >= wait\n", vtos(self->s.origin));
1703  }
1704 
1705  if (self->spawnflags & 1) {
1706  self->nextthink = level.time + 1.0 + st.pausetime + self->delay + self->wait + crandom() * self->random;
1707  self->activator = self;
1708  }
1709 
1710  self->svflags = SVF_NOCLIENT;
1711 }
1712 
1713 
1714 /*QUAKED func_conveyor (0 .5 .8) ? START_ON TOGGLE
1715 Conveyors are stationary brushes that move what's on them.
1716 The brush should be have a surface with at least one current content enabled.
1717 speed default 100
1718 */
1719 
1720 void func_conveyor_use(edict_t *self, edict_t *other, edict_t *activator)
1721 {
1722  if (self->spawnflags & 1) {
1723  self->speed = 0;
1724  self->spawnflags &= ~1;
1725  } else {
1726  self->speed = self->count;
1727  self->spawnflags |= 1;
1728  }
1729 
1730  if (!(self->spawnflags & 2))
1731  self->count = 0;
1732 }
1733 
1734 void SP_func_conveyor(edict_t *self)
1735 {
1736  if (!self->speed)
1737  self->speed = 100;
1738 
1739  if (!(self->spawnflags & 1)) {
1740  self->count = self->speed;
1741  self->speed = 0;
1742  }
1743 
1744  self->use = func_conveyor_use;
1745 
1746  gi.setmodel(self, self->model);
1747  self->solid = SOLID_BSP;
1748  gi.linkentity(self);
1749 }
1750 
1751 
1752 /*QUAKED func_door_secret (0 .5 .8) ? always_shoot 1st_left 1st_down
1753 A secret door. Slide back and then to the side.
1754 
1755 open_once doors never closes
1756 1st_left 1st move is left of arrow
1757 1st_down 1st move is down from arrow
1758 always_shoot door is shootebale even if targeted
1759 
1760 "angle" determines the direction
1761 "dmg" damage to inflic when blocked (default 2)
1762 "wait" how long to hold in the open position (default 5, -1 means hold)
1763 */
1764 
1765 #define SECRET_ALWAYS_SHOOT 1
1766 #define SECRET_1ST_LEFT 2
1767 #define SECRET_1ST_DOWN 4
1768 
1769 void door_secret_move1(edict_t *self);
1770 void door_secret_move2(edict_t *self);
1771 void door_secret_move3(edict_t *self);
1772 void door_secret_move4(edict_t *self);
1773 void door_secret_move5(edict_t *self);
1774 void door_secret_move6(edict_t *self);
1775 void door_secret_done(edict_t *self);
1776 
1777 void door_secret_use(edict_t *self, edict_t *other, edict_t *activator)
1778 {
1779  // make sure we're not already moving
1780  if (!VectorCompare(self->s.origin, vec3_origin))
1781  return;
1782 
1783  Move_Calc(self, self->pos1, door_secret_move1);
1784  door_use_areaportals(self, qtrue);
1785 }
1786 
1787 void door_secret_move1(edict_t *self)
1788 {
1789  self->nextthink = level.time + 1.0;
1790  self->think = door_secret_move2;
1791 }
1792 
1793 void door_secret_move2(edict_t *self)
1794 {
1795  Move_Calc(self, self->pos2, door_secret_move3);
1796 }
1797 
1798 void door_secret_move3(edict_t *self)
1799 {
1800  if (self->wait == -1)
1801  return;
1802  self->nextthink = level.time + self->wait;
1803  self->think = door_secret_move4;
1804 }
1805 
1806 void door_secret_move4(edict_t *self)
1807 {
1808  Move_Calc(self, self->pos1, door_secret_move5);
1809 }
1810 
1811 void door_secret_move5(edict_t *self)
1812 {
1813  self->nextthink = level.time + 1.0;
1814  self->think = door_secret_move6;
1815 }
1816 
1817 void door_secret_move6(edict_t *self)
1818 {
1820 }
1821 
1822 void door_secret_done(edict_t *self)
1823 {
1824  if (!(self->targetname) || (self->spawnflags & SECRET_ALWAYS_SHOOT)) {
1825  self->health = 0;
1826  self->takedamage = DAMAGE_YES;
1827  }
1828  door_use_areaportals(self, qfalse);
1829 }
1830 
1831 void door_secret_blocked(edict_t *self, edict_t *other)
1832 {
1833  if (!(other->svflags & SVF_MONSTER) && (!other->client)) {
1834  // give it a chance to go away on it's own terms (like gibs)
1835  T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH);
1836  // if it's still there, nuke it
1837  if (other)
1839  return;
1840  }
1841 
1842  if (level.time < self->touch_debounce_time)
1843  return;
1844  self->touch_debounce_time = level.time + 0.5;
1845 
1846  T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
1847 }
1848 
1849 void door_secret_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1850 {
1851  self->takedamage = DAMAGE_NO;
1852  door_secret_use(self, attacker, attacker);
1853 }
1854 
1855 void SP_func_door_secret(edict_t *ent)
1856 {
1857  vec3_t forward, right, up;
1858  float side;
1859  float width;
1860  float length;
1861 
1862  ent->moveinfo.sound_start = gi.soundindex("doors/dr1_strt.wav");
1863  ent->moveinfo.sound_middle = gi.soundindex("doors/dr1_mid.wav");
1864  ent->moveinfo.sound_end = gi.soundindex("doors/dr1_end.wav");
1865 
1866  ent->movetype = MOVETYPE_PUSH;
1867  ent->solid = SOLID_BSP;
1868  gi.setmodel(ent, ent->model);
1869 
1870  ent->blocked = door_secret_blocked;
1871  ent->use = door_secret_use;
1872 
1873  if (!(ent->targetname) || (ent->spawnflags & SECRET_ALWAYS_SHOOT)) {
1874  ent->health = 0;
1875  ent->takedamage = DAMAGE_YES;
1876  ent->die = door_secret_die;
1877  }
1878 
1879  if (!ent->dmg)
1880  ent->dmg = 2;
1881 
1882  if (!ent->wait)
1883  ent->wait = 5;
1884 
1885  ent->moveinfo.accel =
1886  ent->moveinfo.decel =
1887  ent->moveinfo.speed = 50;
1888 
1889  // calculate positions
1890  AngleVectors(ent->s.angles, forward, right, up);
1891  VectorClear(ent->s.angles);
1892  side = 1.0 - (ent->spawnflags & SECRET_1ST_LEFT);
1893  if (ent->spawnflags & SECRET_1ST_DOWN)
1894  width = fabs(DotProduct(up, ent->size));
1895  else
1896  width = fabs(DotProduct(right, ent->size));
1897  length = fabs(DotProduct(forward, ent->size));
1898  if (ent->spawnflags & SECRET_1ST_DOWN)
1899  VectorMA(ent->s.origin, -1 * width, up, ent->pos1);
1900  else
1901  VectorMA(ent->s.origin, side * width, right, ent->pos1);
1902  VectorMA(ent->pos1, length, forward, ent->pos2);
1903 
1904  if (ent->health) {
1905  ent->takedamage = DAMAGE_YES;
1906  ent->die = door_killed;
1907  ent->max_health = ent->health;
1908  } else if (ent->targetname && ent->message) {
1909  gi.soundindex("misc/talk.wav");
1910  ent->touch = door_touch;
1911  }
1912 
1913  ent->classname = "func_door";
1914 
1915  gi.linkentity(ent);
1916 }
1917 
1918 
1919 /*QUAKED func_killbox (1 0 0) ?
1920 Kills everything inside when fired, irrespective of protection.
1921 */
1922 void use_killbox(edict_t *self, edict_t *other, edict_t *activator)
1923 {
1924  KillBox(self);
1925 }
1926 
1927 void SP_func_killbox(edict_t *ent)
1928 {
1929  gi.setmodel(ent, ent->model);
1930  ent->use = use_killbox;
1931  ent->svflags = SVF_NOCLIENT;
1932 }
1933 
gi
game_import_t gi
Definition: g_main.c:23
moveinfo_t::decel
float decel
Definition: g_local.h:377
train_use
void train_use(edict_t *self, edict_t *other, edict_t *activator)
Definition: g_func.c:1544
SP_func_train
void SP_func_train(edict_t *self)
Definition: g_func.c:1562
func_train_find
void func_train_find(edict_t *self)
Definition: g_func.c:1515
deathmatch
cvar_t * deathmatch
Definition: g_main.c:33
Think_AccelMove
void Think_AccelMove(edict_t *ent)
Definition: g_func.c:312
button_use
void button_use(edict_t *self, edict_t *other, edict_t *activator)
Definition: g_func.c:700
DOOR_Y_AXIS
#define DOOR_Y_AXIS
Definition: g_func.c:67
door_go_up
void door_go_up(edict_t *self, edict_t *activator)
Definition: g_func.c:875
G_Spawn
edict_t * G_Spawn(void)
Definition: g_utils.c:391
door_secret_move3
void door_secret_move3(edict_t *self)
Definition: g_func.c:1798
func_timer_think
void func_timer_think(edict_t *self)
Definition: g_func.c:1669
func_conveyor_use
void func_conveyor_use(edict_t *self, edict_t *other, edict_t *activator)
Definition: g_func.c:1720
plat_go_down
void plat_go_down(edict_t *ent)
Definition: g_func.c:358
DOOR_NOMONSTER
#define DOOR_NOMONSTER
Definition: g_func.c:64
button_killed
void button_killed(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
Definition: g_func.c:718
Use_Plat
void Use_Plat(edict_t *ent, edict_t *other, edict_t *activator)
Definition: g_func.c:400
FRAMETIME
#define FRAMETIME
Definition: g_local.h:75
door_secret_die
void door_secret_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
Definition: g_func.c:1849
SP_func_timer
void SP_func_timer(edict_t *self)
Definition: g_func.c:1692
door_secret_move2
void door_secret_move2(edict_t *self)
Definition: g_func.c:1793
plat_Accelerate
void plat_Accelerate(moveinfo_t *moveinfo)
Definition: g_func.c:246
st
spawn_temp_t st
Definition: g_main.c:25
MOVETYPE_STOP
@ MOVETYPE_STOP
Definition: g_local.h:189
plat_blocked
void plat_blocked(edict_t *self, edict_t *other)
Definition: g_func.c:380
plat_CalcAcceleratedMove
void plat_CalcAcceleratedMove(moveinfo_t *moveinfo)
Definition: g_func.c:220
SP_func_rotating
void SP_func_rotating(edict_t *ent)
Definition: g_func.c:587
STATE_TOP
#define STATE_TOP
Definition: g_func.c:56
rotating_touch
void rotating_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
Definition: g_func.c:567
SP_func_door_rotating
void SP_func_door_rotating(edict_t *ent)
Definition: g_func.c:1196
MOVETYPE_PUSH
@ MOVETYPE_PUSH
Definition: g_local.h:188
G_Find
edict_t * G_Find(edict_t *from, int fieldofs, char *match)
Definition: g_utils.c:43
BecomeExplosion1
void BecomeExplosion1(edict_t *self)
Definition: g_misc.c:297
train_blocked
void train_blocked(edict_t *self, edict_t *other)
Definition: g_func.c:1389
rotating_use
void rotating_use(edict_t *self, edict_t *other, edict_t *activator)
Definition: g_func.c:573
DAMAGE_YES
@ DAMAGE_YES
Definition: g_local.h:88
Move_Calc
void Move_Calc(edict_t *ent, vec3_t dest, void(*func)(edict_t *))
Definition: g_func.c:110
FOFS
#define FOFS(x)
Definition: g_local.h:498
PLAT_LOW_TRIGGER
#define PLAT_LOW_TRIGGER
Definition: g_func.c:54
door_secret_move6
void door_secret_move6(edict_t *self)
Definition: g_func.c:1817
DOOR_X_AXIS
#define DOOR_X_AXIS
Definition: g_func.c:66
door_use
void door_use(edict_t *self, edict_t *other, edict_t *activator)
Definition: g_func.c:902
SP_func_door_secret
void SP_func_door_secret(edict_t *ent)
Definition: g_func.c:1855
T_Damage
void T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, vec3_t point, vec3_t normal, int damage, int knockback, int dflags, int mod)
Definition: g_combat.c:358
other
@ other
Definition: ogg.c:63
moveinfo_t::decel_distance
float decel_distance
Definition: g_local.h:389
button_return
void button_return(edict_t *self)
Definition: g_func.c:663
door_secret_move4
void door_secret_move4(edict_t *self)
Definition: g_func.c:1806
TRAIN_BLOCK_STOPS
#define TRAIN_BLOCK_STOPS
Definition: g_func.c:1375
button_touch
void button_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
Definition: g_func.c:706
train_next
void train_next(edict_t *self)
Definition: g_func.c:1448
train_wait
void train_wait(edict_t *self)
Definition: g_func.c:1409
level_locals_t::current_entity
edict_t * current_entity
Definition: g_local.h:332
button_fire
void button_fire(edict_t *self)
Definition: g_func.c:689
SECRET_1ST_LEFT
#define SECRET_1ST_LEFT
Definition: g_func.c:1766
vec3_origin
vec3_t vec3_origin
Definition: shared.c:21
STATE_UP
#define STATE_UP
Definition: g_func.c:58
width
static int width
Definition: physical_sky.c:38
button_done
void button_done(edict_t *self)
Definition: g_func.c:656
plat_hit_bottom
void plat_hit_bottom(edict_t *ent)
Definition: g_func.c:348
spawn_temp_t::height
int height
Definition: g_local.h:351
door_secret_done
void door_secret_done(edict_t *self)
Definition: g_func.c:1822
DOOR_TOGGLE
#define DOOR_TOGGLE
Definition: g_func.c:65
button_wait
void button_wait(edict_t *self)
Definition: g_func.c:675
TRAIN_START_ON
#define TRAIN_START_ON
Definition: g_func.c:1373
MOVETYPE_NONE
@ MOVETYPE_NONE
Definition: g_local.h:186
SP_func_water
void SP_func_water(edict_t *self)
Definition: g_func.c:1308
trigger_elevator_use
void trigger_elevator_use(edict_t *self, edict_t *other, edict_t *activator)
Definition: g_func.c:1603
AngleMove_Calc
void AngleMove_Calc(edict_t *ent, void(*func)(edict_t *))
Definition: g_func.c:197
moveinfo_t::remaining_distance
float remaining_distance
Definition: g_local.h:388
door_use_areaportals
void door_use_areaportals(edict_t *self, qboolean open)
Definition: g_func.c:813
use_killbox
void use_killbox(edict_t *self, edict_t *other, edict_t *activator)
Definition: g_func.c:1922
rotating_blocked
void rotating_blocked(edict_t *self, edict_t *other)
Definition: g_func.c:562
forward
static vec3_t forward
Definition: p_view.c:27
crandom
#define crandom()
Definition: g_local.h:505
spawn_temp_t::lip
int lip
Definition: g_local.h:349
moveinfo_t::move_speed
float move_speed
Definition: g_local.h:386
door_go_down
void door_go_down(edict_t *self)
Definition: g_func.c:856
vtos
char * vtos(vec3_t v)
Definition: g_utils.c:275
STATE_DOWN
#define STATE_DOWN
Definition: g_func.c:59
door_secret_move5
void door_secret_move5(edict_t *self)
Definition: g_func.c:1811
AngleVectors
void AngleVectors(vec3_t angles, vec3_t forward, vec3_t right, vec3_t up)
Definition: shared.c:23
DAMAGE_NO
@ DAMAGE_NO
Definition: g_local.h:87
SP_func_door
void SP_func_door(edict_t *ent)
Definition: g_func.c:1078
Touch_DoorTrigger
void Touch_DoorTrigger(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
Definition: g_func.c:929
DOOR_CRUSHER
#define DOOR_CRUSHER
Definition: g_func.c:63
AngleMove_Final
void AngleMove_Final(edict_t *ent)
Definition: g_func.c:143
TRAIN_TOGGLE
#define TRAIN_TOGGLE
Definition: g_func.c:1374
moveinfo_t::current_speed
float current_speed
Definition: g_local.h:385
Move_Begin
void Move_Begin(edict_t *ent)
Definition: g_func.c:93
door_secret_blocked
void door_secret_blocked(edict_t *self, edict_t *other)
Definition: g_func.c:1831
SP_func_button
void SP_func_button(edict_t *ent)
Definition: g_func.c:726
Think_SpawnDoorTrigger
void Think_SpawnDoorTrigger(edict_t *ent)
Definition: g_func.c:985
DOOR_REVERSE
#define DOOR_REVERSE
Definition: g_func.c:62
door_blocked
void door_blocked(edict_t *self, edict_t *other)
Definition: g_func.c:1022
AngleMove_Begin
void AngleMove_Begin(edict_t *ent)
Definition: g_func.c:163
door_killed
void door_killed(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
Definition: g_func.c:1054
door_hit_top
void door_hit_top(edict_t *self)
Definition: g_func.c:829
SP_trigger_elevator
void SP_trigger_elevator(edict_t *self)
Definition: g_func.c:1648
MOD_CRUSH
#define MOD_CRUSH
Definition: g_local.h:477
DOOR_START_OPEN
#define DOOR_START_OPEN
Definition: g_func.c:61
STATE_BOTTOM
#define STATE_BOTTOM
Definition: g_func.c:57
door_secret_use
void door_secret_use(edict_t *self, edict_t *other, edict_t *activator)
Definition: g_func.c:1777
moveinfo_t::next_speed
float next_speed
Definition: g_local.h:387
level_locals_t::time
float time
Definition: g_local.h:299
plat_go_up
void plat_go_up(edict_t *ent)
Definition: g_func.c:369
SECRET_ALWAYS_SHOOT
#define SECRET_ALWAYS_SHOOT
Definition: g_func.c:1765
moveinfo_t
Definition: g_local.h:364
AccelerationDistance
#define AccelerationDistance(target, rate)
Definition: g_func.c:218
Move_Final
void Move_Final(edict_t *ent)
Definition: g_func.c:80
SP_func_conveyor
void SP_func_conveyor(edict_t *self)
Definition: g_func.c:1734
up
static vec3_t up
Definition: p_view.c:27
Think_CalcMoveSpeed
void Think_CalcMoveSpeed(edict_t *self)
Definition: g_func.c:947
right
static vec3_t right
Definition: p_view.c:27
G_UseTargets
void G_UseTargets(edict_t *ent, edict_t *activator)
Definition: g_utils.c:166
SP_func_killbox
void SP_func_killbox(edict_t *ent)
Definition: g_func.c:1927
FL_TEAMSLAVE
#define FL_TEAMSLAVE
Definition: g_local.h:69
level
level_locals_t level
Definition: g_main.c:22
SECRET_1ST_DOWN
#define SECRET_1ST_DOWN
Definition: g_func.c:1767
spawn_temp_t::distance
int distance
Definition: g_local.h:350
moveinfo_t::accel
float accel
Definition: g_local.h:375
door_touch
void door_touch(edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
Definition: g_func.c:1065
G_SetMovedir
void G_SetMovedir(vec3_t angles, vec3_t movedir)
Definition: g_utils.c:296
KillBox
qboolean KillBox(edict_t *ent)
Definition: g_utils.c:515
AngleMove_Done
void AngleMove_Done(edict_t *ent)
Definition: g_func.c:137
func_timer_use
void func_timer_use(edict_t *self, edict_t *other, edict_t *activator)
Definition: g_func.c:1675
SP_func_plat
void SP_func_plat(edict_t *ent)
Definition: g_func.c:483
plat_spawn_inside_trigger
void plat_spawn_inside_trigger(edict_t *ent)
Definition: g_func.c:423
Touch_Plat_Center
void Touch_Plat_Center(edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
Definition: g_func.c:408
train_resume
void train_resume(edict_t *self)
Definition: g_func.c:1500
spawn_temp_t::noise
char * noise
Definition: g_local.h:352
G_PickTarget
edict_t * G_PickTarget(char *targetname)
Definition: g_utils.c:114
spawn_temp_t::pausetime
float pausetime
Definition: g_local.h:353
VectorNormalize
vec_t VectorNormalize(vec3_t v)
Definition: shared.c:55
AddPointToBounds
void AddPointToBounds(const vec3_t v, vec3_t mins, vec3_t maxs)
Definition: shared.c:97
door_secret_move1
void door_secret_move1(edict_t *self)
Definition: g_func.c:1787
door_hit_bottom
void door_hit_bottom(edict_t *self)
Definition: g_func.c:845
trigger_elevator_init
void trigger_elevator_init(edict_t *self)
Definition: g_func.c:1627
Move_Done
void Move_Done(edict_t *ent)
Definition: g_func.c:74
moveinfo_t::speed
float speed
Definition: g_local.h:376
plat_hit_top
void plat_hit_top(edict_t *ent)
Definition: g_func.c:335
g_local.h