p_weapon.cpp 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926
  1. // Copyright (c) ZeniMax Media Inc.
  2. // Licensed under the GNU General Public License 2.0.
  3. // g_weapon.c
  4. #include "g_local.h"
  5. #include "m_player.h"
  6. bool is_quad;
  7. // RAFAEL
  8. bool is_quadfire;
  9. // RAFAEL
  10. player_muzzle_t is_silenced;
  11. // PGM
  12. byte damage_multiplier;
  13. // PGM
  14. void weapon_grenade_fire(edict_t *ent, bool held);
  15. // RAFAEL
  16. void weapon_trap_fire(edict_t *ent, bool held);
  17. // RAFAEL
  18. //========
  19. // [Kex]
  20. bool G_CheckInfiniteAmmo(gitem_t *item)
  21. {
  22. if (item->flags & IF_NO_INFINITE_AMMO)
  23. return false;
  24. return g_infinite_ammo->integer || (deathmatch->integer && g_instagib->integer);
  25. }
  26. //========
  27. // ROGUE
  28. byte P_DamageModifier(edict_t *ent)
  29. {
  30. is_quad = 0;
  31. damage_multiplier = 1;
  32. if (ent->client->quad_time > level.time)
  33. {
  34. damage_multiplier *= 4;
  35. is_quad = 1;
  36. // if we're quad and DF_NO_STACK_DOUBLE is on, return now.
  37. if (g_dm_no_stack_double->integer)
  38. return damage_multiplier;
  39. }
  40. if (ent->client->double_time > level.time)
  41. {
  42. damage_multiplier *= 2;
  43. is_quad = 1;
  44. }
  45. return damage_multiplier;
  46. }
  47. // ROGUE
  48. //========
  49. // [Paril-KEX] kicks in vanilla take place over 2 10hz server
  50. // frames; this is to mimic that visual behavior on any tickrate.
  51. inline float P_CurrentKickFactor(edict_t *ent)
  52. {
  53. if (ent->client->kick.time < level.time)
  54. return 0.f;
  55. float f = (ent->client->kick.time - level.time).seconds() / ent->client->kick.total.seconds();
  56. return f;
  57. }
  58. // [Paril-KEX]
  59. vec3_t P_CurrentKickAngles(edict_t *ent)
  60. {
  61. return ent->client->kick.angles * P_CurrentKickFactor(ent);
  62. }
  63. vec3_t P_CurrentKickOrigin(edict_t *ent)
  64. {
  65. return ent->client->kick.origin * P_CurrentKickFactor(ent);
  66. }
  67. void P_AddWeaponKick(edict_t *ent, const vec3_t &origin, const vec3_t &angles)
  68. {
  69. ent->client->kick.origin = origin;
  70. ent->client->kick.angles = angles;
  71. ent->client->kick.total = 200_ms;
  72. ent->client->kick.time = level.time + ent->client->kick.total;
  73. }
  74. void P_ProjectSource(edict_t *ent, const vec3_t &angles, vec3_t distance, vec3_t &result_start, vec3_t &result_dir)
  75. {
  76. if (ent->client->pers.hand == LEFT_HANDED)
  77. distance[1] *= -1;
  78. else if (ent->client->pers.hand == CENTER_HANDED)
  79. distance[1] = 0;
  80. vec3_t forward, right, up;
  81. vec3_t eye_position = (ent->s.origin + vec3_t{ 0, 0, (float) ent->viewheight });
  82. AngleVectors(angles, forward, right, up);
  83. result_start = G_ProjectSource2(eye_position, distance, forward, right, up);
  84. vec3_t end = eye_position + forward * 8192;
  85. contents_t mask = MASK_PROJECTILE & ~CONTENTS_DEADMONSTER;
  86. // [Paril-KEX]
  87. if (!G_ShouldPlayersCollide(true))
  88. mask &= ~CONTENTS_PLAYER;
  89. trace_t tr = gi.traceline(eye_position, end, ent, mask);
  90. // if the point was a monster & close to us, use raw forward
  91. // so railgun pierces properly
  92. if (tr.startsolid || ((tr.contents & (CONTENTS_MONSTER | CONTENTS_PLAYER)) && (tr.fraction * 8192.f) < 128.f))
  93. result_dir = forward;
  94. else
  95. {
  96. end = tr.endpos;
  97. result_dir = (end - result_start).normalized();
  98. #if 0
  99. // correction for blocked shots
  100. trace_t eye_tr = gi.traceline(result_start, result_start + (result_dir * tr.fraction * 8192.f), ent, mask);
  101. if ((eye_tr.endpos - tr.endpos).length() > 32.f)
  102. {
  103. result_start = eye_position;
  104. result_dir = (end - result_start).normalized();
  105. return;
  106. }
  107. #endif
  108. }
  109. }
  110. /*
  111. ===============
  112. PlayerNoise
  113. Each player can have two noise objects associated with it:
  114. a personal noise (jumping, pain, weapon firing), and a weapon
  115. target noise (bullet wall impacts)
  116. Monsters that don't directly see the player can move
  117. to a noise in hopes of seeing the player from there.
  118. ===============
  119. */
  120. void PlayerNoise(edict_t *who, const vec3_t &where, player_noise_t type)
  121. {
  122. edict_t *noise;
  123. if (type == PNOISE_WEAPON)
  124. {
  125. if (who->client->silencer_shots)
  126. who->client->invisibility_fade_time = level.time + (INVISIBILITY_TIME / 5);
  127. else
  128. who->client->invisibility_fade_time = level.time + INVISIBILITY_TIME;
  129. if (who->client->silencer_shots)
  130. {
  131. who->client->silencer_shots--;
  132. return;
  133. }
  134. }
  135. if (deathmatch->integer)
  136. return;
  137. if (who->flags & FL_NOTARGET)
  138. return;
  139. if (type == PNOISE_SELF &&
  140. (who->client->landmark_free_fall || who->client->landmark_noise_time >= level.time))
  141. return;
  142. // ROGUE
  143. if (who->flags & FL_DISGUISED)
  144. {
  145. if (type == PNOISE_WEAPON)
  146. {
  147. level.disguise_violator = who;
  148. level.disguise_violation_time = level.time + 500_ms;
  149. }
  150. else
  151. return;
  152. }
  153. // ROGUE
  154. if (!who->mynoise)
  155. {
  156. noise = G_Spawn();
  157. noise->classname = "player_noise";
  158. noise->mins = { -8, -8, -8 };
  159. noise->maxs = { 8, 8, 8 };
  160. noise->owner = who;
  161. noise->svflags = SVF_NOCLIENT;
  162. who->mynoise = noise;
  163. noise = G_Spawn();
  164. noise->classname = "player_noise";
  165. noise->mins = { -8, -8, -8 };
  166. noise->maxs = { 8, 8, 8 };
  167. noise->owner = who;
  168. noise->svflags = SVF_NOCLIENT;
  169. who->mynoise2 = noise;
  170. }
  171. if (type == PNOISE_SELF || type == PNOISE_WEAPON)
  172. {
  173. noise = who->mynoise;
  174. who->client->sound_entity = noise;
  175. who->client->sound_entity_time = level.time;
  176. }
  177. else // type == PNOISE_IMPACT
  178. {
  179. noise = who->mynoise2;
  180. who->client->sound2_entity = noise;
  181. who->client->sound2_entity_time = level.time;
  182. }
  183. noise->s.origin = where;
  184. noise->absmin = where - noise->maxs;
  185. noise->absmax = where + noise->maxs;
  186. noise->teleport_time = level.time;
  187. gi.linkentity(noise);
  188. }
  189. inline bool G_WeaponShouldStay()
  190. {
  191. if (deathmatch->integer)
  192. return g_dm_weapons_stay->integer;
  193. else if (coop->integer)
  194. return !P_UseCoopInstancedItems();
  195. return false;
  196. }
  197. void G_CheckAutoSwitch(edict_t *ent, gitem_t *item, bool is_new);
  198. bool Pickup_Weapon(edict_t *ent, edict_t *other)
  199. {
  200. item_id_t index;
  201. gitem_t *ammo;
  202. index = ent->item->id;
  203. if (G_WeaponShouldStay() && other->client->pers.inventory[index])
  204. {
  205. if (!(ent->spawnflags & (SPAWNFLAG_ITEM_DROPPED | SPAWNFLAG_ITEM_DROPPED_PLAYER)))
  206. return false; // leave the weapon for others to pickup
  207. }
  208. bool is_new = !other->client->pers.inventory[index];
  209. other->client->pers.inventory[index]++;
  210. if (!(ent->spawnflags & SPAWNFLAG_ITEM_DROPPED))
  211. {
  212. // give them some ammo with it
  213. // PGM -- IF APPROPRIATE!
  214. if (ent->item->ammo) // PGM
  215. {
  216. ammo = GetItemByIndex(ent->item->ammo);
  217. // RAFAEL: Don't get infinite ammo with trap
  218. if (G_CheckInfiniteAmmo(ammo))
  219. Add_Ammo(other, ammo, 1000);
  220. else
  221. Add_Ammo(other, ammo, ammo->quantity);
  222. }
  223. if (!(ent->spawnflags & SPAWNFLAG_ITEM_DROPPED_PLAYER))
  224. {
  225. if (deathmatch->integer)
  226. {
  227. if (g_dm_weapons_stay->integer)
  228. ent->flags |= FL_RESPAWN;
  229. SetRespawn( ent, gtime_t::from_sec(g_weapon_respawn_time->integer), !g_dm_weapons_stay->integer);
  230. }
  231. if (coop->integer)
  232. ent->flags |= FL_RESPAWN;
  233. }
  234. }
  235. G_CheckAutoSwitch(other, ent->item, is_new);
  236. return true;
  237. }
  238. static void Weapon_RunThink(edict_t *ent)
  239. {
  240. // call active weapon think routine
  241. if (!ent->client->pers.weapon->weaponthink)
  242. return;
  243. P_DamageModifier(ent);
  244. // RAFAEL
  245. is_quadfire = (ent->client->quadfire_time > level.time);
  246. // RAFAEL
  247. if (ent->client->silencer_shots)
  248. is_silenced = MZ_SILENCED;
  249. else
  250. is_silenced = MZ_NONE;
  251. ent->client->pers.weapon->weaponthink(ent);
  252. }
  253. /*
  254. ===============
  255. ChangeWeapon
  256. The old weapon has been dropped all the way, so make the new one
  257. current
  258. ===============
  259. */
  260. void ChangeWeapon(edict_t *ent)
  261. {
  262. // [Paril-KEX]
  263. if (ent->health > 0 && !g_instant_weapon_switch->integer && ((ent->client->latched_buttons | ent->client->buttons) & BUTTON_HOLSTER))
  264. return;
  265. if (ent->client->grenade_time)
  266. {
  267. // force a weapon think to drop the held grenade
  268. ent->client->weapon_sound = 0;
  269. Weapon_RunThink(ent);
  270. ent->client->grenade_time = 0_ms;
  271. }
  272. if (ent->client->pers.weapon)
  273. {
  274. ent->client->pers.lastweapon = ent->client->pers.weapon;
  275. if (ent->client->newweapon && ent->client->newweapon != ent->client->pers.weapon)
  276. gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/change.wav"), 1, ATTN_NORM, 0);
  277. }
  278. ent->client->pers.weapon = ent->client->newweapon;
  279. ent->client->newweapon = nullptr;
  280. ent->client->machinegun_shots = 0;
  281. // set visible model
  282. if (ent->s.modelindex == MODELINDEX_PLAYER)
  283. P_AssignClientSkinnum(ent);
  284. if (!ent->client->pers.weapon)
  285. { // dead
  286. ent->client->ps.gunindex = 0;
  287. ent->client->ps.gunskin = 0;
  288. return;
  289. }
  290. ent->client->weaponstate = WEAPON_ACTIVATING;
  291. ent->client->ps.gunframe = 0;
  292. ent->client->ps.gunindex = gi.modelindex(ent->client->pers.weapon->view_model);
  293. ent->client->ps.gunskin = 0;
  294. ent->client->weapon_sound = 0;
  295. ent->client->anim_priority = ANIM_PAIN;
  296. if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
  297. {
  298. ent->s.frame = FRAME_crpain1;
  299. ent->client->anim_end = FRAME_crpain4;
  300. }
  301. else
  302. {
  303. ent->s.frame = FRAME_pain301;
  304. ent->client->anim_end = FRAME_pain304;
  305. }
  306. ent->client->anim_time = 0_ms;
  307. // for instantweap, run think immediately
  308. // to set up correct start frame
  309. if (g_instant_weapon_switch->integer)
  310. Weapon_RunThink(ent);
  311. }
  312. /*
  313. =================
  314. NoAmmoWeaponChange
  315. =================
  316. */
  317. void NoAmmoWeaponChange(edict_t *ent, bool sound)
  318. {
  319. if (sound)
  320. {
  321. if (level.time >= ent->client->empty_click_sound)
  322. {
  323. gi.sound(ent, CHAN_WEAPON, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
  324. ent->client->empty_click_sound = level.time + 1_sec;
  325. }
  326. }
  327. constexpr item_id_t no_ammo_order[] = {
  328. IT_WEAPON_DISRUPTOR,
  329. IT_WEAPON_RAILGUN,
  330. IT_WEAPON_PLASMABEAM,
  331. IT_WEAPON_IONRIPPER,
  332. IT_WEAPON_HYPERBLASTER,
  333. IT_WEAPON_ETF_RIFLE,
  334. IT_WEAPON_CHAINGUN,
  335. IT_WEAPON_MACHINEGUN,
  336. IT_WEAPON_SSHOTGUN,
  337. IT_WEAPON_SHOTGUN,
  338. IT_WEAPON_PHALANX,
  339. IT_WEAPON_RLAUNCHER,
  340. IT_WEAPON_GLAUNCHER,
  341. IT_WEAPON_PROXLAUNCHER,
  342. IT_WEAPON_CHAINFIST,
  343. IT_WEAPON_BLASTER
  344. };
  345. for (size_t i = 0; i < q_countof(no_ammo_order); i++)
  346. {
  347. gitem_t *item = GetItemByIndex(no_ammo_order[i]);
  348. if (!item)
  349. gi.Com_ErrorFmt("Invalid no ammo weapon switch weapon {}\n", (int32_t) no_ammo_order[i]);
  350. if (!ent->client->pers.inventory[item->id])
  351. continue;
  352. if (item->ammo && ent->client->pers.inventory[item->ammo] < item->quantity)
  353. continue;
  354. ent->client->newweapon = item;
  355. return;
  356. }
  357. }
  358. void G_RemoveAmmo(edict_t *ent, int32_t quantity)
  359. {
  360. if (G_CheckInfiniteAmmo(ent->client->pers.weapon))
  361. return;
  362. bool pre_warning = ent->client->pers.inventory[ent->client->pers.weapon->ammo] <=
  363. ent->client->pers.weapon->quantity_warn;
  364. ent->client->pers.inventory[ent->client->pers.weapon->ammo] -= quantity;
  365. bool post_warning = ent->client->pers.inventory[ent->client->pers.weapon->ammo] <=
  366. ent->client->pers.weapon->quantity_warn;
  367. if (!pre_warning && post_warning)
  368. gi.local_sound(ent, CHAN_AUTO, gi.soundindex("weapons/lowammo.wav"), 1, ATTN_NORM, 0);
  369. if (ent->client->pers.weapon->ammo == IT_AMMO_CELLS)
  370. G_CheckPowerArmor(ent);
  371. }
  372. void G_RemoveAmmo(edict_t *ent)
  373. {
  374. G_RemoveAmmo(ent, ent->client->pers.weapon->quantity);
  375. }
  376. // [Paril-KEX] get time per animation frame
  377. inline gtime_t Weapon_AnimationTime(edict_t *ent)
  378. {
  379. if (g_quick_weapon_switch->integer && (gi.tick_rate >= 20) &&
  380. (ent->client->weaponstate == WEAPON_ACTIVATING || ent->client->weaponstate == WEAPON_DROPPING))
  381. ent->client->ps.gunrate = 20;
  382. else
  383. ent->client->ps.gunrate = 10;
  384. if (ent->client->ps.gunframe != 0 && (!(ent->client->pers.weapon->flags & IF_NO_HASTE) || ent->client->weaponstate != WEAPON_FIRING))
  385. {
  386. if (is_quadfire)
  387. ent->client->ps.gunrate *= 2;
  388. if (CTFApplyHaste(ent))
  389. ent->client->ps.gunrate *= 2;
  390. }
  391. // network optimization...
  392. if (ent->client->ps.gunrate == 10)
  393. {
  394. ent->client->ps.gunrate = 0;
  395. return 100_ms;
  396. }
  397. return gtime_t::from_ms((1.f / ent->client->ps.gunrate) * 1000);
  398. }
  399. /*
  400. =================
  401. Think_Weapon
  402. Called by ClientBeginServerFrame and ClientThink
  403. =================
  404. */
  405. void Think_Weapon(edict_t *ent)
  406. {
  407. if (ent->client->resp.spectator)
  408. return;
  409. // if just died, put the weapon away
  410. if (ent->health < 1)
  411. {
  412. ent->client->newweapon = nullptr;
  413. ChangeWeapon(ent);
  414. }
  415. if (!ent->client->pers.weapon)
  416. {
  417. if (ent->client->newweapon)
  418. ChangeWeapon(ent);
  419. return;
  420. }
  421. // call active weapon think routine
  422. Weapon_RunThink(ent);
  423. // check remainder from haste; on 100ms/50ms server frames we may have
  424. // 'run next frame in' times that we can't possibly catch up to,
  425. // so we have to run them now.
  426. if (33_ms < FRAME_TIME_MS)
  427. {
  428. gtime_t relative_time = Weapon_AnimationTime(ent);
  429. if (relative_time < FRAME_TIME_MS)
  430. {
  431. // check how many we can't run before the next server tick
  432. gtime_t next_frame = level.time + FRAME_TIME_S;
  433. int64_t remaining_ms = (next_frame - ent->client->weapon_think_time).milliseconds();
  434. while (remaining_ms > 0)
  435. {
  436. ent->client->weapon_think_time -= relative_time;
  437. ent->client->weapon_fire_finished -= relative_time;
  438. Weapon_RunThink(ent);
  439. remaining_ms -= relative_time.milliseconds();
  440. }
  441. }
  442. }
  443. }
  444. enum weap_switch_t
  445. {
  446. WEAP_SWITCH_ALREADY_USING,
  447. WEAP_SWITCH_NO_WEAPON,
  448. WEAP_SWITCH_NO_AMMO,
  449. WEAP_SWITCH_NOT_ENOUGH_AMMO,
  450. WEAP_SWITCH_VALID
  451. };
  452. weap_switch_t Weapon_AttemptSwitch(edict_t *ent, gitem_t *item, bool silent)
  453. {
  454. if (ent->client->pers.weapon == item)
  455. return WEAP_SWITCH_ALREADY_USING;
  456. else if (!ent->client->pers.inventory[item->id])
  457. return WEAP_SWITCH_NO_WEAPON;
  458. if (item->ammo && !g_select_empty->integer && !(item->flags & IF_AMMO))
  459. {
  460. gitem_t *ammo_item = GetItemByIndex(item->ammo);
  461. if (!ent->client->pers.inventory[item->ammo])
  462. {
  463. if (!silent)
  464. gi.LocClient_Print(ent, PRINT_HIGH, "$g_no_ammo", ammo_item->pickup_name, item->pickup_name_definite);
  465. return WEAP_SWITCH_NO_AMMO;
  466. }
  467. else if (ent->client->pers.inventory[item->ammo] < item->quantity)
  468. {
  469. if (!silent)
  470. gi.LocClient_Print(ent, PRINT_HIGH, "$g_not_enough_ammo", ammo_item->pickup_name, item->pickup_name_definite);
  471. return WEAP_SWITCH_NOT_ENOUGH_AMMO;
  472. }
  473. }
  474. return WEAP_SWITCH_VALID;
  475. }
  476. inline bool Weapon_IsPartOfChain(gitem_t *item, gitem_t *other)
  477. {
  478. return other && other->chain && item->chain && other->chain == item->chain;
  479. }
  480. /*
  481. ================
  482. Use_Weapon
  483. Make the weapon ready if there is ammo
  484. ================
  485. */
  486. void Use_Weapon(edict_t *ent, gitem_t *item)
  487. {
  488. gitem_t *wanted, *root;
  489. weap_switch_t result = WEAP_SWITCH_NO_WEAPON;
  490. // if we're switching to a weapon in this chain already,
  491. // start from the weapon after this one in the chain
  492. if (!ent->client->no_weapon_chains && Weapon_IsPartOfChain(item, ent->client->newweapon))
  493. {
  494. root = ent->client->newweapon;
  495. wanted = root->chain_next;
  496. }
  497. // if we're already holding a weapon in this chain,
  498. // start from the weapon after that one
  499. else if (!ent->client->no_weapon_chains && Weapon_IsPartOfChain(item, ent->client->pers.weapon))
  500. {
  501. root = ent->client->pers.weapon;
  502. wanted = root->chain_next;
  503. }
  504. // start from beginning of chain (if any)
  505. else
  506. wanted = root = item;
  507. while (true)
  508. {
  509. // try the weapon currently in the chain
  510. if ((result = Weapon_AttemptSwitch(ent, wanted, false)) == WEAP_SWITCH_VALID)
  511. break;
  512. // no chains
  513. if (!wanted->chain_next || ent->client->no_weapon_chains)
  514. break;
  515. wanted = wanted->chain_next;
  516. // we wrapped back to the root item
  517. if (wanted == root)
  518. break;
  519. }
  520. if (result == WEAP_SWITCH_VALID)
  521. ent->client->newweapon = wanted; // change to this weapon when down
  522. else if ((result = Weapon_AttemptSwitch(ent, wanted, true)) == WEAP_SWITCH_NO_WEAPON && wanted != ent->client->pers.weapon && wanted != ent->client->newweapon)
  523. gi.LocClient_Print(ent, PRINT_HIGH, "$g_out_of_item", wanted->pickup_name);
  524. }
  525. /*
  526. ================
  527. Drop_Weapon
  528. ================
  529. */
  530. void Drop_Weapon(edict_t *ent, gitem_t *item)
  531. {
  532. // [Paril-KEX]
  533. if (deathmatch->integer && g_dm_weapons_stay->integer)
  534. return;
  535. item_id_t index = item->id;
  536. // see if we're already using it
  537. if (((item == ent->client->pers.weapon) || (item == ent->client->newweapon)) && (ent->client->pers.inventory[index] == 1))
  538. {
  539. gi.LocClient_Print(ent, PRINT_HIGH, "$g_cant_drop_weapon");
  540. return;
  541. }
  542. edict_t *drop = Drop_Item(ent, item);
  543. drop->spawnflags |= SPAWNFLAG_ITEM_DROPPED_PLAYER;
  544. drop->svflags &= ~SVF_INSTANCED;
  545. ent->client->pers.inventory[index]--;
  546. }
  547. void Weapon_PowerupSound(edict_t *ent)
  548. {
  549. if (!CTFApplyStrengthSound(ent))
  550. {
  551. if (ent->client->quad_time > level.time && ent->client->double_time > level.time)
  552. gi.sound(ent, CHAN_ITEM, gi.soundindex("ctf/tech2x.wav"), 1, ATTN_NORM, 0);
  553. else if (ent->client->quad_time > level.time)
  554. gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage3.wav"), 1, ATTN_NORM, 0);
  555. else if (ent->client->double_time > level.time)
  556. gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/ddamage3.wav"), 1, ATTN_NORM, 0);
  557. else if (ent->client->quadfire_time > level.time
  558. && ent->client->ctf_techsndtime < level.time)
  559. {
  560. ent->client->ctf_techsndtime = level.time + 1_sec;
  561. gi.sound(ent, CHAN_ITEM, gi.soundindex("ctf/tech3.wav"), 1, ATTN_NORM, 0);
  562. }
  563. }
  564. CTFApplyHasteSound(ent);
  565. }
  566. inline bool Weapon_CanAnimate(edict_t *ent)
  567. {
  568. // VWep animations screw up corpses
  569. return !ent->deadflag && ent->s.modelindex == MODELINDEX_PLAYER;
  570. }
  571. // [Paril-KEX] called when finished to set time until
  572. // we're allowed to switch to fire again
  573. inline void Weapon_SetFinished(edict_t *ent)
  574. {
  575. ent->client->weapon_fire_finished = level.time + Weapon_AnimationTime(ent);
  576. }
  577. inline bool Weapon_HandleDropping(edict_t *ent, int FRAME_DEACTIVATE_LAST)
  578. {
  579. if (ent->client->weaponstate == WEAPON_DROPPING)
  580. {
  581. if (ent->client->weapon_think_time <= level.time)
  582. {
  583. if (ent->client->ps.gunframe == FRAME_DEACTIVATE_LAST)
  584. {
  585. ChangeWeapon(ent);
  586. return true;
  587. }
  588. else if ((FRAME_DEACTIVATE_LAST - ent->client->ps.gunframe) == 4)
  589. {
  590. ent->client->anim_priority = ANIM_ATTACK | ANIM_REVERSED;
  591. if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
  592. {
  593. ent->s.frame = FRAME_crpain4 + 1;
  594. ent->client->anim_end = FRAME_crpain1;
  595. }
  596. else
  597. {
  598. ent->s.frame = FRAME_pain304 + 1;
  599. ent->client->anim_end = FRAME_pain301;
  600. }
  601. ent->client->anim_time = 0_ms;
  602. }
  603. ent->client->ps.gunframe++;
  604. ent->client->weapon_think_time = level.time + Weapon_AnimationTime(ent);
  605. }
  606. return true;
  607. }
  608. return false;
  609. }
  610. inline bool Weapon_HandleActivating(edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_IDLE_FIRST)
  611. {
  612. if (ent->client->weaponstate == WEAPON_ACTIVATING)
  613. {
  614. if (ent->client->weapon_think_time <= level.time || g_instant_weapon_switch->integer)
  615. {
  616. ent->client->weapon_think_time = level.time + Weapon_AnimationTime(ent);
  617. if (ent->client->ps.gunframe == FRAME_ACTIVATE_LAST || g_instant_weapon_switch->integer)
  618. {
  619. ent->client->weaponstate = WEAPON_READY;
  620. ent->client->ps.gunframe = FRAME_IDLE_FIRST;
  621. ent->client->weapon_fire_buffered = false;
  622. if (!g_instant_weapon_switch->integer)
  623. Weapon_SetFinished(ent);
  624. else
  625. ent->client->weapon_fire_finished = 0_ms;
  626. return true;
  627. }
  628. ent->client->ps.gunframe++;
  629. return true;
  630. }
  631. }
  632. return false;
  633. }
  634. inline bool Weapon_HandleNewWeapon(edict_t *ent, int FRAME_DEACTIVATE_FIRST, int FRAME_DEACTIVATE_LAST)
  635. {
  636. bool is_holstering = false;
  637. if (!g_instant_weapon_switch->integer)
  638. is_holstering = ((ent->client->latched_buttons | ent->client->buttons) & BUTTON_HOLSTER);
  639. if ((ent->client->newweapon || is_holstering) && (ent->client->weaponstate != WEAPON_FIRING))
  640. {
  641. if (g_instant_weapon_switch->integer || ent->client->weapon_think_time <= level.time)
  642. {
  643. if (!ent->client->newweapon)
  644. ent->client->newweapon = ent->client->pers.weapon;
  645. ent->client->weaponstate = WEAPON_DROPPING;
  646. if (g_instant_weapon_switch->integer)
  647. {
  648. ChangeWeapon(ent);
  649. return true;
  650. }
  651. ent->client->ps.gunframe = FRAME_DEACTIVATE_FIRST;
  652. if ((FRAME_DEACTIVATE_LAST - FRAME_DEACTIVATE_FIRST) < 4)
  653. {
  654. ent->client->anim_priority = ANIM_ATTACK | ANIM_REVERSED;
  655. if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
  656. {
  657. ent->s.frame = FRAME_crpain4 + 1;
  658. ent->client->anim_end = FRAME_crpain1;
  659. }
  660. else
  661. {
  662. ent->s.frame = FRAME_pain304 + 1;
  663. ent->client->anim_end = FRAME_pain301;
  664. }
  665. ent->client->anim_time = 0_ms;
  666. }
  667. ent->client->weapon_think_time = level.time + Weapon_AnimationTime(ent);
  668. }
  669. return true;
  670. }
  671. return false;
  672. }
  673. enum weapon_ready_state_t
  674. {
  675. READY_NONE,
  676. READY_CHANGING,
  677. READY_FIRING
  678. };
  679. inline weapon_ready_state_t Weapon_HandleReady(edict_t *ent, int FRAME_FIRE_FIRST, int FRAME_IDLE_FIRST, int FRAME_IDLE_LAST, const int *pause_frames)
  680. {
  681. if (ent->client->weaponstate == WEAPON_READY)
  682. {
  683. bool request_firing = ent->client->weapon_fire_buffered || ((ent->client->latched_buttons | ent->client->buttons) & BUTTON_ATTACK);
  684. if (request_firing && ent->client->weapon_fire_finished <= level.time)
  685. {
  686. ent->client->latched_buttons &= ~BUTTON_ATTACK;
  687. ent->client->weapon_think_time = level.time;
  688. if ((!ent->client->pers.weapon->ammo) ||
  689. (ent->client->pers.inventory[ent->client->pers.weapon->ammo] >= ent->client->pers.weapon->quantity))
  690. {
  691. ent->client->weaponstate = WEAPON_FIRING;
  692. ent->client->last_firing_time = level.time + COOP_DAMAGE_FIRING_TIME;
  693. return READY_FIRING;
  694. }
  695. else
  696. {
  697. NoAmmoWeaponChange(ent, true);
  698. return READY_CHANGING;
  699. }
  700. }
  701. else if (ent->client->weapon_think_time <= level.time)
  702. {
  703. ent->client->weapon_think_time = level.time + Weapon_AnimationTime(ent);
  704. if (ent->client->ps.gunframe == FRAME_IDLE_LAST)
  705. {
  706. ent->client->ps.gunframe = FRAME_IDLE_FIRST;
  707. return READY_CHANGING;
  708. }
  709. if (pause_frames)
  710. for (int n = 0; pause_frames[n]; n++)
  711. if (ent->client->ps.gunframe == pause_frames[n])
  712. if (irandom(16))
  713. return READY_CHANGING;
  714. ent->client->ps.gunframe++;
  715. return READY_CHANGING;
  716. }
  717. }
  718. return READY_NONE;
  719. }
  720. inline void Weapon_HandleFiring(edict_t *ent, int32_t FRAME_IDLE_FIRST, std::function<void()> fire_handler)
  721. {
  722. Weapon_SetFinished(ent);
  723. if (ent->client->weapon_fire_buffered)
  724. {
  725. ent->client->buttons |= BUTTON_ATTACK;
  726. ent->client->weapon_fire_buffered = false;
  727. }
  728. fire_handler();
  729. if (ent->client->ps.gunframe == FRAME_IDLE_FIRST)
  730. {
  731. ent->client->weaponstate = WEAPON_READY;
  732. ent->client->weapon_fire_buffered = false;
  733. }
  734. ent->client->weapon_think_time = level.time + Weapon_AnimationTime(ent);
  735. }
  736. void Weapon_Generic(edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, int FRAME_IDLE_LAST, int FRAME_DEACTIVATE_LAST, const int *pause_frames, const int *fire_frames, void (*fire)(edict_t *ent))
  737. {
  738. int FRAME_FIRE_FIRST = (FRAME_ACTIVATE_LAST + 1);
  739. int FRAME_IDLE_FIRST = (FRAME_FIRE_LAST + 1);
  740. int FRAME_DEACTIVATE_FIRST = (FRAME_IDLE_LAST + 1);
  741. if (!Weapon_CanAnimate(ent))
  742. return;
  743. if (Weapon_HandleDropping(ent, FRAME_DEACTIVATE_LAST))
  744. return;
  745. else if (Weapon_HandleActivating(ent, FRAME_ACTIVATE_LAST, FRAME_IDLE_FIRST))
  746. return;
  747. else if (Weapon_HandleNewWeapon(ent, FRAME_DEACTIVATE_FIRST, FRAME_DEACTIVATE_LAST))
  748. return;
  749. else if (auto state = Weapon_HandleReady(ent, FRAME_FIRE_FIRST, FRAME_IDLE_FIRST, FRAME_IDLE_LAST, pause_frames))
  750. {
  751. if (state == READY_FIRING)
  752. {
  753. ent->client->ps.gunframe = FRAME_FIRE_FIRST;
  754. ent->client->weapon_fire_buffered = false;
  755. if (ent->client->weapon_thunk)
  756. ent->client->weapon_think_time += FRAME_TIME_S;
  757. ent->client->weapon_think_time += Weapon_AnimationTime(ent);
  758. Weapon_SetFinished(ent);
  759. for (int n = 0; fire_frames[n]; n++)
  760. {
  761. if (ent->client->ps.gunframe == fire_frames[n])
  762. {
  763. Weapon_PowerupSound(ent);
  764. fire(ent);
  765. break;
  766. }
  767. }
  768. // start the animation
  769. ent->client->anim_priority = ANIM_ATTACK;
  770. if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
  771. {
  772. ent->s.frame = FRAME_crattak1 - 1;
  773. ent->client->anim_end = FRAME_crattak9;
  774. }
  775. else
  776. {
  777. ent->s.frame = FRAME_attack1 - 1;
  778. ent->client->anim_end = FRAME_attack8;
  779. }
  780. ent->client->anim_time = 0_ms;
  781. }
  782. return;
  783. }
  784. if (ent->client->weaponstate == WEAPON_FIRING && ent->client->weapon_think_time <= level.time)
  785. {
  786. ent->client->last_firing_time = level.time + COOP_DAMAGE_FIRING_TIME;
  787. ent->client->ps.gunframe++;
  788. Weapon_HandleFiring(ent, FRAME_IDLE_FIRST, [&]() {
  789. for (int n = 0; fire_frames[n]; n++)
  790. {
  791. if (ent->client->ps.gunframe == fire_frames[n])
  792. {
  793. Weapon_PowerupSound(ent);
  794. fire(ent);
  795. break;
  796. }
  797. }
  798. });
  799. }
  800. }
  801. void Weapon_Repeating(edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, int FRAME_IDLE_LAST, int FRAME_DEACTIVATE_LAST, const int *pause_frames, void (*fire)(edict_t *ent))
  802. {
  803. int FRAME_FIRE_FIRST = (FRAME_ACTIVATE_LAST + 1);
  804. int FRAME_IDLE_FIRST = (FRAME_FIRE_LAST + 1);
  805. int FRAME_DEACTIVATE_FIRST = (FRAME_IDLE_LAST + 1);
  806. if (!Weapon_CanAnimate(ent))
  807. return;
  808. if (Weapon_HandleDropping(ent, FRAME_DEACTIVATE_LAST))
  809. return;
  810. else if (Weapon_HandleActivating(ent, FRAME_ACTIVATE_LAST, FRAME_IDLE_FIRST))
  811. return;
  812. else if (Weapon_HandleNewWeapon(ent, FRAME_DEACTIVATE_FIRST, FRAME_DEACTIVATE_LAST))
  813. return;
  814. else if (Weapon_HandleReady(ent, FRAME_FIRE_FIRST, FRAME_IDLE_FIRST, FRAME_IDLE_LAST, pause_frames) == READY_CHANGING)
  815. return;
  816. if (ent->client->weaponstate == WEAPON_FIRING && ent->client->weapon_think_time <= level.time)
  817. {
  818. ent->client->last_firing_time = level.time + COOP_DAMAGE_FIRING_TIME;
  819. Weapon_HandleFiring(ent, FRAME_IDLE_FIRST, [&]() { fire(ent); });
  820. if (ent->client->weapon_thunk)
  821. ent->client->weapon_think_time += FRAME_TIME_S;
  822. }
  823. }
  824. /*
  825. ======================================================================
  826. GRENADE
  827. ======================================================================
  828. */
  829. void weapon_grenade_fire(edict_t *ent, bool held)
  830. {
  831. int damage = 125;
  832. int speed;
  833. float radius;
  834. radius = (float) (damage + 40);
  835. if (is_quad)
  836. damage *= damage_multiplier;
  837. vec3_t start, dir;
  838. // Paril: kill sideways angle on grenades
  839. // limit upwards angle so you don't throw behind you
  840. P_ProjectSource(ent, { max(-62.5f, ent->client->v_angle[0]), ent->client->v_angle[1], ent->client->v_angle[2] }, { 2, 0, -14 }, start, dir);
  841. gtime_t timer = ent->client->grenade_time - level.time;
  842. speed = (int) (ent->health <= 0 ? GRENADE_MINSPEED : min(GRENADE_MINSPEED + (GRENADE_TIMER - timer).seconds() * ((GRENADE_MAXSPEED - GRENADE_MINSPEED) / GRENADE_TIMER.seconds()), GRENADE_MAXSPEED));
  843. ent->client->grenade_time = 0_ms;
  844. fire_grenade2(ent, start, dir, damage, speed, timer, radius, held);
  845. G_RemoveAmmo(ent, 1);
  846. }
  847. void Throw_Generic(edict_t *ent, int FRAME_FIRE_LAST, int FRAME_IDLE_LAST, int FRAME_PRIME_SOUND,
  848. const char *prime_sound,
  849. int FRAME_THROW_HOLD, int FRAME_THROW_FIRE, const int *pause_frames, int EXPLODE,
  850. const char *primed_sound,
  851. void (*fire)(edict_t *ent, bool held), bool extra_idle_frame)
  852. {
  853. // when we die, just toss what we had in our hands.
  854. if (ent->health <= 0)
  855. {
  856. fire(ent, true);
  857. return;
  858. }
  859. int n;
  860. int FRAME_IDLE_FIRST = (FRAME_FIRE_LAST + 1);
  861. if (ent->client->newweapon && (ent->client->weaponstate == WEAPON_READY))
  862. {
  863. if (ent->client->weapon_think_time <= level.time)
  864. {
  865. ChangeWeapon(ent);
  866. ent->client->weapon_think_time = level.time + Weapon_AnimationTime(ent);
  867. }
  868. return;
  869. }
  870. if (ent->client->weaponstate == WEAPON_ACTIVATING)
  871. {
  872. if (ent->client->weapon_think_time <= level.time)
  873. {
  874. ent->client->weaponstate = WEAPON_READY;
  875. if (!extra_idle_frame)
  876. ent->client->ps.gunframe = FRAME_IDLE_FIRST;
  877. else
  878. ent->client->ps.gunframe = FRAME_IDLE_LAST + 1;
  879. ent->client->weapon_think_time = level.time + Weapon_AnimationTime(ent);
  880. Weapon_SetFinished(ent);
  881. }
  882. return;
  883. }
  884. if (ent->client->weaponstate == WEAPON_READY)
  885. {
  886. bool request_firing = ent->client->weapon_fire_buffered || ((ent->client->latched_buttons | ent->client->buttons) & BUTTON_ATTACK);
  887. if (request_firing && ent->client->weapon_fire_finished <= level.time)
  888. {
  889. ent->client->latched_buttons &= ~BUTTON_ATTACK;
  890. if (ent->client->pers.inventory[ent->client->pers.weapon->ammo])
  891. {
  892. ent->client->ps.gunframe = 1;
  893. ent->client->weaponstate = WEAPON_FIRING;
  894. ent->client->grenade_time = 0_ms;
  895. ent->client->weapon_think_time = level.time + Weapon_AnimationTime(ent);
  896. }
  897. else
  898. NoAmmoWeaponChange(ent, true);
  899. return;
  900. }
  901. else if (ent->client->weapon_think_time <= level.time)
  902. {
  903. ent->client->weapon_think_time = level.time + Weapon_AnimationTime(ent);
  904. if (ent->client->ps.gunframe >= FRAME_IDLE_LAST)
  905. {
  906. ent->client->ps.gunframe = FRAME_IDLE_FIRST;
  907. return;
  908. }
  909. if (pause_frames)
  910. {
  911. for (n = 0; pause_frames[n]; n++)
  912. {
  913. if (ent->client->ps.gunframe == pause_frames[n])
  914. {
  915. if (irandom(16))
  916. return;
  917. }
  918. }
  919. }
  920. ent->client->ps.gunframe++;
  921. }
  922. return;
  923. }
  924. if (ent->client->weaponstate == WEAPON_FIRING)
  925. {
  926. ent->client->last_firing_time = level.time + COOP_DAMAGE_FIRING_TIME;
  927. if (ent->client->weapon_think_time <= level.time)
  928. {
  929. if (prime_sound && ent->client->ps.gunframe == FRAME_PRIME_SOUND)
  930. gi.sound(ent, CHAN_WEAPON, gi.soundindex(prime_sound), 1, ATTN_NORM, 0);
  931. // [Paril-KEX] dualfire/time accel
  932. gtime_t grenade_wait_time = 1_sec;
  933. if (CTFApplyHaste(ent))
  934. grenade_wait_time *= 0.5f;
  935. if (is_quadfire)
  936. grenade_wait_time *= 0.5f;
  937. if (ent->client->ps.gunframe == FRAME_THROW_HOLD)
  938. {
  939. if (!ent->client->grenade_time && !ent->client->grenade_finished_time)
  940. ent->client->grenade_time = level.time + GRENADE_TIMER + 200_ms;
  941. if (primed_sound && !ent->client->grenade_blew_up)
  942. ent->client->weapon_sound = gi.soundindex(primed_sound);
  943. // they waited too long, detonate it in their hand
  944. if (EXPLODE && !ent->client->grenade_blew_up && level.time >= ent->client->grenade_time)
  945. {
  946. Weapon_PowerupSound(ent);
  947. ent->client->weapon_sound = 0;
  948. fire(ent, true);
  949. ent->client->grenade_blew_up = true;
  950. ent->client->grenade_finished_time = level.time + grenade_wait_time;
  951. }
  952. if (ent->client->buttons & BUTTON_ATTACK)
  953. {
  954. ent->client->weapon_think_time = level.time + 1_ms;
  955. return;
  956. }
  957. if (ent->client->grenade_blew_up)
  958. {
  959. if (level.time >= ent->client->grenade_finished_time)
  960. {
  961. ent->client->ps.gunframe = FRAME_FIRE_LAST;
  962. ent->client->grenade_blew_up = false;
  963. ent->client->weapon_think_time = level.time + Weapon_AnimationTime(ent);
  964. }
  965. else
  966. {
  967. return;
  968. }
  969. }
  970. else
  971. {
  972. ent->client->ps.gunframe++;
  973. Weapon_PowerupSound(ent);
  974. ent->client->weapon_sound = 0;
  975. fire(ent, false);
  976. if (!EXPLODE || !ent->client->grenade_blew_up)
  977. ent->client->grenade_finished_time = level.time + grenade_wait_time;
  978. if (!ent->deadflag && ent->s.modelindex == MODELINDEX_PLAYER && ent->health > 0) // VWep animations screw up corpses
  979. {
  980. if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
  981. {
  982. ent->client->anim_priority = ANIM_ATTACK;
  983. ent->s.frame = FRAME_crattak1 - 1;
  984. ent->client->anim_end = FRAME_crattak3;
  985. }
  986. else
  987. {
  988. ent->client->anim_priority = ANIM_ATTACK | ANIM_REVERSED;
  989. ent->s.frame = FRAME_wave08;
  990. ent->client->anim_end = FRAME_wave01;
  991. }
  992. ent->client->anim_time = 0_ms;
  993. }
  994. }
  995. }
  996. ent->client->weapon_think_time = level.time + Weapon_AnimationTime(ent);
  997. if ((ent->client->ps.gunframe == FRAME_FIRE_LAST) && (level.time < ent->client->grenade_finished_time))
  998. return;
  999. ent->client->ps.gunframe++;
  1000. if (ent->client->ps.gunframe == FRAME_IDLE_FIRST)
  1001. {
  1002. ent->client->grenade_finished_time = 0_ms;
  1003. ent->client->weaponstate = WEAPON_READY;
  1004. ent->client->weapon_fire_buffered = false;
  1005. Weapon_SetFinished(ent);
  1006. if (extra_idle_frame)
  1007. ent->client->ps.gunframe = FRAME_IDLE_LAST + 1;
  1008. // Paril: if we ran out of the throwable, switch
  1009. // so we don't appear to be holding one that we
  1010. // can't throw
  1011. if (!ent->client->pers.inventory[ent->client->pers.weapon->ammo])
  1012. {
  1013. NoAmmoWeaponChange(ent, false);
  1014. ChangeWeapon(ent);
  1015. }
  1016. }
  1017. }
  1018. }
  1019. }
  1020. void Weapon_Grenade(edict_t *ent)
  1021. {
  1022. constexpr int pause_frames[] = { 29, 34, 39, 48, 0 };
  1023. Throw_Generic(ent, 15, 48, 5, "weapons/hgrena1b.wav", 11, 12, pause_frames, true, "weapons/hgrenc1b.wav", weapon_grenade_fire, true);
  1024. // [Paril-KEX] skip the duped frame
  1025. if (ent->client->ps.gunframe == 1)
  1026. ent->client->ps.gunframe = 2;
  1027. }
  1028. /*
  1029. ======================================================================
  1030. GRENADE LAUNCHER
  1031. ======================================================================
  1032. */
  1033. void weapon_grenadelauncher_fire(edict_t *ent)
  1034. {
  1035. int damage = 120;
  1036. float radius;
  1037. radius = (float) (damage + 40);
  1038. if (is_quad)
  1039. damage *= damage_multiplier;
  1040. vec3_t start, dir;
  1041. // Paril: kill sideways angle on grenades
  1042. // limit upwards angle so you don't fire it behind you
  1043. P_ProjectSource(ent, { max(-62.5f, ent->client->v_angle[0]), ent->client->v_angle[1], ent->client->v_angle[2] }, { 8, 0, -8 }, start, dir);
  1044. P_AddWeaponKick(ent, ent->client->v_forward * -2, { -1.f, 0.f, 0.f });
  1045. fire_grenade(ent, start, dir, damage, 600, 2.5_sec, radius, (crandom_open() * 10.0f), (200 + crandom_open() * 10.0f), false);
  1046. gi.WriteByte(svc_muzzleflash);
  1047. gi.WriteEntity(ent);
  1048. gi.WriteByte(MZ_GRENADE | is_silenced);
  1049. gi.multicast(ent->s.origin, MULTICAST_PVS, false);
  1050. PlayerNoise(ent, start, PNOISE_WEAPON);
  1051. G_RemoveAmmo(ent);
  1052. }
  1053. void Weapon_GrenadeLauncher(edict_t *ent)
  1054. {
  1055. constexpr int pause_frames[] = { 34, 51, 59, 0 };
  1056. constexpr int fire_frames[] = { 6, 0 };
  1057. Weapon_Generic(ent, 5, 16, 59, 64, pause_frames, fire_frames, weapon_grenadelauncher_fire);
  1058. }
  1059. /*
  1060. ======================================================================
  1061. ROCKET
  1062. ======================================================================
  1063. */
  1064. void Weapon_RocketLauncher_Fire(edict_t *ent)
  1065. {
  1066. int damage;
  1067. float damage_radius;
  1068. int radius_damage;
  1069. damage = irandom(100, 120);
  1070. radius_damage = 120;
  1071. damage_radius = 120;
  1072. if (is_quad)
  1073. {
  1074. damage *= damage_multiplier;
  1075. radius_damage *= damage_multiplier;
  1076. }
  1077. vec3_t start, dir;
  1078. P_ProjectSource(ent, ent->client->v_angle, { 8, 8, -8 }, start, dir);
  1079. fire_rocket(ent, start, dir, damage, 650, damage_radius, radius_damage);
  1080. P_AddWeaponKick(ent, ent->client->v_forward * -2, { -1.f, 0.f, 0.f });
  1081. // send muzzle flash
  1082. gi.WriteByte(svc_muzzleflash);
  1083. gi.WriteEntity(ent);
  1084. gi.WriteByte(MZ_ROCKET | is_silenced);
  1085. gi.multicast(ent->s.origin, MULTICAST_PVS, false);
  1086. PlayerNoise(ent, start, PNOISE_WEAPON);
  1087. G_RemoveAmmo(ent);
  1088. }
  1089. void Weapon_RocketLauncher(edict_t *ent)
  1090. {
  1091. constexpr int pause_frames[] = { 25, 33, 42, 50, 0 };
  1092. constexpr int fire_frames[] = { 5, 0 };
  1093. Weapon_Generic(ent, 4, 12, 50, 54, pause_frames, fire_frames, Weapon_RocketLauncher_Fire);
  1094. }
  1095. /*
  1096. ======================================================================
  1097. BLASTER / HYPERBLASTER
  1098. ======================================================================
  1099. */
  1100. void Blaster_Fire(edict_t *ent, const vec3_t &g_offset, int damage, bool hyper, effects_t effect)
  1101. {
  1102. if (is_quad)
  1103. damage *= damage_multiplier;
  1104. vec3_t start, dir;
  1105. P_ProjectSource(ent, ent->client->v_angle, vec3_t{ 24, 8, -8 } + g_offset, start, dir);
  1106. if (hyper)
  1107. P_AddWeaponKick(ent, ent->client->v_forward * -2, { crandom() * 0.7f, crandom() * 0.7f, crandom() * 0.7f });
  1108. else
  1109. P_AddWeaponKick(ent, ent->client->v_forward * -2, { -1.f, 0.f, 0.f });
  1110. // let the regular blaster projectiles travel a bit faster because it is a completely useless gun
  1111. int speed = hyper ? 1000 : 1500;
  1112. fire_blaster(ent, start, dir, damage, speed, effect, hyper ? MOD_HYPERBLASTER : MOD_BLASTER);
  1113. // send muzzle flash
  1114. gi.WriteByte(svc_muzzleflash);
  1115. gi.WriteEntity(ent);
  1116. if (hyper)
  1117. gi.WriteByte(MZ_HYPERBLASTER | is_silenced);
  1118. else
  1119. gi.WriteByte(MZ_BLASTER | is_silenced);
  1120. gi.multicast(ent->s.origin, MULTICAST_PVS, false);
  1121. PlayerNoise(ent, start, PNOISE_WEAPON);
  1122. }
  1123. void Weapon_Blaster_Fire(edict_t *ent)
  1124. {
  1125. // give the blaster 15 across the board instead of just in dm
  1126. int damage = 15;
  1127. Blaster_Fire(ent, vec3_origin, damage, false, EF_BLASTER);
  1128. }
  1129. void Weapon_Blaster(edict_t *ent)
  1130. {
  1131. constexpr int pause_frames[] = { 19, 32, 0 };
  1132. constexpr int fire_frames[] = { 5, 0 };
  1133. Weapon_Generic(ent, 4, 8, 52, 55, pause_frames, fire_frames, Weapon_Blaster_Fire);
  1134. }
  1135. void Weapon_HyperBlaster_Fire(edict_t *ent)
  1136. {
  1137. float rotation;
  1138. vec3_t offset;
  1139. int damage;
  1140. // start on frame 6
  1141. if (ent->client->ps.gunframe > 20)
  1142. ent->client->ps.gunframe = 6;
  1143. else
  1144. ent->client->ps.gunframe++;
  1145. // if we reached end of loop, have ammo & holding attack, reset loop
  1146. // otherwise play wind down
  1147. if (ent->client->ps.gunframe == 12)
  1148. {
  1149. if (ent->client->pers.inventory[ent->client->pers.weapon->ammo] && (ent->client->buttons & BUTTON_ATTACK))
  1150. ent->client->ps.gunframe = 6;
  1151. else
  1152. gi.sound(ent, CHAN_AUTO, gi.soundindex("weapons/hyprbd1a.wav"), 1, ATTN_NORM, 0);
  1153. }
  1154. // play weapon sound for firing loop
  1155. if (ent->client->ps.gunframe >= 6 && ent->client->ps.gunframe <= 11)
  1156. ent->client->weapon_sound = gi.soundindex("weapons/hyprbl1a.wav");
  1157. else
  1158. ent->client->weapon_sound = 0;
  1159. // fire frames
  1160. bool request_firing = ent->client->weapon_fire_buffered || (ent->client->buttons & BUTTON_ATTACK);
  1161. if (request_firing)
  1162. {
  1163. if (ent->client->ps.gunframe >= 6 && ent->client->ps.gunframe <= 11)
  1164. {
  1165. ent->client->weapon_fire_buffered = false;
  1166. if (!ent->client->pers.inventory[ent->client->pers.weapon->ammo])
  1167. {
  1168. NoAmmoWeaponChange(ent, true);
  1169. return;
  1170. }
  1171. rotation = (ent->client->ps.gunframe - 5) * 2 * PIf / 6;
  1172. offset[0] = -4 * sinf(rotation);
  1173. offset[2] = 0;
  1174. offset[1] = 4 * cosf(rotation);
  1175. if (deathmatch->integer)
  1176. damage = 15;
  1177. else
  1178. damage = 20;
  1179. Blaster_Fire(ent, offset, damage, true, (ent->client->ps.gunframe % 4) ? EF_NONE : EF_HYPERBLASTER);
  1180. Weapon_PowerupSound(ent);
  1181. G_RemoveAmmo(ent);
  1182. ent->client->anim_priority = ANIM_ATTACK;
  1183. if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
  1184. {
  1185. ent->s.frame = FRAME_crattak1 - (int) (frandom() + 0.25f);
  1186. ent->client->anim_end = FRAME_crattak9;
  1187. }
  1188. else
  1189. {
  1190. ent->s.frame = FRAME_attack1 - (int) (frandom() + 0.25f);
  1191. ent->client->anim_end = FRAME_attack8;
  1192. }
  1193. ent->client->anim_time = 0_ms;
  1194. }
  1195. }
  1196. }
  1197. void Weapon_HyperBlaster(edict_t *ent)
  1198. {
  1199. constexpr int pause_frames[] = { 0 };
  1200. Weapon_Repeating(ent, 5, 20, 49, 53, pause_frames, Weapon_HyperBlaster_Fire);
  1201. }
  1202. /*
  1203. ======================================================================
  1204. MACHINEGUN / CHAINGUN
  1205. ======================================================================
  1206. */
  1207. void Machinegun_Fire(edict_t *ent)
  1208. {
  1209. int i;
  1210. int damage = 8;
  1211. int kick = 2;
  1212. if (!(ent->client->buttons & BUTTON_ATTACK))
  1213. {
  1214. ent->client->machinegun_shots = 0;
  1215. ent->client->ps.gunframe = 6;
  1216. return;
  1217. }
  1218. if (ent->client->ps.gunframe == 4)
  1219. ent->client->ps.gunframe = 5;
  1220. else
  1221. ent->client->ps.gunframe = 4;
  1222. if (ent->client->pers.inventory[ent->client->pers.weapon->ammo] < 1)
  1223. {
  1224. ent->client->ps.gunframe = 6;
  1225. NoAmmoWeaponChange(ent, true);
  1226. return;
  1227. }
  1228. if (is_quad)
  1229. {
  1230. damage *= damage_multiplier;
  1231. kick *= damage_multiplier;
  1232. }
  1233. vec3_t kick_origin {}, kick_angles {};
  1234. for (i = 0; i < 3; i++)
  1235. {
  1236. kick_origin[i] = crandom() * 0.35f;
  1237. kick_angles[i] = crandom() * 0.7f;
  1238. }
  1239. //kick_angles[0] = ent->client->machinegun_shots * -1.5f;
  1240. P_AddWeaponKick(ent, kick_origin, kick_angles);
  1241. // raise the gun as it is firing
  1242. // [Paril-KEX] disabled as this is a bit hard to do with high
  1243. // tickrate, but it also just sucks in general.
  1244. /*if (!deathmatch->integer)
  1245. {
  1246. ent->client->machinegun_shots++;
  1247. if (ent->client->machinegun_shots > 9)
  1248. ent->client->machinegun_shots = 9;
  1249. }*/
  1250. // get start / end positions
  1251. vec3_t start, dir;
  1252. // Paril: kill sideways angle on hitscan
  1253. P_ProjectSource(ent, ent->client->v_angle, { 0, 0, -8 }, start, dir);
  1254. G_LagCompensate(ent, start, dir);
  1255. fire_bullet(ent, start, dir, damage, kick, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MOD_MACHINEGUN);
  1256. G_UnLagCompensate();
  1257. Weapon_PowerupSound(ent);
  1258. gi.WriteByte(svc_muzzleflash);
  1259. gi.WriteEntity(ent);
  1260. gi.WriteByte(MZ_MACHINEGUN | is_silenced);
  1261. gi.multicast(ent->s.origin, MULTICAST_PVS, false);
  1262. PlayerNoise(ent, start, PNOISE_WEAPON);
  1263. G_RemoveAmmo(ent);
  1264. ent->client->anim_priority = ANIM_ATTACK;
  1265. if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
  1266. {
  1267. ent->s.frame = FRAME_crattak1 - (int) (frandom() + 0.25f);
  1268. ent->client->anim_end = FRAME_crattak9;
  1269. }
  1270. else
  1271. {
  1272. ent->s.frame = FRAME_attack1 - (int) (frandom() + 0.25f);
  1273. ent->client->anim_end = FRAME_attack8;
  1274. }
  1275. ent->client->anim_time = 0_ms;
  1276. }
  1277. void Weapon_Machinegun(edict_t *ent)
  1278. {
  1279. constexpr int pause_frames[] = { 23, 45, 0 };
  1280. Weapon_Repeating(ent, 3, 5, 45, 49, pause_frames, Machinegun_Fire);
  1281. }
  1282. void Chaingun_Fire(edict_t *ent)
  1283. {
  1284. int i;
  1285. int shots;
  1286. float r, u;
  1287. int damage;
  1288. int kick = 2;
  1289. if (deathmatch->integer)
  1290. damage = 6;
  1291. else
  1292. damage = 8;
  1293. if (ent->client->ps.gunframe > 31)
  1294. {
  1295. ent->client->ps.gunframe = 5;
  1296. gi.sound(ent, CHAN_AUTO, gi.soundindex("weapons/chngnu1a.wav"), 1, ATTN_IDLE, 0);
  1297. }
  1298. else if ((ent->client->ps.gunframe == 14) && !(ent->client->buttons & BUTTON_ATTACK))
  1299. {
  1300. ent->client->ps.gunframe = 32;
  1301. ent->client->weapon_sound = 0;
  1302. return;
  1303. }
  1304. else if ((ent->client->ps.gunframe == 21) && (ent->client->buttons & BUTTON_ATTACK) && ent->client->pers.inventory[ent->client->pers.weapon->ammo])
  1305. {
  1306. ent->client->ps.gunframe = 15;
  1307. }
  1308. else
  1309. {
  1310. ent->client->ps.gunframe++;
  1311. }
  1312. if (ent->client->ps.gunframe == 22)
  1313. {
  1314. ent->client->weapon_sound = 0;
  1315. gi.sound(ent, CHAN_AUTO, gi.soundindex("weapons/chngnd1a.wav"), 1, ATTN_IDLE, 0);
  1316. }
  1317. if (ent->client->ps.gunframe < 5 || ent->client->ps.gunframe > 21)
  1318. return;
  1319. ent->client->weapon_sound = gi.soundindex("weapons/chngnl1a.wav");
  1320. ent->client->anim_priority = ANIM_ATTACK;
  1321. if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
  1322. {
  1323. ent->s.frame = FRAME_crattak1 - (ent->client->ps.gunframe & 1);
  1324. ent->client->anim_end = FRAME_crattak9;
  1325. }
  1326. else
  1327. {
  1328. ent->s.frame = FRAME_attack1 - (ent->client->ps.gunframe & 1);
  1329. ent->client->anim_end = FRAME_attack8;
  1330. }
  1331. ent->client->anim_time = 0_ms;
  1332. if (ent->client->ps.gunframe <= 9)
  1333. shots = 1;
  1334. else if (ent->client->ps.gunframe <= 14)
  1335. {
  1336. if (ent->client->buttons & BUTTON_ATTACK)
  1337. shots = 2;
  1338. else
  1339. shots = 1;
  1340. }
  1341. else
  1342. shots = 3;
  1343. if (ent->client->pers.inventory[ent->client->pers.weapon->ammo] < shots)
  1344. shots = ent->client->pers.inventory[ent->client->pers.weapon->ammo];
  1345. if (!shots)
  1346. {
  1347. NoAmmoWeaponChange(ent, true);
  1348. return;
  1349. }
  1350. if (is_quad)
  1351. {
  1352. damage *= damage_multiplier;
  1353. kick *= damage_multiplier;
  1354. }
  1355. vec3_t kick_origin {}, kick_angles {};
  1356. for (i = 0; i < 3; i++)
  1357. {
  1358. kick_origin[i] = crandom() * 0.35f;
  1359. kick_angles[i] = crandom() * (0.5f + (shots * 0.15f));
  1360. }
  1361. P_AddWeaponKick(ent, kick_origin, kick_angles);
  1362. vec3_t start, dir;
  1363. P_ProjectSource(ent, ent->client->v_angle, { 0, 0, -8 }, start, dir);
  1364. G_LagCompensate(ent, start, dir);
  1365. for (i = 0; i < shots; i++)
  1366. {
  1367. // get start / end positions
  1368. // Paril: kill sideways angle on hitscan
  1369. r = crandom() * 4;
  1370. u = crandom() * 4;
  1371. P_ProjectSource(ent, ent->client->v_angle, { 0, r, u + -8 }, start, dir);
  1372. fire_bullet(ent, start, dir, damage, kick, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MOD_CHAINGUN);
  1373. }
  1374. G_UnLagCompensate();
  1375. Weapon_PowerupSound(ent);
  1376. // send muzzle flash
  1377. gi.WriteByte(svc_muzzleflash);
  1378. gi.WriteEntity(ent);
  1379. gi.WriteByte((MZ_CHAINGUN1 + shots - 1) | is_silenced);
  1380. gi.multicast(ent->s.origin, MULTICAST_PVS, false);
  1381. PlayerNoise(ent, start, PNOISE_WEAPON);
  1382. G_RemoveAmmo(ent, shots);
  1383. }
  1384. void Weapon_Chaingun(edict_t *ent)
  1385. {
  1386. constexpr int pause_frames[] = { 38, 43, 51, 61, 0 };
  1387. Weapon_Repeating(ent, 4, 31, 61, 64, pause_frames, Chaingun_Fire);
  1388. }
  1389. /*
  1390. ======================================================================
  1391. SHOTGUN / SUPERSHOTGUN
  1392. ======================================================================
  1393. */
  1394. void weapon_shotgun_fire(edict_t *ent)
  1395. {
  1396. int damage = 4;
  1397. int kick = 8;
  1398. vec3_t start, dir;
  1399. // Paril: kill sideways angle on hitscan
  1400. P_ProjectSource(ent, ent->client->v_angle, { 0, 0, -8 }, start, dir);
  1401. P_AddWeaponKick(ent, ent->client->v_forward * -2, { -2.f, 0.f, 0.f });
  1402. if (is_quad)
  1403. {
  1404. damage *= damage_multiplier;
  1405. kick *= damage_multiplier;
  1406. }
  1407. G_LagCompensate(ent, start, dir);
  1408. if (deathmatch->integer)
  1409. fire_shotgun(ent, start, dir, damage, kick, 500, 500, DEFAULT_DEATHMATCH_SHOTGUN_COUNT, MOD_SHOTGUN);
  1410. else
  1411. fire_shotgun(ent, start, dir, damage, kick, 500, 500, DEFAULT_SHOTGUN_COUNT, MOD_SHOTGUN);
  1412. G_UnLagCompensate();
  1413. // send muzzle flash
  1414. gi.WriteByte(svc_muzzleflash);
  1415. gi.WriteEntity(ent);
  1416. gi.WriteByte(MZ_SHOTGUN | is_silenced);
  1417. gi.multicast(ent->s.origin, MULTICAST_PVS, false);
  1418. PlayerNoise(ent, start, PNOISE_WEAPON);
  1419. G_RemoveAmmo(ent);
  1420. }
  1421. void Weapon_Shotgun(edict_t *ent)
  1422. {
  1423. constexpr int pause_frames[] = { 22, 28, 34, 0 };
  1424. constexpr int fire_frames[] = { 8, 0 };
  1425. Weapon_Generic(ent, 7, 18, 36, 39, pause_frames, fire_frames, weapon_shotgun_fire);
  1426. }
  1427. void weapon_supershotgun_fire(edict_t *ent)
  1428. {
  1429. int damage = 6;
  1430. int kick = 12;
  1431. if (is_quad)
  1432. {
  1433. damage *= damage_multiplier;
  1434. kick *= damage_multiplier;
  1435. }
  1436. vec3_t start, dir;
  1437. // Paril: kill sideways angle on hitscan
  1438. P_ProjectSource(ent, ent->client->v_angle, { 0, 0, -8 }, start, dir);
  1439. G_LagCompensate(ent, start, dir);
  1440. vec3_t v;
  1441. v[PITCH] = ent->client->v_angle[PITCH];
  1442. v[YAW] = ent->client->v_angle[YAW] - 5;
  1443. v[ROLL] = ent->client->v_angle[ROLL];
  1444. // Paril: kill sideways angle on hitscan
  1445. P_ProjectSource(ent, v, { 0, 0, -8 }, start, dir);
  1446. fire_shotgun(ent, start, dir, damage, kick, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, DEFAULT_SSHOTGUN_COUNT / 2, MOD_SSHOTGUN);
  1447. v[YAW] = ent->client->v_angle[YAW] + 5;
  1448. P_ProjectSource(ent, v, { 0, 0, -8 }, start, dir);
  1449. fire_shotgun(ent, start, dir, damage, kick, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, DEFAULT_SSHOTGUN_COUNT / 2, MOD_SSHOTGUN);
  1450. G_UnLagCompensate();
  1451. P_AddWeaponKick(ent, ent->client->v_forward * -2, { -2.f, 0.f, 0.f });
  1452. // send muzzle flash
  1453. gi.WriteByte(svc_muzzleflash);
  1454. gi.WriteEntity(ent);
  1455. gi.WriteByte(MZ_SSHOTGUN | is_silenced);
  1456. gi.multicast(ent->s.origin, MULTICAST_PVS, false);
  1457. PlayerNoise(ent, start, PNOISE_WEAPON);
  1458. G_RemoveAmmo(ent);
  1459. }
  1460. void Weapon_SuperShotgun(edict_t *ent)
  1461. {
  1462. constexpr int pause_frames[] = { 29, 42, 57, 0 };
  1463. constexpr int fire_frames[] = { 7, 0 };
  1464. Weapon_Generic(ent, 6, 17, 57, 61, pause_frames, fire_frames, weapon_supershotgun_fire);
  1465. }
  1466. /*
  1467. ======================================================================
  1468. RAILGUN
  1469. ======================================================================
  1470. */
  1471. void weapon_railgun_fire(edict_t *ent)
  1472. {
  1473. int damage, kick;
  1474. // normal damage too extreme for DM
  1475. if (deathmatch->integer)
  1476. {
  1477. damage = 100;
  1478. kick = 200;
  1479. }
  1480. else
  1481. {
  1482. damage = 125;
  1483. kick = 225;
  1484. }
  1485. if (is_quad)
  1486. {
  1487. damage *= damage_multiplier;
  1488. kick *= damage_multiplier;
  1489. }
  1490. vec3_t start, dir;
  1491. P_ProjectSource(ent, ent->client->v_angle, { 0, 7, -8 }, start, dir);
  1492. G_LagCompensate(ent, start, dir);
  1493. fire_rail(ent, start, dir, damage, kick);
  1494. G_UnLagCompensate();
  1495. P_AddWeaponKick(ent, ent->client->v_forward * -3, { -3.f, 0.f, 0.f });
  1496. // send muzzle flash
  1497. gi.WriteByte(svc_muzzleflash);
  1498. gi.WriteEntity(ent);
  1499. gi.WriteByte(MZ_RAILGUN | is_silenced);
  1500. gi.multicast(ent->s.origin, MULTICAST_PVS, false);
  1501. PlayerNoise(ent, start, PNOISE_WEAPON);
  1502. G_RemoveAmmo(ent);
  1503. }
  1504. void Weapon_Railgun(edict_t *ent)
  1505. {
  1506. constexpr int pause_frames[] = { 56, 0 };
  1507. constexpr int fire_frames[] = { 4, 0 };
  1508. Weapon_Generic(ent, 3, 18, 56, 61, pause_frames, fire_frames, weapon_railgun_fire);
  1509. }
  1510. /*
  1511. ======================================================================
  1512. BFG10K
  1513. ======================================================================
  1514. */
  1515. void weapon_bfg_fire(edict_t *ent)
  1516. {
  1517. int damage;
  1518. float damage_radius = 1000;
  1519. if (deathmatch->integer)
  1520. damage = 200;
  1521. else
  1522. damage = 500;
  1523. if (ent->client->ps.gunframe == 9)
  1524. {
  1525. // send muzzle flash
  1526. gi.WriteByte(svc_muzzleflash);
  1527. gi.WriteEntity(ent);
  1528. gi.WriteByte(MZ_BFG | is_silenced);
  1529. gi.multicast(ent->s.origin, MULTICAST_PVS, false);
  1530. PlayerNoise(ent, ent->s.origin, PNOISE_WEAPON);
  1531. return;
  1532. }
  1533. // cells can go down during windup (from power armor hits), so
  1534. // check again and abort firing if we don't have enough now
  1535. if (ent->client->pers.inventory[ent->client->pers.weapon->ammo] < 50)
  1536. return;
  1537. if (is_quad)
  1538. damage *= damage_multiplier;
  1539. vec3_t start, dir;
  1540. P_ProjectSource(ent, ent->client->v_angle, { 8, 8, -8 }, start, dir);
  1541. fire_bfg(ent, start, dir, damage, 400, damage_radius);
  1542. P_AddWeaponKick(ent, ent->client->v_forward * -2, { -20.f, 0, crandom() * 8 });
  1543. ent->client->kick.total = DAMAGE_TIME();
  1544. ent->client->kick.time = level.time + ent->client->kick.total;
  1545. // send muzzle flash
  1546. gi.WriteByte(svc_muzzleflash);
  1547. gi.WriteEntity(ent);
  1548. gi.WriteByte(MZ_BFG2 | is_silenced);
  1549. gi.multicast(ent->s.origin, MULTICAST_PVS, false);
  1550. PlayerNoise(ent, start, PNOISE_WEAPON);
  1551. G_RemoveAmmo(ent);
  1552. }
  1553. void Weapon_BFG(edict_t *ent)
  1554. {
  1555. constexpr int pause_frames[] = { 39, 45, 50, 55, 0 };
  1556. constexpr int fire_frames[] = { 9, 17, 0 };
  1557. Weapon_Generic(ent, 8, 32, 54, 58, pause_frames, fire_frames, weapon_bfg_fire);
  1558. }
  1559. //======================================================================
  1560. void weapon_disint_fire(edict_t *self)
  1561. {
  1562. vec3_t start, dir;
  1563. P_ProjectSource(self, self->client->v_angle, { 24, 8, -8 }, start, dir);
  1564. P_AddWeaponKick(self, self->client->v_forward * -2, { -1.f, 0.f, 0.f });
  1565. fire_disintegrator(self, start, dir, 800);
  1566. // send muzzle flash
  1567. gi.WriteByte(svc_muzzleflash);
  1568. gi.WriteEntity(self);
  1569. gi.WriteByte(MZ_BLASTER2);
  1570. gi.multicast(self->s.origin, MULTICAST_PVS, false);
  1571. PlayerNoise(self, start, PNOISE_WEAPON);
  1572. G_RemoveAmmo(self);
  1573. }
  1574. void Weapon_Beta_Disintegrator(edict_t *ent)
  1575. {
  1576. constexpr int pause_frames[] = { 30, 37, 45, 0 };
  1577. constexpr int fire_frames[] = { 17, 0 };
  1578. Weapon_Generic(ent, 16, 23, 46, 50, pause_frames, fire_frames, weapon_disint_fire);
  1579. }