/* * Copyright (c) 2023 Marco Cawthorne * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /*QUAKED weapon_crossbow (0 0 1) (-16 -16 0) (16 16 32) "model" "models/g_light.mdl" HALF-LIFE (1998) ENTITY Crossbow Weapon */ #ifdef CLIENT #define LIGHTNINGBEAM_COUNT 16 string g_lightning_beamtex; static float lightning_jitlut[BEAM_COUNT] = { 0.000000, 0.195090, 0.382683, 0.555570, 0.707106, 0.831469, 0.923879, 0.980785, 1.000000, 0.980786, 0.923880, 0.831471, 0.707108, 0.555572, 0.382685, 0.000000, }; void w_lightning_renderbeam(string spriteFrame, vector startOrg, vector endOrg, float beamWidth, float beamAmp, vector colorTint, float timeScale, float seedValue) { #if 1 vector vecPlayer; NSClientPlayer pl; #define RANDOMNUM seedValue int s = (float)getproperty(VF_ACTIVESEAT); pSeat = &g_seats[s]; pl = (NSClientPlayer)pSeat->m_ePlayer; vecPlayer = pl.GetEyePos(); if (spriteFrame) { float last_progression = 0.0f; makevectors(g_view.GetCameraAngle()); setproperty(VF_ORIGIN, vecPlayer); R_BeginPolygon(spriteFrame, DRAWFLAG_ADDITIVE, 0); for (float i = 0; i < LIGHTNINGBEAM_COUNT; i++) { float progression = (i / (LIGHTNINGBEAM_COUNT-1)); vector point; vector jitter; float a = 1.0f; /* our steps from a - b */ point[0] = Math_Lerp(startOrg[0], endOrg[0], progression); point[1] = Math_Lerp(startOrg[1], endOrg[1], progression); point[2] = Math_Lerp(startOrg[2], endOrg[2], progression); /* get the direction the beam is 'looking' */ makevectors(vectoangles(endOrg - startOrg)); /* nudge it a lil bit up/down left/right from its trajectory */ /* these are all randomly chosen constants */ jitter = v_right * (pseudorand((timeScale * time) + i + RANDOMNUM) - 0.5); jitter += v_up * (pseudorand((timeScale * time) + i + 64.12 + RANDOMNUM) - 0.5); jitter += v_right * (pseudorand(100 + ((timeScale*2) * time) + i + RANDOMNUM) - 0.5); jitter += v_up * (pseudorand(100 + ((timeScale*2) * time) + i + 25.4 + RANDOMNUM) - 0.5); jitter *= beamAmp; /* start/end points get less jittery the closer we get*/ jitter *= lightning_jitlut[i]; /* apply jitter */ point += jitter; R_PolygonVertex(point, [1, 0], colorTint, a); if (autocvar(cl_showoff, 0)) dynamiclight_add(point, 150, [0.25, 0.25, 1.0]); last_progression = progression; } R_EndPolygonRibbon(beamWidth, [-1,0]); } #else vector vecPlayer; NSClientPlayer pl; int s = (float)getproperty(VF_ACTIVESEAT); pSeat = &g_seats[s]; pl = (NSClientPlayer)pSeat->m_ePlayer; vecPlayer = pl.GetEyePos(); makevectors(g_view.GetCameraAngle()); setproperty(VF_ORIGIN, vecPlayer); R_BeginPolygon(spriteFrame, DRAWFLAG_ADDITIVE, 0); R_PolygonVertex(startOrg, [1, 0], colorTint, 1.0); R_PolygonVertex(endOrg, [1, 0], colorTint, 1.0); R_EndPolygonRibbon(4, [-1,0]); #endif } #endif enum { LIGHTNING_IDLE, LIGHTNING_SHOOT, }; void w_lightning_precache(void) { #ifdef SERVER Sound_Precache("weapon_lightning.start"); Sound_Precache("weapon_lightning.shaft"); precache_model("models/g_light.mdl"); #else g_lightning_beamtex = spriteframe("sprites/lgtning.spr", 0, 0.0f); precache_model("models/v_light.mdl"); precache_model("models/p_light.mdl"); #endif } void w_lightning_updateammo(player pl) { Weapons_UpdateAmmo(pl, -1, pl.ammo_cells, -1); } string w_lightning_wmodel(void) { return "models/g_light.mdl"; } string w_lightning_pmodel(player pl) { return "models/p_light.mdl"; } string w_lightning_deathmsg(void) { return ""; } int w_lightning_pickup(player pl, int new, int startammo) { #ifdef SERVER if (pl.ammo_cells < MAX_A_CELLS) { pl.ammo_cells = bound(0, pl.ammo_cells + 15, MAX_A_CELLS); } else { if (!new) return (0); } #endif return (1); } void w_lightning_draw(player pl) { Weapons_SetModel("models/v_light.mdl"); Weapons_ViewAnimation(pl, LIGHTNING_IDLE); } void w_lightning_primary(player pl) { if (pl.w_attack_next > 0.0) return; if (pl.ammo_cells <= 0) { Weapons_SwitchBest(pl, 0); return; } #if 1 pl.ammo_cells--; #ifdef SERVER { if (pl.WaterLevel() > 1) { // we're underwater... fry everyone (around us) */ float lightDmg = (float)pl.ammo_cells * 35.0f; Damage_Radius(self.origin, self, lightDmg, lightDmg + 40, TRUE, WEAPON_LIGHTNING); } else { vector startPos = Weapons_GetCameraPos(pl); Weapons_MakeVectors(pl); traceline(startPos, startPos + v_forward * 600, MOVE_NORMAL, pl); WriteByte(MSG_MULTICAST, SVC_TEMPENTITY); WriteByte(MSG_MULTICAST, TE_LIGHTNING2); WriteEntity(MSG_MULTICAST, pl); WriteCoord(MSG_MULTICAST, startPos[0]); WriteCoord(MSG_MULTICAST, startPos[1]); WriteCoord(MSG_MULTICAST, startPos[2]); WriteCoord(MSG_MULTICAST, trace_endpos[0]); WriteCoord(MSG_MULTICAST, trace_endpos[1]); WriteCoord(MSG_MULTICAST, trace_endpos[2]); multicast(startPos, MULTICAST_PHS); if (trace_ent.takedamage) { Damage_Apply(trace_ent, pl, Skill_GetValue("plr_lightning", 30), WEAPON_LIGHTNING, DMG_BLUNT); /* throw players into the air */ if (trace_ent.classname == "player") { NSClientPlayer tp = (NSClientPlayer)trace_ent; tp.SetVelocity(tp.GetVelocity() + [0, 0, 400]); } } } } if (pl.HasQuadDamage()) { Sound_Play(pl, CHAN_ITEM, "item_artifact_super_damage.attack"); } if (!(pl.gflags & GF_EGONBEAM)) Sound_Play(pl, CHAN_WEAPON, "weapon_lightning.start"); else { if (pl.m_lightningTime < time) { Sound_Play(pl, CHAN_WEAPON, "weapon_lightning.shaft"); pl.m_lightningTime = time + 0.6f; } } #endif Weapons_ViewAnimation(pl, LIGHTNING_SHOOT); Animation_PlayerTop(pl, (pl.flags & FL_CROUCHING) ? ANIM_CR_SHOOTGAUSS : ANIM_SHOOTGAUSS, 0.43f); Weapons_ViewPunchAngle(pl, [-2,0,0]); pl.w_attack_next = 0.1f; pl.gflags |= GF_EGONBEAM; #else #ifdef SERVER static float foo = 0; Weapons_MakeVectors(pl); NSPortal_Spawn(pl.origin, v_forward, pl, foo); foo = 1 - foo; #endif Weapons_ViewAnimation(pl, LIGHTNING_SHOOT); Animation_PlayerTop(pl, (pl.flags & FL_CROUCHING) ? ANIM_CR_SHOOTGAUSS : ANIM_SHOOTGAUSS, 0.43f); Weapons_ViewPunchAngle(pl, [-2,0,0]); pl.w_attack_next = 1.0f; pl.gflags |= GF_EGONBEAM; #endif } void w_lightning_crosshair(player pl) { #ifdef CLIENT vector aicon_pos; HUD_DrawQuakeCrosshair(); HUD_DrawAmmo2(); aicon_pos = g_hudmins + [g_hudres[0] - 48, g_hudres[1] - 42]; drawsubpic( aicon_pos, [24,24], g_hudlgammo, [0, 0], [24/24, 24/24], g_hud_color, pSeatLocal->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE ); #endif } float w_lightning_aimanim(player pl) { return pl.flags & FL_CROUCHING ? ANIM_CR_AIMGAUSS : ANIM_AIMGAUSS; } int w_lightning_isempty(player pl) { if (pl.ammo_cells <= 0) return 1; return 0; } void w_lightning_hudpic(player pl, int selected, vector pos, float a) { #ifdef CLIENT vector hud_col; if (w_lightning_isempty(pl)) hud_col = [1,0,0]; else hud_col = g_hud_color; if (selected) { drawsubpic(pos, [170,45], g_hudlg_spr, [0,0], [1,0.625], hud_col, a, DRAWFLAG_ADDITIVE); } else { drawsubpic(pos, [170,45], g_hudlg_spr, [0,0], [1,0.625], hud_col, a, DRAWFLAG_ADDITIVE); } HUD_DrawAmmoBar(pos, pl.ammo_cells, MAX_A_CELLS, a); #endif } weapontype_t w_lightning_type(player pl) { return WPNTYPE_RANGED; } void w_lightning_postdraw(player pl, int thirdperson) { #ifdef CLIENT if (!(pl.gflags & GF_EGONBEAM)) return; vector src; vector endpos; if (thirdperson) { makevectors(pl.v_angle); src = pl.origin; endpos = pl.origin + (v_forward * 1024); traceline(src, endpos, MOVE_NORMAL, pl); //w_lightning_beamfx(gettaginfo(pl.p_model, 10), trace_endpos, pl); w_lightning_renderbeam(g_lightning_beamtex, gettaginfo(pl.p_model, 10), trace_endpos, trace_fraction * 32, trace_fraction * 48.0, [1,1,1], 60.0f, 12516); } else { vector gunpos = gettaginfo(pSeat->m_eViewModel, 33); src = pl.GetEyePos(); makevectors(view_angles); gunpos += (v_up * -16) + (v_forward * 42); endpos = src + v_forward * 1024; traceline(src, endpos, FALSE, pl); endpos = trace_endpos; //w_lightning_beamfx(gunpos, endpos, pl); w_lightning_renderbeam(g_lightning_beamtex, gunpos, endpos, trace_fraction * 32, trace_fraction * 48.0, [1,1,1], 60.0f, 125156); } int i = (cltime*10.0f) % 11.0f; vector fsize = [32,32]; makevectors(view_angles); trace_endpos += v_forward * -16; /* nudge towards our camera */ dynamiclight_add(trace_endpos, 128, [0.5, 0.5, 1.0]); #endif } void w_lightning_release(player pl) { pl.gflags &= ~GF_EGONBEAM; } weapon_t w_lightning = { .name = "lightning", .id = ITEM_LIGHTNING, .slot = 7, .slot_pos = 0, .weight = 1, .draw = w_lightning_draw, .holster = __NULL__, .primary = w_lightning_primary, .secondary = __NULL__, .reload = __NULL__, .release = w_lightning_release, .postdraw = w_lightning_crosshair, .precache = w_lightning_precache, .pickup = w_lightning_pickup, .updateammo = w_lightning_updateammo, .wmodel = w_lightning_wmodel, .pmodel = w_lightning_pmodel, .deathmsg = w_lightning_deathmsg, .aimanim = w_lightning_aimanim, .isempty = w_lightning_isempty, .type = w_lightning_type, .hudpic = w_lightning_hudpic, .predraw = w_lightning_postdraw };