X-Git-Url: https://de.git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Fmutators%2Fgamemode_keepaway.qc;h=9f8647d9dfa5cab3a61a1a37735360bcaf4602ec;hb=22dd9943727b1f6566a2401347663ed60a3eefb9;hp=b4c4b3391ab2293a5d4eb6edd6abb61fc134c072;hpb=70b696e4e2bbe7322bf580be42961b2d5a159f31;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/mutators/gamemode_keepaway.qc b/qcsrc/server/mutators/gamemode_keepaway.qc index b4c4b3391..9f8647d9d 100644 --- a/qcsrc/server/mutators/gamemode_keepaway.qc +++ b/qcsrc/server/mutators/gamemode_keepaway.qc @@ -1,60 +1,23 @@ -void ka_SpawnBall(void); -void ka_TouchEvent(void); -void ka_RespawnBall(void); -void ka_DropEvent(entity); -void ka_TimeScoring(void); +// =========================================================== +// Keepaway game mode coding, written by Samual and Diabolik +// Last updated: September, 2012 +// =========================================================== -entity ka_ball; - -float ka_ballcarrier_waypointsprite_visible_for_player(entity); - -void ka_Initialize() // run at the start of a match, initiates game mode +float ka_ballcarrier_waypointsprite_visible_for_player(entity e) // runs on waypoints which are attached to ballcarriers, updates once per frame { - if(!g_keepaway) - return; + if(e.ballcarried) + if(other.classname == "spectator") + return FALSE; // we don't want spectators of the ballcarrier to see the attached waypoint on the top of their screen - precache_sound("keepaway/pickedup.wav"); - precache_sound("keepaway/dropped.wav"); - precache_sound("keepaway/respawn.wav"); - precache_sound("keepaway/touch.wav"); - - ScoreRules_keepaway(); - ka_SpawnBall(); -} - -void ka_Reset() // used to clear the ballcarrier whenever the match switches from warmup to normal -{ - if(self.owner) - if(self.owner.classname == "player") - ka_DropEvent(self.owner); + // TODO: Make the ballcarrier lack a waypointsprite whenever they have the invisibility powerup - ka_RespawnBall(); + return TRUE; } -void ka_SpawnBall() // loads various values for the ball, runs only once at start of match +void ka_EventLog(string mode, entity actor) // use an alias for easy changing and quick editing later { - if(!g_keepaway) { return; } - - entity e; - e = spawn(); - e.model = "models/orbs/orbblue.md3"; - precache_model(e.model); - setmodel(e, e.model); - setsize(e, '-16 -16 -20', '16 16 20'); // 20 20 20 was too big, player is only 16 16 24... gotta cheat with the Z (20) axis so that the particle isn't cut off - e.classname = "keepawayball"; - e.damageforcescale = autocvar_g_keepawayball_damageforcescale; - e.takedamage = DAMAGE_YES; - e.solid = SOLID_TRIGGER; - e.movetype = MOVETYPE_BOUNCE; - e.glow_color = autocvar_g_keepawayball_trail_color; - e.glow_trail = TRUE; - e.flags = FL_ITEM; - e.reset = ka_Reset; - e.touch = ka_TouchEvent; - e.owner = world; - ka_ball = e; - - InitializeEntity(e, ka_RespawnBall, INITPRIO_SETLOCATION); // is this the right priority? Neh, I have no idea.. Well-- it works! So. + if(autocvar_sv_eventlog) + GameLogEcho(strcat(":ka:", mode, ((actor != world) ? (strcat(":", ftos(actor.playerid))) : ""))); } void ka_RespawnBall() // runs whenever the ball needs to be relocated @@ -78,7 +41,7 @@ void ka_RespawnBall() // runs whenever the ball needs to be relocated WaypointSprite_Spawn("ka-ball", 0, 0, self, '0 0 64', world, self.team, self, waypointsprite_attachedforcarrier, FALSE, RADARICON_FLAGCARRIER, '0 1 1'); WaypointSprite_Ping(self.waypointsprite_attachedforcarrier); - asound(self, CH_TRIGGER, "keepaway/respawn.wav", VOL_BASE, ATTN_NONE); // ATTN_NONE (it's a sound intended to be heard anywhere) + sound(self, CH_TRIGGER, "keepaway/respawn.wav", VOL_BASE, ATTN_NONE); // ATTN_NONE (it's a sound intended to be heard anywhere) } else { @@ -86,6 +49,18 @@ void ka_RespawnBall() // runs whenever the ball needs to be relocated } } +void ka_TimeScoring() +{ + if(self.owner.ballcarried) + { // add points for holding the ball after a certain amount of time + if(autocvar_g_keepaway_score_timepoints) + PlayerScore_Add(self.owner, SP_SCORE, autocvar_g_keepaway_score_timepoints); + + PlayerScore_Add(self.owner, SP_KEEPAWAY_BCTIME, (autocvar_g_keepaway_score_timeinterval / 1)); // interval is divided by 1 so that time always shows "seconds" + self.nextthink = time + autocvar_g_keepaway_score_timeinterval; + } +} + void ka_TouchEvent() // runs any time that the ball comes in contact with something { if(gameover) { return; } @@ -99,7 +74,7 @@ void ka_TouchEvent() // runs any time that the ball comes in contact with someth if(other.classname != "player") { // The ball just touched an object, most likely the world pointparticles(particleeffectnum("kaball_sparks"), self.origin, '0 0 0', 1); - asound(self, CH_TRIGGER, "keepaway/touch.wav", VOL_BASE, ATTN_NORM); + sound(self, CH_TRIGGER, "keepaway/touch.wav", VOL_BASE, ATTN_NORM); return; } else if(self.wait > time) { return; } @@ -114,7 +89,7 @@ void ka_TouchEvent() // runs any time that the ball comes in contact with someth self.velocity = '0 0 0'; self.movetype = MOVETYPE_NONE; self.effects |= EF_NODRAW; - self.touch = SUB_Null; + self.touch = func_null; self.think = ka_TimeScoring; self.nextthink = time + autocvar_g_keepaway_score_timeinterval; self.takedamage = DAMAGE_NO; @@ -125,10 +100,11 @@ void ka_TouchEvent() // runs any time that the ball comes in contact with someth other.effects |= autocvar_g_keepaway_ballcarrier_effects; // messages and sounds + ka_EventLog("pickup", other); Send_KillNotification(other.netname, "", "", KA_PICKUPBALL, MSG_KA); WriteByte(MSG_BROADCAST, SVC_CENTERPRINT); - WriteString(MSG_BROADCAST, strcat("\n\n", other.netname, "^7 has picked up the ball!\n")); - asound(self.owner, CH_TRIGGER, "keepaway/pickedup.wav", VOL_BASE, ATTN_NONE); // ATTN_NONE (it's a sound intended to be heard anywhere) + WriteString(MSG_BROADCAST, strcat(other.netname, "^7 has picked up the ball!")); + sound(self.owner, CH_TRIGGER, "keepaway/pickedup.wav", VOL_BASE, ATTN_NONE); // ATTN_NONE (it's a sound intended to be heard anywhere) // scoring PlayerScore_Add(other, SP_KEEPAWAY_PICKUPS, 1); @@ -167,10 +143,11 @@ void ka_DropEvent(entity plyr) // runs any time that a player is supposed to los plyr.effects &~= autocvar_g_keepaway_ballcarrier_effects; // messages and sounds + ka_EventLog("dropped", plyr); Send_KillNotification(plyr.netname, "", "", KA_DROPBALL, MSG_KA); WriteByte(MSG_BROADCAST, SVC_CENTERPRINT); - WriteString(MSG_BROADCAST, strcat("\n\n", plyr.netname, "^7 has dropped the ball!\n")); - asound(plyr, CH_TRIGGER, "keepaway/dropped.wav", VOL_BASE, ATTN_NONE); // ATTN_NONE (it's a sound intended to be heard anywhere) + WriteString(MSG_BROADCAST, strcat(plyr.netname, "^7 has dropped the ball!")); + sound(other, CH_TRIGGER, "keepaway/dropped.wav", VOL_BASE, ATTN_NONE); // ATTN_NONE (it's a sound intended to be heard anywhere) // scoring // PlayerScore_Add(plyr, SP_KEEPAWAY_DROPS, 1); Not anymore, this is 100% the same as pickups and is useless. @@ -182,29 +159,90 @@ void ka_DropEvent(entity plyr) // runs any time that a player is supposed to los WaypointSprite_Kill(plyr.waypointsprite_attachedforcarrier); } -float ka_ballcarrier_waypointsprite_visible_for_player(entity e) // runs on waypoints which are attached to ballcarriers, updates once per frame +void ka_Reset() // used to clear the ballcarrier whenever the match switches from warmup to normal { - if(e.ballcarried) - if(other.classname == "spectator") - return FALSE; // we don't want spectators of the ballcarrier to see the attached waypoint on the top of their screen - - // TODO: Make the ballcarrier lack a waypointsprite whenever they have the invisibility powerup + if((self.owner) && (self.owner.classname == "player")) + ka_DropEvent(self.owner); - return TRUE; + ka_RespawnBall(); } -void ka_TimeScoring() + +// ================ +// Bot player logic +// ================ + +void havocbot_goalrating_ball(float ratingscale, vector org) { - if(self.owner.ballcarried) - { // add points for holding the ball after a certain amount of time - if(autocvar_g_keepaway_score_timepoints) - PlayerScore_Add(self.owner, SP_SCORE, autocvar_g_keepaway_score_timepoints); - - PlayerScore_Add(self.owner, SP_KEEPAWAY_BCTIME, (autocvar_g_keepaway_score_timeinterval / 1)); // interval is divided by 1 so that time always shows "seconds" - self.nextthink = time + autocvar_g_keepaway_score_timeinterval; + float t; + entity ball_owner; + ball_owner = ka_ball.owner; + + if (ball_owner == self) + return; + + // If ball is carried by player then hunt them down. + if (ball_owner) + { + t = (self.health + self.armorvalue) / (ball_owner.health + ball_owner.armorvalue); + navigation_routerating(ball_owner, t * ratingscale, 2000); + } + + // Ball has been dropped so collect. + navigation_routerating(ka_ball, ratingscale, 2000); +} + +void havocbot_role_ka_carrier() +{ + if (self.deadflag != DEAD_NO) + return; + + if (time > self.bot_strategytime) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + + navigation_goalrating_start(); + havocbot_goalrating_items(10000, self.origin, 10000); + havocbot_goalrating_enemyplayers(20000, self.origin, 10000); + //havocbot_goalrating_waypoints(1, self.origin, 1000); + navigation_goalrating_end(); + } + + if (!self.ballcarried) + { + self.havocbot_role = havocbot_role_ka_collector; + self.bot_strategytime = 0; } } +void havocbot_role_ka_collector() +{ + if (self.deadflag != DEAD_NO) + return; + + if (time > self.bot_strategytime) + { + self.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + + navigation_goalrating_start(); + havocbot_goalrating_items(10000, self.origin, 10000); + havocbot_goalrating_enemyplayers(1000, self.origin, 10000); + havocbot_goalrating_ball(20000, self.origin); + navigation_goalrating_end(); + } + + if (self.ballcarried) + { + self.havocbot_role = havocbot_role_ka_carrier; + self.bot_strategytime = 0; + } +} + + +// ============== +// Hook Functions +// ============== + MUTATOR_HOOKFUNCTION(ka_Scoring) { if((frag_attacker != frag_target) && (frag_attacker.classname == "player")) @@ -216,7 +254,7 @@ MUTATOR_HOOKFUNCTION(ka_Scoring) } else if(!frag_attacker.ballcarried) if(autocvar_g_keepaway_noncarrier_warn) - centerprint_atprio(frag_attacker, (CENTERPRIO_SPAM + 5), "Killing people while you don't have the ball gives no points!"); + centerprint(frag_attacker, "Killing people while you don't have the ball gives no points!"); if(frag_attacker.ballcarried) // add to amount of kills while ballcarrier PlayerScore_Add(frag_attacker, SP_SCORE, autocvar_g_keepaway_score_killac); @@ -305,6 +343,70 @@ MUTATOR_HOOKFUNCTION(ka_PlayerPowerups) return 0; } +MUTATOR_HOOKFUNCTION(ka_BotRoles) +{ + if (self.ballcarried) + self.havocbot_role = havocbot_role_ka_carrier; + else + self.havocbot_role = havocbot_role_ka_collector; + return TRUE; +} + + +// ============== +// Initialization +// ============== + +void ka_SpawnBall() // loads various values for the ball, runs only once at start of match +{ + if(!g_keepaway) { return; } + + entity e; + e = spawn(); + e.model = "models/orbs/orbblue.md3"; + precache_model(e.model); + setmodel(e, e.model); + setsize(e, '-16 -16 -20', '16 16 20'); // 20 20 20 was too big, player is only 16 16 24... gotta cheat with the Z (20) axis so that the particle isn't cut off + e.classname = "keepawayball"; + e.damageforcescale = autocvar_g_keepawayball_damageforcescale; + e.takedamage = DAMAGE_YES; + e.solid = SOLID_TRIGGER; + e.movetype = MOVETYPE_BOUNCE; + e.glow_color = autocvar_g_keepawayball_trail_color; + e.glow_trail = TRUE; + e.flags = FL_ITEM; + e.reset = ka_Reset; + e.touch = ka_TouchEvent; + e.owner = world; + ka_ball = e; + + InitializeEntity(e, ka_RespawnBall, INITPRIO_SETLOCATION); // is this the right priority? Neh, I have no idea.. Well-- it works! So. +} + +void ka_ScoreRules() +{ + ScoreRules_basics(0, SFL_SORT_PRIO_PRIMARY, 0, TRUE); // SFL_SORT_PRIO_PRIMARY + ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_PICKUPS, "pickups", 0); + ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_CARRIERKILLS, "bckills", 0); + ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_BCTIME, "bctime", SFL_SORT_PRIO_SECONDARY); + ScoreRules_basics_end(); +} + +void ka_Initialize() // run at the start of a match, initiates game mode +{ + if(!g_keepaway) + return; + + precache_sound("keepaway/pickedup.wav"); + precache_sound("keepaway/dropped.wav"); + precache_sound("keepaway/respawn.wav"); + precache_sound("keepaway/touch.wav"); + + ka_ScoreRules(); + ka_SpawnBall(); +} + + MUTATOR_DEFINITION(gamemode_keepaway) { MUTATOR_HOOK(MakePlayerObserver, ka_RemovePlayer, CBC_ORDER_ANY); @@ -315,18 +417,17 @@ MUTATOR_DEFINITION(gamemode_keepaway) MUTATOR_HOOK(PlayerDamage_Calculate, ka_PlayerDamage, CBC_ORDER_ANY); MUTATOR_HOOK(PlayerPowerups, ka_PlayerPowerups, CBC_ORDER_ANY); MUTATOR_HOOK(PlayerUseKey, ka_PlayerUseKey, CBC_ORDER_ANY); + MUTATOR_HOOK(HavocBot_ChooseRule, ka_BotRoles, CBC_ORDER_ANY); MUTATOR_ONADD { if(time > 1) // game loads at time 1 error("This is a game type and it cannot be added at runtime."); - g_keepaway = 1; ka_Initialize(); } MUTATOR_ONREMOVE { - g_keepaway = 0; error("This is a game type and it cannot be removed at runtime."); }