1 #include "g_local.h"
2
3 /*
4 =========================================================
5
6 PLATS
7
8 movement options:
9
10 linear
11 smooth start, hard stop
12 smooth start, smooth stop
13
14 start
15 end
16 acceleration
17 speed
18 deceleration
19 begin sound
20 end sound
21 target fired when reaching end
22 wait at end
23
24 object characteristics that use move segments
25 ---------------------------------------------
26 movetype_push, or movetype_stop
27 action when touched
28 action when blocked
29 action when used
30 disabled?
31 auto trigger spawning
32
33
34 =========================================================
35 */
36
37 #define PLAT_LOW_TRIGGER 1
38
39 //====
40 //PGM
41 #define PLAT2_TOGGLE 2
42 #define PLAT2_TOP 4
43 #define PLAT2_TRIGGER_TOP 8
44 #define PLAT2_TRIGGER_BOTTOM 16
45 #define PLAT2_BOX_LIFT 32
46
47 void plat2_spawn_danger_area (edict_t *ent);
48 void plat2_kill_danger_area (edict_t *ent);
49
50 //PGM
51 //====
52
53 #define STATE_TOP 0
54 #define STATE_BOTTOM 1
55 #define STATE_UP 2
56 #define STATE_DOWN 3
57
58 #define DOOR_START_OPEN 1
59 #define DOOR_REVERSE 2
60 #define DOOR_CRUSHER 4
61 #define DOOR_NOMONSTER 8
62 #define DOOR_TOGGLE 32
63 #define DOOR_X_AXIS 64
64 #define DOOR_Y_AXIS 128
65 // !easy 256
66 // !med 512
67 // !hard 1024
68 // !dm 2048
69 // !coop 4096
70 #define DOOR_INACTIVE 8192
71
72 //
73 // Support routines for movement (changes in origin using velocity)
74 //
75
Move_Done(edict_t * ent)76 void Move_Done (edict_t *ent)
77 {
78 VectorClear (ent->velocity);
79 ent->moveinfo.endfunc (ent);
80 }
81
Move_Final(edict_t * ent)82 void Move_Final (edict_t *ent)
83 {
84 if (ent->moveinfo.remaining_distance == 0)
85 {
86 Move_Done (ent);
87 return;
88 }
89
90 VectorScale (ent->moveinfo.dir, ent->moveinfo.remaining_distance / FRAMETIME, ent->velocity);
91
92 ent->think = Move_Done;
93 ent->nextthink = level.time + FRAMETIME;
94 }
95
Move_Begin(edict_t * ent)96 void Move_Begin (edict_t *ent)
97 {
98 float frames;
99
100 if ((ent->moveinfo.speed * FRAMETIME) >= ent->moveinfo.remaining_distance)
101 {
102 Move_Final (ent);
103 return;
104 }
105 VectorScale (ent->moveinfo.dir, ent->moveinfo.speed, ent->velocity);
106 frames = floor((ent->moveinfo.remaining_distance / ent->moveinfo.speed) / FRAMETIME);
107 ent->moveinfo.remaining_distance -= frames * ent->moveinfo.speed * FRAMETIME;
108 ent->nextthink = level.time + (frames * FRAMETIME);
109 ent->think = Move_Final;
110 }
111
112 void Think_AccelMove (edict_t *ent);
113
Move_Calc(edict_t * ent,vec3_t dest,void (* func)(edict_t *))114 void Move_Calc (edict_t *ent, vec3_t dest, void(*func)(edict_t*))
115 {
116 VectorClear (ent->velocity);
117 VectorSubtract (dest, ent->s.origin, ent->moveinfo.dir);
118 ent->moveinfo.remaining_distance = VectorNormalize (ent->moveinfo.dir);
119 ent->moveinfo.endfunc = func;
120
121 if (ent->moveinfo.speed == ent->moveinfo.accel && ent->moveinfo.speed == ent->moveinfo.decel)
122 {
123 if (level.current_entity == ((ent->flags & FL_TEAMSLAVE) ? ent->teammaster : ent))
124 {
125 Move_Begin (ent);
126 }
127 else
128 {
129 ent->nextthink = level.time + FRAMETIME;
130 ent->think = Move_Begin;
131 }
132 }
133 else
134 {
135 // accelerative
136 ent->moveinfo.current_speed = 0;
137 ent->think = Think_AccelMove;
138 ent->nextthink = level.time + FRAMETIME;
139 }
140 }
141
142
143 //
144 // Support routines for angular movement (changes in angle using avelocity)
145 //
146
AngleMove_Done(edict_t * ent)147 void AngleMove_Done (edict_t *ent)
148 {
149 VectorClear (ent->avelocity);
150 ent->moveinfo.endfunc (ent);
151 }
152
AngleMove_Final(edict_t * ent)153 void AngleMove_Final (edict_t *ent)
154 {
155 vec3_t move;
156
157 if (ent->moveinfo.state == STATE_UP)
158 VectorSubtract (ent->moveinfo.end_angles, ent->s.angles, move);
159 else
160 VectorSubtract (ent->moveinfo.start_angles, ent->s.angles, move);
161
162 if (VectorCompare (move, vec3_origin))
163 {
164 AngleMove_Done (ent);
165 return;
166 }
167
168 VectorScale (move, 1.0/FRAMETIME, ent->avelocity);
169
170 ent->think = AngleMove_Done;
171 ent->nextthink = level.time + FRAMETIME;
172 }
173
AngleMove_Begin(edict_t * ent)174 void AngleMove_Begin (edict_t *ent)
175 {
176 vec3_t destdelta;
177 float len;
178 float traveltime;
179 float frames;
180
181 //PGM accelerate as needed
182 if(ent->moveinfo.speed < ent->speed)
183 {
184 ent->moveinfo.speed += ent->accel;
185 if(ent->moveinfo.speed > ent->speed)
186 ent->moveinfo.speed = ent->speed;
187 }
188 //PGM
189
190 // set destdelta to the vector needed to move
191 if (ent->moveinfo.state == STATE_UP)
192 VectorSubtract (ent->moveinfo.end_angles, ent->s.angles, destdelta);
193 else
194 VectorSubtract (ent->moveinfo.start_angles, ent->s.angles, destdelta);
195
196 // calculate length of vector
197 len = VectorLength (destdelta);
198
199 // divide by speed to get time to reach dest
200 traveltime = len / ent->moveinfo.speed;
201
202 if (traveltime < FRAMETIME)
203 {
204 AngleMove_Final (ent);
205 return;
206 }
207
208 frames = floor(traveltime / FRAMETIME);
209
210 // scale the destdelta vector by the time spent traveling to get velocity
211 VectorScale (destdelta, 1.0 / traveltime, ent->avelocity);
212
213 //PGM
214 // if we're done accelerating, act as a normal rotation
215 if(ent->moveinfo.speed >= ent->speed)
216 {
217 // set nextthink to trigger a think when dest is reached
218 ent->nextthink = level.time + frames * FRAMETIME;
219 ent->think = AngleMove_Final;
220 }
221 else
222 {
223 ent->nextthink = level.time + FRAMETIME;
224 ent->think = AngleMove_Begin;
225 }
226 //PGM
227 }
228
AngleMove_Calc(edict_t * ent,void (* func)(edict_t *))229 void AngleMove_Calc (edict_t *ent, void(*func)(edict_t*))
230 {
231 VectorClear (ent->avelocity);
232 ent->moveinfo.endfunc = func;
233
234 //PGM
235 // if we're supposed to accelerate, this will tell anglemove_begin to do so
236 if(ent->accel != ent->speed)
237 ent->moveinfo.speed = 0;
238 //PGM
239
240 if (level.current_entity == ((ent->flags & FL_TEAMSLAVE) ? ent->teammaster : ent))
241 {
242 AngleMove_Begin (ent);
243 }
244 else
245 {
246 ent->nextthink = level.time + FRAMETIME;
247 ent->think = AngleMove_Begin;
248 }
249 }
250
251
252 /*
253 ==============
254 Think_AccelMove
255
256 The team has completed a frame of movement, so
257 change the speed for the next frame
258 ==============
259 */
260 #define AccelerationDistance(target, rate) (target * ((target / rate) + 1) / 2)
261
plat_CalcAcceleratedMove(moveinfo_t * moveinfo)262 void plat_CalcAcceleratedMove(moveinfo_t *moveinfo)
263 {
264 float accel_dist;
265 float decel_dist;
266
267 moveinfo->move_speed = moveinfo->speed;
268
269 if (moveinfo->remaining_distance < moveinfo->accel)
270 {
271 moveinfo->current_speed = moveinfo->remaining_distance;
272 return;
273 }
274
275 accel_dist = AccelerationDistance (moveinfo->speed, moveinfo->accel);
276 decel_dist = AccelerationDistance (moveinfo->speed, moveinfo->decel);
277
278 if ((moveinfo->remaining_distance - accel_dist - decel_dist) < 0)
279 {
280 float f;
281
282 f = (moveinfo->accel + moveinfo->decel) / (moveinfo->accel * moveinfo->decel);
283 moveinfo->move_speed = (-2 + sqrt(4 - 4 * f * (-2 * moveinfo->remaining_distance))) / (2 * f);
284 decel_dist = AccelerationDistance (moveinfo->move_speed, moveinfo->decel);
285 }
286
287 moveinfo->decel_distance = decel_dist;
288 };
289
plat_Accelerate(moveinfo_t * moveinfo)290 void plat_Accelerate (moveinfo_t *moveinfo)
291 {
292 // are we decelerating?
293 if (moveinfo->remaining_distance <= moveinfo->decel_distance)
294 {
295 if (moveinfo->remaining_distance < moveinfo->decel_distance)
296 {
297 if (moveinfo->next_speed)
298 {
299 moveinfo->current_speed = moveinfo->next_speed;
300 moveinfo->next_speed = 0;
301 return;
302 }
303 if (moveinfo->current_speed > moveinfo->decel)
304 moveinfo->current_speed -= moveinfo->decel;
305 }
306 return;
307 }
308
309 // are we at full speed and need to start decelerating during this move?
310 if (moveinfo->current_speed == moveinfo->move_speed)
311 if ((moveinfo->remaining_distance - moveinfo->current_speed) < moveinfo->decel_distance)
312 {
313 float p1_distance;
314 float p2_distance;
315 float distance;
316
317 p1_distance = moveinfo->remaining_distance - moveinfo->decel_distance;
318 p2_distance = moveinfo->move_speed * (1.0 - (p1_distance / moveinfo->move_speed));
319 distance = p1_distance + p2_distance;
320 moveinfo->current_speed = moveinfo->move_speed;
321 moveinfo->next_speed = moveinfo->move_speed - moveinfo->decel * (p2_distance / distance);
322 return;
323 }
324
325 // are we accelerating?
326 if (moveinfo->current_speed < moveinfo->speed)
327 {
328 float old_speed;
329 float p1_distance;
330 float p1_speed;
331 float p2_distance;
332 float distance;
333
334 old_speed = moveinfo->current_speed;
335
336 // figure simple acceleration up to move_speed
337 moveinfo->current_speed += moveinfo->accel;
338 if (moveinfo->current_speed > moveinfo->speed)
339 moveinfo->current_speed = moveinfo->speed;
340
341 // are we accelerating throughout this entire move?
342 if ((moveinfo->remaining_distance - moveinfo->current_speed) >= moveinfo->decel_distance)
343 return;
344
345 // during this move we will accelrate from current_speed to move_speed
346 // and cross over the decel_distance; figure the average speed for the
347 // entire move
348 p1_distance = moveinfo->remaining_distance - moveinfo->decel_distance;
349 p1_speed = (old_speed + moveinfo->move_speed) / 2.0;
350 p2_distance = moveinfo->move_speed * (1.0 - (p1_distance / p1_speed));
351 distance = p1_distance + p2_distance;
352 moveinfo->current_speed = (p1_speed * (p1_distance / distance)) + (moveinfo->move_speed * (p2_distance / distance));
353 moveinfo->next_speed = moveinfo->move_speed - moveinfo->decel * (p2_distance / distance);
354 return;
355 }
356
357 // we are at constant velocity (move_speed)
358 return;
359 };
360
Think_AccelMove(edict_t * ent)361 void Think_AccelMove (edict_t *ent)
362 {
363 ent->moveinfo.remaining_distance -= ent->moveinfo.current_speed;
364
365 // PGM 04/21/98 - this should fix sthoms' sinking drop pod. Hopefully it wont break stuff.
366 // if (ent->moveinfo.current_speed == 0) // starting or blocked
367 plat_CalcAcceleratedMove(&ent->moveinfo);
368
369 plat_Accelerate (&ent->moveinfo);
370
371 // will the entire move complete on next frame?
372 if (ent->moveinfo.remaining_distance <= ent->moveinfo.current_speed)
373 {
374 Move_Final (ent);
375 return;
376 }
377
378 VectorScale (ent->moveinfo.dir, ent->moveinfo.current_speed*10, ent->velocity);
379 ent->nextthink = level.time + FRAMETIME;
380 ent->think = Think_AccelMove;
381 }
382
383
384 void plat_go_down (edict_t *ent);
385
plat_hit_top(edict_t * ent)386 void plat_hit_top (edict_t *ent)
387 {
388 if (!(ent->flags & FL_TEAMSLAVE))
389 {
390 if (ent->moveinfo.sound_end)
391 gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, ent->moveinfo.sound_end, 1, ATTN_STATIC, 0);
392 ent->s.sound = 0;
393 }
394 ent->moveinfo.state = STATE_TOP;
395
396 ent->think = plat_go_down;
397 ent->nextthink = level.time + 3;
398 }
399
plat_hit_bottom(edict_t * ent)400 void plat_hit_bottom (edict_t *ent)
401 {
402 if (!(ent->flags & FL_TEAMSLAVE))
403 {
404 if (ent->moveinfo.sound_end)
405 gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, ent->moveinfo.sound_end, 1, ATTN_STATIC, 0);
406 ent->s.sound = 0;
407 }
408 ent->moveinfo.state = STATE_BOTTOM;
409
410 plat2_kill_danger_area (ent); // PGM
411 }
412
plat_go_down(edict_t * ent)413 void plat_go_down (edict_t *ent)
414 {
415 if (!(ent->flags & FL_TEAMSLAVE))
416 {
417 if (ent->moveinfo.sound_start)
418 gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, ent->moveinfo.sound_start, 1, ATTN_STATIC, 0);
419 ent->s.sound = ent->moveinfo.sound_middle;
420 }
421 ent->moveinfo.state = STATE_DOWN;
422 Move_Calc (ent, ent->moveinfo.end_origin, plat_hit_bottom);
423 }
424
plat_go_up(edict_t * ent)425 void plat_go_up (edict_t *ent)
426 {
427 if (!(ent->flags & FL_TEAMSLAVE))
428 {
429 if (ent->moveinfo.sound_start)
430 gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, ent->moveinfo.sound_start, 1, ATTN_STATIC, 0);
431 ent->s.sound = ent->moveinfo.sound_middle;
432 }
433 ent->moveinfo.state = STATE_UP;
434 Move_Calc (ent, ent->moveinfo.start_origin, plat_hit_top);
435
436 plat2_spawn_danger_area(ent); // PGM
437 }
438
plat_blocked(edict_t * self,edict_t * other)439 void plat_blocked (edict_t *self, edict_t *other)
440 {
441 if (!(other->svflags & SVF_MONSTER) && (!other->client) )
442 {
443 // give it a chance to go away on it's own terms (like gibs)
444 T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH);
445 // if it's still there, nuke it
446 if (other && other->inuse) // PGM
447 BecomeExplosion1 (other);
448 return;
449 }
450
451 //PGM
452 // gib dead things
453 if(other->health < 1)
454 {
455 T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, 100, 1, 0, MOD_CRUSH);
456 }
457 //PGM
458
459 T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
460
461 if (self->moveinfo.state == STATE_UP)
462 plat_go_down (self);
463 else if (self->moveinfo.state == STATE_DOWN)
464 plat_go_up (self);
465 }
466
467
Use_Plat(edict_t * ent,edict_t * other,edict_t * activator)468 void Use_Plat (edict_t *ent, edict_t *other, edict_t *activator)
469 {
470 //======
471 //ROGUE
472 // if a monster is using us, then allow the activity when stopped.
473 if (other->svflags & SVF_MONSTER)
474 {
475 if (ent->moveinfo.state == STATE_TOP)
476 plat_go_down (ent);
477 else if (ent->moveinfo.state == STATE_BOTTOM)
478 plat_go_up (ent);
479
480 return;
481 }
482 //ROGUE
483 //======
484
485 if (ent->think)
486 return; // already down
487 plat_go_down (ent);
488 }
489
490
Touch_Plat_Center(edict_t * ent,edict_t * other,cplane_t * plane,csurface_t * surf)491 void Touch_Plat_Center (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
492 {
493 if (!other->client)
494 return;
495
496 if (other->health <= 0)
497 return;
498
499 ent = ent->enemy; // now point at the plat, not the trigger
500 if (ent->moveinfo.state == STATE_BOTTOM)
501 plat_go_up (ent);
502 else if (ent->moveinfo.state == STATE_TOP)
503 ent->nextthink = level.time + 1; // the player is still on the plat, so delay going down
504 }
505
506 // PGM - plat2's change the trigger field
507 //void plat_spawn_inside_trigger (edict_t *ent)
plat_spawn_inside_trigger(edict_t * ent)508 edict_t *plat_spawn_inside_trigger (edict_t *ent)
509 {
510 edict_t *trigger;
511 vec3_t tmin, tmax;
512
513 //
514 // middle trigger
515 //
516 trigger = G_Spawn();
517 trigger->touch = Touch_Plat_Center;
518 trigger->movetype = MOVETYPE_NONE;
519 trigger->solid = SOLID_TRIGGER;
520 trigger->enemy = ent;
521
522 tmin[0] = ent->mins[0] + 25;
523 tmin[1] = ent->mins[1] + 25;
524 tmin[2] = ent->mins[2];
525
526 tmax[0] = ent->maxs[0] - 25;
527 tmax[1] = ent->maxs[1] - 25;
528 tmax[2] = ent->maxs[2] + 8;
529
530 tmin[2] = tmax[2] - (ent->pos1[2] - ent->pos2[2] + st.lip);
531
532 if (ent->spawnflags & PLAT_LOW_TRIGGER)
533 tmax[2] = tmin[2] + 8;
534
535 if (tmax[0] - tmin[0] <= 0)
536 {
537 tmin[0] = (ent->mins[0] + ent->maxs[0]) *0.5;
538 tmax[0] = tmin[0] + 1;
539 }
540 if (tmax[1] - tmin[1] <= 0)
541 {
542 tmin[1] = (ent->mins[1] + ent->maxs[1]) *0.5;
543 tmax[1] = tmin[1] + 1;
544 }
545
546 VectorCopy (tmin, trigger->mins);
547 VectorCopy (tmax, trigger->maxs);
548
549 gi.linkentity (trigger);
550
551 return trigger; // PGM 11/17/97
552 }
553
554
555 /*QUAKED func_plat (0 .5 .8) ? PLAT_LOW_TRIGGER
556 speed default 150
557
558 Plats are always drawn in the extended position, so they will light correctly.
559
560 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.
561
562 "speed" overrides default 200.
563 "accel" overrides default 500
564 "lip" overrides default 8 pixel lip
565
566 If the "height" key is set, that will determine the amount the plat moves, instead of being implicitly determoveinfoned by the model's height.
567
568 Set "sounds" to one of the following:
569 1) base fast
570 2) chain slow
571 */
SP_func_plat(edict_t * ent)572 void SP_func_plat (edict_t *ent)
573 {
574 VectorClear (ent->s.angles);
575 ent->solid = SOLID_BSP;
576 ent->movetype = MOVETYPE_PUSH;
577
578 gi.setmodel (ent, ent->model);
579
580 ent->blocked = plat_blocked;
581
582 if (!ent->speed)
583 ent->speed = 20;
584 else
585 ent->speed *= 0.1;
586
587 if (!ent->accel)
588 ent->accel = 5;
589 else
590 ent->accel *= 0.1;
591
592 if (!ent->decel)
593 ent->decel = 5;
594 else
595 ent->decel *= 0.1;
596
597 if (!ent->dmg)
598 ent->dmg = 2;
599
600 if (!st.lip)
601 st.lip = 8;
602
603 // pos1 is the top position, pos2 is the bottom
604 VectorCopy (ent->s.origin, ent->pos1);
605 VectorCopy (ent->s.origin, ent->pos2);
606 if (st.height)
607 ent->pos2[2] -= st.height;
608 else
609 ent->pos2[2] -= (ent->maxs[2] - ent->mins[2]) - st.lip;
610
611 ent->use = Use_Plat;
612
613 plat_spawn_inside_trigger (ent); // the "start moving" trigger
614
615 if (ent->targetname)
616 {
617 ent->moveinfo.state = STATE_UP;
618 }
619 else
620 {
621 VectorCopy (ent->pos2, ent->s.origin);
622 gi.linkentity (ent);
623 ent->moveinfo.state = STATE_BOTTOM;
624 }
625
626 ent->moveinfo.speed = ent->speed;
627 ent->moveinfo.accel = ent->accel;
628 ent->moveinfo.decel = ent->decel;
629 ent->moveinfo.wait = ent->wait;
630 VectorCopy (ent->pos1, ent->moveinfo.start_origin);
631 VectorCopy (ent->s.angles, ent->moveinfo.start_angles);
632 VectorCopy (ent->pos2, ent->moveinfo.end_origin);
633 VectorCopy (ent->s.angles, ent->moveinfo.end_angles);
634
635 ent->moveinfo.sound_start = gi.soundindex ("plats/pt1_strt.wav");
636 ent->moveinfo.sound_middle = gi.soundindex ("plats/pt1_mid.wav");
637 ent->moveinfo.sound_end = gi.soundindex ("plats/pt1_end.wav");
638 }
639
640 // ==========================================
641 // PLAT 2
642 // ==========================================
643 #define PLAT2_CALLED 1
644 #define PLAT2_MOVING 2
645 #define PLAT2_WAITING 4
646
647 void plat2_go_down (edict_t *ent);
648 void plat2_go_up (edict_t *ent);
649
plat2_spawn_danger_area(edict_t * ent)650 void plat2_spawn_danger_area (edict_t *ent)
651 {
652 vec3_t mins, maxs;
653
654 VectorCopy(ent->mins, mins);
655 VectorCopy(ent->maxs, maxs);
656 maxs[2] = ent->mins[2] + 64;
657
658 SpawnBadArea(mins, maxs, 0, ent);
659 }
660
plat2_kill_danger_area(edict_t * ent)661 void plat2_kill_danger_area (edict_t *ent)
662 {
663 edict_t *t;
664
665 t = NULL;
666 while ((t = G_Find (t, FOFS(classname), "bad_area")))
667 {
668 if(t->owner == ent)
669 G_FreeEdict(t);
670 }
671 }
672
plat2_hit_top(edict_t * ent)673 void plat2_hit_top (edict_t *ent)
674 {
675 if (!(ent->flags & FL_TEAMSLAVE))
676 {
677 if (ent->moveinfo.sound_end)
678 gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, ent->moveinfo.sound_end, 1, ATTN_STATIC, 0);
679 ent->s.sound = 0;
680 }
681 ent->moveinfo.state = STATE_TOP;
682
683 if(ent->plat2flags & PLAT2_CALLED)
684 {
685 ent->plat2flags = PLAT2_WAITING;
686 if(!(ent->spawnflags & PLAT2_TOGGLE))
687 {
688 ent->think = plat2_go_down;
689 ent->nextthink = level.time + 5.0;
690 }
691 if(deathmatch->value)
692 ent->last_move_time = level.time - 1.0;
693 else
694 ent->last_move_time = level.time - 2.0;
695 }
696 else if(!(ent->spawnflags & PLAT2_TOP) && !(ent->spawnflags & PLAT2_TOGGLE))
697 {
698 ent->plat2flags = 0;
699 ent->think = plat2_go_down;
700 ent->nextthink = level.time + 2.0;
701 ent->last_move_time = level.time;
702 }
703 else
704 {
705 ent->plat2flags = 0;
706 ent->last_move_time = level.time;
707 }
708
709 G_UseTargets (ent, ent);
710 }
711
plat2_hit_bottom(edict_t * ent)712 void plat2_hit_bottom (edict_t *ent)
713 {
714 if (!(ent->flags & FL_TEAMSLAVE))
715 {
716 if (ent->moveinfo.sound_end)
717 gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, ent->moveinfo.sound_end, 1, ATTN_STATIC, 0);
718 ent->s.sound = 0;
719 }
720 ent->moveinfo.state = STATE_BOTTOM;
721
722 if(ent->plat2flags & PLAT2_CALLED)
723 {
724 ent->plat2flags = PLAT2_WAITING;
725 if(!(ent->spawnflags & PLAT2_TOGGLE))
726 {
727 ent->think = plat2_go_up;
728 ent->nextthink = level.time + 5.0;
729 }
730 if(deathmatch->value)
731 ent->last_move_time = level.time - 1.0;
732 else
733 ent->last_move_time = level.time - 2.0;
734 }
735 else if ((ent->spawnflags & PLAT2_TOP) && !(ent->spawnflags & PLAT2_TOGGLE))
736 {
737 ent->plat2flags = 0;
738 ent->think = plat2_go_up;
739 ent->nextthink = level.time + 2.0;
740 ent->last_move_time = level.time;
741 }
742 else
743 {
744 ent->plat2flags = 0;
745 ent->last_move_time = level.time;
746 }
747
748 plat2_kill_danger_area (ent);
749 G_UseTargets (ent, ent);
750 }
751
plat2_go_down(edict_t * ent)752 void plat2_go_down (edict_t *ent)
753 {
754 if (!(ent->flags & FL_TEAMSLAVE))
755 {
756 if (ent->moveinfo.sound_start)
757 gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, ent->moveinfo.sound_start, 1, ATTN_STATIC, 0);
758 ent->s.sound = ent->moveinfo.sound_middle;
759 }
760 ent->moveinfo.state = STATE_DOWN;
761 ent->plat2flags |= PLAT2_MOVING;
762
763 Move_Calc (ent, ent->moveinfo.end_origin, plat2_hit_bottom);
764 }
765
plat2_go_up(edict_t * ent)766 void plat2_go_up (edict_t *ent)
767 {
768 if (!(ent->flags & FL_TEAMSLAVE))
769 {
770 if (ent->moveinfo.sound_start)
771 gi.sound (ent, CHAN_NO_PHS_ADD+CHAN_VOICE, ent->moveinfo.sound_start, 1, ATTN_STATIC, 0);
772 ent->s.sound = ent->moveinfo.sound_middle;
773 }
774 ent->moveinfo.state = STATE_UP;
775 ent->plat2flags |= PLAT2_MOVING;
776
777 plat2_spawn_danger_area(ent);
778
779 Move_Calc (ent, ent->moveinfo.start_origin, plat2_hit_top);
780 }
781
plat2_operate(edict_t * ent,edict_t * other)782 void plat2_operate (edict_t *ent, edict_t *other)
783 {
784 int otherState;
785 float pauseTime;
786 float platCenter;
787 edict_t *trigger;
788
789 trigger = ent;
790 ent = ent->enemy; // now point at the plat, not the trigger
791
792 if (ent->plat2flags & PLAT2_MOVING)
793 return;
794
795 if ((ent->last_move_time + 2) > level.time)
796 return;
797
798 platCenter = (trigger->absmin[2] + trigger->absmax[2]) / 2;
799
800 if(ent->moveinfo.state == STATE_TOP)
801 {
802 otherState = STATE_TOP;
803 if(ent->spawnflags & PLAT2_BOX_LIFT)
804 {
805 if(platCenter > other->s.origin[2])
806 otherState = STATE_BOTTOM;
807 }
808 else
809 {
810 if(trigger->absmax[2] > other->s.origin[2])
811 otherState = STATE_BOTTOM;
812 }
813 }
814 else
815 {
816 otherState = STATE_BOTTOM;
817 if(other->s.origin[2] > platCenter)
818 otherState = STATE_TOP;
819 }
820
821 ent->plat2flags = PLAT2_MOVING;
822
823 if(deathmatch->value)
824 pauseTime = 0.3;
825 else
826 pauseTime = 0.5;
827
828 if(ent->moveinfo.state != otherState)
829 {
830 ent->plat2flags |= PLAT2_CALLED;
831 pauseTime = 0.1;
832 }
833
834 ent->last_move_time = level.time;
835
836 if(ent->moveinfo.state == STATE_BOTTOM)
837 {
838 ent->think = plat2_go_up;
839 ent->nextthink = level.time + pauseTime;
840 }
841 else
842 {
843 ent->think = plat2_go_down;
844 ent->nextthink = level.time + pauseTime;
845 }
846 }
847
Touch_Plat_Center2(edict_t * ent,edict_t * other,cplane_t * plane,csurface_t * surf)848 void Touch_Plat_Center2 (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
849 {
850 // this requires monsters to actively trigger plats, not just step on them.
851
852 //FIXME - commented out for E3
853 //if (!other->client)
854 // return;
855
856 if (other->health <= 0)
857 return;
858
859 // PMM - don't let non-monsters activate plat2s
860 if ((!(other->svflags & SVF_MONSTER)) && (!other->client))
861 return;
862
863 plat2_operate(ent, other);
864 }
865
plat2_blocked(edict_t * self,edict_t * other)866 void plat2_blocked (edict_t *self, edict_t *other)
867 {
868 if (!(other->svflags & SVF_MONSTER) && (!other->client))
869 {
870 // give it a chance to go away on it's own terms (like gibs)
871 T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH);
872 // if it's still there, nuke it
873 if(other && other->inuse)
874 BecomeExplosion1 (other);
875 return;
876 }
877
878 // gib dead things
879 if(other->health < 1)
880 {
881 T_Damage(other, self, self, vec3_origin, other->s.origin, vec3_origin, 100, 1, 0, MOD_CRUSH);
882 }
883
884 T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
885
886 if (self->moveinfo.state == STATE_UP)
887 plat2_go_down (self);
888 else if (self->moveinfo.state == STATE_DOWN)
889 plat2_go_up (self);
890 }
891
Use_Plat2(edict_t * ent,edict_t * other,edict_t * activator)892 void Use_Plat2 (edict_t *ent, edict_t *other, edict_t *activator)
893 {
894 edict_t *trigger;
895 int i;
896
897 if(ent->moveinfo.state > STATE_BOTTOM)
898 return;
899 if((ent->last_move_time + 2) > level.time)
900 return;
901
902 for (i = 1, trigger = g_edicts + 1; i < globals.num_edicts; i++, trigger++)
903 {
904 if (!trigger->inuse)
905 continue;
906 if (trigger->touch == Touch_Plat_Center2)
907 {
908 if (trigger->enemy == ent)
909 {
910 // Touch_Plat_Center2 (trigger, activator, NULL, NULL);
911 plat2_operate (trigger, activator);
912 return;
913 }
914 }
915 }
916 }
917
plat2_activate(edict_t * ent,edict_t * other,edict_t * activator)918 void plat2_activate (edict_t *ent, edict_t *other, edict_t *activator)
919 {
920 edict_t *trigger;
921
922 // if(ent->targetname)
923 // ent->targetname[0] = 0;
924
925 ent->use = Use_Plat2;
926
927 trigger = plat_spawn_inside_trigger (ent); // the "start moving" trigger
928
929 trigger->maxs[0]+=10;
930 trigger->maxs[1]+=10;
931 trigger->mins[0]-=10;
932 trigger->mins[1]-=10;
933
934 gi.linkentity (trigger);
935
936 trigger->touch = Touch_Plat_Center2; // Override trigger touch function
937
938 plat2_go_down(ent);
939 }
940
941 /*QUAKED func_plat2 (0 .5 .8) ? PLAT_LOW_TRIGGER PLAT2_TOGGLE PLAT2_TOP PLAT2_TRIGGER_TOP PLAT2_TRIGGER_BOTTOM BOX_LIFT
942 speed default 150
943
944 PLAT_LOW_TRIGGER - creates a short trigger field at the bottom
945 PLAT2_TOGGLE - plat will not return to default position.
946 PLAT2_TOP - plat's default position will the the top.
947 PLAT2_TRIGGER_TOP - plat will trigger it's targets each time it hits top
948 PLAT2_TRIGGER_BOTTOM - plat will trigger it's targets each time it hits bottom
949 BOX_LIFT - this indicates that the lift is a box, rather than just a platform
950
951 Plats are always drawn in the extended position, so they will light correctly.
952
953 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.
954
955 "speed" overrides default 200.
956 "accel" overrides default 500
957 "lip" no default
958
959 If the "height" key is set, that will determine the amount the plat moves, instead of being implicitly determoveinfoned by the model's height.
960
961 */
SP_func_plat2(edict_t * ent)962 void SP_func_plat2 (edict_t *ent)
963 {
964 edict_t *trigger;
965
966 VectorClear (ent->s.angles);
967 ent->solid = SOLID_BSP;
968 ent->movetype = MOVETYPE_PUSH;
969
970 gi.setmodel (ent, ent->model);
971
972 ent->blocked = plat2_blocked;
973
974 if (!ent->speed)
975 ent->speed = 20;
976 else
977 ent->speed *= 0.1;
978
979 if (!ent->accel)
980 ent->accel = 5;
981 else
982 ent->accel *= 0.1;
983
984 if (!ent->decel)
985 ent->decel = 5;
986 else
987 ent->decel *= 0.1;
988
989 if (deathmatch->value)
990 {
991 ent->speed *= 2;
992 ent->accel *= 2;
993 ent->decel *= 2;
994 }
995
996
997 //PMM Added to kill things it's being blocked by
998 if (!ent->dmg)
999 ent->dmg = 2;
1000
1001 // if (!st.lip)
1002 // st.lip = 8;
1003
1004 // pos1 is the top position, pos2 is the bottom
1005 VectorCopy (ent->s.origin, ent->pos1);
1006 VectorCopy (ent->s.origin, ent->pos2);
1007
1008 if (st.height)
1009 ent->pos2[2] -= (st.height - st.lip);
1010 else
1011 ent->pos2[2] -= (ent->maxs[2] - ent->mins[2]) - st.lip;
1012
1013 ent->moveinfo.state = STATE_TOP;
1014
1015 if(ent->targetname)
1016 {
1017 ent->use = plat2_activate;
1018 }
1019 else
1020 {
1021 ent->use = Use_Plat2;
1022
1023 trigger = plat_spawn_inside_trigger (ent); // the "start moving" trigger
1024
1025 // PGM - debugging??
1026 trigger->maxs[0]+=10;
1027 trigger->maxs[1]+=10;
1028 trigger->mins[0]-=10;
1029 trigger->mins[1]-=10;
1030
1031 gi.linkentity (trigger);
1032
1033 trigger->touch = Touch_Plat_Center2; // Override trigger touch function
1034
1035 if(!(ent->spawnflags & PLAT2_TOP))
1036 {
1037 VectorCopy (ent->pos2, ent->s.origin);
1038 ent->moveinfo.state = STATE_BOTTOM;
1039 }
1040 }
1041
1042 gi.linkentity (ent);
1043
1044 ent->moveinfo.speed = ent->speed;
1045 ent->moveinfo.accel = ent->accel;
1046 ent->moveinfo.decel = ent->decel;
1047 ent->moveinfo.wait = ent->wait;
1048 VectorCopy (ent->pos1, ent->moveinfo.start_origin);
1049 VectorCopy (ent->s.angles, ent->moveinfo.start_angles);
1050 VectorCopy (ent->pos2, ent->moveinfo.end_origin);
1051 VectorCopy (ent->s.angles, ent->moveinfo.end_angles);
1052
1053 ent->moveinfo.sound_start = gi.soundindex ("plats/pt1_strt.wav");
1054 ent->moveinfo.sound_middle = gi.soundindex ("plats/pt1_mid.wav");
1055 ent->moveinfo.sound_end = gi.soundindex ("plats/pt1_end.wav");
1056 }
1057
1058
1059 //====================================================================
1060
1061 /*QUAKED func_rotating (0 .5 .8) ? START_ON REVERSE X_AXIS Y_AXIS TOUCH_PAIN STOP ANIMATED ANIMATED_FAST EAST MED HARD DM COOP ACCEL
1062 You need to have an origin brush as part of this entity. The center of that brush will be
1063 the point around which it is rotated. It will rotate around the Z axis by default. You can
1064 check either the X_AXIS or Y_AXIS box to change that.
1065
1066 func_rotating will use it's targets when it stops and starts.
1067
1068 "speed" determines how fast it moves; default value is 100.
1069 "dmg" damage to inflict when blocked (2 default)
1070 "accel" if specified, is how much the rotation speed will increase per .1sec.
1071
1072 REVERSE will cause the it to rotate in the opposite direction.
1073 STOP mean it will stop moving instead of pushing entities
1074 ACCEL means it will accelerate to it's final speed and decelerate when shutting down.
1075 */
1076
1077 //============
1078 //PGM
rotating_accel(edict_t * self)1079 void rotating_accel (edict_t *self)
1080 {
1081 float current_speed;
1082
1083 current_speed = VectorLength (self->avelocity);
1084 if(current_speed >= (self->speed - self->accel)) // done
1085 {
1086 VectorScale (self->movedir, self->speed, self->avelocity);
1087 G_UseTargets (self, self);
1088 }
1089 else
1090 {
1091 current_speed += self->accel;
1092 VectorScale (self->movedir, current_speed, self->avelocity);
1093 self->think = rotating_accel;
1094 self->nextthink = level.time + FRAMETIME;
1095 }
1096 }
1097
rotating_decel(edict_t * self)1098 void rotating_decel (edict_t *self)
1099 {
1100 float current_speed;
1101
1102 current_speed = VectorLength (self->avelocity);
1103 if(current_speed <= self->decel) // done
1104 {
1105 VectorClear (self->avelocity);
1106 G_UseTargets (self, self);
1107 self->touch = NULL;
1108 }
1109 else
1110 {
1111 current_speed -= self->decel;
1112 VectorScale (self->movedir, current_speed, self->avelocity);
1113 self->think = rotating_decel;
1114 self->nextthink = level.time + FRAMETIME;
1115 }
1116 }
1117 //PGM
1118 //============
1119
1120
rotating_blocked(edict_t * self,edict_t * other)1121 void rotating_blocked (edict_t *self, edict_t *other)
1122 {
1123 T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
1124 }
1125
rotating_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)1126 void rotating_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
1127 {
1128 if (self->avelocity[0] || self->avelocity[1] || self->avelocity[2])
1129 T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
1130 }
1131
rotating_use(edict_t * self,edict_t * other,edict_t * activator)1132 void rotating_use (edict_t *self, edict_t *other, edict_t *activator)
1133 {
1134 if (!VectorCompare (self->avelocity, vec3_origin))
1135 {
1136 self->s.sound = 0;
1137 //PGM
1138 if(self->spawnflags & 8192) // Decelerate
1139 rotating_decel (self);
1140 else
1141 {
1142 VectorClear (self->avelocity);
1143 G_UseTargets (self, self);
1144 self->touch = NULL;
1145 }
1146 //PGM
1147 }
1148 else
1149 {
1150 self->s.sound = self->moveinfo.sound_middle;
1151 //PGM
1152 if(self->spawnflags & 8192) // accelerate
1153 rotating_accel (self);
1154 else
1155 {
1156 VectorScale (self->movedir, self->speed, self->avelocity);
1157 G_UseTargets (self, self);
1158 }
1159 if (self->spawnflags & 16)
1160 self->touch = rotating_touch;
1161 //PGM
1162 }
1163 }
1164
SP_func_rotating(edict_t * ent)1165 void SP_func_rotating (edict_t *ent)
1166 {
1167 ent->solid = SOLID_BSP;
1168 if (ent->spawnflags & 32)
1169 ent->movetype = MOVETYPE_STOP;
1170 else
1171 ent->movetype = MOVETYPE_PUSH;
1172
1173 // set the axis of rotation
1174 VectorClear(ent->movedir);
1175 if (ent->spawnflags & 4)
1176 ent->movedir[2] = 1.0;
1177 else if (ent->spawnflags & 8)
1178 ent->movedir[0] = 1.0;
1179 else // Z_AXIS
1180 ent->movedir[1] = 1.0;
1181
1182 // check for reverse rotation
1183 if (ent->spawnflags & 2)
1184 VectorNegate (ent->movedir, ent->movedir);
1185
1186 if (!ent->speed)
1187 ent->speed = 100;
1188 if (!ent->dmg)
1189 ent->dmg = 2;
1190
1191 // ent->moveinfo.sound_middle = "doors/hydro1.wav";
1192
1193 ent->use = rotating_use;
1194 if (ent->dmg)
1195 ent->blocked = rotating_blocked;
1196
1197 if (ent->spawnflags & 1)
1198 ent->use (ent, NULL, NULL);
1199
1200 if (ent->spawnflags & 64)
1201 ent->s.effects |= EF_ANIM_ALL;
1202 if (ent->spawnflags & 128)
1203 ent->s.effects |= EF_ANIM_ALLFAST;
1204
1205 //PGM
1206 if(ent->spawnflags & 8192) // Accelerate / Decelerate
1207 {
1208 if(!ent->accel)
1209 ent->accel = 1;
1210 else if (ent->accel > ent->speed)
1211 ent->accel = ent->speed;
1212
1213 if(!ent->decel)
1214 ent->decel = 1;
1215 else if (ent->decel > ent->speed)
1216 ent->decel = ent->speed;
1217 }
1218 //PGM
1219
1220 gi.setmodel (ent, ent->model);
1221 gi.linkentity (ent);
1222 }
1223
1224 /*
1225 ======================================================================
1226
1227 BUTTONS
1228
1229 ======================================================================
1230 */
1231
1232 /*QUAKED func_button (0 .5 .8) ?
1233 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.
1234
1235 "angle" determines the opening direction
1236 "target" all entities with a matching targetname will be used
1237 "speed" override the default 40 speed
1238 "wait" override the default 1 second wait (-1 = never return)
1239 "lip" override the default 4 pixel lip remaining at end of move
1240 "health" if set, the button must be killed instead of touched
1241 "sounds"
1242 1) silent
1243 2) steam metal
1244 3) wooden clunk
1245 4) metallic click
1246 5) in-out
1247 */
1248
button_done(edict_t * self)1249 void button_done (edict_t *self)
1250 {
1251 self->moveinfo.state = STATE_BOTTOM;
1252 self->s.effects &= ~EF_ANIM23;
1253 self->s.effects |= EF_ANIM01;
1254 }
1255
button_return(edict_t * self)1256 void button_return (edict_t *self)
1257 {
1258 self->moveinfo.state = STATE_DOWN;
1259
1260 Move_Calc (self, self->moveinfo.start_origin, button_done);
1261
1262 self->s.frame = 0;
1263
1264 if (self->health)
1265 self->takedamage = DAMAGE_YES;
1266 }
1267
button_wait(edict_t * self)1268 void button_wait (edict_t *self)
1269 {
1270 self->moveinfo.state = STATE_TOP;
1271 self->s.effects &= ~EF_ANIM01;
1272 self->s.effects |= EF_ANIM23;
1273
1274 G_UseTargets (self, self->activator);
1275 self->s.frame = 1;
1276 if (self->moveinfo.wait >= 0)
1277 {
1278 self->nextthink = level.time + self->moveinfo.wait;
1279 self->think = button_return;
1280 }
1281 }
1282
button_fire(edict_t * self)1283 void button_fire (edict_t *self)
1284 {
1285 if (self->moveinfo.state == STATE_UP || self->moveinfo.state == STATE_TOP)
1286 return;
1287
1288 self->moveinfo.state = STATE_UP;
1289 if (self->moveinfo.sound_start && !(self->flags & FL_TEAMSLAVE))
1290 gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_start, 1, ATTN_STATIC, 0);
1291 Move_Calc (self, self->moveinfo.end_origin, button_wait);
1292 }
1293
button_use(edict_t * self,edict_t * other,edict_t * activator)1294 void button_use (edict_t *self, edict_t *other, edict_t *activator)
1295 {
1296 self->activator = activator;
1297 button_fire (self);
1298 }
1299
button_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)1300 void button_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
1301 {
1302 if (!other->client)
1303 return;
1304
1305 if (other->health <= 0)
1306 return;
1307
1308 self->activator = other;
1309 button_fire (self);
1310 }
1311
button_killed(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)1312 void button_killed (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1313 {
1314 self->activator = attacker;
1315 self->health = self->max_health;
1316 self->takedamage = DAMAGE_NO;
1317 button_fire (self);
1318 }
1319
SP_func_button(edict_t * ent)1320 void SP_func_button (edict_t *ent)
1321 {
1322 vec3_t abs_movedir;
1323 float dist;
1324
1325 G_SetMovedir (ent->s.angles, ent->movedir);
1326 ent->movetype = MOVETYPE_STOP;
1327 ent->solid = SOLID_BSP;
1328 gi.setmodel (ent, ent->model);
1329
1330 if (ent->sounds != 1)
1331 ent->moveinfo.sound_start = gi.soundindex ("switches/butn2.wav");
1332
1333 if (!ent->speed)
1334 ent->speed = 40;
1335 if (!ent->accel)
1336 ent->accel = ent->speed;
1337 if (!ent->decel)
1338 ent->decel = ent->speed;
1339
1340 if (!ent->wait)
1341 ent->wait = 3;
1342 if (!st.lip)
1343 st.lip = 4;
1344
1345 VectorCopy (ent->s.origin, ent->pos1);
1346 abs_movedir[0] = fabs(ent->movedir[0]);
1347 abs_movedir[1] = fabs(ent->movedir[1]);
1348 abs_movedir[2] = fabs(ent->movedir[2]);
1349 dist = abs_movedir[0] * ent->size[0] + abs_movedir[1] * ent->size[1] + abs_movedir[2] * ent->size[2] - st.lip;
1350 VectorMA (ent->pos1, dist, ent->movedir, ent->pos2);
1351
1352 ent->use = button_use;
1353 ent->s.effects |= EF_ANIM01;
1354
1355 if (ent->health)
1356 {
1357 ent->max_health = ent->health;
1358 ent->die = button_killed;
1359 ent->takedamage = DAMAGE_YES;
1360 }
1361 else if (! ent->targetname)
1362 ent->touch = button_touch;
1363
1364 ent->moveinfo.state = STATE_BOTTOM;
1365
1366 ent->moveinfo.speed = ent->speed;
1367 ent->moveinfo.accel = ent->accel;
1368 ent->moveinfo.decel = ent->decel;
1369 ent->moveinfo.wait = ent->wait;
1370 VectorCopy (ent->pos1, ent->moveinfo.start_origin);
1371 VectorCopy (ent->s.angles, ent->moveinfo.start_angles);
1372 VectorCopy (ent->pos2, ent->moveinfo.end_origin);
1373 VectorCopy (ent->s.angles, ent->moveinfo.end_angles);
1374
1375 gi.linkentity (ent);
1376 }
1377
1378 /*
1379 ======================================================================
1380
1381 DOORS
1382
1383 spawn a trigger surrounding the entire team unless it is
1384 already targeted by another
1385
1386 ======================================================================
1387 */
1388
1389 /*QUAKED func_door (0 .5 .8) ? START_OPEN x CRUSHER NOMONSTER ANIMATED TOGGLE ANIMATED_FAST
1390 TOGGLE wait in both the start and end states for a trigger event.
1391 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).
1392 NOMONSTER monsters will not trigger this door
1393
1394 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1395 "angle" determines the opening direction
1396 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1397 "health" if set, door must be shot open
1398 "speed" movement speed (100 default)
1399 "wait" wait before returning (3 default, -1 = never return)
1400 "lip" lip remaining at end of move (8 default)
1401 "dmg" damage to inflict when blocked (2 default)
1402 "sounds"
1403 1) silent
1404 2) light
1405 3) medium
1406 4) heavy
1407 */
1408
door_use_areaportals(edict_t * self,qboolean open)1409 void door_use_areaportals (edict_t *self, qboolean open)
1410 {
1411 edict_t *t = NULL;
1412
1413 if (!self->target)
1414 return;
1415
1416 while ((t = G_Find (t, FOFS(targetname), self->target)))
1417 {
1418 if (Q_stricmp(t->classname, "func_areaportal") == 0)
1419 {
1420 gi.SetAreaPortalState (t->style, open);
1421 }
1422 }
1423 }
1424
1425 void door_go_down (edict_t *self);
1426
door_hit_top(edict_t * self)1427 void door_hit_top (edict_t *self)
1428 {
1429 if (!(self->flags & FL_TEAMSLAVE))
1430 {
1431 if (self->moveinfo.sound_end)
1432 gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_end, 1, ATTN_STATIC, 0);
1433 self->s.sound = 0;
1434 }
1435 self->moveinfo.state = STATE_TOP;
1436 if (self->spawnflags & DOOR_TOGGLE)
1437 return;
1438 if (self->moveinfo.wait >= 0)
1439 {
1440 self->think = door_go_down;
1441 self->nextthink = level.time + self->moveinfo.wait;
1442 }
1443 }
1444
door_hit_bottom(edict_t * self)1445 void door_hit_bottom (edict_t *self)
1446 {
1447 if (!(self->flags & FL_TEAMSLAVE))
1448 {
1449 if (self->moveinfo.sound_end)
1450 gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_end, 1, ATTN_STATIC, 0);
1451 self->s.sound = 0;
1452 }
1453 self->moveinfo.state = STATE_BOTTOM;
1454 door_use_areaportals (self, false);
1455 }
1456
door_go_down(edict_t * self)1457 void door_go_down (edict_t *self)
1458 {
1459 if (!(self->flags & FL_TEAMSLAVE))
1460 {
1461 if (self->moveinfo.sound_start)
1462 gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_start, 1, ATTN_STATIC, 0);
1463 self->s.sound = self->moveinfo.sound_middle;
1464 }
1465 if (self->max_health)
1466 {
1467 self->takedamage = DAMAGE_YES;
1468 self->health = self->max_health;
1469 }
1470
1471 self->moveinfo.state = STATE_DOWN;
1472 if (strcmp(self->classname, "func_door") == 0)
1473 Move_Calc (self, self->moveinfo.start_origin, door_hit_bottom);
1474 else if (strcmp(self->classname, "func_door_rotating") == 0)
1475 AngleMove_Calc (self, door_hit_bottom);
1476 }
1477
door_go_up(edict_t * self,edict_t * activator)1478 void door_go_up (edict_t *self, edict_t *activator)
1479 {
1480 if (self->moveinfo.state == STATE_UP)
1481 return; // already going up
1482
1483 if (self->moveinfo.state == STATE_TOP)
1484 { // reset top wait time
1485 if (self->moveinfo.wait >= 0)
1486 self->nextthink = level.time + self->moveinfo.wait;
1487 return;
1488 }
1489
1490 if (!(self->flags & FL_TEAMSLAVE))
1491 {
1492 if (self->moveinfo.sound_start)
1493 gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_start, 1, ATTN_STATIC, 0);
1494 self->s.sound = self->moveinfo.sound_middle;
1495 }
1496 self->moveinfo.state = STATE_UP;
1497 if (strcmp(self->classname, "func_door") == 0)
1498 Move_Calc (self, self->moveinfo.end_origin, door_hit_top);
1499 else if (strcmp(self->classname, "func_door_rotating") == 0)
1500 AngleMove_Calc (self, door_hit_top);
1501
1502 G_UseTargets (self, activator);
1503 door_use_areaportals (self, true);
1504 }
1505
1506 //======
1507 //PGM
smart_water_go_up(edict_t * self)1508 void smart_water_go_up (edict_t *self)
1509 {
1510 float distance;
1511 edict_t *lowestPlayer;
1512 edict_t *ent;
1513 float lowestPlayerPt;
1514 int i;
1515
1516 if (self->moveinfo.state == STATE_TOP)
1517 { // reset top wait time
1518 if (self->moveinfo.wait >= 0)
1519 self->nextthink = level.time + self->moveinfo.wait;
1520 return;
1521 }
1522
1523 if (self->health)
1524 {
1525 if(self->absmax[2] >= self->health)
1526 {
1527 VectorClear (self->velocity);
1528 self->nextthink = 0;
1529 self->moveinfo.state = STATE_TOP;
1530 return;
1531 }
1532 }
1533
1534 if (!(self->flags & FL_TEAMSLAVE))
1535 {
1536 if (self->moveinfo.sound_start)
1537 gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_start, 1, ATTN_STATIC, 0);
1538 self->s.sound = self->moveinfo.sound_middle;
1539 }
1540
1541 // find the lowest player point.
1542 lowestPlayerPt = 999999;
1543 lowestPlayer = NULL;
1544 for (i=0 ; i<game.maxclients ; i++)
1545 {
1546 ent = &g_edicts[1+i];
1547
1548 // don't count dead or unused player slots
1549 if((ent->inuse) && (ent->health > 0))
1550 {
1551 if (ent->absmin[2] < lowestPlayerPt)
1552 {
1553 lowestPlayerPt = ent->absmin[2];
1554 lowestPlayer = ent;
1555 }
1556 }
1557 }
1558
1559 if(!lowestPlayer)
1560 {
1561 return;
1562 }
1563
1564 distance = lowestPlayerPt - self->absmax[2];
1565
1566 // for the calculations, make sure we intend to go up at least a little.
1567 if(distance < self->accel)
1568 {
1569 distance = 100;
1570 self->moveinfo.speed = 5;
1571 }
1572 else
1573 self->moveinfo.speed = distance / self->accel;
1574
1575 if(self->moveinfo.speed < 5)
1576 self->moveinfo.speed = 5;
1577 else if(self->moveinfo.speed > self->speed)
1578 self->moveinfo.speed = self->speed;
1579
1580 // FIXME - should this allow any movement other than straight up?
1581 VectorSet(self->moveinfo.dir, 0, 0, 1);
1582 VectorScale (self->moveinfo.dir, self->moveinfo.speed, self->velocity);
1583 self->moveinfo.remaining_distance = distance;
1584
1585 if(self->moveinfo.state != STATE_UP)
1586 {
1587 G_UseTargets (self, lowestPlayer);
1588 door_use_areaportals (self, true);
1589 self->moveinfo.state = STATE_UP;
1590 }
1591
1592 self->think = smart_water_go_up;
1593 self->nextthink = level.time + FRAMETIME;
1594 }
1595 //PGM
1596 //======
1597
door_use(edict_t * self,edict_t * other,edict_t * activator)1598 void door_use (edict_t *self, edict_t *other, edict_t *activator)
1599 {
1600 edict_t *ent;
1601 vec3_t center; //PGM
1602
1603 if (self->flags & FL_TEAMSLAVE)
1604 return;
1605
1606 if (self->spawnflags & DOOR_TOGGLE)
1607 {
1608 if (self->moveinfo.state == STATE_UP || self->moveinfo.state == STATE_TOP)
1609 {
1610 // trigger all paired doors
1611 for (ent = self ; ent ; ent = ent->teamchain)
1612 {
1613 ent->message = NULL;
1614 ent->touch = NULL;
1615 door_go_down (ent);
1616 }
1617 return;
1618 }
1619 }
1620
1621 //PGM
1622 // smart water is different
1623 VectorAdd(self->mins, self->maxs, center);
1624 VectorScale(center, 0.5, center);
1625 if ((gi.pointcontents (center) & MASK_WATER) && self->spawnflags & 2)
1626 {
1627 self->message = NULL;
1628 self->touch = NULL;
1629 self->enemy = activator;
1630 smart_water_go_up (self);
1631 return;
1632 }
1633 //PGM
1634
1635 // trigger all paired doors
1636 for (ent = self ; ent ; ent = ent->teamchain)
1637 {
1638 ent->message = NULL;
1639 ent->touch = NULL;
1640 door_go_up (ent, activator);
1641 }
1642 };
1643
Touch_DoorTrigger(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)1644 void Touch_DoorTrigger (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
1645 {
1646 if (other->health <= 0)
1647 return;
1648
1649 if (!(other->svflags & SVF_MONSTER) && (!other->client))
1650 return;
1651
1652 if ((self->owner->spawnflags & DOOR_NOMONSTER) && (other->svflags & SVF_MONSTER))
1653 return;
1654
1655 if (level.time < self->touch_debounce_time)
1656 return;
1657 self->touch_debounce_time = level.time + 1.0;
1658
1659 door_use (self->owner, other, other);
1660 }
1661
Think_CalcMoveSpeed(edict_t * self)1662 void Think_CalcMoveSpeed (edict_t *self)
1663 {
1664 edict_t *ent;
1665 float min;
1666 float time;
1667 float newspeed;
1668 float ratio;
1669 float dist;
1670
1671 if (self->flags & FL_TEAMSLAVE)
1672 return; // only the team master does this
1673
1674 // find the smallest distance any member of the team will be moving
1675 min = fabs(self->moveinfo.distance);
1676 for (ent = self->teamchain; ent; ent = ent->teamchain)
1677 {
1678 dist = fabs(ent->moveinfo.distance);
1679 if (dist < min)
1680 min = dist;
1681 }
1682
1683 time = min / self->moveinfo.speed;
1684
1685 // adjust speeds so they will all complete at the same time
1686 for (ent = self; ent; ent = ent->teamchain)
1687 {
1688 newspeed = fabs(ent->moveinfo.distance) / time;
1689 ratio = newspeed / ent->moveinfo.speed;
1690 if (ent->moveinfo.accel == ent->moveinfo.speed)
1691 ent->moveinfo.accel = newspeed;
1692 else
1693 ent->moveinfo.accel *= ratio;
1694 if (ent->moveinfo.decel == ent->moveinfo.speed)
1695 ent->moveinfo.decel = newspeed;
1696 else
1697 ent->moveinfo.decel *= ratio;
1698 ent->moveinfo.speed = newspeed;
1699 }
1700 }
1701
Think_SpawnDoorTrigger(edict_t * ent)1702 void Think_SpawnDoorTrigger (edict_t *ent)
1703 {
1704 edict_t *other;
1705 vec3_t mins, maxs;
1706
1707 if (ent->flags & FL_TEAMSLAVE)
1708 return; // only the team leader spawns a trigger
1709
1710 VectorCopy (ent->absmin, mins);
1711 VectorCopy (ent->absmax, maxs);
1712
1713 for (other = ent->teamchain ; other ; other=other->teamchain)
1714 {
1715 AddPointToBounds (other->absmin, mins, maxs);
1716 AddPointToBounds (other->absmax, mins, maxs);
1717 }
1718
1719 // expand
1720 mins[0] -= 60;
1721 mins[1] -= 60;
1722 maxs[0] += 60;
1723 maxs[1] += 60;
1724
1725 other = G_Spawn ();
1726 VectorCopy (mins, other->mins);
1727 VectorCopy (maxs, other->maxs);
1728 other->owner = ent;
1729 other->solid = SOLID_TRIGGER;
1730 other->movetype = MOVETYPE_NONE;
1731 other->touch = Touch_DoorTrigger;
1732 gi.linkentity (other);
1733
1734 if (ent->spawnflags & DOOR_START_OPEN)
1735 door_use_areaportals (ent, true);
1736
1737 Think_CalcMoveSpeed (ent);
1738 }
1739
door_blocked(edict_t * self,edict_t * other)1740 void door_blocked (edict_t *self, edict_t *other)
1741 {
1742 edict_t *ent;
1743
1744 if (!(other->svflags & SVF_MONSTER) && (!other->client) )
1745 {
1746 // give it a chance to go away on it's own terms (like gibs)
1747 T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH);
1748 // if it's still there, nuke it
1749 if (other && other->inuse)
1750 BecomeExplosion1 (other);
1751 return;
1752 }
1753
1754 T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
1755
1756 if (self->spawnflags & DOOR_CRUSHER)
1757 return;
1758
1759
1760 // if a door has a negative wait, it would never come back if blocked,
1761 // so let it just squash the object to death real fast
1762 if (self->moveinfo.wait >= 0)
1763 {
1764 if (self->moveinfo.state == STATE_DOWN)
1765 {
1766 for (ent = self->teammaster ; ent ; ent = ent->teamchain)
1767 door_go_up (ent, ent->activator);
1768 }
1769 else
1770 {
1771 for (ent = self->teammaster ; ent ; ent = ent->teamchain)
1772 door_go_down (ent);
1773 }
1774 }
1775 }
1776
door_killed(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)1777 void door_killed (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
1778 {
1779 edict_t *ent;
1780
1781 for (ent = self->teammaster ; ent ; ent = ent->teamchain)
1782 {
1783 ent->health = ent->max_health;
1784 ent->takedamage = DAMAGE_NO;
1785 }
1786 door_use (self->teammaster, attacker, attacker);
1787 }
1788
door_touch(edict_t * self,edict_t * other,cplane_t * plane,csurface_t * surf)1789 void door_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
1790 {
1791 if (!other->client)
1792 return;
1793
1794 if (level.time < self->touch_debounce_time)
1795 return;
1796 self->touch_debounce_time = level.time + 5.0;
1797
1798 gi.centerprintf (other, "%s", self->message);
1799 gi.sound (other, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0);
1800 }
1801
SP_func_door(edict_t * ent)1802 void SP_func_door (edict_t *ent)
1803 {
1804 vec3_t abs_movedir;
1805
1806 if (ent->sounds != 1)
1807 {
1808 ent->moveinfo.sound_start = gi.soundindex ("doors/dr1_strt.wav");
1809 ent->moveinfo.sound_middle = gi.soundindex ("doors/dr1_mid.wav");
1810 ent->moveinfo.sound_end = gi.soundindex ("doors/dr1_end.wav");
1811 }
1812
1813 G_SetMovedir (ent->s.angles, ent->movedir);
1814 ent->movetype = MOVETYPE_PUSH;
1815 ent->solid = SOLID_BSP;
1816 gi.setmodel (ent, ent->model);
1817
1818 ent->blocked = door_blocked;
1819 ent->use = door_use;
1820
1821 if (!ent->speed)
1822 ent->speed = 100;
1823 if (deathmatch->value)
1824 ent->speed *= 2;
1825
1826 if (!ent->accel)
1827 ent->accel = ent->speed;
1828 if (!ent->decel)
1829 ent->decel = ent->speed;
1830
1831 if (!ent->wait)
1832 ent->wait = 3;
1833 if (!st.lip)
1834 st.lip = 8;
1835 if (!ent->dmg)
1836 ent->dmg = 2;
1837
1838 // calculate second position
1839 VectorCopy (ent->s.origin, ent->pos1);
1840 abs_movedir[0] = fabs(ent->movedir[0]);
1841 abs_movedir[1] = fabs(ent->movedir[1]);
1842 abs_movedir[2] = fabs(ent->movedir[2]);
1843 ent->moveinfo.distance = abs_movedir[0] * ent->size[0] + abs_movedir[1] * ent->size[1] + abs_movedir[2] * ent->size[2] - st.lip;
1844 VectorMA (ent->pos1, ent->moveinfo.distance, ent->movedir, ent->pos2);
1845
1846 // if it starts open, switch the positions
1847 if (ent->spawnflags & DOOR_START_OPEN)
1848 {
1849 VectorCopy (ent->pos2, ent->s.origin);
1850 VectorCopy (ent->pos1, ent->pos2);
1851 VectorCopy (ent->s.origin, ent->pos1);
1852 }
1853
1854 ent->moveinfo.state = STATE_BOTTOM;
1855
1856 if (ent->health)
1857 {
1858 ent->takedamage = DAMAGE_YES;
1859 ent->die = door_killed;
1860 ent->max_health = ent->health;
1861 }
1862 else if (ent->targetname && ent->message)
1863 {
1864 gi.soundindex ("misc/talk.wav");
1865 ent->touch = door_touch;
1866 }
1867
1868 ent->moveinfo.speed = ent->speed;
1869 ent->moveinfo.accel = ent->accel;
1870 ent->moveinfo.decel = ent->decel;
1871 ent->moveinfo.wait = ent->wait;
1872 VectorCopy (ent->pos1, ent->moveinfo.start_origin);
1873 VectorCopy (ent->s.angles, ent->moveinfo.start_angles);
1874 VectorCopy (ent->pos2, ent->moveinfo.end_origin);
1875 VectorCopy (ent->s.angles, ent->moveinfo.end_angles);
1876
1877 if (ent->spawnflags & 16)
1878 ent->s.effects |= EF_ANIM_ALL;
1879 if (ent->spawnflags & 64)
1880 ent->s.effects |= EF_ANIM_ALLFAST;
1881
1882 // to simplify logic elsewhere, make non-teamed doors into a team of one
1883 if (!ent->team)
1884 ent->teammaster = ent;
1885
1886 gi.linkentity (ent);
1887
1888 ent->nextthink = level.time + FRAMETIME;
1889 if (ent->health || ent->targetname)
1890 ent->think = Think_CalcMoveSpeed;
1891 else
1892 ent->think = Think_SpawnDoorTrigger;
1893 }
1894
1895 //PGM
Door_Activate(edict_t * self,edict_t * other,edict_t * activator)1896 void Door_Activate (edict_t *self, edict_t *other, edict_t *activator)
1897 {
1898 self->use = NULL;
1899
1900 if (self->health)
1901 {
1902 self->takedamage = DAMAGE_YES;
1903 self->die = door_killed;
1904 self->max_health = self->health;
1905 }
1906
1907 if (self->health)
1908 self->think = Think_CalcMoveSpeed;
1909 else
1910 self->think = Think_SpawnDoorTrigger;
1911 self->nextthink = level.time + FRAMETIME;
1912
1913 }
1914 //PGM
1915
1916 /*QUAKED func_door_rotating (0 .5 .8) ? START_OPEN REVERSE CRUSHER NOMONSTER ANIMATED TOGGLE X_AXIS Y_AXIS EASY MED HARD DM COOP INACTIVE
1917 TOGGLE causes the door to wait in both the start and end states for a trigger event.
1918
1919 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).
1920 NOMONSTER monsters will not trigger this door
1921
1922 You need to have an origin brush as part of this entity. The center of that brush will be
1923 the point around which it is rotated. It will rotate around the Z axis by default. You can
1924 check either the X_AXIS or Y_AXIS box to change that.
1925
1926 "distance" is how many degrees the door will be rotated.
1927 "speed" determines how fast the door moves; default value is 100.
1928 "accel" if specified,is how much the rotation speed will increase each .1 sec. (default: no accel)
1929
1930 REVERSE will cause the door to rotate in the opposite direction.
1931 INACTIVE will cause the door to be inactive until triggered.
1932
1933 "message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet
1934 "angle" determines the opening direction
1935 "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door.
1936 "health" if set, door must be shot open
1937 "speed" movement speed (100 default)
1938 "wait" wait before returning (3 default, -1 = never return)
1939 "dmg" damage to inflict when blocked (2 default)
1940 "sounds"
1941 1) silent
1942 2) light
1943 3) medium
1944 4) heavy
1945 */
1946
SP_func_door_rotating(edict_t * ent)1947 void SP_func_door_rotating (edict_t *ent)
1948 {
1949 VectorClear (ent->s.angles);
1950
1951 // set the axis of rotation
1952 VectorClear(ent->movedir);
1953 if (ent->spawnflags & DOOR_X_AXIS)
1954 ent->movedir[2] = 1.0;
1955 else if (ent->spawnflags & DOOR_Y_AXIS)
1956 ent->movedir[0] = 1.0;
1957 else // Z_AXIS
1958 ent->movedir[1] = 1.0;
1959
1960 // check for reverse rotation
1961 if (ent->spawnflags & DOOR_REVERSE)
1962 VectorNegate (ent->movedir, ent->movedir);
1963
1964 if (!st.distance)
1965 {
1966 gi.dprintf("%s at %s with no distance set\n", ent->classname, vtos(ent->s.origin));
1967 st.distance = 90;
1968 }
1969
1970 VectorCopy (ent->s.angles, ent->pos1);
1971 VectorMA (ent->s.angles, st.distance, ent->movedir, ent->pos2);
1972 ent->moveinfo.distance = st.distance;
1973
1974 ent->movetype = MOVETYPE_PUSH;
1975 ent->solid = SOLID_BSP;
1976 gi.setmodel (ent, ent->model);
1977
1978 ent->blocked = door_blocked;
1979 ent->use = door_use;
1980
1981 if (!ent->speed)
1982 ent->speed = 100;
1983 if (!ent->accel)
1984 ent->accel = ent->speed;
1985 if (!ent->decel)
1986 ent->decel = ent->speed;
1987
1988 if (!ent->wait)
1989 ent->wait = 3;
1990 if (!ent->dmg)
1991 ent->dmg = 2;
1992
1993 if (ent->sounds != 1)
1994 {
1995 ent->moveinfo.sound_start = gi.soundindex ("doors/dr1_strt.wav");
1996 ent->moveinfo.sound_middle = gi.soundindex ("doors/dr1_mid.wav");
1997 ent->moveinfo.sound_end = gi.soundindex ("doors/dr1_end.wav");
1998 }
1999
2000 // if it starts open, switch the positions
2001 if (ent->spawnflags & DOOR_START_OPEN)
2002 {
2003 VectorCopy (ent->pos2, ent->s.angles);
2004 VectorCopy (ent->pos1, ent->pos2);
2005 VectorCopy (ent->s.angles, ent->pos1);
2006 VectorNegate (ent->movedir, ent->movedir);
2007 }
2008
2009 if (ent->health)
2010 {
2011 ent->takedamage = DAMAGE_YES;
2012 ent->die = door_killed;
2013 ent->max_health = ent->health;
2014 }
2015
2016 if (ent->targetname && ent->message)
2017 {
2018 gi.soundindex ("misc/talk.wav");
2019 ent->touch = door_touch;
2020 }
2021
2022 ent->moveinfo.state = STATE_BOTTOM;
2023 ent->moveinfo.speed = ent->speed;
2024 ent->moveinfo.accel = ent->accel;
2025 ent->moveinfo.decel = ent->decel;
2026 ent->moveinfo.wait = ent->wait;
2027 VectorCopy (ent->s.origin, ent->moveinfo.start_origin);
2028 VectorCopy (ent->pos1, ent->moveinfo.start_angles);
2029 VectorCopy (ent->s.origin, ent->moveinfo.end_origin);
2030 VectorCopy (ent->pos2, ent->moveinfo.end_angles);
2031
2032 if (ent->spawnflags & 16)
2033 ent->s.effects |= EF_ANIM_ALL;
2034
2035 // to simplify logic elsewhere, make non-teamed doors into a team of one
2036 if (!ent->team)
2037 ent->teammaster = ent;
2038
2039 gi.linkentity (ent);
2040
2041 ent->nextthink = level.time + FRAMETIME;
2042 if (ent->health || ent->targetname)
2043 ent->think = Think_CalcMoveSpeed;
2044 else
2045 ent->think = Think_SpawnDoorTrigger;
2046
2047 //PGM
2048 if (ent->spawnflags & DOOR_INACTIVE)
2049 {
2050 ent->takedamage = DAMAGE_NO;
2051 ent->die = NULL;
2052 ent->think = NULL;
2053 ent->nextthink = 0;
2054 ent->use = Door_Activate;
2055 }
2056 //PGM
2057 }
2058
smart_water_blocked(edict_t * self,edict_t * other)2059 void smart_water_blocked (edict_t *self, edict_t *other)
2060 {
2061 if (!(other->svflags & SVF_MONSTER) && (!other->client) )
2062 {
2063 // give it a chance to go away on it's own terms (like gibs)
2064 T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0, MOD_LAVA);
2065 // if it's still there, nuke it
2066 if (other && other->inuse) // PGM
2067 BecomeExplosion1 (other);
2068 return;
2069 }
2070
2071 T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 100, 1, 0, MOD_LAVA);
2072 }
2073
2074 /*QUAKED func_water (0 .5 .8) ? START_OPEN SMART
2075 func_water is a moveable water brush. It must be targeted to operate. Use a non-water texture at your own risk.
2076
2077 START_OPEN causes the water to move to its destination when spawned and operate in reverse.
2078
2079 SMART causes the water to adjust its speed depending on distance to player.
2080 (speed = distance/accel, min 5, max self->speed)
2081 "accel" for smart water, the divisor to determine water speed. default 20 (smaller = faster)
2082
2083 "health" maximum height of this water brush
2084 "angle" determines the opening direction (up or down only)
2085 "speed" movement speed (25 default)
2086 "wait" wait before returning (-1 default, -1 = TOGGLE)
2087 "lip" lip remaining at end of move (0 default)
2088 "sounds" (yes, these need to be changed)
2089 0) no sound
2090 1) water
2091 2) lava
2092 */
2093
SP_func_water(edict_t * self)2094 void SP_func_water (edict_t *self)
2095 {
2096 vec3_t abs_movedir;
2097
2098 G_SetMovedir (self->s.angles, self->movedir);
2099 self->movetype = MOVETYPE_PUSH;
2100 self->solid = SOLID_BSP;
2101 gi.setmodel (self, self->model);
2102
2103 switch (self->sounds)
2104 {
2105 default:
2106 break;
2107
2108 case 1: // water
2109 self->moveinfo.sound_start = gi.soundindex ("world/mov_watr.wav");
2110 self->moveinfo.sound_end = gi.soundindex ("world/stp_watr.wav");
2111 break;
2112
2113 case 2: // lava
2114 self->moveinfo.sound_start = gi.soundindex ("world/mov_watr.wav");
2115 self->moveinfo.sound_end = gi.soundindex ("world/stp_watr.wav");
2116 break;
2117 }
2118
2119 // calculate second position
2120 VectorCopy (self->s.origin, self->pos1);
2121 abs_movedir[0] = fabs(self->movedir[0]);
2122 abs_movedir[1] = fabs(self->movedir[1]);
2123 abs_movedir[2] = fabs(self->movedir[2]);
2124 self->moveinfo.distance = abs_movedir[0] * self->size[0] + abs_movedir[1] * self->size[1] + abs_movedir[2] * self->size[2] - st.lip;
2125 VectorMA (self->pos1, self->moveinfo.distance, self->movedir, self->pos2);
2126
2127 // if it starts open, switch the positions
2128 if (self->spawnflags & DOOR_START_OPEN)
2129 {
2130 VectorCopy (self->pos2, self->s.origin);
2131 VectorCopy (self->pos1, self->pos2);
2132 VectorCopy (self->s.origin, self->pos1);
2133 }
2134
2135 VectorCopy (self->pos1, self->moveinfo.start_origin);
2136 VectorCopy (self->s.angles, self->moveinfo.start_angles);
2137 VectorCopy (self->pos2, self->moveinfo.end_origin);
2138 VectorCopy (self->s.angles, self->moveinfo.end_angles);
2139
2140 self->moveinfo.state = STATE_BOTTOM;
2141
2142 if (!self->speed)
2143 self->speed = 25;
2144 self->moveinfo.accel = self->moveinfo.decel = self->moveinfo.speed = self->speed;
2145
2146 if ( self->spawnflags & 2) // smart water
2147 {
2148 // this is actually the divisor of the lowest player's distance to determine speed.
2149 // self->speed then becomes the cap of the speed.
2150 if(!self->accel)
2151 self->accel = 20;
2152 self->blocked = smart_water_blocked;
2153 }
2154
2155 if (!self->wait)
2156 self->wait = -1;
2157 self->moveinfo.wait = self->wait;
2158
2159 self->use = door_use;
2160
2161 if (self->wait == -1)
2162 self->spawnflags |= DOOR_TOGGLE;
2163
2164 self->classname = "func_door";
2165
2166 gi.linkentity (self);
2167 }
2168
2169
2170 #define TRAIN_START_ON 1
2171 #define TRAIN_TOGGLE 2
2172 #define TRAIN_BLOCK_STOPS 4
2173
2174 /*QUAKED func_train (0 .5 .8) ? START_ON TOGGLE BLOCK_STOPS
2175 Trains are moving platforms that players can ride.
2176 The targets origin specifies the min point of the train at each corner.
2177 The train spawns at the first target it is pointing at.
2178 If the train is the target of a button or trigger, it will not begin moving until activated.
2179 speed default 100
2180 dmg default 2
2181 noise looping sound to play when the train is in motion
2182
2183 To have other entities move with the train, set all the piece's team value to the same thing. They will move in unison.
2184 */
2185 void train_next (edict_t *self);
2186
train_blocked(edict_t * self,edict_t * other)2187 void train_blocked (edict_t *self, edict_t *other)
2188 {
2189 if (!(other->svflags & SVF_MONSTER) && (!other->client) )
2190 {
2191 // give it a chance to go away on it's own terms (like gibs)
2192 T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH);
2193 // if it's still there, nuke it
2194 if (other && other->inuse)
2195 BecomeExplosion1 (other);
2196 return;
2197 }
2198
2199 if (level.time < self->touch_debounce_time)
2200 return;
2201
2202 if (!self->dmg)
2203 return;
2204 self->touch_debounce_time = level.time + 0.5;
2205 T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
2206 }
2207
train_wait(edict_t * self)2208 void train_wait (edict_t *self)
2209 {
2210 if (self->target_ent->pathtarget)
2211 {
2212 char *savetarget;
2213 edict_t *ent;
2214
2215 ent = self->target_ent;
2216 savetarget = ent->target;
2217 ent->target = ent->pathtarget;
2218 G_UseTargets (ent, self->activator);
2219 ent->target = savetarget;
2220
2221 // make sure we didn't get killed by a killtarget
2222 if (!self->inuse)
2223 return;
2224 }
2225
2226 if (self->moveinfo.wait)
2227 {
2228 if (self->moveinfo.wait > 0)
2229 {
2230 self->nextthink = level.time + self->moveinfo.wait;
2231 self->think = train_next;
2232 }
2233 else if (self->spawnflags & TRAIN_TOGGLE) // && wait < 0
2234 {
2235 // PMM - clear target_ent, let train_next get called when we get used
2236 // train_next (self);
2237 self->target_ent = NULL;
2238 // pmm
2239 self->spawnflags &= ~TRAIN_START_ON;
2240 VectorClear (self->velocity);
2241 self->nextthink = 0;
2242 }
2243
2244 if (!(self->flags & FL_TEAMSLAVE))
2245 {
2246 if (self->moveinfo.sound_end)
2247 gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_end, 1, ATTN_STATIC, 0);
2248 self->s.sound = 0;
2249 }
2250 }
2251 else
2252 {
2253 train_next (self);
2254 }
2255
2256 }
2257
2258 //PGM
train_piece_wait(edict_t * self)2259 void train_piece_wait (edict_t *self)
2260 {
2261 }
2262 //PGM
2263
train_next(edict_t * self)2264 void train_next (edict_t *self)
2265 {
2266 edict_t *ent;
2267 vec3_t dest;
2268 qboolean first;
2269
2270 first = true;
2271 again:
2272 if (!self->target)
2273 {
2274 // gi.dprintf ("train_next: no next target\n");
2275 return;
2276 }
2277
2278 ent = G_PickTarget (self->target);
2279 if (!ent)
2280 {
2281 gi.dprintf ("train_next: bad target %s\n", self->target);
2282 return;
2283 }
2284
2285 self->target = ent->target;
2286
2287 // check for a teleport path_corner
2288 if (ent->spawnflags & 1)
2289 {
2290 if (!first)
2291 {
2292 gi.dprintf ("connected teleport path_corners, see %s at %s\n", ent->classname, vtos(ent->s.origin));
2293 return;
2294 }
2295 first = false;
2296 VectorSubtract (ent->s.origin, self->mins, self->s.origin);
2297 VectorCopy (self->s.origin, self->s.old_origin);
2298 self->s.event = EV_OTHER_TELEPORT;
2299 gi.linkentity (self);
2300 goto again;
2301 }
2302
2303 //PGM
2304 if (ent->speed)
2305 {
2306 self->speed = ent->speed;
2307 self->moveinfo.speed = ent->speed;
2308 if(ent->accel)
2309 self->moveinfo.accel = ent->accel;
2310 else
2311 self->moveinfo.accel = ent->speed;
2312 if(ent->decel)
2313 self->moveinfo.decel = ent->decel;
2314 else
2315 self->moveinfo.decel = ent->speed;
2316 self->moveinfo.current_speed = 0;
2317 }
2318 //PGM
2319
2320 self->moveinfo.wait = ent->wait;
2321 self->target_ent = ent;
2322
2323 if (!(self->flags & FL_TEAMSLAVE))
2324 {
2325 if (self->moveinfo.sound_start)
2326 gi.sound (self, CHAN_NO_PHS_ADD+CHAN_VOICE, self->moveinfo.sound_start, 1, ATTN_STATIC, 0);
2327 self->s.sound = self->moveinfo.sound_middle;
2328 }
2329
2330 VectorSubtract (ent->s.origin, self->mins, dest);
2331 self->moveinfo.state = STATE_TOP;
2332 VectorCopy (self->s.origin, self->moveinfo.start_origin);
2333 VectorCopy (dest, self->moveinfo.end_origin);
2334 Move_Calc (self, dest, train_wait);
2335 self->spawnflags |= TRAIN_START_ON;
2336
2337 //PGM
2338 if(self->team)
2339 {
2340 edict_t *e;
2341 vec3_t dir, dst;
2342
2343 VectorSubtract (dest, self->s.origin, dir);
2344 for (e=self->teamchain; e ; e = e->teamchain)
2345 {
2346 VectorAdd(dir, e->s.origin, dst);
2347 VectorCopy(e->s.origin, e->moveinfo.start_origin);
2348 VectorCopy(dst, e->moveinfo.end_origin);
2349
2350 e->moveinfo.state = STATE_TOP;
2351 e->speed = self->speed;
2352 e->moveinfo.speed = self->moveinfo.speed;
2353 e->moveinfo.accel = self->moveinfo.accel;
2354 e->moveinfo.decel = self->moveinfo.decel;
2355 e->movetype = MOVETYPE_PUSH;
2356 Move_Calc (e, dst, train_piece_wait);
2357 }
2358
2359 }
2360 //PGM
2361 }
2362
train_resume(edict_t * self)2363 void train_resume (edict_t *self)
2364 {
2365 edict_t *ent;
2366 vec3_t dest;
2367
2368 ent = self->target_ent;
2369
2370 VectorSubtract (ent->s.origin, self->mins, dest);
2371 self->moveinfo.state = STATE_TOP;
2372 VectorCopy (self->s.origin, self->moveinfo.start_origin);
2373 VectorCopy (dest, self->moveinfo.end_origin);
2374 Move_Calc (self, dest, train_wait);
2375 self->spawnflags |= TRAIN_START_ON;
2376 }
2377
func_train_find(edict_t * self)2378 void func_train_find (edict_t *self)
2379 {
2380 edict_t *ent;
2381
2382 if (!self->target)
2383 {
2384 gi.dprintf ("train_find: no target\n");
2385 return;
2386 }
2387 ent = G_PickTarget (self->target);
2388 if (!ent)
2389 {
2390 gi.dprintf ("train_find: target %s not found\n", self->target);
2391 return;
2392 }
2393 self->target = ent->target;
2394
2395 VectorSubtract (ent->s.origin, self->mins, self->s.origin);
2396 gi.linkentity (self);
2397
2398 // if not triggered, start immediately
2399 if (!self->targetname)
2400 self->spawnflags |= TRAIN_START_ON;
2401
2402 if (self->spawnflags & TRAIN_START_ON)
2403 {
2404 self->nextthink = level.time + FRAMETIME;
2405 self->think = train_next;
2406 self->activator = self;
2407 }
2408 }
2409
train_use(edict_t * self,edict_t * other,edict_t * activator)2410 void train_use (edict_t *self, edict_t *other, edict_t *activator)
2411 {
2412 self->activator = activator;
2413
2414 if (self->spawnflags & TRAIN_START_ON)
2415 {
2416 if (!(self->spawnflags & TRAIN_TOGGLE))
2417 return;
2418 self->spawnflags &= ~TRAIN_START_ON;
2419 VectorClear (self->velocity);
2420 self->nextthink = 0;
2421 }
2422 else
2423 {
2424 if (self->target_ent)
2425 train_resume(self);
2426 else
2427 train_next(self);
2428 }
2429 }
2430
SP_func_train(edict_t * self)2431 void SP_func_train (edict_t *self)
2432 {
2433 self->movetype = MOVETYPE_PUSH;
2434
2435 VectorClear (self->s.angles);
2436 self->blocked = train_blocked;
2437 if (self->spawnflags & TRAIN_BLOCK_STOPS)
2438 self->dmg = 0;
2439 else
2440 {
2441 if (!self->dmg)
2442 self->dmg = 100;
2443 }
2444 self->solid = SOLID_BSP;
2445 gi.setmodel (self, self->model);
2446
2447 if (st.noise)
2448 self->moveinfo.sound_middle = gi.soundindex (st.noise);
2449
2450 if (!self->speed)
2451 self->speed = 100;
2452
2453 self->moveinfo.speed = self->speed;
2454 self->moveinfo.accel = self->moveinfo.decel = self->moveinfo.speed;
2455
2456 self->use = train_use;
2457
2458 gi.linkentity (self);
2459
2460 if (self->target)
2461 {
2462 // start trains on the second frame, to make sure their targets have had
2463 // a chance to spawn
2464 self->nextthink = level.time + FRAMETIME;
2465 self->think = func_train_find;
2466 }
2467 else
2468 {
2469 gi.dprintf ("func_train without a target at %s\n", vtos(self->absmin));
2470 }
2471 }
2472
2473
2474 /*QUAKED trigger_elevator (0.3 0.1 0.6) (-8 -8 -8) (8 8 8)
2475 */
trigger_elevator_use(edict_t * self,edict_t * other,edict_t * activator)2476 void trigger_elevator_use (edict_t *self, edict_t *other, edict_t *activator)
2477 {
2478 edict_t *target;
2479
2480 if (self->movetarget->nextthink)
2481 {
2482 // gi.dprintf("elevator busy\n");
2483 return;
2484 }
2485
2486 if (!other->pathtarget)
2487 {
2488 gi.dprintf("elevator used with no pathtarget\n");
2489 return;
2490 }
2491
2492 target = G_PickTarget (other->pathtarget);
2493 if (!target)
2494 {
2495 gi.dprintf("elevator used with bad pathtarget: %s\n", other->pathtarget);
2496 return;
2497 }
2498
2499 self->movetarget->target_ent = target;
2500 train_resume (self->movetarget);
2501 }
2502
trigger_elevator_init(edict_t * self)2503 void trigger_elevator_init (edict_t *self)
2504 {
2505 if (!self->target)
2506 {
2507 gi.dprintf("trigger_elevator has no target\n");
2508 return;
2509 }
2510 self->movetarget = G_PickTarget (self->target);
2511 if (!self->movetarget)
2512 {
2513 gi.dprintf("trigger_elevator unable to find target %s\n", self->target);
2514 return;
2515 }
2516 if (strcmp(self->movetarget->classname, "func_train") != 0)
2517 {
2518 gi.dprintf("trigger_elevator target %s is not a train\n", self->target);
2519 return;
2520 }
2521
2522 self->use = trigger_elevator_use;
2523 self->svflags = SVF_NOCLIENT;
2524
2525 }
2526
SP_trigger_elevator(edict_t * self)2527 void SP_trigger_elevator (edict_t *self)
2528 {
2529 self->think = trigger_elevator_init;
2530 self->nextthink = level.time + FRAMETIME;
2531 }
2532
2533
2534 /*QUAKED func_timer (0.3 0.1 0.6) (-8 -8 -8) (8 8 8) START_ON
2535 "wait" base time between triggering all targets, default is 1
2536 "random" wait variance, default is 0
2537
2538 so, the basic time between firing is a random time between
2539 (wait - random) and (wait + random)
2540
2541 "delay" delay before first firing when turned on, default is 0
2542
2543 "pausetime" additional delay used only the very first time
2544 and only if spawned with START_ON
2545
2546 These can used but not touched.
2547 */
func_timer_think(edict_t * self)2548 void func_timer_think (edict_t *self)
2549 {
2550 G_UseTargets (self, self->activator);
2551 self->nextthink = level.time + self->wait + crandom() * self->random;
2552 }
2553
func_timer_use(edict_t * self,edict_t * other,edict_t * activator)2554 void func_timer_use (edict_t *self, edict_t *other, edict_t *activator)
2555 {
2556 self->activator = activator;
2557
2558 // if on, turn it off
2559 if (self->nextthink)
2560 {
2561 self->nextthink = 0;
2562 return;
2563 }
2564
2565 // turn it on
2566 if (self->delay)
2567 self->nextthink = level.time + self->delay;
2568 else
2569 func_timer_think (self);
2570 }
2571
SP_func_timer(edict_t * self)2572 void SP_func_timer (edict_t *self)
2573 {
2574 if (!self->wait)
2575 self->wait = 1.0;
2576
2577 self->use = func_timer_use;
2578 self->think = func_timer_think;
2579
2580 if (self->random >= self->wait)
2581 {
2582 self->random = self->wait - FRAMETIME;
2583 gi.dprintf("func_timer at %s has random >= wait\n", vtos(self->s.origin));
2584 }
2585
2586 if (self->spawnflags & 1)
2587 {
2588 self->nextthink = level.time + 1.0 + st.pausetime + self->delay + self->wait + crandom() * self->random;
2589 self->activator = self;
2590 }
2591
2592 self->svflags = SVF_NOCLIENT;
2593 }
2594
2595
2596 /*QUAKED func_conveyor (0 .5 .8) ? START_ON TOGGLE
2597 Conveyors are stationary brushes that move what's on them.
2598 The brush should be have a surface with at least one current content enabled.
2599 speed default 100
2600 */
2601
func_conveyor_use(edict_t * self,edict_t * other,edict_t * activator)2602 void func_conveyor_use (edict_t *self, edict_t *other, edict_t *activator)
2603 {
2604 if (self->spawnflags & 1)
2605 {
2606 self->speed = 0;
2607 self->spawnflags &= ~1;
2608 }
2609 else
2610 {
2611 self->speed = self->count;
2612 self->spawnflags |= 1;
2613 }
2614
2615 if (!(self->spawnflags & 2))
2616 self->count = 0;
2617 }
2618
SP_func_conveyor(edict_t * self)2619 void SP_func_conveyor (edict_t *self)
2620 {
2621 if (!self->speed)
2622 self->speed = 100;
2623
2624 if (!(self->spawnflags & 1))
2625 {
2626 self->count = self->speed;
2627 self->speed = 0;
2628 }
2629
2630 self->use = func_conveyor_use;
2631
2632 gi.setmodel (self, self->model);
2633 self->solid = SOLID_BSP;
2634 gi.linkentity (self);
2635 }
2636
2637
2638 /*QUAKED func_door_secret (0 .5 .8) ? always_shoot 1st_left 1st_down
2639 A secret door. Slide back and then to the side.
2640
2641 open_once doors never closes
2642 1st_left 1st move is left of arrow
2643 1st_down 1st move is down from arrow
2644 always_shoot door is shootebale even if targeted
2645
2646 "angle" determines the direction
2647 "dmg" damage to inflic when blocked (default 2)
2648 "wait" how long to hold in the open position (default 5, -1 means hold)
2649 */
2650
2651 #define SECRET_ALWAYS_SHOOT 1
2652 #define SECRET_1ST_LEFT 2
2653 #define SECRET_1ST_DOWN 4
2654
2655 void door_secret_move1 (edict_t *self);
2656 void door_secret_move2 (edict_t *self);
2657 void door_secret_move3 (edict_t *self);
2658 void door_secret_move4 (edict_t *self);
2659 void door_secret_move5 (edict_t *self);
2660 void door_secret_move6 (edict_t *self);
2661 void door_secret_done (edict_t *self);
2662
door_secret_use(edict_t * self,edict_t * other,edict_t * activator)2663 void door_secret_use (edict_t *self, edict_t *other, edict_t *activator)
2664 {
2665 // make sure we're not already moving
2666 if (!VectorCompare(self->s.origin, vec3_origin))
2667 return;
2668
2669 Move_Calc (self, self->pos1, door_secret_move1);
2670 door_use_areaportals (self, true);
2671 }
2672
door_secret_move1(edict_t * self)2673 void door_secret_move1 (edict_t *self)
2674 {
2675 self->nextthink = level.time + 1.0;
2676 self->think = door_secret_move2;
2677 }
2678
door_secret_move2(edict_t * self)2679 void door_secret_move2 (edict_t *self)
2680 {
2681 Move_Calc (self, self->pos2, door_secret_move3);
2682 }
2683
door_secret_move3(edict_t * self)2684 void door_secret_move3 (edict_t *self)
2685 {
2686 if (self->wait == -1)
2687 return;
2688 self->nextthink = level.time + self->wait;
2689 self->think = door_secret_move4;
2690 }
2691
door_secret_move4(edict_t * self)2692 void door_secret_move4 (edict_t *self)
2693 {
2694 Move_Calc (self, self->pos1, door_secret_move5);
2695 }
2696
door_secret_move5(edict_t * self)2697 void door_secret_move5 (edict_t *self)
2698 {
2699 self->nextthink = level.time + 1.0;
2700 self->think = door_secret_move6;
2701 }
2702
door_secret_move6(edict_t * self)2703 void door_secret_move6 (edict_t *self)
2704 {
2705 Move_Calc (self, vec3_origin, door_secret_done);
2706 }
2707
door_secret_done(edict_t * self)2708 void door_secret_done (edict_t *self)
2709 {
2710 if (!(self->targetname) || (self->spawnflags & SECRET_ALWAYS_SHOOT))
2711 {
2712 self->health = 0;
2713 self->takedamage = DAMAGE_YES;
2714 }
2715 door_use_areaportals (self, false);
2716 }
2717
door_secret_blocked(edict_t * self,edict_t * other)2718 void door_secret_blocked (edict_t *self, edict_t *other)
2719 {
2720 if (!(other->svflags & SVF_MONSTER) && (!other->client) )
2721 {
2722 // give it a chance to go away on it's own terms (like gibs)
2723 T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 100000, 1, 0, MOD_CRUSH);
2724 // if it's still there, nuke it
2725 if (other && other->inuse)
2726 BecomeExplosion1 (other);
2727 return;
2728 }
2729
2730 if (level.time < self->touch_debounce_time)
2731 return;
2732 self->touch_debounce_time = level.time + 0.5;
2733
2734 T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, 1, 0, MOD_CRUSH);
2735 }
2736
door_secret_die(edict_t * self,edict_t * inflictor,edict_t * attacker,int damage,vec3_t point)2737 void door_secret_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
2738 {
2739 self->takedamage = DAMAGE_NO;
2740 door_secret_use (self, attacker, attacker);
2741 }
2742
SP_func_door_secret(edict_t * ent)2743 void SP_func_door_secret (edict_t *ent)
2744 {
2745 vec3_t forward, right, up;
2746 float side;
2747 float width;
2748 float length;
2749
2750 ent->moveinfo.sound_start = gi.soundindex ("doors/dr1_strt.wav");
2751 ent->moveinfo.sound_middle = gi.soundindex ("doors/dr1_mid.wav");
2752 ent->moveinfo.sound_end = gi.soundindex ("doors/dr1_end.wav");
2753
2754 ent->movetype = MOVETYPE_PUSH;
2755 ent->solid = SOLID_BSP;
2756 gi.setmodel (ent, ent->model);
2757
2758 ent->blocked = door_secret_blocked;
2759 ent->use = door_secret_use;
2760
2761 if (!(ent->targetname) || (ent->spawnflags & SECRET_ALWAYS_SHOOT))
2762 {
2763 ent->health = 0;
2764 ent->takedamage = DAMAGE_YES;
2765 ent->die = door_secret_die;
2766 }
2767
2768 if (!ent->dmg)
2769 ent->dmg = 2;
2770
2771 if (!ent->wait)
2772 ent->wait = 5;
2773
2774 ent->moveinfo.accel =
2775 ent->moveinfo.decel =
2776 ent->moveinfo.speed = 50;
2777
2778 // calculate positions
2779 AngleVectors (ent->s.angles, forward, right, up);
2780 VectorClear (ent->s.angles);
2781 side = 1.0 - (ent->spawnflags & SECRET_1ST_LEFT);
2782 if (ent->spawnflags & SECRET_1ST_DOWN)
2783 width = fabs(DotProduct(up, ent->size));
2784 else
2785 width = fabs(DotProduct(right, ent->size));
2786 length = fabs(DotProduct(forward, ent->size));
2787 if (ent->spawnflags & SECRET_1ST_DOWN)
2788 VectorMA (ent->s.origin, -1 * width, up, ent->pos1);
2789 else
2790 VectorMA (ent->s.origin, side * width, right, ent->pos1);
2791 VectorMA (ent->pos1, length, forward, ent->pos2);
2792
2793 if (ent->health)
2794 {
2795 ent->takedamage = DAMAGE_YES;
2796 ent->die = door_killed;
2797 ent->max_health = ent->health;
2798 }
2799 else if (ent->targetname && ent->message)
2800 {
2801 gi.soundindex ("misc/talk.wav");
2802 ent->touch = door_touch;
2803 }
2804
2805 ent->classname = "func_door";
2806
2807 gi.linkentity (ent);
2808 }
2809
2810
2811 /*QUAKED func_killbox (1 0 0) ?
2812 Kills everything inside when fired, irrespective of protection.
2813 */
use_killbox(edict_t * self,edict_t * other,edict_t * activator)2814 void use_killbox (edict_t *self, edict_t *other, edict_t *activator)
2815 {
2816 KillBox (self);
2817 }
2818
SP_func_killbox(edict_t * ent)2819 void SP_func_killbox (edict_t *ent)
2820 {
2821 gi.setmodel (ent, ent->model);
2822 ent->use = use_killbox;
2823 ent->svflags = SVF_NOCLIENT;
2824 }
2825
2826