#endif
REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_PUSH)
+REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_PUSH_VELOCITY)
REGISTER_NET_LINKED(ENT_CLIENT_TARGET_PUSH)
/*
return sdir * vs + '0 0 1' * vz;
}
-bool jumppad_push(entity this, entity targ)
+vector trigger_push_velocity_calculatevelocity(entity this, vector org, entity tgt, float speed, float count, entity pushed_entity)
+{
+ vector sdir = normalize(vec2(pushed_entity.velocity));
+ float zdir = copysign(1, pushed_entity.velocity.z);
+ if(pushed_entity.velocity.z == 0) zdir = 1; // copysign is negative on zero, we don't want that
+
+ vector vs_tgt = '0 0 0';
+ float vz_tgt = 0;
+ if (!(this.spawnflags & PLAYERDIR_XY) || !(this.spawnflags & PLAYERDIR_Z))
+ {
+ vector vel_tgt = trigger_push_calculatevelocity(org, tgt, 0, pushed_entity);
+ vs_tgt = vec2(vel_tgt);
+ vz_tgt = vel_tgt.z;
+
+ if (this.spawnflags & BIDIRECTIONAL_XY)
+ {
+ if (normalize(vs_tgt) * sdir < 0)
+ {
+ vs_tgt *= -1;
+ }
+ }
+
+ if (this.spawnflags & BIDIRECTIONAL_Z)
+ {
+ if (signbit(vz_tgt) != signbit(zdir))
+ {
+ vz_tgt *= -1;
+ }
+ }
+ }
+
+ vector vs;
+ if (this.spawnflags & PLAYERDIR_XY)
+ {
+ vs = sdir * speed;
+ }
+ else
+ {
+ vs = vs_tgt;
+ }
+
+ float vz;
+ if (this.spawnflags & PLAYERDIR_Z)
+ {
+ vz = zdir * count;
+ }
+ else
+ {
+ vz = vz_tgt;
+ }
+
+ if (this.spawnflags & ADD_XY)
+ {
+ vs += vec2(pushed_entity.velocity);
+ if (this.spawnflags & CLAMP_NEGATIVE_ADDS)
+ {
+ if ((normalize(vs) * sdir) < 0)
+ {
+ vs = '0 0 0';
+ }
+ }
+ }
+
+ if (this.spawnflags & ADD_Z)
+ {
+ vz += pushed_entity.velocity.z;
+ if (this.spawnflags & CLAMP_NEGATIVE_ADDS)
+ {
+ if (signbit(vz) != signbit(zdir))
+ {
+ vz = 0;
+ }
+ }
+ }
+
+ return vs + '0 0 1' * vz;
+}
+
+void check_pushed(entity this)
+{
+ IL_EACH(this.pushed, WarpZoneLib_ExactTrigger_Touch(this, it),
+ {
+ IL_REMOVE(this.pushed, it);
+ });
+ if(!IL_EMPTY(this.pushed))
+ {
+ this.nextthink = time;
+ }
+}
+
+bool jumppad_push(entity this, entity targ, bool is_velocity_pad)
{
if (!isPushable(targ))
return false;
+ if(is_velocity_pad && IL_CONTAINS(this.pushed, targ))
+ return false;
+
+ if(is_velocity_pad)
+ {
+ IL_PUSH(this.pushed, targ); // may be briefly out of sync between client and server if client prediction is toggled
+ this.nextthink = time;
+ }
+
vector org = targ.origin;
if(STAT(Q3COMPAT))
if(this.enemy)
{
- targ.velocity = trigger_push_calculatevelocity(org, this.enemy, this.height, targ);
+ if(!is_velocity_pad)
+ {
+ targ.velocity = trigger_push_calculatevelocity(org, this.enemy, this.height, targ);
+ }
+ else
+ {
+ targ.velocity = trigger_push_velocity_calculatevelocity(this, org, this.enemy, this.speed, this.count, targ);
+ }
}
else if(this.target && this.target != "")
{
else
RandomSelection_AddEnt(e, 1, 1);
}
- targ.velocity = trigger_push_calculatevelocity(org, RandomSelection_chosen_ent, this.height, targ);
+ if(!is_velocity_pad)
+ {
+ targ.velocity = trigger_push_calculatevelocity(org, RandomSelection_chosen_ent, this.height, targ);
+ }
+ else
+ {
+ targ.velocity = trigger_push_velocity_calculatevelocity(this, org, RandomSelection_chosen_ent, this.speed, this.count, targ);
+ }
}
else
{
- targ.velocity = this.movedir;
+ if(!is_velocity_pad)
+ {
+ targ.velocity = this.movedir;
+ }
+ else
+ {
+#ifdef SVQC
+ objerror (this, "Jumppad with no target");
+#endif
+ return false;
+ }
}
UNSET_ONGROUND(targ);
EXACTTRIGGER_TOUCH(this, toucher);
- noref bool success = jumppad_push(this, toucher);
+ noref bool success = jumppad_push(this, toucher, false);
#ifdef SVQC
if (success && (this.spawnflags & PUSH_ONCE))
#endif
}
+void trigger_push_velocity_touch(entity this, entity toucher)
+{
+ if (this.active == ACTIVE_NOT)
+ return;
+
+ if(this.team && DIFF_TEAM(this, toucher))
+ return;
+
+ EXACTTRIGGER_TOUCH(this, toucher);
+
+ noref bool success = jumppad_push(this, toucher, true);
+}
+
#ifdef SVQC
void trigger_push_link(entity this);
void trigger_push_updatelink(entity this);
return true;
}
+float trigger_push_velocity_send(entity this, entity to, float sf)
+{
+ WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_PUSH_VELOCITY);
+
+ WriteByte(MSG_ENTITY, this.team);
+ WriteInt24_t(MSG_ENTITY, this.spawnflags);
+ WriteByte(MSG_ENTITY, this.active);
+ WriteCoord(MSG_ENTITY, this.speed);
+ WriteCoord(MSG_ENTITY, this.count);
+
+ trigger_common_write(this, true);
+
+ return true;
+}
+
void trigger_push_updatelink(entity this)
{
this.SendFlags |= SF_TRIGGER_INIT;
trigger_link(this, trigger_push_send);
}
+void trigger_push_velocity_link(entity this)
+{
+ trigger_link(this, trigger_push_velocity_send);
+}
+
/*
* ENTITY PARAMETERS:
*
InitializeEntity(this, trigger_push_findtarget, INITPRIO_FINDTARGET);
}
+/*
+ * ENTITY PARAMETERS:
+ *
+ * target: this points to the target_position to which the player will jump.
+ * speed: XY speed for player-directional velocity pads - either sets or adds to the player's horizontal velocity.
+ * count: Z speed for player-directional velocity pads - either sets or adds to the player's vertical velocity.
+ */
+spawnfunc(trigger_push_velocity)
+{
+ SetMovedir(this);
+
+ trigger_init(this);
+
+ this.active = ACTIVE_ACTIVE;
+ this.use = trigger_push_use;
+ settouch(this, trigger_push_velocity_touch);
+
+ // normal push setup
+ if (!this.noise)
+ this.noise = "misc/jumppad.wav";
+ precache_sound (this.noise);
+
+ trigger_push_velocity_link(this); // link it now
+
+ this.pushed = IL_NEW();
+ setthink(this, check_pushed);
+}
+
bool target_push_send(entity this, entity to, float sf)
{
if(trigger.classname == "trigger_push" || trigger == this)
return; // WTF, why is this a thing
- jumppad_push(this, actor);
+ jumppad_push(this, actor, false);
}
void target_push_link(entity this)
return true;
}
+NET_HANDLE(ENT_CLIENT_TRIGGER_PUSH_VELOCITY, bool isnew)
+{
+ int mytm = ReadByte(); if(mytm) { this.team = mytm - 1; }
+ this.spawnflags = ReadInt24_t();
+ this.active = ReadByte();
+ this.speed = ReadCoord();
+ this.count = ReadCoord();
+
+ trigger_common_read(this, true);
+
+ this.entremove = trigger_remove_generic;
+ this.solid = SOLID_TRIGGER;
+ settouch(this, trigger_push_velocity_touch);
+ this.move_time = time;
+ this.pushed = IL_NEW();
+ setthink(this, check_pushed);
+
+ return true;
+}
+
void target_push_remove(entity this)
{
// strfree(this.classname);
const int PUSH_ONCE = BIT(0); // legacy, deactivate with relay instead
const int PUSH_SILENT = BIT(1); // not used?
+const int PLAYERDIR_XY = BIT(0); // if set, trigger will apply the horizontal speed in the player's horizontal direction of travel, otherwise it uses the target XY component.
+const int ADD_XY = BIT(1); // if set, trigger will add to the player's horizontal velocity, otherwise it sets the player's horizontal velocity.
+const int PLAYERDIR_Z = BIT(2); // if set, trigger will apply the vertical speed in the player's vertical direction of travel, otherwise it uses the target Z component.
+const int ADD_Z = BIT(3); // if set, trigger will add to the player's vertical velocity, otherwise it sets the player's vertical velocity.
+const int BIDIRECTIONAL_XY = BIT(4); // if set, non-playerdir velocity pads will function in 2 directions based on the target specified. The chosen direction is based on the current direction of travel. Applies to horizontal direction.
+const int BIDIRECTIONAL_Z = BIT(5); // if set, non-playerdir velocity pads will function in 2 directions based on the target specified. The chosen direction is based on the current direction of travel. Applies to vertical direction.
+const int CLAMP_NEGATIVE_ADDS = BIT(6); // if set, then a velocity pad that adds negative velocity will be clamped to 0, if the resultant velocity would bounce the player in the opposite direction.
+
IntrusiveList g_jumppads;
STATIC_INIT(g_jumppads) { g_jumppads = IL_NEW(); }
.float pushltime;
.bool istypefrag;
.float height;
+.IntrusiveList pushed;
const int NUM_JUMPPADSUSED = 3;
.float jumppadcount;
* values to target a point on the ceiling.
* movedir: if target is not set, this * speed * 10 is the velocity to be reached.
*/
+
+/*
+ * ENTITY PARAMETERS:
+ *
+ * target: this points to the target_position to which the player will jump.
+ * speed: XY speed for player-directional velocity pads - either sets or adds to the player's horizontal velocity.
+ * count: Z speed for player-directional velocity pads - either sets or adds to the player's vertical velocity.
+ */
#ifdef SVQC
spawnfunc(trigger_push);
+spawnfunc(trigger_push_velocity);
spawnfunc(target_push);
spawnfunc(info_notnull);