|
|
1.1 ! root 1: // dm_ball.c ! 2: // pmack ! 3: // june 98 ! 4: ! 5: #include "g_local.h" ! 6: ! 7: // defines ! 8: ! 9: #define DBALL_GOAL_TEAM1 0x0001 ! 10: #define DBALL_GOAL_TEAM2 0x0002 ! 11: ! 12: // globals ! 13: ! 14: edict_t *dball_ball_entity = NULL; ! 15: int dball_ball_startpt_count; ! 16: int dball_team1_goalscore; ! 17: int dball_team2_goalscore; ! 18: ! 19: cvar_t *dball_team1_skin; ! 20: cvar_t *dball_team2_skin; ! 21: cvar_t *goallimit; ! 22: ! 23: // prototypes ! 24: ! 25: extern void EndDMLevel (void); ! 26: extern void ClientUserinfoChanged (edict_t *ent, char *userinfo); ! 27: extern void SelectSpawnPoint (edict_t *ent, vec3_t origin, vec3_t angles); ! 28: extern float PlayersRangeFromSpot (edict_t *spot); ! 29: ! 30: void DBall_BallDie (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point); ! 31: void DBall_BallRespawn (edict_t *self); ! 32: ! 33: // ************************** ! 34: // Game rules ! 35: // ************************** ! 36: ! 37: int DBall_CheckDMRules (void) ! 38: { ! 39: if(goallimit && goallimit->value) ! 40: { ! 41: if(dball_team1_goalscore >= goallimit->value) ! 42: gi.bprintf (PRINT_HIGH, "Team 1 Wins.\n"); ! 43: else if(dball_team2_goalscore >= goallimit->value) ! 44: gi.bprintf (PRINT_HIGH, "Team 2 Wins.\n"); ! 45: else ! 46: return 0; ! 47: ! 48: EndDMLevel (); ! 49: return 1; ! 50: } ! 51: ! 52: return 0; ! 53: } ! 54: ! 55: //================== ! 56: //================== ! 57: void DBall_ClientBegin (edict_t *ent) ! 58: { ! 59: int team1, team2, unassigned; ! 60: edict_t *other; ! 61: char *p; ! 62: static char value[512]; ! 63: int j; ! 64: ! 65: team1 = 0; ! 66: team2 = 0; ! 67: unassigned = 0; ! 68: ! 69: for (j = 1; j <= game.maxclients; j++) ! 70: { ! 71: other = &g_edicts[j]; ! 72: if (!other->inuse) ! 73: continue; ! 74: if (!other->client) ! 75: continue; ! 76: if (other == ent) // don't count the new player ! 77: continue; ! 78: ! 79: strcpy(value, Info_ValueForKey (other->client->pers.userinfo, "skin")); ! 80: p = strchr(value, '/'); ! 81: if (p) ! 82: { ! 83: if(!strcmp(dball_team1_skin->string, value)) ! 84: team1++; ! 85: else if(!strcmp(dball_team2_skin->string, value)) ! 86: team2++; ! 87: else ! 88: unassigned++; ! 89: } ! 90: else ! 91: unassigned++; ! 92: } ! 93: ! 94: if(team1 > team2) ! 95: { ! 96: gi.dprintf("assigned to team 2\n"); ! 97: Info_SetValueForKey(ent->client->pers.userinfo, "skin", dball_team2_skin->string); ! 98: } ! 99: else ! 100: { ! 101: gi.dprintf("assigned to team 1\n"); ! 102: Info_SetValueForKey(ent->client->pers.userinfo, "skin", dball_team1_skin->string); ! 103: } ! 104: ! 105: ClientUserinfoChanged(ent, ent->client->pers.userinfo); ! 106: ! 107: if(unassigned) ! 108: gi.dprintf("%d unassigned players present!\n", unassigned); ! 109: } ! 110: ! 111: //================== ! 112: //================== ! 113: void DBall_SelectSpawnPoint (edict_t *ent, vec3_t origin, vec3_t angles) ! 114: { ! 115: edict_t *bestspot; ! 116: float bestdistance, bestplayerdistance; ! 117: edict_t *spot; ! 118: char *spottype; ! 119: char skin[512]; ! 120: ! 121: strcpy(skin, Info_ValueForKey (ent->client->pers.userinfo, "skin")); ! 122: if(!strcmp(dball_team1_skin->string, skin)) ! 123: spottype = "dm_dball_team1_start"; ! 124: else if(!strcmp(dball_team2_skin->string, skin)) ! 125: spottype = "dm_dball_team2_start"; ! 126: else ! 127: spottype = "info_player_deathmatch"; ! 128: ! 129: spot = NULL; ! 130: bestspot = NULL; ! 131: bestdistance = 0; ! 132: while ((spot = G_Find (spot, FOFS(classname), spottype)) != NULL) ! 133: { ! 134: bestplayerdistance = PlayersRangeFromSpot (spot); ! 135: ! 136: if (bestplayerdistance > bestdistance) ! 137: { ! 138: bestspot = spot; ! 139: bestdistance = bestplayerdistance; ! 140: } ! 141: } ! 142: ! 143: if (bestspot) ! 144: { ! 145: VectorCopy (bestspot->s.origin, origin); ! 146: origin[2] += 9; ! 147: VectorCopy (bestspot->s.angles, angles); ! 148: return; ! 149: } ! 150: ! 151: // if we didn't find an appropriate spawnpoint, just ! 152: // call the standard one. ! 153: SelectSpawnPoint(ent, origin, angles); ! 154: } ! 155: ! 156: //================== ! 157: //================== ! 158: void DBall_GameInit (void) ! 159: { ! 160: // we don't want a minimum speed for friction to take effect. ! 161: // this will allow any knockback to move stuff. ! 162: sv_stopspeed->value = 0; ! 163: dball_team1_goalscore = 0; ! 164: dball_team2_goalscore = 0; ! 165: ! 166: dmflags->value = (int)dmflags->value | DF_NO_MINES | DF_NO_NUKES | DF_NO_STACK_DOUBLE | ! 167: DF_NO_FRIENDLY_FIRE | DF_SKINTEAMS; ! 168: ! 169: dball_team1_skin = gi.cvar ("dball_team1_skin", "male/ctf_r", 0); ! 170: dball_team2_skin = gi.cvar ("dball_team2_skin", "male/ctf_b", 0); ! 171: goallimit = gi.cvar ("goallimit", "0", 0); ! 172: } ! 173: ! 174: //================== ! 175: //================== ! 176: void DBall_PostInitSetup (void) ! 177: { ! 178: edict_t *e; ! 179: ! 180: e=NULL; ! 181: // turn teleporter destinations nonsolid. ! 182: while(e = G_Find (e, FOFS(classname), "misc_teleporter_dest")) ! 183: { ! 184: e->solid = SOLID_NOT; ! 185: gi.linkentity (e); ! 186: } ! 187: ! 188: // count the ball start points ! 189: dball_ball_startpt_count = 0; ! 190: e=NULL; ! 191: while(e = G_Find (e, FOFS(classname), "dm_dball_ball_start")) ! 192: { ! 193: dball_ball_startpt_count++; ! 194: } ! 195: ! 196: if(dball_ball_startpt_count == 0) ! 197: gi.dprintf("No Deathball start points!\n"); ! 198: } ! 199: ! 200: //================== ! 201: // DBall_ChangeDamage - half damage between players. full if it involves ! 202: // the ball entity ! 203: //================== ! 204: int DBall_ChangeDamage (edict_t *targ, edict_t *attacker, int damage, int mod) ! 205: { ! 206: // cut player -> ball damage to 1 ! 207: if (targ == dball_ball_entity) ! 208: return 1; ! 209: ! 210: // damage player -> player is halved ! 211: if (attacker != dball_ball_entity) ! 212: return damage / 2; ! 213: ! 214: return damage; ! 215: } ! 216: ! 217: //================== ! 218: //================== ! 219: int DBall_ChangeKnockback (edict_t *targ, edict_t *attacker, int knockback, int mod) ! 220: { ! 221: if(targ != dball_ball_entity) ! 222: return knockback; ! 223: ! 224: if(knockback < 1) ! 225: { ! 226: // FIXME - these don't account for quad/double ! 227: if(mod == MOD_ROCKET) // rocket ! 228: knockback = 70; ! 229: else if(mod == MOD_BFG_EFFECT) // bfg ! 230: knockback = 90; ! 231: else ! 232: gi.dprintf ("zero knockback, mod %d\n", mod); ! 233: } ! 234: else ! 235: { ! 236: // FIXME - change this to an array? ! 237: switch(mod) ! 238: { ! 239: case MOD_BLASTER: ! 240: knockback *= 3; ! 241: break; ! 242: case MOD_SHOTGUN: ! 243: knockback = (knockback * 3) / 8; ! 244: break; ! 245: case MOD_SSHOTGUN: ! 246: knockback = knockback / 3; ! 247: break; ! 248: case MOD_MACHINEGUN: ! 249: knockback = (knockback * 3) / 2; ! 250: break; ! 251: case MOD_HYPERBLASTER: ! 252: knockback *= 4; ! 253: break; ! 254: case MOD_GRENADE: ! 255: case MOD_HANDGRENADE: ! 256: case MOD_PROX: ! 257: case MOD_G_SPLASH: ! 258: case MOD_HG_SPLASH: ! 259: case MOD_HELD_GRENADE: ! 260: case MOD_TRACKER: ! 261: case MOD_DISINTEGRATOR: ! 262: knockback /= 2; ! 263: break; ! 264: case MOD_R_SPLASH: ! 265: knockback = (knockback * 3) / 2; ! 266: break; ! 267: case MOD_RAILGUN: ! 268: case MOD_HEATBEAM: ! 269: knockback /= 3; ! 270: break; ! 271: } ! 272: } ! 273: ! 274: // gi.dprintf("mod: %d knockback: %d\n", mod, knockback); ! 275: return knockback; ! 276: } ! 277: ! 278: // ************************** ! 279: // Goals ! 280: // ************************** ! 281: ! 282: void DBall_GoalTouch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) ! 283: { ! 284: int team_score; ! 285: int scorechange; ! 286: int j; ! 287: char value[512]; ! 288: char *p; ! 289: edict_t *ent; ! 290: ! 291: if(other != dball_ball_entity) ! 292: return; ! 293: ! 294: self->health = self->max_health; ! 295: ! 296: // determine which team scored, and bump the team score ! 297: if(self->spawnflags & DBALL_GOAL_TEAM1) ! 298: { ! 299: dball_team1_goalscore += self->wait; ! 300: team_score = 1; ! 301: } ! 302: else ! 303: { ! 304: dball_team2_goalscore += self->wait; ! 305: team_score = 2; ! 306: } ! 307: ! 308: // bump the score for everyone on the correct team. ! 309: for (j = 1; j <= game.maxclients; j++) ! 310: { ! 311: ent = &g_edicts[j]; ! 312: if (!ent->inuse) ! 313: continue; ! 314: if (!ent->client) ! 315: continue; ! 316: ! 317: if (ent == other->enemy) ! 318: scorechange = self->wait + 5; ! 319: else ! 320: scorechange = self->wait; ! 321: ! 322: strcpy(value, Info_ValueForKey (ent->client->pers.userinfo, "skin")); ! 323: p = strchr(value, '/'); ! 324: if (p) ! 325: { ! 326: if(!strcmp(dball_team1_skin->string, value)) ! 327: { ! 328: if(team_score == 1) ! 329: ent->client->resp.score += scorechange; ! 330: else if(other->enemy == ent) ! 331: ent->client->resp.score -= scorechange; ! 332: } ! 333: else if(!strcmp(dball_team2_skin->string, value)) ! 334: { ! 335: if(team_score == 2) ! 336: ent->client->resp.score += scorechange; ! 337: else if(other->enemy == ent) ! 338: ent->client->resp.score -= scorechange; ! 339: } ! 340: else ! 341: gi.dprintf("unassigned player!!!!\n"); ! 342: } ! 343: } ! 344: ! 345: if(other->enemy) ! 346: gi.dprintf("score for team %d by %s\n", team_score, other->enemy->client->pers.netname); ! 347: else ! 348: gi.dprintf("score for team %d by someone\n", team_score); ! 349: ! 350: DBall_BallDie (other, other->enemy, other->enemy, 0, vec3_origin); ! 351: ! 352: G_UseTargets (self, other); ! 353: } ! 354: ! 355: // ************************** ! 356: // Ball ! 357: // ************************** ! 358: ! 359: edict_t *PickBallStart (edict_t *ent) ! 360: { ! 361: int which, current; ! 362: edict_t *e; ! 363: ! 364: which = ceil(random() * dball_ball_startpt_count); ! 365: e = NULL; ! 366: current = 0; ! 367: ! 368: while(e = G_Find (e, FOFS(classname), "dm_dball_ball_start")) ! 369: { ! 370: current++; ! 371: if(current == which) ! 372: return e; ! 373: } ! 374: ! 375: if(current == 0) ! 376: gi.dprintf("No ball start points found!\n"); ! 377: ! 378: return G_Find(NULL, FOFS(classname), "dm_dball_ball_start"); ! 379: } ! 380: ! 381: //================== ! 382: // DBall_BallTouch - if the ball hit another player, hurt them ! 383: //================== ! 384: void DBall_BallTouch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf) ! 385: { ! 386: vec3_t dir; ! 387: float dot; ! 388: float speed; ! 389: ! 390: if(other->takedamage == DAMAGE_NO) ! 391: return; ! 392: ! 393: // hit a player ! 394: if(other->client) ! 395: { ! 396: if(ent->velocity[0] || ent->velocity[1] || ent->velocity[2]) ! 397: { ! 398: speed = VectorLength(ent->velocity); ! 399: ! 400: VectorSubtract(ent->s.origin, other->s.origin, dir); ! 401: dot = DotProduct(dir, ent->velocity); ! 402: ! 403: if(dot > 0.7) ! 404: { ! 405: T_Damage (other, ent, ent, vec3_origin, ent->s.origin, vec3_origin, ! 406: speed/10, speed/10, 0, MOD_DBALL_CRUSH); ! 407: } ! 408: } ! 409: } ! 410: } ! 411: ! 412: //================== ! 413: // DBall_BallPain ! 414: //================== ! 415: void DBall_BallPain (edict_t *self, edict_t *other, float kick, int damage) ! 416: { ! 417: self->enemy = other; ! 418: self->health = self->max_health; ! 419: // if(other->classname) ! 420: // gi.dprintf("hurt by %s -- %d\n", other->classname, self->health); ! 421: } ! 422: ! 423: void DBall_BallDie (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) ! 424: { ! 425: // do the splash effect ! 426: gi.WriteByte (svc_temp_entity); ! 427: gi.WriteByte (TE_DBALL_GOAL); ! 428: gi.WritePosition (self->s.origin); ! 429: gi.multicast (self->s.origin, MULTICAST_PVS); ! 430: ! 431: VectorClear(self->s.angles); ! 432: VectorClear(self->velocity); ! 433: VectorClear(self->avelocity); ! 434: ! 435: // make it invisible and desolid until respawn time ! 436: self->solid = SOLID_NOT; ! 437: // self->s.modelindex = 0; ! 438: self->think = DBall_BallRespawn; ! 439: self->nextthink = level.time + 2; ! 440: gi.linkentity(self); ! 441: ! 442: } ! 443: ! 444: void DBall_BallRespawn (edict_t *self) ! 445: { ! 446: edict_t *start; ! 447: ! 448: // do the splash effect ! 449: gi.WriteByte (svc_temp_entity); ! 450: gi.WriteByte (TE_DBALL_GOAL); ! 451: gi.WritePosition (self->s.origin); ! 452: gi.multicast (self->s.origin, MULTICAST_PVS); ! 453: ! 454: // move the ball and stop it ! 455: start = PickBallStart(self); ! 456: if(start) ! 457: { ! 458: VectorCopy(start->s.origin, self->s.origin); ! 459: VectorCopy(start->s.origin, self->s.old_origin); ! 460: } ! 461: ! 462: VectorClear(self->s.angles); ! 463: VectorClear(self->velocity); ! 464: VectorClear(self->avelocity); ! 465: ! 466: self->solid = SOLID_BBOX; ! 467: self->s.modelindex = gi.modelindex ("models/objects/dball/tris.md2"); ! 468: self->s.event = EV_PLAYER_TELEPORT; ! 469: self->groundentity = NULL; ! 470: ! 471: // kill anything at the destination ! 472: KillBox (self); ! 473: ! 474: gi.linkentity (self); ! 475: } ! 476: ! 477: // ************************ ! 478: // SPEED CHANGES ! 479: // ************************ ! 480: ! 481: #define DBALL_SPEED_ONEWAY 1 ! 482: ! 483: void DBall_SpeedTouch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf) ! 484: { ! 485: float dot; ! 486: vec3_t vel; ! 487: ! 488: if(other != dball_ball_entity) ! 489: return; ! 490: ! 491: if(self->timestamp >= level.time) ! 492: return; ! 493: ! 494: if(VectorLength(other->velocity) < 1) ! 495: return; ! 496: ! 497: if(self->spawnflags & DBALL_SPEED_ONEWAY) ! 498: { ! 499: VectorCopy (other->velocity, vel); ! 500: VectorNormalize (vel); ! 501: dot = DotProduct (vel, self->movedir); ! 502: if(dot < 0.8) ! 503: return; ! 504: } ! 505: ! 506: self->timestamp = level.time + self->delay; ! 507: VectorScale (other->velocity, self->speed, other->velocity); ! 508: } ! 509: ! 510: // ************************ ! 511: // SPAWN FUNCTIONS ! 512: // ************************ ! 513: ! 514: /*QUAKED dm_dball_ball (1 .5 .5) (-48 -48 -48) (48 48 48) ! 515: Deathball Ball ! 516: */ ! 517: void SP_dm_dball_ball (edict_t *self) ! 518: { ! 519: if(!(deathmatch->value)) ! 520: { ! 521: G_FreeEdict(self); ! 522: return; ! 523: } ! 524: ! 525: if(gamerules && (gamerules->value != RDM_DEATHBALL)) ! 526: { ! 527: G_FreeEdict(self); ! 528: return; ! 529: } ! 530: ! 531: dball_ball_entity = self; ! 532: // VectorCopy (self->s.origin, dball_ball_startpt); ! 533: ! 534: self->s.modelindex = gi.modelindex ("models/objects/dball/tris.md2"); ! 535: VectorSet (self->mins, -32, -32, -32); ! 536: VectorSet (self->maxs, 32, 32, 32); ! 537: self->solid = SOLID_BBOX; ! 538: self->movetype = MOVETYPE_NEWTOSS; ! 539: self->clipmask = MASK_MONSTERSOLID; ! 540: ! 541: self->takedamage = DAMAGE_YES; ! 542: self->mass = 50; ! 543: self->health = 50000; ! 544: self->max_health = 50000; ! 545: self->pain = DBall_BallPain; ! 546: self->die = DBall_BallDie; ! 547: self->touch = DBall_BallTouch; ! 548: ! 549: gi.linkentity (self); ! 550: } ! 551: ! 552: /*QUAKED dm_dball_team1_start (1 .5 .5) (-16 -16 -24) (16 16 32) ! 553: Deathball team 1 start point ! 554: */ ! 555: void SP_dm_dball_team1_start (edict_t *self) ! 556: { ! 557: if (!deathmatch->value) ! 558: { ! 559: G_FreeEdict (self); ! 560: return; ! 561: } ! 562: if(gamerules && (gamerules->value != RDM_DEATHBALL)) ! 563: { ! 564: G_FreeEdict(self); ! 565: return; ! 566: } ! 567: } ! 568: ! 569: /*QUAKED dm_dball_team2_start (1 .5 .5) (-16 -16 -24) (16 16 32) ! 570: Deathball team 2 start point ! 571: */ ! 572: void SP_dm_dball_team2_start (edict_t *self) ! 573: { ! 574: if (!deathmatch->value) ! 575: { ! 576: G_FreeEdict (self); ! 577: return; ! 578: } ! 579: if(gamerules && (gamerules->value != RDM_DEATHBALL)) ! 580: { ! 581: G_FreeEdict(self); ! 582: return; ! 583: } ! 584: } ! 585: ! 586: /*QUAKED dm_dball_ball_start (1 .5 .5) (-48 -48 -48) (48 48 48) ! 587: Deathball ball start point ! 588: */ ! 589: void SP_dm_dball_ball_start (edict_t *self) ! 590: { ! 591: if (!deathmatch->value) ! 592: { ! 593: G_FreeEdict (self); ! 594: return; ! 595: } ! 596: if(gamerules && (gamerules->value != RDM_DEATHBALL)) ! 597: { ! 598: G_FreeEdict(self); ! 599: return; ! 600: } ! 601: } ! 602: ! 603: /*QUAKED dm_dball_speed_change (1 .5 .5) ? ONEWAY ! 604: Deathball ball speed changing field. ! 605: ! 606: speed: multiplier for speed (.5 = half, 2 = double, etc) (default = double) ! 607: angle: used with ONEWAY so speed change is only one way. ! 608: delay: time between speed changes (default: 0.2 sec) ! 609: */ ! 610: void SP_dm_dball_speed_change (edict_t *self) ! 611: { ! 612: if (!deathmatch->value) ! 613: { ! 614: G_FreeEdict (self); ! 615: return; ! 616: } ! 617: if(gamerules && (gamerules->value != RDM_DEATHBALL)) ! 618: { ! 619: G_FreeEdict(self); ! 620: return; ! 621: } ! 622: ! 623: if(!self->speed) ! 624: self->speed = 2; ! 625: ! 626: if(!self->delay) ! 627: self->delay = 0.2; ! 628: ! 629: self->touch = DBall_SpeedTouch; ! 630: self->solid = SOLID_TRIGGER; ! 631: self->movetype = MOVETYPE_NONE; ! 632: self->svflags |= SVF_NOCLIENT; ! 633: ! 634: if (!VectorCompare(self->s.angles, vec3_origin)) ! 635: G_SetMovedir (self->s.angles, self->movedir); ! 636: else ! 637: VectorSet (self->movedir, 1, 0, 0); ! 638: ! 639: gi.setmodel (self, self->model); ! 640: gi.linkentity (self); ! 641: } ! 642: ! 643: /*QUAKED dm_dball_goal (1 .5 .5) ? TEAM1 TEAM2 ! 644: Deathball goal ! 645: ! 646: Team1/Team2 - beneficiary of this goal. when the ball enters this goal, the beneficiary team will score. ! 647: ! 648: "wait": score to be given for this goal (default 10) player gets score+5. ! 649: */ ! 650: void SP_dm_dball_goal (edict_t *self) ! 651: { ! 652: if(!(deathmatch->value)) ! 653: { ! 654: G_FreeEdict(self); ! 655: return; ! 656: } ! 657: ! 658: if(gamerules && (gamerules->value != RDM_DEATHBALL)) ! 659: { ! 660: G_FreeEdict(self); ! 661: return; ! 662: } ! 663: ! 664: if(!self->wait) ! 665: self->wait = 10; ! 666: ! 667: self->touch = DBall_GoalTouch; ! 668: self->solid = SOLID_TRIGGER; ! 669: self->movetype = MOVETYPE_NONE; ! 670: self->svflags |= SVF_NOCLIENT; ! 671: ! 672: if (!VectorCompare(self->s.angles, vec3_origin)) ! 673: G_SetMovedir (self->s.angles, self->movedir); ! 674: ! 675: gi.setmodel (self, self->model); ! 676: gi.linkentity (self); ! 677: ! 678: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.