]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/mutators/gamemode_keepaway.qc
Code cleanups + keepaway powerups, this one might be questionable and might require...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / gamemode_keepaway.qc
index eb1bc4bb002511795d5ccd85dca21c1e1f1242a3..8acb81fb8bf1c2b334b4d0a6ff767db8b19a69c9 100644 (file)
@@ -1,35 +1,60 @@
-void ka_Initialize()
+void ka_SpawnBall(void);
+void ka_TouchEvent(void);
+void ka_RespawnBall(void);
+void ka_DropEvent(entity);
+
+void ka_Initialize() // run at the start of a match, initiates game mode
 {
-       if(!g_keepaway) { 
-               remove(self); 
-               return; 
-       }
-       if (!self.model) {
-               self.model = "models/nexball/ball.md3"; 
-               self.scale = 1.3;
-       }
+       if(!g_keepaway)
+               return;
+               
+       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);
+
+       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_SpawnBall;
-       self.nextthink = time;
-       self.flags = FL_ITEM;
-       self.reset = ka_Reset;
-       self.owner = world;
+void ka_SpawnBall() // loads various values for the ball
+{
+       if(!g_keepaway) { return; }
        
-       // todo: Waypoints and radar
-       //WaypointSprite_AttachCarrier();
+       entity e;
+       e = spawn();
+       e.model = "models/orbs/orbblue.md3";    
+       e.scale = 1;
+       precache_model(e.model);
+       setmodel(e, e.model);
+       setsize(e, BALL_MINS, BALL_MAXS);
+       e.classname = "keepawayball";
+       e.damageforcescale = cvar("g_keepawayball_damageforcescale");
+       e.takedamage = DAMAGE_YES;
+       e.glow_color = cvar("g_keepawayball_trail_color");
+       e.glow_trail = TRUE;
+       e.movetype = MOVETYPE_BOUNCE;
+       e.touch = ka_TouchEvent;
+       e.flags = FL_ITEM;
+       e.reset = ka_Reset;
+       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_SpawnBall()
+void ka_RespawnBall() // runs whenever the ball needs to be relocated
 {
+       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);
@@ -37,249 +62,222 @@ void ka_SpawnBall()
                self.velocity = '0 0 200';
                self.angles = '0 0 0';
                self.solid = SOLID_TRIGGER;
-               //self.touch = ka_TouchEvent;
-               self.think = ka_SpawnBall;
+               self.think = ka_RespawnBall;
                self.nextthink = time + cvar("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);
        }
        else
        {
-               // sorry, can't spawn, better luck next frame
-               self.think = ka_SpawnBall;
-               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(!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; 
+       }
+       if(self.wait > time) { return; }
 
+       // attach the ball to the player
        self.owner = other;
        other.ballcarried = self;
        setattachment(self, other, "");
        setorigin(self, BALL_ATTACHORG);
        
+       // make the ball invisible/unable to do anything
        self.velocity = '0 0 0';
        self.movetype = MOVETYPE_NONE;
        self.touch = SUB_Null;
-       self.alpha = 0.01;
-       
+       self.effects |= EF_NODRAW;
        self.think = SUB_Null;
        self.nextthink = 0;
+       self.takedamage = DAMAGE_NO;
 
-       self.glow_color = cvar("g_keepawayball_trail_color");
-       self.glowtrail = TRUE;
-       plyr.effects |= 8;
-       plyr.alpha = 0.6
+       // apply effects to player
+       other.glow_color = cvar("g_keepawayball_trail_color");
+       other.glow_trail = TRUE;
+       other.effects |= 8;
+       other.alpha = 0.6;
 
+       // messages and prints
        bprint(other.netname, "^7 has picked up the ball!\n");
        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);
        
+       // scoring
        PlayerScore_Add(other, SP_KEEPAWAY_PICKUPS, 1);
 
-       // todo: Waypoints and radar
+       // waypoints
+       WaypointSprite_AttachCarrier("ka-ballcarrier", other);
+       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);
 }
 
-void ka_DropEvent(entity plyr, entity ball)
-{      
+void ka_DropEvent(entity plyr) // runs any time that a player is supposed to lose the ball
+{
+       entity ball;
+       ball = plyr.ballcarried;
+
+       if(!ball) { return; }
+       
+       // 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.wait = time + 1; 
+       ball.think = ka_RespawnBall;
        ball.nextthink = time + cvar("g_keepawayball_respawntime");
        ball.touch = ka_TouchEvent;
-       plyr.effects = EF_LOWPRECISION;
-       plyr.alpha = 1.0;
-       ball.alpha = 1.0;
+       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;
+       ball.owner = world;
        
+       // reset the player effects
+       plyr.effects &~= 8;
+       plyr.alpha = 1.0;
+       plyr.glow_trail = FALSE;
+       
+       // messages and prints
        bprint(plyr.netname, "^7 has dropped the ball!\n");
        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);   
+       sound(other, CHAN_AUTO, "keepaway/dropped.wav", VOL_BASE, ATTN_NONE);   
        
+       // scoring
        PlayerScore_Add(plyr, SP_KEEPAWAY_DROPS, 1);
        
-       // todo
-       //WaypointSprite_AttachCarrier("ka-ball", ball);
-       //WaypointSprite_Kill(plyr.waypointsprite_attachedforcarrier);
-       
-       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()
+MUTATOR_HOOKFUNCTION(ka_RemovePlayer)
 {
-
+       if(self.ballcarried) { ka_DropEvent(self); } // a player with the ball has left the match, drop it
+       return 0;
 }
 
-MUTATOR_HOOKFUNCTION(ka_PlayerDies)
+MUTATOR_HOOKFUNCTION(ka_Scoring)
 {
-       float i;
-       entity e;
-
-       float temp_tag_players_count;
-       temp_tag_players_count = tag_players_count;
-
-       if(frag_target.tagcolor == frag_target.tagcolor_original) // if this is the first time we die... (our tagcolor remained unchanged)
+       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(cvar("g_keepaway_bckillscore")) // add bckills to the score
+                               PlayerScore_Add(frag_attacker, SP_KEEPAWAY_SCORE, 1);
                }
-       }
-       else
-       {
-               frag_target.tagcolor = frag_attacker.tagcolor;
-               setcolor(frag_target, 16 * frag_target.tagcolor + frag_target.tagcolor);
-       }
+               else if(!frag_attacker.ballcarried)
+                       if(cvar("g_keepaway_noncarrier_warn"))
+                               centerprint_atprio(frag_attacker, (CENTERPRIO_SPAM + 5), "Killing people while you don't have the ball gives no points!");
 
-       tag_GetFragAttackers_ColorOwner();
+               if(frag_attacker.ballcarried) // add to amount of kills while ballcarrier
+                       PlayerScore_Add(frag_attacker, SP_KEEPAWAY_SCORE, 1);
+       }
 
-       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(self.ballcarried) { ka_DropEvent(self); } // a player with the ball has died, drop it
+       return 0;
+}
 
-       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");
+MUTATOR_HOOKFUNCTION(ka_GiveFragsForKill)
+{
+       frag_score = 0; // no frags counted in keepaway
+       return 0;
+}
 
-       frag_target.health = cvar("g_balance_health_start"); // "respawn" the player :P
+MUTATOR_HOOKFUNCTION(ka_PlayerPreThink)
+{
+       self.items &~= IT_KEY1;
 
-       tag_CheckWinner();
+       if(self.ballcarried)
+               self.items |= IT_KEY1;
+       
+       if(self.BUTTON_USE)
+               if(self.ballcarried) { ka_DropEvent(self); } // drop the ball if the player presses the use button
 
-       return 1;
+       return 0;
 }
 
-MUTATOR_HOOKFUNCTION(tag_RemovePlayer)
+MUTATOR_HOOKFUNCTION(ka_PlayerDamage) // for changing damage and force values that are applied to players in g_damage.qc
 {
-       if(self.tag_playernum == -1)
-               return 0; // nothing to remove
-
-       float i, j;
-       /*for (i = self.tag_playernum; i < tag_players_count; ++i)
+       if(frag_attacker.items & IT_KEY1) // if the attacker is a ballcarrier
        {
-               tag_players[i] = tag_players[i+1];
-               tag_players[i].tag_playernum = tag_players[i].tag_playernum - 1;
+               if(frag_target == frag_attacker) // damage done to yourself
+               {
+                       frag_damage *= cvar("g_keepaway_ballcarrier_selfdamage");
+                       frag_force *= cvar("g_keepaway_ballcarrier_selfforce");
+               }
+               else // damage done to noncarriers
+               {
+                       frag_damage *= cvar("g_keepaway_ballcarrier_damage");
+                       frag_force *= cvar("g_keepaway_ballcarrier_force");
+               }
        }
-       */
-
-       tag_players[self.tag_playernum] = world;
-       tag_players_count = tag_players_count - 1;
-
-       // if other players have our color, randomize their color
-       entity e, random_player;
-       float temp_tag_players_count;
-       temp_tag_players_count = tag_players_count;
-
-       if(!next_round) // ... but ONLY if next_round isn't set. We don't care about the colors if the round has already ended
-       for(i = 0; i < temp_tag_players_count; ++i) // check other players...
+       else if not(frag_target.items & IT_KEY1) // if the target is a noncarrier
        {
-               e = tag_players[i];
-               if(e == world) // empty slot, skip to next
+               if(frag_target == frag_attacker) // damage done to yourself
                {
-                       if(temp_tag_players_count < TAGCOLOR_MAX - 1) // just in case
-                               ++temp_tag_players_count;
-                       continue;
+                       frag_damage *= cvar("g_keepaway_noncarrier_selfdamage");
+                       frag_force *= cvar("g_keepaway_noncarrier_selfforce");
                }
-
-               if(e.tagcolor == self.tagcolor_original) // and see if they have our original tag color
+               else // damage done to other noncarriers
                {
-                       for(j = 0; j < 100; ++j) // try 100 times to find a color that isn't the same as our color. If this fails we are either damn unlucky, or there are really only players left of our color
-                       {
-                               random_player = tag_players[floor(random() * (TAGCOLOR_MAX - 1))];
-
-                               if(random_player == world) // hit empty slot, try again
-                                       continue;
-
-                               if(random_player.tagcolor != self.tagcolor_original) // break if we found another color
-                               {
-                                       break;
-                               }
-                       }
-                       e.tagcolor = random_player.tagcolor;
-                       setcolor(e, 16 * e.tagcolor + e.tagcolor);
+                       frag_damage *= cvar("g_keepaway_noncarrier_damage");
+                       frag_force *= cvar("g_keepaway_noncarrier_force");
                }
        }
-
-       self.tag_playernum = -1;
-
-       if(tag_players_count > 1 && time > warmup)
-               tag_CheckWinner();
-
-       return 1;
-}
-
-MUTATOR_HOOKFUNCTION(tag_GiveFragsForKill)
-{
-       frag_score = 0; // no frags counted in Tag, maybe later (TODO)
-       return 1;
-}
-
-MUTATOR_HOOKFUNCTION(tag_PlayerPreThink)
-{
-       setcolor(self, 16 * self.tagcolor + self.tagcolor); // prevent cheating by changing player colors
-       return 1;
+       return 0;
 }
-*/
-
 
-MUTATOR_DEFINITION(gamemode_ka)
+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_FIRST);
+       MUTATOR_HOOK(PlayerDamage_Calculate, ka_PlayerDamage, 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_ka = 1;
+               g_keepaway = 1;
                ka_Initialize();
        }
 
        MUTATOR_ONREMOVE
        {
-               g_ka = 0;
+               g_keepaway = 0;
                error("This is a game type and it cannot be removed at runtime.");
        }
 
        return 0;
-}
-
+}
\ No newline at end of file