g_rogue_newdm.cpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. // Copyright (c) ZeniMax Media Inc.
  2. // Licensed under the GNU General Public License 2.0.
  3. // g_newdm.c
  4. // pmack
  5. // june 1998
  6. #include "../g_local.h"
  7. #include "../m_player.h"
  8. dm_game_rt DMGame;
  9. //=================
  10. //=================
  11. constexpr item_flags_t IF_TYPE_MASK = (IF_WEAPON | IF_AMMO | IF_POWERUP | IF_ARMOR | IF_KEY);
  12. void ED_CallSpawn(edict_t *ent);
  13. bool Pickup_Health(edict_t *ent, edict_t *other);
  14. bool Pickup_Armor(edict_t *ent, edict_t *other);
  15. bool Pickup_PowerArmor(edict_t *ent, edict_t *other);
  16. inline item_flags_t GetSubstituteItemFlags(item_id_t id)
  17. {
  18. const gitem_t *item = GetItemByIndex(id);
  19. // we want to stay within the item class
  20. item_flags_t flags = item->flags & IF_TYPE_MASK;
  21. if ((flags & (IF_WEAPON | IF_AMMO)) == (IF_WEAPON | IF_AMMO))
  22. flags = IF_AMMO;
  23. // Adrenaline and Mega Health count as powerup
  24. else if (id == IT_ITEM_ADRENALINE || id == IT_HEALTH_MEGA)
  25. flags = IF_POWERUP;
  26. return flags;
  27. }
  28. inline item_id_t FindSubstituteItem(edict_t *ent)
  29. {
  30. // never replace flags
  31. if (ent->item->id == IT_FLAG1 || ent->item->id == IT_FLAG2 || ent->item->id == IT_ITEM_TAG_TOKEN)
  32. return IT_NULL;
  33. // stimpack/shard randomizes
  34. if (ent->item->id == IT_HEALTH_SMALL ||
  35. ent->item->id == IT_ARMOR_SHARD)
  36. return brandom() ? IT_HEALTH_SMALL : IT_ARMOR_SHARD;
  37. // health is special case
  38. if (ent->item->id == IT_HEALTH_MEDIUM ||
  39. ent->item->id == IT_HEALTH_LARGE)
  40. {
  41. float rnd = frandom();
  42. if (rnd < 0.6f)
  43. return IT_HEALTH_MEDIUM;
  44. else
  45. return IT_HEALTH_LARGE;
  46. }
  47. // armor is also special case
  48. else if (ent->item->id == IT_ARMOR_JACKET ||
  49. ent->item->id == IT_ARMOR_COMBAT ||
  50. ent->item->id == IT_ARMOR_BODY ||
  51. ent->item->id == IT_ITEM_POWER_SCREEN ||
  52. ent->item->id == IT_ITEM_POWER_SHIELD)
  53. {
  54. float rnd = frandom();
  55. if (rnd < 0.4f)
  56. return IT_ARMOR_JACKET;
  57. else if (rnd < 0.6f)
  58. return IT_ARMOR_COMBAT;
  59. else if (rnd < 0.8f)
  60. return IT_ARMOR_BODY;
  61. else if (rnd < 0.9f)
  62. return IT_ITEM_POWER_SCREEN;
  63. else
  64. return IT_ITEM_POWER_SHIELD;
  65. }
  66. item_flags_t myflags = GetSubstituteItemFlags(ent->item->id);
  67. std::array<item_id_t, MAX_ITEMS> possible_items;
  68. size_t possible_item_count = 0;
  69. // gather matching items
  70. for (item_id_t i = static_cast<item_id_t>(IT_NULL + 1); i < IT_TOTAL; i = static_cast<item_id_t>(static_cast<int32_t>(i) + 1))
  71. {
  72. const gitem_t *it = GetItemByIndex(i);
  73. item_flags_t itflags = it->flags;
  74. if (!itflags || (itflags & (IF_NOT_GIVEABLE | IF_TECH | IF_NOT_RANDOM)) || !it->pickup || !it->world_model)
  75. continue;
  76. // don't respawn spheres if they're dmflag disabled.
  77. if (g_no_spheres->integer)
  78. {
  79. if (i == IT_ITEM_SPHERE_VENGEANCE ||
  80. i == IT_ITEM_SPHERE_HUNTER ||
  81. i == IT_ITEM_SPHERE_DEFENDER)
  82. {
  83. continue;
  84. }
  85. }
  86. if (g_no_nukes->integer && i == IT_AMMO_NUKE)
  87. continue;
  88. if (g_no_mines->integer &&
  89. (i == IT_AMMO_PROX || i == IT_AMMO_TESLA || i == IT_AMMO_TRAP || i == IT_WEAPON_PROXLAUNCHER))
  90. continue;
  91. itflags = GetSubstituteItemFlags(i);
  92. if ((itflags & IF_TYPE_MASK) == (myflags & IF_TYPE_MASK))
  93. possible_items[possible_item_count++] = i;
  94. }
  95. if (!possible_item_count)
  96. return IT_NULL;
  97. return possible_items[irandom(possible_item_count)];
  98. }
  99. //=================
  100. //=================
  101. item_id_t DoRandomRespawn(edict_t *ent)
  102. {
  103. if (!ent->item)
  104. return IT_NULL; // why
  105. item_id_t id = FindSubstituteItem(ent);
  106. if (id == IT_NULL)
  107. return IT_NULL;
  108. return id;
  109. }
  110. //=================
  111. //=================
  112. void PrecacheForRandomRespawn()
  113. {
  114. gitem_t *it;
  115. int i;
  116. int itflags;
  117. it = itemlist;
  118. for (i = 0; i < IT_TOTAL; i++, it++)
  119. {
  120. itflags = it->flags;
  121. if (!itflags || (itflags & (IF_NOT_GIVEABLE | IF_TECH | IF_NOT_RANDOM)) || !it->pickup || !it->world_model)
  122. continue;
  123. PrecacheItem(it);
  124. }
  125. }
  126. // ***************************
  127. // DOPPLEGANGER
  128. // ***************************
  129. edict_t *Sphere_Spawn(edict_t *owner, spawnflags_t spawnflags);
  130. DIE(doppleganger_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
  131. {
  132. edict_t *sphere;
  133. float dist;
  134. vec3_t dir;
  135. if ((self->enemy) && (self->enemy != self->teammaster))
  136. {
  137. dir = self->enemy->s.origin - self->s.origin;
  138. dist = dir.length();
  139. if (dist > 80.f)
  140. {
  141. if (dist > 768)
  142. {
  143. sphere = Sphere_Spawn(self, SPHERE_HUNTER | SPHERE_DOPPLEGANGER);
  144. sphere->pain(sphere, attacker, 0, 0, mod);
  145. }
  146. else
  147. {
  148. sphere = Sphere_Spawn(self, SPHERE_VENGEANCE | SPHERE_DOPPLEGANGER);
  149. sphere->pain(sphere, attacker, 0, 0, mod);
  150. }
  151. }
  152. }
  153. self->takedamage = DAMAGE_NONE;
  154. // [Paril-KEX]
  155. T_RadiusDamage(self, self->teammaster, 160.f, self, 140.f, DAMAGE_NONE, MOD_DOPPLE_EXPLODE);
  156. if (self->teamchain)
  157. BecomeExplosion1(self->teamchain);
  158. BecomeExplosion1(self);
  159. }
  160. PAIN(doppleganger_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
  161. {
  162. self->enemy = other;
  163. }
  164. THINK(doppleganger_timeout) (edict_t *self) -> void
  165. {
  166. doppleganger_die(self, self, self, 9999, self->s.origin, MOD_UNKNOWN);
  167. }
  168. THINK(body_think) (edict_t *self) -> void
  169. {
  170. float r;
  171. if (fabsf(self->ideal_yaw - anglemod(self->s.angles[YAW])) < 2)
  172. {
  173. if (self->timestamp < level.time)
  174. {
  175. r = frandom();
  176. if (r < 0.10f)
  177. {
  178. self->ideal_yaw = frandom(350.0f);
  179. self->timestamp = level.time + 1_sec;
  180. }
  181. }
  182. }
  183. else
  184. M_ChangeYaw(self);
  185. if (self->teleport_time <= level.time)
  186. {
  187. self->s.frame++;
  188. if (self->s.frame > FRAME_stand40)
  189. self->s.frame = FRAME_stand01;
  190. self->teleport_time = level.time + 10_hz;
  191. }
  192. self->nextthink = level.time + FRAME_TIME_MS;
  193. }
  194. void fire_doppleganger(edict_t *ent, const vec3_t &start, const vec3_t &aimdir)
  195. {
  196. edict_t *base;
  197. edict_t *body;
  198. vec3_t dir;
  199. vec3_t forward, right, up;
  200. int number;
  201. dir = vectoangles(aimdir);
  202. AngleVectors(dir, forward, right, up);
  203. base = G_Spawn();
  204. base->s.origin = start;
  205. base->s.angles = dir;
  206. base->movetype = MOVETYPE_TOSS;
  207. base->solid = SOLID_BBOX;
  208. base->s.renderfx |= RF_IR_VISIBLE;
  209. base->s.angles[PITCH] = 0;
  210. base->mins = { -16, -16, -24 };
  211. base->maxs = { 16, 16, 32 };
  212. base->s.modelindex = gi.modelindex ("models/objects/dopplebase/tris.md2");
  213. base->s.alpha = 0.1f;
  214. base->teammaster = ent;
  215. base->flags |= ( FL_DAMAGEABLE | FL_TRAP );
  216. base->takedamage = true;
  217. base->health = 30;
  218. base->pain = doppleganger_pain;
  219. base->die = doppleganger_die;
  220. base->nextthink = level.time + 30_sec;
  221. base->think = doppleganger_timeout;
  222. base->classname = "doppleganger";
  223. gi.linkentity(base);
  224. body = G_Spawn();
  225. number = body->s.number;
  226. body->s = ent->s;
  227. body->s.sound = 0;
  228. body->s.event = EV_NONE;
  229. body->s.number = number;
  230. body->yaw_speed = 30;
  231. body->ideal_yaw = 0;
  232. body->s.origin = start;
  233. body->s.origin[2] += 8;
  234. body->teleport_time = level.time + 10_hz;
  235. body->think = body_think;
  236. body->nextthink = level.time + FRAME_TIME_MS;
  237. gi.linkentity(body);
  238. base->teamchain = body;
  239. body->teammaster = base;
  240. // [Paril-KEX]
  241. body->owner = ent;
  242. gi.sound(body, CHAN_AUTO, gi.soundindex("medic_commander/monsterspawn1.wav"), 1.f, ATTN_NORM, 0.f);
  243. }
  244. void Tag_GameInit();
  245. void Tag_PostInitSetup();
  246. void Tag_PlayerDeath(edict_t *targ, edict_t *inflictor, edict_t *attacker);
  247. void Tag_Score(edict_t *attacker, edict_t *victim, int scoreChange, const mod_t &mod);
  248. void Tag_PlayerEffects(edict_t *ent);
  249. void Tag_DogTag(edict_t *ent, edict_t *killer, const char **pic);
  250. void Tag_PlayerDisconnect(edict_t *ent);
  251. int Tag_ChangeDamage(edict_t *targ, edict_t *attacker, int damage, mod_t mod);
  252. void DBall_GameInit();
  253. void DBall_ClientBegin(edict_t *ent);
  254. bool DBall_SelectSpawnPoint(edict_t *ent, vec3_t &origin, vec3_t &angles, bool force_spawn);
  255. int DBall_ChangeKnockback(edict_t *targ, edict_t *attacker, int knockback, mod_t mod);
  256. int DBall_ChangeDamage(edict_t *targ, edict_t *attacker, int damage, mod_t mod);
  257. void DBall_PostInitSetup();
  258. int DBall_CheckDMRules();
  259. // ****************************
  260. // General DM Stuff
  261. // ****************************
  262. void InitGameRules()
  263. {
  264. // clear out the game rule structure before we start
  265. memset(&DMGame, 0, sizeof(dm_game_rt));
  266. if (gamerules->integer)
  267. {
  268. switch (gamerules->integer)
  269. {
  270. case RDM_TAG:
  271. DMGame.GameInit = Tag_GameInit;
  272. DMGame.PostInitSetup = Tag_PostInitSetup;
  273. DMGame.PlayerDeath = Tag_PlayerDeath;
  274. DMGame.Score = Tag_Score;
  275. DMGame.PlayerEffects = Tag_PlayerEffects;
  276. DMGame.DogTag = Tag_DogTag;
  277. DMGame.PlayerDisconnect = Tag_PlayerDisconnect;
  278. DMGame.ChangeDamage = Tag_ChangeDamage;
  279. break;
  280. case RDM_DEATHBALL:
  281. DMGame.GameInit = DBall_GameInit;
  282. DMGame.ChangeKnockback = DBall_ChangeKnockback;
  283. DMGame.ChangeDamage = DBall_ChangeDamage;
  284. DMGame.ClientBegin = DBall_ClientBegin;
  285. DMGame.SelectSpawnPoint = DBall_SelectSpawnPoint;
  286. DMGame.PostInitSetup = DBall_PostInitSetup;
  287. DMGame.CheckDMRules = DBall_CheckDMRules;
  288. break;
  289. // reset gamerules if it's not a valid number
  290. default:
  291. gi.cvar_forceset("gamerules", "0");
  292. break;
  293. }
  294. }
  295. // if we're set up to play, initialize the game as needed.
  296. if (DMGame.GameInit)
  297. DMGame.GameInit();
  298. }