1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050 |
- // Copyright (c) ZeniMax Media Inc.
- // Licensed under the GNU General Public License 2.0.
- /*
- ==============================================================================
- SOLDIER
- ==============================================================================
- */
- #include "g_local.h"
- #include "m_soldier.h"
- #include "m_flash.h"
- static cached_soundindex sound_idle;
- static cached_soundindex sound_sight1;
- static cached_soundindex sound_sight2;
- static cached_soundindex sound_pain_light;
- static cached_soundindex sound_pain;
- static cached_soundindex sound_pain_ss;
- static cached_soundindex sound_death_light;
- static cached_soundindex sound_death;
- static cached_soundindex sound_death_ss;
- static cached_soundindex sound_cock;
- void soldier_start_charge(edict_t *self)
- {
- self->monsterinfo.aiflags |= AI_CHARGING;
- }
- void soldier_stop_charge(edict_t *self)
- {
- self->monsterinfo.aiflags &= ~AI_CHARGING;
- }
- void soldier_idle(edict_t *self)
- {
- if (frandom() > 0.8f)
- gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0);
- }
- void soldier_cock(edict_t *self)
- {
- if (self->s.frame == FRAME_stand322)
- gi.sound(self, CHAN_WEAPON, sound_cock, 1, ATTN_IDLE, 0);
- else
- gi.sound(self, CHAN_WEAPON, sound_cock, 1, ATTN_NORM, 0);
- // [Paril-KEX] reset cockness
- self->dmg = 0;
- }
- // RAFAEL
- void soldierh_hyper_laser_sound_start(edict_t *self)
- {
- if (self->style == 1)
- {
- if (self->count >= 2 && self->count < 4)
- self->monsterinfo.weapon_sound = gi.soundindex("weapons/hyprbl1a.wav");
- }
- }
- void soldierh_hyper_laser_sound_end(edict_t *self)
- {
- if (self->monsterinfo.weapon_sound)
- {
- if (self->count >= 2 && self->count < 4)
- gi.sound(self, CHAN_AUTO, gi.soundindex("weapons/hyprbd1a.wav"), 1, ATTN_NORM, 0);
- self->monsterinfo.weapon_sound = 0;
- }
- }
- // RAFAEL
- // STAND
- void soldier_stand(edict_t *self);
- mframe_t soldier_frames_stand1[] = {
- { ai_stand, 0, soldier_idle },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand }
- };
- MMOVE_T(soldier_move_stand1) = { FRAME_stand101, FRAME_stand130, soldier_frames_stand1, soldier_stand };
- mframe_t soldier_frames_stand2[] = {
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand, 0, monster_footstep },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand, 0, monster_footstep }
- };
- MMOVE_T(soldier_move_stand2) = { FRAME_stand201, FRAME_stand240, soldier_frames_stand2, soldier_stand };
- mframe_t soldier_frames_stand3[] = {
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand, 0, soldier_cock },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand },
- { ai_stand }
- };
- MMOVE_T(soldier_move_stand3) = { FRAME_stand301, FRAME_stand339, soldier_frames_stand3, soldier_stand };
- MONSTERINFO_STAND(soldier_stand) (edict_t *self) -> void
- {
- float r = frandom();
- if ((self->monsterinfo.active_move != &soldier_move_stand1) || (r < 0.6f))
- M_SetAnimation(self, &soldier_move_stand1);
- else if (r < 0.8f)
- M_SetAnimation(self, &soldier_move_stand2);
- else
- M_SetAnimation(self, &soldier_move_stand3);
- soldierh_hyper_laser_sound_end(self);
- }
- //
- // WALK
- //
- void soldier_walk1_random(edict_t *self)
- {
- if (frandom() > 0.1f)
- self->monsterinfo.nextframe = FRAME_walk101;
- }
- mframe_t soldier_frames_walk1[] = {
- { ai_walk, 3 },
- { ai_walk, 6 },
- { ai_walk, 2 },
- { ai_walk, 2, monster_footstep },
- { ai_walk, 2 },
- { ai_walk, 1 },
- { ai_walk, 6 },
- { ai_walk, 5 },
- { ai_walk, 3, monster_footstep },
- { ai_walk, -1, soldier_walk1_random },
- { ai_walk },
- { ai_walk },
- { ai_walk },
- { ai_walk },
- { ai_walk },
- { ai_walk },
- { ai_walk },
- { ai_walk },
- { ai_walk },
- { ai_walk },
- { ai_walk },
- { ai_walk },
- { ai_walk },
- { ai_walk },
- { ai_walk },
- { ai_walk },
- { ai_walk },
- { ai_walk },
- { ai_walk },
- { ai_walk },
- { ai_walk },
- { ai_walk },
- { ai_walk }
- };
- MMOVE_T(soldier_move_walk1) = { FRAME_walk101, FRAME_walk133, soldier_frames_walk1, nullptr };
- mframe_t soldier_frames_walk2[] = {
- { ai_walk, 4, monster_footstep },
- { ai_walk, 4 },
- { ai_walk, 9 },
- { ai_walk, 8 },
- { ai_walk, 5 },
- { ai_walk, 1, monster_footstep },
- { ai_walk, 3 },
- { ai_walk, 7 },
- { ai_walk, 6 },
- { ai_walk, 7 }
- };
- MMOVE_T(soldier_move_walk2) = { FRAME_walk209, FRAME_walk218, soldier_frames_walk2, nullptr };
- MONSTERINFO_WALK(soldier_walk) (edict_t *self) -> void
- {
- // [Paril-KEX] during N64 cutscene, always use fast walk or we bog down the line
- if (!(self->hackflags & HACKFLAG_END_CUTSCENE) && frandom() < 0.5f)
- M_SetAnimation(self, &soldier_move_walk1);
- else
- M_SetAnimation(self, &soldier_move_walk2);
- }
- //
- // RUN
- //
- void soldier_run(edict_t *self);
- mframe_t soldier_frames_start_run[] = {
- { ai_run, 7 },
- { ai_run, 5 }
- };
- MMOVE_T(soldier_move_start_run) = { FRAME_run01, FRAME_run02, soldier_frames_start_run, soldier_run };
- mframe_t soldier_frames_run[] = {
- { ai_run, 10 },
- { ai_run, 11, [](edict_t *self) { monster_done_dodge(self); monster_footstep(self); } },
- { ai_run, 11 },
- { ai_run, 16 },
- { ai_run, 10, monster_footstep },
- { ai_run, 15, monster_done_dodge }
- };
- MMOVE_T(soldier_move_run) = { FRAME_run03, FRAME_run08, soldier_frames_run, nullptr };
- MONSTERINFO_RUN(soldier_run) (edict_t *self) -> void
- {
- monster_done_dodge(self);
- soldierh_hyper_laser_sound_end(self);
- if (self->monsterinfo.aiflags & AI_STAND_GROUND)
- {
- M_SetAnimation(self, &soldier_move_stand1);
- return;
- }
- if (self->monsterinfo.active_move == &soldier_move_walk1 ||
- self->monsterinfo.active_move == &soldier_move_walk2 ||
- self->monsterinfo.active_move == &soldier_move_start_run ||
- self->monsterinfo.active_move == &soldier_move_run)
- {
- M_SetAnimation(self, &soldier_move_run);
- }
- else
- {
- M_SetAnimation(self, &soldier_move_start_run);
- }
- }
- //
- // PAIN
- //
- mframe_t soldier_frames_pain1[] = {
- { ai_move, -3 },
- { ai_move, 4 },
- { ai_move, 1 },
- { ai_move, 1 },
- { ai_move }
- };
- MMOVE_T(soldier_move_pain1) = { FRAME_pain101, FRAME_pain105, soldier_frames_pain1, soldier_run };
- mframe_t soldier_frames_pain2[] = {
- { ai_move, -13 },
- { ai_move, -1 },
- { ai_move, 2 },
- { ai_move, 4 },
- { ai_move, 2 },
- { ai_move, 3 },
- { ai_move, 2 }
- };
- MMOVE_T(soldier_move_pain2) = { FRAME_pain201, FRAME_pain207, soldier_frames_pain2, soldier_run };
- mframe_t soldier_frames_pain3[] = {
- { ai_move, -8 },
- { ai_move, 10 },
- { ai_move, -4, monster_footstep },
- { ai_move, -1 },
- { ai_move, -3 },
- { ai_move },
- { ai_move, 3 },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move, 1 },
- { ai_move },
- { ai_move, 1 },
- { ai_move, 2 },
- { ai_move, 4 },
- { ai_move, 3 },
- { ai_move, 2, monster_footstep }
- };
- MMOVE_T(soldier_move_pain3) = { FRAME_pain301, FRAME_pain318, soldier_frames_pain3, soldier_run };
- mframe_t soldier_frames_pain4[] = {
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move, -10 },
- { ai_move, -6 },
- { ai_move, 8 },
- { ai_move, 4 },
- { ai_move, 1 },
- { ai_move },
- { ai_move, 2 },
- { ai_move, 5 },
- { ai_move, 2 },
- { ai_move, -1 },
- { ai_move, -1 },
- { ai_move, 3 },
- { ai_move, 2 },
- { ai_move }
- };
- MMOVE_T(soldier_move_pain4) = { FRAME_pain401, FRAME_pain417, soldier_frames_pain4, soldier_run };
- PAIN(soldier_pain) (edict_t *self, edict_t *other, float kick, int damage, const mod_t &mod) -> void
- {
- float r;
- int n;
- monster_done_dodge(self);
- soldier_stop_charge(self);
- // if we're blind firing, this needs to be turned off here
- self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
- if (level.time < self->pain_debounce_time)
- {
- if ((self->velocity[2] > 100) && ((self->monsterinfo.active_move == &soldier_move_pain1) || (self->monsterinfo.active_move == &soldier_move_pain2) || (self->monsterinfo.active_move == &soldier_move_pain3)))
- {
- // PMM - clear duck flag
- if (self->monsterinfo.aiflags & AI_DUCKED)
- monster_duck_up(self);
- M_SetAnimation(self, &soldier_move_pain4);
- soldierh_hyper_laser_sound_end(self);
- }
- return;
- }
- self->pain_debounce_time = level.time + 3_sec;
- n = self->count | 1;
- if (n == 1)
- gi.sound(self, CHAN_VOICE, sound_pain_light, 1, ATTN_NORM, 0);
- else if (n == 3)
- gi.sound(self, CHAN_VOICE, sound_pain, 1, ATTN_NORM, 0);
- else
- gi.sound(self, CHAN_VOICE, sound_pain_ss, 1, ATTN_NORM, 0);
- if (self->velocity[2] > 100)
- {
- // PMM - clear duck flag
- if (self->monsterinfo.aiflags & AI_DUCKED)
- monster_duck_up(self);
- M_SetAnimation(self, &soldier_move_pain4);
- soldierh_hyper_laser_sound_end(self);
- return;
- }
-
- if (!M_ShouldReactToPain(self, mod))
- return; // no pain anims in nightmare
- r = frandom();
- if (r < 0.33f)
- M_SetAnimation(self, &soldier_move_pain1);
- else if (r < 0.66f)
- M_SetAnimation(self, &soldier_move_pain2);
- else
- M_SetAnimation(self, &soldier_move_pain3);
- // PMM - clear duck flag
- if (self->monsterinfo.aiflags & AI_DUCKED)
- monster_duck_up(self);
- soldierh_hyper_laser_sound_end(self);
- }
- MONSTERINFO_SETSKIN(soldier_setskin) (edict_t *self) -> void
- {
- if (self->health < (self->max_health / 2))
- self->s.skinnum |= 1;
- else
- self->s.skinnum &= ~1;
- }
- //
- // ATTACK
- //
- constexpr monster_muzzleflash_id_t blaster_flash[] = { MZ2_SOLDIER_BLASTER_1, MZ2_SOLDIER_BLASTER_2, MZ2_SOLDIER_BLASTER_3, MZ2_SOLDIER_BLASTER_4, MZ2_SOLDIER_BLASTER_5, MZ2_SOLDIER_BLASTER_6, MZ2_SOLDIER_BLASTER_7, MZ2_SOLDIER_BLASTER_8, MZ2_SOLDIER_BLASTER_9 };
- constexpr monster_muzzleflash_id_t shotgun_flash[] = { MZ2_SOLDIER_SHOTGUN_1, MZ2_SOLDIER_SHOTGUN_2, MZ2_SOLDIER_SHOTGUN_3, MZ2_SOLDIER_SHOTGUN_4, MZ2_SOLDIER_SHOTGUN_5, MZ2_SOLDIER_SHOTGUN_6, MZ2_SOLDIER_SHOTGUN_7, MZ2_SOLDIER_SHOTGUN_8, MZ2_SOLDIER_SHOTGUN_9 };
- constexpr monster_muzzleflash_id_t machinegun_flash[] = { MZ2_SOLDIER_MACHINEGUN_1, MZ2_SOLDIER_MACHINEGUN_2, MZ2_SOLDIER_MACHINEGUN_3, MZ2_SOLDIER_MACHINEGUN_4, MZ2_SOLDIER_MACHINEGUN_5, MZ2_SOLDIER_MACHINEGUN_6, MZ2_SOLDIER_MACHINEGUN_7, MZ2_SOLDIER_MACHINEGUN_8, MZ2_SOLDIER_MACHINEGUN_9 };
- void soldier_fire_vanilla(edict_t *self, int flash_number, bool angle_limited)
- {
- vec3_t start;
- vec3_t forward, right, up;
- vec3_t aim;
- vec3_t dir;
- vec3_t end;
- float r, u;
- monster_muzzleflash_id_t flash_index;
- vec3_t aim_norm;
- float angle;
- vec3_t aim_good;
- if (self->count < 2)
- flash_index = blaster_flash[flash_number];
- else if (self->count < 4)
- flash_index = shotgun_flash[flash_number];
- else
- flash_index = machinegun_flash[flash_number];
- AngleVectors(self->s.angles, forward, right, nullptr);
- start = M_ProjectFlashSource(self, monster_flash_offset[flash_index], forward, right);
- if (flash_number == 5 || flash_number == 6) // he's dead
- {
- if (self->spawnflags.has(SPAWNFLAG_MONSTER_DEAD))
- return;
- aim = forward;
- }
- else
- {
- if ((!self->enemy) || (!self->enemy->inuse))
- {
- self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
- return;
- }
- // PMM
- if (self->monsterinfo.attack_state == AS_BLIND)
- end = self->monsterinfo.blind_fire_target;
- else
- end = self->enemy->s.origin;
- // pmm
- end[2] += self->enemy->viewheight;
- aim = end - start;
- aim_good = end;
- // PMM
- if (angle_limited)
- {
- aim_norm = aim;
- aim_norm.normalize();
- angle = aim_norm.dot(forward);
- if (angle < 0.5f) // ~25 degree angle
- {
- if (level.time >= self->monsterinfo.fire_wait)
- self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
- else
- self->monsterinfo.aiflags |= AI_HOLD_FRAME;
- return;
- }
- }
- //-PMM
- dir = vectoangles(aim);
- AngleVectors(dir, forward, right, up);
- r = crandom() * 1000;
- u = crandom() * 500;
- end = start + (forward * 8192);
- end += (right * r);
- end += (up * u);
- aim = end - start;
- aim.normalize();
- }
- if (self->count <= 1)
- {
- monster_fire_blaster(self, start, aim, 5, 600, flash_index, EF_BLASTER);
- }
- else if (self->count <= 3)
- {
- monster_fire_shotgun(self, start, aim, 2, 1, 1500, 750, 9, flash_index);
- // [Paril-KEX] indicates to soldier that he must cock
- self->dmg = 1;
- }
- else
- {
- // PMM - changed to wait from pausetime to not interfere with dodge code
- if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME))
- self->monsterinfo.fire_wait = level.time + random_time(300_ms, 1.1_sec);
- monster_fire_bullet(self, start, aim, 2, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_index);
- if (level.time >= self->monsterinfo.fire_wait)
- self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
- else
- self->monsterinfo.aiflags |= AI_HOLD_FRAME;
- }
- }
- PRETHINK(soldierh_laser_update) (edict_t *laser) -> void
- {
- edict_t *self = laser->owner;
- vec3_t forward, right, up;
- vec3_t start;
- vec3_t tempvec;
- AngleVectors(self->s.angles, forward, right, up);
- start = self->s.origin;
- tempvec = monster_flash_offset[self->radius_dmg];
- start += (forward * tempvec[0]);
- start += (right * tempvec[1]);
- start += (up * (tempvec[2] + 6));
- if (!self->deadflag)
- PredictAim(self, self->enemy, start, 0, false, frandom(0.1f, 0.2f), &forward, nullptr);
-
- laser->s.origin = start;
- laser->movedir = forward;
- gi.linkentity(laser);
- dabeam_update(laser, false);
- }
- // RAFAEL
- void soldierh_laserbeam(edict_t *self, int flash_index)
- {
- self->radius_dmg = flash_index;
- monster_fire_dabeam(self, 1, false, soldierh_laser_update);
- }
- constexpr monster_muzzleflash_id_t ripper_flash[] = { MZ2_SOLDIER_RIPPER_1, MZ2_SOLDIER_RIPPER_2, MZ2_SOLDIER_RIPPER_3, MZ2_SOLDIER_RIPPER_4, MZ2_SOLDIER_RIPPER_5, MZ2_SOLDIER_RIPPER_6, MZ2_SOLDIER_RIPPER_7, MZ2_SOLDIER_RIPPER_8, MZ2_SOLDIER_RIPPER_9 };
- constexpr monster_muzzleflash_id_t hyper_flash[] = { MZ2_SOLDIER_HYPERGUN_1, MZ2_SOLDIER_HYPERGUN_2, MZ2_SOLDIER_HYPERGUN_3, MZ2_SOLDIER_HYPERGUN_4, MZ2_SOLDIER_HYPERGUN_5, MZ2_SOLDIER_HYPERGUN_6, MZ2_SOLDIER_HYPERGUN_7, MZ2_SOLDIER_HYPERGUN_8, MZ2_SOLDIER_HYPERGUN_9 };
- void soldier_fire_xatrix(edict_t *self, int flash_number, bool angle_limited)
- {
- vec3_t start;
- vec3_t forward, right, up;
- vec3_t aim;
- vec3_t dir;
- vec3_t end;
- float r, u;
- monster_muzzleflash_id_t flash_index;
- vec3_t aim_norm;
- float angle;
- vec3_t aim_good;
- if (self->count < 2)
- flash_index = ripper_flash[flash_number]; // ripper
- else if (self->count < 4)
- flash_index = hyper_flash[flash_number]; // hyperblaster
- else
- flash_index = machinegun_flash[flash_number]; // laserbeam
- AngleVectors(self->s.angles, forward, right, nullptr);
- start = M_ProjectFlashSource(self, monster_flash_offset[flash_index], forward, right);
- if (flash_number == 5 || flash_number == 6)
- {
- if (self->spawnflags.has(SPAWNFLAG_MONSTER_DEAD))
- return;
- aim = forward;
- }
- else
- {
- // [Paril-KEX] no enemy = no fire
- if ((!self->enemy) || (!self->enemy->inuse))
- {
- self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
- return;
- }
- // PMM
- if (self->monsterinfo.attack_state == AS_BLIND)
- end = self->monsterinfo.blind_fire_target;
- else
- end = self->enemy->s.origin;
- // pmm
- end[2] += self->enemy->viewheight;
- aim = end - start;
- aim_good = end;
- // PMM
- if (angle_limited)
- {
- aim_norm = aim;
- aim_norm.normalize();
- angle = aim_norm.dot(forward);
-
- if (angle < 0.5f) // ~25 degree angle
- {
- if (level.time >= self->monsterinfo.fire_wait)
- self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
- else
- self->monsterinfo.aiflags |= AI_HOLD_FRAME;
- return;
- }
- }
- //-PMM
- dir = vectoangles(aim);
- AngleVectors(dir, forward, right, up);
- r = crandom() * 100;
- u = crandom() * 50;
- end = start + (forward * 8192);
- end += (right * r);
- end += (up * u);
- aim = end - start;
- aim.normalize();
- }
- if (self->count <= 1)
- {
- // RAFAEL 24-APR-98
- // droped the damage from 15 to 5
- monster_fire_ionripper(self, start, aim, 5, 600, flash_index, EF_IONRIPPER);
- }
- else if (self->count <= 3)
- {
- monster_fire_blueblaster(self, start, aim, 1, 600, flash_index, EF_BLUEHYPERBLASTER);
- }
- else
- {
- // PMM - changed to wait from pausetime to not interfere with dodge code
- if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME))
- self->monsterinfo.fire_wait = level.time + random_time(300_ms, 1.1_sec);
- soldierh_laserbeam(self, flash_index);
- if (level.time >= self->monsterinfo.fire_wait)
- self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
- else
- self->monsterinfo.aiflags |= AI_HOLD_FRAME;
- }
- }
- // RAFAEL
- void soldier_fire(edict_t *self, int flash_number, bool angle_limited)
- {
- // RAFAEL
- if (self->style == 1)
- soldier_fire_xatrix(self, flash_number, angle_limited);
- else
- // RAFAEL
- soldier_fire_vanilla(self, flash_number, angle_limited);
- }
- // ATTACK1 (blaster/shotgun)
- void soldier_fire1(edict_t *self)
- {
- soldier_fire(self, 0, false);
- }
- void soldier_attack1_refire1(edict_t *self)
- {
- // [Paril-KEX]
- if (self->count <= 0)
- self->monsterinfo.nextframe = FRAME_attak110;
- // PMM - blindfire
- if (self->monsterinfo.aiflags & AI_MANUAL_STEERING)
- {
- self->monsterinfo.aiflags &= ~AI_MANUAL_STEERING;
- return;
- }
- // pmm
- if (!self->enemy)
- return;
- if (self->count > 1)
- return;
- if (self->enemy->health <= 0)
- return;
- if (((frandom() < 0.5f) && visible(self, self->enemy)) || (range_to(self, self->enemy) <= RANGE_MELEE))
- self->monsterinfo.nextframe = FRAME_attak102;
- else
- self->monsterinfo.nextframe = FRAME_attak110;
- }
- void soldier_attack1_refire2(edict_t *self)
- {
- if (!self->enemy)
- return;
- if (self->count < 2)
- return;
- if (self->enemy->health <= 0)
- return;
- if (((self->radius_dmg || frandom() < 0.5f) && visible(self, self->enemy)) || (range_to(self, self->enemy) <= RANGE_MELEE))
- {
- self->monsterinfo.nextframe = FRAME_attak102;
- self->radius_dmg = 0;
- }
- }
- static void soldier_attack1_shotgun_check(edict_t *self)
- {
- if (self->dmg)
- {
- self->monsterinfo.nextframe = FRAME_attak106;
- // [Paril-KEX] indicate that we should force a refire
- self->radius_dmg = 1;
- }
- }
- static void soldier_blind_check(edict_t *self)
- {
- if (self->monsterinfo.aiflags & AI_MANUAL_STEERING)
- {
- vec3_t aim = self->monsterinfo.blind_fire_target - self->s.origin;
- self->ideal_yaw = vectoyaw(aim);
- }
- }
- mframe_t soldier_frames_attack1[] = {
- { ai_charge, 0, soldier_blind_check },
- { ai_charge, 0, soldier_attack1_shotgun_check },
- { ai_charge, 0, soldier_fire1 },
- { ai_charge },
- { ai_charge },
- { ai_charge, 0, soldier_attack1_refire1 },
- { ai_charge },
- { ai_charge, 0, soldier_cock },
- { ai_charge, 0, soldier_attack1_refire2 },
- { ai_charge },
- { ai_charge },
- { ai_charge }
- };
- MMOVE_T(soldier_move_attack1) = { FRAME_attak101, FRAME_attak112, soldier_frames_attack1, soldier_run };
- // ATTACK1 (blaster/shotgun)
- void soldierh_hyper_refire1(edict_t *self)
- {
- if (!self->enemy)
- return;
- if (self->count >= 2 && self->count < 4)
- {
- if (frandom() < 0.7f && visible(self, self->enemy))
- self->s.frame = FRAME_attak103;
- }
- }
- void soldierh_hyperripper1(edict_t *self)
- {
- if (self->count < 4)
- soldier_fire(self, 0, false);
- }
- mframe_t soldierh_frames_attack1[] = {
- { ai_charge, 0, soldier_blind_check },
- { ai_charge, 0, soldierh_hyper_laser_sound_start },
- { ai_charge, 0, soldier_fire1 },
- { ai_charge, 0, soldierh_hyperripper1 },
- { ai_charge, 0, soldierh_hyperripper1 },
- { ai_charge, 0, soldier_attack1_refire1 },
- { ai_charge, 0, soldierh_hyper_refire1 },
- { ai_charge, 0, soldier_cock },
- { ai_charge, 0, soldier_attack1_refire2 },
- { ai_charge, 0, soldierh_hyper_laser_sound_end },
- { ai_charge },
- { ai_charge }
- };
- MMOVE_T(soldierh_move_attack1) = { FRAME_attak101, FRAME_attak112, soldierh_frames_attack1, soldier_run };
- // ATTACK2 (blaster/shotgun)
- void soldier_fire2(edict_t *self)
- {
- soldier_fire(self, 1, false);
- }
- void soldier_attack2_refire1(edict_t *self)
- {
- if (self->count <= 0)
- self->monsterinfo.nextframe = FRAME_attak216;
- if (!self->enemy)
- return;
- if (self->count > 1)
- return;
- if (self->enemy->health <= 0)
- return;
- if (((frandom() < 0.5f) && visible(self, self->enemy)) || (range_to(self, self->enemy) <= RANGE_MELEE))
- self->monsterinfo.nextframe = FRAME_attak204;
- }
- void soldier_attack2_refire2(edict_t *self)
- {
- if (!self->enemy)
- return;
- if (self->count < 2)
- return;
- if (self->enemy->health <= 0)
- return;
- // RAFAEL
- if (((self->radius_dmg || frandom() < 0.5f) && visible(self, self->enemy)) || ((self->style == 0 || self->count < 4) && (range_to(self, self->enemy) <= RANGE_MELEE)))
- {
- // RAFAEL
- self->monsterinfo.nextframe = FRAME_attak204;
- self->radius_dmg = 0;
- }
- }
- static void soldier_attack2_shotgun_check(edict_t *self)
- {
- if (self->dmg)
- {
- self->monsterinfo.nextframe = FRAME_attak210;
- // [Paril-KEX] indicate that we should force a refire
- self->radius_dmg = 1;
- }
- }
- mframe_t soldier_frames_attack2[] = {
- { ai_charge },
- { ai_charge },
- { ai_charge, 0, soldier_attack2_shotgun_check },
- { ai_charge },
- { ai_charge, 0, soldier_fire2 },
- { ai_charge },
- { ai_charge },
- { ai_charge, 0, soldier_attack2_refire1 },
- { ai_charge },
- { ai_charge },
- { ai_charge },
- { ai_charge },
- { ai_charge, 0, soldier_cock },
- { ai_charge },
- { ai_charge, 0, soldier_attack2_refire2 },
- { ai_charge },
- { ai_charge },
- { ai_charge }
- };
- MMOVE_T(soldier_move_attack2) = { FRAME_attak201, FRAME_attak218, soldier_frames_attack2, soldier_run };
- // RAFAEL
- void soldierh_hyper_refire2(edict_t *self)
- {
- if (!self->enemy)
- return;
- if (self->count < 2)
- return;
- else if (self->count < 4)
- {
- if (frandom() < 0.7f && visible(self, self->enemy))
- self->s.frame = FRAME_attak205;
- }
- }
- void soldierh_hyperripper2(edict_t *self)
- {
- if (self->count < 4)
- soldier_fire(self, 1, false);
- }
- mframe_t soldierh_frames_attack2[] = {
- { ai_charge },
- { ai_charge },
- { ai_charge },
- { ai_charge, 0, soldierh_hyper_laser_sound_start },
- { ai_charge, 0, soldier_fire2 },
- { ai_charge, 0, soldierh_hyperripper2 },
- { ai_charge, 0, soldierh_hyperripper2 },
- { ai_charge, 0, soldier_attack2_refire1 },
- { ai_charge, 0, soldierh_hyper_refire2 },
- { ai_charge },
- { ai_charge },
- { ai_charge },
- { ai_charge, 0, soldier_cock },
- { ai_charge },
- { ai_charge, 0, soldier_attack2_refire2 },
- { ai_charge, 0, soldierh_hyper_laser_sound_end },
- { ai_charge },
- { ai_charge }
- };
- MMOVE_T(soldierh_move_attack2) = { FRAME_attak201, FRAME_attak218, soldierh_frames_attack2, soldier_run };
- // RAFAEL
- // ATTACK3 (duck and shoot)
- void soldier_fire3(edict_t *self)
- {
- soldier_fire(self, 2, false);
- }
- void soldierh_hyperripper3(edict_t *self)
- {
- if (self->s.skinnum >= 6 && self->count < 4)
- soldier_fire(self, 2, false);
- }
- void soldier_attack3_refire(edict_t *self)
- {
- if (self->dmg)
- monster_duck_hold(self);
- else if ((level.time + 400_ms) < self->monsterinfo.duck_wait_time)
- self->monsterinfo.nextframe = FRAME_attak303;
- }
- mframe_t soldier_frames_attack3[] = {
- { ai_charge, 0, monster_duck_down },
- { ai_charge, 0, soldierh_hyper_laser_sound_start },
- { ai_charge, 0, soldier_fire3 },
- { ai_charge, 0, soldierh_hyperripper3 },
- { ai_charge, 0, soldierh_hyperripper3 },
- { ai_charge, 0, soldier_attack3_refire },
- { ai_charge, 0, monster_duck_up },
- { ai_charge, 0, soldierh_hyper_laser_sound_end },
- { ai_charge }
- };
- MMOVE_T(soldier_move_attack3) = { FRAME_attak301, FRAME_attak309, soldier_frames_attack3, soldier_run };
- // ATTACK4 (machinegun)
- void soldier_fire4(edict_t *self)
- {
- soldier_fire(self, 3, false);
- }
- mframe_t soldier_frames_attack4[] = {
- { ai_charge },
- { ai_charge, 0, soldierh_hyper_laser_sound_start },
- { ai_charge, 0, soldier_fire4 },
- { ai_charge, 0, soldierh_hyper_laser_sound_end },
- { ai_charge },
- { ai_charge }
- };
- MMOVE_T(soldier_move_attack4) = { FRAME_attak401, FRAME_attak406, soldier_frames_attack4, soldier_run };
- // ATTACK6 (run & shoot)
- void soldier_fire8(edict_t *self)
- {
- soldier_fire(self, 7, true);
- }
- void soldier_attack6_refire1(edict_t *self)
- {
- // PMM - make sure dodge & charge bits are cleared
- monster_done_dodge(self);
- soldier_stop_charge(self);
- if (!self->enemy)
- return;
- if (self->count > 1)
- return;
- if (self->enemy->health <= 0 ||
- range_to(self, self->enemy) < RANGE_NEAR ||
- !visible(self, self->enemy)) // don't endlessly run into walls
- {
- soldier_run(self);
- return;
- }
- if (frandom() < 0.25f)
- self->monsterinfo.nextframe = FRAME_runs03;
- else
- soldier_run(self);
- }
- void soldier_attack6_refire2(edict_t *self)
- {
- // PMM - make sure dodge & charge bits are cleared
- monster_done_dodge(self);
- soldier_stop_charge(self);
- if (!self->enemy || self->count <= 0)
- return;
- if (self->enemy->health <= 0 ||
- (!self->radius_dmg && range_to(self, self->enemy) < RANGE_NEAR) ||
- !visible(self, self->enemy)) // don't endlessly run into walls
- {
- soldierh_hyper_laser_sound_end(self);
- return;
- }
- if (self->radius_dmg || frandom() < 0.25f)
- {
- self->monsterinfo.nextframe = FRAME_runs03;
- self->radius_dmg = 0;
- }
- }
- static void soldier_attack6_shotgun_check(edict_t *self)
- {
- if (self->dmg)
- {
- self->monsterinfo.nextframe = FRAME_runs09;
- // [Paril-KEX] indicate that we should force a refire
- self->radius_dmg = 1;
- }
- }
- void soldierh_hyperripper8(edict_t *self)
- {
- if (self->s.skinnum >= 6 && self->count < 4)
- soldier_fire(self, 7, true);
- }
- mframe_t soldier_frames_attack6[] = {
- { ai_run, 10, soldier_start_charge },
- { ai_run, 4, soldier_attack6_shotgun_check },
- { ai_run, 12, soldierh_hyper_laser_sound_start },
- { ai_run, 11, [](edict_t *self) { soldier_fire8(self); monster_footstep(self); } },
- { ai_run, 13, [](edict_t *self ) { soldierh_hyperripper8(self); monster_done_dodge(self); } },
- { ai_run, 18, soldierh_hyperripper8 },
- { ai_run, 15, monster_footstep },
- { ai_run, 14, soldier_attack6_refire1 },
- { ai_run, 11 },
- { ai_run, 8, monster_footstep },
- { ai_run, 11, soldier_cock },
- { ai_run, 12 },
- { ai_run, 12, monster_footstep },
- { ai_run, 17, soldier_attack6_refire2 }
- };
- MMOVE_T(soldier_move_attack6) = { FRAME_runs01, FRAME_runs14, soldier_frames_attack6, soldier_run, 0.65f };
- MONSTERINFO_ATTACK(soldier_attack) (edict_t *self) -> void
- {
- float r, chance;
- monster_done_dodge(self);
- // PMM - blindfire!
- if (self->monsterinfo.attack_state == AS_BLIND)
- {
- // setup shot probabilities
- if (self->monsterinfo.blind_fire_delay < 1_sec)
- chance = 1.0f;
- else if (self->monsterinfo.blind_fire_delay < 7.5_sec)
- chance = 0.4f;
- else
- chance = 0.1f;
- r = frandom();
- // minimum of 4.1 seconds, plus 0-3, after the shots are done
- self->monsterinfo.blind_fire_delay += 4.1_sec + random_time(3_sec);
- // don't shoot at the origin
- if (!self->monsterinfo.blind_fire_target)
- return;
- // don't shoot if the dice say not to
- if (r > chance)
- return;
- // turn on manual steering to signal both manual steering and blindfire
- self->monsterinfo.aiflags |= AI_MANUAL_STEERING;
- // RAFAEL
- if (self->style == 1)
- M_SetAnimation(self, &soldierh_move_attack1);
- else
- // RAFAEL
- M_SetAnimation(self, &soldier_move_attack1);
- self->monsterinfo.attack_finished = level.time + random_time(1.5_sec, 2.5_sec);
- return;
- }
- // pmm
- // PMM - added this so the soldiers now run toward you and shoot instead of just stopping and shooting
- r = frandom();
- // nb: run-shoot not limited by `M_CheckClearShot` since they will be far enough
- // away that it doesn't matter
- if ((!(self->monsterinfo.aiflags & (AI_BLOCKED | AI_STAND_GROUND))) &&
- (r < 0.25f &&
- (self->count <= 3)) &&
- (range_to(self, self->enemy) >= (RANGE_NEAR * 0.5f)))
- {
- M_SetAnimation(self, &soldier_move_attack6);
- }
- else
- {
- if (self->count < 4)
- {
- bool attack1_possible;
- // [Paril-KEX] shotgun guard only uses attack2 at close range
- if ((!self->style && self->count >= 2 && self->count <= 3) && range_to(self, self->enemy) <= (RANGE_NEAR * 0.65f))
- attack1_possible = false;
- else
- attack1_possible = M_CheckClearShot(self, monster_flash_offset[MZ2_SOLDIER_BLASTER_1]);
-
- bool attack2_possible = M_CheckClearShot(self, monster_flash_offset[MZ2_SOLDIER_BLASTER_2]);
- if (attack1_possible && (!attack2_possible || frandom() < 0.5f))
- {
- // RAFAEL
- if (self->style == 1)
- M_SetAnimation(self, &soldierh_move_attack1);
- else
- // RAFAEL
- M_SetAnimation(self, &soldier_move_attack1);
- }
- else if (attack2_possible)
- {
- // RAFAEL
- if (self->style == 1)
- M_SetAnimation(self, &soldierh_move_attack2);
- else
- // RAFAEL
- M_SetAnimation(self, &soldier_move_attack2);
- }
- }
- else if (M_CheckClearShot(self, monster_flash_offset[MZ2_SOLDIER_MACHINEGUN_4]))
- {
- M_SetAnimation(self, &soldier_move_attack4);
- }
- }
- }
- //
- // SIGHT
- //
- MONSTERINFO_SIGHT(soldier_sight) (edict_t *self, edict_t *other) -> void
- {
- if (frandom() < 0.5f)
- gi.sound(self, CHAN_VOICE, sound_sight1, 1, ATTN_NORM, 0);
- else
- gi.sound(self, CHAN_VOICE, sound_sight2, 1, ATTN_NORM, 0);
- if (self->enemy && (range_to(self, self->enemy) >= RANGE_NEAR) &&
- visible(self, self->enemy) // Paril: don't run-shoot if we can't see them
- )
- {
- // RAFAEL
- if (self->style == 1 || frandom() > 0.75f)
- // RAFAEL
- {
- // RAFAEL + legacy bug fix
- // don't use run+shoot for machinegun/laser because
- // the animation is a bit weird
- if (self->count < 4)
- M_SetAnimation(self, &soldier_move_attack6);
- else if (M_CheckClearShot(self, monster_flash_offset[MZ2_SOLDIER_MACHINEGUN_4]))
- // RAFAEL
- M_SetAnimation(self, &soldier_move_attack4);
- }
- }
- }
- //
- // DUCK
- //
- mframe_t soldier_frames_duck[] = {
- { ai_move, 5, monster_duck_down },
- { ai_move, -1, monster_duck_hold },
- { ai_move, 1 },
- { ai_move, 0, monster_duck_up },
- { ai_move, 5 }
- };
- MMOVE_T(soldier_move_duck) = { FRAME_duck01, FRAME_duck05, soldier_frames_duck, soldier_run };
- extern const mmove_t soldier_move_trip;
- static void soldier_stand_up(edict_t *self)
- {
- soldierh_hyper_laser_sound_end(self);
- M_SetAnimation(self, &soldier_move_trip, false);
- self->monsterinfo.nextframe = FRAME_runt08;
- }
- static bool soldier_prone_shoot_ok(edict_t *self)
- {
- if (!self->enemy || !self->enemy->inuse)
- return false;
- vec3_t fwd;
- AngleVectors(self->s.angles, fwd, nullptr, nullptr);
- vec3_t diff = self->enemy->s.origin - self->s.origin;
- diff.z = 0;
- diff.normalize();
- float v = fwd.dot(diff);
- if (v < 0.80f)
- return false;
-
- return true;
- }
- static void ai_soldier_move(edict_t *self, float dist)
- {
- ai_move(self, dist);
- if (!soldier_prone_shoot_ok(self))
- {
- soldier_stand_up(self);
- return;
- }
- }
- void soldier_fire5(edict_t *self)
- {
- soldier_fire(self, 8, true);
- }
- void soldierh_hyperripper5(edict_t *self)
- {
- if (self->style && self->count < 4)
- soldier_fire(self, 8, true);
- }
- mframe_t soldier_frames_attack5[] = {
- { ai_move, 18, monster_duck_down },
- { ai_move, 11, monster_footstep },
- { ai_move, 0, monster_footstep },
- { ai_soldier_move },
- { ai_soldier_move, 0, soldierh_hyper_laser_sound_start },
- { ai_soldier_move, 0, soldier_fire5 },
- { ai_soldier_move, 0, soldierh_hyperripper5 },
- { ai_soldier_move, 0, soldierh_hyperripper5 },
- };
- MMOVE_T(soldier_move_attack5) = { FRAME_attak501, FRAME_attak508, soldier_frames_attack5, soldier_stand_up };
- static void monster_check_prone(edict_t *self)
- {
- // we're a shotgun guard waiting to cock
- if (!self->style && self->count >= 2 && self->count <= 3 && self->dmg)
- return;
- // not going to shoot at this angle
- if (!soldier_prone_shoot_ok(self))
- return;
- M_SetAnimation(self, &soldier_move_attack5, false);
- }
- mframe_t soldier_frames_trip[] = {
- { ai_move, 10 },
- { ai_move, 2, monster_check_prone },
- { ai_move, 18, monster_duck_down },
- { ai_move, 11, monster_footstep },
- { ai_move, 9 },
- { ai_move, -11, monster_footstep },
- { ai_move, -2 },
- { ai_move, 0 },
- { ai_move, 6 },
- { ai_move, -5 },
- { ai_move, 0 },
- { ai_move, 1 },
- { ai_move, 0, monster_footstep },
- { ai_move, 0, monster_duck_up },
- { ai_move, 3 },
- { ai_move, 2, monster_footstep },
- { ai_move, -1 },
- { ai_move, 2 },
- { ai_move, 0 },
- };
- MMOVE_T(soldier_move_trip) = { FRAME_runt01, FRAME_runt19, soldier_frames_trip, soldier_run };
- // pmm - blocking code
- MONSTERINFO_BLOCKED(soldier_blocked) (edict_t *self, float dist) -> bool
- {
- // don't do anything if you're dodging
- if ((self->monsterinfo.aiflags & AI_DODGING) || (self->monsterinfo.aiflags & AI_DUCKED))
- return false;
- return blocked_checkplat(self, dist);
- }
- //
- // DEATH
- //
- void soldier_fire6(edict_t *self)
- {
- soldier_fire(self, 5, false);
- if (self->dmg)
- self->monsterinfo.nextframe = FRAME_death126;
- }
- void soldier_fire7(edict_t *self)
- {
- soldier_fire(self, 6, false);
- }
- void soldier_dead(edict_t *self)
- {
- self->mins = { -16, -16, -24 };
- self->maxs = { 16, 16, -8 };
- monster_dead(self);
- }
- static void soldier_death_shrink(edict_t *self)
- {
- self->svflags |= SVF_DEADMONSTER;
- self->maxs[2] = 0;
- gi.linkentity(self);
- }
- mframe_t soldier_frames_death1[] = {
- { ai_move },
- { ai_move, -10 },
- { ai_move, -10 },
- { ai_move, -10, soldier_death_shrink },
- { ai_move, -5 },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move, 0, soldierh_hyper_laser_sound_start },
- { ai_move, 0, soldier_fire6 },
- { ai_move },
- { ai_move },
- { ai_move, 0, soldier_fire7 },
- { ai_move, 0, soldierh_hyper_laser_sound_end },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move }
- };
- MMOVE_T(soldier_move_death1) = { FRAME_death101, FRAME_death136, soldier_frames_death1, soldier_dead };
- mframe_t soldier_frames_death2[] = {
- { ai_move, -5 },
- { ai_move, -5 },
- { ai_move, -5 },
- { ai_move, 0, soldier_death_shrink },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move }
- };
- MMOVE_T(soldier_move_death2) = { FRAME_death201, FRAME_death235, soldier_frames_death2, soldier_dead };
- mframe_t soldier_frames_death3[] = {
- { ai_move, -5 },
- { ai_move, -5 },
- { ai_move, -5 },
- { ai_move, 0, soldier_death_shrink },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- };
- MMOVE_T(soldier_move_death3) = { FRAME_death301, FRAME_death345, soldier_frames_death3, soldier_dead };
- mframe_t soldier_frames_death4[] = {
- { ai_move },
- { ai_move },
- { ai_move, 1.5f },
- { ai_move, 2.5f },
- { ai_move, -1.5f },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move, -0.5f },
- { ai_move },
- { ai_move },
- { ai_move, 4.0f },
- { ai_move, 4.0f },
- { ai_move, 8.0f, soldier_death_shrink },
- { ai_move, 8.0f },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move, 5.5f },
- { ai_move, 2.5f },
- { ai_move, -2.0f },
- { ai_move, -2.0f }
- };
- MMOVE_T(soldier_move_death4) = { FRAME_death401, FRAME_death453, soldier_frames_death4, soldier_dead };
- mframe_t soldier_frames_death5[] = {
- { ai_move, -5 },
- { ai_move, -5 },
- { ai_move, -5 },
- { ai_move },
- { ai_move },
- { ai_move, 0, soldier_death_shrink },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move }
- };
- MMOVE_T(soldier_move_death5) = { FRAME_death501, FRAME_death524, soldier_frames_death5, soldier_dead };
- mframe_t soldier_frames_death6[] = {
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move, 0, soldier_death_shrink },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move }
- };
- MMOVE_T(soldier_move_death6) = { FRAME_death601, FRAME_death610, soldier_frames_death6, soldier_dead };
- DIE(soldier_die) (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, const vec3_t &point, const mod_t &mod) -> void
- {
- int n;
- soldierh_hyper_laser_sound_end(self);
- // check for gib
- if (M_CheckGib(self, mod))
- {
- gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
- self->s.skinnum /= 2;
- if (self->beam)
- {
- G_FreeEdict(self->beam);
- self->beam = nullptr;
- }
- ThrowGibs(self, damage, {
- { 3, "models/objects/gibs/sm_meat/tris.md2" },
- { "models/objects/gibs/bone2/tris.md2" },
- { "models/objects/gibs/bone/tris.md2" },
- { "models/monsters/soldier/gibs/arm.md2", GIB_SKINNED },
- { "models/monsters/soldier/gibs/gun.md2", GIB_SKINNED | GIB_UPRIGHT },
- { "models/monsters/soldier/gibs/chest.md2", GIB_SKINNED },
- { "models/monsters/soldier/gibs/head.md2", GIB_HEAD | GIB_SKINNED }
- });
- self->deadflag = true;
- return;
- }
- if (self->deadflag)
- return;
- // regular death
- self->deadflag = true;
- self->takedamage = true;
-
- n = self->count | 1;
- if (n == 1)
- gi.sound(self, CHAN_VOICE, sound_death_light, 1, ATTN_NORM, 0);
- else if (n == 3)
- gi.sound(self, CHAN_VOICE, sound_death, 1, ATTN_NORM, 0);
- else // (n == 5)
- gi.sound(self, CHAN_VOICE, sound_death_ss, 1, ATTN_NORM, 0);
- if (fabsf((self->s.origin[2] + self->viewheight) - point[2]) <= 4 &&
- self->velocity.z < 65.f)
- {
- // head shot
- M_SetAnimation(self, &soldier_move_death3);
- return;
- }
- // if we die while on the ground, do a quicker death4
- if (self->monsterinfo.active_move == &soldier_move_trip ||
- self->monsterinfo.active_move == &soldier_move_attack5)
- {
- M_SetAnimation(self, &soldier_move_death4);
- self->monsterinfo.nextframe = FRAME_death413;
- soldier_death_shrink(self);
- return;
- }
- // only do the spin-death if we have enough velocity to justify it
- if (self->velocity.z > 65.f || self->velocity.length() > 150.f)
- n = irandom(5);
- else
- n = irandom(4);
- if (n == 0)
- M_SetAnimation(self, &soldier_move_death1);
- else if (n == 1)
- M_SetAnimation(self, &soldier_move_death2);
- else if (n == 2)
- M_SetAnimation(self, &soldier_move_death4);
- else if (n == 3)
- M_SetAnimation(self, &soldier_move_death5);
- else
- M_SetAnimation(self, &soldier_move_death6);
- }
- //
- // NEW DODGE CODE
- //
- MONSTERINFO_SIDESTEP(soldier_sidestep) (edict_t *self) -> bool
- {
- // don't sidestep during trip or up pain
- if (self->monsterinfo.active_move == &soldier_move_trip ||
- self->monsterinfo.active_move == &soldier_move_attack5 ||
- self->monsterinfo.active_move == &soldier_move_pain4)
- return false;
- if (self->count <= 3)
- {
- if (self->monsterinfo.active_move != &soldier_move_attack6)
- {
- M_SetAnimation(self, &soldier_move_attack6);
- soldierh_hyper_laser_sound_end(self);
- }
- }
- else
- {
- if (self->monsterinfo.active_move != &soldier_move_start_run &&
- self->monsterinfo.active_move != &soldier_move_run)
- {
- M_SetAnimation(self, &soldier_move_start_run);
- soldierh_hyper_laser_sound_end(self);
- }
- }
- return true;
- }
- MONSTERINFO_DUCK(soldier_duck) (edict_t *self, gtime_t eta) -> bool
- {
- self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
- if (self->monsterinfo.active_move == &soldier_move_attack6)
- {
- M_SetAnimation(self, &soldier_move_trip);
- }
- else if (self->dmg || brandom())
- {
- M_SetAnimation(self, &soldier_move_duck);
- }
- else
- {
- M_SetAnimation(self, &soldier_move_attack3);
- }
- soldierh_hyper_laser_sound_end(self);
- return true;
- }
- //=========
- // ROGUE
- void soldier_blind(edict_t *self);
- mframe_t soldier_frames_blind[] = {
- { ai_move, 0, soldier_idle },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move },
- { ai_move }
- };
- MMOVE_T(soldier_move_blind) = { FRAME_stand101, FRAME_stand130, soldier_frames_blind, soldier_blind };
- MONSTERINFO_STAND(soldier_blind) (edict_t *self) -> void
- {
- M_SetAnimation(self, &soldier_move_blind);
- }
- // ROGUE
- //=========
- //
- // SPAWN
- //
- constexpr spawnflags_t SPAWNFLAG_SOLDIER_BLIND = 8_spawnflag;
- void SP_monster_soldier_x(edict_t *self)
- {
- self->s.modelindex = gi.modelindex("models/monsters/soldier/tris.md2");
- self->monsterinfo.scale = MODEL_SCALE;
- self->mins = { -16, -16, -24 };
- self->maxs = { 16, 16, 32 };
- self->movetype = MOVETYPE_STEP;
- self->solid = SOLID_BBOX;
- sound_idle.assign("soldier/solidle1.wav");
- sound_sight1.assign("soldier/solsght1.wav");
- sound_sight2.assign("soldier/solsrch1.wav");
- sound_cock.assign("infantry/infatck3.wav");
-
- gi.modelindex("models/monsters/soldier/gibs/head.md2");
- gi.modelindex("models/monsters/soldier/gibs/gun.md2");
- gi.modelindex("models/monsters/soldier/gibs/arm.md2");
- gi.modelindex("models/monsters/soldier/gibs/chest.md2");
- self->mass = 100;
- self->pain = soldier_pain;
- self->die = soldier_die;
- self->monsterinfo.stand = soldier_stand;
- self->monsterinfo.walk = soldier_walk;
- self->monsterinfo.run = soldier_run;
- self->monsterinfo.dodge = M_MonsterDodge;
- self->monsterinfo.attack = soldier_attack;
- self->monsterinfo.melee = nullptr;
- self->monsterinfo.sight = soldier_sight;
- self->monsterinfo.setskin = soldier_setskin;
- //=====
- // ROGUE
- self->monsterinfo.blocked = soldier_blocked;
- self->monsterinfo.duck = soldier_duck;
- self->monsterinfo.unduck = monster_duck_up;
- self->monsterinfo.sidestep = soldier_sidestep;
- if (self->spawnflags.has(SPAWNFLAG_SOLDIER_BLIND)) // blind
- self->monsterinfo.stand = soldier_blind;
- // ROGUE
- //=====
- gi.linkentity(self);
- self->monsterinfo.stand(self);
- walkmonster_start(self);
- }
- void SP_monster_soldier_vanilla(edict_t *self)
- {
- SP_monster_soldier_x(self);
- }
- /*QUAKED monster_soldier_light (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
- */
- void SP_monster_soldier_light(edict_t *self)
- {
- if ( !M_AllowSpawn( self ) ) {
- G_FreeEdict( self );
- return;
- }
- SP_monster_soldier_x(self);
- sound_pain_light.assign("soldier/solpain2.wav");
- sound_death_light.assign("soldier/soldeth2.wav");
- gi.modelindex("models/objects/laser/tris.md2");
- gi.soundindex("misc/lasfly.wav");
- gi.soundindex("soldier/solatck2.wav");
- self->s.skinnum = 0;
- self->count = self->s.skinnum;
- self->health = self->max_health = 20 * st.health_multiplier;
- self->gib_health = -30;
- // PMM - blindfire
- self->monsterinfo.blindfire = true;
- }
- /*QUAKED monster_soldier (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
- */
- void SP_monster_soldier(edict_t *self)
- {
- if( !M_AllowSpawn( self ) ) {
- G_FreeEdict( self );
- return;
- }
- SP_monster_soldier_x(self);
- sound_pain.assign("soldier/solpain1.wav");
- sound_death.assign("soldier/soldeth1.wav");
- gi.soundindex("soldier/solatck1.wav");
- self->s.skinnum = 2;
- self->count = self->s.skinnum;
- self->health = self->max_health = 30 * st.health_multiplier;
- self->gib_health = -30;
- }
- /*QUAKED monster_soldier_ss (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
- */
- void SP_monster_soldier_ss(edict_t *self)
- {
- if ( !M_AllowSpawn( self ) ) {
- G_FreeEdict( self );
- return;
- }
- SP_monster_soldier_x(self);
- sound_pain_ss.assign("soldier/solpain3.wav");
- sound_death_ss.assign("soldier/soldeth3.wav");
- gi.soundindex("soldier/solatck3.wav");
- self->s.skinnum = 4;
- self->count = self->s.skinnum;
- self->health = self->max_health = 40 * st.health_multiplier;
- self->gib_health = -30;
- }
- //
- // SPAWN
- //
- void SP_monster_soldier_h(edict_t *self)
- {
- SP_monster_soldier_x(self);
- self->style = 1;
- }
- /*QUAKED monster_soldier_ripper (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
- */
- void SP_monster_soldier_ripper(edict_t *self)
- {
- if ( !M_AllowSpawn( self ) ) {
- G_FreeEdict( self );
- return;
- }
- SP_monster_soldier_h(self);
- sound_pain_light.assign("soldier/solpain2.wav");
- sound_death_light.assign("soldier/soldeth2.wav");
- gi.modelindex("models/objects/boomrang/tris.md2");
- gi.soundindex("misc/lasfly.wav");
- gi.soundindex("soldier/solatck2.wav");
- self->s.skinnum = 6;
- self->count = self->s.skinnum - 6;
- self->health = self->max_health = 50 * st.health_multiplier;
- self->gib_health = -30;
- // PMM - blindfire
- self->monsterinfo.blindfire = true;
- }
- /*QUAKED monster_soldier_hypergun (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
- */
- void SP_monster_soldier_hypergun(edict_t *self)
- {
- if ( !M_AllowSpawn( self ) ) {
- G_FreeEdict( self );
- return;
- }
- SP_monster_soldier_h(self);
- gi.modelindex("models/objects/laser/tris.md2");
- sound_pain.assign("soldier/solpain1.wav");
- sound_death.assign("soldier/soldeth1.wav");
- gi.soundindex("soldier/solatck1.wav");
- gi.soundindex("weapons/hyprbd1a.wav");
- gi.soundindex("weapons/hyprbl1a.wav");
- self->s.skinnum = 8;
- self->count = self->s.skinnum - 6;
- self->health = self->max_health = 60 * st.health_multiplier;
- self->gib_health = -30;
- // PMM - blindfire
- self->monsterinfo.blindfire = true;
- }
- /*QUAKED monster_soldier_lasergun (1 .5 0) (-16 -16 -24) (16 16 32) Ambush Trigger_Spawn Sight
- */
- void SP_monster_soldier_lasergun(edict_t *self)
- {
- if ( !M_AllowSpawn( self ) ) {
- G_FreeEdict( self );
- return;
- }
- SP_monster_soldier_h(self);
- sound_pain_ss.assign("soldier/solpain3.wav");
- sound_death_ss.assign("soldier/soldeth3.wav");
- gi.soundindex("soldier/solatck3.wav");
- self->s.skinnum = 10;
- self->count = self->s.skinnum - 6;
- self->health = self->max_health = 70 * st.health_multiplier;
- self->gib_health = -30;
- }
- // END 13-APR-98
|