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