1 2 // prototypes 3 void () W_WeaponFrame; 4 void() W_SetCurrentAmmo; 5 void() player_pain; 6 void() player_stand1; 7 void (vector org) spawn_tfog; 8 void (vector org, entity death_owner) spawn_tdeath; 9 10 float modelindex_eyes, modelindex_player; 11 12 /* 13 ============================================================================= 14 15 LEVEL CHANGING / INTERMISSION 16 17 ============================================================================= 18 */ 19 20 string nextmap; 21 22 float intermission_running; 23 float intermission_exittime; 24 25 /*QUAKED info_intermission (1 0.5 0.5) (-16 -16 -16) (16 16 16) 26 This is the camera point for the intermission. 27 Use mangle instead of angle, so you can set pitch or roll as well as yaw. 'pitch roll yaw' 28 */ 29 void() info_intermission = 30 { 31 self.angles = self.mangle; // so C can get at it 32 }; 33 34 35 36 void() SetChangeParms = 37 { 38 if (self.health <= 0) 39 { 40 SetNewParms (); 41 return; 42 } 43 44 // remove items 45 self.items = self.items - (self.items & 46 (IT_KEY1 | IT_KEY2 | IT_INVISIBILITY | IT_INVULNERABILITY | IT_SUIT | IT_QUAD) ); 47 48 // cap super health 49 if (self.health > 100) 50 self.health = 100; 51 if (self.health < 50) 52 self.health = 50; 53 parm1 = self.items; 54 parm2 = self.health; 55 parm3 = self.armorvalue; 56 if (self.ammo_shells < 25) 57 parm4 = 25; 58 else 59 parm4 = self.ammo_shells; 60 parm5 = self.ammo_nails; 61 parm6 = self.ammo_rockets; 62 parm7 = self.ammo_cells; 63 parm8 = self.weapon; 64 parm9 = self.armortype * 100; 65 }; 66 67 void() SetNewParms = 68 { 69 parm1 = IT_SHOTGUN | IT_AXE; 70 parm2 = 100; 71 parm3 = 0; 72 parm4 = 25; 73 parm5 = 0; 74 parm6 = 0; 75 parm7 = 0; 76 parm8 = 1; 77 parm9 = 0; 78 }; 79 80 void() DecodeLevelParms = 81 { 82 if (serverflags) 83 { 84 if (world.model == "maps/start.bsp") 85 SetNewParms (); // take away all stuff on starting new episode 86 } 87 88 self.items = parm1; 89 self.health = parm2; 90 self.armorvalue = parm3; 91 self.ammo_shells = parm4; 92 self.ammo_nails = parm5; 93 self.ammo_rockets = parm6; 94 self.ammo_cells = parm7; 95 self.weapon = parm8; 96 self.armortype = parm9 * 0.01; 97 }; 98 99 /* 100 ============ 101 FindIntermission 102 103 Returns the entity to view from 104 ============ 105 */ 106 entity() FindIntermission = 107 { 108 local entity spot; 109 local float cyc; 110 111 // look for info_intermission first 112 spot = find (world, classname, "info_intermission"); 113 if (spot) 114 { // pick a random one 115 cyc = random() * 4; 116 while (cyc > 1) 117 { 118 spot = find (spot, classname, "info_intermission"); 119 if (!spot) 120 spot = find (spot, classname, "info_intermission"); 121 cyc = cyc - 1; 122 } 123 return spot; 124 } 125 126 // then look for the start position 127 spot = find (world, classname, "info_player_start"); 128 if (spot) 129 return spot; 130 131 objerror ("FindIntermission: no spot"); 132 }; 133 134 135 void() GotoNextMap = 136 { 137 local string newmap; 138 139 //ZOID: 12-13-96, samelevel is overloaded, only 1 works for same level 140 141 if (cvar("samelevel") == 1) // if samelevel is set, stay on same level 142 changelevel (mapname); 143 else { 144 // configurable map lists, see if the current map exists as a 145 // serverinfo/localinfo var 146 newmap = infokey(world, mapname); 147 if (newmap != "") 148 changelevel (newmap); 149 else 150 changelevel (nextmap); 151 } 152 }; 153 154 155 156 /* 157 ============ 158 IntermissionThink 159 160 When the player presses attack or jump, change to the next level 161 ============ 162 */ 163 void() IntermissionThink = 164 { 165 if (time < intermission_exittime) 166 return; 167 168 if (!self.button0 && !self.button1 && !self.button2) 169 return; 170 171 GotoNextMap (); 172 }; 173 174 /* 175 ============ 176 execute_changelevel 177 178 The global "nextmap" has been set previously. 179 Take the players to the intermission spot 180 ============ 181 */ 182 void() execute_changelevel = 183 { 184 local entity pos; 185 186 intermission_running = 1; 187 188 // enforce a wait time before allowing changelevel 189 intermission_exittime = time + 5; 190 191 pos = FindIntermission (); 192 193 // play intermission music 194 WriteByte (MSG_ALL, SVC_CDTRACK); 195 WriteByte (MSG_ALL, 3); 196 197 WriteByte (MSG_ALL, SVC_INTERMISSION); 198 WriteCoord (MSG_ALL, pos.origin_x); 199 WriteCoord (MSG_ALL, pos.origin_y); 200 WriteCoord (MSG_ALL, pos.origin_z); 201 WriteAngle (MSG_ALL, pos.mangle_x); 202 WriteAngle (MSG_ALL, pos.mangle_y); 203 WriteAngle (MSG_ALL, pos.mangle_z); 204 205 other = find (world, classname, "player"); 206 while (other != world) 207 { 208 other.takedamage = DAMAGE_NO; 209 other.solid = SOLID_NOT; 210 other.movetype = MOVETYPE_NONE; 211 other.modelindex = 0; 212 other = find (other, classname, "player"); 213 } 214 215 }; 216 217 218 void() changelevel_touch = 219 { 220 local entity pos; 221 222 if (other.classname != "player") 223 return; 224 225 // if "noexit" is set, blow up the player trying to leave 226 //ZOID, 12-13-96, noexit isn't supported in QW. Overload samelevel 227 // if ((cvar("noexit") == 1) || ((cvar("noexit") == 2) && (mapname != "start"))) 228 if ((cvar("samelevel") == 2) || ((cvar("samelevel") == 3) && (mapname != "start"))) 229 { 230 T_Damage (other, self, self, 50000); 231 return; 232 } 233 234 bprint (PRINT_HIGH, other.netname); 235 bprint (PRINT_HIGH," exited the level\n"); 236 237 nextmap = self.map; 238 239 SUB_UseTargets (); 240 241 self.touch = SUB_Null; 242 243 // we can't move people right now, because touch functions are called 244 // in the middle of C movement code, so set a think time to do it 245 self.think = execute_changelevel; 246 self.nextthink = time + 0.1; 247 }; 248 249 /*QUAKED trigger_changelevel (0.5 0.5 0.5) ? NO_INTERMISSION 250 When the player touches this, he gets sent to the map listed in the "map" variable. Unless the NO_INTERMISSION flag is set, the view will go to the info_intermission spot and display stats. 251 */ 252 void() trigger_changelevel = 253 { 254 if (!self.map) 255 objerror ("chagnelevel trigger doesn't have map"); 256 257 InitTrigger (); 258 self.touch = changelevel_touch; 259 }; 260 261 262 /* 263 ============================================================================= 264 265 PLAYER GAME EDGE FUNCTIONS 266 267 ============================================================================= 268 */ 269 270 void() set_suicide_frame; 271 272 // called by ClientKill and DeadThink 273 void() respawn = 274 { 275 // make a copy of the dead body for appearances sake 276 CopyToBodyQue (self); 277 // set default spawn parms 278 SetNewParms (); 279 // respawn 280 PutClientInServer (); 281 }; 282 283 284 /* 285 ============ 286 ClientKill 287 288 Player entered the suicide command 289 ============ 290 */ 291 void() ClientKill = 292 { 293 bprint (PRINT_MEDIUM, self.netname); 294 bprint (PRINT_MEDIUM, " suicides\n"); 295 set_suicide_frame (); 296 self.modelindex = modelindex_player; 297 logfrag (self, self); 298 self.frags = self.frags - 2; // extra penalty 299 respawn (); 300 }; 301 302 float(vector v) CheckSpawnPoint = 303 { 304 return FALSE; 305 }; 306 307 /* 308 ============ 309 SelectSpawnPoint 310 311 Returns the entity to spawn at 312 ============ 313 */ 314 entity() SelectSpawnPoint = 315 { 316 local entity spot, newspot, thing; 317 local float numspots, totalspots; 318 local float rnum, pcount; 319 local float rs; 320 local entity spots; 321 322 numspots = 0; 323 totalspots = 0; 324 325 // testinfo_player_start is only found in regioned levels 326 spot = find (world, classname, "testplayerstart"); 327 if (spot) 328 return spot; 329 330 // choose a info_player_deathmatch point 331 332 // ok, find all spots that don't have players nearby 333 334 spots = world; 335 spot = find (world, classname, "info_player_deathmatch"); 336 while (spot) 337 { 338 totalspots = totalspots + 1; 339 340 thing=findradius(spot.origin, 84); 341 pcount=0; 342 while (thing) 343 { 344 if (thing.classname == "player") 345 pcount=pcount + 1; 346 thing=thing.chain; 347 } 348 if (pcount == 0) { 349 spot.goalentity = spots; 350 spots = spot; 351 numspots = numspots + 1; 352 } 353 354 // Get the next spot in the chain 355 spot = find (spot, classname, "info_player_deathmatch"); 356 } 357 totalspots=totalspots - 1; 358 if (!numspots) { 359 // ack, they are all full, just pick one at random 360 // bprint (PRINT_HIGH, "Ackk! All spots are full. Selecting random spawn spot\n"); 361 totalspots = rint((random() * totalspots)); 362 spot = find (world, classname, "info_player_deathmatch"); 363 while (totalspots > 0) { 364 totalspots = totalspots - 1; 365 spot = find (spot, classname, "info_player_deathmatch"); 366 } 367 return spot; 368 } 369 370 // We now have the number of spots available on the map in numspots 371 372 // Generate a random number between 1 and numspots 373 374 numspots = numspots - 1; 375 376 numspots = rint((random() * numspots ) ); 377 378 spot = spots; 379 while (numspots > 0) { 380 spot = spot.goalentity; 381 numspots = numspots - 1; 382 } 383 return spot; 384 385 }; 386 void() DecodeLevelParms; 387 void() PlayerDie; 388 389 /* 390 =========== 391 ValidateUser 392 393 394 ============ 395 */ 396 float(entity e) ValidateUser = 397 { 398 /* 399 local string s; 400 local string userclan; 401 local float rank, rankmin, rankmax; 402 403 // 404 // if the server has set "clan1" and "clan2", then it 405 // is a clan match that will allow only those two clans in 406 // 407 s = serverinfo("clan1"); 408 if (s) 409 { 410 userclan = masterinfo(e,"clan"); 411 if (s == userclan) 412 return true; 413 s = serverinfo("clan2"); 414 if (s == userclan) 415 return true; 416 return false; 417 } 418 419 // 420 // if the server has set "rankmin" and/or "rankmax" then 421 // the users rank must be between those two values 422 // 423 s = masterinfo (e, "rank"); 424 rank = stof (s); 425 426 s = serverinfo("rankmin"); 427 if (s) 428 { 429 rankmin = stof (s); 430 if (rank < rankmin) 431 return false; 432 } 433 s = serverinfo("rankmax"); 434 if (s) 435 { 436 rankmax = stof (s); 437 if (rankmax < rank) 438 return false; 439 } 440 441 return true; 442 */ 443 }; 444 445 446 /* 447 =========== 448 PutClientInServer 449 450 called each time a player enters a new level 451 ============ 452 */ 453 void() PutClientInServer = 454 { 455 local entity spot; 456 local string s; 457 458 self.classname = "player"; 459 self.health = 100; 460 self.takedamage = DAMAGE_AIM; 461 self.solid = SOLID_SLIDEBOX; 462 self.movetype = MOVETYPE_WALK; 463 self.show_hostile = 0; 464 self.max_health = 100; 465 self.flags = FL_CLIENT; 466 self.air_finished = time + 12; 467 self.dmg = 2; // initial water damage 468 self.super_damage_finished = 0; 469 self.radsuit_finished = 0; 470 self.invisible_finished = 0; 471 self.invincible_finished = 0; 472 self.effects = 0; 473 self.invincible_time = 0; 474 475 DecodeLevelParms (); 476 477 W_SetCurrentAmmo (); 478 479 self.attack_finished = time; 480 self.th_pain = player_pain; 481 self.th_die = PlayerDie; 482 483 self.deadflag = DEAD_NO; 484 // paustime is set by teleporters to keep the player from moving a while 485 self.pausetime = 0; 486 487 spot = SelectSpawnPoint (); 488 489 self.origin = spot.origin + '0 0 1'; 490 self.angles = spot.angles; 491 self.fixangle = TRUE; // turn this way immediately 492 493 // oh, this is a hack! 494 setmodel (self, "progs/eyes.mdl"); 495 modelindex_eyes = self.modelindex; 496 497 setmodel (self, "progs/player.mdl"); 498 modelindex_player = self.modelindex; 499 500 setsize (self, VEC_HULL_MIN, VEC_HULL_MAX); 501 502 self.view_ofs = '0 0 22'; 503 504 // Mod - Xian (May.20.97) 505 // Bug where player would have velocity from their last kill 506 507 self.velocity = '0 0 0'; 508 509 player_stand1 (); 510 511 makevectors(self.angles); 512 spawn_tfog (self.origin + v_forward*20); 513 514 spawn_tdeath (self.origin, self); 515 516 // Set Rocket Jump Modifiers 517 if (stof(infokey(world, "rj")) != 0) 518 { 519 rj = stof(infokey(world, "rj")); 520 } 521 522 if (deathmatch == 4) 523 { 524 self.ammo_shells = 0; 525 if (stof(infokey(world, "axe")) == 0) 526 { 527 self.ammo_nails = 255; 528 self.ammo_shells = 255; 529 self.ammo_rockets = 255; 530 self.ammo_cells = 255; 531 self.items = self.items | IT_NAILGUN; 532 self.items = self.items | IT_SUPER_NAILGUN; 533 self.items = self.items | IT_SUPER_SHOTGUN; 534 self.items = self.items | IT_ROCKET_LAUNCHER; 535 // self.items = self.items | IT_GRENADE_LAUNCHER; 536 self.items = self.items | IT_LIGHTNING; 537 } 538 self.items = self.items - (self.items & (IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)) + IT_ARMOR3; 539 self.armorvalue = 200; 540 self.armortype = 0.8; 541 self.health = 250; 542 self.items = self.items | IT_INVULNERABILITY; 543 self.invincible_time = 1; 544 self.invincible_finished = time + 3; 545 } 546 547 if (deathmatch == 5) 548 { 549 self.ammo_nails = 80; 550 self.ammo_shells = 30; 551 self.ammo_rockets = 10; 552 self.ammo_cells = 30; 553 self.items = self.items | IT_NAILGUN; 554 self.items = self.items | IT_SUPER_NAILGUN; 555 self.items = self.items | IT_SUPER_SHOTGUN; 556 self.items = self.items | IT_ROCKET_LAUNCHER; 557 self.items = self.items | IT_GRENADE_LAUNCHER; 558 self.items = self.items | IT_LIGHTNING; 559 self.items = self.items - (self.items & (IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)) + IT_ARMOR3; 560 self.armorvalue = 200; 561 self.armortype = 0.8; 562 self.health = 200; 563 self.items = self.items | IT_INVULNERABILITY; 564 self.invincible_time = 1; 565 self.invincible_finished = time + 3; 566 } 567 568 569 }; 570 571 572 /* 573 ============================================================================= 574 575 QUAKED FUNCTIONS 576 577 ============================================================================= 578 */ 579 580 581 /*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 24) 582 The normal starting point for a level. 583 */ 584 void() info_player_start = 585 { 586 }; 587 588 589 /*QUAKED info_player_start2 (1 0 0) (-16 -16 -24) (16 16 24) 590 Only used on start map for the return point from an episode. 591 */ 592 void() info_player_start2 = 593 { 594 }; 595 596 /*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 24) 597 potential spawning position for deathmatch games 598 */ 599 void() info_player_deathmatch = 600 { 601 }; 602 603 /*QUAKED info_player_coop (1 0 1) (-16 -16 -24) (16 16 24) 604 potential spawning position for coop games 605 */ 606 void() info_player_coop = 607 { 608 }; 609 610 /* 611 =============================================================================== 612 613 RULES 614 615 =============================================================================== 616 */ 617 618 /* 619 go to the next level for deathmatch 620 */ 621 void() NextLevel = 622 { 623 local entity o; 624 local string newmap; 625 626 if (nextmap != "") 627 return; // already done 628 629 if (mapname == "start") 630 { 631 if (!cvar("registered")) 632 { 633 mapname = "e1m1"; 634 } 635 else if (!(serverflags & 1)) 636 { 637 mapname = "e1m1"; 638 serverflags = serverflags | 1; 639 } 640 else if (!(serverflags & 2)) 641 { 642 mapname = "e2m1"; 643 serverflags = serverflags | 2; 644 } 645 else if (!(serverflags & 4)) 646 { 647 mapname = "e3m1"; 648 serverflags = serverflags | 4; 649 } 650 else if (!(serverflags & 8)) 651 { 652 mapname = "e4m1"; 653 serverflags = serverflags - 7; 654 } 655 656 o = spawn(); 657 o.map = mapname; 658 } 659 else 660 { 661 // find a trigger changelevel 662 o = find(world, classname, "trigger_changelevel"); 663 if (!o || mapname == "start") 664 { // go back to same map if no trigger_changelevel 665 o = spawn(); 666 o.map = mapname; 667 } 668 } 669 670 nextmap = o.map; 671 672 if (o.nextthink < time) 673 { 674 o.think = execute_changelevel; 675 o.nextthink = time + 0.1; 676 } 677 }; 678 679 /* 680 ============ 681 CheckRules 682 683 Exit deathmatch games upon conditions 684 ============ 685 */ 686 void() CheckRules = 687 { 688 if (timelimit && time >= timelimit) 689 NextLevel (); 690 691 if (fraglimit && self.frags >= fraglimit) 692 NextLevel (); 693 }; 694 695 //============================================================================ 696 697 void() PlayerDeathThink = 698 { 699 local entity old_self; 700 local float forward; 701 702 if ((self.flags & FL_ONGROUND)) 703 { 704 forward = vlen (self.velocity); 705 forward = forward - 20; 706 if (forward <= 0) 707 self.velocity = '0 0 0'; 708 else 709 self.velocity = forward * normalize(self.velocity); 710 } 711 712 // wait for all buttons released 713 if (self.deadflag == DEAD_DEAD) 714 { 715 if (self.button2 || self.button1 || self.button0) 716 return; 717 self.deadflag = DEAD_RESPAWNABLE; 718 return; 719 } 720 721 // wait for any button down 722 if (!self.button2 && !self.button1 && !self.button0) 723 return; 724 725 self.button0 = 0; 726 self.button1 = 0; 727 self.button2 = 0; 728 respawn(); 729 }; 730 731 732 void() PlayerJump = 733 { 734 local vector start, end; 735 736 if (self.flags & FL_WATERJUMP) 737 return; 738 739 if (self.waterlevel >= 2) 740 { 741 // play swiming sound 742 if (self.swim_flag < time) 743 { 744 self.swim_flag = time + 1; 745 if (random() < 0.5) 746 sound (self, CHAN_BODY, "misc/water1.wav", 1, ATTN_NORM); 747 else 748 sound (self, CHAN_BODY, "misc/water2.wav", 1, ATTN_NORM); 749 } 750 751 return; 752 } 753 754 if (!(self.flags & FL_ONGROUND)) 755 return; 756 757 if ( !(self.flags & FL_JUMPRELEASED) ) 758 return; // don't pogo stick 759 760 self.flags = self.flags - (self.flags & FL_JUMPRELEASED); 761 self.button2 = 0; 762 763 // player jumping sound 764 sound (self, CHAN_BODY, "player/plyrjmp8.wav", 1, ATTN_NORM); 765 }; 766 767 768 /* 769 =========== 770 WaterMove 771 772 ============ 773 */ 774 .float dmgtime; 775 776 void() WaterMove = 777 { 778 //dprint (ftos(self.waterlevel)); 779 if (self.movetype == MOVETYPE_NOCLIP) 780 return; 781 if (self.health < 0) 782 return; 783 784 if (self.waterlevel != 3) 785 { 786 if (self.air_finished < time) 787 sound (self, CHAN_VOICE, "player/gasp2.wav", 1, ATTN_NORM); 788 else if (self.air_finished < time + 9) 789 sound (self, CHAN_VOICE, "player/gasp1.wav", 1, ATTN_NORM); 790 self.air_finished = time + 12; 791 self.dmg = 2; 792 } 793 else if (self.air_finished < time) 794 { // drown! 795 if (self.pain_finished < time) 796 { 797 self.dmg = self.dmg + 2; 798 if (self.dmg > 15) 799 self.dmg = 10; 800 T_Damage (self, world, world, self.dmg); 801 self.pain_finished = time + 1; 802 } 803 } 804 805 if (!self.waterlevel) 806 { 807 if (self.flags & FL_INWATER) 808 { 809 // play leave water sound 810 sound (self, CHAN_BODY, "misc/outwater.wav", 1, ATTN_NORM); 811 self.flags = self.flags - FL_INWATER; 812 } 813 return; 814 } 815 816 if (self.watertype == CONTENT_LAVA) 817 { // do damage 818 if (self.dmgtime < time) 819 { 820 if (self.radsuit_finished > time) 821 self.dmgtime = time + 1; 822 else 823 self.dmgtime = time + 0.2; 824 825 T_Damage (self, world, world, 10*self.waterlevel); 826 } 827 } 828 else if (self.watertype == CONTENT_SLIME) 829 { // do damage 830 if (self.dmgtime < time && self.radsuit_finished < time) 831 { 832 self.dmgtime = time + 1; 833 T_Damage (self, world, world, 4*self.waterlevel); 834 } 835 } 836 837 if ( !(self.flags & FL_INWATER) ) 838 { 839 840 // player enter water sound 841 842 if (self.watertype == CONTENT_LAVA) 843 sound (self, CHAN_BODY, "player/inlava.wav", 1, ATTN_NORM); 844 if (self.watertype == CONTENT_WATER) 845 sound (self, CHAN_BODY, "player/inh2o.wav", 1, ATTN_NORM); 846 if (self.watertype == CONTENT_SLIME) 847 sound (self, CHAN_BODY, "player/slimbrn2.wav", 1, ATTN_NORM); 848 849 self.flags = self.flags + FL_INWATER; 850 self.dmgtime = 0; 851 } 852 }; 853 854 void() CheckWaterJump = 855 { 856 local vector start, end; 857 858 // check for a jump-out-of-water 859 makevectors (self.angles); 860 start = self.origin; 861 start_z = start_z + 8; 862 v_forward_z = 0; 863 normalize(v_forward); 864 end = start + v_forward*24; 865 traceline (start, end, TRUE, self); 866 if (trace_fraction < 1) 867 { // solid at waist 868 start_z = start_z + self.maxs_z - 8; 869 end = start + v_forward*24; 870 self.movedir = trace_plane_normal * -50; 871 traceline (start, end, TRUE, self); 872 if (trace_fraction == 1) 873 { // open at eye level 874 self.flags = self.flags | FL_WATERJUMP; 875 self.velocity_z = 225; 876 self.flags = self.flags - (self.flags & FL_JUMPRELEASED); 877 self.teleport_time = time + 2; // safety net 878 return; 879 } 880 } 881 }; 882 883 /* 884 ================ 885 PlayerPreThink 886 887 Called every frame before physics are run 888 ================ 889 */ 890 void() PlayerPreThink = 891 { 892 local float mspeed, aspeed; 893 local float r; 894 895 if (intermission_running) 896 { 897 IntermissionThink (); // otherwise a button could be missed between 898 return; // the think tics 899 } 900 901 if (self.view_ofs == '0 0 0') 902 return; // intermission or finale 903 904 makevectors (self.v_angle); // is this still used 905 906 self.deathtype = ""; 907 908 CheckRules (); 909 WaterMove (); 910 /* 911 if (self.waterlevel == 2) 912 CheckWaterJump (); 913 */ 914 915 if (self.deadflag >= DEAD_DEAD) 916 { 917 PlayerDeathThink (); 918 return; 919 } 920 921 if (self.deadflag == DEAD_DYING) 922 return; // dying, so do nothing 923 924 if (self.button2) 925 { 926 PlayerJump (); 927 } 928 else 929 self.flags = self.flags | FL_JUMPRELEASED; 930 931 // teleporters can force a non-moving pause time 932 if (time < self.pausetime) 933 self.velocity = '0 0 0'; 934 935 if(time > self.attack_finished && self.currentammo == 0 && self.weapon != IT_AXE) 936 { 937 self.weapon = W_BestWeapon (); 938 W_SetCurrentAmmo (); 939 } 940 }; 941 942 /* 943 ================ 944 CheckPowerups 945 946 Check for turning off powerups 947 ================ 948 */ 949 void() CheckPowerups = 950 { 951 if (self.health <= 0) 952 return; 953 954 // invisibility 955 if (self.invisible_finished) 956 { 957 // sound and screen flash when items starts to run out 958 if (self.invisible_sound < time) 959 { 960 sound (self, CHAN_AUTO, "items/inv3.wav", 0.5, ATTN_IDLE); 961 self.invisible_sound = time + ((random() * 3) + 1); 962 } 963 964 965 if (self.invisible_finished < time + 3) 966 { 967 if (self.invisible_time == 1) 968 { 969 sprint (self, PRINT_HIGH, "Ring of Shadows magic is fading\n"); 970 stuffcmd (self, "bf\n"); 971 sound (self, CHAN_AUTO, "items/inv2.wav", 1, ATTN_NORM); 972 self.invisible_time = time + 1; 973 } 974 975 if (self.invisible_time < time) 976 { 977 self.invisible_time = time + 1; 978 stuffcmd (self, "bf\n"); 979 } 980 } 981 982 if (self.invisible_finished < time) 983 { // just stopped 984 self.items = self.items - IT_INVISIBILITY; 985 self.invisible_finished = 0; 986 self.invisible_time = 0; 987 } 988 989 // use the eyes 990 self.frame = 0; 991 self.modelindex = modelindex_eyes; 992 } 993 else 994 self.modelindex = modelindex_player; // don't use eyes 995 996 // invincibility 997 if (self.invincible_finished) 998 { 999 // sound and screen flash when items starts to run out 1000 if (self.invincible_finished < time + 3) 1001 { 1002 if (self.invincible_time == 1) 1003 { 1004 sprint (self, PRINT_HIGH, "Protection is almost burned out\n"); 1005 stuffcmd (self, "bf\n"); 1006 sound (self, CHAN_AUTO, "items/protect2.wav", 1, ATTN_NORM); 1007 self.invincible_time = time + 1; 1008 } 1009 1010 if (self.invincible_time < time) 1011 { 1012 self.invincible_time = time + 1; 1013 stuffcmd (self, "bf\n"); 1014 } 1015 } 1016 1017 if (self.invincible_finished < time) 1018 { // just stopped 1019 self.items = self.items - IT_INVULNERABILITY; 1020 self.invincible_time = 0; 1021 self.invincible_finished = 0; 1022 } 1023 if (self.invincible_finished > time) 1024 { 1025 self.effects = self.effects | EF_DIMLIGHT; 1026 self.effects = self.effects | EF_RED; 1027 } 1028 else 1029 { 1030 self.effects = self.effects - (self.effects & EF_DIMLIGHT); 1031 self.effects = self.effects - (self.effects & EF_RED); 1032 } 1033 } 1034 1035 // super damage 1036 if (self.super_damage_finished) 1037 { 1038 1039 // sound and screen flash when items starts to run out 1040 1041 if (self.super_damage_finished < time + 3) 1042 { 1043 if (self.super_time == 1) 1044 { 1045 if (deathmatch == 4) 1046 sprint (self, PRINT_HIGH, "OctaPower is wearing off\n"); 1047 else 1048 sprint (self, PRINT_HIGH, "Quad Damage is wearing off\n"); 1049 stuffcmd (self, "bf\n"); 1050 sound (self, CHAN_AUTO, "items/damage2.wav", 1, ATTN_NORM); 1051 self.super_time = time + 1; 1052 } 1053 1054 if (self.super_time < time) 1055 { 1056 self.super_time = time + 1; 1057 stuffcmd (self, "bf\n"); 1058 } 1059 } 1060 1061 if (self.super_damage_finished < time) 1062 { // just stopped 1063 self.items = self.items - IT_QUAD; 1064 if (deathmatch == 4) 1065 { 1066 self.ammo_cells = 255; 1067 self.armorvalue = 1; 1068 self.armortype = 0.8; 1069 self.health = 100; 1070 } 1071 self.super_damage_finished = 0; 1072 self.super_time = 0; 1073 } 1074 if (self.super_damage_finished > time) 1075 { 1076 self.effects = self.effects | EF_DIMLIGHT; 1077 self.effects = self.effects | EF_BLUE; 1078 } 1079 else 1080 { 1081 self.effects = self.effects - (self.effects & EF_DIMLIGHT); 1082 self.effects = self.effects - (self.effects & EF_BLUE); 1083 } 1084 } 1085 1086 // suit 1087 if (self.radsuit_finished) 1088 { 1089 self.air_finished = time + 12; // don't drown 1090 1091 // sound and screen flash when items starts to run out 1092 if (self.radsuit_finished < time + 3) 1093 { 1094 if (self.rad_time == 1) 1095 { 1096 sprint (self, PRINT_HIGH, "Air supply in Biosuit expiring\n"); 1097 stuffcmd (self, "bf\n"); 1098 sound (self, CHAN_AUTO, "items/suit2.wav", 1, ATTN_NORM); 1099 self.rad_time = time + 1; 1100 } 1101 1102 if (self.rad_time < time) 1103 { 1104 self.rad_time = time + 1; 1105 stuffcmd (self, "bf\n"); 1106 } 1107 } 1108 1109 if (self.radsuit_finished < time) 1110 { // just stopped 1111 self.items = self.items - IT_SUIT; 1112 self.rad_time = 0; 1113 self.radsuit_finished = 0; 1114 } 1115 } 1116 1117 }; 1118 1119 1120 /* 1121 ================ 1122 PlayerPostThink 1123 1124 Called every frame after physics are run 1125 ================ 1126 */ 1127 void() PlayerPostThink = 1128 { 1129 local float mspeed, aspeed; 1130 local float r; 1131 1132 //dprint ("post think\n"); 1133 if (self.view_ofs == '0 0 0') 1134 return; // intermission or finale 1135 if (self.deadflag) 1136 return; 1137 1138 // check to see if player landed and play landing sound 1139 if ((self.jump_flag < -300) && (self.flags & FL_ONGROUND) ) 1140 { 1141 if (self.watertype == CONTENT_WATER) 1142 sound (self, CHAN_BODY, "player/h2ojump.wav", 1, ATTN_NORM); 1143 else if (self.jump_flag < -650) 1144 { 1145 self.deathtype = "falling"; 1146 T_Damage (self, world, world, 5); 1147 sound (self, CHAN_VOICE, "player/land2.wav", 1, ATTN_NORM); 1148 } 1149 else 1150 sound (self, CHAN_VOICE, "player/land.wav", 1, ATTN_NORM); 1151 } 1152 1153 self.jump_flag = self.velocity_z; 1154 1155 CheckPowerups (); 1156 1157 W_WeaponFrame (); 1158 1159 }; 1160 1161 1162 /* 1163 =========== 1164 ClientConnect 1165 1166 called when a player connects to a server 1167 ============ 1168 */ 1169 void() ClientConnect = 1170 { 1171 bprint (PRINT_HIGH, self.netname); 1172 bprint (PRINT_HIGH, " entered the game\n"); 1173 1174 // a client connecting during an intermission can cause problems 1175 if (intermission_running) 1176 GotoNextMap (); 1177 }; 1178 1179 1180 /* 1181 =========== 1182 ClientDisconnect 1183 1184 called when a player disconnects from a server 1185 ============ 1186 */ 1187 void() ClientDisconnect = 1188 { 1189 // let everyone else know 1190 bprint (PRINT_HIGH, self.netname); 1191 bprint (PRINT_HIGH, " left the game with "); 1192 bprint (PRINT_HIGH, ftos(self.frags)); 1193 bprint (PRINT_HIGH, " frags\n"); 1194 sound (self, CHAN_BODY, "player/tornoff2.wav", 1, ATTN_NONE); 1195 set_suicide_frame (); 1196 }; 1197 1198 /* 1199 =========== 1200 ClientObituary 1201 1202 called when a player dies 1203 ============ 1204 */ 1205 1206 void(entity targ, entity attacker) ClientObituary = 1207 { 1208 local float rnum; 1209 local string deathstring, deathstring2; 1210 local string s; 1211 local string attackerteam, targteam; 1212 1213 rnum = random(); 1214 //ZOID 12-13-96: self.team doesn't work in QW. Use keys 1215 attackerteam = infokey(attacker, "team"); 1216 targteam = infokey(targ, "team"); 1217 1218 if (targ.classname == "player") 1219 { 1220 1221 if (deathmatch > 3) 1222 { 1223 if (targ.deathtype == "selfwater") 1224 { 1225 bprint (PRINT_MEDIUM, targ.netname); 1226 bprint (PRINT_MEDIUM," electrocutes himself.\n "); 1227 targ.frags = targ.frags - 1; 1228 return; 1229 } 1230 } 1231 1232 if (attacker.classname == "teledeath") 1233 { 1234 bprint (PRINT_MEDIUM,targ.netname); 1235 bprint (PRINT_MEDIUM," was telefragged by "); 1236 bprint (PRINT_MEDIUM,attacker.owner.netname); 1237 bprint (PRINT_MEDIUM,"\n"); 1238 logfrag (attacker.owner, targ); 1239 1240 attacker.owner.frags = attacker.owner.frags + 1; 1241 return; 1242 } 1243 1244 if (attacker.classname == "teledeath2") 1245 { 1246 bprint (PRINT_MEDIUM,"Satan's power deflects "); 1247 bprint (PRINT_MEDIUM,targ.netname); 1248 bprint (PRINT_MEDIUM,"'s telefrag\n"); 1249 1250 targ.frags = targ.frags - 1; 1251 logfrag (targ, targ); 1252 return; 1253 } 1254 1255 // double 666 telefrag (can happen often in deathmatch 4) 1256 if (attacker.classname == "teledeath3") 1257 { 1258 bprint (PRINT_MEDIUM,targ.netname); 1259 bprint (PRINT_MEDIUM," was telefragged by "); 1260 bprint (PRINT_MEDIUM,attacker.owner.netname); 1261 bprint (PRINT_MEDIUM, "'s Satan's power\n"); 1262 targ.frags = targ.frags - 1; 1263 logfrag (targ, targ); 1264 return; 1265 } 1266 1267 1268 if (targ.deathtype == "squish") 1269 { 1270 if (teamplay && targteam == attackerteam && attackerteam != "" && targ != attacker) 1271 { 1272 logfrag (attacker, attacker); 1273 attacker.frags = attacker.frags - 1; 1274 bprint (PRINT_MEDIUM,attacker.netname); 1275 bprint (PRINT_MEDIUM," squished a teammate\n"); 1276 return; 1277 } 1278 else if (attacker.classname == "player" && attacker != targ) 1279 { 1280 bprint (PRINT_MEDIUM, attacker.netname); 1281 bprint (PRINT_MEDIUM," squishes "); 1282 bprint (PRINT_MEDIUM,targ.netname); 1283 bprint (PRINT_MEDIUM,"\n"); 1284 logfrag (attacker, targ); 1285 attacker.frags = attacker.frags + 1; 1286 return; 1287 } 1288 else 1289 { 1290 logfrag (targ, targ); 1291 targ.frags = targ.frags - 1; // killed self 1292 bprint (PRINT_MEDIUM,targ.netname); 1293 bprint (PRINT_MEDIUM," was squished\n"); 1294 return; 1295 } 1296 } 1297 1298 if (attacker.classname == "player") 1299 { 1300 if (targ == attacker) 1301 { 1302 // killed self 1303 logfrag (attacker, attacker); 1304 attacker.frags = attacker.frags - 1; 1305 bprint (PRINT_MEDIUM,targ.netname); 1306 if (targ.deathtype == "grenade") 1307 bprint (PRINT_MEDIUM," tries to put the pin back in\n"); 1308 else if (targ.deathtype == "rocket") 1309 bprint (PRINT_MEDIUM," becomes bored with life\n"); 1310 else if (targ.weapon == 64 && targ.waterlevel > 1) 1311 { 1312 if (targ.watertype == CONTENT_SLIME) 1313 bprint (PRINT_MEDIUM," discharges into the slime\n"); 1314 else if (targ.watertype == CONTENT_LAVA) 1315 bprint (PRINT_MEDIUM," discharges into the lava\n"); 1316 else 1317 bprint (PRINT_MEDIUM," discharges into the water.\n"); 1318 } 1319 else 1320 bprint (PRINT_MEDIUM," becomes bored with life\n"); 1321 return; 1322 } 1323 else if ( (teamplay == 2) && (targteam == attackerteam) && 1324 (attackerteam != "") ) 1325 { 1326 if (rnum < 0.25) 1327 deathstring = " mows down a teammate\n"; 1328 else if (rnum < 0.50) 1329 deathstring = " checks his glasses\n"; 1330 else if (rnum < 0.75) 1331 deathstring = " gets a frag for the other team\n"; 1332 else 1333 deathstring = " loses another friend\n"; 1334 bprint (PRINT_MEDIUM, attacker.netname); 1335 bprint (PRINT_MEDIUM, deathstring); 1336 attacker.frags = attacker.frags - 1; 1337 //ZOID 12-13-96: killing a teammate logs as suicide 1338 logfrag (attacker, attacker); 1339 return; 1340 } 1341 else 1342 { 1343 logfrag (attacker, targ); 1344 attacker.frags = attacker.frags + 1; 1345 1346 rnum = attacker.weapon; 1347 if (targ.deathtype == "nail") 1348 { 1349 deathstring = " was nailed by "; 1350 deathstring2 = "\n"; 1351 } 1352 else if (targ.deathtype == "supernail") 1353 { 1354 deathstring = " was punctured by "; 1355 deathstring2 = "\n"; 1356 } 1357 else if (targ.deathtype == "grenade") 1358 { 1359 deathstring = " eats "; 1360 deathstring2 = "'s pineapple\n"; 1361 if (targ.health < -40) 1362 { 1363 deathstring = " was gibbed by "; 1364 deathstring2 = "'s grenade\n"; 1365 } 1366 } 1367 else if (targ.deathtype == "rocket") 1368 { 1369 if (attacker.super_damage_finished > 0 && targ.health < -40) 1370 { 1371 rnum = random(); 1372 if (rnum < 0.3) 1373 deathstring = " was brutalized by "; 1374 else if (rnum < 0.6) 1375 deathstring = " was smeared by "; 1376 else 1377 { 1378 bprint (PRINT_MEDIUM, attacker.netname); 1379 bprint (PRINT_MEDIUM, " rips "); 1380 bprint (PRINT_MEDIUM, targ.netname); 1381 bprint (PRINT_MEDIUM, " a new one\n"); 1382 return; 1383 } 1384 deathstring2 = "'s quad rocket\n"; 1385 } 1386 else 1387 { 1388 deathstring = " rides "; 1389 deathstring2 = "'s rocket\n"; 1390 if (targ.health < -40) 1391 { 1392 deathstring = " was gibbed by "; 1393 deathstring2 = "'s rocket\n" ; 1394 } 1395 } 1396 } 1397 else if (rnum == IT_AXE) 1398 { 1399 deathstring = " was ax-murdered by "; 1400 deathstring2 = "\n"; 1401 } 1402 else if (rnum == IT_SHOTGUN) 1403 { 1404 deathstring = " chewed on "; 1405 deathstring2 = "'s boomstick\n"; 1406 } 1407 else if (rnum == IT_SUPER_SHOTGUN) 1408 { 1409 deathstring = " ate 2 loads of "; 1410 deathstring2 = "'s buckshot\n"; 1411 } 1412 else if (rnum == IT_LIGHTNING) 1413 { 1414 deathstring = " accepts "; 1415 if (attacker.waterlevel > 1) 1416 deathstring2 = "'s discharge\n"; 1417 else 1418 deathstring2 = "'s shaft\n"; 1419 } 1420 bprint (PRINT_MEDIUM,targ.netname); 1421 bprint (PRINT_MEDIUM,deathstring); 1422 bprint (PRINT_MEDIUM,attacker.netname); 1423 bprint (PRINT_MEDIUM,deathstring2); 1424 } 1425 return; 1426 } 1427 else 1428 { 1429 logfrag (targ, targ); 1430 targ.frags = targ.frags - 1; // killed self 1431 rnum = targ.watertype; 1432 1433 bprint (PRINT_MEDIUM,targ.netname); 1434 if (rnum == -3) 1435 { 1436 if (random() < 0.5) 1437 bprint (PRINT_MEDIUM," sleeps with the fishes\n"); 1438 else 1439 bprint (PRINT_MEDIUM," sucks it down\n"); 1440 return; 1441 } 1442 else if (rnum == -4) 1443 { 1444 if (random() < 0.5) 1445 bprint (PRINT_MEDIUM," gulped a load of slime\n"); 1446 else 1447 bprint (PRINT_MEDIUM," can't exist on slime alone\n"); 1448 return; 1449 } 1450 else if (rnum == -5) 1451 { 1452 if (targ.health < -15) 1453 { 1454 bprint (PRINT_MEDIUM," burst into flames\n"); 1455 return; 1456 } 1457 if (random() < 0.5) 1458 bprint (PRINT_MEDIUM," turned into hot slag\n"); 1459 else 1460 bprint (PRINT_MEDIUM," visits the Volcano God\n"); 1461 return; 1462 } 1463 1464 if (attacker.classname == "explo_box") 1465 { 1466 bprint (PRINT_MEDIUM," blew up\n"); 1467 return; 1468 } 1469 if (targ.deathtype == "falling") 1470 { 1471 bprint (PRINT_MEDIUM," fell to his death\n"); 1472 return; 1473 } 1474 if (targ.deathtype == "nail" || targ.deathtype == "supernail") 1475 { 1476 bprint (PRINT_MEDIUM," was spiked\n"); 1477 return; 1478 } 1479 if (targ.deathtype == "laser") 1480 { 1481 bprint (PRINT_MEDIUM," was zapped\n"); 1482 return; 1483 } 1484 if (attacker.classname == "fireball") 1485 { 1486 bprint (PRINT_MEDIUM," ate a lavaball\n"); 1487 return; 1488 } 1489 if (attacker.classname == "trigger_changelevel") 1490 { 1491 bprint (PRINT_MEDIUM," tried to leave\n"); 1492 return; 1493 } 1494 1495 bprint (PRINT_MEDIUM," died\n"); 1496 } 1497 } 1498 }; 1499