123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- /* Copyright (C) 1996-2022 id Software LLC
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- See file, 'COPYING', for details.
- */
- void() SUB_Null = {};
- void() SUB_Remove = {remove(self);};
- /*
- QuakeEd only writes a single float for angles (bad idea), so up and down are
- just constant angles.
- */
- void() SetMovedir =
- {
- if (self.angles == '0 -1 0')
- self.movedir = '0 0 1';
- else if (self.angles == '0 -2 0')
- self.movedir = '0 0 -1';
- else
- {
- makevectors(self.angles);
- self.movedir = v_forward;
- }
-
- self.angles = '0 0 0';
- };
- /*
- ================
- InitTrigger
- ================
- */
- void() InitTrigger =
- {
- // trigger angles are used for one-way touches. An angle of 0 is assumed
- // to mean no restrictions, so use a yaw of 360 instead.
- if (self.angles != '0 0 0')
- SetMovedir ();
- self.solid = SOLID_TRIGGER;
- setmodel (self, self.model); // set size and link into world
- self.movetype = MOVETYPE_NONE;
- self.modelindex = 0;
- self.model = string_null;
- };
- /*
- =============
- SUB_CalcMove
- calculate self.velocity and self.nextthink to reach dest from
- self.origin traveling at speed
- ===============
- */
- void(entity ent, vector tdest, float tspeed, void() func) SUB_CalcMoveEnt =
- {
- local entity stemp;
- stemp = self;
- self = ent;
- SUB_CalcMove (tdest, tspeed, func);
- self = stemp;
- };
- void(vector tdest, float tspeed, void() func) SUB_CalcMove =
- {
- local vector vdestdelta;
- local float len, traveltime;
- if (!tspeed)
- objerror("No speed is defined!");
- self.think1 = func;
- self.finaldest = tdest;
- self.think = SUB_CalcMoveDone;
- if (tdest == self.origin)
- {
- self.velocity = '0 0 0';
- self.nextthink = self.ltime + 0.1;
- return;
- }
-
- // set destdelta to the vector needed to move
- vdestdelta = tdest - self.origin;
-
- // calculate length of vector
- len = vlen (vdestdelta);
-
- // divide by speed to get time to reach dest
- traveltime = len / tspeed;
- if (traveltime < 0.1)
- {
- self.velocity = '0 0 0';
- self.nextthink = self.ltime + 0.1;
- return;
- }
-
- // set nextthink to trigger a think when dest is reached
- self.nextthink = self.ltime + traveltime;
- // scale the destdelta vector by the time spent traveling to get velocity
- self.velocity = vdestdelta * (1/traveltime); // qcc won't take vec/float
- };
- /*
- ============
- After moving, set origin to exact final destination
- ============
- */
- void() SUB_CalcMoveDone =
- {
- setorigin(self, self.finaldest);
- self.velocity = '0 0 0';
- self.nextthink = -1;
- if (self.think1)
- self.think1();
- };
- /*
- =============
- SUB_CalcAngleMove
- calculate self.avelocity and self.nextthink to reach destangle from
- self.angles rotating
- The calling function should make sure self.think is valid
- ===============
- */
- void(entity ent, vector destangle, float tspeed, void() func) SUB_CalcAngleMoveEnt =
- {
- local entity stemp;
- stemp = self;
- self = ent;
- SUB_CalcAngleMove (destangle, tspeed, func);
- self = stemp;
- };
- void(vector destangle, float tspeed, void() func) SUB_CalcAngleMove =
- {
- local vector destdelta;
- local float len, traveltime;
- if (!tspeed)
- objerror("No speed is defined!");
-
- // set destdelta to the vector needed to move
- destdelta = destangle - self.angles;
-
- // calculate length of vector
- len = vlen (destdelta);
-
- // divide by speed to get time to reach dest
- traveltime = len / tspeed;
- // set nextthink to trigger a think when dest is reached
- self.nextthink = self.ltime + traveltime;
- // scale the destdelta vector by the time spent traveling to get velocity
- self.avelocity = destdelta * (1 / traveltime);
-
- self.think1 = func;
- self.finalangle = destangle;
- self.think = SUB_CalcAngleMoveDone;
- };
- /*
- ============
- After rotating, set angle to exact final angle
- ============
- */
- void() SUB_CalcAngleMoveDone =
- {
- self.angles = self.finalangle;
- self.avelocity = '0 0 0';
- self.nextthink = -1;
- if (self.think1)
- self.think1();
- };
- //=============================================================================
- void() DelayThink =
- {
- activator = self.enemy;
- SUB_UseTargets ();
- remove(self);
- };
- /*
- ==============================
- SUB_UseTargets
- the global "activator" should be set to the entity that initiated the firing.
- If self.delay is set, a DelayedUse entity will be created that will actually
- do the SUB_UseTargets after that many seconds have passed.
- Centerprints any self.message to the activator.
- Removes all entities with a targetname that match self.killtarget,
- and removes them, so some events can remove other triggers.
- Search for (string)targetname in all entities that
- match (string)self.target and call their .use function
- ==============================
- */
- void() SUB_UseTargets =
- {
- local entity t, act, stemp, otemp;
- //
- // check for a delay
- //
- if (self.delay)
- {
- // create a temp object to fire at a later time
- t = spawn();
- t.classname = "DelayedUse";
- t.nextthink = time + self.delay;
- t.think = DelayThink;
- t.enemy = activator;
- t.message = self.message;
- t.killtarget = self.killtarget;
- t.target = self.target;
- return;
- }
-
-
- //
- // print the message
- //
- if (activator.classname == "player" && self.message != string_null)
- {
- centerprint (activator, self.message);
- if (!self.noise)
- sound (activator, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NORM);
- }
- //
- // kill the killtagets
- //
- if (self.killtarget != string_null)
- {
- t = find (world, targetname, self.killtarget);
-
- while( t )
- {
- remove (t);
- t = find (t, targetname, self.killtarget);
- }
- }
-
- //
- // fire targets
- //
- if (self.target != string_null)
- {
- act = activator;
- t = find (world, targetname, self.target);
- while( t )
- {
- stemp = self;
- otemp = other;
- self = t;
- other = stemp;
- if (self.use != SUB_Null)
- {
- if (self.use)
- self.use ();
- }
- self = stemp;
- other = otemp;
- activator = act;
- t = find (t, targetname, self.target);
- }
- }
- };
- /*
- in nightmare mode, all attack_finished times become 0
- some monsters refire twice automatically
- update: not anymore! it makes nightmare too easy
- */
- void(float normal) SUB_AttackFinished =
- {
- self.cnt = 0; // refire count for nightmare
- //if (skill != 3)
- self.attack_finished = time + normal;
- };
- float (entity targ) visible;
- void (void() thinkst) SUB_CheckRefire =
- {
- if (skill != 3)
- return;
- if (self.cnt == 1)
- return;
- if (!visible (self.enemy))
- return;
- self.cnt = 1;
- self.think = thinkst;
- };
|