Files
quakemapping/mod_progdump/development/quakec src/subs.qc
2019-12-30 17:23:05 +01:00

552 lines
12 KiB
Plaintext

void() SUB_Null = {};
void(entity attacker, float damage) SUB_NullPain = {};
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 = "";
};
/*
=============
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);
};
/* ### (added targets, killtargets, and targetnames)
==============================
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,
or killtarget2, so some events can remove other triggers.
Search for (string)targetname, targetname2, targetname3, and targetname 4
in all entities that match (string)self.target, self.target2, self.target3,
or self.target4 and use their .use function.
==============================
*/
void(string matchstring, .string matchfield) SUB_UseSpecificTarget =
{
local entity t, stemp, otemp, act;
act = activator;
t = find (world, matchfield, matchstring);
while ( t != world )
{
stemp = self;
otemp = other;
self = t;
other = stemp;
if (self.use != SUB_Null)
{
if (self.use)
{
lastnameused = matchstring;
self.use ();
}
}
self = stemp;
other = otemp;
activator = act;
t = find (t, matchfield, matchstring);
}
};
void() SUB_UseTargets =
{
// local entity t, stemp, otemp, act;
local entity t;
//
// 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.killtarget2 = self.killtarget2;
t.target = self.target;
t.target2 = self.target2;
t.target3 = self.target3;
t.target4 = self.target4;
return;
}
//
// print the message
//
if (activator.classname == "player" && self.message != "")
{
centerprint (activator, self.message);
if (!self.noise)
sound (activator, CHAN_VOICE, "misc/talk.wav", 1, ATTN_NORM);
}
//
// kill the killtarget entities
//
if (self.killtarget != "")
{
t = find(world, targetname, self.killtarget);
while(t != world)
{
remove(t);
t = find(t, targetname, self.killtarget);
}
t = find(world, targetname2, self.killtarget);
while(t != world)
{
remove(t);
t = find(t, targetname2, self.killtarget);
}
t = find(world, targetname3, self.killtarget);
while(t != world)
{
remove(t);
t = find(t, targetname3, self.killtarget);
}
t = find(world, targetname4, self.killtarget);
while(t != world)
{
remove(t);
t = find(t, targetname4, self.killtarget);
}
}
//
// kill the killtaget2 entities
//
if (self.killtarget2 != "")
{
t = find(world, targetname, self.killtarget2);
while(t != world)
{
remove(t);
t = find(t, targetname, self.killtarget2);
}
t = find(world, targetname2, self.killtarget2);
while(t != world)
{
remove(t);
t = find(t, targetname2, self.killtarget2);
}
t = find(world, targetname3, self.killtarget2);
while(t != world)
{
remove(t);
t = find(t, targetname3, self.killtarget2);
}
t = find(world, targetname4, self.killtarget2);
while(t != world)
{
remove(t);
t = find(t, targetname4, self.killtarget2);
}
}
//
// fire targets
//
// target 1
if (self.target != "")
{
SUB_UseSpecificTarget(self.target, targetname);
SUB_UseSpecificTarget(self.target, targetname2);
SUB_UseSpecificTarget(self.target, targetname3);
SUB_UseSpecificTarget(self.target, targetname4);
}
// target 2
if (self.target2 != "")
{
SUB_UseSpecificTarget(self.target2, targetname);
SUB_UseSpecificTarget(self.target2, targetname2);
SUB_UseSpecificTarget(self.target2, targetname3);
SUB_UseSpecificTarget(self.target2, targetname4);
}
// target 3
if (self.target3 != "")
{
SUB_UseSpecificTarget(self.target3, targetname);
SUB_UseSpecificTarget(self.target3, targetname2);
SUB_UseSpecificTarget(self.target3, targetname3);
SUB_UseSpecificTarget(self.target3, targetname4);
}
// target 4
if (self.target4 != "")
{
SUB_UseSpecificTarget(self.target4, targetname);
SUB_UseSpecificTarget(self.target4, targetname2);
SUB_UseSpecificTarget(self.target4, targetname3);
SUB_UseSpecificTarget(self.target4, targetname4);
}
};
void(string matchstring) SUB_UseName =
{
SUB_UseSpecificTarget(matchstring, targetname);
SUB_UseSpecificTarget(matchstring, targetname2);
SUB_UseSpecificTarget(matchstring, targetname3);
SUB_UseSpecificTarget(matchstring, targetname4);
};
// ### end of Custents triggering code
/*
================
SUB_UseAndForgetTargets
This calls SUB_UseTargets, then clears all of self's fields that cause
SUB_UseTargets to do things. The intention is that if SUB_UseTargets
gets called again in the future, it won't do anything. Call this
function if you want an entity to fire its targets just this once, and
never again.
Note that this function relies on the fact that SUB_UseTargets has
already been modified to work around the engine bug involving tests of
the form 'if (string)', i.e. that the tests in SUB_UseTargets are now of
the form 'if (string != "")'. -- iw
================
*/
void() SUB_UseAndForgetTargets =
{
SUB_UseTargets ();
self.delay = 0;
self.killtarget = "";
self.killtarget2 = "";
self.message = "";
self.target = "";
self.target2 = "";
self.target3 = "";
self.target4 = "";
};
/*
================
SUB_FieldIsTargeted
Return TRUE if the "fld" field of this entity is non-empty and matches
the target (or target2/3/4 or pain_target) field of any other entity,
otherwise return FALSE. -- iw
================
*/
float(.string fld) SUB_FieldIsTargeted =
{
if (self.fld == "")
return FALSE;
// the following function calls are staggered to avoid the silly
// "return value conflict" problem with traditional compilers -- iw
if (find (world, target, self.fld) != world)
return TRUE;
if (find (world, target2, self.fld) != world)
return TRUE;
if (find (world, target3, self.fld) != world)
return TRUE;
if (find (world, target4, self.fld) != world)
return TRUE;
if (find (world, pain_target, self.fld) != world)
return TRUE;
return FALSE;
};
/*
================
SUB_IsTargeted
Return TRUE if the targetname (or targetname2/3/4) field of this entity
is non-empty and matches the target (or target2/3/4 or pain_target)
field of any other entity, otherwise return FALSE. -- iw
================
*/
float() SUB_IsTargeted =
{
// the following function calls are staggered to avoid the silly
// "return value conflict" problem with traditional compilers -- iw
if (SUB_FieldIsTargeted (targetname))
return TRUE;
if (SUB_FieldIsTargeted (targetname2))
return TRUE;
if (SUB_FieldIsTargeted (targetname3))
return TRUE;
if (SUB_FieldIsTargeted (targetname4))
return TRUE;
return FALSE;
};
//
// pain_target //dumptruck_ds
//
void() SUB_UsePain =
{
if (self.pain_target != "")
{
SUB_UseSpecificTarget (self.pain_target, targetname);
SUB_UseSpecificTarget (self.pain_target, targetname2);
SUB_UseSpecificTarget (self.pain_target, targetname3);
SUB_UseSpecificTarget (self.pain_target, targetname4);
}
self.pain_target = ""; //dumptruck_ds via Discord - thanks Spike, Snaut and QueenJazz
};
/*
in nightmare mode, all attack_finished times become 0
some monsters refire twice automatically
*/
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;
};
/*
================
SUB_DislodgeRestingEntities
This clears the FL_ONGROUND flag from any entities that are on top of
self.
The engine does not update the FL_ONGROUND flag automatically in some
cases, with the result that certain types of entities can be left
floating in mid-air if the entity they are resting on is removed from
under them. This function is intended to be called in the case where
self is going to be removed, to ensure that other entities are not left
floating. -- iw
================
*/
void() SUB_DislodgeRestingEntities =
{
local entity e;
e = nextent (world);
while (e != world)
{
if ((e.flags & FL_ONGROUND) && e.groundentity == self)
e.flags = e.flags - (e.flags & FL_ONGROUND);
e = nextent (e);
}
};