378c45b5e31d3ef91d30535c7763cb7ba114975c
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mutators / gamemode_keepaway.qc
1 void ka_SpawnBall(void);
2 void ka_TouchEvent(entity);
3 void ka_RespawnBall(void);
4
5 void ka_Initialize()
6 {
7         print("^4ka_Initialize was just called!\n");
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_SpawnBall() // self = the ball
24 {
25         if(!g_keepaway) { 
26                 remove(self); 
27                 return; 
28         }
29         if (!self.model) {
30                 self.model = "models/orbs/orbblue.md3"; 
31                 self.scale = 1;
32         }
33
34         precache_model(self.model);
35         setmodel(self, self.model);
36         setsize(self, BALL_MINS, BALL_MAXS);
37         ball_scale = self.scale;
38         self.classname = "keepawayball";
39         self.damageforcescale = cvar("g_keepawayball_damageforcescale");
40         self.effects = self.effects | EF_FULLBRIGHT;
41         self.movetype = MOVETYPE_BOUNCE;
42         self.touch = ka_TouchEvent;
43         self.think = ka_RespawnBall;
44         self.nextthink = time;
45         self.flags = FL_ITEM;
46         //self.reset = ka_Reset;
47         self.owner = world;
48         
49         // todo: Waypoints and radar
50         WaypointSprite_AttachCarrier("nb-ball", self);
51         //bprint("^4ka_SpawnBall was just called!\n");
52 }
53
54 void ka_RespawnBall()
55 {
56         if(MoveToRandomMapLocation(self, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, 10, 1024, 256))
57         {
58                 makevectors(self.angles);
59                 self.movetype = MOVETYPE_BOUNCE;
60                 self.velocity = '0 0 200';
61                 self.angles = '0 0 0';
62                 self.solid = SOLID_TRIGGER;
63                 //self.touch = ka_TouchEvent;
64                 self.think = ka_RespawnBall;
65                 self.nextthink = time + cvar("g_keepawayball_respawntime");
66         }
67         else
68         {
69                 // sorry, can't spawn, better luck next frame
70                 self.think = ka_RespawnBall;
71                 self.nextthink = time;
72         }
73         //bprint("^4ka_RespawnBall was just called!\n");
74 }
75
76 void ka_TouchEvent(entity plyr)
77 {
78         if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
79         {
80                 self.think = ka_SpawnBall;
81                 self.nextthink = time;
82                 return;
83         }
84         if(!plyr) 
85                 return;
86         if(!self) 
87                 return;
88         if(other.classname != "player" || other.health < 1)
89                 return;
90         if(self.wait > time)
91                 return;
92         //if(time > self.ctf_droptime + cvar("g_keepawayball_respawntime"))
93         //      return;
94
95         self.owner = other;
96         other.kaballcarried = self;
97         setattachment(self, other, "");
98         setorigin(self, BALL_ATTACHORG);
99         
100         self.velocity = '0 0 0';
101         self.movetype = MOVETYPE_NONE;
102         self.touch = SUB_Null;
103         self.alpha = 0.01;
104         
105         self.think = SUB_Null;
106         self.nextthink = 0;
107
108         self.glow_color = cvar("g_keepawayball_trail_color");
109         self.glow_trail = TRUE;
110         plyr.effects |= 8;
111         plyr.alpha = 0.6;
112
113         bprint(other.netname, "^7 has picked up the ball!\n");
114         WriteByte(MSG_BROADCAST, SVC_CENTERPRINT);
115         WriteString(MSG_BROADCAST, strcat("\n\n", other.netname, "^7 has picked up the ball!\n"));
116         sound(self.owner, CHAN_AUTO, "keepaway/pickedup.wav", VOL_BASE, ATTN_NORM);
117         
118         PlayerScore_Add(other, SP_KEEPAWAY_PICKUPS, 1);
119
120         // todo: Waypoints and radar
121 }
122
123 MUTATOR_HOOKFUNCTION(ka_RemovePlayer)
124 {
125         if(self.kaballcarried) {
126                 entity ball;
127                 ball = self.kaballcarried;
128
129                 setattachment(ball, world, "");
130                 ball.movetype = MOVETYPE_BOUNCE;
131                 ball.solid = SOLID_TRIGGER;
132                 ball.wait = time + 1;
133                 ball.ctf_droptime = time;
134                 ball.think = ka_SpawnBall;
135                 ball.nextthink = time + cvar("g_keepawayball_respawntime");
136                 ball.touch = ka_TouchEvent;
137                 self.effects = EF_LOWPRECISION;
138                 self.alpha = 1.0;
139                 ball.alpha = 1.0;
140                 setorigin(ball, self.origin + '0 0 10');
141                 ball.velocity = '0 0 200' + '0 100 0'*crandom() + '100 0 0'*crandom();
142         
143                 bprint(self.netname, "^7 has dropped the ball!\n");
144                 WriteByte(MSG_BROADCAST, SVC_CENTERPRINT);
145                 WriteString(MSG_BROADCAST, strcat("\n\n", self.netname, "^7 has dropped the ball!\n"));
146                 sound(other, CHAN_AUTO, "keepaway/dropped.wav", VOL_BASE, ATTN_NORM);   
147                 
148                 PlayerScore_Add(self, SP_KEEPAWAY_DROPS, 1);
149                 
150                 // todo
151                 WaypointSprite_AttachCarrier("nb-ball", ball);
152                 WaypointSprite_Kill(self.waypointsprite_attachedforcarrier);
153                 
154                 ball.owner.kaballcarried = world;
155                 ball.owner = world;
156         }
157         
158         if((frag_attacker || frag_target) && !(frag_attacker == frag_target)) {
159                 if(frag_target.kaballcarried) // get amount of times killing carrier
160                         PlayerScore_Add(frag_attacker, SP_KEEPAWAY_CARRIERKILLS, 1);
161                 else if not(frag_attacker.kaballcarried)
162                         if(cvar("g_keepaway_noncarrier_warn"))
163                                 centerprint_atprio(frag_attacker, (CENTERPRIO_SPAM + 5), "Killing people while you don't have the ball gives no points!");
164         
165                 if(frag_attacker.kaballcarried) // get kills as carrier
166                         PlayerScore_Add(frag_attacker, SP_KEEPAWAY_SCORE, 1);
167         }
168         return 1;
169 }
170 /*
171 MUTATOR_HOOKFUNCTION(ka_Scoring)
172 {
173         if not(frag_attacker == frag_target)
174         {
175                 if(frag_target.kaballcarried) { // get amount of times killing carrier
176                         PlayerScore_Add(frag_attacker, SP_KEEPAWAY_CARRIERKILLS, 1);
177                         //ka_RemovePlayer();
178                 }
179                 else if not(frag_attacker.kaballcarried)
180                         if(cvar("g_keepaway_noncarrier_warn"))
181                                 centerprint_atprio(frag_attacker, (CENTERPRIO_SPAM + 5), "Killing people while you don't have the ball gives no points!");
182
183                 if(frag_attacker.kaballcarried) // get kills as carrier
184                         PlayerScore_Add(frag_attacker, SP_KEEPAWAY_SCORE, 1);
185         }
186         return 1;
187 }
188
189
190 MUTATOR_HOOKFUNCTION(ka_PlayerDies)
191 {
192         float i;
193         entity e;
194
195         float temp_tag_players_count;
196         temp_tag_players_count = tag_players_count;
197
198         if(frag_target.tagcolor == frag_target.tagcolor_original) // if this is the first time we die... (our tagcolor remained unchanged)
199         {
200                 for(i = 0; i < temp_tag_players_count; ++i) // check other players...
201                 {
202                         e = tag_players[i];
203                         if(e == world) // empty slot, skip to next
204                         {
205                                 if(temp_tag_players_count < TAGCOLOR_MAX - 1) // just in case
206                                         ++temp_tag_players_count;
207                                 continue;
208                         }
209
210                         if(e.tagcolor == frag_target.tagcolor_original) // and see if they have our original tag color
211                         {
212                                 tag_GetFragAttackers_ColorOwner();
213                                 centerprint(e, strcat("^1Your master ^7", frag_target.netname, "^1 was tagged by ^7", frag_attacker.netname, " ^1with ^7", color_owner_red, " ^1color.\n"));
214                                 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.
215                                 setcolor(e, 16 * e.tagcolor + e.tagcolor);
216                         }
217                 }
218         }
219         else
220         {
221                 frag_target.tagcolor = frag_attacker.tagcolor;
222                 setcolor(frag_target, 16 * frag_target.tagcolor + frag_target.tagcolor);
223         }
224
225         tag_GetFragAttackers_ColorOwner();
226
227         if(color_owner_self)
228                 color_owner_green = "^2your own";
229         centerprint(frag_attacker, strcat("^2You tagged ^7", frag_target.netname, " ^2with ^7", color_owner_green, " ^2color.\n"));
230
231         if(color_owner_self)
232                 color_owner_red = "^1their own";
233         centerprint(frag_target, strcat("^1You were tagged by ^7", frag_attacker.netname, " ^1with ^7", color_owner_red, " ^1color.\n"));
234         bprint("^7", frag_target.netname, "^1 was tagged by ^7", frag_attacker.netname, " ^1with ^7", color_owner_red, " ^1color.\n");
235
236         frag_target.health = cvar("g_balance_health_start"); // "respawn" the player :P
237
238         tag_CheckWinner();
239
240         return 1;
241 }
242 */
243
244
245 MUTATOR_DEFINITION(gamemode_keepaway)
246 {
247         MUTATOR_HOOK(MakePlayerObserver, ka_RemovePlayer, CBC_ORDER_ANY);
248         MUTATOR_HOOK(ClientDisconnect, ka_RemovePlayer, CBC_ORDER_ANY);
249         MUTATOR_HOOK(PlayerDies, ka_RemovePlayer, CBC_ORDER_ANY);
250         //MUTATOR_HOOK(PlayerSpawn, ka_PlayerSpawn, CBC_ORDER_ANY);
251         //MUTATOR_HOOK(GiveFragsForKill, ka_GiveFragsForKill, CBC_ORDER_FIRST);
252         //MUTATOR_HOOK(PlayerPreThink, ka_PlayerPreThink, CBC_ORDER_FIRST);
253
254         MUTATOR_ONADD
255         {
256                 if(time > 1) // game loads at time 1
257                         error("This is a game type and it cannot be added at runtime.");
258                 g_keepaway = 1;
259                 ka_Initialize();
260         }
261
262         MUTATOR_ONREMOVE
263         {
264                 g_keepaway = 0;
265                 error("This is a game type and it cannot be removed at runtime.");
266         }
267
268         return 0;
269 }
270