From 5605d1d809490cc25b64dfece04a47b8737d5169 Mon Sep 17 00:00:00 2001 From: Juhu <5894800-Juhu_@users.noreply.gitlab.com> Date: Tue, 27 Jun 2023 03:56:31 +0000 Subject: [PATCH] Velocity pads --- qcsrc/common/mapobjects/trigger/jumppads.qc | 233 +++++++++++++++++++- qcsrc/common/mapobjects/trigger/jumppads.qh | 21 +- qcsrc/common/util.qc | 15 ++ 3 files changed, 260 insertions(+), 9 deletions(-) diff --git a/qcsrc/common/mapobjects/trigger/jumppads.qc b/qcsrc/common/mapobjects/trigger/jumppads.qc index 517a2d484..f7281e5e4 100644 --- a/qcsrc/common/mapobjects/trigger/jumppads.qc +++ b/qcsrc/common/mapobjects/trigger/jumppads.qc @@ -14,6 +14,7 @@ void trigger_push_use(entity this, entity actor, entity trigger) #endif REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_PUSH) +REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_PUSH_VELOCITY) REGISTER_NET_LINKED(ENT_CLIENT_TARGET_PUSH) /* @@ -136,7 +137,112 @@ vector trigger_push_calculatevelocity(vector org, entity tgt, float ht, entity p 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, bool already_pushed) +{ + bool is_playerdir_xy = boolean(this.spawnflags & PUSH_VELOCITY_PLAYERDIR_XY); + bool is_add_xy = boolean(this.spawnflags & PUSH_VELOCITY_ADD_XY); + bool is_playerdir_z = boolean(this.spawnflags & PUSH_VELOCITY_PLAYERDIR_Z); + bool is_add_z = boolean(this.spawnflags & PUSH_VELOCITY_ADD_Z); + bool is_bidirectional_xy = boolean(this.spawnflags & PUSH_VELOCITY_BIDIRECTIONAL_XY); + bool is_bidirectional_z = boolean(this.spawnflags & PUSH_VELOCITY_BIDIRECTIONAL_Z); + bool is_clamp_negative_adds = boolean(this.spawnflags & PUSH_VELOCITY_CLAMP_NEGATIVE_ADDS); + + vector sdir = normalize(vec2(pushed_entity.velocity)); + float zdir = pushed_entity.velocity.z; + if(zdir != 0) zdir = copysign(1, zdir); + + vector vs_tgt = '0 0 0'; + float vz_tgt = 0; + if (!is_playerdir_xy || !is_playerdir_z) + { + vector vel_tgt = trigger_push_calculatevelocity(org, tgt, 0, pushed_entity); + vs_tgt = vec2(vel_tgt); + vz_tgt = vel_tgt.z; + + // bidirectional jump pads do not play nicely with xonotic's jump pad targets + if (is_bidirectional_xy) + { + if (normalize(vs_tgt) * sdir < 0) + { + vs_tgt *= -1; + } + } + + if (is_bidirectional_z) + { + if (signbit(vz_tgt) != signbit(zdir)) + { + vz_tgt *= -1; + } + } + } + + vector vs; + if (is_playerdir_xy) + { + vs = sdir * speed; + } + else + { + vs = vs_tgt; + } + + float vz; + if (is_playerdir_z) + { + vz = zdir * count; + } + else + { + vz = vz_tgt; + } + + if (is_add_xy) + { + vector vs_add = vec2(pushed_entity.velocity); + if (already_pushed) + { + vs = vs_add; + } + else + { + vs += vs_add; + + if (is_clamp_negative_adds) + { + if ((normalize(vs) * sdir) < 0) + { + vs = '0 0 0'; + } + } + } + } + + if (is_add_z) + { + float vz_add = pushed_entity.velocity.z; + if (already_pushed) + { + vz = vz_add; + } + else + { + vz += vz_add; + + if (is_clamp_negative_adds) + { + if (signbit(vz) != signbit(zdir)) + { + vz = 0; + } + } + } + } + + return vs + '0 0 1' * vz; +} + +bool jumppad_push(entity this, entity targ, bool is_velocity_pad) { if (!isPushable(targ)) return false; @@ -146,9 +252,29 @@ bool jumppad_push(entity this, entity targ) if(Q3COMPAT_COMMON || this.spawnflags & PUSH_STATIC) org = (this.absmin + this.absmax) * 0.5; + bool already_pushed = false; + if(is_velocity_pad) // remember velocity jump pads + { + if(this == targ.last_pushed || (targ.last_pushed && !STAT(Q3COMPAT, targ))) // if q3compat is active overwrite last stored jump pad, otherwise ignore + { + already_pushed = true; + } + else + { + targ.last_pushed = this; // may be briefly out of sync between client and server if client prediction is toggled + } + } + 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, already_pushed); + } } else if(this.target && this.target != "") { @@ -161,14 +287,31 @@ bool jumppad_push(entity this, entity targ) 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, already_pushed); + } } 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); + if(!is_velocity_pad) UNSET_ONGROUND(targ); #ifdef CSQC if (targ.flags & FL_PROJECTILE) @@ -196,7 +339,7 @@ bool jumppad_push(entity this, entity targ) // prevent sound spam when a player hits the jumppad more than once // or when a dead player gets stuck in the jumppad for some reason - if(this.pushltime < time && !(IS_DEAD(targ) && targ.velocity == '0 0 0')) + if(!already_pushed && this.pushltime < time && !(IS_DEAD(targ) && targ.velocity == '0 0 0')) { // flash when activated Send_Effect(EFFECT_JUMPPAD, targ.origin, targ.velocity, 1); @@ -273,7 +416,7 @@ void trigger_push_touch(entity this, entity toucher) 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)) @@ -285,6 +428,19 @@ void trigger_push_touch(entity this, entity toucher) #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); + + jumppad_push(this, toucher, true); +} + #ifdef SVQC void trigger_push_link(entity this); void trigger_push_updatelink(entity this); @@ -580,6 +736,21 @@ float trigger_push_send(entity this, entity to, float sf) 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; @@ -590,6 +761,11 @@ void trigger_push_link(entity this) trigger_link(this, trigger_push_send); } +void trigger_push_velocity_link(entity this) +{ + trigger_link(this, trigger_push_velocity_send); +} + /* * ENTITY PARAMETERS: * @@ -629,6 +805,29 @@ spawnfunc(trigger_push) 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) +{ + WarpZoneLib_ExactTrigger_Init(this, false); + BITSET_ASSIGN(this.effects, EF_NODEPTHTEST); + 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 +} + bool target_push_send(entity this, entity to, float sf) { @@ -648,7 +847,7 @@ void target_push_use(entity this, entity actor, entity trigger) 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) @@ -710,6 +909,24 @@ NET_HANDLE(ENT_CLIENT_TRIGGER_PUSH, bool isnew) 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; + + return true; +} + void target_push_remove(entity this) { // strfree(this.classname); diff --git a/qcsrc/common/mapobjects/trigger/jumppads.qh b/qcsrc/common/mapobjects/trigger/jumppads.qh index c994bc610..7146cc52c 100644 --- a/qcsrc/common/mapobjects/trigger/jumppads.qh +++ b/qcsrc/common/mapobjects/trigger/jumppads.qh @@ -5,6 +5,14 @@ const int PUSH_ONCE = BIT(0); // legacy, deactivate with relay instead const int PUSH_SILENT = BIT(1); // not used? const int PUSH_STATIC = BIT(12); // xonotic-only, Q3 already behaves like this by default +#define PUSH_VELOCITY_PLAYERDIR_XY BIT(0) +#define PUSH_VELOCITY_ADD_XY BIT(1) +#define PUSH_VELOCITY_PLAYERDIR_Z BIT(2) +#define PUSH_VELOCITY_ADD_Z BIT(3) +#define PUSH_VELOCITY_BIDIRECTIONAL_XY BIT(4) +#define PUSH_VELOCITY_BIDIRECTIONAL_Z BIT(5) +#define PUSH_VELOCITY_CLAMP_NEGATIVE_ADDS BIT(6) + IntrusiveList g_jumppads; STATIC_INIT(g_jumppads) { g_jumppads = IL_NEW(); } @@ -12,6 +20,8 @@ STATIC_INIT(g_jumppads) { g_jumppads = IL_NEW(); } .bool istypefrag; .float height; +.entity last_pushed; + const int NUM_JUMPPADSUSED = 3; .float jumppadcount; .entity jumppadsused[NUM_JUMPPADSUSED]; @@ -44,7 +54,7 @@ bool trigger_push_test(entity this, entity item); void trigger_push_findtarget(entity this); /* - * ENTITY PARAMETERS: + * ENTITY PARAMETERS trigger_push: * * target: target of jump * height: the absolute value is the height of the highest point of the jump @@ -55,8 +65,17 @@ void trigger_push_findtarget(entity this); * 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 trigger_push_velocity: + * + * 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); diff --git a/qcsrc/common/util.qc b/qcsrc/common/util.qc index df54e3536..08bd71298 100644 --- a/qcsrc/common/util.qc +++ b/qcsrc/common/util.qc @@ -1677,6 +1677,21 @@ void Skeleton_SetBones(entity e) string to_execute_next_frame; void execute_next_frame() { +#ifdef SVQC + IL_EACH(g_moveables, it.last_pushed, + { + if(!WarpZoneLib_ExactTrigger_Touch(it.last_pushed, it, false)) + { + it.last_pushed = NULL; + } + }); +#elif defined(CSQC) + if(csqcplayer.last_pushed && !WarpZoneLib_ExactTrigger_Touch(csqcplayer.last_pushed, csqcplayer, false)) + { + csqcplayer.last_pushed = NULL; + } +#endif + if(to_execute_next_frame) { localcmd("\n", to_execute_next_frame, "\n"); -- 2.39.2