]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/cheats.qc
Merge remote branch 'origin/master' into samual/config_updates
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / cheats.qc
1 void CopyBody(float keepvelocity);
2
3 #ifdef NOCHEATS
4
5 float CheatImpulse(float i) { return 0; }
6 float CheatCommand(float argc) { return 0; }
7 float CheatFrame() { return 0; }
8 void CheatInit() { cheatcount_total = world.cheatcount; }
9 void CheatShutdown() { }
10 void CheatInitClient() { }
11 void CheatShutdownClient() { }
12 void Drag_MoveDrag(entity from, entity to) { }
13
14 #else
15
16 .float maycheat;
17 float gamestart_sv_cheats;
18
19 #define CHIMPULSE_SPEEDRUN_INIT 30
20 #define CHIMPULSE_GIVE_ALL 99
21 #define CHIMPULSE_CLONE_MOVING 140
22 #define CHIMPULSE_SPEEDRUN 141
23 #define CHIMPULSE_CLONE_STANDING 142
24 #define CHIMPULSE_TELEPORT 143
25 #define CHIMPULSE_R00T 148
26
27 #define CHRAME_DRAG 8
28
29 void CheatInit()
30 {
31         gamestart_sv_cheats = autocvar_sv_cheats;
32 }
33
34 void CheatShutdown()
35 {
36 }
37
38 void CheatInitClient()
39 {
40 }
41
42 void CheatShutdownClient()
43 {
44 }
45
46 float CheatsAllowed(float i, float argc, float fr) // the cheat gets passed as argument for possible future ACL checking
47 {
48         // dead people cannot cheat
49         if(self.deadflag != DEAD_NO)
50                 return 0;
51         if(self.classname != "player")
52                 return 0;
53         
54         // sv_clones
55         if(i == CHIMPULSE_CLONE_MOVING || i == CHIMPULSE_CLONE_STANDING)
56                 if(self.lip < sv_clones)
57                         return 1;
58
59         // haha
60         if(self.maycheat)
61                 return 1;
62         
63         // sv_cheats
64         if(gamestart_sv_cheats && autocvar_sv_cheats)
65                 return 1;
66
67         // if we get here, player is not allowed to cheat. Log it.
68         if(i)
69                 bprint(sprintf("Player %s^7 tried to use cheat 'impulse %d'\n", self.netname, i));
70         else if(argc)
71                 bprint(sprintf("Player %s^7 tried to use cheat '%s'\n", self.netname, argv(0)));
72         else if(fr)
73                 bprint(sprintf("Player %s^7 tried to use cheat frame %d\n", self.netname, fr));
74         else
75                 bprint(sprintf("Player %s^7 tried to use an unknown cheat\n", self.netname));
76
77         return 0;
78 }
79
80 #define BEGIN_CHEAT_FUNCTION() \
81         float cheating, attempting; \
82         cheating = 0; attempting = 0
83 #define DID_CHEAT() \
84         ++cheating
85 #define ADD_CHEATS(e,n) \
86         cheatcount_total += n; \
87         e.cheatcount += n
88 #define END_CHEAT_FUNCTION() \
89         ADD_CHEATS(self,cheating); \
90         return attempting
91 #define IS_CHEAT(i,argc,fr) \
92         if((++attempting, !CheatsAllowed(i,argc,fr))) \
93                 break
94
95 float CheatImpulse(float i)
96 {
97         BEGIN_CHEAT_FUNCTION();
98         switch(i)
99         {
100                 entity e, e2;
101                 vector org;
102
103                 case CHIMPULSE_SPEEDRUN_INIT: // deploy personal waypoint
104                         // shared with regular waypoint init, so this is not a cheat by itself
105                         if(!self.personal)
106                         {
107                                 self.personal = spawn();
108                                 self.personal.classname = "personal_wp";
109                         }
110                         self.personal.origin = self.origin;
111                         self.personal.v_angle = self.v_angle;
112                         self.personal.velocity = self.velocity;
113                         self.personal.ammo_rockets = self.ammo_rockets;
114                         self.personal.ammo_nails = self.ammo_nails;
115                         self.personal.ammo_cells = self.ammo_cells;
116                         self.personal.ammo_shells = self.ammo_shells;
117                         self.personal.ammo_fuel = self.ammo_fuel;
118                         self.personal.health = self.health;
119                         self.personal.armorvalue = self.armorvalue;
120                         self.personal.weapons = self.weapons;
121                         self.personal.items = self.items;
122                         self.personal.pauserotarmor_finished = self.pauserotarmor_finished;
123                         self.personal.pauserothealth_finished = self.pauserothealth_finished;
124                         self.personal.pauserotfuel_finished = self.pauserotfuel_finished;
125                         self.personal.pauseregen_finished = self.pauseregen_finished;
126                         self.personal.strength_finished = self.strength_finished;
127                         self.personal.invincible_finished = self.invincible_finished;
128                         self.personal.teleport_time = time;
129                         break; // this part itself doesn't cheat, so let's not count this
130                 case CHIMPULSE_CLONE_MOVING:
131                         IS_CHEAT(i, 0, 0);
132                         makevectors (self.v_angle);
133                         self.velocity = self.velocity + v_forward * 300;
134                         CopyBody(1);
135                         self.lip += 1;
136                         self.velocity = self.velocity - v_forward * 300;
137                         DID_CHEAT();
138                         break;
139                 case CHIMPULSE_CLONE_STANDING:
140                         IS_CHEAT(i, 0, 0);
141                         CopyBody(0);
142                         self.lip += 1;
143                         DID_CHEAT();
144                         break;
145                 case CHIMPULSE_GIVE_ALL:
146                         IS_CHEAT(i, 0, 0);
147                         CheatCommand(tokenize_console("give all"));
148                         break; // already counted as cheat
149                 case CHIMPULSE_SPEEDRUN:
150                         IS_CHEAT(i, 0, 0);
151                         if(self.personal)
152                         {
153                                 self.speedrunning = TRUE;
154                                 tracebox(self.personal.origin, self.mins, self.maxs, self.personal.origin, MOVE_WORLDONLY, self);
155                                 if(trace_startsolid)
156                                 {
157                                         sprint(self, "Cannot move there, cheater - only waypoints set using g_waypointsprite_personal work\n");
158                                 }
159                                 else
160                                 {
161                                         // Abort speedrun, teleport back
162                                         setorigin(self, self.personal.origin);
163                                         self.oldvelocity = self.velocity = self.personal.velocity;
164                                         self.angles = self.personal.v_angle;
165                                         self.fixangle = TRUE;
166                                         if(self.flagcarried)
167                                         {
168                                                 bprint("The ", self.flagcarried.netname, " was returned to base by its carrier\n");
169                                                 ReturnFlag(self.flagcarried);
170                                         }
171                                 }
172                                 if(g_ctf)
173                                 {
174                                         self.ammo_rockets = 999;
175                                         self.ammo_nails = 999;
176                                         self.ammo_cells = 999;
177                                         self.ammo_shells = 999;
178                                         self.ammo_fuel = 999;
179                                         self.health = start_health;
180                                         self.armorvalue = start_armorvalue;
181                                         self.weapons |= weaponsInMap;
182                                         self.pauserotarmor_finished = time + autocvar_g_balance_pause_armor_rot_spawn;
183                                         self.pauserothealth_finished = time + autocvar_g_balance_pause_health_rot_spawn;
184                                         self.pauserotfuel_finished = time + autocvar_g_balance_pause_fuel_rot_spawn;
185                                         self.pauseregen_finished = time + autocvar_g_balance_pause_health_regen_spawn;
186                                         self.strength_finished = 0;
187                                         self.invincible_finished = 0;
188                                 }
189                                 else
190                                 {
191                                         self.ammo_rockets = self.personal.ammo_rockets;
192                                         self.ammo_nails = self.personal.ammo_nails;
193                                         self.ammo_cells = self.personal.ammo_cells;
194                                         self.ammo_shells = self.personal.ammo_shells;
195                                         self.ammo_fuel = self.personal.ammo_fuel;
196                                         self.health = self.personal.health;
197                                         self.armorvalue = self.personal.armorvalue;
198                                         self.weapons = self.personal.weapons;
199                                         self.items = self.personal.items;
200                                         self.pauserotarmor_finished = time + self.personal.pauserotarmor_finished - self.personal.teleport_time;
201                                         self.pauserothealth_finished = time + self.personal.pauserothealth_finished - self.personal.teleport_time;
202                                         self.pauserotfuel_finished = time + self.personal.pauserotfuel_finished - self.personal.teleport_time;
203                                         self.pauseregen_finished = time + self.personal.pauseregen_finished - self.personal.teleport_time;
204                                         self.strength_finished = time + self.personal.strength_finished - self.personal.teleport_time;
205                                         self.invincible_finished = time + self.personal.invincible_finished - self.personal.teleport_time;
206                                 }
207                                 DID_CHEAT();
208                                 break;
209                         }
210                         if(self.deadflag != DEAD_NO)
211                                 sprint(self, "UR DEAD AHAHAH))\n");
212                         else
213                                 sprint(self, "No waypoint set, cheater (use g_waypointsprite_personal to set one)\n");
214                         break;
215                 case CHIMPULSE_TELEPORT:
216                         IS_CHEAT(i, 0, 0);
217                         if(MoveToRandomMapLocation(self, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, ((gamestart_sv_cheats >= 2) ? 100000 : 100), 1024, 256))
218                         {
219                                 self.angles_x = -self.angles_x;
220                                 self.fixangle = TRUE;
221                                 self.velocity = '0 0 0';
222                                 DID_CHEAT();
223                                 break;
224                         }
225                         sprint(self, "Emergency teleport could not find a good location, forget it!\n");
226                         break;
227                 case CHIMPULSE_R00T:
228                         IS_CHEAT(i, 0, 0);
229                         FOR_EACH_PLAYER(e)
230                         {
231                                 get_model_parameters(e.playermodel, e.skin);
232                                 if(get_model_parameters_sex == "Female")
233                                 {
234                                         makevectors(e.angles);
235                                         traceline(e.origin, e.origin + v_right * 256, MOVE_NORMAL, e);
236                                 }
237                                 else
238                                 {
239                                         org_x = random();
240                                         org_y = random();
241                                         org_z = 0;
242                                         org = normalize(org);
243                                         traceline(e.origin, e.origin + org * 256, MOVE_NORMAL, e); // random direction
244                                 }
245
246                                 org = findbetterlocation(trace_endpos, 12);
247
248                                 e2 = spawn();
249                                 setorigin(e2, org);
250                                 pointparticles(particleeffectnum("rocket_explode"), org, '0 0 0', 1);
251                                 sound(e2, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
252                                 RadiusDamage(e2, e, 1000, 0, 128, e, 500, DEATH_CHEAT, world);
253                                 remove(e2);
254                         }
255                         print("404 Sportsmanship not found.\n");
256                         DID_CHEAT();
257                         break;
258         }
259
260         END_CHEAT_FUNCTION();
261 }
262
263 void DragBox_Think();
264 float drag_lastcnt;
265 float CheatCommand(float argc)
266 {
267         BEGIN_CHEAT_FUNCTION();
268         string cmd;
269         cmd = argv(0);
270         switch(cmd)
271         {
272                 entity e;
273                 float effectnum, f;
274                 vector start, end;
275                 entity oldself;
276
277                 case "pointparticles":
278                         IS_CHEAT(0, argc, 0);
279                         if(argc == 5)
280                         {
281                                 // arguments:
282                                 //   effectname
283                                 //   origin (0..1, on crosshair line)
284                                 //   velocity
285                                 //   howmany
286                                 effectnum = particleeffectnum(argv(1));
287                                 f = stof(argv(2));
288                                 crosshair_trace(self);
289                                 start = (1-f) * self.origin + f * trace_endpos;
290                                 end = stov(argv(3));
291                                 f = stof(argv(4));
292                                 pointparticles(effectnum, start, end, f);
293                                 DID_CHEAT();
294                                 break;
295                         }
296                         sprint(self, "Usage: sv_cheats 1; restart; cmd pointparticles effectname position(0..1) velocityvector multiplier\n");
297                         break;
298                 case "trailparticles":
299                         IS_CHEAT(0, argc, 0);
300                         if(argc == 2)
301                         {
302                                 // arguments:
303                                 //   effectname
304                                 effectnum = particleeffectnum(argv(1));
305                                 W_SetupShot(self, FALSE, FALSE, "", CH_WEAPON_A, 0);
306                                 traceline(w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, MOVE_NORMAL, self);
307                                 trailparticles(self, effectnum, w_shotorg, trace_endpos);
308                                 DID_CHEAT();
309                                 break;
310                         }
311                         sprint(self, "Usage: sv_cheats 1; restart; cmd trailparticles effectname\n");
312                         break;
313                 case "make":
314                         IS_CHEAT(0, argc, 0);
315                         if(argc == 3)
316                         {
317                                 // arguments:
318                                 //   modelname mode
319                                 f = stof(argv(2));
320                                 W_SetupShot(self, FALSE, FALSE, "", CH_WEAPON_A, 0);
321                                 traceline(w_shotorg, w_shotorg + w_shotdir * 2048, MOVE_NORMAL, self);
322                                 if((trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) || trace_fraction == 1)
323                                 {
324                                         sprint(self, "cannot make stuff there (bad surface)\n");
325                                 }
326                                 else
327                                 {
328                                         e = spawn();
329                                         e.model = strzone(argv(1));
330                                         e.mdl = "rocket_explode";
331                                         e.health = 1000;
332                                         setorigin(e, trace_endpos);
333                                         e.effects = EF_NOMODELFLAGS;
334                                         if(f == 1)
335                                         {
336                                                 e.angles = fixedvectoangles2(trace_plane_normal, v_forward);
337                                                 e.angles = AnglesTransform_ApplyToAngles(e.angles, '-90 0 0'); // so unrotated models work
338                                         }
339                                         oldself = self;
340                                         self = e;
341                                         spawnfunc_func_breakable();
342                                         self = oldself;
343                                         // now, is it valid?
344                                         if(f == 0)
345                                         {
346                                                 tracebox(e.origin, e.mins, e.maxs, e.origin, MOVE_NORMAL, e);
347                                                 if(trace_startsolid)
348                                                 {
349                                                         remove(e);
350                                                         sprint(self, "cannot make stuff there (no space)\n");
351                                                 }
352                                                 else
353                                                         DID_CHEAT();
354                                         }
355                                         else
356                                                 DID_CHEAT();
357                                 }
358                         }
359                         else
360                                 sprint(self, "Usage: sv_cheats 1; restart; cmd make models/... 0/1/2\n");
361                         break;
362                 case "penalty":
363                         IS_CHEAT(0, argc, 0);
364                         if(argc == 3)
365                         {
366                                 race_ImposePenaltyTime(self, stof(argv(1)), argv(2));
367                                 DID_CHEAT();
368                                 break;
369                         }
370                         sprint(self, "Usage: sv_cheats 1; restart; cmd penalty 5.0 AHAHAHAHAHAHAH))\n");
371                         break;
372                 case "dragbox_spawn":
373                         IS_CHEAT(0, argc, 0);
374                         e = spawn();
375                         e.classname = "dragbox_box";
376                         e.think = DragBox_Think;
377                         e.nextthink = time;
378                         e.solid = -1; // black
379                         setmodel(e, "null"); // network it
380                         if(argc == 4)
381                                 e.cnt = stof(argv(1));
382                         else
383                                 e.cnt = max(0, drag_lastcnt);
384
385                         e.aiment = spawn();
386                         e.aiment.classname = "dragbox_corner_1";
387                         e.aiment.owner = e;
388                         setmodel(e.aiment, "models/marker.md3");
389                         e.aiment.skin = 0;
390                         setsize(e.aiment, '0 0 0', '0 0 0');
391                         if(argc == 4)
392                                 setorigin(e.aiment, stov(argv(2)));
393                         else
394                         {
395                                 crosshair_trace(self);
396                                 setorigin(e.aiment, trace_endpos);
397                         }
398
399                         e.enemy = spawn();
400                         e.enemy.classname = "dragbox_corner_2";
401                         e.enemy.owner = e;
402                         setmodel(e.enemy, "models/marker.md3");
403                         e.enemy.skin = 1;
404                         setsize(e.enemy, '0 0 0', '0 0 0');
405                         end = normalize(self.origin + self.view_ofs - e.aiment.origin);
406                         end_x = (end_x > 0) * 2 - 1;
407                         end_y = (end_y > 0) * 2 - 1;
408                         end_z = (end_z > 0) * 2 - 1;
409                         if(argc == 4)
410                                 setorigin(e.enemy, stov(argv(3)));
411                         else
412                                 setorigin(e.enemy, e.aiment.origin + 32 * end);
413
414                         e.killindicator = spawn();
415                         e.killindicator.classname = "drag_digit";
416                         e.killindicator.owner = e;
417                         setattachment(e.killindicator, e, "");
418                         setorigin(e.killindicator, '0 0 -8');
419                         e.killindicator.killindicator = spawn();
420                         e.killindicator.killindicator.classname = "drag_digit";
421                         e.killindicator.killindicator.owner = e;
422                         setattachment(e.killindicator.killindicator, e, "");
423                         setorigin(e.killindicator.killindicator, '0 0 8');
424                         DID_CHEAT();
425                         break;
426                 case "dragpoint_spawn":
427                         IS_CHEAT(0, argc, 0);
428                         e = spawn();
429                         e.classname = "dragpoint";
430                         e.think = DragBox_Think;
431                         e.nextthink = time;
432                         e.solid = 0; // nothing special
433                         setmodel(e, "models/marker.md3");
434                         setsize(e, PL_MIN, PL_MAX);
435                         e.skin = 2;
436                         if(argc == 3)
437                                 e.cnt = stof(argv(1));
438                         else
439                                 e.cnt = drag_lastcnt;
440                         if(argc == 3)
441                                 setorigin(e, stov(argv(2)));
442                         else
443                         {
444                                 crosshair_trace(self);
445                                 setorigin(e, trace_endpos + normalize(self.origin + self.view_ofs - trace_endpos));
446                                 move_out_of_solid(e);
447                         }
448
449                         e.killindicator = spawn();
450                         e.killindicator.classname = "drag_digit";
451                         e.killindicator.owner = e;
452                         setattachment(e.killindicator, e, "");
453                         setorigin(e.killindicator, '0 0 40');
454                         e.killindicator.killindicator = spawn();
455                         e.killindicator.killindicator.classname = "drag_digit";
456                         e.killindicator.killindicator.owner = e;
457                         setattachment(e.killindicator.killindicator, e, "");
458                         setorigin(e.killindicator.killindicator, '0 0 56');
459                         DID_CHEAT();
460                         break;
461                 case "drag_remove":
462                         IS_CHEAT(0, argc, 0);
463                         RandomSelection_Init();
464                         crosshair_trace(self);
465                         for(e = world; (e = find(e, classname, "dragbox_box")); )
466                                 RandomSelection_Add(e, 0, string_null, 1, 1 / vlen(e.origin + (e.mins + e.maxs) * 0.5 - trace_endpos));
467                         for(e = world; (e = find(e, classname, "dragpoint")); )
468                                 RandomSelection_Add(e, 0, string_null, 1, 1 / vlen(e.origin + (e.mins + e.maxs) * 0.5 - trace_endpos));
469                         if(RandomSelection_chosen_ent)
470                         {
471                                 remove(RandomSelection_chosen_ent.killindicator.killindicator);
472                                 remove(RandomSelection_chosen_ent.killindicator);
473                                 if(RandomSelection_chosen_ent.aiment)
474                                         remove(RandomSelection_chosen_ent.aiment);
475                                 if(RandomSelection_chosen_ent.enemy)
476                                         remove(RandomSelection_chosen_ent.enemy);
477                                 remove(RandomSelection_chosen_ent);
478                         }
479                         DID_CHEAT();
480                         break;
481                 case "drag_setcnt":
482                         IS_CHEAT(0, argc, 0);
483                         if(argc == 2)
484                         {
485                                 RandomSelection_Init();
486                                 crosshair_trace(self);
487                                 for(e = world; (e = find(e, classname, "dragbox_box")); )
488                                         RandomSelection_Add(e, 0, string_null, 1, 1 / vlen(e.origin + (e.mins + e.maxs) * 0.5 - trace_endpos));
489                                 for(e = world; (e = find(e, classname, "dragpoint")); )
490                                         RandomSelection_Add(e, 0, string_null, 1, 1 / vlen(e.origin + (e.mins + e.maxs) * 0.5 - trace_endpos));
491                                 if(RandomSelection_chosen_ent)
492                                 {
493                                         if(substring(argv(1), 0, 1) == "*")
494                                                 RandomSelection_chosen_ent.cnt = drag_lastcnt = RandomSelection_chosen_ent.cnt + stof(substring(argv(1), 1, -1));
495                                         else
496                                                 RandomSelection_chosen_ent.cnt = drag_lastcnt = stof(argv(1));
497                                 }
498                                 DID_CHEAT();
499                                 break;
500                         }
501                         sprint(self, "Usage: sv_cheats 1; restart; cmd dragbox_setcnt cnt\n");
502                         break;
503                 case "drag_save":
504                         IS_CHEAT(0, argc, 0);
505                         if(argc == 2)
506                         {
507                                 f = fopen(argv(1), FILE_WRITE);
508                                 fputs(f, "cmd drag_clear\n");
509                                 for(e = world; (e = find(e, classname, "dragbox_box")); )
510                                 {
511                                         fputs(f, strcat("cmd dragbox_spawn ", ftos(e.cnt), " \"", vtos(e.aiment.origin), "\" \"", vtos(e.enemy.origin), "\"\n"));
512                                 }
513                                 for(e = world; (e = find(e, classname, "dragpoint")); )
514                                 {
515                                         fputs(f, strcat("cmd dragpoint_spawn ", ftos(e.cnt), " \"", vtos(e.origin), "\"\n"));
516                                 }
517                                 fclose(f);
518                                 DID_CHEAT();
519                                 break;
520                         }
521                         sprint(self, "Usage: sv_cheats 1; restart; cmd dragbox_save filename\n");
522                         break;
523                 case "drag_saveraceent":
524                         IS_CHEAT(0, argc, 0);
525                         if(argc == 2)
526                         {
527                                 f = fopen(argv(1), FILE_WRITE);
528                                 for(e = world; (e = find(e, classname, "dragbox_box")); )
529                                 {
530                                         fputs(f, "{\n");
531                                         fputs(f, "\"classname\" \"trigger_race_checkpoint\"\n");
532                                         fputs(f, strcat("\"origin\" \"", ftos(e.absmin_x), " ", ftos(e.absmin_y), " ", ftos(e.absmin_z), "\"\n"));
533                                         fputs(f, strcat("\"maxs\" \"", ftos(e.absmax_x - e.absmin_x), " ", ftos(e.absmax_y - e.absmin_y), " ", ftos(e.absmax_z - e.absmin_z), "\"\n"));
534                                         fputs(f, strcat("\"cnt\" \"", ftos(e.cnt), "\"\n"));
535                                         fputs(f, strcat("\"targetname\" \"checkpoint", ftos(e.cnt), "\"\n"));
536                                         fputs(f, "}\n");
537                                 }
538                                 for(e = world; (e = find(e, classname, "dragpoint")); )
539                                 {
540                                         start = '0 0 0';
541                                         effectnum = 0;
542                                         for(oldself = world; (oldself = find(oldself, classname, "dragbox_box")); )
543                                         {
544                                                 if(e.cnt <= 0 && oldself.cnt == 0 || e.cnt == oldself.cnt)
545                                                 {
546                                                         start = start + oldself.origin;
547                                                         ++effectnum;
548                                                 }
549                                         }
550                                         start *= 1 / effectnum;
551                                         fputs(f, "{\n");
552                                         fputs(f, "\"classname\" \"info_player_race\"\n");
553                                         fputs(f, strcat("\"angle\" \"", ftos(vectoyaw(start - e.origin)), "\"\n"));
554                                         fputs(f, strcat("\"origin\" \"", ftos(e.origin_x), " ", ftos(e.origin_y), " ", ftos(e.origin_z), "\"\n"));
555                                         if(e.cnt == -2)
556                                         {
557                                                 fputs(f, "\"target\" \"checkpoint0\"\n");
558                                                 fputs(f, "\"race_place\" \"0\"\n");
559                                         }
560                                         else if(e.cnt == -1)
561                                         {
562                                                 fputs(f, "\"target\" \"checkpoint0\"\n");
563                                                 fputs(f, "\"race_place\" \"-1\"\n");
564                                         }
565                                         else
566                                         {
567                                                 fputs(f, strcat("\"target\" \"checkpoint", ftos(e.cnt), "\"\n"));
568                                                 if(e.cnt == 0)
569                                                 {
570                                                         // these need race_place
571                                                         // counting...
572                                                         effectnum = 1;
573                                                         for(oldself = world; (oldself = find(oldself, classname, "dragpoint")); )
574                                                         if(oldself.cnt == 0)
575                                                         {
576                                                                 if(vlen(oldself.origin - start) < vlen(e.origin - start))
577                                                                         ++effectnum;
578                                                                 else if(vlen(oldself.origin - start) == vlen(e.origin - start) && num_for_edict(oldself) < num_for_edict(e))
579                                                                         ++effectnum;
580                                                         }
581                                                         fputs(f, strcat("\"race_place\" \"", ftos(effectnum), "\"\n"));
582                                                 }
583                                         }
584                                         fputs(f, "}\n");
585                                 }
586                                 fclose(f);
587                                 DID_CHEAT();
588                                 break;
589                         }
590                         sprint(self, "Usage: sv_cheats 1; restart; cmd dragbox_save filename\n");
591                         break;
592                 case "drag_clear":
593                         IS_CHEAT(0, argc, 0);
594                         for(e = world; (e = find(e, classname, "dragbox_box")); )
595                                 remove(e);
596                         for(e = world; (e = find(e, classname, "dragbox_corner_1")); )
597                                 remove(e);
598                         for(e = world; (e = find(e, classname, "dragbox_corner_2")); )
599                                 remove(e);
600                         for(e = world; (e = find(e, classname, "dragpoint")); )
601                                 remove(e);
602                         for(e = world; (e = find(e, classname, "drag_digit")); )
603                                 remove(e);
604                         DID_CHEAT();
605                         break;
606                 case "god":
607                         IS_CHEAT(0, argc, 0);
608                         BITXOR_ASSIGN(self.flags, FL_GODMODE);
609                         if(self.flags & FL_GODMODE)
610                         {
611                                 sprint(self, "godmode ON\n");
612                                 DID_CHEAT();
613                         }
614                         else
615                                 sprint(self, "godmode OFF\n");
616                         break;
617                 case "notarget":
618                         IS_CHEAT(0, argc, 0);
619                         BITXOR_ASSIGN(self.flags, FL_NOTARGET);
620                         if(self.flags & FL_NOTARGET)
621                         {
622                                 sprint(self, "notarget ON\n");
623                                 DID_CHEAT();
624                         }
625                         else
626                                 sprint(self, "notarget OFF\n");
627                         break;
628                 case "noclip":
629                         IS_CHEAT(0, argc, 0);
630                         if(self.movetype != MOVETYPE_NOCLIP)
631                         {
632                                 self.movetype = MOVETYPE_NOCLIP;
633                                 sprint(self, "noclip ON\n");
634                                 DID_CHEAT();
635                         }
636                         else
637                         {
638                                 self.movetype = MOVETYPE_WALK;
639                                 sprint(self, "noclip OFF\n");
640                         }
641                         break;
642                 case "fly":
643                         IS_CHEAT(0, argc, 0);
644                         if(self.movetype != MOVETYPE_FLY)
645                         {
646                                 self.movetype = MOVETYPE_FLY;
647                                 sprint(self, "flymode ON\n");
648                                 DID_CHEAT();
649                         }
650                         else
651                         {
652                                 self.movetype = MOVETYPE_WALK;
653                                 sprint(self, "flymode OFF\n");
654                         }
655                         break;
656                 case "give":
657                         IS_CHEAT(0, argc, 0);
658                         if(GiveItems(self, 1, argc))
659                                 DID_CHEAT();
660                         break;
661         }
662
663         END_CHEAT_FUNCTION();
664 }
665
666 float Drag(entity e, float grab, float ischeat);
667 void Drag_Begin(entity dragger, entity draggee, vector touchpoint);
668 void Drag_Finish(entity dragger);
669 float Drag_IsDraggable(entity draggee);
670 float Drag_MayChangeAngles(entity draggee);
671 void Drag_MoveForward(entity dragger);
672 void Drag_SetSpeed(entity dragger, float s);
673 void Drag_MoveBackward(entity dragger);
674 void Drag_Update(entity dragger);
675 float Drag_CanDrag(entity dragger);
676 float Drag_IsDragging(entity dragger);
677 void Drag_MoveDrag(entity from, entity to);
678 .entity dragentity;
679
680 float CheatFrame()
681 {
682         BEGIN_CHEAT_FUNCTION();
683
684         // Dragging can be used as either a cheat, or a function for some objects. If sv_cheats is active,
685         // the cheat dragging is used (unlimited pickup range and any entity can be carried). If sv_cheats
686         // is disabled, normal dragging is used (limited pickup range and only dragable objects can be carried),
687         // grabbing itself no longer being accounted as cheating.
688
689         switch(0)
690         {
691                 default:
692                         if(self.maycheat || (gamestart_sv_cheats && autocvar_sv_cheats))
693                         {
694                                 // use cheat dragging if cheats are enabled
695                                 crosshair_trace_plusvisibletriggers(self);
696                                 Drag(trace_ent, TRUE, TRUE);
697                         }
698                         else
699                         {
700                                 // drag is TRUE if the object can be picked up. While an object is being carried, the Drag() function
701                                 // must execute for it either way, otherwise it would cause bugs if it went out of the player's trace.
702                                 // This also makes sure that an object can only pe picked up if in range, but does not get dropped if
703                                 // it goes out of range while slinging it around.
704
705                                 float drag;
706                                 crosshair_trace_plusvisibletriggers(self);
707                                 if(vlen(self.origin - trace_ent.origin) <= autocvar_g_grab_range)
708                                 {
709                                         switch(trace_ent.grab)
710                                         {
711                                                 case 0: // can't grab
712                                                         break;
713                                                 case 1: // owner can grab
714                                                         if(trace_ent.owner == self || trace_ent.realowner == self)
715                                                                 drag = TRUE;
716                                                         break;
717                                                 case 2: // owner and team mates can grab
718                                                         if(!IsDifferentTeam(trace_ent.owner, self) || !IsDifferentTeam(trace_ent.realowner, self) || trace_ent.team == self.team)
719                                                                 drag = TRUE;
720                                                         break;
721                                                 case 3: // anyone can grab
722                                                         drag = TRUE;
723                                                         break;
724                                                 default:
725                                                         break;
726                                         }
727                                 }
728                                 Drag(trace_ent, drag, FALSE); // execute dragging
729                         }
730                         break;
731         }
732
733         END_CHEAT_FUNCTION();
734 }
735
736
737
738
739
740 // ENTITY DRAGGING
741
742 float Drag(entity e, float pick, float ischeat)
743 {
744         BEGIN_CHEAT_FUNCTION();
745
746         // returns TRUE when an entity has been picked up
747         // If pick is TRUE, the object can also be picked up if it's not being held already
748         // If pick is FALSE, only keep dragging the object if it's already being held
749
750         switch(0)
751         {
752                 default:
753                         if(Drag_IsDragging(self))
754                         {
755                                 if(self.BUTTON_DRAG)
756                                 {
757                                         if(self.impulse == 10 || self.impulse == 15 || self.impulse == 18)
758                                         {
759                                                 Drag_MoveForward(self);
760                                                 self.impulse = 0;
761                                         }
762                                         else if(self.impulse == 12 || self.impulse == 16 || self.impulse == 19)
763                                         {
764                                                 Drag_MoveBackward(self);
765                                                 self.impulse = 0;
766                                         }
767                                         else if(self.impulse >= 1 && self.impulse <= 9)
768                                         {
769                                                 Drag_SetSpeed(self, self.impulse - 1);
770                                         }
771                                         else if(self.impulse == 14)
772                                         {
773                                                 Drag_SetSpeed(self, 9);
774                                         }
775
776                                         if(frametime)
777                                                 Drag_Update(self);
778                                 }
779                                 else
780                                 {
781                                         Drag_Finish(self);
782                                 }
783                         }
784                         else
785                         {
786                                 if(Drag_CanDrag(self))
787                                         if(self.BUTTON_DRAG && pick)
788                                         {
789                                                 if(e)
790                                                         if(Drag_IsDraggable(e))
791                                                         {
792                                                                 if(ischeat)
793                                                                         IS_CHEAT(0, 0, CHRAME_DRAG);
794                                                                 if(e.draggedby)
795                                                                         Drag_Finish(e.draggedby);
796                                                                 if(e.tag_entity)
797                                                                         detach_sameorigin(e);
798                                                                 Drag_Begin(self, e, trace_endpos);
799                                                                 if(ischeat)
800                                                                         DID_CHEAT();
801                                                                 return TRUE;
802                                                         }
803                                         }
804                         }
805                         break;
806         }
807         return FALSE;
808 }
809
810 // on dragger:
811 .float draggravity;
812 .float dragspeed; // speed of mouse wheel action
813 .float dragdistance; // distance of dragentity's draglocalvector from view_ofs
814 .vector draglocalvector; // local attachment vector of the dragentity
815 .float draglocalangle;
816 // on draggee:
817 .entity draggedby;
818 .float dragmovetype;
819 void Drag_Begin(entity dragger, entity draggee, vector touchpoint)
820 {
821         float tagscale;
822
823         draggee.dragmovetype = draggee.movetype;
824         draggee.draggravity = draggee.gravity;
825         draggee.movetype = MOVETYPE_WALK;
826         draggee.gravity = 0.00001;
827         draggee.flags &~= FL_ONGROUND;
828         draggee.draggedby = dragger;
829
830         dragger.dragentity = draggee;
831
832         dragger.dragdistance = vlen(touchpoint - dragger.origin - dragger.view_ofs);
833         dragger.draglocalangle = draggee.angles_y - dragger.v_angle_y;
834         touchpoint = touchpoint - gettaginfo(draggee, 0);
835         tagscale = pow(vlen(v_forward), -2);
836         dragger.draglocalvector_x = touchpoint * v_forward * tagscale;
837         dragger.draglocalvector_y = touchpoint * v_right * tagscale;
838         dragger.draglocalvector_z = touchpoint * v_up * tagscale;
839
840         dragger.dragspeed = 64;
841 }
842
843 void Drag_Finish(entity dragger)
844 {
845         entity draggee;
846         draggee = dragger.dragentity;
847         if(dragger)
848                 dragger.dragentity = world;
849         draggee.draggedby = world;
850         draggee.movetype = draggee.dragmovetype;
851         draggee.gravity = draggee.draggravity;
852
853         switch(draggee.movetype)
854         {
855                 case MOVETYPE_TOSS:
856                 case MOVETYPE_WALK:
857                 case MOVETYPE_STEP:
858                 case MOVETYPE_FLYMISSILE:
859                 case MOVETYPE_BOUNCE:
860                 case MOVETYPE_BOUNCEMISSILE:
861                 case MOVETYPE_PHYSICS:
862                         break;
863                 default:
864                         draggee.velocity = '0 0 0';
865                         break;
866         }
867
868         if((draggee.flags & FL_ITEM) && (vlen(draggee.velocity) < 32))
869         {
870                 draggee.velocity = '0 0 0';
871                 draggee.flags |= FL_ONGROUND; // floating items are FUN
872         }
873 }
874
875 float Drag_IsDraggable(entity draggee)
876 {
877         // TODO add more checks for bad stuff here
878         if(draggee == world)
879                 return FALSE;
880         if(draggee.classname == "func_bobbing")
881                 return FALSE;
882         if(draggee.classname == "door") // FIXME find out why these must be excluded, or work around the problem (trying to drag these causes like 4 fps)
883                 return FALSE;
884         if(draggee.classname == "plat")
885                 return FALSE;
886         if(draggee.classname == "func_button")
887                 return FALSE;
888 //      if(draggee.model == "")
889 //              return FALSE;
890         if(draggee.classname == "spectator")
891                 return FALSE;
892         if(draggee.classname == "observer")
893                 return FALSE;
894         if(draggee.classname == "exteriorweaponentity")
895                 return FALSE;
896         if(draggee.classname == "weaponentity")
897                 return FALSE;
898
899         return TRUE;
900 }
901
902 float Drag_MayChangeAngles(entity draggee)
903 {
904         // TODO add more checks for bad stuff here
905         if(substring(draggee.model, 0, 1) == "*")
906                 return FALSE;
907         return TRUE;
908 }
909
910 void Drag_MoveForward(entity dragger)
911 {
912         dragger.dragdistance += dragger.dragspeed;
913 }
914
915 void Drag_SetSpeed(entity dragger, float s)
916 {
917         dragger.dragspeed = pow(2, s);
918 }
919
920 void Drag_MoveBackward(entity dragger)
921 {
922         dragger.dragdistance = max(0, dragger.dragdistance - dragger.dragspeed);
923 }
924
925 void Drag_Update(entity dragger)
926 {
927         vector curorigin, neworigin, goodvelocity;
928         float f;
929         entity draggee;
930
931         draggee = dragger.dragentity;
932         draggee.flags &~= FL_ONGROUND;
933
934         curorigin = gettaginfo(draggee, 0);
935         curorigin = curorigin + v_forward * dragger.draglocalvector_x + v_right * dragger.draglocalvector_y + v_up * dragger.draglocalvector_z;
936         makevectors(dragger.v_angle);
937         neworigin = dragger.origin + dragger.view_ofs + v_forward * dragger.dragdistance;
938         goodvelocity = (neworigin - curorigin) * (1 / frametime);
939
940         while(draggee.angles_y - dragger.v_angle_y - dragger.draglocalangle > 180)
941                 dragger.draglocalangle += 360;
942         while(draggee.angles_y - dragger.v_angle_y - dragger.draglocalangle <= -180)
943                 dragger.draglocalangle -= 360;
944
945         f = min(frametime * 10, 1);
946         draggee.velocity = draggee.velocity * (1 - f) + goodvelocity * f;
947
948         if(Drag_MayChangeAngles(draggee))
949                 draggee.angles_y = draggee.angles_y * (1 - f) + (dragger.v_angle_y + dragger.draglocalangle) * f;
950
951         draggee.ltime = max(servertime + serverframetime, draggee.ltime); // fixes func_train breakage
952
953         te_lightning1(dragger, dragger.origin + dragger.view_ofs, curorigin);
954 }
955
956 float Drag_CanDrag(entity dragger)
957 {
958         return (dragger.deadflag == DEAD_NO) || (dragger.classname == "player");
959 }
960
961 float Drag_IsDragging(entity dragger)
962 {
963         if(!dragger.dragentity)
964                 return FALSE;
965         if(wasfreed(dragger.dragentity) || dragger.dragentity.draggedby != dragger)
966         {
967                 dragger.dragentity = world;
968                 return FALSE;
969         }
970         if(!Drag_CanDrag(dragger) || !Drag_IsDraggable(dragger.dragentity))
971         {
972                 Drag_Finish(dragger);
973                 return FALSE;
974         }
975         return TRUE;
976 }
977
978 void Drag_MoveDrag(entity from, entity to)
979 {
980         if(from.draggedby)
981         {
982                 to.draggedby = from.draggedby;
983                 to.draggedby.dragentity = to;
984                 from.draggedby = world;
985         }
986 }
987
988 void DragBox_Think()
989 {
990         if(self.aiment && self.enemy)
991         {
992                 self.origin_x = (self.aiment.origin_x + self.enemy.origin_x) * 0.5;
993                 self.origin_y = (self.aiment.origin_y + self.enemy.origin_y) * 0.5;
994                 self.origin_z = (self.aiment.origin_z + self.enemy.origin_z) * 0.5;
995                 self.maxs_x = fabs(self.aiment.origin_x - self.enemy.origin_x) * 0.5;
996                 self.maxs_y = fabs(self.aiment.origin_y - self.enemy.origin_y) * 0.5;
997                 self.maxs_z = fabs(self.aiment.origin_z - self.enemy.origin_z) * 0.5;
998                 self.mins = -1 * self.maxs;
999                 setorigin(self, self.origin); setsize(self, self.mins, self.maxs); // link edict
1000         }
1001
1002         if(self.cnt == -1) // actually race_place -1
1003         {
1004                 // show "10 10" for qualifying spawns
1005                 setmodel(self.killindicator, "models/sprites/10.spr32");
1006                 setmodel(self.killindicator.killindicator, "models/sprites/10.spr32");
1007         }
1008         else if(self.cnt == -2) // actually race_place 0
1009         {
1010                 // show "10 0" for loser spawns
1011                 setmodel(self.killindicator, "models/sprites/10.spr32");
1012                 setmodel(self.killindicator.killindicator, "models/sprites/0.spr32");
1013         }
1014         else
1015         {
1016                 setmodel(self.killindicator, strcat("models/sprites/", ftos(mod(self.cnt, 10)), ".spr32"));
1017                 setmodel(self.killindicator.killindicator, strcat("models/sprites/", ftos(floor(self.cnt / 10)), ".spr32"));
1018         }
1019
1020         self.nextthink = time;
1021 }
1022
1023 #endif