From 7c64be44e0325bd7f3772110d2c4b7d561c864ff Mon Sep 17 00:00:00 2001 From: bones_was_here Date: Fri, 17 Jan 2025 19:08:29 +1000 Subject: [PATCH] ka, tka: draw and animate ball(s) while carried, support multiple balls Removes g_*_ballcarrier_effects cvars because they're now redundant because the ball's effects are now visible while it's carried. --- gamemodes-server.cfg | 6 +- .../gamemode/keepaway/sv_keepaway.qc | 73 +++++++++++-------- .../gamemode/keepaway/sv_keepaway.qh | 2 - qcsrc/common/gamemodes/gamemode/tka/sv_tka.qc | 73 +++++++++++-------- qcsrc/common/gamemodes/gamemode/tka/sv_tka.qh | 2 - 5 files changed, 92 insertions(+), 64 deletions(-) diff --git a/gamemodes-server.cfg b/gamemodes-server.cfg index 260d9e7e8..5a443924b 100644 --- a/gamemodes-server.cfg +++ b/gamemodes-server.cfg @@ -435,7 +435,7 @@ set g_keepaway 0 "Keepaway: game mode which focuses around a ball" // script-ign set g_keepaway_score_bckill 1 "points for killing the ball carrier (Ball Carrier Kill)" set g_keepaway_score_killac 1 "points for kills while holding the ball (Kill As Carrier)" set g_keepaway_score_timepoints 0 "points to add to ball carrier's score per second" -set g_keepaway_ballcarrier_effects 8 "add together the numbers you want; EF_ADDITIVE (32) / EF_NODEPTHTEST (8192) / EF_DIMLIGHT (8)" +set g_keepaway_ballcarrier_maxballs 1 "how many balls a single player may carry at once" set g_keepaway_ballcarrier_highspeed 1 "speed multiplier done to the person holding the ball (recommended when used with some mutators)" set g_keepaway_ballcarrier_damage 1 "damage multiplier while holding the ball" set g_keepaway_ballcarrier_force 1 "force multiplier while holding the ball" @@ -446,6 +446,7 @@ set g_keepaway_noncarrier_damage 1 "damage done to other players if both you and set g_keepaway_noncarrier_force 1 "force done to other players if both you and they don't have the ball" set g_keepaway_noncarrier_selfdamage 1 "self damage if you don't have the ball" set g_keepaway_noncarrier_selfforce 1 "self force if you don't have the ball" +set g_keepawayball_count 1 "how many balls to spawn" set g_keepawayball_effects 8 "add together the numbers you want; EF_ADDITIVE (32) / EF_NODEPTHTEST (8192) / EF_DIMLIGHT (8)" set g_keepawayball_trail_color 254 "particle trail color from player/ball" set g_keepawayball_damageforcescale 2 "scale of force which is applied to the ball by weapons/explosions/etc" @@ -674,7 +675,7 @@ set g_tka_score_team 1 "allow points to be awarded to teammates for any kill whe set g_tka_score_bckill 1 "points for killing the ball carrier (Ball Carrier Kill)" set g_tka_score_killac 1 "points for kills while holding the ball (Kill As Carrier)" set g_tka_score_timepoints 0 "points to add to ball carrier's team's score per second" -set g_tka_ballcarrier_effects 8 "add together the numbers you want; EF_ADDITIVE (32) / EF_NODEPTHTEST (8192) / EF_DIMLIGHT (8)" +set g_tka_ballcarrier_maxballs 1 "how many balls a single player may carry at once" set g_tka_ballcarrier_highspeed 1 "speed multiplier done to the person holding the ball (recommended when used with some mutators)" set g_tka_ballcarrier_damage 1 "damage multiplier while holding the ball" set g_tka_ballcarrier_force 1 "force multiplier while holding the ball" @@ -685,6 +686,7 @@ set g_tka_noncarrier_damage 1 "damage done to other players if both you and they set g_tka_noncarrier_force 1 "force done to other players if both you and they don't have the ball" set g_tka_noncarrier_selfdamage 1 "self damage if you don't have the ball" set g_tka_noncarrier_selfforce 1 "self force if you don't have the ball" +set g_tkaball_count 1 "how many balls to spawn" set g_tkaball_effects 8 "add together the numbers you want; EF_ADDITIVE (32) / EF_NODEPTHTEST (8192) / EF_DIMLIGHT (8)" set g_tkaball_trail_color 254 "particle trail color from player/ball" set g_tkaball_damageforcescale 2 "scale of force which is applied to the ball by weapons/explosions/etc" diff --git a/qcsrc/common/gamemodes/gamemode/keepaway/sv_keepaway.qc b/qcsrc/common/gamemodes/gamemode/keepaway/sv_keepaway.qc index 7944863fb..d81d05b3f 100644 --- a/qcsrc/common/gamemodes/gamemode/keepaway/sv_keepaway.qc +++ b/qcsrc/common/gamemodes/gamemode/keepaway/sv_keepaway.qc @@ -11,7 +11,7 @@ .entity ballcarried; .entity previous_owner; // also used on kh keys -int autocvar_g_keepaway_ballcarrier_effects; +int autocvar_g_keepaway_ballcarrier_maxballs; float autocvar_g_keepaway_ballcarrier_damage; float autocvar_g_keepaway_ballcarrier_force; float autocvar_g_keepaway_ballcarrier_highspeed; @@ -25,6 +25,7 @@ bool autocvar_g_keepaway_noncarrier_warn; int autocvar_g_keepaway_score_bckill; int autocvar_g_keepaway_score_killac; int autocvar_g_keepaway_score_timepoints; +int autocvar_g_keepawayball_count; float autocvar_g_keepawayball_damageforcescale; int autocvar_g_keepawayball_effects; float autocvar_g_keepawayball_respawntime; @@ -90,13 +91,22 @@ MUTATOR_HOOKFUNCTION(ka, reset_map_global) } /// runs (only) while a player is carrying the ball -void ka_TimeScoring(entity this) +void ka_BallThink_Carried(entity this) { if (autocvar_g_keepaway_score_timepoints) GameRules_scoring_add_float2int(this.owner, SCORE, autocvar_g_keepaway_score_timepoints * frametime, timepoints_accum, 1); GameRules_scoring_add(this.owner, KEEPAWAY_BCTIME, frametime); this.nextthink = time; + + // animate, this is ~copied from KH +#define BALL_XYSPEED 100 // KH 45 +#define BALL_XYDIST 24 // KH 24 + makevectors(vec3(0, (360 * this.cnt / this.owner.ballcarried.cnt) + (time % 360) * BALL_XYSPEED, 0)); + setorigin(this, vec3(v_forward.x * BALL_XYDIST, v_forward.y * BALL_XYDIST, this.origin.z)); + + // sync any invisibility effect + this.alpha = this.owner.alpha; } void ka_DamageEvent(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) @@ -115,7 +125,6 @@ void ka_TouchEvent(entity this, entity toucher) // runs any time that the ball c ka_RespawnBall(this); return; } - if(toucher.ballcarried) { return; } if(IS_INDEPENDENT_PLAYER(toucher)) { return; } if(IS_DEAD(toucher)) { return; } if(STAT(FROZEN, toucher)) { return; } @@ -128,6 +137,16 @@ void ka_TouchEvent(entity this, entity toucher) // runs any time that the ball c else if(this.wait > time && this.previous_owner == toucher) return; + if (toucher.ballcarried) // multiple balls exist + { + if (toucher.ballcarried.cnt >= autocvar_g_keepaway_ballcarrier_maxballs) + return; + this.ballcarried = toucher.ballcarried; // new ball will be inserted at start of chain + this.cnt = toucher.ballcarried.cnt + 1; // for orbit animation offset + } + else + this.cnt = 1; + // attach the ball to the player this.owner = toucher; toucher.ballcarried = this; @@ -135,12 +154,12 @@ void ka_TouchEvent(entity this, entity toucher) // runs any time that the ball c setattachment(this, toucher, ""); setorigin(this, '0 0 0'); - // make the ball invisible/unable to do anything/set up time scoring + // make the ball unable to do anything, set up time scoring this.velocity = '0 0 0'; set_movetype(this, MOVETYPE_NONE); - this.effects |= EF_NODRAW; + this.scale = 12/16; // somewhat smaller while carried settouch(this, func_null); - setthink(this, ka_TimeScoring); + setthink(this, ka_BallThink_Carried); this.nextthink = time; this.takedamage = DAMAGE_NO; this.event_damage = func_null; @@ -148,11 +167,6 @@ void ka_TouchEvent(entity this, entity toucher) // runs any time that the ball c IL_REMOVE(g_damagedbycontents, this); navigation_dynamicgoal_unset(this); - // apply effects to player - toucher.glow_color = autocvar_g_keepawayball_trail_color; - toucher.glow_trail = true; - toucher.effects |= autocvar_g_keepaway_ballcarrier_effects; - // messages and sounds ka_EventLog("pickup", toucher); Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_KEEPAWAY_PICKUP, toucher.netname); @@ -176,10 +190,6 @@ void ka_PlayerReset(entity player) player.ballcarried = NULL; GameRules_scoring_vip(player, false); WaypointSprite_Kill(player.waypointsprite_attachedforcarrier); - - // reset the player effects - player.glow_trail = false; - player.effects &= ~autocvar_g_keepaway_ballcarrier_effects; } void ka_DropEvent(entity player) // runs any time that a player is supposed to lose the ball @@ -200,8 +210,9 @@ void ka_DropEvent(entity player) // runs any time that a player is supposed to l ball.event_damage = ka_DamageEvent; ball.damagedbycontents = true; IL_PUSH(g_damagedbycontents, ball); - ball.effects &= ~EF_NODRAW; - setorigin(ball, player.origin + '0 0 10'); + ball.scale = 1; // it's smaller while carried + ball.alpha = 1; // in case the carrier had an invisibility effect + setorigin(ball, player.origin + ball.origin + '0 0 10'); // include attachment offset to reduce jump nudgeoutofsolid_OrFallback(ball); // a ball has a horizontally bigger bbox than a player ball.velocity = '0 0 200' + '0 100 0'*crandom() + '100 0 0'*crandom(); ball.owner = NULL; @@ -221,7 +232,13 @@ void ka_DropEvent(entity player) // runs any time that a player is supposed to l WaypointSprite_Ping(ball.waypointsprite_attachedforcarrier); } - ka_PlayerReset(player); + if (ball.ballcarried) // >1 ball was chained, first one was just dropped + { + player.ballcarried = ball.ballcarried; // move the next one up + ball.ballcarried = NULL; // prevent infinite loop + } + else // no balls remaining so remove bc status + ka_PlayerReset(player); } .bool pushable; @@ -272,7 +289,7 @@ void ka_SpawnBalls() ++i; } - while (i < KA_BALL_COUNT); + while (i < autocvar_g_keepawayball_count); } void ka_Handler_CheckBall(entity this) @@ -409,7 +426,8 @@ MUTATOR_HOOKFUNCTION(ka, PlayerDies) GameRules_scoring_add(frag_attacker, SCORE, autocvar_g_keepaway_score_killac); } - if(frag_target.ballcarried) { ka_DropEvent(frag_target); } // a player with the ball has died, drop it + while (frag_target.ballcarried) + ka_DropEvent(frag_target); // a player with ball(s) has died, drop them } MUTATOR_HOOKFUNCTION(ka, GiveFragsForKill) @@ -485,27 +503,24 @@ MUTATOR_HOOKFUNCTION(ka, ClientDisconnect) { entity player = M_ARGV(0, entity); - if(player.ballcarried) { ka_DropEvent(player); } // a player with the ball has left the match, drop it + while (player.ballcarried) + ka_DropEvent(player); // a player with ball(s) has left the match, drop them } MUTATOR_HOOKFUNCTION(ka, MakePlayerObserver) { entity player = M_ARGV(0, entity); - if(player.ballcarried) { ka_DropEvent(player); } // a player with the ball has left the match, drop it + while (player.ballcarried) + ka_DropEvent(player); // a player with ball(s) has left the match, drop them } MUTATOR_HOOKFUNCTION(ka, PlayerPowerups) { - entity player = M_ARGV(0, entity); +// entity player = M_ARGV(0, entity); // In the future this hook is supposed to allow me to do some extra stuff with waypointsprites and invisibility powerup // So bare with me until I can fix a certain bug with ka_ballcarrier_waypointsprite_visible_for_player() - - player.effects &= ~autocvar_g_keepaway_ballcarrier_effects; - - if(player.ballcarried) - player.effects |= autocvar_g_keepaway_ballcarrier_effects; } @@ -549,6 +564,6 @@ MUTATOR_HOOKFUNCTION(ka, DropSpecialItems) { entity frag_target = M_ARGV(0, entity); - if(frag_target.ballcarried) + while (frag_target.ballcarried) ka_DropEvent(frag_target); } diff --git a/qcsrc/common/gamemodes/gamemode/keepaway/sv_keepaway.qh b/qcsrc/common/gamemodes/gamemode/keepaway/sv_keepaway.qh index a06a937b1..736a65539 100644 --- a/qcsrc/common/gamemodes/gamemode/keepaway/sv_keepaway.qh +++ b/qcsrc/common/gamemodes/gamemode/keepaway/sv_keepaway.qh @@ -25,8 +25,6 @@ REGISTER_MUTATOR(ka, false) return false; } -const int KA_BALL_COUNT = 1; - void(entity this) havocbot_role_ka_carrier; void(entity this) havocbot_role_ka_collector; diff --git a/qcsrc/common/gamemodes/gamemode/tka/sv_tka.qc b/qcsrc/common/gamemodes/gamemode/tka/sv_tka.qc index 5180ef224..36ee08fd8 100644 --- a/qcsrc/common/gamemodes/gamemode/tka/sv_tka.qc +++ b/qcsrc/common/gamemodes/gamemode/tka/sv_tka.qc @@ -6,7 +6,7 @@ .entity ballcarried; .entity previous_owner; // also used on kh keys -int autocvar_g_tka_ballcarrier_effects; +int autocvar_g_tka_ballcarrier_maxballs; float autocvar_g_tka_ballcarrier_damage; float autocvar_g_tka_ballcarrier_force; float autocvar_g_tka_ballcarrier_highspeed; @@ -21,6 +21,7 @@ int autocvar_g_tka_score_bckill; int autocvar_g_tka_score_killac; bool autocvar_g_tka_score_team; int autocvar_g_tka_score_timepoints; +int autocvar_g_tkaball_count; float autocvar_g_tkaball_damageforcescale; int autocvar_g_tkaball_effects; float autocvar_g_tkaball_respawntime; @@ -87,13 +88,22 @@ MUTATOR_HOOKFUNCTION(tka, reset_map_global) } /// runs (only) while a player is carrying the ball -void tka_TimeScoring(entity this) +void tka_BallThink_Carried(entity this) { if (autocvar_g_tka_score_timepoints) GameRules_scoring_add_team_float2int(this.owner, SCORE, autocvar_g_tka_score_timepoints * frametime, timepoints_accum, 1); GameRules_scoring_add(this.owner, TKA_BCTIME, frametime); this.nextthink = time; + + // animate, this is ~copied from KH +#define BALL_XYSPEED 100 // KH 45 +#define BALL_XYDIST 24 // KH 24 + makevectors(vec3(0, (360 * this.cnt / this.owner.ballcarried.cnt) + (time % 360) * BALL_XYSPEED, 0)); + setorigin(this, vec3(v_forward.x * BALL_XYDIST, v_forward.y * BALL_XYDIST, this.origin.z)); + + // sync any invisibility effect + this.alpha = this.owner.alpha; } void tka_TouchEvent(entity this, entity toucher) // runs any time that the ball comes in contact with something @@ -106,7 +116,6 @@ void tka_TouchEvent(entity this, entity toucher) // runs any time that the ball tka_RespawnBall(this); return; } - if(toucher.ballcarried) { return; } if(IS_INDEPENDENT_PLAYER(toucher)) { return; } if(IS_DEAD(toucher)) { return; } if(STAT(FROZEN, toucher)) { return; } @@ -119,6 +128,16 @@ void tka_TouchEvent(entity this, entity toucher) // runs any time that the ball else if(this.wait > time && this.previous_owner == toucher) return; + if (toucher.ballcarried) // multiple balls exist + { + if (toucher.ballcarried.cnt >= autocvar_g_tka_ballcarrier_maxballs) + return; + this.ballcarried = toucher.ballcarried; // new ball will be inserted at start of chain + this.cnt = toucher.ballcarried.cnt + 1; // for orbit animation offset + } + else + this.cnt = 1; + // attach the ball to the player this.owner = toucher; toucher.ballcarried = this; @@ -126,21 +145,16 @@ void tka_TouchEvent(entity this, entity toucher) // runs any time that the ball setattachment(this, toucher, ""); setorigin(this, '0 0 0'); - // make the ball invisible/unable to do anything/set up time scoring + // make the ball unable to do anything, set up time scoring this.velocity = '0 0 0'; set_movetype(this, MOVETYPE_NONE); - this.effects |= EF_NODRAW; + this.scale = 12/16; // somewhat smaller while carried settouch(this, func_null); - setthink(this, tka_TimeScoring); + setthink(this, tka_BallThink_Carried); this.nextthink = time; this.takedamage = DAMAGE_NO; navigation_dynamicgoal_unset(this); - // apply effects to player - toucher.glow_color = autocvar_g_tkaball_trail_color; - toucher.glow_trail = true; - toucher.effects |= autocvar_g_tka_ballcarrier_effects; - // messages and sounds tka_EventLog("pickup", toucher); Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_KEEPAWAY_PICKUP, toucher.netname); @@ -173,10 +187,6 @@ void tka_PlayerReset(entity player) player.ballcarried = NULL; GameRules_scoring_vip(player, false); WaypointSprite_Kill(player.waypointsprite_attachedforcarrier); - - // reset the player effects - player.glow_trail = false; - player.effects &= ~autocvar_g_tka_ballcarrier_effects; } void tka_DropEvent(entity player) // runs any time that a player is supposed to lose the ball @@ -195,8 +205,9 @@ void tka_DropEvent(entity player) // runs any time that a player is supposed to setthink(ball, tka_RespawnBall); ball.nextthink = time + autocvar_g_tkaball_respawntime; ball.takedamage = DAMAGE_YES; - ball.effects &= ~EF_NODRAW; - setorigin(ball, player.origin + '0 0 10'); + ball.scale = 1; // it's smaller while carried + ball.alpha = 1; // in case the carrier had an invisibility effect + setorigin(ball, player.origin + ball.origin + '0 0 10'); // include attachment offset to reduce jump nudgeoutofsolid_OrFallback(ball); // a ball has a horizontally bigger bbox than a player ball.velocity = '0 0 200' + '0 100 0'*crandom() + '100 0 0'*crandom(); ball.owner = NULL; @@ -216,7 +227,13 @@ void tka_DropEvent(entity player) // runs any time that a player is supposed to WaypointSprite_Ping(ball.waypointsprite_attachedforcarrier); } - tka_PlayerReset(player); + if (ball.ballcarried) // >1 ball was chained, first one was just dropped + { + player.ballcarried = ball.ballcarried; // move the next one up + ball.ballcarried = NULL; // prevent infinite loop + } + else // no balls remaining so remove bc status + tka_PlayerReset(player); } .bool pushable; @@ -264,7 +281,7 @@ void tka_SpawnBalls() ++i; } - while (i < TKA_BALL_COUNT); + while (i < autocvar_g_tkaball_count); } void tka_Handler_CheckBall(entity this) @@ -412,7 +429,8 @@ MUTATOR_HOOKFUNCTION(tka, PlayerDies) GameRules_scoring_add_team(frag_attacker, SCORE, autocvar_g_tka_score_killac); } - if(frag_target.ballcarried) { tka_DropEvent(frag_target); } // a player with the ball has died, drop it + while (frag_target.ballcarried) + tka_DropEvent(frag_target); // a player with ball(s) has died, drop them } MUTATOR_HOOKFUNCTION(tka, GiveFragsForKill) @@ -514,27 +532,24 @@ MUTATOR_HOOKFUNCTION(tka, ClientDisconnect) { entity player = M_ARGV(0, entity); - if(player.ballcarried) { tka_DropEvent(player); } // a player with the ball has left the match, drop it + while (player.ballcarried) + tka_DropEvent(player); // a player with ball(s) has left the match, drop them } MUTATOR_HOOKFUNCTION(tka, MakePlayerObserver) { entity player = M_ARGV(0, entity); - if(player.ballcarried) { tka_DropEvent(player); } // a player with the ball has left the match, drop it + while (player.ballcarried) + tka_DropEvent(player); // a player with ball(s) has left the match, drop them } MUTATOR_HOOKFUNCTION(tka, PlayerPowerups) { - entity player = M_ARGV(0, entity); +// entity player = M_ARGV(0, entity); // In the future this hook is supposed to allow me to do some extra stuff with waypointsprites and invisibility powerup // So bare with me until I can fix a certain bug with tka_ballcarrier_waypointsprite_visible_for_player() - - player.effects &= ~autocvar_g_tka_ballcarrier_effects; - - if(player.ballcarried) - player.effects |= autocvar_g_tka_ballcarrier_effects; } @@ -580,7 +595,7 @@ MUTATOR_HOOKFUNCTION(tka, DropSpecialItems) { entity frag_target = M_ARGV(0, entity); - if(frag_target.ballcarried) + while (frag_target.ballcarried) tka_DropEvent(frag_target); } diff --git a/qcsrc/common/gamemodes/gamemode/tka/sv_tka.qh b/qcsrc/common/gamemodes/gamemode/tka/sv_tka.qh index 20f92ee1d..5d84704e7 100644 --- a/qcsrc/common/gamemodes/gamemode/tka/sv_tka.qh +++ b/qcsrc/common/gamemodes/gamemode/tka/sv_tka.qh @@ -26,8 +26,6 @@ REGISTER_MUTATOR(tka, false) return false; } -const int TKA_BALL_COUNT = 1; - void(entity this) havocbot_role_tka_carrier; void(entity this) havocbot_role_tka_collector; -- 2.39.5