first commit
This commit is contained in:
551
mod_mine/quakec_src/subs.qc
Normal file
551
mod_mine/quakec_src/subs.qc
Normal file
@@ -0,0 +1,551 @@
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user