-void SUB_Null() {}
-float SUB_True() { return 1; }
-float SUB_False() { return 0; }
+void SUB_NullThink(void) { }
-void(vector destangle, float tspeed, void() func) SUB_CalcAngleMove;
void() SUB_CalcMoveDone;
void() SUB_CalcAngleMoveDone;
//void() SUB_UseTargets;
void setanim(entity e, vector anim, float looping, float override, float restart)
{
if (!anim)
- return; // no animation was given to us! We can't use this.
-
+ return; // no animation was given to us! We can't use this.
+
if (anim_x == e.animstate_startframe)
if (anim_y == e.animstate_numframes)
if (anim_z == e.animstate_framerate)
//print(ftos(time), " -> ", ftos(e.frame), "\n");
}
-vector animfixfps(entity e, vector a)
-{
- // multi-frame anim: keep as-is
- if(a_y == 1)
- {
- float dur;
- dur = frameduration(e.modelindex, a_x);
- if(dur > 0)
- a_z = 1.0 / dur;
- }
- return a;
-}
-
/*
==================
SUB_Remove
*/
void SUB_VanishOrRemove (entity ent)
{
- if (ent.flags & FL_CLIENT)
+ if (IS_CLIENT(ent))
{
// vanish
ent.alpha = -1;
*/
void SUB_SetFade (entity ent, float when, float fadetime)
{
- //if (ent.flags & FL_CLIENT) // && ent.deadflag != DEAD_NO)
- // return;
- //ent.alpha = 1;
ent.fade_rate = 1/fadetime;
ent.think = SUB_SetFade_Think;
ent.nextthink = when;
self.think1 ();
}
+.float platmovetype_turn;
void SUB_CalcMove_controller_think (void)
{
entity oldself;
float phasepos;
float nexttick;
vector delta;
+ vector delta2;
vector veloc;
+ vector angloc;
vector nextpos;
+ delta = self.destvec;
+ delta2 = self.destvec2;
if(time < self.animstate_endtime) {
- delta = self.destvec;
nexttick = time + sys_frametime;
- if(nexttick < self.animstate_endtime) {
- traveltime = self.animstate_endtime - self.animstate_starttime;
- phasepos = (nexttick - self.animstate_starttime) / traveltime; // range: [0, 1]
- phasepos = 3.14159265 + (phasepos * 3.14159265); // range: [pi, 2pi]
- phasepos = cos(phasepos); // cos [pi, 2pi] is in [-1, 1]
- phasepos = phasepos + 1; // correct range to [0, 2]
- phasepos = phasepos / 2; // correct range to [0, 1]
- nextpos = self.origin + (delta * phasepos);
+ traveltime = self.animstate_endtime - self.animstate_starttime;
+ phasepos = (nexttick - self.animstate_starttime) / traveltime; // range: [0, 1]
+ phasepos = cubic_speedfunc(self.platmovetype_start, self.platmovetype_end, phasepos);
+ nextpos = self.origin + (delta * phasepos) + (delta2 * phasepos * phasepos);
+ // derivative: delta + 2 * delta2 * phasepos (e.g. for angle positioning)
+ if(self.owner.platmovetype_turn)
+ {
+ vector destangle;
+ destangle = delta + 2 * delta2 * phasepos;
+ destangle = vectoangles(destangle);
+ destangle_x = -destangle_x; // flip up / down orientation
+
+ // take the shortest distance for the angles
+ self.owner.angles_x -= 360 * floor((self.owner.angles_x - destangle_x) / 360 + 0.5);
+ self.owner.angles_y -= 360 * floor((self.owner.angles_y - destangle_y) / 360 + 0.5);
+ self.owner.angles_z -= 360 * floor((self.owner.angles_z - destangle_z) / 360 + 0.5);
+ angloc = destangle - self.owner.angles;
+ angloc = angloc * (1 / sys_frametime); // so it arrives for the next frame
+ self.owner.avelocity = angloc;
+ }
+ if(nexttick < self.animstate_endtime)
veloc = nextpos - self.owner.origin;
- veloc = veloc * (1 / sys_frametime); // so it arrives for the next frame
-
- } else {
+ else
veloc = self.finaldest - self.owner.origin;
- veloc = veloc * (1 / sys_frametime); // so it arrives for the next frame
- }
+ veloc = veloc * (1 / sys_frametime); // so it arrives for the next frame
+
self.owner.velocity = veloc;
self.nextthink = nexttick;
} else {
+ // derivative: delta + 2 * delta2 (e.g. for angle positioning)
oldself = self;
self.owner.think = self.think1;
self = self.owner;
}
}
-void SUB_CalcMove (vector tdest, float tspeed, void() func)
+void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector control, vector dest)
+{
+ // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + dest * t * t
+ // 2 * control * t - 2 * control * t * t + dest * t * t
+ // 2 * control * t + (dest - 2 * control) * t * t
+
+ controller.origin = org; // starting point
+ control -= org;
+ dest -= org;
+
+ controller.destvec = 2 * control; // control point
+ controller.destvec2 = dest - 2 * control; // quadratic part required to reach end point
+ // also: initial d/dphasepos origin = 2 * control, final speed = 2 * (dest - control)
+}
+
+void SUB_CalcMove_controller_setlinear (entity controller, vector org, vector dest)
+{
+ // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + dest * t * t
+ // 2 * control * t - 2 * control * t * t + dest * t * t
+ // 2 * control * t + (dest - 2 * control) * t * t
+
+ controller.origin = org; // starting point
+ dest -= org;
+
+ controller.destvec = dest; // end point
+ controller.destvec2 = '0 0 0';
+}
+
+float TSPEED_TIME = -1;
+float TSPEED_LINEAR = 0;
+float TSPEED_START = 1;
+float TSPEED_END = 2;
+// TODO average too?
+
+void SUB_CalcMove_Bezier (vector tcontrol, vector tdest, float tspeedtype, float tspeed, void() func)
{
- vector delta;
float traveltime;
entity controller;
self.finaldest = tdest;
self.think = SUB_CalcMoveDone;
- if (tdest == self.origin)
+ switch(tspeedtype)
{
- self.velocity = '0 0 0';
- self.nextthink = self.ltime + 0.1;
- return;
+ default:
+ case TSPEED_START:
+ traveltime = 2 * vlen(tcontrol - self.origin) / tspeed;
+ break;
+ case TSPEED_END:
+ traveltime = 2 * vlen(tcontrol - tdest) / tspeed;
+ break;
+ case TSPEED_LINEAR:
+ traveltime = vlen(tdest - self.origin) / tspeed;
+ break;
+ case TSPEED_TIME:
+ traveltime = tspeed;
+ break;
}
- delta = tdest - self.origin;
- traveltime = vlen (delta) / tspeed;
-
- if (traveltime < 0.1)
+ if (traveltime < 0.1) // useless anim
{
self.velocity = '0 0 0';
self.nextthink = self.ltime + 0.1;
return;
}
- // Very short animations don't really show off the effect
- // of controlled animation, so let's just use linear movement.
- // Alternatively entities can choose to specify non-controlled movement.
- // The only currently implemented alternative movement is linear (value 1)
- if (traveltime < 0.15 || self.platmovetype == 1)
- {
- self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
- self.nextthink = self.ltime + traveltime;
- return;
- }
-
controller = spawn();
controller.classname = "SUB_CalcMove_controller";
controller.owner = self;
- controller.origin = self.origin; // starting point
+ controller.platmovetype = self.platmovetype;
+ controller.platmovetype_start = self.platmovetype_start;
+ controller.platmovetype_end = self.platmovetype_end;
+ SUB_CalcMove_controller_setbezier(controller, self.origin, tcontrol, tdest);
controller.finaldest = (tdest + '0 0 0.125'); // where do we want to end? Offset to overshoot a bit.
- controller.destvec = delta;
controller.animstate_starttime = time;
controller.animstate_endtime = time + traveltime;
controller.think = SUB_CalcMove_controller_think;
controller.think1 = self.think;
// the thinking is now done by the controller
- self.think = SUB_Null;
+ self.think = SUB_NullThink; // for PushMove
self.nextthink = self.ltime + traveltime;
-
+
// invoke controller
self = controller;
self.think();
self = self.owner;
}
-void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func)
+void SUB_CalcMove (vector tdest, float tspeedtype, float tspeed, void() func)
+{
+ vector delta;
+ float 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;
+ }
+
+ delta = tdest - self.origin;
+
+ switch(tspeedtype)
+ {
+ default:
+ case TSPEED_START:
+ case TSPEED_END:
+ case TSPEED_LINEAR:
+ traveltime = vlen (delta) / tspeed;
+ break;
+ case TSPEED_TIME:
+ traveltime = tspeed;
+ break;
+ }
+
+ // Very short animations don't really show off the effect
+ // of controlled animation, so let's just use linear movement.
+ // Alternatively entities can choose to specify non-controlled movement.
+ // The only currently implemented alternative movement is linear (value 1)
+ if (traveltime < 0.15 || (self.platmovetype_start == 1 && self.platmovetype_end == 1)) // is this correct?
+ {
+ self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
+ self.nextthink = self.ltime + traveltime;
+ return;
+ }
+
+ // now just run like a bezier curve...
+ SUB_CalcMove_Bezier((self.origin + tdest) * 0.5, tdest, tspeedtype, tspeed, func);
+}
+
+void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeedtype, float tspeed, void() func)
{
entity oldself;
oldself = self;
self = ent;
- SUB_CalcMove (tdest, tspeed, func);
+ SUB_CalcMove (tdest, tspeedtype, tspeed, func);
self = oldself;
}
}
// FIXME: I fixed this function only for rotation around the main axes
-void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)
+void SUB_CalcAngleMove (vector destangle, float tspeedtype, float tspeed, void() func)
{
vector delta;
float traveltime;
self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
delta = destangle - self.angles;
- traveltime = vlen (delta) / tspeed;
+
+ switch(tspeedtype)
+ {
+ default:
+ case TSPEED_START:
+ case TSPEED_END:
+ case TSPEED_LINEAR:
+ traveltime = vlen (delta) / tspeed;
+ break;
+ case TSPEED_TIME:
+ traveltime = tspeed;
+ break;
+ }
self.think1 = func;
self.finalangle = destangle;
self.nextthink = self.ltime + traveltime;
}
-void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeed, void() func)
+void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeedtype, float tspeed, void() func)
{
entity oldself;
oldself = self;
self = ent;
- SUB_CalcAngleMove (destangle, tspeed, func);
+ SUB_CalcAngleMove (destangle, tspeedtype, tspeed, func);
self = oldself;
}
// check whether antilagged traces are enabled
if (lag < 0.001)
lag = 0;
- if (clienttype(forent) != CLIENTTYPE_REAL)
+ if (!IS_REAL_CLIENT(forent))
lag = 0; // only antilag for clients
// change shooter to SOLID_BBOX so the shot can hit corpses
tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, TRUE);
}
-float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent) // returns the number of traces done, for benchmarking
+float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent, float stopatentity) // returns the number of traces done, for benchmarking
{
vector pos, dir, t;
float nudge;
+ entity stopentity;
//nudge = 2 * cvar("collision_impactnudge"); // why not?
nudge = 0.5;
dprint(" trace distance is ", ftos(vlen(pos - trace_endpos)), "\n");
}
+ stopentity = trace_ent;
+
if(trace_startsolid)
{
// we started inside solid.
// t is still inside solid? bad
// force advance, then, and retry
pos = t + dir * nudge;
+
+ // but if we hit an entity, stop RIGHT before it
+ if(stopatentity && stopentity)
+ {
+ trace_ent = stopentity;
+ trace_endpos = t;
+ trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
+ return c;
+ }
}
else
{
}
}
-void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent)
+void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent, float stopatentity)
{
-#if 0
- vector pos, dir, t;
- float nudge;
-
- //nudge = 2 * cvar("collision_impactnudge"); // why not?
- nudge = 0.5;
-
- dir = normalize(v2 - v1);
-
- pos = v1 + dir * nudge;
-
- for(;;)
- {
- if((pos - v1) * dir >= (v2 - v1) * dir)
- {
- // went too far
- trace_fraction = 1;
- return;
- }
-
- traceline(pos, v2, nomonsters, forent);
-
- if(trace_startsolid)
- {
- // we started inside solid.
- // then trace from endpos to pos
- t = trace_endpos;
- traceline(t, pos, nomonsters, forent);
- if(trace_startsolid)
- {
- // t is inside solid? bad
- // force advance, then
- pos = pos + dir * nudge;
- }
- else
- {
- // we actually LEFT solid!
- trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
- return;
- }
- }
- else
- {
- // pos is outside solid?!? but why?!? never mind, just return it.
- trace_endpos = pos;
- trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
- return;
- }
- }
-#else
- tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent);
+ tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent, stopatentity);
}
/*
}
if(self.lodmodelindex1)
- if not(self.SendEntity)
+ if (!self.SendEntity)
SetCustomizer(self, LOD_customize, LOD_uncustomize);
}
if(self.model != "")
{
precache_model(self.model);
- setmodel(self, self.model); // no precision needed
+ if(self.mins != '0 0 0' || self.maxs != '0 0 0')
+ {
+ vector mi = self.mins;
+ vector ma = self.maxs;
+ setmodel(self, self.model); // no precision needed
+ setsize(self, mi, ma);
+ }
+ else
+ setmodel(self, self.model); // no precision needed
InitializeEntity(self, LODmodel_attach, INITPRIO_FINDTARGET);
}
setorigin(self, self.origin);
if(self.model != "")
{
precache_model(self.model);
- setmodel(self, self.model); // no precision needed
+ if(self.mins != '0 0 0' || self.maxs != '0 0 0')
+ {
+ vector mi = self.mins;
+ vector ma = self.maxs;
+ setmodel(self, self.model); // no precision needed
+ setsize(self, mi, ma);
+ }
+ else
+ setmodel(self, self.model); // no precision needed
}
setorigin(self, self.origin);
ApplyMinMaxScaleAngles(self);