]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/mutators/gamemode_keepaway.qc
Fix 2 bugs (Warmup to normal match carrier bug - waypoint spawn in solid in ka_SpawnB...
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / gamemode_keepaway.qc
1 void ka_SpawnBall(void);
2 void ka_TouchEvent(void);
3 void ka_RespawnBall(void);
4 void ka_DropEvent(entity);
5 //.float dropperid;
6
7 void ka_Initialize() // run at the start of a match, initiates game mode
8 {
9         if(!g_keepaway)
10                 return;
11                 
12         precache_sound("keepaway/pickedup.wav");
13         precache_sound("keepaway/dropped.wav");
14
15         ScoreRules_keepaway();
16         
17         entity e;
18         e = spawn();
19         e.think = ka_SpawnBall;
20         e.nextthink = time;
21 }
22
23 void ka_Reset() // used to clear the ballcarrier whenever the match switches from warmup to normal
24 {
25         if(self.owner)
26                 if(self.owner.classname == "player")
27                         ka_DropEvent(self.owner);
28
29         ka_RespawnBall();
30 }
31
32 void ka_SpawnBall() // loads various values for the ball
33 {
34         if(!g_keepaway) { 
35                 remove(self); 
36                 return; 
37         }
38         if (!self.model) {
39                 self.model = "models/orbs/orbblue.md3"; 
40                 self.scale = 1;
41         }
42
43         precache_model(self.model);
44         setmodel(self, self.model);
45         setsize(self, BALL_MINS, BALL_MAXS);
46         ball_scale = self.scale;
47         self.classname = "keepawayball";
48         self.damageforcescale = cvar("g_keepawayball_damageforcescale");
49         self.takedamage = DAMAGE_YES;
50         //self.effects |= "sparks";
51         self.glow_color = cvar("g_keepawayball_trail_color");
52         self.glow_trail = TRUE;
53         self.movetype = MOVETYPE_BOUNCE;
54         self.touch = ka_TouchEvent;
55         self.think = ka_RespawnBall;
56         self.nextthink = time;
57         self.flags = FL_ITEM;
58         self.reset = ka_Reset;
59         self.owner = world;
60 }
61
62 void ka_RespawnBall() // runs whenever the ball needs to be relocated
63 {
64         vector oldballorigin = self.origin;
65
66         if(MoveToRandomMapLocation(self, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, 10, 1024, 256))
67         {
68                 makevectors(self.angles);
69                 self.movetype = MOVETYPE_BOUNCE;
70                 self.velocity = '0 0 200';
71                 self.angles = '0 0 0';
72                 self.solid = SOLID_TRIGGER;
73                 //self.touch = ka_TouchEvent;
74                 self.think = ka_RespawnBall;
75                 self.nextthink = time + cvar("g_keepawayball_respawntime");
76                 pointparticles(particleeffectnum("electro_combo"), oldballorigin, '0 0 0', 1);
77                 pointparticles(particleeffectnum("electro_combo"), self.origin, '0 0 0', 1);
78
79                 WaypointSprite_Spawn("ka-ball", 0, 0, self, '0 0 64', world, self.team, self, waypointsprite_attachedforcarrier, FALSE);
80                 WaypointSprite_UpdateTeamRadar(self.waypointsprite_attachedforcarrier, RADARICON_FLAGCARRIER, '0 1 1');
81
82                 sound(self.owner, CHAN_AUTO, "keepaway/respawn.wav", VOL_BASE, ATTN_NONE);
83         }
84         else
85         {
86                 // sorry, can't spawn, better luck next frame
87                 self.think = ka_RespawnBall;
88                 self.nextthink = time;
89         }
90 }
91
92 void ka_TouchEvent() // runs any time that the ball comes in contact with something
93 {
94         if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
95         {
96                 self.think = ka_SpawnBall;
97                 self.nextthink = time;
98                 return;
99         }
100         if(other.deadflag != DEAD_NO) { return; }
101         if(other.classname != "player") 
102         { 
103                 pointparticles(particleeffectnum("kaball_sparks"), self.origin, '0 0 0', 1);
104                 return; 
105         }
106         if(!self) { return; }
107         if(self.wait > time) { return; }
108
109         self.owner = other;
110         other.ballcarried = self;
111         setattachment(self, other, "");
112         setorigin(self, BALL_ATTACHORG);
113         
114         self.velocity = '0 0 0';
115         self.movetype = MOVETYPE_NONE;
116         self.touch = SUB_Null;
117         self.effects |= EF_NODRAW;
118         self.think = SUB_Null;
119         self.nextthink = 0;
120         self.takedamage = DAMAGE_NO;
121
122         other.glow_color = cvar("g_keepawayball_trail_color");
123         other.glow_trail = TRUE;
124         other.effects |= 8;
125         other.alpha = 0.6;
126
127         bprint(other.netname, "^7 has picked up the ball!\n");
128         WriteByte(MSG_BROADCAST, SVC_CENTERPRINT);
129         WriteString(MSG_BROADCAST, strcat("\n\n", other.netname, "^7 has picked up the ball!\n"));
130         sound(self.owner, CHAN_AUTO, "keepaway/pickedup.wav", VOL_BASE, ATTN_NONE);
131         
132         PlayerScore_Add(other, SP_KEEPAWAY_PICKUPS, 1);
133
134         WaypointSprite_AttachCarrier("ka-ballcarrier", other);
135         WaypointSprite_UpdateRule(other.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT);
136         WaypointSprite_UpdateTeamRadar(other.waypointsprite_attachedforcarrier, RADARICON_FLAGCARRIER, '1 0 0');
137         WaypointSprite_Ping(other.waypointsprite_attachedforcarrier);   
138         WaypointSprite_Kill(self.waypointsprite_attachedforcarrier);
139 }
140
141 void ka_DropEvent(entity plyr) // runs any time that a player is supposed to lose the ball
142 {
143         entity ball;
144         ball = plyr.ballcarried;
145
146         if(!ball) { return; }
147         
148         setattachment(ball, world, "");
149         ball.movetype = MOVETYPE_BOUNCE;
150         ball.solid = SOLID_TRIGGER;
151         ball.wait = time + 1; 
152         ball.think = ka_SpawnBall;
153         ball.nextthink = time + cvar("g_keepawayball_respawntime");
154         ball.touch = ka_TouchEvent;
155         ball.takedamage = DAMAGE_YES;
156         ball.effects &~= EF_NODRAW; //ball.alpha = 1.0;
157         setorigin(ball, plyr.origin + '0 0 10');
158         ball.velocity = '0 0 200' + '0 100 0'*crandom() + '100 0 0'*crandom();
159         ball.owner.ballcarried = world;
160         ball.owner = world;
161
162         plyr.effects &~= 8;
163         plyr.alpha = 1.0;
164         plyr.glow_trail = FALSE;
165         
166         bprint(plyr.netname, "^7 has dropped the ball!\n");
167         WriteByte(MSG_BROADCAST, SVC_CENTERPRINT);
168         WriteString(MSG_BROADCAST, strcat("\n\n", plyr.netname, "^7 has dropped the ball!\n"));
169         sound(other, CHAN_AUTO, "keepaway/dropped.wav", VOL_BASE, ATTN_NONE);   
170         
171         PlayerScore_Add(plyr, SP_KEEPAWAY_DROPS, 1);
172         
173         WaypointSprite_Spawn("ka-ball", 0, 0, ball, '0 0 64', world, ball.team, ball, waypointsprite_attachedforcarrier, FALSE);
174         WaypointSprite_UpdateRule(ball.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT);
175         WaypointSprite_UpdateTeamRadar(ball.waypointsprite_attachedforcarrier, RADARICON_FLAGCARRIER, '0 1 1');
176         WaypointSprite_Ping(ball.waypointsprite_attachedforcarrier);    
177         WaypointSprite_Kill(plyr.waypointsprite_attachedforcarrier);
178 }
179
180 MUTATOR_HOOKFUNCTION(ka_RemovePlayer)
181 {
182         if(self.ballcarried) { ka_DropEvent(self); }
183         return 1;
184 }
185
186 MUTATOR_HOOKFUNCTION(ka_Scoring)
187 {
188         if((frag_attacker != frag_target) && (frag_attacker.classname == "player"))
189         {
190                 if(frag_target.ballcarried) { // get amount of times killing carrier
191                         PlayerScore_Add(frag_attacker, SP_KEEPAWAY_CARRIERKILLS, 1);
192                         if(cvar("g_keepaway_bckillscore"))
193                                 PlayerScore_Add(frag_attacker, SP_KEEPAWAY_SCORE, 1);
194                 }
195                 else if(!frag_attacker.ballcarried)
196                         if(cvar("g_keepaway_noncarrier_warn"))
197                                 centerprint_atprio(frag_attacker, (CENTERPRIO_SPAM + 5), "Killing people while you don't have the ball gives no points!");
198
199                 if(frag_attacker.ballcarried) // get kills as carrier
200                         PlayerScore_Add(frag_attacker, SP_KEEPAWAY_SCORE, 1);
201         }
202
203         if(self.ballcarried) { ka_DropEvent(self); }
204         return 1;
205 }
206
207 MUTATOR_HOOKFUNCTION(ka_GiveFragsForKill)
208 {
209         frag_score = 0; // no frags counted in keepaway
210         return 1;
211 }
212
213 MUTATOR_HOOKFUNCTION(ka_PlayerPreThink)
214 {
215         self.items &~= IT_KEY1;
216
217         if(self.ballcarried)
218                 self.items |= IT_KEY1; 
219         
220         if(self.BUTTON_USE)
221                 if(self.ballcarried) { ka_DropEvent(self); }
222
223         return 1;
224 }
225
226 MUTATOR_DEFINITION(gamemode_keepaway)
227 {
228         MUTATOR_HOOK(MakePlayerObserver, ka_RemovePlayer, CBC_ORDER_ANY);
229         MUTATOR_HOOK(ClientDisconnect, ka_RemovePlayer, CBC_ORDER_ANY);
230         MUTATOR_HOOK(PlayerDies, ka_Scoring, CBC_ORDER_ANY);
231         MUTATOR_HOOK(GiveFragsForKill, ka_GiveFragsForKill, CBC_ORDER_FIRST);
232         MUTATOR_HOOK(PlayerPreThink, ka_PlayerPreThink, CBC_ORDER_FIRST);
233
234         MUTATOR_ONADD
235         {
236                 if(time > 1) // game loads at time 1
237                         error("This is a game type and it cannot be added at runtime.");
238                 g_keepaway = 1;
239                 ka_Initialize();
240         }
241
242         MUTATOR_ONREMOVE
243         {
244                 g_keepaway = 0;
245                 error("This is a game type and it cannot be removed at runtime.");
246         }
247
248         return 0;
249 }