|
|
1.1 ! root 1: ! 2: #include "g_local.h" ! 3: ! 4: typedef struct ! 5: { ! 6: char *name; ! 7: void (*spawn)(edict_t *ent); ! 8: } spawn_t; ! 9: ! 10: ! 11: void SP_item_health (edict_t *self); ! 12: void SP_item_health_small (edict_t *self); ! 13: void SP_item_health_large (edict_t *self); ! 14: void SP_item_health_mega (edict_t *self); ! 15: ! 16: void SP_info_player_start (edict_t *ent); ! 17: void SP_info_player_deathmatch (edict_t *ent); ! 18: void SP_info_player_coop (edict_t *ent); ! 19: void SP_info_player_intermission (edict_t *ent); ! 20: ! 21: void SP_func_plat (edict_t *ent); ! 22: void SP_func_rotating (edict_t *ent); ! 23: void SP_func_button (edict_t *ent); ! 24: void SP_func_door (edict_t *ent); ! 25: void SP_func_door_secret (edict_t *ent); ! 26: void SP_func_door_rotating (edict_t *ent); ! 27: void SP_func_water (edict_t *ent); ! 28: void SP_func_train (edict_t *ent); ! 29: void SP_func_conveyor (edict_t *self); ! 30: void SP_func_wall (edict_t *self); ! 31: void SP_func_object (edict_t *self); ! 32: void SP_func_explosive (edict_t *self); ! 33: void SP_func_timer (edict_t *self); ! 34: void SP_func_areaportal (edict_t *ent); ! 35: void SP_func_clock (edict_t *ent); ! 36: void SP_func_killbox (edict_t *ent); ! 37: ! 38: void SP_trigger_always (edict_t *ent); ! 39: void SP_trigger_once (edict_t *ent); ! 40: void SP_trigger_multiple (edict_t *ent); ! 41: void SP_trigger_relay (edict_t *ent); ! 42: void SP_trigger_push (edict_t *ent); ! 43: void SP_trigger_hurt (edict_t *ent); ! 44: void SP_trigger_key (edict_t *ent); ! 45: void SP_trigger_counter (edict_t *ent); ! 46: void SP_trigger_elevator (edict_t *ent); ! 47: void SP_trigger_gravity (edict_t *ent); ! 48: void SP_trigger_monsterjump (edict_t *ent); ! 49: ! 50: void SP_target_temp_entity (edict_t *ent); ! 51: void SP_target_speaker (edict_t *ent); ! 52: void SP_target_explosion (edict_t *ent); ! 53: void SP_target_changelevel (edict_t *ent); ! 54: void SP_target_secret (edict_t *ent); ! 55: void SP_target_goal (edict_t *ent); ! 56: void SP_target_splash (edict_t *ent); ! 57: void SP_target_spawner (edict_t *ent); ! 58: void SP_target_blaster (edict_t *ent); ! 59: void SP_target_crosslevel_trigger (edict_t *ent); ! 60: void SP_target_crosslevel_target (edict_t *ent); ! 61: void SP_target_laser (edict_t *self); ! 62: void SP_target_help (edict_t *ent); ! 63: void SP_target_actor (edict_t *ent); ! 64: void SP_target_lightramp (edict_t *self); ! 65: void SP_target_earthquake (edict_t *ent); ! 66: void SP_target_character (edict_t *ent); ! 67: void SP_target_string (edict_t *ent); ! 68: ! 69: void SP_worldspawn (edict_t *ent); ! 70: void SP_viewthing (edict_t *ent); ! 71: ! 72: void SP_light (edict_t *self); ! 73: void SP_light_mine1 (edict_t *ent); ! 74: void SP_light_mine2 (edict_t *ent); ! 75: void SP_info_null (edict_t *self); ! 76: void SP_info_notnull (edict_t *self); ! 77: void SP_path_corner (edict_t *self); ! 78: void SP_point_combat (edict_t *self); ! 79: ! 80: void SP_misc_explobox (edict_t *self); ! 81: void SP_misc_banner (edict_t *self); ! 82: void SP_misc_satellite_dish (edict_t *self); ! 83: void SP_misc_actor (edict_t *self); ! 84: void SP_misc_gib_arm (edict_t *self); ! 85: void SP_misc_gib_leg (edict_t *self); ! 86: void SP_misc_gib_head (edict_t *self); ! 87: void SP_misc_insane (edict_t *self); ! 88: void SP_misc_deadsoldier (edict_t *self); ! 89: void SP_misc_viper (edict_t *self); ! 90: void SP_misc_viper_bomb (edict_t *self); ! 91: void SP_misc_bigviper (edict_t *self); ! 92: void SP_misc_strogg_ship (edict_t *self); ! 93: void SP_misc_teleporter (edict_t *self); ! 94: void SP_misc_teleporter_dest (edict_t *self); ! 95: void SP_misc_blackhole (edict_t *self); ! 96: void SP_misc_eastertank (edict_t *self); ! 97: void SP_misc_easterchick (edict_t *self); ! 98: void SP_misc_easterchick2 (edict_t *self); ! 99: ! 100: void SP_monster_berserk (edict_t *self); ! 101: void SP_monster_gladiator (edict_t *self); ! 102: void SP_monster_gunner (edict_t *self); ! 103: void SP_monster_infantry (edict_t *self); ! 104: void SP_monster_soldier_light (edict_t *self); ! 105: void SP_monster_soldier (edict_t *self); ! 106: void SP_monster_soldier_ss (edict_t *self); ! 107: void SP_monster_tank (edict_t *self); ! 108: void SP_monster_medic (edict_t *self); ! 109: void SP_monster_flipper (edict_t *self); ! 110: void SP_monster_chick (edict_t *self); ! 111: void SP_monster_parasite (edict_t *self); ! 112: void SP_monster_flyer (edict_t *self); ! 113: void SP_monster_brain (edict_t *self); ! 114: void SP_monster_floater (edict_t *self); ! 115: void SP_monster_hover (edict_t *self); ! 116: void SP_monster_mutant (edict_t *self); ! 117: void SP_monster_supertank (edict_t *self); ! 118: void SP_monster_boss2 (edict_t *self); ! 119: void SP_monster_jorg (edict_t *self); ! 120: void SP_monster_boss3_stand (edict_t *self); ! 121: ! 122: void SP_monster_commander_body (edict_t *self); ! 123: ! 124: void SP_turret_breach (edict_t *self); ! 125: void SP_turret_base (edict_t *self); ! 126: void SP_turret_driver (edict_t *self); ! 127: ! 128: //=========== ! 129: //ROGUE ! 130: void SP_func_plat2 (edict_t *ent); ! 131: void SP_func_door_secret2(edict_t *ent); ! 132: void SP_func_force_wall(edict_t *ent); ! 133: void SP_info_player_coop_lava (edict_t *self); ! 134: void SP_info_teleport_destination (edict_t *self); ! 135: void SP_trigger_teleport (edict_t *self); ! 136: void SP_trigger_disguise (edict_t *self); ! 137: void SP_monster_stalker (edict_t *self); ! 138: void SP_monster_turret (edict_t *self); ! 139: void SP_target_steam (edict_t *self); ! 140: void SP_target_anger (edict_t *self); ! 141: void SP_target_killplayers (edict_t *self); ! 142: // PMM - still experimental! ! 143: void SP_target_blacklight (edict_t *self); ! 144: void SP_target_orb (edict_t *self); ! 145: // pmm ! 146: //void SP_target_spawn (edict_t *self); ! 147: void SP_hint_path (edict_t *self); ! 148: void SP_monster_carrier (edict_t *self); ! 149: void SP_monster_widow (edict_t *self); ! 150: void SP_monster_widow2 (edict_t *self); ! 151: void SP_dm_tag_token (edict_t *self); ! 152: void SP_dm_dball_goal (edict_t *self); ! 153: void SP_dm_dball_ball (edict_t *self); ! 154: void SP_dm_dball_team1_start (edict_t *self); ! 155: void SP_dm_dball_team2_start (edict_t *self); ! 156: void SP_dm_dball_ball_start (edict_t *self); ! 157: void SP_dm_dball_speed_change (edict_t *self); ! 158: void SP_monster_kamikaze (edict_t *self); ! 159: //void SP_monster_chick2 (edict_t *self); ! 160: void SP_turret_invisible_brain (edict_t *self); ! 161: void SP_xatrix_item (edict_t *self); ! 162: void SP_misc_nuke_core (edict_t *self); ! 163: //ROGUE ! 164: //=========== ! 165: ! 166: spawn_t spawns[] = { ! 167: {"item_health", SP_item_health}, ! 168: {"item_health_small", SP_item_health_small}, ! 169: {"item_health_large", SP_item_health_large}, ! 170: {"item_health_mega", SP_item_health_mega}, ! 171: ! 172: {"info_player_start", SP_info_player_start}, ! 173: {"info_player_deathmatch", SP_info_player_deathmatch}, ! 174: {"info_player_coop", SP_info_player_coop}, ! 175: {"info_player_intermission", SP_info_player_intermission}, ! 176: ! 177: {"func_plat", SP_func_plat}, ! 178: {"func_button", SP_func_button}, ! 179: {"func_door", SP_func_door}, ! 180: {"func_door_secret", SP_func_door_secret}, ! 181: {"func_door_rotating", SP_func_door_rotating}, ! 182: {"func_rotating", SP_func_rotating}, ! 183: {"func_train", SP_func_train}, ! 184: {"func_water", SP_func_water}, ! 185: {"func_conveyor", SP_func_conveyor}, ! 186: {"func_areaportal", SP_func_areaportal}, ! 187: {"func_clock", SP_func_clock}, ! 188: {"func_wall", SP_func_wall}, ! 189: {"func_object", SP_func_object}, ! 190: {"func_timer", SP_func_timer}, ! 191: {"func_explosive", SP_func_explosive}, ! 192: {"func_killbox", SP_func_killbox}, ! 193: ! 194: {"trigger_always", SP_trigger_always}, ! 195: {"trigger_once", SP_trigger_once}, ! 196: {"trigger_multiple", SP_trigger_multiple}, ! 197: {"trigger_relay", SP_trigger_relay}, ! 198: {"trigger_push", SP_trigger_push}, ! 199: {"trigger_hurt", SP_trigger_hurt}, ! 200: {"trigger_key", SP_trigger_key}, ! 201: {"trigger_counter", SP_trigger_counter}, ! 202: {"trigger_elevator", SP_trigger_elevator}, ! 203: {"trigger_gravity", SP_trigger_gravity}, ! 204: {"trigger_monsterjump", SP_trigger_monsterjump}, ! 205: ! 206: {"target_temp_entity", SP_target_temp_entity}, ! 207: {"target_speaker", SP_target_speaker}, ! 208: {"target_explosion", SP_target_explosion}, ! 209: {"target_changelevel", SP_target_changelevel}, ! 210: {"target_secret", SP_target_secret}, ! 211: {"target_goal", SP_target_goal}, ! 212: {"target_splash", SP_target_splash}, ! 213: {"target_spawner", SP_target_spawner}, ! 214: {"target_blaster", SP_target_blaster}, ! 215: {"target_crosslevel_trigger", SP_target_crosslevel_trigger}, ! 216: {"target_crosslevel_target", SP_target_crosslevel_target}, ! 217: {"target_laser", SP_target_laser}, ! 218: {"target_help", SP_target_help}, ! 219: {"target_actor", SP_target_actor}, ! 220: {"target_lightramp", SP_target_lightramp}, ! 221: {"target_earthquake", SP_target_earthquake}, ! 222: {"target_character", SP_target_character}, ! 223: {"target_string", SP_target_string}, ! 224: ! 225: {"worldspawn", SP_worldspawn}, ! 226: {"viewthing", SP_viewthing}, ! 227: ! 228: {"light", SP_light}, ! 229: {"light_mine1", SP_light_mine1}, ! 230: {"light_mine2", SP_light_mine2}, ! 231: {"info_null", SP_info_null}, ! 232: {"func_group", SP_info_null}, ! 233: {"info_notnull", SP_info_notnull}, ! 234: {"path_corner", SP_path_corner}, ! 235: {"point_combat", SP_point_combat}, ! 236: ! 237: {"misc_explobox", SP_misc_explobox}, ! 238: {"misc_banner", SP_misc_banner}, ! 239: {"misc_satellite_dish", SP_misc_satellite_dish}, ! 240: {"misc_actor", SP_misc_actor}, ! 241: {"misc_gib_arm", SP_misc_gib_arm}, ! 242: {"misc_gib_leg", SP_misc_gib_leg}, ! 243: {"misc_gib_head", SP_misc_gib_head}, ! 244: {"misc_insane", SP_misc_insane}, ! 245: {"misc_deadsoldier", SP_misc_deadsoldier}, ! 246: {"misc_viper", SP_misc_viper}, ! 247: {"misc_viper_bomb", SP_misc_viper_bomb}, ! 248: {"misc_bigviper", SP_misc_bigviper}, ! 249: {"misc_strogg_ship", SP_misc_strogg_ship}, ! 250: {"misc_teleporter", SP_misc_teleporter}, ! 251: {"misc_teleporter_dest", SP_misc_teleporter_dest}, ! 252: {"misc_blackhole", SP_misc_blackhole}, ! 253: {"misc_eastertank", SP_misc_eastertank}, ! 254: {"misc_easterchick", SP_misc_easterchick}, ! 255: {"misc_easterchick2", SP_misc_easterchick2}, ! 256: ! 257: {"monster_berserk", SP_monster_berserk}, ! 258: {"monster_gladiator", SP_monster_gladiator}, ! 259: {"monster_gunner", SP_monster_gunner}, ! 260: {"monster_infantry", SP_monster_infantry}, ! 261: {"monster_soldier_light", SP_monster_soldier_light}, ! 262: {"monster_soldier", SP_monster_soldier}, ! 263: {"monster_soldier_ss", SP_monster_soldier_ss}, ! 264: {"monster_tank", SP_monster_tank}, ! 265: {"monster_tank_commander", SP_monster_tank}, ! 266: {"monster_medic", SP_monster_medic}, ! 267: {"monster_flipper", SP_monster_flipper}, ! 268: {"monster_chick", SP_monster_chick}, ! 269: {"monster_parasite", SP_monster_parasite}, ! 270: {"monster_flyer", SP_monster_flyer}, ! 271: {"monster_brain", SP_monster_brain}, ! 272: {"monster_floater", SP_monster_floater}, ! 273: {"monster_hover", SP_monster_hover}, ! 274: {"monster_mutant", SP_monster_mutant}, ! 275: {"monster_supertank", SP_monster_supertank}, ! 276: {"monster_boss2", SP_monster_boss2}, ! 277: {"monster_boss3_stand", SP_monster_boss3_stand}, ! 278: {"monster_jorg", SP_monster_jorg}, ! 279: ! 280: {"monster_commander_body", SP_monster_commander_body}, ! 281: ! 282: {"turret_breach", SP_turret_breach}, ! 283: {"turret_base", SP_turret_base}, ! 284: {"turret_driver", SP_turret_driver}, ! 285: ! 286: //============== ! 287: //ROGUE ! 288: {"func_plat2", SP_func_plat2}, ! 289: {"func_door_secret2", SP_func_door_secret2}, ! 290: {"func_force_wall", SP_func_force_wall}, ! 291: {"trigger_teleport", SP_trigger_teleport}, ! 292: {"trigger_disguise", SP_trigger_disguise}, ! 293: {"info_teleport_destination", SP_info_teleport_destination}, ! 294: {"info_player_coop_lava", SP_info_player_coop_lava}, ! 295: {"monster_stalker", SP_monster_stalker}, ! 296: {"monster_turret", SP_monster_turret}, ! 297: {"target_steam", SP_target_steam}, ! 298: {"target_anger", SP_target_anger}, ! 299: // {"target_spawn", SP_target_spawn}, ! 300: {"target_killplayers", SP_target_killplayers}, ! 301: // PMM - experiment ! 302: {"target_blacklight", SP_target_blacklight}, ! 303: {"target_orb", SP_target_orb}, ! 304: // pmm ! 305: {"monster_daedalus", SP_monster_hover}, ! 306: {"hint_path", SP_hint_path}, ! 307: {"monster_carrier", SP_monster_carrier}, ! 308: {"monster_widow", SP_monster_widow}, ! 309: {"monster_widow2", SP_monster_widow2}, ! 310: {"monster_medic_commander", SP_monster_medic}, ! 311: {"dm_tag_token", SP_dm_tag_token}, ! 312: {"dm_dball_goal", SP_dm_dball_goal}, ! 313: {"dm_dball_ball", SP_dm_dball_ball}, ! 314: {"dm_dball_team1_start", SP_dm_dball_team1_start}, ! 315: {"dm_dball_team2_start", SP_dm_dball_team2_start}, ! 316: {"dm_dball_ball_start", SP_dm_dball_ball_start}, ! 317: {"dm_dball_speed_change", SP_dm_dball_speed_change}, ! 318: {"monster_kamikaze", SP_monster_kamikaze}, ! 319: // {"monster_chick2", SP_monster_chick2}, ! 320: {"turret_invisible_brain", SP_turret_invisible_brain}, ! 321: {"misc_nuke_core", SP_misc_nuke_core}, ! 322: ! 323: {"ammo_magslug", SP_xatrix_item}, ! 324: {"ammo_trap", SP_xatrix_item}, ! 325: {"item_quadfire", SP_xatrix_item}, ! 326: {"weapon_boomer", SP_xatrix_item}, ! 327: {"weapon_phalanx", SP_xatrix_item}, ! 328: //ROGUE ! 329: //============== ! 330: ! 331: {NULL, NULL} ! 332: }; ! 333: ! 334: /* ! 335: =============== ! 336: ED_CallSpawn ! 337: ! 338: Finds the spawn function for the entity and calls it ! 339: =============== ! 340: */ ! 341: void ED_CallSpawn (edict_t *ent) ! 342: { ! 343: spawn_t *s; ! 344: gitem_t *item; ! 345: int i; ! 346: ! 347: if (!ent->classname) ! 348: { ! 349: gi.dprintf ("ED_CallSpawn: NULL classname\n"); ! 350: return; ! 351: } ! 352: ! 353: //PGM - do this before calling the spawn function so it can be overridden. ! 354: #ifdef ROGUE_GRAVITY ! 355: ent->gravityVector[0] = 0.0; ! 356: ent->gravityVector[1] = 0.0; ! 357: ent->gravityVector[2] = -1.0; ! 358: #endif ! 359: //PGM ! 360: ! 361: // FIXME - PMM nailgun hack ! 362: if (!strcmp(ent->classname, "weapon_nailgun")) ! 363: ent->classname = (FindItem("ETF Rifle"))->classname; ! 364: if (!strcmp(ent->classname, "ammo_nails")) ! 365: ent->classname = (FindItem("Flechettes"))->classname; ! 366: if (!strcmp(ent->classname, "weapon_heatbeam")) ! 367: ent->classname = (FindItem("Plasma Beam"))->classname; ! 368: // pmm ! 369: ! 370: // check item spawn functions ! 371: for (i=0,item=itemlist ; i<game.num_items ; i++,item++) ! 372: { ! 373: if (!item->classname) ! 374: continue; ! 375: if (!strcmp(item->classname, ent->classname)) ! 376: { // found it ! 377: SpawnItem (ent, item); ! 378: return; ! 379: } ! 380: } ! 381: ! 382: // check normal spawn functions ! 383: for (s=spawns ; s->name ; s++) ! 384: { ! 385: if (!strcmp(s->name, ent->classname)) ! 386: { // found it ! 387: s->spawn (ent); ! 388: return; ! 389: } ! 390: } ! 391: gi.dprintf ("%s doesn't have a spawn function\n", ent->classname); ! 392: } ! 393: ! 394: /* ! 395: ============= ! 396: ED_NewString ! 397: ============= ! 398: */ ! 399: char *ED_NewString (char *string) ! 400: { ! 401: char *newb, *new_p; ! 402: int i,l; ! 403: ! 404: l = strlen(string) + 1; ! 405: ! 406: newb = gi.TagMalloc (l, TAG_LEVEL); ! 407: ! 408: new_p = newb; ! 409: ! 410: for (i=0 ; i< l ; i++) ! 411: { ! 412: if (string[i] == '\\' && i < l-1) ! 413: { ! 414: i++; ! 415: if (string[i] == 'n') ! 416: *new_p++ = '\n'; ! 417: else ! 418: *new_p++ = '\\'; ! 419: } ! 420: else ! 421: *new_p++ = string[i]; ! 422: } ! 423: ! 424: return newb; ! 425: } ! 426: ! 427: ! 428: ! 429: ! 430: /* ! 431: =============== ! 432: ED_ParseField ! 433: ! 434: Takes a key/value pair and sets the binary values ! 435: in an edict ! 436: =============== ! 437: */ ! 438: void ED_ParseField (char *key, char *value, edict_t *ent) ! 439: { ! 440: field_t *f; ! 441: byte *b; ! 442: float v; ! 443: vec3_t vec; ! 444: ! 445: for (f=fields ; f->name ; f++) ! 446: { ! 447: if (!(f->flags & FFL_NOSPAWN) && !Q_stricmp(f->name, key)) ! 448: { // found it ! 449: if (f->flags & FFL_SPAWNTEMP) ! 450: b = (byte *)&st; ! 451: else ! 452: b = (byte *)ent; ! 453: ! 454: switch (f->type) ! 455: { ! 456: case F_LSTRING: ! 457: *(char **)(b+f->ofs) = ED_NewString (value); ! 458: break; ! 459: case F_VECTOR: ! 460: sscanf (value, "%f %f %f", &vec[0], &vec[1], &vec[2]); ! 461: ((float *)(b+f->ofs))[0] = vec[0]; ! 462: ((float *)(b+f->ofs))[1] = vec[1]; ! 463: ((float *)(b+f->ofs))[2] = vec[2]; ! 464: break; ! 465: case F_INT: ! 466: *(int *)(b+f->ofs) = atoi(value); ! 467: break; ! 468: case F_FLOAT: ! 469: *(float *)(b+f->ofs) = atof(value); ! 470: break; ! 471: case F_ANGLEHACK: ! 472: v = atof(value); ! 473: ((float *)(b+f->ofs))[0] = 0; ! 474: ((float *)(b+f->ofs))[1] = v; ! 475: ((float *)(b+f->ofs))[2] = 0; ! 476: break; ! 477: case F_IGNORE: ! 478: break; ! 479: } ! 480: return; ! 481: } ! 482: } ! 483: gi.dprintf ("%s is not a field\n", key); ! 484: } ! 485: ! 486: /* ! 487: ==================== ! 488: ED_ParseEdict ! 489: ! 490: Parses an edict out of the given string, returning the new position ! 491: ed should be a properly initialized empty edict. ! 492: ==================== ! 493: */ ! 494: char *ED_ParseEdict (char *data, edict_t *ent) ! 495: { ! 496: qboolean init; ! 497: char keyname[256]; ! 498: char *com_token; ! 499: ! 500: init = false; ! 501: memset (&st, 0, sizeof(st)); ! 502: ! 503: // go through all the dictionary pairs ! 504: while (1) ! 505: { ! 506: // parse key ! 507: com_token = COM_Parse (&data); ! 508: if (com_token[0] == '}') ! 509: break; ! 510: if (!data) ! 511: gi.error ("ED_ParseEntity: EOF without closing brace"); ! 512: ! 513: strncpy (keyname, com_token, sizeof(keyname)-1); ! 514: ! 515: // parse value ! 516: com_token = COM_Parse (&data); ! 517: if (!data) ! 518: gi.error ("ED_ParseEntity: EOF without closing brace"); ! 519: ! 520: if (com_token[0] == '}') ! 521: gi.error ("ED_ParseEntity: closing brace without data"); ! 522: ! 523: init = true; ! 524: ! 525: // keynames with a leading underscore are used for utility comments, ! 526: // and are immediately discarded by quake ! 527: if (keyname[0] == '_') ! 528: continue; ! 529: ! 530: ED_ParseField (keyname, com_token, ent); ! 531: } ! 532: ! 533: if (!init) ! 534: memset (ent, 0, sizeof(*ent)); ! 535: ! 536: return data; ! 537: } ! 538: ! 539: ! 540: /* ! 541: ================ ! 542: G_FindTeams ! 543: ! 544: Chain together all entities with a matching team field. ! 545: ! 546: All but the first will have the FL_TEAMSLAVE flag set. ! 547: All but the last will have the teamchain field set to the next one ! 548: ================ ! 549: */ ! 550: void G_FixTeams (void) ! 551: { ! 552: edict_t *e, *e2, *chain; ! 553: int i, j; ! 554: int c, c2; ! 555: ! 556: c = 0; ! 557: c2 = 0; ! 558: for (i=1, e=g_edicts+i ; i < globals.num_edicts ; i++,e++) ! 559: { ! 560: if (!e->inuse) ! 561: continue; ! 562: if (!e->team) ! 563: continue; ! 564: if (!strcmp(e->classname, "func_train")) ! 565: { ! 566: if(e->flags & FL_TEAMSLAVE) ! 567: { ! 568: chain = e; ! 569: e->teammaster = e; ! 570: e->teamchain = NULL; ! 571: e->flags &= ~FL_TEAMSLAVE; ! 572: c++; ! 573: c2++; ! 574: for (j=1, e2=g_edicts+j ; j < globals.num_edicts ; j++,e2++) ! 575: { ! 576: if (e2 == e) ! 577: continue; ! 578: if (!e2->inuse) ! 579: continue; ! 580: if (!e2->team) ! 581: continue; ! 582: if (!strcmp(e->team, e2->team)) ! 583: { ! 584: c2++; ! 585: chain->teamchain = e2; ! 586: e2->teammaster = e; ! 587: e2->teamchain = NULL; ! 588: chain = e2; ! 589: e2->flags |= FL_TEAMSLAVE; ! 590: e2->movetype = MOVETYPE_PUSH; ! 591: e2->speed = e->speed; ! 592: } ! 593: } ! 594: } ! 595: } ! 596: } ! 597: gi.dprintf ("%i teams repaired\n", c); ! 598: } ! 599: ! 600: void G_FindTeams (void) ! 601: { ! 602: edict_t *e, *e2, *chain; ! 603: int i, j; ! 604: int c, c2; ! 605: ! 606: c = 0; ! 607: c2 = 0; ! 608: for (i=1, e=g_edicts+i ; i < globals.num_edicts ; i++,e++) ! 609: { ! 610: if (!e->inuse) ! 611: continue; ! 612: if (!e->team) ! 613: continue; ! 614: if (e->flags & FL_TEAMSLAVE) ! 615: continue; ! 616: chain = e; ! 617: e->teammaster = e; ! 618: c++; ! 619: c2++; ! 620: for (j=i+1, e2=e+1 ; j < globals.num_edicts ; j++,e2++) ! 621: { ! 622: if (!e2->inuse) ! 623: continue; ! 624: if (!e2->team) ! 625: continue; ! 626: if (e2->flags & FL_TEAMSLAVE) ! 627: continue; ! 628: if (!strcmp(e->team, e2->team)) ! 629: { ! 630: c2++; ! 631: chain->teamchain = e2; ! 632: e2->teammaster = e; ! 633: chain = e2; ! 634: e2->flags |= FL_TEAMSLAVE; ! 635: } ! 636: } ! 637: } ! 638: ! 639: G_FixTeams(); ! 640: ! 641: gi.dprintf ("%i teams with %i entities\n", c, c2); ! 642: } ! 643: ! 644: /* ! 645: ============== ! 646: SpawnEntities ! 647: ! 648: Creates a server's entity / program execution context by ! 649: parsing textual entity definitions out of an ent file. ! 650: ============== ! 651: */ ! 652: void SpawnEntities (char *mapname, char *entities, char *spawnpoint) ! 653: { ! 654: edict_t *ent; ! 655: int inhibit; ! 656: char *com_token; ! 657: int i; ! 658: float skill_level; ! 659: ! 660: skill_level = floor (skill->value); ! 661: if (skill_level < 0) ! 662: skill_level = 0; ! 663: if (skill_level > 3) ! 664: skill_level = 3; ! 665: if (skill->value != skill_level) ! 666: gi.cvar_forceset("skill", va("%f", skill_level)); ! 667: ! 668: SaveClientData (); ! 669: ! 670: gi.FreeTags (TAG_LEVEL); ! 671: ! 672: memset (&level, 0, sizeof(level)); ! 673: memset (g_edicts, 0, game.maxentities * sizeof (g_edicts[0])); ! 674: ! 675: strncpy (level.mapname, mapname, sizeof(level.mapname)-1); ! 676: strncpy (game.spawnpoint, spawnpoint, sizeof(game.spawnpoint)-1); ! 677: ! 678: // set client fields on player ents ! 679: for (i=0 ; i<game.maxclients ; i++) ! 680: g_edicts[i+1].client = game.clients + i; ! 681: ! 682: ent = NULL; ! 683: inhibit = 0; ! 684: ! 685: // parse ents ! 686: while (1) ! 687: { ! 688: // parse the opening brace ! 689: com_token = COM_Parse (&entities); ! 690: if (!entities) ! 691: break; ! 692: if (com_token[0] != '{') ! 693: gi.error ("ED_LoadFromFile: found %s when expecting {",com_token); ! 694: ! 695: if (!ent) ! 696: ent = g_edicts; ! 697: else ! 698: ent = G_Spawn (); ! 699: entities = ED_ParseEdict (entities, ent); ! 700: ! 701: // yet another map hack ! 702: if (!Q_stricmp(level.mapname, "command") && !Q_stricmp(ent->classname, "trigger_once") && !Q_stricmp(ent->model, "*27")) ! 703: ent->spawnflags &= ~SPAWNFLAG_NOT_HARD; ! 704: ! 705: // remove things (except the world) from different skill levels or deathmatch ! 706: if (ent != g_edicts) ! 707: { ! 708: if (deathmatch->value) ! 709: { ! 710: if ( ent->spawnflags & SPAWNFLAG_NOT_DEATHMATCH ) ! 711: { ! 712: G_FreeEdict (ent); ! 713: inhibit++; ! 714: continue; ! 715: } ! 716: } ! 717: else if(coop->value) ! 718: { ! 719: if (ent->spawnflags & SPAWNFLAG_NOT_COOP) ! 720: { ! 721: G_FreeEdict (ent); ! 722: inhibit++; ! 723: continue; ! 724: } ! 725: ! 726: // stuff marked !easy & !med & !hard are coop only, all levels ! 727: if(!((ent->spawnflags & SPAWNFLAG_NOT_EASY) && ! 728: (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM) && ! 729: (ent->spawnflags & SPAWNFLAG_NOT_HARD)) ) ! 730: { ! 731: if( ((skill->value == 0) && (ent->spawnflags & SPAWNFLAG_NOT_EASY)) || ! 732: ((skill->value == 1) && (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM)) || ! 733: (((skill->value == 2) || (skill->value == 3)) && (ent->spawnflags & SPAWNFLAG_NOT_HARD)) ! 734: ) ! 735: { ! 736: G_FreeEdict (ent); ! 737: inhibit++; ! 738: continue; ! 739: } ! 740: } ! 741: } ! 742: else ! 743: { ! 744: if ( /*((coop->value) && (ent->spawnflags & SPAWNFLAG_NOT_COOP)) || */ ! 745: ((skill->value == 0) && (ent->spawnflags & SPAWNFLAG_NOT_EASY)) || ! 746: ((skill->value == 1) && (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM)) || ! 747: (((skill->value == 2) || (skill->value == 3)) && (ent->spawnflags & SPAWNFLAG_NOT_HARD)) ! 748: ) ! 749: { ! 750: G_FreeEdict (ent); ! 751: inhibit++; ! 752: continue; ! 753: } ! 754: } ! 755: ! 756: ent->spawnflags &= ~(SPAWNFLAG_NOT_EASY|SPAWNFLAG_NOT_MEDIUM|SPAWNFLAG_NOT_HARD|SPAWNFLAG_NOT_COOP|SPAWNFLAG_NOT_DEATHMATCH); ! 757: } ! 758: ! 759: //PGM - do this before calling the spawn function so it can be overridden. ! 760: #ifdef ROGUE_GRAVITY ! 761: ent->gravityVector[0] = 0.0; ! 762: ent->gravityVector[1] = 0.0; ! 763: ent->gravityVector[2] = -1.0; ! 764: #endif ! 765: //PGM ! 766: ED_CallSpawn (ent); ! 767: ! 768: ent->s.renderfx |= RF_IR_VISIBLE; //PGM ! 769: } ! 770: ! 771: gi.dprintf ("%i entities inhibited\n", inhibit); ! 772: ! 773: #ifdef DEBUG ! 774: i = 1; ! 775: ent = EDICT_NUM(i); ! 776: while (i < globals.num_edicts) { ! 777: if (ent->inuse != 0 || ent->inuse != 1) ! 778: Com_DPrintf("Invalid entity %d\n", i); ! 779: i++, ent++; ! 780: } ! 781: #endif ! 782: ! 783: G_FindTeams (); ! 784: ! 785: PlayerTrail_Init (); ! 786: ! 787: //ROGUE ! 788: if(deathmatch->value) ! 789: { ! 790: if(randomrespawn && randomrespawn->value) ! 791: PrecacheForRandomRespawn(); ! 792: } ! 793: else ! 794: { ! 795: InitHintPaths(); // if there aren't hintpaths on this map, enable quick aborts ! 796: } ! 797: //ROGUE ! 798: ! 799: // ROGUE -- allow dm games to do init stuff right before game starts. ! 800: if(deathmatch->value && gamerules && gamerules->value) ! 801: { ! 802: if(DMGame.PostInitSetup) ! 803: DMGame.PostInitSetup (); ! 804: } ! 805: // ROGUE ! 806: } ! 807: ! 808: ! 809: //=================================================================== ! 810: ! 811: #if 0 ! 812: // cursor positioning ! 813: xl <value> ! 814: xr <value> ! 815: yb <value> ! 816: yt <value> ! 817: xv <value> ! 818: yv <value> ! 819: ! 820: // drawing ! 821: statpic <name> ! 822: pic <stat> ! 823: num <fieldwidth> <stat> ! 824: string <stat> ! 825: ! 826: // control ! 827: if <stat> ! 828: ifeq <stat> <value> ! 829: ifbit <stat> <value> ! 830: endif ! 831: ! 832: #endif ! 833: ! 834: char *single_statusbar = ! 835: "yb -24 " ! 836: ! 837: // health ! 838: "xv 0 " ! 839: "hnum " ! 840: "xv 50 " ! 841: "pic 0 " ! 842: ! 843: // ammo ! 844: "if 2 " ! 845: " xv 100 " ! 846: " anum " ! 847: " xv 150 " ! 848: " pic 2 " ! 849: "endif " ! 850: ! 851: // armor ! 852: "if 4 " ! 853: " xv 200 " ! 854: " rnum " ! 855: " xv 250 " ! 856: " pic 4 " ! 857: "endif " ! 858: ! 859: // selected item ! 860: "if 6 " ! 861: " xv 296 " ! 862: " pic 6 " ! 863: "endif " ! 864: ! 865: "yb -50 " ! 866: ! 867: // picked up item ! 868: "if 7 " ! 869: " xv 0 " ! 870: " pic 7 " ! 871: " xv 26 " ! 872: " yb -42 " ! 873: " stat_string 8 " ! 874: " yb -50 " ! 875: "endif " ! 876: ! 877: // timer ! 878: "if 9 " ! 879: " xv 262 " ! 880: " num 2 10 " ! 881: " xv 296 " ! 882: " pic 9 " ! 883: "endif " ! 884: ! 885: // help / weapon icon ! 886: "if 11 " ! 887: " xv 148 " ! 888: " pic 11 " ! 889: "endif " ! 890: ; ! 891: ! 892: char *dm_statusbar = ! 893: "yb -24 " ! 894: ! 895: // health ! 896: "xv 0 " ! 897: "hnum " ! 898: "xv 50 " ! 899: "pic 0 " ! 900: ! 901: // ammo ! 902: "if 2 " ! 903: " xv 100 " ! 904: " anum " ! 905: " xv 150 " ! 906: " pic 2 " ! 907: "endif " ! 908: ! 909: // armor ! 910: "if 4 " ! 911: " xv 200 " ! 912: " rnum " ! 913: " xv 250 " ! 914: " pic 4 " ! 915: "endif " ! 916: ! 917: // selected item ! 918: "if 6 " ! 919: " xv 296 " ! 920: " pic 6 " ! 921: "endif " ! 922: ! 923: "yb -50 " ! 924: ! 925: // picked up item ! 926: "if 7 " ! 927: " xv 0 " ! 928: " pic 7 " ! 929: " xv 26 " ! 930: " yb -42 " ! 931: " stat_string 8 " ! 932: " yb -50 " ! 933: "endif " ! 934: ! 935: // timer ! 936: "if 9 " ! 937: " xv 246 " ! 938: " num 2 10 " ! 939: " xv 296 " ! 940: " pic 9 " ! 941: "endif " ! 942: ! 943: // help / weapon icon ! 944: "if 11 " ! 945: " xv 148 " ! 946: " pic 11 " ! 947: "endif " ! 948: ! 949: // frags ! 950: "xr -50 " ! 951: "yt 2 " ! 952: "num 3 14 " ! 953: ! 954: // spectator ! 955: "if 17 " ! 956: "xv 0 " ! 957: "yb -58 " ! 958: "string2 \"SPECTATOR MODE\" " ! 959: "endif " ! 960: ! 961: // chase camera ! 962: "if 16 " ! 963: "xv 0 " ! 964: "yb -68 " ! 965: "string \"Chasing\" " ! 966: "xv 64 " ! 967: "stat_string 16 " ! 968: "endif " ! 969: ; ! 970: ! 971: ! 972: /*QUAKED worldspawn (0 0 0) ? ! 973: ! 974: Only used for the world. ! 975: "sky" environment map name ! 976: "skyaxis" vector axis for rotating sky ! 977: "skyrotate" speed of rotation in degrees/second ! 978: "sounds" music cd track number ! 979: "gravity" 800 is default gravity ! 980: "message" text to print at user logon ! 981: */ ! 982: void SP_worldspawn (edict_t *ent) ! 983: { ! 984: ent->movetype = MOVETYPE_PUSH; ! 985: ent->solid = SOLID_BSP; ! 986: ent->inuse = true; // since the world doesn't use G_Spawn() ! 987: ent->s.modelindex = 1; // world model is always index 1 ! 988: ! 989: //--------------- ! 990: ! 991: // reserve some spots for dead player bodies for coop / deathmatch ! 992: InitBodyQue (); ! 993: ! 994: // set configstrings for items ! 995: SetItemNames (); ! 996: ! 997: if (st.nextmap) ! 998: strcpy (level.nextmap, st.nextmap); ! 999: ! 1000: // make some data visible to the server ! 1001: ! 1002: if (ent->message && ent->message[0]) ! 1003: { ! 1004: gi.configstring (CS_NAME, ent->message); ! 1005: strncpy (level.level_name, ent->message, sizeof(level.level_name)); ! 1006: } ! 1007: else ! 1008: strncpy (level.level_name, level.mapname, sizeof(level.level_name)); ! 1009: ! 1010: if (st.sky && st.sky[0]) ! 1011: gi.configstring (CS_SKY, st.sky); ! 1012: else ! 1013: gi.configstring (CS_SKY, "unit1_"); ! 1014: ! 1015: gi.configstring (CS_SKYROTATE, va("%f", st.skyrotate) ); ! 1016: ! 1017: gi.configstring (CS_SKYAXIS, va("%f %f %f", ! 1018: st.skyaxis[0], st.skyaxis[1], st.skyaxis[2]) ); ! 1019: ! 1020: gi.configstring (CS_CDTRACK, va("%i", ent->sounds) ); ! 1021: ! 1022: gi.configstring (CS_MAXCLIENTS, va("%i", (int)(maxclients->value) ) ); ! 1023: ! 1024: // status bar program ! 1025: if (deathmatch->value) ! 1026: gi.configstring (CS_STATUSBAR, dm_statusbar); ! 1027: else ! 1028: gi.configstring (CS_STATUSBAR, single_statusbar); ! 1029: ! 1030: //--------------- ! 1031: ! 1032: ! 1033: // help icon for statusbar ! 1034: gi.imageindex ("i_help"); ! 1035: level.pic_health = gi.imageindex ("i_health"); ! 1036: gi.imageindex ("help"); ! 1037: gi.imageindex ("field_3"); ! 1038: ! 1039: if (!st.gravity) ! 1040: gi.cvar_set("sv_gravity", "800"); ! 1041: else ! 1042: gi.cvar_set("sv_gravity", st.gravity); ! 1043: ! 1044: snd_fry = gi.soundindex ("player/fry.wav"); // standing in lava / slime ! 1045: ! 1046: PrecacheItem (FindItem ("Blaster")); ! 1047: ! 1048: gi.soundindex ("player/lava1.wav"); ! 1049: gi.soundindex ("player/lava2.wav"); ! 1050: ! 1051: gi.soundindex ("misc/pc_up.wav"); ! 1052: gi.soundindex ("misc/talk1.wav"); ! 1053: ! 1054: gi.soundindex ("misc/udeath.wav"); ! 1055: ! 1056: // gibs ! 1057: gi.soundindex ("items/respawn1.wav"); ! 1058: ! 1059: // sexed sounds ! 1060: gi.soundindex ("*death1.wav"); ! 1061: gi.soundindex ("*death2.wav"); ! 1062: gi.soundindex ("*death3.wav"); ! 1063: gi.soundindex ("*death4.wav"); ! 1064: gi.soundindex ("*fall1.wav"); ! 1065: gi.soundindex ("*fall2.wav"); ! 1066: gi.soundindex ("*gurp1.wav"); // drowning damage ! 1067: gi.soundindex ("*gurp2.wav"); ! 1068: gi.soundindex ("*jump1.wav"); // player jump ! 1069: gi.soundindex ("*pain25_1.wav"); ! 1070: gi.soundindex ("*pain25_2.wav"); ! 1071: gi.soundindex ("*pain50_1.wav"); ! 1072: gi.soundindex ("*pain50_2.wav"); ! 1073: gi.soundindex ("*pain75_1.wav"); ! 1074: gi.soundindex ("*pain75_2.wav"); ! 1075: gi.soundindex ("*pain100_1.wav"); ! 1076: gi.soundindex ("*pain100_2.wav"); ! 1077: ! 1078: // sexed models ! 1079: // THIS ORDER MUST MATCH THE DEFINES IN g_local.h ! 1080: // you can add more, max 19 (pete change) ! 1081: // these models are only loaded in coop or deathmatch. not singleplayer. ! 1082: if (coop->value || deathmatch->value) ! 1083: { ! 1084: gi.modelindex ("#w_blaster.md2"); ! 1085: gi.modelindex ("#w_shotgun.md2"); ! 1086: gi.modelindex ("#w_sshotgun.md2"); ! 1087: gi.modelindex ("#w_machinegun.md2"); ! 1088: gi.modelindex ("#w_chaingun.md2"); ! 1089: gi.modelindex ("#a_grenades.md2"); ! 1090: gi.modelindex ("#w_glauncher.md2"); ! 1091: gi.modelindex ("#w_rlauncher.md2"); ! 1092: gi.modelindex ("#w_hyperblaster.md2"); ! 1093: gi.modelindex ("#w_railgun.md2"); ! 1094: gi.modelindex ("#w_bfg.md2"); ! 1095: ! 1096: gi.modelindex ("#w_disrupt.md2"); // PGM ! 1097: gi.modelindex ("#w_etfrifle.md2"); // PGM ! 1098: gi.modelindex ("#w_plasma.md2"); // PGM ! 1099: gi.modelindex ("#w_plauncher.md2"); // PGM ! 1100: gi.modelindex ("#w_chainfist.md2"); // PGM ! 1101: } ! 1102: ! 1103: //------------------- ! 1104: ! 1105: gi.soundindex ("player/gasp1.wav"); // gasping for air ! 1106: gi.soundindex ("player/gasp2.wav"); // head breaking surface, not gasping ! 1107: ! 1108: gi.soundindex ("player/watr_in.wav"); // feet hitting water ! 1109: gi.soundindex ("player/watr_out.wav"); // feet leaving water ! 1110: ! 1111: gi.soundindex ("player/watr_un.wav"); // head going underwater ! 1112: ! 1113: gi.soundindex ("player/u_breath1.wav"); ! 1114: gi.soundindex ("player/u_breath2.wav"); ! 1115: ! 1116: gi.soundindex ("items/pkup.wav"); // bonus item pickup ! 1117: gi.soundindex ("world/land.wav"); // landing thud ! 1118: gi.soundindex ("misc/h2ohit1.wav"); // landing splash ! 1119: ! 1120: gi.soundindex ("items/damage.wav"); ! 1121: gi.soundindex ("items/protect.wav"); ! 1122: gi.soundindex ("items/protect4.wav"); ! 1123: gi.soundindex ("weapons/noammo.wav"); ! 1124: ! 1125: gi.soundindex ("infantry/inflies1.wav"); ! 1126: ! 1127: sm_meat_index = gi.modelindex ("models/objects/gibs/sm_meat/tris.md2"); ! 1128: gi.modelindex ("models/objects/gibs/arm/tris.md2"); ! 1129: gi.modelindex ("models/objects/gibs/bone/tris.md2"); ! 1130: gi.modelindex ("models/objects/gibs/bone2/tris.md2"); ! 1131: gi.modelindex ("models/objects/gibs/chest/tris.md2"); ! 1132: gi.modelindex ("models/objects/gibs/skull/tris.md2"); ! 1133: gi.modelindex ("models/objects/gibs/head2/tris.md2"); ! 1134: ! 1135: // ! 1136: // Setup light animation tables. 'a' is total darkness, 'z' is doublebright. ! 1137: // ! 1138: ! 1139: // 0 normal ! 1140: gi.configstring(CS_LIGHTS+0, "m"); ! 1141: ! 1142: // 1 FLICKER (first variety) ! 1143: gi.configstring(CS_LIGHTS+1, "mmnmmommommnonmmonqnmmo"); ! 1144: ! 1145: // 2 SLOW STRONG PULSE ! 1146: gi.configstring(CS_LIGHTS+2, "abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba"); ! 1147: ! 1148: // 3 CANDLE (first variety) ! 1149: gi.configstring(CS_LIGHTS+3, "mmmmmaaaaammmmmaaaaaabcdefgabcdefg"); ! 1150: ! 1151: // 4 FAST STROBE ! 1152: gi.configstring(CS_LIGHTS+4, "mamamamamama"); ! 1153: ! 1154: // 5 GENTLE PULSE 1 ! 1155: gi.configstring(CS_LIGHTS+5,"jklmnopqrstuvwxyzyxwvutsrqponmlkj"); ! 1156: ! 1157: // 6 FLICKER (second variety) ! 1158: gi.configstring(CS_LIGHTS+6, "nmonqnmomnmomomno"); ! 1159: ! 1160: // 7 CANDLE (second variety) ! 1161: gi.configstring(CS_LIGHTS+7, "mmmaaaabcdefgmmmmaaaammmaamm"); ! 1162: ! 1163: // 8 CANDLE (third variety) ! 1164: gi.configstring(CS_LIGHTS+8, "mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa"); ! 1165: ! 1166: // 9 SLOW STROBE (fourth variety) ! 1167: gi.configstring(CS_LIGHTS+9, "aaaaaaaazzzzzzzz"); ! 1168: ! 1169: // 10 FLUORESCENT FLICKER ! 1170: gi.configstring(CS_LIGHTS+10, "mmamammmmammamamaaamammma"); ! 1171: ! 1172: // 11 SLOW PULSE NOT FADE TO BLACK ! 1173: gi.configstring(CS_LIGHTS+11, "abcdefghijklmnopqrrqponmlkjihgfedcba"); ! 1174: ! 1175: // styles 32-62 are assigned by the light program for switchable lights ! 1176: ! 1177: // 63 testing ! 1178: gi.configstring(CS_LIGHTS+63, "a"); ! 1179: } ! 1180: ! 1181: // ! 1182: //ROGUE ! 1183: // ! 1184: ! 1185: // ! 1186: // Monster spawning code ! 1187: // ! 1188: // Used by the carrier, the medic_commander, and the black widow ! 1189: // ! 1190: // The sequence to create a flying monster is: ! 1191: // ! 1192: // FindSpawnPoint - tries to find suitable spot to spawn the monster in ! 1193: // CreateFlyMonster - this verifies the point as good and creates the monster ! 1194: ! 1195: // To create a ground walking monster: ! 1196: // ! 1197: // FindSpawnPoint - same thing ! 1198: // CreateGroundMonster - this checks the volume and makes sure the floor under the volume is suitable ! 1199: // ! 1200: ! 1201: // FIXME - for the black widow, if we want the stalkers coming in on the roof, we'll have to tweak some things ! 1202: ! 1203: // ! 1204: // CreateMonster ! 1205: // ! 1206: edict_t *CreateMonster(vec3_t origin, vec3_t angles, char *classname) ! 1207: { ! 1208: edict_t *newEnt; ! 1209: ! 1210: newEnt = G_Spawn(); ! 1211: ! 1212: VectorCopy(origin, newEnt->s.origin); ! 1213: VectorCopy(angles, newEnt->s.angles); ! 1214: newEnt->classname = ED_NewString (classname); ! 1215: newEnt->monsterinfo.aiflags |= AI_DO_NOT_COUNT; ! 1216: ! 1217: VectorSet(newEnt->gravityVector, 0, 0, -1); ! 1218: ED_CallSpawn(newEnt); ! 1219: newEnt->s.renderfx |= RF_IR_VISIBLE; ! 1220: ! 1221: return newEnt; ! 1222: } ! 1223: ! 1224: edict_t *CreateFlyMonster (vec3_t origin, vec3_t angles, vec3_t mins, vec3_t maxs, char *classname) ! 1225: { ! 1226: if (!mins || !maxs || VectorCompare (mins, vec3_origin) || VectorCompare (maxs, vec3_origin)) ! 1227: { ! 1228: DetermineBBox (classname, mins, maxs); ! 1229: } ! 1230: ! 1231: if (!CheckSpawnPoint(origin, mins, maxs)) ! 1232: return NULL; ! 1233: ! 1234: return (CreateMonster (origin, angles, classname)); ! 1235: } ! 1236: ! 1237: // This is just a wrapper for CreateMonster that looks down height # of CMUs and sees if there ! 1238: // are bad things down there or not ! 1239: // ! 1240: // this is from m_move.c ! 1241: #define STEPSIZE 18 ! 1242: ! 1243: edict_t *CreateGroundMonster (vec3_t origin, vec3_t angles, vec3_t entMins, vec3_t entMaxs, char *classname, int height) ! 1244: { ! 1245: // trace_t tr; ! 1246: edict_t *newEnt; ! 1247: // vec3_t start, stop; ! 1248: // int failure = 0; ! 1249: // vec3_t mins, maxs; ! 1250: // int x, y; ! 1251: // float mid, bottom; ! 1252: vec3_t mins, maxs; ! 1253: ! 1254: // if they don't provide us a bounding box, figure it out ! 1255: if (!entMins || !entMaxs || VectorCompare (entMins, vec3_origin) || VectorCompare (entMaxs, vec3_origin)) ! 1256: { ! 1257: DetermineBBox (classname, mins, maxs); ! 1258: } ! 1259: else ! 1260: { ! 1261: VectorCopy (entMins, mins); ! 1262: VectorCopy (entMaxs, maxs); ! 1263: } ! 1264: ! 1265: // check the ground to make sure it's there, it's relatively flat, and it's not toxic ! 1266: if (!CheckGroundSpawnPoint(origin, mins, maxs, height, -1)) ! 1267: return NULL; ! 1268: ! 1269: newEnt = CreateMonster (origin, angles, classname); ! 1270: if (!newEnt) ! 1271: return NULL; ! 1272: ! 1273: return newEnt; ! 1274: } ! 1275: ! 1276: ! 1277: // FindSpawnPoint ! 1278: // PMM - this is used by the medic commander (possibly by the carrier) to find a good spawn point ! 1279: // if the startpoint is bad, try above the startpoint for a bit ! 1280: ! 1281: qboolean FindSpawnPoint (vec3_t startpoint, vec3_t mins, vec3_t maxs, vec3_t spawnpoint, float maxMoveUp) ! 1282: { ! 1283: trace_t tr; ! 1284: float height; ! 1285: vec3_t top; ! 1286: ! 1287: height = maxs[2] - mins[2]; ! 1288: ! 1289: tr = gi.trace (startpoint, mins, maxs, startpoint, NULL, MASK_MONSTERSOLID|CONTENTS_PLAYERCLIP); ! 1290: if((tr.startsolid || tr.allsolid) || (tr.ent != world)) ! 1291: { ! 1292: // if ( ((tr.ent->svflags & SVF_MONSTER) && (tr.ent->health <= 0)) || ! 1293: // (tr.ent->svflags & SVF_DAMAGEABLE) ) ! 1294: // { ! 1295: // T_Damage (tr.ent, self, self, vec3_origin, self->enemy->s.origin, ! 1296: // pain_normal, hurt, 0, 0, MOD_UNKNOWN); ! 1297: ! 1298: VectorCopy (startpoint, top); ! 1299: top[2] += maxMoveUp; ! 1300: /* ! 1301: gi.WriteByte (svc_temp_entity); ! 1302: gi.WriteByte (TE_DEBUGTRAIL); ! 1303: gi.WritePosition (top); ! 1304: gi.WritePosition (startpoint); ! 1305: gi.multicast (startpoint, MULTICAST_ALL); ! 1306: */ ! 1307: tr = gi.trace (top, mins, maxs, startpoint, NULL, MASK_MONSTERSOLID); ! 1308: if (tr.startsolid || tr.allsolid) ! 1309: { ! 1310: if ((g_showlogic) && (g_showlogic->value)) ! 1311: if (tr.ent) ! 1312: gi.dprintf("FindSpawnPoint: failed to find a point -- blocked by %s\n", tr.ent->classname); ! 1313: else ! 1314: gi.dprintf("FindSpawnPoint: failed to find a point\n"); ! 1315: ! 1316: return false; ! 1317: } ! 1318: else ! 1319: { ! 1320: // if ((g_showlogic) && (g_showlogic->value)) ! 1321: // gi.dprintf ("FindSpawnPoint: %s -> %s\n", vtos (startpoint), vtos (tr.endpos)); ! 1322: VectorCopy (tr.endpos, spawnpoint); ! 1323: return true; ! 1324: } ! 1325: } ! 1326: else ! 1327: { ! 1328: VectorCopy (startpoint, spawnpoint); ! 1329: return true; ! 1330: } ! 1331: } ! 1332: ! 1333: // FIXME - all of this needs to be tweaked to handle the new gravity rules ! 1334: // if we ever want to spawn stuff on the roof ! 1335: ! 1336: // ! 1337: // CheckSpawnPoint ! 1338: // ! 1339: // PMM - checks volume to make sure we can spawn a monster there (is it solid?) ! 1340: // ! 1341: // This is all fliers should need ! 1342: ! 1343: qboolean CheckSpawnPoint (vec3_t origin, vec3_t mins, vec3_t maxs) ! 1344: { ! 1345: trace_t tr; ! 1346: ! 1347: if (!mins || !maxs || VectorCompare(mins, vec3_origin) || VectorCompare (maxs, vec3_origin)) ! 1348: { ! 1349: return false; ! 1350: } ! 1351: ! 1352: tr = gi.trace (origin, mins, maxs, origin, NULL, MASK_MONSTERSOLID); ! 1353: if(tr.startsolid || tr.allsolid) ! 1354: { ! 1355: if ((g_showlogic) && (g_showlogic->value)) ! 1356: gi.dprintf("createmonster in wall. removing\n"); ! 1357: return false; ! 1358: } ! 1359: if (tr.ent != world) ! 1360: { ! 1361: if ((g_showlogic) && (g_showlogic->value)) ! 1362: gi.dprintf("createmonster in entity %s\n", tr.ent->classname); ! 1363: return false; ! 1364: } ! 1365: return true; ! 1366: } ! 1367: ! 1368: // ! 1369: // CheckGroundSpawnPoint ! 1370: // ! 1371: // PMM - used for walking monsters ! 1372: // checks: ! 1373: // 1) is there a ground within the specified height of the origin? ! 1374: // 2) is the ground non-water? ! 1375: // 3) is the ground flat enough to walk on? ! 1376: // ! 1377: ! 1378: qboolean CheckGroundSpawnPoint (vec3_t origin, vec3_t entMins, vec3_t entMaxs, float height, float gravity) ! 1379: { ! 1380: trace_t tr; ! 1381: vec3_t start, stop; ! 1382: int failure = 0; ! 1383: vec3_t mins, maxs; ! 1384: int x, y; ! 1385: float mid, bottom; ! 1386: ! 1387: if (!CheckSpawnPoint (origin, entMins, entMaxs)) ! 1388: return false; ! 1389: ! 1390: // FIXME - this is too conservative about angled surfaces ! 1391: ! 1392: VectorCopy (origin, stop); ! 1393: // FIXME - gravity vector ! 1394: stop[2] = origin[2] + entMins[2] - height; ! 1395: ! 1396: /* ! 1397: gi.WriteByte (svc_temp_entity); ! 1398: gi.WriteByte (TE_DEBUGTRAIL); ! 1399: gi.WritePosition (origin); ! 1400: gi.WritePosition (stop); ! 1401: gi.multicast (start, MULTICAST_ALL); ! 1402: */ ! 1403: ! 1404: tr = gi.trace (origin, entMins, entMaxs, stop, NULL, MASK_MONSTERSOLID | MASK_WATER); ! 1405: // it's not going to be all solid or start solid, since that's checked above ! 1406: ! 1407: if ((tr.fraction < 1) && (tr.contents & MASK_MONSTERSOLID)) ! 1408: { ! 1409: // we found a non-water surface down there somewhere. now we need to check to make sure it's not too sloped ! 1410: // ! 1411: // algorithm straight out of m_move.c:M_CheckBottom() ! 1412: // ! 1413: ! 1414: // first, do the midpoint trace ! 1415: ! 1416: VectorAdd (tr.endpos, entMins, mins); ! 1417: VectorAdd (tr.endpos, entMaxs, maxs); ! 1418: ! 1419: ! 1420: // first, do the easy flat check ! 1421: // ! 1422: #ifdef ROGUE_GRAVITY ! 1423: // FIXME - this will only handle 0,0,1 and 0,0,-1 gravity vectors ! 1424: if(gravity > 0) ! 1425: start[2] = maxs[2] + 1; ! 1426: else ! 1427: start[2] = mins[2] - 1; ! 1428: #else ! 1429: start[2] = mins[2] - 1; ! 1430: #endif ! 1431: for (x=0 ; x<=1 ; x++) ! 1432: { ! 1433: for (y=0 ; y<=1 ; y++) ! 1434: { ! 1435: start[0] = x ? maxs[0] : mins[0]; ! 1436: start[1] = y ? maxs[1] : mins[1]; ! 1437: if (gi.pointcontents (start) != CONTENTS_SOLID) ! 1438: goto realcheck; ! 1439: } ! 1440: } ! 1441: ! 1442: // if it passed all four above checks, we're done ! 1443: return true; ! 1444: ! 1445: realcheck: ! 1446: ! 1447: // check it for real ! 1448: ! 1449: start[0] = stop[0] = (mins[0] + maxs[0])*0.5; ! 1450: start[1] = stop[1] = (mins[1] + maxs[1])*0.5; ! 1451: start[2] = mins[2]; ! 1452: ! 1453: tr = gi.trace (start, vec3_origin, vec3_origin, stop, NULL, MASK_MONSTERSOLID); ! 1454: ! 1455: if (tr.fraction == 1.0) ! 1456: return false; ! 1457: mid = bottom = tr.endpos[2]; ! 1458: ! 1459: #ifdef ROGUE_GRAVITY ! 1460: if(gravity < 0) ! 1461: { ! 1462: start[2] = mins[2]; ! 1463: stop[2] = start[2] - STEPSIZE - STEPSIZE; ! 1464: mid = bottom = tr.endpos[2] + entMins[2]; ! 1465: } ! 1466: else ! 1467: { ! 1468: start[2] = maxs[2]; ! 1469: stop[2] = start[2] + STEPSIZE + STEPSIZE; ! 1470: mid = bottom = tr.endpos[2] - entMaxs[2]; ! 1471: } ! 1472: #else ! 1473: stop[2] = start[2] - 2*STEPSIZE; ! 1474: mid = bottom = tr.endpos[2] + entMins[2]; ! 1475: #endif ! 1476: ! 1477: for (x=0 ; x<=1 ; x++) ! 1478: for (y=0 ; y<=1 ; y++) ! 1479: { ! 1480: start[0] = stop[0] = x ? maxs[0] : mins[0]; ! 1481: start[1] = stop[1] = y ? maxs[1] : mins[1]; ! 1482: ! 1483: /* ! 1484: gi.WriteByte (svc_temp_entity); ! 1485: gi.WriteByte (TE_DEBUGTRAIL); ! 1486: gi.WritePosition (start); ! 1487: gi.WritePosition (stop); ! 1488: gi.multicast (start, MULTICAST_ALL); ! 1489: */ ! 1490: tr = gi.trace (start, vec3_origin, vec3_origin, stop, NULL, MASK_MONSTERSOLID); ! 1491: ! 1492: //PGM ! 1493: #ifdef ROGUE_GRAVITY ! 1494: // FIXME - this will only handle 0,0,1 and 0,0,-1 gravity vectors ! 1495: if(gravity > 0) ! 1496: { ! 1497: if (tr.fraction != 1.0 && tr.endpos[2] < bottom) ! 1498: bottom = tr.endpos[2]; ! 1499: if (tr.fraction == 1.0 || tr.endpos[2] - mid > STEPSIZE) ! 1500: { ! 1501: if ((g_showlogic) && (g_showlogic->value)) ! 1502: gi.dprintf ("spawn - rejecting due to uneven ground\n"); ! 1503: return false; ! 1504: } ! 1505: } ! 1506: else ! 1507: { ! 1508: if (tr.fraction != 1.0 && tr.endpos[2] > bottom) ! 1509: bottom = tr.endpos[2]; ! 1510: if (tr.fraction == 1.0 || mid - tr.endpos[2] > STEPSIZE) ! 1511: { ! 1512: if ((g_showlogic) && (g_showlogic->value)) ! 1513: gi.dprintf ("spawn - rejecting due to uneven ground\n"); ! 1514: return false; ! 1515: } ! 1516: } ! 1517: #else ! 1518: if (tr.fraction != 1.0 && tr.endpos[2] > bottom) ! 1519: bottom = tr.endpos[2]; ! 1520: if (tr.fraction == 1.0 || mid - tr.endpos[2] > STEPSIZE) ! 1521: { ! 1522: return false; ! 1523: } ! 1524: #endif ! 1525: } ! 1526: ! 1527: return true; // we can land on it, it's ok ! 1528: } ! 1529: ! 1530: // otherwise, it's either water (bad) or not there (too far) ! 1531: // if we're here, it's bad below ! 1532: if ((g_showlogic) && (g_showlogic->value)) ! 1533: { ! 1534: if (tr.fraction < 1) ! 1535: if ((g_showlogic) && (g_showlogic->value)) ! 1536: gi.dprintf("groundmonster would fall into water/slime/lava\n"); ! 1537: else ! 1538: if ((g_showlogic) && (g_showlogic->value)) ! 1539: gi.dprintf("groundmonster would fall too far\n"); ! 1540: } ! 1541: ! 1542: return false; ! 1543: } ! 1544: ! 1545: void DetermineBBox (char *classname, vec3_t mins, vec3_t maxs) ! 1546: { ! 1547: // FIXME - cache this stuff ! 1548: edict_t *newEnt; ! 1549: ! 1550: newEnt = G_Spawn(); ! 1551: ! 1552: VectorCopy(vec3_origin, newEnt->s.origin); ! 1553: VectorCopy(vec3_origin, newEnt->s.angles); ! 1554: newEnt->classname = ED_NewString (classname); ! 1555: newEnt->monsterinfo.aiflags |= AI_DO_NOT_COUNT; ! 1556: ! 1557: ED_CallSpawn(newEnt); ! 1558: ! 1559: VectorCopy (newEnt->mins, mins); ! 1560: VectorCopy (newEnt->maxs, maxs); ! 1561: ! 1562: G_FreeEdict (newEnt); ! 1563: } ! 1564: ! 1565: // **************************** ! 1566: // SPAWNGROW stuff ! 1567: // **************************** ! 1568: ! 1569: #define SPAWNGROW_LIFESPAN 0.3 ! 1570: ! 1571: void spawngrow_think (edict_t *self) ! 1572: { ! 1573: int i; ! 1574: ! 1575: for (i=0; i<2; i++) ! 1576: { ! 1577: self->s.angles[0] = rand()%360; ! 1578: self->s.angles[1] = rand()%360; ! 1579: self->s.angles[2] = rand()%360; ! 1580: } ! 1581: if ((level.time < self->wait) && (self->s.frame < 2)) ! 1582: self->s.frame++; ! 1583: if (level.time >= self->wait) ! 1584: { ! 1585: if (self->s.effects & EF_SPHERETRANS) ! 1586: { ! 1587: G_FreeEdict (self); ! 1588: return; ! 1589: } ! 1590: else if (self->s.frame > 0) ! 1591: self->s.frame--; ! 1592: else ! 1593: { ! 1594: G_FreeEdict (self); ! 1595: return; ! 1596: } ! 1597: } ! 1598: self->nextthink += FRAMETIME; ! 1599: } ! 1600: ! 1601: void SpawnGrow_Spawn (vec3_t startpos, int size) ! 1602: { ! 1603: edict_t *ent; ! 1604: int i; ! 1605: float lifespan; ! 1606: ! 1607: ent = G_Spawn(); ! 1608: VectorCopy(startpos, ent->s.origin); ! 1609: for (i=0; i<2; i++) ! 1610: { ! 1611: ent->s.angles[0] = rand()%360; ! 1612: ent->s.angles[1] = rand()%360; ! 1613: ent->s.angles[2] = rand()%360; ! 1614: } ! 1615: ent->solid = SOLID_NOT; ! 1616: // ent->s.renderfx = RF_FULLBRIGHT | RF_IR_VISIBLE; ! 1617: ent->s.renderfx = RF_IR_VISIBLE; ! 1618: ent->movetype = MOVETYPE_NONE; ! 1619: ent->classname = "spawngro"; ! 1620: ! 1621: if (size <= 1) ! 1622: { ! 1623: lifespan = SPAWNGROW_LIFESPAN; ! 1624: ent->s.modelindex = gi.modelindex("models/items/spawngro2/tris.md2"); ! 1625: } ! 1626: else if (size == 2) ! 1627: { ! 1628: ent->s.modelindex = gi.modelindex("models/items/spawngro3/tris.md2"); ! 1629: lifespan = 2; ! 1630: } ! 1631: else ! 1632: { ! 1633: ent->s.modelindex = gi.modelindex("models/items/spawngro/tris.md2"); ! 1634: lifespan = SPAWNGROW_LIFESPAN; ! 1635: } ! 1636: ! 1637: ent->think = spawngrow_think; ! 1638: ! 1639: ent->wait = level.time + lifespan; ! 1640: ent->nextthink = level.time + FRAMETIME; ! 1641: if (size != 2) ! 1642: ent->s.effects |= EF_SPHERETRANS; ! 1643: gi.linkentity (ent); ! 1644: } ! 1645: ! 1646: ! 1647: // **************************** ! 1648: // WidowLeg stuff ! 1649: // **************************** ! 1650: ! 1651: #define MAX_LEGSFRAME 23 ! 1652: #define LEG_WAIT_TIME 1 ! 1653: ! 1654: void ThrowMoreStuff (edict_t *self, vec3_t point); ! 1655: void ThrowSmallStuff (edict_t *self, vec3_t point); ! 1656: void ThrowWidowGibLoc (edict_t *self, char *gibname, int damage, int type, vec3_t startpos, qboolean fade); ! 1657: void ThrowWidowGibSized (edict_t *self, char *gibname, int damage, int type, vec3_t startpos, int hitsound, qboolean fade); ! 1658: ! 1659: void widowlegs_think (edict_t *self) ! 1660: { ! 1661: vec3_t offset; ! 1662: vec3_t point; ! 1663: vec3_t f,r,u; ! 1664: ! 1665: if (self->s.frame == 17) ! 1666: { ! 1667: VectorSet (offset, 11.77, -7.24, 23.31); ! 1668: AngleVectors (self->s.angles, f, r, u); ! 1669: G_ProjectSource2 (self->s.origin, offset, f, r, u, point); ! 1670: gi.WriteByte (svc_temp_entity); ! 1671: gi.WriteByte (TE_EXPLOSION1); ! 1672: gi.WritePosition (point); ! 1673: gi.multicast (point, MULTICAST_ALL); ! 1674: ThrowSmallStuff (self, point); ! 1675: } ! 1676: ! 1677: if (self->s.frame < MAX_LEGSFRAME) ! 1678: { ! 1679: self->s.frame++; ! 1680: self->nextthink = level.time + FRAMETIME; ! 1681: return; ! 1682: } ! 1683: else if (self->wait == 0) ! 1684: { ! 1685: self->wait = level.time + LEG_WAIT_TIME; ! 1686: } ! 1687: if (level.time > self->wait) ! 1688: { ! 1689: AngleVectors (self->s.angles, f, r, u); ! 1690: ! 1691: VectorSet (offset, -65.6, -8.44, 28.59); ! 1692: G_ProjectSource2 (self->s.origin, offset, f, r, u, point); ! 1693: gi.WriteByte (svc_temp_entity); ! 1694: gi.WriteByte (TE_EXPLOSION1); ! 1695: gi.WritePosition (point); ! 1696: gi.multicast (point, MULTICAST_ALL); ! 1697: ThrowMoreStuff (self, point); ! 1698: ! 1699: ThrowWidowGibSized (self, "models/monsters/blackwidow/gib1/tris.md2", 80 + (int)(random()*20.0), GIB_METALLIC, point, 0, true); ! 1700: ThrowWidowGibSized (self, "models/monsters/blackwidow/gib2/tris.md2", 80 + (int)(random()*20.0), GIB_METALLIC, point, 0, true); ! 1701: ! 1702: VectorSet (offset, -1.04, -51.18, 7.04); ! 1703: G_ProjectSource2 (self->s.origin, offset, f, r, u, point); ! 1704: gi.WriteByte (svc_temp_entity); ! 1705: gi.WriteByte (TE_EXPLOSION1); ! 1706: gi.WritePosition (point); ! 1707: gi.multicast (point, MULTICAST_ALL); ! 1708: ThrowMoreStuff (self, point); ! 1709: ! 1710: ThrowWidowGibSized (self, "models/monsters/blackwidow/gib1/tris.md2", 80 + (int)(random()*20.0), GIB_METALLIC, point, 0, true); ! 1711: ThrowWidowGibSized (self, "models/monsters/blackwidow/gib2/tris.md2", 80 + (int)(random()*20.0), GIB_METALLIC, point, 0, true); ! 1712: ThrowWidowGibSized (self, "models/monsters/blackwidow/gib3/tris.md2", 80 + (int)(random()*20.0), GIB_METALLIC, point, 0, true); ! 1713: ! 1714: G_FreeEdict (self); ! 1715: return; ! 1716: } ! 1717: if ((level.time > (self->wait - 0.5)) && (self->count == 0)) ! 1718: { ! 1719: self->count = 1; ! 1720: AngleVectors (self->s.angles, f, r, u); ! 1721: ! 1722: VectorSet (offset, 31, -88.7, 10.96); ! 1723: G_ProjectSource2 (self->s.origin, offset, f, r, u, point); ! 1724: gi.WriteByte (svc_temp_entity); ! 1725: gi.WriteByte (TE_EXPLOSION1); ! 1726: gi.WritePosition (point); ! 1727: gi.multicast (point, MULTICAST_ALL); ! 1728: ThrowSmallStuff (self, point); ! 1729: ! 1730: VectorSet (offset, -12.67, -4.39, 15.68); ! 1731: G_ProjectSource2 (self->s.origin, offset, f, r, u, point); ! 1732: gi.WriteByte (svc_temp_entity); ! 1733: gi.WriteByte (TE_EXPLOSION1); ! 1734: gi.WritePosition (point); ! 1735: gi.multicast (point, MULTICAST_ALL); ! 1736: ThrowSmallStuff (self, point); ! 1737: ! 1738: self->nextthink = level.time + FRAMETIME; ! 1739: return; ! 1740: } ! 1741: self->nextthink = level.time + FRAMETIME; ! 1742: } ! 1743: ! 1744: void Widowlegs_Spawn (vec3_t startpos, vec3_t angles) ! 1745: { ! 1746: edict_t *ent; ! 1747: ! 1748: ent = G_Spawn(); ! 1749: VectorCopy(startpos, ent->s.origin); ! 1750: VectorCopy(angles, ent->s.angles); ! 1751: ent->solid = SOLID_NOT; ! 1752: ent->s.renderfx = RF_IR_VISIBLE; ! 1753: ent->movetype = MOVETYPE_NONE; ! 1754: ent->classname = "widowlegs"; ! 1755: ! 1756: ent->s.modelindex = gi.modelindex("models/monsters/legs/tris.md2"); ! 1757: ent->think = widowlegs_think; ! 1758: ! 1759: ent->nextthink = level.time + FRAMETIME; ! 1760: gi.linkentity (ent); ! 1761: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.