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