]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/mutators/gamemode_keepaway.qc
Remove the priority system for centerprint messages in the server, we no longer need it
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / gamemode_keepaway.qc
index 607705384b610f23850b5a0205043a2f29cd60cf..4d67e1aa82b59df5e5bc26982e8d48a0d54d504a 100644 (file)
 void ka_SpawnBall(void);
-void ka_TouchEvent(entity);
+void ka_TouchEvent(void);
 void ka_RespawnBall(void);
+void ka_DropEvent(entity);
+void ka_TimeScoring(void);
 
-void ka_Initialize()
+float ka_ballcarrier_waypointsprite_visible_for_player(entity);
+
+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");
-       
-       entity e;
-       e = spawn();
-       e.think = ka_SpawnBall;
-       e.nextthink = time;
+       precache_sound("keepaway/respawn.wav");
+       precache_sound("keepaway/touch.wav");
+
+       ScoreRules_keepaway();
+       ka_SpawnBall();
 }
 
-void ka_SpawnBall()
+void ka_Reset() // used to clear the ballcarrier whenever the match switches from warmup to normal
 {
-       if(!g_keepaway) { 
-               remove(self); 
-               return; 
-       }
-       if (!self.model) {
-               self.model = "models/nexball/ball.md3"; 
-               self.scale = 1.3;
-       }
+       if(self.owner)
+               if(self.owner.classname == "player")
+                       ka_DropEvent(self.owner);
+
+       ka_RespawnBall();
+}
 
-       precache_model(self.model);
-       setmodel(self, self.model);
-       setsize(self, BALL_MINS, BALL_MAXS);
-       ball_scale = self.scale;
-       self.classname = "keepawayball";
-       self.damageforcescale = cvar("g_keepawayball_damageforcescale");
-       self.effects = self.effects | EF_FULLBRIGHT;
-       self.movetype = MOVETYPE_BOUNCE;
-       self.touch = ka_TouchEvent;
-       self.think = ka_RespawnBall;
-       self.nextthink = time;
-       self.flags = FL_ITEM;
-       //self.reset = ka_Reset;
-       self.owner = world;
+void ka_SpawnBall() // loads various values for the ball, runs only once at start of match
+{
+       if(!g_keepaway) { return; }
        
-       // todo: Waypoints and radar
-       //WaypointSprite_AttachCarrier();
+       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;
+
+       InitializeEntity(e, ka_RespawnBall, INITPRIO_SETLOCATION); // is this the right priority? Neh, I have no idea.. Well-- it works! So. 
 }
 
-void ka_RespawnBall()
+void ka_RespawnBall() // runs whenever the ball needs to be relocated
 {
+       if(gameover) { return; }
+       vector oldballorigin = self.origin;
+       
        if(MoveToRandomMapLocation(self, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, 10, 1024, 256))
        {
                makevectors(self.angles);
                self.movetype = MOVETYPE_BOUNCE;
                self.velocity = '0 0 200';
                self.angles = '0 0 0';
-               self.solid = SOLID_TRIGGER;
-               //self.touch = ka_TouchEvent;
+               self.effects = autocvar_g_keepawayball_effects;
                self.think = ka_RespawnBall;
-               self.nextthink = time + cvar("g_keepawayball_respawntime");
+               self.nextthink = time + autocvar_g_keepawayball_respawntime;
+               
+               pointparticles(particleeffectnum("electro_combo"), oldballorigin, '0 0 0', 1);
+               pointparticles(particleeffectnum("electro_combo"), self.origin, '0 0 0', 1);
+
+               WaypointSprite_Spawn("ka-ball", 0, 0, self, '0 0 64', world, self.team, self, waypointsprite_attachedforcarrier, FALSE);
+               WaypointSprite_UpdateTeamRadar(self.waypointsprite_attachedforcarrier, RADARICON_FLAGCARRIER, '0 1 1');
+               WaypointSprite_Ping(self.waypointsprite_attachedforcarrier);    
+
+               sound(self, CHAN_AUTO, "keepaway/respawn.wav", VOL_BASE, ATTN_NONE); // ATTN_NONE (it's a sound intended to be heard anywhere) 
        }
        else
        {
-               // sorry, can't spawn, better luck next frame
-               self.think = ka_RespawnBall;
-               self.nextthink = time;
+               ka_RespawnBall(); // finding a location failed, retry 
        }
 }
 
-void ka_TouchEvent(entity plyr)
+void ka_TouchEvent() // runs any time that the ball comes in contact with something
 {
+       if(gameover) { return; }
+       if(!self) { return; }
        if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
-       {
-               self.think = ka_SpawnBall;
-               self.nextthink = time;
+       { // The ball fell off the map, respawn it since players can't get to it
+               ka_RespawnBall();
                return;
        }
-       if (!plyr) 
-               return;
-       if (!self) 
-               return;
-       if ((other.classname != "player" || other.health < 1) && (time > self.ctf_droptime + cvar("g_keepawayball_respawntime")))
-               return;
-       if (self.wait > time)
-               return;
+       if(other.deadflag != DEAD_NO) { return; }
+       if(other.classname != "player") 
+       {  // The ball just touched an object, most likely the world
+               pointparticles(particleeffectnum("kaball_sparks"), self.origin, '0 0 0', 1);
+               sound(self, CHAN_AUTO, "keepaway/touch.wav", VOL_BASE, ATTN_NORM);
+               return; 
+       }
+       else if(self.wait > time) { return; }
 
+       // attach the ball to the player
        self.owner = other;
        other.ballcarried = self;
        setattachment(self, other, "");
-       setorigin(self, BALL_ATTACHORG);
+       setorigin(self, '0 0 0');
        
+       // make the ball invisible/unable to do anything/set up time scoring
        self.velocity = '0 0 0';
        self.movetype = MOVETYPE_NONE;
+       self.effects |= EF_NODRAW;
        self.touch = SUB_Null;
-       self.alpha = 0.01;
+       self.think = ka_TimeScoring;
+       self.nextthink = time + autocvar_g_keepaway_score_timeinterval;
+       self.takedamage = DAMAGE_NO;
+
+       // apply effects to player
+       other.glow_color = autocvar_g_keepawayball_trail_color;
+       other.glow_trail = TRUE;
+       other.effects |= autocvar_g_keepaway_ballcarrier_effects;
        
-       self.think = SUB_Null;
-       self.nextthink = 0;
-
-       self.glow_color = cvar("g_keepawayball_trail_color");
-       self.glow_trail = TRUE;
-       plyr.effects |= 8;
-       plyr.alpha = 0.6;
-
-       bprint(other.netname, "^7 has picked up the ball!\n");
+       // messages and sounds
+       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"));
-       sound(self.owner, CHAN_AUTO, "keepaway/pickedup.wav", VOL_BASE, ATTN_NORM);
+       sound(self.owner, CHAN_AUTO, "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);
 
-       // todo: Waypoints and radar
+       // waypoints
+       WaypointSprite_AttachCarrier("ka-ballcarrier", other);
+       other.waypointsprite_attachedforcarrier.waypointsprite_visible_for_player = ka_ballcarrier_waypointsprite_visible_for_player;
+       WaypointSprite_UpdateRule(other.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT);
+       WaypointSprite_UpdateTeamRadar(other.waypointsprite_attachedforcarrier, RADARICON_FLAGCARRIER, '1 0 0');
+       WaypointSprite_Ping(other.waypointsprite_attachedforcarrier);   
+       WaypointSprite_Kill(self.waypointsprite_attachedforcarrier);
 }
 
-MUTATOR_HOOKFUNCTION(ka_RemovePlayer)
+void ka_DropEvent(entity plyr) // runs any time that a player is supposed to lose the ball
 {
        entity ball;
-       ball = self.ballcarried;
+       ball = plyr.ballcarried;
 
-       setattachment(ball, world, "");
-       ball.movetype = MOVETYPE_BOUNCE;
-       ball.solid = SOLID_TRIGGER;
-       ball.wait = time + 1;
-       ball.ctf_droptime = time;
-       ball.think = ka_SpawnBall;
-       ball.nextthink = time + cvar("g_keepawayball_respawntime");
-       ball.touch = ka_TouchEvent;
-       self.effects = EF_LOWPRECISION;
-       self.alpha = 1.0;
-       ball.alpha = 1.0;
-       setorigin(ball, self.origin + '0 0 10');
-       ball.velocity = '0 0 200' + '0 100 0'*crandom() + '100 0 0'*crandom();
-       
-       bprint(self.netname, "^7 has dropped the ball!\n");
-       WriteByte(MSG_BROADCAST, SVC_CENTERPRINT);
-       WriteString(MSG_BROADCAST, strcat("\n\n", self.netname, "^7 has dropped the ball!\n"));
-       sound(other, CHAN_AUTO, "keepaway/dropped.wav", VOL_BASE, ATTN_NORM);   
-       
-       PlayerScore_Add(self, SP_KEEPAWAY_DROPS, 1);
+       if(!ball) { return; }
        
-       // todo
-       //WaypointSprite_AttachCarrier("ka-ball", ball);
-       //WaypointSprite_Kill(plyr.waypointsprite_attachedforcarrier);
-       
-       ball.owner.ballcarried = world;
-       ball.owner = world;
-
-       return 1;
-}
-
-/*
-void ka_DropEvent(entity plyr, entity ball)
-{      
+       // reset the ball
        setattachment(ball, world, "");
        ball.movetype = MOVETYPE_BOUNCE;
-       ball.solid = SOLID_TRIGGER;
-       ball.wait = time + 1;
-       ball.ctf_droptime = time;
-       ball.think = ka_SpawnBall;
-       ball.nextthink = time + cvar("g_keepawayball_respawntime");
+       ball.wait = time + 1; 
        ball.touch = ka_TouchEvent;
-       plyr.effects = EF_LOWPRECISION;
-       plyr.alpha = 1.0;
-       ball.alpha = 1.0;
+       ball.think = ka_RespawnBall;
+       ball.nextthink = time + autocvar_g_keepawayball_respawntime;
+       ball.takedamage = DAMAGE_YES;
+       ball.effects &~= EF_NODRAW; 
        setorigin(ball, plyr.origin + '0 0 10');
        ball.velocity = '0 0 200' + '0 100 0'*crandom() + '100 0 0'*crandom();
+       ball.owner.ballcarried = world; // I hope nothing checks to see if the world has the ball in the rest of my code :P 
+       ball.owner = world;
        
-       bprint(plyr.netname, "^7 has dropped the ball!\n");
+       // reset the player effects
+       plyr.glow_trail = FALSE;
+       plyr.effects &~= autocvar_g_keepaway_ballcarrier_effects;
+
+       // messages and sounds
+       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"));
-       sound(other, CHAN_AUTO, "keepaway/dropped.wav", VOL_BASE, ATTN_NORM);   
-       
-       PlayerScore_Add(plyr, SP_KEEPAWAY_DROPS, 1);
+       sound(other, CHAN_AUTO, "keepaway/dropped.wav", VOL_BASE, ATTN_NONE);   // ATTN_NONE (it's a sound intended to be heard anywhere) 
        
-       // todo
-       //WaypointSprite_AttachCarrier("ka-ball", ball);
-       //WaypointSprite_Kill(plyr.waypointsprite_attachedforcarrier);
+       // scoring
+       // PlayerScore_Add(plyr, SP_KEEPAWAY_DROPS, 1); Not anymore, this is 100% the same as pickups and is useless.
        
-       ball.owner.kaballcarried = world;
-       ball.owner = world;
+       // waypoints
+       WaypointSprite_Spawn("ka-ball", 0, 0, ball, '0 0 64', world, ball.team, ball, waypointsprite_attachedforcarrier, FALSE);
+       WaypointSprite_UpdateRule(ball.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT);
+       WaypointSprite_UpdateTeamRadar(ball.waypointsprite_attachedforcarrier, RADARICON_FLAGCARRIER, '0 1 1');
+       WaypointSprite_Ping(ball.waypointsprite_attachedforcarrier);    
+       WaypointSprite_Kill(plyr.waypointsprite_attachedforcarrier);
 }
 
-
-void ka_CheckWinner()
+float ka_ballcarrier_waypointsprite_visible_for_player(entity e) // runs on waypoints which are attached to ballcarriers, updates once per frame 
 {
+       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
 
+       return TRUE;
 }
 
-MUTATOR_HOOKFUNCTION(ka_PlayerDies)
+void ka_TimeScoring()
 {
-       float i;
-       entity e;
-
-       float temp_tag_players_count;
-       temp_tag_players_count = tag_players_count;
+       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;
+       }
+}
 
-       if(frag_target.tagcolor == frag_target.tagcolor_original) // if this is the first time we die... (our tagcolor remained unchanged)
+MUTATOR_HOOKFUNCTION(ka_Scoring)
+{
+       if((frag_attacker != frag_target) && (frag_attacker.classname == "player"))
        {
-               for(i = 0; i < temp_tag_players_count; ++i) // check other players...
-               {
-                       e = tag_players[i];
-                       if(e == world) // empty slot, skip to next
-                       {
-                               if(temp_tag_players_count < TAGCOLOR_MAX - 1) // just in case
-                                       ++temp_tag_players_count;
-                               continue;
-                       }
-
-                       if(e.tagcolor == frag_target.tagcolor_original) // and see if they have our original tag color
-                       {
-                               tag_GetFragAttackers_ColorOwner();
-                               centerprint(e, strcat("^1Your master ^7", frag_target.netname, "^1 was tagged by ^7", frag_attacker.netname, " ^1with ^7", color_owner_red, " ^1color.\n"));
-                               e.tagcolor = frag_attacker.tagcolor; // if so, remove it, our tag color has now "died out" from this round and we can not win anymore. The attacker will "summon" all of our previously fragged targets, and also us.
-                               setcolor(e, 16 * e.tagcolor + e.tagcolor);
-                       }
+               if(frag_target.ballcarried) { // add to amount of times killing carrier
+                       PlayerScore_Add(frag_attacker, SP_KEEPAWAY_CARRIERKILLS, 1);
+                       if(autocvar_g_keepaway_score_bckill) // add bckills to the score
+                               PlayerScore_Add(frag_attacker, SP_SCORE, autocvar_g_keepaway_score_bckill);
                }
-       }
-       else
-       {
-               frag_target.tagcolor = frag_attacker.tagcolor;
-               setcolor(frag_target, 16 * frag_target.tagcolor + frag_target.tagcolor);
-       }
-
-       tag_GetFragAttackers_ColorOwner();
+               else if(!frag_attacker.ballcarried)
+                       if(autocvar_g_keepaway_noncarrier_warn)
+                               centerprint(frag_attacker, "Killing people while you don't have the ball gives no points!");
 
-       if(color_owner_self)
-               color_owner_green = "^2your own";
-       centerprint(frag_attacker, strcat("^2You tagged ^7", frag_target.netname, " ^2with ^7", color_owner_green, " ^2color.\n"));
+               if(frag_attacker.ballcarried) // add to amount of kills while ballcarrier
+                       PlayerScore_Add(frag_attacker, SP_SCORE, autocvar_g_keepaway_score_killac);
+       }
 
-       if(color_owner_self)
-               color_owner_red = "^1their own";
-       centerprint(frag_target, strcat("^1You were tagged by ^7", frag_attacker.netname, " ^1with ^7", color_owner_red, " ^1color.\n"));
-       bprint("^7", frag_target.netname, "^1 was tagged by ^7", frag_attacker.netname, " ^1with ^7", color_owner_red, " ^1color.\n");
+       if(self.ballcarried) { ka_DropEvent(self); } // a player with the ball has died, drop it
+       return 0;
+}
 
-       frag_target.health = cvar("g_balance_health_start"); // "respawn" the player :P
+MUTATOR_HOOKFUNCTION(ka_GiveFragsForKill)
+{
+       frag_score = 0; // no frags counted in keepaway
+       return 1; // you deceptive little bugger ;3 This needs to be true in order for this function to even count. 
+}
 
-       tag_CheckWinner();
+MUTATOR_HOOKFUNCTION(ka_PlayerPreThink)
+{
+       // clear the item used for the ball in keepaway
+       self.items &~= IT_KEY1;
+       
+       // if the player has the ball, make sure they have the item for it (Used for HUD primarily)
+       if(self.ballcarried)
+               self.items |= IT_KEY1;
 
-       return 1;
+       return 0;
 }
 
-MUTATOR_HOOKFUNCTION(tag_RemovePlayer)
+MUTATOR_HOOKFUNCTION(ka_PlayerUseKey)
 {
-
-       return 1;
+       if(MUTATOR_RETURNVALUE == 0)
+       if(self.ballcarried)
+       {
+               ka_DropEvent(self);
+               return 1;
+       }
+       return 0;
 }
 
-MUTATOR_HOOKFUNCTION(tag_GiveFragsForKill)
+MUTATOR_HOOKFUNCTION(ka_PlayerDamage) // for changing damage and force values that are applied to players in g_damage.qc
 {
-       frag_score = 0; // no frags counted in Tag, maybe later (TODO)
-       return 1;
+       if(frag_attacker.ballcarried) // if the attacker is a ballcarrier
+       {
+               if(frag_target == frag_attacker) // damage done to yourself
+               {
+                       frag_damage *= autocvar_g_keepaway_ballcarrier_selfdamage;
+                       frag_force *= autocvar_g_keepaway_ballcarrier_selfforce;
+               }
+               else // damage done to noncarriers
+               {
+                       frag_damage *= autocvar_g_keepaway_ballcarrier_damage;
+                       frag_force *= autocvar_g_keepaway_ballcarrier_force;
+               }
+       }
+       else if not(frag_target.ballcarried) // if the target is a noncarrier
+       {
+               if(frag_target == frag_attacker) // damage done to yourself
+               {
+                       frag_damage *= autocvar_g_keepaway_noncarrier_selfdamage;
+                       frag_force *= autocvar_g_keepaway_noncarrier_selfforce;
+               }
+               else // damage done to other noncarriers
+               {
+                       frag_damage *= autocvar_g_keepaway_noncarrier_damage;
+                       frag_force *= autocvar_g_keepaway_noncarrier_force;
+               }
+       }
+       return 0;
 }
 
-MUTATOR_HOOKFUNCTION(tag_PlayerPreThink)
+MUTATOR_HOOKFUNCTION(ka_RemovePlayer)
 {
-       setcolor(self, 16 * self.tagcolor + self.tagcolor); // prevent cheating by changing player colors
-       return 1;
+       if(self.ballcarried) { ka_DropEvent(self); } // a player with the ball has left the match, drop it
+       return 0;
 }
-*/
 
+MUTATOR_HOOKFUNCTION(ka_PlayerPowerups)
+{
+       // 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() 
+       
+       self.effects &~= autocvar_g_keepaway_ballcarrier_effects;
+
+       if(self.ballcarried)
+               self.effects |= autocvar_g_keepaway_ballcarrier_effects;
+       
+       return 0;
+}
 
 MUTATOR_DEFINITION(gamemode_keepaway)
 {
        MUTATOR_HOOK(MakePlayerObserver, ka_RemovePlayer, CBC_ORDER_ANY);
        MUTATOR_HOOK(ClientDisconnect, ka_RemovePlayer, CBC_ORDER_ANY);
-       //MUTATOR_HOOK(PlayerDies, ka_PlayerDies, CBC_ORDER_ANY);
-       //MUTATOR_HOOK(PlayerSpawn, ka_PlayerSpawn, CBC_ORDER_ANY);
-       //MUTATOR_HOOK(GiveFragsForKill, ka_GiveFragsForKill, CBC_ORDER_FIRST);
-       //MUTATOR_HOOK(PlayerPreThink, ka_PlayerPreThink, CBC_ORDER_FIRST);
+       MUTATOR_HOOK(PlayerDies, ka_Scoring, CBC_ORDER_ANY);
+       MUTATOR_HOOK(GiveFragsForKill, ka_GiveFragsForKill, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerPreThink, ka_PlayerPreThink, CBC_ORDER_ANY);
+       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_ONADD
        {
@@ -284,4 +332,3 @@ MUTATOR_DEFINITION(gamemode_keepaway)
 
        return 0;
 }
-