NSEntity: new methods to simplify a lot of common tasks added. ScheduleThink(void(), float) and WithinBounds(entity) will surely be helpful.

Went over gs-entbase/server and made sure to use the new Think handlers to safely deal with MOVETYPE_PUSH
and handle any fun edge cases where time may be 0.0f.
func_breakable has also been fixed, since the changed Show/Hide behaviour prevented
them from getting destroyed fully.
This commit is contained in:
Marco Cawthorne 2022-08-26 15:39:00 -07:00
parent acaa918403
commit 7a252ad213
Signed by: eukara
GPG key ID: CE2032F0A2882A22
41 changed files with 380 additions and 343 deletions

View file

@ -144,13 +144,12 @@ env_beverage::Trigger(entity act, int unused)
return; return;
} }
entity eCan = spawn(); NSEntity eCan = spawn(NSEntity);
setorigin(eCan, origin); eCan.SetOrigin(GetOrigin());
eCan.angles = angles; eCan.SetAngles(GetAngles());
eCan.owner = this; eCan.SetOwner(this);
eCan.think = item_sodacan; eCan.ScheduleThink(item_sodacan, 0.0f);
eCan.nextthink = time; eCan.SetSkin((float)m_sodaSkin);
eCan.skin = m_sodaSkin;
m_iUses--; m_iUses--;
m_bReady = false; m_bReady = false;

View file

@ -69,7 +69,7 @@ env_laser:NSPointTrigger
virtual void(string,string) Restore; virtual void(string,string) Restore;
virtual void(string, string) SpawnKey; virtual void(string, string) SpawnKey;
virtual void(void) Respawn; virtual void(void) Respawn;
virtual void(void) think; virtual void(void) LaserThink;
virtual void(entity, int) Trigger; virtual void(entity, int) Trigger;
virtual void(void) EvaluateEntity; virtual void(void) EvaluateEntity;
virtual float(entity, float) SendEntity; virtual float(entity, float) SendEntity;
@ -154,26 +154,26 @@ env_laser::Respawn(void)
{ {
if (HasSpawnFlags(ENVLAZ_STARTON)) { if (HasSpawnFlags(ENVLAZ_STARTON)) {
m_iState = 1; m_iState = 1;
nextthink = time + 0.1; ScheduleThink(LaserThink, 0.0f);
} }
} }
void void
env_laser::think(void) env_laser::LaserThink(void)
{ {
entity t; entity t;
if (!m_iState) { if (!m_iState) {
return; return;
} else { } else {
nextthink = time + 0.1; ScheduleThink(LaserThink, 0.1f);
} }
t = find(world, ::targetname, m_strLaserDest); t = find(world, ::targetname, m_strLaserDest);
angles = t.origin; SetAngles(t.origin);
if (!t) { if (!t) {
print(sprintf("^1env_laser::^3think^7: %s has no valid target. Aborting\n", targetname)); print(sprintf("^1env_laser::^3LaserThink^7: %s has no valid target. Aborting\n", targetname));
return; return;
} }
@ -199,9 +199,9 @@ env_laser::Trigger(entity act, int state)
} }
if (m_iState) { if (m_iState) {
nextthink = time; ScheduleThink(LaserThink, 0.0f);
} else { } else {
nextthink = 0.25; ReleaseThink();
} }
} }

View file

@ -178,33 +178,35 @@ env_shooter::Respawn(void)
} }
m_iGibsLeft = m_iGibs; m_iGibsLeft = m_iGibs;
think = __NULL__; ReleaseThink();
} }
void void
env_shooter::ShootGib(void) env_shooter::ShootGib(void)
{ {
static void Gib_Remove(void) { remove(self); } vector vecSpinVel = [0.0f, 0.0f, 0.0f];
vector vecThrowVel = [0.0f, 0.0f, 0.0f];
entity eGib = spawn(); NSRenderableEntity eGib = spawn(NSRenderableEntity);
eGib.movetype = MOVETYPE_BOUNCE; eGib.SetMovetype(MOVETYPE_BOUNCE);
setmodel(eGib, m_strShootModel); eGib.SetModel(m_strShootModel);
setorigin(eGib, origin); eGib.SetOrigin(GetOrigin());
eGib.SetAngles(GetAngles());
makevectors(angles); makevectors(GetAngles());
eGib.velocity = v_forward * m_flVelocity + [0,0,64 + (random()*64)]; vecThrowVel = v_forward * m_flVelocity;
eGib.avelocity[0] = random(-1,1) * 32; vecThrowVel += [0.0f, 0.0f, 64.0f + (random() * 64.0f)];
eGib.avelocity[1] = random(-1,1) * 32; vecSpinVel[0] = random(-1,1) * 32;
eGib.avelocity[2] = random(-1,1) * 32; vecSpinVel[1] = random(-1,1) * 32;
vecSpinVel[2] = random(-1,1) * 32;
eGib.think = Gib_Remove; eGib.SetVelocity(vecThrowVel);
eGib.nextthink = time + m_flGibLife; eGib.SetAngularVelocity(vecSpinVel);
eGib.angles = angles; eGib.ScheduleThink(Destroy, m_flGibLife);
m_iGibsLeft--; m_iGibsLeft--;
if (m_iGibsLeft) { if (m_iGibsLeft) {
nextthink = time + m_flVariance; ScheduleThink(ShootGib, m_flVariance);
} }
} }
@ -213,18 +215,16 @@ env_shooter::Trigger(entity act, int state)
{ {
switch (state) { switch (state) {
case TRIG_OFF: case TRIG_OFF:
think = __NULL__; ReleaseThink();
nextthink = 0.0f;
break; break;
case TRIG_ON: case TRIG_ON:
if (spawnflags & EVSHOOTER_REPEATABLE) if (spawnflags & EVSHOOTER_REPEATABLE)
m_iGibsLeft = m_iGibs; m_iGibsLeft = m_iGibs;
think = ShootGib; ScheduleThink(ShootGib, m_flVariance);
nextthink = time + m_flVariance;
break; break;
default: default:
if (think == __NULL__) if (IsThinking() == false)
Trigger(act, TRIG_ON); Trigger(act, TRIG_ON);
else else
Trigger(act, TRIG_OFF); Trigger(act, TRIG_OFF);

View file

@ -131,15 +131,13 @@ env_spark::Trigger(entity act, int state)
switch (state) { switch (state) {
case TRIG_OFF: case TRIG_OFF:
think = __NULL__; ReleaseThink();
nextthink = 0;
break; break;
case TRIG_ON: case TRIG_ON:
think = TimedSpark; ScheduleThink(CreateSpark, (random() * m_flMaxDelay));
nextthink = time + (random() * m_flMaxDelay);
break; break;
default: default:
if (think != __NULL__) { if (IsThinking() == true) {
Trigger(act, TRIG_OFF); Trigger(act, TRIG_OFF);
} else { } else {
Trigger(act, TRIG_ON); Trigger(act, TRIG_ON);
@ -158,5 +156,5 @@ void
env_spark::TimedSpark(void) env_spark::TimedSpark(void)
{ {
CreateSpark(); CreateSpark();
nextthink = time + (random() * m_flMaxDelay); ScheduleThink(CreateSpark, (random() * m_flMaxDelay));
} }

View file

@ -62,6 +62,7 @@ func_areaportal::Respawn(void)
SetModel(GetSpawnModel()); SetModel(GetSpawnModel());
SetOrigin(GetSpawnOrigin()); SetOrigin(GetSpawnOrigin());
Hide(); Hide();
SetSolid(SOLID_NOT);
if (!m_iStartOpen) if (!m_iStartOpen)
PortalClose(); PortalClose();

View file

@ -251,30 +251,30 @@ func_breakable::Respawn(void)
SetModel(GetSpawnModel()); SetModel(GetSpawnModel());
SetOrigin(GetSpawnOrigin()); SetOrigin(GetSpawnOrigin());
ClearAngles(); ClearAngles();
think = __NULL__; ReleaseThink();
m_bCanTouch = true; m_bCanTouch = true;
if (HasSpawnFlags(SF_TRIGGER)) { if (HasSpawnFlags(SF_TRIGGER)) {
takedamage = DAMAGE_NO; SetTakedamage(DAMAGE_NO);
} else { } else {
takedamage = DAMAGE_YES; SetTakedamage(DAMAGE_YES);
} }
/* initially set the health to that of the ent-data */ /* initially set the health to that of the ent-data */
float sh = GetSpawnHealth(); float spawnhealth = GetSpawnHealth();
if (HasPropData() == TRUE && sh == 0) { if (HasPropData() == TRUE && spawnhealth <= 0) {
/* assign propdata health */ /* assign propdata health */
health = GetPropData(PROPINFO_HEALTH); SetHealth(GetPropData(PROPINFO_HEALTH));
m_flExplodeMag = GetPropData(PROPINFO_EXPLOSIVE_DMG); m_flExplodeMag = GetPropData(PROPINFO_EXPLOSIVE_DMG);
m_flExplodeRad = GetPropData(PROPINFO_EXPLOSIVE_RADIUS); m_flExplodeRad = GetPropData(PROPINFO_EXPLOSIVE_RADIUS);
} else { } else {
health = sh; SetHealth(spawnhealth);
} }
/* unassigned health isn't valid */ /* unassigned health isn't valid */
if (!health) if (GetHealth() <= 0)
health = 15; SetHealth(15);
} }
void void
@ -297,6 +297,8 @@ func_breakable::Explode(void)
Damage_Radius(rp, this, m_flExplodeMag, m_flExplodeRad, TRUE, 0); Damage_Radius(rp, this, m_flExplodeMag, m_flExplodeRad, TRUE, 0);
UseTargets(this, TRIG_TOGGLE, 0.0f); /* delay... ignored. */ UseTargets(this, TRIG_TOGGLE, 0.0f); /* delay... ignored. */
Hide(); Hide();
SetSolid(SOLID_NOT);
SetTakedamage(DAMAGE_NO);
} }
void void
@ -340,11 +342,12 @@ func_breakable::Death(void)
* The only way around this is to set a hard-coded limit of loops per * The only way around this is to set a hard-coded limit of loops per
* frame and that would break functionality. */ * frame and that would break functionality. */
if (m_flExplodeMag) { if (m_flExplodeMag) {
think = Explode; ScheduleThink(Explode, random(0.0f, 0.5f));
nextthink = time + random(0.0,0.5);
} else { } else {
FX_BreakModel(vlen(size) / 10, absmin, absmax, [0,0,0], GetSurfaceData(SURFDATA_MATERIAL)); FX_BreakModel(vlen(size) / 10, absmin, absmax, [0,0,0], GetSurfaceData(SURFDATA_MATERIAL));
Hide(); Hide();
SetSolid(SOLID_NOT);
SetTakedamage(DAMAGE_NO);
UseTargets(eActivator, TRIG_TOGGLE, 0.0f); UseTargets(eActivator, TRIG_TOGGLE, 0.0f);
} }
} }
@ -352,7 +355,7 @@ func_breakable::Death(void)
void void
func_breakable::Trigger(entity act, int state) func_breakable::Trigger(entity act, int state)
{ {
if (health > 0) if (GetHealth() > 0)
Death(); Death();
} }
@ -392,12 +395,11 @@ func_breakable::Touch(entity eToucher)
if (HasSpawnFlags(SF_PRESSURE) && (eToucher.absmin[2] >= maxs[2] - 2)) { if (HasSpawnFlags(SF_PRESSURE) && (eToucher.absmin[2] >= maxs[2] - 2)) {
m_bCanTouch = false; m_bCanTouch = false;
think = TriggerWrap;
if (m_flDelay == 0) { if (m_flDelay <= 0) {
m_flDelay = 0.1f; m_flDelay = 0.1f;
} }
nextthink = time + m_flDelay; ScheduleThink(TriggerWrap, m_flDelay);
} }
} }

View file

@ -313,9 +313,9 @@ func_button::Respawn(void)
SetOrigin(GetSpawnOrigin()); SetOrigin(GetSpawnOrigin());
SetModel(GetSpawnModel()); SetModel(GetSpawnModel());
velocity = [0,0,0]; ClearVelocity();
nextthink = -1; ReleaseThink();
health = GetSpawnHealth(); SetHealth(GetSpawnHealth());
if (health > 0) { if (health > 0) {
takedamage = DAMAGE_YES; takedamage = DAMAGE_YES;
@ -342,20 +342,19 @@ void
func_button::Arrived(void) func_button::Arrived(void)
{ {
SetOrigin(m_vecDest); SetOrigin(m_vecDest);
velocity = [0,0,0]; ClearVelocity();
nextthink = 0; ReleaseThink();
m_bCanTouch = true; m_bCanTouch = true;
UseOutput(this, m_strOnIn); UseOutput(this, m_strOnIn);
m_iState = STATE_RAISED; m_iState = STATE_RAISED;
if (HasSpawnFlags(SF_BTT_TOGGLE)) { if (HasSpawnFlags(SF_BTT_TOGGLE) == true) {
return; return;
} }
if (m_flWait != -1) { if (m_flWait != -1) {
think = MoveBack; ScheduleThink(MoveBack, m_flWait);
nextthink = (ltime + m_flWait);
} }
} }
@ -364,12 +363,11 @@ func_button::Returned(void)
{ {
UseOutput(this, m_strOnOut); UseOutput(this, m_strOnOut);
SetOrigin(m_vecDest); SetOrigin(m_vecDest);
velocity = [0,0,0]; ClearVelocity();
nextthink = 0; ReleaseThink();
m_bCanTouch = true;
m_iState = STATE_LOWERED;
SetFrame(FRAME_OFF); SetFrame(FRAME_OFF);
m_bCanTouch = true;
m_iState = STATE_LOWERED;
} }
void void
@ -396,13 +394,8 @@ func_button::MoveAway(void)
if (m_iState == STATE_UP) { if (m_iState == STATE_UP) {
return; return;
} }
m_bCanTouch = false; m_bCanTouch = false;
if (m_iState == STATE_RAISED) {
nextthink = (ltime + m_flWait);
return;
}
m_iState = STATE_UP; m_iState = STATE_UP;
if (m_vecPos2 != m_vecPos1) { if (m_vecPos2 != m_vecPos1) {
@ -448,7 +441,7 @@ func_button::Trigger(entity act, int state)
if (message) if (message)
env_message_single(act, message); env_message_single(act, message);
health = GetSpawnHealth(); SetHealth(GetSpawnHealth());
} }
void void
@ -460,7 +453,7 @@ func_button::DeathTrigger(void)
void void
func_button::Touch(entity eToucher) func_button::Touch(entity eToucher)
{ {
if (!HasSpawnFlags(SF_BTT_TOUCH_ONLY)) { if (HasSpawnFlags(SF_BTT_TOUCH_ONLY) == false) {
return; return;
} }
@ -517,11 +510,10 @@ func_button::MoveToDestination(vector vecDest, void(void) func)
} }
m_vecDest = vecDest; m_vecDest = vecDest;
think = func;
if (vecDest == origin) { if (vecDest == origin) {
velocity = [0,0,0]; ClearVelocity();
nextthink = (ltime + 0.1); ScheduleThink(func, 0.0f);
return; return;
} }
@ -530,11 +522,11 @@ func_button::MoveToDestination(vector vecDest, void(void) func)
fTravelTime = (flTravel / m_flSpeed); fTravelTime = (flTravel / m_flSpeed);
if (fTravelTime < 0.1) { if (fTravelTime < 0.1) {
velocity = [0,0,0]; ClearVelocity();
nextthink = ltime + 0.1; ScheduleThink(func, 0.0f);
return; return;
} }
nextthink = (ltime + fTravelTime); ScheduleThink(func, fTravelTime);
velocity = (vecDifference * (1 / fTravelTime)); SetVelocity(vecDifference * (1 / fTravelTime));
} }

View file

@ -375,10 +375,8 @@ func_door::Respawn(void)
SetMovetype(MOVETYPE_PUSH); SetMovetype(MOVETYPE_PUSH);
SetModel(GetSpawnModel()); SetModel(GetSpawnModel());
SetOrigin(GetSpawnOrigin()); SetOrigin(GetSpawnOrigin());
flags |= FL_FINDABLE_NONSOLID; AddFlags(FL_FINDABLE_NONSOLID);
ReleaseThink();
think = __NULL__;
nextthink = 0.0f;
/* FIXME: Is this correct? */ /* FIXME: Is this correct? */
if (m_flWait == -1) { if (m_flWait == -1) {
@ -472,8 +470,8 @@ void
func_door::Arrived(void) func_door::Arrived(void)
{ {
SetOrigin(m_vecDest); SetOrigin(m_vecDest);
velocity = [0,0,0]; ClearVelocity();
nextthink = 0.0f; ReleaseThink();
m_iState = DOORSTATE_RAISED; m_iState = DOORSTATE_RAISED;
@ -489,16 +487,15 @@ func_door::Arrived(void)
if ((m_flWait < 0.0f) || HasSpawnFlags(SF_MOV_TOGGLE) == true) if ((m_flWait < 0.0f) || HasSpawnFlags(SF_MOV_TOGGLE) == true)
return; return;
think = MoveBack; ScheduleThink(MoveBack, m_flWait);
nextthink = (ltime + m_flWait);
} }
void void
func_door::Returned(void) func_door::Returned(void)
{ {
SetOrigin(m_vecDest); SetOrigin(m_vecDest);
velocity = [0,0,0]; ClearVelocity();
nextthink = 0.0f; ReleaseThink();
if (targetClose) if (targetClose)
for (entity f = world; (f = find(f, ::targetname, targetClose));) { for (entity f = world; (f = find(f, ::targetname, targetClose));) {
@ -554,13 +551,6 @@ func_door::MoveAway(void)
if (m_strSndMove) if (m_strSndMove)
Sound_Play(this, CHAN_WEAPON, m_strSndMove); Sound_Play(this, CHAN_WEAPON, m_strSndMove);
if (!HasSpawnFlags(SF_MOV_TOGGLE)) {
if (m_iState == DOORSTATE_RAISED) {
nextthink = (ltime + m_flWait);
return;
}
}
m_iValue = 1; m_iValue = 1;
m_iState = DOORSTATE_UP; m_iState = DOORSTATE_UP;
MoveToDestination(m_vecPos2, Arrived); MoveToDestination(m_vecPos2, Arrived);
@ -568,13 +558,13 @@ func_door::MoveAway(void)
} }
void void
func_door::Trigger(entity act, int state) func_door::Trigger(entity act, int triggerstate)
{ {
if (GetMaster() == 0) if (GetMaster() == 0)
return; return;
if (m_flNextTrigger > time) { if (m_flNextTrigger > time) {
if (!HasSpawnFlags(SF_MOV_TOGGLE)) { if (HasSpawnFlags(SF_MOV_TOGGLE) == false) {
return; return;
} }
} }
@ -585,9 +575,9 @@ func_door::Trigger(entity act, int state)
UseTargets(act, TRIG_TOGGLE, m_flDelay); UseTargets(act, TRIG_TOGGLE, m_flDelay);
} }
if (state == TRIG_OFF) { if (triggerstate == TRIG_OFF) {
MoveBack(); MoveBack();
} else if (state == TRIG_ON) { } else if (triggerstate == TRIG_ON) {
MoveAway(); MoveAway();
} else { } else {
if ((m_iState == DOORSTATE_UP) || (m_iState == DOORSTATE_RAISED)){ if ((m_iState == DOORSTATE_UP) || (m_iState == DOORSTATE_RAISED)){
@ -604,7 +594,7 @@ func_door::Touch(entity eToucher)
if (m_iCanTouch == false) if (m_iCanTouch == false)
return; return;
if (HasSpawnFlags(SF_MOV_USE)) if (HasSpawnFlags(SF_MOV_USE) == true)
return; return;
if (m_iLocked || !GetMaster()) { if (m_iLocked || !GetMaster()) {
@ -615,7 +605,7 @@ func_door::Touch(entity eToucher)
return; return;
} }
if (HasSpawnFlags(SF_MOV_TOGGLE)) { if (HasSpawnFlags(SF_MOV_TOGGLE) == true) {
return; return;
} }
@ -669,11 +659,10 @@ func_door::MoveToDestination(vector vecDest, void(void) func)
} }
m_vecDest = vecDest; m_vecDest = vecDest;
think = func;
if (vecDest == origin) { if (vecDest == origin) {
velocity = [0,0,0]; ScheduleThink(func, 0.1f);
nextthink = (ltime + 0.1f); ClearVelocity();
return; return;
} }
@ -682,13 +671,13 @@ func_door::MoveToDestination(vector vecDest, void(void) func)
fTravelTime = (flTravel / m_flSpeed); fTravelTime = (flTravel / m_flSpeed);
if (fTravelTime < 0.1) { if (fTravelTime < 0.1) {
velocity = [0,0,0]; ScheduleThink(func, 0.1f);
nextthink = ltime + 0.1f; ClearVelocity();
return; return;
} }
nextthink = (ltime + fTravelTime); ScheduleThink(func, fTravelTime);
velocity = (vecDifference * (1.0f / fTravelTime)); SetVelocity(vecDifference * (1.0f / fTravelTime));
} }
void void

View file

@ -259,8 +259,8 @@ func_door_rotating::Respawn(void)
ClearAngles(); ClearAngles();
#ifdef GS_PHYSICS #ifdef GS_PHYSICS
takedamage = DAMAGE_YES; SetTakedamage(DAMAGE_YES);
health = 100; SetHealth(100);
Death = func_door_rotating::Unhinge; Death = func_door_rotating::Unhinge;
#endif #endif
@ -268,9 +268,8 @@ func_door_rotating::Respawn(void)
SetMovetype(MOVETYPE_PUSH); SetMovetype(MOVETYPE_PUSH);
SetModel(GetSpawnModel()); SetModel(GetSpawnModel());
SetOrigin(GetSpawnOrigin()); SetOrigin(GetSpawnOrigin());
think = __NULL__; ClearVelocity();
nextthink = 0.0f; ReleaseThink();
avelocity = [0,0,0];
if (spawnflags & SF_ROT_USE) if (spawnflags & SF_ROT_USE)
m_iCanTouch = false; m_iCanTouch = false;
@ -397,8 +396,8 @@ func_door_rotating::PortalClose(void)
void void
func_door_rotating::Unhinge(void) func_door_rotating::Unhinge(void)
{ {
takedamage = DAMAGE_NO; SetTakedamage(DAMAGE_NO);
think = __NULL__; ReleaseThink();
m_iCanTouch = false; m_iCanTouch = false;
SetSolid(SOLID_PHYSICS_BOX); SetSolid(SOLID_PHYSICS_BOX);
SetMovetype(MOVETYPE_PHYSICS); SetMovetype(MOVETYPE_PHYSICS);
@ -411,7 +410,7 @@ func_door_rotating::Arrived(void)
{ {
SetAngles(m_vecDest); SetAngles(m_vecDest);
SetAngularVelocity([0,0,0]); SetAngularVelocity([0,0,0]);
nextthink = 0.0f; ReleaseThink();
m_iState = STATE_RAISED; m_iState = STATE_RAISED;
@ -424,8 +423,7 @@ func_door_rotating::Arrived(void)
if ((m_flWait < 0.0f) || HasSpawnFlags(SF_ROT_TOGGLE) == true) if ((m_flWait < 0.0f) || HasSpawnFlags(SF_ROT_TOGGLE) == true)
return; return;
think = Back; ScheduleThink(Back, m_flWait);
nextthink = (ltime + m_flWait);
} }
void void
@ -433,7 +431,7 @@ func_door_rotating::Returned(void)
{ {
SetAngles(m_vecDest); SetAngles(m_vecDest);
SetAngularVelocity([0,0,0]); SetAngularVelocity([0,0,0]);
nextthink = 0.0f; ReleaseThink();
if (m_strSndStop) { if (m_strSndStop) {
Sound_Play(this, CHAN_VOICE, m_strSndStop); Sound_Play(this, CHAN_VOICE, m_strSndStop);
@ -458,7 +456,6 @@ void
func_door_rotating::Back(void) func_door_rotating::Back(void)
{ {
if (!HasSpawnFlags(SF_DOOR_SILENT)) { if (!HasSpawnFlags(SF_DOOR_SILENT)) {
if (m_strSndClose) { if (m_strSndClose) {
Sound_Play(this, CHAN_VOICE, m_strSndClose); Sound_Play(this, CHAN_VOICE, m_strSndClose);
} else { } else {
@ -487,11 +484,6 @@ func_door_rotating::Away(void)
} }
} }
if (m_iState == STATE_RAISED) {
nextthink = (ltime + m_flWait);
return;
}
m_iState = STATE_UP; m_iState = STATE_UP;
if (!HasSpawnFlags(SF_ROT_ONEWAY)) { if (!HasSpawnFlags(SF_ROT_ONEWAY)) {
@ -566,8 +558,9 @@ func_door_rotating::Touch(entity eToucher)
return; return;
} }
if ((m_iState == STATE_UP) || (m_iState == STATE_DOWN)) if ((m_iState == STATE_UP) || (m_iState == STATE_DOWN)) {
return; return;
}
if (eToucher.movetype == MOVETYPE_WALK) { if (eToucher.movetype == MOVETYPE_WALK) {
Trigger(eToucher, TRIG_TOGGLE); Trigger(eToucher, TRIG_TOGGLE);
@ -610,7 +603,7 @@ func_door_rotating::RotToDest(vector vDestAngle, void(void) func)
if (!m_flSpeed) { if (!m_flSpeed) {
NSLog("^1func_door_rotating::^3RotToDest^7: No speed defined for %s!", targetname); NSLog("^1func_door_rotating::^3RotToDest^7: No speed defined for %s!", targetname);
func_door_rotating::Respawn(); Respawn();
return; return;
} }
@ -620,12 +613,10 @@ func_door_rotating::RotToDest(vector vDestAngle, void(void) func)
/* Avoid NAN hack */ /* Avoid NAN hack */
if (flTravelTime <= 0.0f) { if (flTravelTime <= 0.0f) {
func(); ScheduleThink(func, 0.0f);
nextthink = 0.0f;
} else { } else {
avelocity = (vecAngleDifference * (1 / flTravelTime)); avelocity = (vecAngleDifference * (1 / flTravelTime));
m_vecDest = vDestAngle; m_vecDest = vDestAngle;
think = func; ScheduleThink(func, flTravelTime);
nextthink = (ltime + flTravelTime);
} }
} }

View file

@ -128,11 +128,10 @@ func_guntarget::Respawn(void)
SetMovetype(MOVETYPE_PUSH); SetMovetype(MOVETYPE_PUSH);
SetModel(GetSpawnModel()); SetModel(GetSpawnModel());
SetOrigin(GetSpawnOrigin()); SetOrigin(GetSpawnOrigin());
health = GetSpawnHealth(); SetHealth(GetSpawnHealth());
if (HasSpawnFlags(SF_GUNTARGET_ON)) { if (HasSpawnFlags(SF_GUNTARGET_ON) == true) {
think = ThinkWrap; ScheduleThink(ThinkWrap, 0.25f);
nextthink = ltime + 0.25f;
} }
} }
@ -156,14 +155,13 @@ func_guntarget::Move(void)
vel_to_pos = (node.origin - vecWorldPos); vel_to_pos = (node.origin - vecWorldPos);
flTravelTime = (vlen(vel_to_pos) / m_flSpeed); flTravelTime = (vlen(vel_to_pos) / m_flSpeed);
if (!flTravelTime) { if (flTravelTime <= 0.0f) {
NextPath(); NextPath();
return; return;
} }
velocity = (vel_to_pos * (1 / flTravelTime)); SetVelocity(vel_to_pos * (1 / flTravelTime));
think = NextPath; ScheduleThink(NextPath, flTravelTime);
nextthink = (ltime + flTravelTime);
} }
void void
@ -181,7 +179,7 @@ func_guntarget::NextPath(void)
} }
target = node.target; target = node.target;
velocity = [0,0,0]; ClearVelocity();
if (target) { if (target) {
Move(); Move();
@ -211,7 +209,7 @@ func_guntarget::Death(void)
void void
func_guntarget::Start(void) func_guntarget::Start(void)
{ {
takedamage = DAMAGE_YES; SetTakedamage(DAMAGE_YES);
NextPath(); NextPath();
m_iValue = 0; m_iValue = 0;
} }
@ -219,10 +217,9 @@ func_guntarget::Start(void)
void void
func_guntarget::Stop(void) func_guntarget::Stop(void)
{ {
takedamage = DAMAGE_NO; SetTakedamage(DAMAGE_NO);
velocity = [0,0,0]; ClearVelocity();
nextthink = 0; ReleaseThink();
think = __NULL__;
m_iValue = 1; m_iValue = 1;
} }

View file

@ -191,8 +191,7 @@ func_healthcharger::OnPlayerUse(void)
/* Reset 30 seconds after first being used successfully */ /* Reset 30 seconds after first being used successfully */
//if (health == max_health) { //if (health == max_health) {
think = ResetHealth; ScheduleThink(ResetHealth, 60.0f);
nextthink = time + 60.0f;
//} //}
health -= 1; health -= 1;

View file

@ -77,6 +77,8 @@ void
func_physbox::Death(void) func_physbox::Death(void)
{ {
Hide(); Hide();
SetSolid(SOLID_NOT);
SetTakedamage(DAMAGE_NO);
} }
void void

View file

@ -120,21 +120,20 @@ func_plat::Respawn(void)
ClearAngles(); ClearAngles();
m_iState = PLATSTATE_RAISED; m_iState = PLATSTATE_RAISED;
think = __NULL__; ReleaseThink();
nextthink = 0.0f;
} }
void void
func_plat::ArrivedUp(void) func_plat::ArrivedUp(void)
{ {
velocity = [0,0,0]; ClearVelocity();
m_iState = PLATSTATE_RAISED; m_iState = PLATSTATE_RAISED;
} }
void void
func_plat::ArrivedDown(void) func_plat::ArrivedDown(void)
{ {
velocity = [0,0,0]; ClearVelocity();
m_iState = PLATSTATE_LOWERED; m_iState = PLATSTATE_LOWERED;
} }
@ -148,16 +147,16 @@ func_plat::Move(vector vecDest, void() vFunc)
vecDifference = (vecDest - origin); vecDifference = (vecDest - origin);
flTravel = vlen(vecDifference); flTravel = vlen(vecDifference);
fTravelTime = (flTravel / m_flSpeed); fTravelTime = (flTravel / m_flSpeed);
think = vFunc; SetThink(vFunc);
if (fTravelTime < 0.1) { if (fTravelTime < 0.1) {
velocity = [0,0,0]; ClearVelocity();
nextthink = ltime + 0.1f; SetNextThink(0.1f);
return; return;
} }
velocity = (vecDifference * (1.0f / fTravelTime)); SetVelocity(vecDifference * (1.0f / fTravelTime));
nextthink = (ltime + fTravelTime); SetNextThink(fTravelTime);
} }
void void

View file

@ -152,14 +152,13 @@ func_platrot::Respawn(void)
SetAngles(GetSpawnAngles()); SetAngles(GetSpawnAngles());
m_iState = PLATSTATE_RAISED; m_iState = PLATSTATE_RAISED;
think = __NULL__; ReleaseThink();
nextthink = 0.0f;
} }
void void
func_platrot::ArrivedUp(void) func_platrot::ArrivedUp(void)
{ {
avelocity = velocity = [0,0,0]; ClearVelocity();
m_iState = PLATSTATE_RAISED; m_iState = PLATSTATE_RAISED;
sound(this, CHAN_VOICE, "common/null.wav", 1.0f, ATTN_NORM); sound(this, CHAN_VOICE, "common/null.wav", 1.0f, ATTN_NORM);
@ -171,7 +170,7 @@ func_platrot::ArrivedUp(void)
void void
func_platrot::ArrivedDown(void) func_platrot::ArrivedDown(void)
{ {
avelocity = velocity = [0,0,0]; ClearVelocity();
m_iState = PLATSTATE_LOWERED; m_iState = PLATSTATE_LOWERED;
sound(this, CHAN_VOICE, "common/null.wav", 1.0f, ATTN_NORM); sound(this, CHAN_VOICE, "common/null.wav", 1.0f, ATTN_NORM);
@ -191,17 +190,18 @@ func_platrot::Move(vector vecDest, vector vecADest, void() vFunc)
vecADifference = vecADest - angles; vecADifference = vecADest - angles;
flTravel = vlen(vecDifference); flTravel = vlen(vecDifference);
fTravelTime = (flTravel / m_flSpeed); fTravelTime = (flTravel / m_flSpeed);
think = vFunc; SetThink(vFunc);
if (fTravelTime < 0.1) { if (fTravelTime < 0.1) {
velocity = [0,0,0]; ClearVelocity();
nextthink = ltime + 0.1f; SetNextThink(0.1f);
return; return;
} }
avelocity = (vecADifference * (1.0f / fTravelTime));
velocity = (vecDifference * (1.0f / fTravelTime)); SetAngularVelocity(vecADifference * (1.0f / fTravelTime));
nextthink = (ltime + fTravelTime); SetVelocity(vecDifference * (1.0f / fTravelTime));
SetNextThink(ltime + fTravelTime);
if (m_strNoise1) if (m_strNoise1)
sound(this, CHAN_VOICE, m_strNoise1, 1.0f, ATTN_NORM); sound(this, CHAN_VOICE, m_strNoise1, 1.0f, ATTN_NORM);

View file

@ -201,8 +201,7 @@ func_recharge::OnPlayerUse(void)
/* Reset 30 seconds after first being used successfully */ /* Reset 30 seconds after first being used successfully */
//if (health == max_health) { //if (health == max_health) {
think = ResetHealth; ScheduleThink(ResetHealth, 30.0f);
nextthink = time + 30.0f;
//} //}
health -= 1; health -= 1;

View file

@ -61,11 +61,10 @@ enum
}; };
class class
func_rot_button:NSRenderableEntity func_rot_button:NSSurfacePropEntity
{ {
vector m_vecMoveAngle; vector m_vecMoveAngle;
int m_iState; int m_iState;
int m_iHealth;
float m_flSpeed; float m_flSpeed;
float m_flDistance; float m_flDistance;
@ -94,7 +93,6 @@ func_rot_button::func_rot_button(void)
{ {
m_vecMoveAngle = [0.0f, 0.0f, 0.0f]; m_vecMoveAngle = [0.0f, 0.0f, 0.0f];
m_iState = 0i; m_iState = 0i;
m_iHealth = 0i;
m_flSpeed = 0.0f; m_flSpeed = 0.0f;
m_flDistance = 0.0f; m_flDistance = 0.0f;
m_flReturnTime = 0.0f; m_flReturnTime = 0.0f;
@ -106,7 +104,6 @@ func_rot_button::Save(float handle)
super::Save(handle); super::Save(handle);
SaveVector(handle, "m_vecMoveAngle", m_vecMoveAngle); SaveVector(handle, "m_vecMoveAngle", m_vecMoveAngle);
SaveInt(handle, "m_iState", m_iState); SaveInt(handle, "m_iState", m_iState);
SaveInt(handle, "m_iHealth", m_iHealth);
SaveFloat(handle, "m_flSpeed", m_flSpeed); SaveFloat(handle, "m_flSpeed", m_flSpeed);
SaveFloat(handle, "m_flDistance", m_flDistance); SaveFloat(handle, "m_flDistance", m_flDistance);
SaveFloat(handle, "m_flReturnTime", m_flReturnTime); SaveFloat(handle, "m_flReturnTime", m_flReturnTime);
@ -122,9 +119,6 @@ func_rot_button::Restore(string strKey, string strValue)
case "m_iState": case "m_iState":
m_iState = ReadInt(strValue); m_iState = ReadInt(strValue);
break; break;
case "m_iHealth":
m_iHealth = ReadInt(strValue);
break;
case "m_flSpeed": case "m_flSpeed":
m_flSpeed = ReadFloat(strValue); m_flSpeed = ReadFloat(strValue);
break; break;
@ -152,9 +146,6 @@ func_rot_button::SpawnKey(string strKey, string strValue)
case "wait": case "wait":
m_flReturnTime = stof(strValue); m_flReturnTime = stof(strValue);
break; break;
case "health":
m_iHealth = stoi(strValue);
break;
default: default:
super::SpawnKey(strKey, strValue); super::SpawnKey(strKey, strValue);
} }
@ -173,16 +164,15 @@ func_rot_button::Respawn(void)
SetModel(GetSpawnModel()); SetModel(GetSpawnModel());
SetOrigin(GetSpawnOrigin()); SetOrigin(GetSpawnOrigin());
SetAngles(GetSpawnAngles()); SetAngles(GetSpawnAngles());
AddFlags(FL_FINDABLE_NONSOLID);
PlayerUse = OnPlayerUse; PlayerUse = OnPlayerUse;
flags |= FL_FINDABLE_NONSOLID;
m_iState = ROTBTNSTATE_OPENED; m_iState = ROTBTNSTATE_OPENED;
think = __NULL__; ReleaseThink();
nextthink = 0.0f;
if (m_iHealth > 0) { if (GetSpawnHealth() > 0) {
takedamage = DAMAGE_YES; SetTakedamage(DAMAGE_YES);
health = m_iHealth; SetHealth(GetSpawnHealth());
} }
vector vecMoveDir; vector vecMoveDir;
@ -210,20 +200,22 @@ func_rot_button::TriggerTargets(void)
void void
func_rot_button::ArrivedClosed(void) func_rot_button::ArrivedClosed(void)
{ {
avelocity = [0,0,0]; ClearVelocity();
ReleaseThink();
m_iState = ROTBTNSTATE_CLOSED; m_iState = ROTBTNSTATE_CLOSED;
TriggerTargets(); TriggerTargets();
if (m_flReturnTime > 0.0f) { if (m_flReturnTime > 0.0f) {
think = TurnToggle; ScheduleThink(TurnToggle, m_flReturnTime);
nextthink = ltime + m_flReturnTime;
} }
} }
void void
func_rot_button::ArrivedOpened(void) func_rot_button::ArrivedOpened(void)
{ {
avelocity = [0,0,0]; ClearVelocity();
ReleaseThink();
m_iState = ROTBTNSTATE_OPENED; m_iState = ROTBTNSTATE_OPENED;
} }
@ -236,10 +228,9 @@ func_rot_button::Rotate(vector vecDest, void(void) vFunc)
vecAngleDifference = (vecDest - angles); vecAngleDifference = (vecDest - angles);
flTravelLength = vlen(vecAngleDifference); flTravelLength = vlen(vecAngleDifference);
flTravelTime = (flTravelLength / m_flSpeed); flTravelTime = (flTravelLength / m_flSpeed);
avelocity = (vecAngleDifference * (1 / flTravelTime));
think = vFunc; SetAngularVelocity(vecAngleDifference * (1 / flTravelTime));
nextthink = (ltime + flTravelTime); ScheduleThink(vFunc, flTravelTime);
} }
void void
@ -274,6 +265,6 @@ func_rot_button::TurnToggle(void)
void void
func_rot_button::Death(void) func_rot_button::Death(void)
{ {
takedamage = DAMAGE_NO; SetTakedamage(DAMAGE_NO);
TurnToggle(); TurnToggle();
} }

View file

@ -162,9 +162,8 @@ func_rotating::Respawn(void)
ClearAngles(); ClearAngles();
if (HasSpawnFlags(FR_STARTON)) { if (HasSpawnFlags(FR_STARTON)) {
avelocity = m_vecMoveDir * m_flSpeed; SetAngularVelocity(m_vecMoveDir * m_flSpeed);
think = Rotate; ScheduleThink(Rotate, 1.5f);
nextthink = ltime + 1.5f;
} }
} }
@ -173,9 +172,8 @@ void
func_rotating::Trigger(entity act, int state) func_rotating::Trigger(entity act, int state)
{ {
if (vlen(avelocity) > 0) { if (vlen(avelocity) > 0) {
avelocity = [0,0,0]; ClearVelocity();
think = __NULL__; ReleaseThink();
nextthink = 0.0f;
} else { } else {
float flSpeed; float flSpeed;
@ -185,19 +183,18 @@ func_rotating::Trigger(entity act, int state)
flSpeed = m_flSpeed; flSpeed = m_flSpeed;
} }
avelocity = m_vecMoveDir * flSpeed; SetAngularVelocity(m_vecMoveDir * flSpeed);
m_flDir = 1 - m_flDir; m_flDir = 1 - m_flDir;
/* HACK HACK HACK! This is terrible */ /* HACK HACK HACK! This is terrible, but the only way PUSH movetypes rotate */
think = Rotate; ScheduleThink(Rotate, 99999.0f);
nextthink = ltime + 99999.0f;
} }
} }
void void
func_rotating::Rotate(void) func_rotating::Rotate(void)
{ {
nextthink = ltime + 10.0f; SetNextThink(10.0f);
} }
void void
@ -210,12 +207,7 @@ func_rotating::Blocked(entity eBlocker)
if (other.takedamage == DAMAGE_YES) { if (other.takedamage == DAMAGE_YES) {
/* this is to work around a Q1 BSP bug. don't attempt to damage our /* this is to work around a Q1 BSP bug. don't attempt to damage our
* target unless we're absolutely sure he's within the bounds of the entity */ * target unless we're absolutely sure he's within the bounds of the entity */
if not (other.absmin[0] >= absmin[0] && other.absmax[0] <= absmax[0]) if (WithinBounds(other))
return;
if not (other.absmin[1] >= absmin[1] && other.absmax[1] <= absmax[1])
return;
if not (other.absmin[2] >= absmin[2] && other.absmax[2] <= absmax[2])
return;
Damage_Apply(other, this, m_flDamage, 0, DMG_CRUSH); Damage_Apply(other, this, m_flDamage, 0, DMG_CRUSH);
} }
} }

View file

@ -188,9 +188,8 @@ func_tracktrain::Respawn(void)
/* let's wait 1/4 a second to give the path_corner entities a chance to /* let's wait 1/4 a second to give the path_corner entities a chance to
* spawn in case they're after us in the ent lump */ * spawn in case they're after us in the ent lump */
target = m_oldstrTarget; ScheduleThink(AfterSpawn, 0.25f);
think = AfterSpawn; SetTriggerTarget(m_oldstrTarget);
nextthink = ltime + 0.25f;
} }
void void
@ -250,14 +249,13 @@ func_tracktrain::PathMove(void)
if (!flTravelTime) { if (!flTravelTime) {
print("^1func_tracktrain::^3PathMove^7: Distance short, going next\n"); print("^1func_tracktrain::^3PathMove^7: Distance short, going next\n");
think = PathNext; ScheduleThink(PathNext, 0.0f);
nextthink = ltime;
return; return;
} }
SoundMove(); SoundMove();
velocity = (vecVelocity * (1 / flTravelTime)); SetVelocity(vecVelocity * (1 / flTravelTime));
vector vecAngleDest; vector vecAngleDest;
vector vecDiff; vector vecDiff;
@ -284,15 +282,14 @@ func_tracktrain::PathMove(void)
//print(sprintf("vecAngleDiff: %v\n", vecAngleDiff)); //print(sprintf("vecAngleDiff: %v\n", vecAngleDiff));
if (vecAngleDiff[1] == 0) if (vecAngleDiff[1] == 0)
angles = vecAngleDest; SetAngles(vecAngleDest);
else else
avelocity = (vecAngleDiff * (1 / flTravelTime)); SetAngularVelocity(vecAngleDiff * (1 / flTravelTime));
if (!eNode) if (!eNode)
avelocity = [0,0,0]; SetAngularVelocity([0,0,0]);
think = PathNext; ScheduleThink(PathNext, flTravelTime);
nextthink = (ltime + flTravelTime);
} }
void void
@ -302,7 +299,7 @@ func_tracktrain::PathDone(void)
eNode = (path_corner)find(world, ::targetname, target); eNode = (path_corner)find(world, ::targetname, target);
/* stop */ /* stop */
avelocity = [0,0,0]; ClearVelocity();
if (!eNode) { if (!eNode) {
return; return;
@ -334,7 +331,7 @@ func_tracktrain::PathNext(void)
m_flWait = eNode.m_flWait; m_flWait = eNode.m_flWait;
target = eNode.target; target = eNode.target;
velocity = [0,0,0]; ClearVelocity();
/* warp */ /* warp */
if (eNode.HasSpawnFlags(PC_TELEPORT)) { if (eNode.HasSpawnFlags(PC_TELEPORT)) {
@ -347,13 +344,7 @@ func_tracktrain::PathNext(void)
return; return;
} }
/* move after delay, or instantly when none is given */
/*if (m_flWait > 0) {
think = PathMove;
nextthink = ltime + m_flWait;
} else */ {
PathMove(); PathMove();
}
} }
/* TODO: Handle state? */ /* TODO: Handle state? */

View file

@ -172,9 +172,8 @@ func_train::Respawn(void)
/* let's wait 1/4 a second to give the path_corner entities a chance to /* let's wait 1/4 a second to give the path_corner entities a chance to
* spawn in case they're after us in the ent lump */ * spawn in case they're after us in the ent lump */
target = m_oldstrTarget; SetTriggerTarget(m_oldstrTarget);
think = AfterSpawn; ScheduleThink(AfterSpawn, 0.25f);
nextthink = ltime + 0.25f;
} }
void void
@ -228,22 +227,20 @@ func_train::PathMove(void)
} }
vecWorldPos = WorldSpaceCenter(); vecWorldPos = WorldSpaceCenter();
vecVelocity = (eNode.origin - vecWorldPos); vecVelocity = (eNode.origin - vecWorldPos);
flTravelTime = (vlen(vecVelocity) / m_flSpeed); flTravelTime = (vlen(vecVelocity) / m_flSpeed);
if (!flTravelTime) { if (!flTravelTime) {
print("^1func_train::^3PathMove^7: Distance short, going next\n"); print("^1func_train::^3PathMove^7: Distance short, going next\n");
think = PathNext; ClearVelocity();
nextthink = ltime; ScheduleThink(PathNext, 0.0f);
return; return;
} }
SoundMove(); SoundMove();
velocity = (vecVelocity * (1 / flTravelTime)); SetVelocity(vecVelocity * (1 / flTravelTime));
think = PathNext; ScheduleThink(PathNext, flTravelTime);
nextthink = (ltime + flTravelTime);
} }
void void
@ -281,8 +278,8 @@ func_train::PathNext(void)
m_flSpeed = eNode.m_flSpeed; m_flSpeed = eNode.m_flSpeed;
m_flWait = eNode.m_flWait; m_flWait = eNode.m_flWait;
target = eNode.target; SetTriggerTarget(eNode.target);
velocity = [0,0,0]; ClearVelocity();
/* warp */ /* warp */
if (eNode.HasSpawnFlags(PC_TELEPORT)) { if (eNode.HasSpawnFlags(PC_TELEPORT)) {
@ -297,8 +294,7 @@ func_train::PathNext(void)
/* move after delay, or instantly when none is given */ /* move after delay, or instantly when none is given */
if (m_flWait > 0) { if (m_flWait > 0) {
think = PathMove; ScheduleThink(PathMove, m_flWait);
nextthink = ltime + m_flWait;
} else { } else {
PathMove(); PathMove();
} }
@ -316,7 +312,8 @@ func_train::AfterSpawn(void)
{ {
PathNext(); PathNext();
if (!targetname) { /* if we're unable to be triggered by anything, begin moving */
if (HasTargetname() == false) {
PathMove(); PathMove();
} }
} }

View file

@ -497,14 +497,14 @@ func_vehicle::Respawn(void)
SetModel(GetSpawnModel()); SetModel(GetSpawnModel());
SetOrigin(GetSpawnOrigin()); SetOrigin(GetSpawnOrigin());
SetAngles(GetSpawnAngles()); SetAngles(GetSpawnAngles());
think = Realign; ScheduleThink(Realign, 0.0f);
nextthink = time + 0.1f;
m_wlFL.velocity = m_wlFL.velocity =
m_wlFR.velocity = m_wlFR.velocity =
m_wlBL.velocity = m_wlBL.velocity =
m_wlBR.velocity = m_wlBR.velocity = [0.0f, 0.0f, 0.0f];
velocity = [0,0,0]; ClearVelocity();
PlayerUse = OnPlayerUse; PlayerUse = OnPlayerUse;
if (m_eDriver) if (m_eDriver)
@ -613,7 +613,7 @@ func_vehicle::customphysics(void)
new_origin += m_wlBL.origin; new_origin += m_wlBL.origin;
new_origin += m_wlBR.origin; new_origin += m_wlBR.origin;
new_origin *= 0.25f; new_origin *= 0.25f;
setorigin(this, new_origin); SetOrigin(new_origin);
PlayerAlign(); PlayerAlign();
/* support for think/nextthink */ /* support for think/nextthink */

View file

@ -69,7 +69,6 @@ info_null::WarnDeveloper(void)
void void
info_null::Respawn(void) info_null::Respawn(void)
{ {
nextthink = time + 0.1f; ScheduleThink(WarnDeveloper, 0.0f);
think = WarnDeveloper;
} }
#endif #endif

View file

@ -78,8 +78,7 @@ item_food::Spawned(void)
} }
SetSize([0,0,0], [0,0,0]); SetSize([0,0,0], [0,0,0]);
think = Setup; ScheduleThink(Setup, 1.0f);
nextthink = time + 1.0f;
} }
void void

View file

@ -136,11 +136,9 @@ logic_auto::Restore(string strKey, string strValue)
void void
logic_auto::RestoreComplete(void) logic_auto::RestoreComplete(void)
{ {
think = Processing; ScheduleThink(Processing, 0.2f);
nextthink = time + 0.2f;
} }
void void
logic_auto::SpawnKey(string strKey, string strValue) logic_auto::SpawnKey(string strKey, string strValue)
{ {
@ -196,8 +194,7 @@ logic_auto::Spawned(void)
void void
logic_auto::Respawn(void) logic_auto::Respawn(void)
{ {
think = Processing; ScheduleThink(Processing, 0.2f);
nextthink = time + 0.2f;
} }
void void
@ -232,7 +229,7 @@ logic_auto::Processing(void)
UseOutput(this, m_strOnBackgroundMap); UseOutput(this, m_strOnBackgroundMap);
if (HasSpawnFlags(1)) { if (HasSpawnFlags(1)) {
NSLog("^2logic_auto::^3think^7: %s triggerer removed self", target); NSLog("^2logic_auto::^3Processing ^7: %s triggerer removed self", target);
remove(this); remove(this);
} }
} }

View file

@ -67,6 +67,7 @@ momentary_door::Respawn(void)
RestoreAngles(); RestoreAngles();
SetMovementDirection(); SetMovementDirection();
ClearAngles(); ClearAngles();
ClearVelocity();
SetMovetype(MOVETYPE_PUSH); SetMovetype(MOVETYPE_PUSH);
SetSolid(SOLID_BSP); SetSolid(SOLID_BSP);
@ -87,7 +88,8 @@ momentary_door::GetProgress(void)
void void
momentary_door::MovementDone(void) momentary_door::MovementDone(void)
{ {
m_vecDest = velocity = [0,0,0]; m_vecDest = [0,0,0];
ClearVelocity();
} }
void void
@ -122,9 +124,8 @@ momentary_door::MovementStateChanged(void)
return; return;
} }
think = MovementDone; ScheduleThink(MovementDone, flTravelTime);
nextthink = (ltime + flTravelTime); SetVelocity(vecDifference * (1.0f / flTravelTime));
velocity = (vecDifference * (1.0f / flTravelTime));
} }
void void

View file

@ -183,13 +183,12 @@ momentary_rot_button::MovementStateChanged(void)
/* Avoid NAN hack */ /* Avoid NAN hack */
if (flTravelTime <= 0.0f) { if (flTravelTime <= 0.0f) {
angles = m_vecDest; SetAngles(m_vecDest);
MovementDone(); MovementDone();
nextthink = 0.0f; ReleaseThink();
} else { } else {
avelocity = (vecAngleDifference * (1 / flTravelTime)); SetAngularVelocity(vecAngleDifference * (1 / flTravelTime));
think = MovementDone; ScheduleThink(MovementDone, flTravelTime);
nextthink = (ltime + flTravelTime);
} }
} }

View file

@ -158,16 +158,14 @@ monstermaker::Respawn(void)
void void
monstermaker::TurnOff(void) monstermaker::TurnOff(void)
{ {
think = __NULL__; ReleaseThink();
nextthink = 0;
m_iValue = 0; m_iValue = 0;
} }
void void
monstermaker::TurnOn(void) monstermaker::TurnOn(void)
{ {
think = Spawner; ScheduleThink(Spawner, m_flDelay);
nextthink = time + m_flDelay;
m_iValue = 1; m_iValue = 1;
} }
@ -210,14 +208,14 @@ monstermaker::Spawner(void)
/* too many alive at a time */ /* too many alive at a time */
if ((m_iMaxChildren > 0 && c >= m_iMaxChildren) || (m_flDelay <= 0 && c >= 1)) { if ((m_iMaxChildren > 0 && c >= m_iMaxChildren) || (m_flDelay <= 0 && c >= 1)) {
nextthink = time + m_flDelay; ScheduleThink(Spawner, m_flDelay);
return; return;
} }
tracebox(origin, [-16,-16,-16], [16,16,16], origin, FALSE, this); tracebox(origin, [-16,-16,-16], [16,16,16], origin, FALSE, this);
if (trace_startsolid == TRUE) { if (trace_startsolid == TRUE) {
nextthink = time + m_flDelay; ScheduleThink(Spawner, m_flDelay);
return; return;
} }
@ -249,7 +247,7 @@ monstermaker::Spawner(void)
/* shut off for good when we've spawned all we ever wanted */ /* shut off for good when we've spawned all we ever wanted */
if ((m_iTotalMonsters > 0) && m_iMonsterSpawned >= m_iTotalMonsters) { if ((m_iTotalMonsters > 0) && m_iMonsterSpawned >= m_iTotalMonsters) {
think = __NULL__; ReleaseThink();
return; return;
} }
@ -257,7 +255,7 @@ monstermaker::Spawner(void)
if (HasSpawnFlags(MMF_NONTOGGLE)) { if (HasSpawnFlags(MMF_NONTOGGLE)) {
TurnOff(); TurnOff();
} else { } else {
nextthink = time + m_flDelay; ScheduleThink(Spawner, m_flDelay);
} }
} }

View file

@ -262,7 +262,7 @@ multi_manager::Spawned(void)
if ((!HasSpawnFlags(MM_MULTITHREADED) && targetname == argv(i))) if ((!HasSpawnFlags(MM_MULTITHREADED) && targetname == argv(i)))
continue; continue;
m_eTriggers[b].target = argv(i); m_eTriggers[b].SetTriggerTarget(argv(i));
m_eTriggers[b].m_oldstrTarget = argv(i); m_eTriggers[b].m_oldstrTarget = argv(i);
m_eTriggers[b].m_flUntilTriggered = stof(argv(i+1)); m_eTriggers[b].m_flUntilTriggered = stof(argv(i+1));
b++; b++;
@ -303,7 +303,7 @@ multi_manager::Trigger(entity act, int state)
/* If not multi-threaded, we have to watch out 'til all triggers are done. */ /* If not multi-threaded, we have to watch out 'til all triggers are done. */
if (!HasSpawnFlags(MM_MULTITHREADED)) { if (!HasSpawnFlags(MM_MULTITHREADED)) {
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
if (m_eTriggers[i].nextthink > time) { if (m_eTriggers[i].IsThinking() == true) {
return; return;
} }
} }
@ -311,12 +311,11 @@ multi_manager::Trigger(entity act, int state)
/* time to trigger our sub triggers */ /* time to trigger our sub triggers */
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
if (!m_eTriggers[i].target) if (m_eTriggers[i].HasTriggerTarget() == false)
continue; continue;
m_eTriggers[i].think = mm_enttrigger; m_eTriggers[i].ScheduleThink(mm_enttrigger, m_eTriggers[i].m_flUntilTriggered);
m_eTriggers[i].m_iValue = FALSE; m_eTriggers[i].m_iValue = FALSE;
m_eTriggers[i].nextthink = time + m_eTriggers[i].m_flUntilTriggered;
m_eTriggers[i].m_eActivator = act; m_eTriggers[i].m_eActivator = act;
} }
} }

View file

@ -123,19 +123,16 @@ player_loadsaved::Trigger(entity act, int unused)
{ {
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EV_FADE); WriteByte(MSG_MULTICAST, EV_FADE);
WriteFloat(MSG_MULTICAST, m_vecRenderColor[0]); WriteFloat(MSG_MULTICAST, m_vecRenderColor[0]);
WriteFloat(MSG_MULTICAST, m_vecRenderColor[1]); WriteFloat(MSG_MULTICAST, m_vecRenderColor[1]);
WriteFloat(MSG_MULTICAST, m_vecRenderColor[2]); WriteFloat(MSG_MULTICAST, m_vecRenderColor[2]);
WriteFloat(MSG_MULTICAST, m_flRenderAmt); WriteFloat(MSG_MULTICAST, m_flRenderAmt);
WriteFloat(MSG_MULTICAST, m_flFadeDuration); WriteFloat(MSG_MULTICAST, m_flFadeDuration);
WriteFloat(MSG_MULTICAST, m_flFadeHold); WriteFloat(MSG_MULTICAST, m_flFadeHold);
WriteByte(MSG_MULTICAST, 0); WriteByte(MSG_MULTICAST, 0);
msg_entity = world; msg_entity = world;
multicast([0,0,0], MULTICAST_ALL); multicast([0,0,0], MULTICAST_ALL);
env_message_single(act, m_strMessage);
think = ReloadSave; env_message_single(act, m_strMessage);
nextthink = time + m_flLoadTime; ScheduleThink(ReloadSave, m_flLoadTime);
} }

View file

@ -124,24 +124,25 @@ prop_door_rotating::Turn(vector vecDest, void(void) vFunc)
vecAngleDifference = (vecDest - angles); vecAngleDifference = (vecDest - angles);
flTravelLength = vlen(vecAngleDifference); flTravelLength = vlen(vecAngleDifference);
flTravelTime = (flTravelLength / m_flSpeed); flTravelTime = (flTravelLength / m_flSpeed);
avelocity = (vecAngleDifference * (1 / flTravelTime)); SetAngularVelocity(vecAngleDifference * (1 / flTravelTime));
think = vFunc; ScheduleThink(vFunc, flTravelTime);
nextthink = (ltime + flTravelTime);
} }
void void
prop_door_rotating::Closed(void) prop_door_rotating::Closed(void)
{ {
avelocity = [0,0,0]; ClearVelocity();
angles = m_vecDest1; ReleaseThink();
SetAngles(m_vecDest1);
PlayerUse = Interact; PlayerUse = Interact;
} }
void void
prop_door_rotating::Opened(void) prop_door_rotating::Opened(void)
{ {
avelocity = [0,0,0]; ClearVelocity();
angles = m_vecDest2; ReleaseThink();
SetAngles(m_vecDest2);
PlayerUse = Interact; PlayerUse = Interact;
} }
@ -156,15 +157,14 @@ prop_door_rotating::Interact(void)
} }
m_iValue = 1 - m_iValue; m_iValue = 1 - m_iValue;
frame = 1;
frame1time = 0.0f; SetFrame(1);
SetSendFlags(BASEFL_CHANGED_FRAME); SetSendFlags(BASEFL_CHANGED_FRAME);
if (m_iValue) if (m_iValue)
think = TurnAway; ScheduleThink(TurnAway, 0.25f);
else else
think = TurnBack; ScheduleThink(TurnBack, 0.25f);
nextthink = ltime + 0.25f;
PlayerUse = __NULL__; PlayerUse = __NULL__;
} }

View file

@ -165,15 +165,14 @@ random_speaker::Enable(void)
m_iValue = 1; m_iValue = 1;
/* at least wait this amount */ /* at least wait this amount */
r = time + m_flMinPos; r = m_flMinPos;
/* then take the specified percentage of 'wait', by random and add it */ /* then take the specified percentage of 'wait', by random and add it */
r += (m_flMinPos * (m_flRandPercent / 100)) * random(); r += (m_flMinPos * (m_flRandPercent / 100)) * random();
think = PlaySample; ScheduleThink(PlaySample, r);
nextthink = r;
print("^2random_speaker::^3Disable^7: " \ NSLog("^2random_speaker::^3Disable^7: " \
"%s playing %s in %d\n", \ "%s playing %s in %d\n", \
targetname, m_strSample, r); targetname, m_strSample, r);
} }
@ -181,11 +180,10 @@ random_speaker::Enable(void)
void void
random_speaker::Disable(void) random_speaker::Disable(void)
{ {
dprint("^2random_speaker::^3Disable^7: " \ NSLog("^2random_speaker::^3Disable^7: " \
"Disabled %s playing %s\n", \ "Disabled %s playing %s\n", \
targetname, m_strSample); targetname, m_strSample);
m_iValue = 0; m_iValue = 0;
think = __NULL__; ReleaseThink();
nextthink = 0;
} }

View file

@ -210,8 +210,7 @@ scripted_sequence::Respawn(void)
} }
if (m_strIdleAnim) { if (m_strIdleAnim) {
think = InitIdle; ScheduleThink(InitIdle, 0.0f);
nextthink = time + 0.1f;
} }
} }
@ -287,7 +286,7 @@ scripted_sequence::RunOnEntity(entity targ)
/* all the non-moving targets will do this at least */ /* all the non-moving targets will do this at least */
if (m_strActionAnim) { if (m_strActionAnim) {
duration = frameduration(f.modelindex, f.m_flSequenceEnd); duration = frameduration(f.modelindex, f.m_flSequenceEnd);
f.nextthink = time + duration; f.SetNextThink(duration);
NSLog( NSLog(
"\tAnimation: %s Duration: %f seconds (modelindex %d, frame %d)", "\tAnimation: %s Duration: %f seconds (modelindex %d, frame %d)",
m_strActionAnim, m_strActionAnim,
@ -296,7 +295,7 @@ scripted_sequence::RunOnEntity(entity targ)
f.m_flSequenceEnd f.m_flSequenceEnd
); );
} else { } else {
f.nextthink = time; f.SetNextThink(0.0f);
NSLog( NSLog(
"\t^1WARNING: %s skipping animation on script type %i", "\t^1WARNING: %s skipping animation on script type %i",
f.targetname, f.targetname,
@ -307,11 +306,11 @@ scripted_sequence::RunOnEntity(entity targ)
f.m_iSequenceState = SEQUENCESTATE_ENDING; f.m_iSequenceState = SEQUENCESTATE_ENDING;
if (HasSpawnFlags(SSFL_NOSCRIPTMOVE)) if (HasSpawnFlags(SSFL_NOSCRIPTMOVE))
f.think = NSMonster::FreeState; f.SetThink(NSMonster::FreeState);
else else
f.think = NSMonster::FreeStateMoved; f.SetThink(NSMonster::FreeStateMoved);
NSLog("\tEnding: %f", f.nextthink); NSLog("\tEnding: %f", f.GetNextThinkTime());
/* make sure we're forgetting about enemies and attack states in sequence */ /* make sure we're forgetting about enemies and attack states in sequence */
f.m_eEnemy = __NULL__; f.m_eEnemy = __NULL__;

View file

@ -79,7 +79,7 @@ speaker:NSTalkMonster
virtual void(void) Respawn; virtual void(void) Respawn;
virtual void(entity, int) Trigger; virtual void(entity, int) Trigger;
virtual void(void) Annouce; virtual void(void) Announce;
}; };
void void
@ -143,14 +143,12 @@ speaker::Respawn(void)
SetRenderMode(RM_COLOR); SetRenderMode(RM_COLOR);
SetRenderAmt(0); SetRenderAmt(0);
think = Annouce; if (HasSpawnFlags(SPEAKFL_SILENT) == false)
ScheduleThink(Announce, 10.0f);
if (!HasSpawnFlags(SPEAKFL_SILENT))
nextthink = time + 10.0f;
} }
void void
speaker::Annouce(void) speaker::Announce(void)
{ {
string seq = Sentences_GetSamples(m_strSentence); string seq = Sentences_GetSamples(m_strSentence);
@ -165,11 +163,12 @@ speaker::Annouce(void)
msg_entity = this; msg_entity = this;
multicast(origin, MULTICAST_PVS); multicast(origin, MULTICAST_PVS);
nextthink = time + random(30,60); /* ...onto the next announcement */
ScheduleThink(Announce, random(30.0f, 60.0f));
} }
void void
speaker::Trigger(entity eAct, int foo) speaker::Trigger(entity eAct, int foo)
{ {
nextthink = time; ScheduleThink(Announce, 0.0f);
} }

View file

@ -56,7 +56,6 @@ trigger_auto::trigger_auto(void)
{ {
/* default is always toggle */ /* default is always toggle */
m_iTriggerState = TRIG_TOGGLE; m_iTriggerState = TRIG_TOGGLE;
m_strGlobalState = __NULL__;
m_flDelay = 0.0f; m_flDelay = 0.0f;
} }
@ -86,8 +85,7 @@ trigger_auto::Restore(string strKey, string strValue)
void void
trigger_auto::RestoreComplete(void) trigger_auto::RestoreComplete(void)
{ {
think = Processing; ScheduleThink(Processing, 0.25f);
nextthink = time + 0.2f;
} }
void void
@ -110,8 +108,8 @@ trigger_auto::Respawn(void)
{ {
InitPointTrigger(); InitPointTrigger();
think = Processing; /* deliberately add a bit more time in case we're first in the ent-lump */
nextthink = time + 0.2f; ScheduleThink(Processing, 0.25f);
} }
void void
@ -125,7 +123,7 @@ trigger_auto::Processing(void)
print(sprintf("%S %i %f\n", target, m_iTriggerState, m_flDelay)); print(sprintf("%S %i %f\n", target, m_iTriggerState, m_flDelay));
if (HasSpawnFlags(1)) { if (HasSpawnFlags(1)) {
NSLog("^2trigger_auto::^3think^7: %s triggerer removed self", target); NSLog("^2trigger_auto::^3Processing^7: %s triggerer removed self", target);
remove(this); remove(this);
} }
} }

View file

@ -101,8 +101,6 @@ trigger_autosave::Touch(entity eToucher)
eToucher.netname); eToucher.netname);
localcmd("save autosave\n"); localcmd("save autosave\n");
Hide();
SetSolid(SOLID_NOT); SetSolid(SOLID_NOT);
UseTargets(eToucher, TRIG_TOGGLE, m_flDelay); UseTargets(eToucher, TRIG_TOGGLE, m_flDelay);

View file

@ -50,6 +50,8 @@ enumflags
void void
ChangeTarget_Activate(void) ChangeTarget_Activate(void)
{ {
NSTimer foo = __NULL__;
static void Finalize(void) { static void Finalize(void) {
string ctarg = cvar_string("_bsp_changetarget"); string ctarg = cvar_string("_bsp_changetarget");
if (ctarg) { if (ctarg) {
@ -64,10 +66,7 @@ ChangeTarget_Activate(void)
readcmd("set _bsp_changedelay \"\"\n"); readcmd("set _bsp_changedelay \"\"\n");
} }
entity foo = spawn(); foo.TemporaryTimer(self, Finalize, cvar("_bsp_changedelay"), false);
float cdel = cvar("_bsp_changedelay");
foo.think = Finalize;
foo.nextthink = time + 0.1f + cdel;
} }
class class

View file

@ -169,15 +169,14 @@ trigger_multiple::Touch(entity eToucher)
} }
/* if the target key isn't used, assume we're using the new I/O system */ /* if the target key isn't used, assume we're using the new I/O system */
if (!target) if (HasTriggerTarget() == false)
UseOutput(eToucher, m_strOnStartTouch); UseOutput(eToucher, m_strOnStartTouch);
else else
UseTargets(eToucher, TRIG_TOGGLE, m_flDelay); UseTargets(eToucher, TRIG_TOGGLE, m_flDelay);
/* This is effectively a trigger_once...*/ /* This is effectively a trigger_once...*/
if (m_flWait != -1) { if (m_flWait != -1) {
think = Respawn; ScheduleThink(Respawn, m_flWait);
nextthink = time + m_flWait;
} }
SetSolid(SOLID_NOT); SetSolid(SOLID_NOT);

View file

@ -150,7 +150,7 @@ trigger_push::Touch(entity eToucher)
if (eToucher.velocity[2] > 0) { if (eToucher.velocity[2] > 0) {
eToucher.flags &= ~FL_ONGROUND; eToucher.flags &= ~FL_ONGROUND;
} }
Hide(); SetSolid(SOLID_NOT);
} else { } else {
eToucher.basevelocity += vecPush; eToucher.basevelocity += vecPush;
} }

View file

@ -136,6 +136,9 @@ class NSEntity:NSTrigger
virtual void(vector, vector) SetSize; virtual void(vector, vector) SetSize;
virtual void(float) AddFlags; virtual void(float) AddFlags;
virtual void(float) RemoveFlags; virtual void(float) RemoveFlags;
virtual void(void()) SetThink;
virtual void(float) SetNextThink;
virtual void(void(void), float) ScheduleThink;
/* gets */ /* gets */
nonvirtual vector(void) GetSpawnOrigin; nonvirtual vector(void) GetSpawnOrigin;
@ -163,6 +166,10 @@ class NSEntity:NSTrigger
nonvirtual vector(void) GetAbsoluteMins; nonvirtual vector(void) GetAbsoluteMins;
nonvirtual vector(void) GetAbsoluteMaxs; nonvirtual vector(void) GetAbsoluteMaxs;
nonvirtual float(void) GetFlags; nonvirtual float(void) GetFlags;
nonvirtual float(void) GetNextThinkTime;
nonvirtual bool(void) IsThinking;
nonvirtual void(void) ReleaseThink;
nonvirtual void(void) ClearVelocity;
virtual void(void) Show; virtual void(void) Show;
virtual void(void) Hide; virtual void(void) Hide;
@ -186,6 +193,7 @@ class NSEntity:NSTrigger
nonvirtual bool(void) IsOnGround; nonvirtual bool(void) IsOnGround;
nonvirtual entity(void) GetGroundEntity; nonvirtual entity(void) GetGroundEntity;
nonvirtual bool(void) CreatedByMap; nonvirtual bool(void) CreatedByMap;
nonvirtual bool(entity) WithinBounds;
virtual void(entity) Blocked; virtual void(entity) Blocked;
virtual void(entity) StartTouch; virtual void(entity) StartTouch;

View file

@ -677,6 +677,42 @@ NSEntity::RemoveFlags(float fl)
flags &= ~fl; flags &= ~fl;
} }
void
NSEntity::SetThink(void(void) func)
{
think = func;
}
/* FIXME: Why do we need to declare this?! */
#ifdef CSQC
noref .float ltime;
#endif
void
NSEntity::SetNextThink(float fl)
{
float flTime;
/* thinks work differently for pushmovables */
flTime = (movetype == MOVETYPE_PUSH) ? ltime : time;
/* HACK: to make sure things happen post-spawn */
if (flTime <= 0.0f)
flTime = 0.1f;
if (fl >= 0)
nextthink = flTime + fl;
else
NSLog("%s sets bogus nextthink value %f\n", classname, fl);
}
void
NSEntity::ScheduleThink(void(void) func, float fl)
{
SetThink(func);
SetNextThink(fl);
}
vector vector
NSEntity::GetSpawnOrigin(void) NSEntity::GetSpawnOrigin(void)
{ {
@ -821,6 +857,34 @@ NSEntity::GetFlags(void)
return flags; return flags;
} }
float
NSEntity::GetNextThinkTime(void)
{
return nextthink;
}
bool
NSEntity::IsThinking(void)
{
if (movetype == MOVETYPE_PUSH)
return (nextthink > ltime) ? true : false;
else
return (nextthink > time) ? true : false;
}
void
NSEntity::ReleaseThink(void)
{
think = __NULL__;
nextthink = 0.0f;
}
void
NSEntity::ClearVelocity(void)
{
velocity = avelocity = [0.0f, 0.0f, 0.0f];
}
#ifdef SERVER #ifdef SERVER
void void
NSEntity::Respawn(void) NSEntity::Respawn(void)
@ -1058,3 +1122,16 @@ NSEntity::MakeStatic(void)
{ {
makestatic(this); makestatic(this);
} }
bool
NSEntity::WithinBounds(entity check)
{
if not (check.absmin[0] >= absmin[0] && check.absmax[0] <= absmax[0])
return false;
if not (check.absmin[1] >= absmin[1] && check.absmax[1] <= absmax[1])
return false;
if not (check.absmin[2] >= absmin[2] && check.absmax[2] <= absmax[2])
return false;
return true;
}

View file

@ -50,11 +50,15 @@ class NSTrigger:NSIO
float m_flDelay; float m_flDelay;
virtual void(entity, int) Trigger; virtual void(entity, int) Trigger;
virtual void(entity, int, float) UseTargets; virtual void(entity, int, float) UseTargets;
virtual void(string) SetTriggerTarget;
/* master feature */ /* master feature */
nonvirtual int(void) GetValue; nonvirtual int(void) GetValue;
nonvirtual int(void) GetMaster; nonvirtual int(void) GetMaster;
nonvirtual globalstate_t(string) GetGlobalValue; nonvirtual globalstate_t(string) GetGlobalValue;
nonvirtual string(void) GetTriggerTarget;
nonvirtual bool(void) HasTriggerTarget;
nonvirtual bool(void) HasTargetname;
/* overrides */ /* overrides */
virtual void(float) Save; virtual void(float) Save;

View file

@ -87,6 +87,12 @@ NSTrigger::UseTargets(entity act, int state, float fDelay)
} }
} }
void
NSTrigger::SetTriggerTarget(string name)
{
target = name;
}
int int
NSTrigger::GetValue(void) NSTrigger::GetValue(void)
{ {
@ -135,6 +141,30 @@ NSTrigger::GetMaster(void)
return t.GetValue(); return t.GetValue();
} }
string
NSTrigger::GetTriggerTarget(void)
{
return target;
}
bool
NSTrigger::HasTriggerTarget(void)
{
if not (target)
return false;
return true;
}
bool
NSTrigger::HasTargetname(void)
{
if not (targetname)
return false;
return true;
}
void void
NSTrigger::Save(float handle) NSTrigger::Save(float handle)
{ {