commit 1a25ccc3341ed6e01b75d66f9f4fd2fb3273ac2f Author: Marco Hladik Date: Mon Mar 8 10:53:48 2021 +0100 Initial commit, carried over from Nuclide's Git on March 8th 2021 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..73679b8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2016-2021, Marco "eukara" Hladik + +Permission to use, copy, modify, and/or 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 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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..9864ee1 --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +# FreeHL +Clean-room reimplementation of Half-Life: Deathmatch and Half-Life (Experimental). + +Similar to FreeCS, this aims to recreate the feeling of the original game. +It's designed to work with the content from the CD version of the game. + +Netplay improvements, such as prediction of both player physics and weapon-logic +are present. + +This is 100% original code, feel free to audit however you wish. +No proprietary SDKs have been looked at or taken apart, unlike similar projects. + +![Preview 1](img/preview1.jpg) +![Preview 2](img/preview2.jpg) +![Preview 3](img/preview3.jpg) +![Preview 4](img/preview4.jpg) + +## Building +Clone the repository into the Nuclide-SDK: + +> git clone REPOURL valve + +then either run Nuclide's ./build_game.sh shell script, or issue 'make' inside +./valve/src! + +Obviously make sure that Nuclide has fteqw and fteqcc set-up for building. + +## Community +Join us on #halflife via irc.frag-net.com and chat. + +## License +ISC License + +Copyright (c) 2016-2021 Marco Hladik + +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. diff --git a/img/preview1.jpg b/img/preview1.jpg new file mode 100644 index 0000000..898f71d Binary files /dev/null and b/img/preview1.jpg differ diff --git a/img/preview2.jpg b/img/preview2.jpg new file mode 100644 index 0000000..c9a75cf Binary files /dev/null and b/img/preview2.jpg differ diff --git a/img/preview3.jpg b/img/preview3.jpg new file mode 100644 index 0000000..ad0c591 Binary files /dev/null and b/img/preview3.jpg differ diff --git a/img/preview4.jpg b/img/preview4.jpg new file mode 100644 index 0000000..3bff75a Binary files /dev/null and b/img/preview4.jpg differ diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..cc3f82d --- /dev/null +++ b/src/Makefile @@ -0,0 +1,5 @@ +CC=fteqcc + +all: + cd client && $(MAKE) + cd server && $(MAKE) diff --git a/src/client/Makefile b/src/client/Makefile new file mode 100644 index 0000000..627019a --- /dev/null +++ b/src/client/Makefile @@ -0,0 +1,4 @@ +CC=fteqcc + +all: + $(CC) progs.src diff --git a/src/client/cmds.qc b/src/client/cmds.qc new file mode 100644 index 0000000..7109de1 --- /dev/null +++ b/src/client/cmds.qc @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +int +ClientGame_ConsoleCommand(void) +{ + switch(argv(0)) { + default: + return FALSE; + } + return TRUE; +} diff --git a/src/client/defs.h b/src/client/defs.h new file mode 100644 index 0000000..60dd330 --- /dev/null +++ b/src/client/defs.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2016-2021 Marco Hladik + * + * 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. + */ + +#include "obituary.h" +#include "particles.h" + +var int autocvar_cl_autoweaponswitch = TRUE; + +vector g_hud_color; +vector g_hudmins; +vector g_hudres; + +var string g_hud1_spr; +var string g_hud2_spr; +var string g_hud3_spr; +var string g_hud4_spr; +var string g_hud5_spr; +var string g_hud6_spr; +var string g_hud7_spr; +var string g_cross_spr; +var string g_laser_spr; + +struct +{ + /* viewmodel stuff */ + entity m_eViewModel; + entity m_eMuzzleflash; + int m_iVMBones; + int m_iVMEjectBone; + int m_iLastWeapon; + int m_iOldWeapon; + float m_flBobTime; + float m_flBob; + + /* damage overlay */ + float m_flDamageAlpha; + vector m_vecDamagePos; + + /* +zoomin cmd */ + int m_iZoomed; + float m_flZoomTime; + + /* player fields */ + entity m_ePlayer; + vector m_vecPredictedOrigin; + vector m_vecPredictedOriginOld; + vector m_vecPredictedVelocity; + float m_flPredictedFlags; + + /* camera fields */ + vector m_vecCameraOrigin; + vector m_vecCameraAngle; + float m_flCameraTime; + + /* hud.c */ + int m_iHealthOld; + float m_flHealthAlpha; + int m_iArmorOld; + float m_flArmorAlpha; + int m_iAmmo1Old; + float m_flAmmo1Alpha; + int m_iAmmo2Old; + float m_flAmmo2Alpha; + int m_iAmmo3Old; + float m_flAmmo3Alpha; + int m_iPickupWeapon; + float m_flPickupAlpha; + + /* This is seperated from the other VGUI stuff so we can check scores + * while buying and whatnot */ + int m_iScoresVisible; + int m_iHUDWeaponSelected; + float m_flHUDWeaponSelectTime; + + /* saturn controller */ + int m_iSaturnMenu; + + /* centerprint related */ + float m_flCenterprintAlpha; + float m_flCenterprintTime; + float m_iCenterprintLines; + string m_strCenterprintBuffer[18]; + + /* chat related */ + float m_flPrintTime; + string m_strPrintBuffer[5]; + int m_iPrintLines; + + int m_iInputAttack2; + int m_iInputReload; + int m_iInputUse; + int m_iInputDuck; + float m_flInputBlockTime; + + /* fading */ + float m_flFadeDuration; + float m_flFadeHold; + float m_flFadeMaxAlpha; + float m_flFadeStyle; + float m_flFadeAlpha; + float m_flFadeTime; + vector m_vecFadeColor; + int m_iFadeActive; + + /* shake */ + float m_flShakeFreq; + float m_flShakeDuration; + float m_flShakeTime; + float m_flShakeAmp; + + entity m_pWeaponFX; +} g_seats[4], *pSeat; + +void HUD_DrawAmmo1(void); +void HUD_DrawAmmo2(void); +void HUD_DrawAmmo3(void); +void HUD_DrawAmmoBar(vector pos, float val, float max, float a); +void HUD_WeaponPickupNotify(int); diff --git a/src/client/entities.qc b/src/client/entities.qc new file mode 100644 index 0000000..d0ccf21 --- /dev/null +++ b/src/client/entities.qc @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +int +ClientGame_EntityUpdate(float id, float new) +{ + switch (id) { + case ENT_TRIPMINE: + w_tripmine_parse(); + break; + default: + return FALSE; + } + + return TRUE; +} diff --git a/src/client/game_event.qc b/src/client/game_event.qc new file mode 100644 index 0000000..233d752 --- /dev/null +++ b/src/client/game_event.qc @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +void +ClientGame_EventParse(float fHeader) +{ + switch (fHeader) { + case EV_OBITUARY: + Obituary_Parse(); + break; + case EV_SPARK: + vector vSparkPos, vSparkAngle; + vSparkPos[0] = readcoord(); + vSparkPos[1] = readcoord(); + vSparkPos[2] = readcoord(); + vSparkAngle[0] = readcoord(); + vSparkAngle[1] = readcoord(); + vSparkAngle[2] = readcoord(); + FX_Spark(vSparkPos, vSparkAngle); + break; + case EV_GIBHUMAN: + vector vGibPos; + vGibPos[0] = readcoord(); + vGibPos[1] = readcoord(); + vGibPos[2] = readcoord(); + FX_GibHuman(vGibPos); + break; + case EV_BLOOD: + vector vBloodPos; + vector vBloodColor; + + vBloodPos[0] = readcoord(); + vBloodPos[1] = readcoord(); + vBloodPos[2] = readcoord(); + + vBloodColor[0] = readbyte() / 255; + vBloodColor[1] = readbyte() / 255; + vBloodColor[2] = readbyte() / 255; + + FX_Blood(vBloodPos, vBloodColor); + break; + case EV_EXPLOSION: + vector vExploPos; + + vExploPos[0] = readcoord(); + vExploPos[1] = readcoord(); + vExploPos[2] = readcoord(); + + FX_Explosion(vExploPos); + break; + case EV_MODELGIB: + vector vecPos; + vecPos[0] = readcoord(); + vecPos[1] = readcoord(); + vecPos[2] = readcoord(); + + vector vSize; + vSize[0] = readcoord(); + vSize[1] = readcoord(); + vSize[2] = readcoord(); + + float fStyle = readbyte(); + int count = readbyte(); + FX_BreakModel(count, vecPos, vSize, [0,0,0], fStyle); + break; + case EV_IMPACT: + int iType; + vector vOrigin, vNormal; + + iType = (int)readbyte(); + vOrigin[0] = readcoord(); + vOrigin[1] = readcoord(); + vOrigin[2] = readcoord(); + + vNormal[0] = readcoord(); + vNormal[1] = readcoord(); + vNormal[2] = readcoord(); + + FX_Impact(iType, vOrigin, vNormal); + break; + case EV_CHAT: + float fSender = readbyte(); + float fTeam = readbyte(); + string sMessage = readstring(); + + CSQC_Parse_Print(sprintf("%s: %s", getplayerkeyvalue(fSender, "name"), sMessage), PRINT_CHAT); + break; + case EV_CHAT_TEAM: + float fSender2 = readbyte(); + float fTeam2 = readbyte(); + string sMessage2 = readstring(); + + CSQC_Parse_Print(sprintf("[TEAM] %s: %s", getplayerkeyvalue(fSender2, "name"), sMessage2), PRINT_CHAT); + break; + case EV_CHAT_VOX: + Vox_Play(readstring()); + break; + case EV_VIEWMODEL: + View_PlayAnimation(readbyte()); + break; + case EV_WEAPON_PICKUP: + int w = readbyte(); + + if (autocvar_cl_autoweaponswitch == 1) { + sendevent("PlayerSwitchWeapon", "i", w); + } + + HUD_WeaponPickupNotify(w); + break; + } +} diff --git a/src/client/hud.qc b/src/client/hud.qc new file mode 100644 index 0000000..06b59e5 --- /dev/null +++ b/src/client/hud.qc @@ -0,0 +1,438 @@ +/* + * Copyright (c) 2016-2021 Marco Hladik + * + * 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. + */ + +void HUD_DrawWeaponSelect(void); + +/* Use first frame for drawing (needs precache) */ +#define NUMSIZE_X 24/256 +#define NUMSIZE_Y 24/128 +#define HUD_ALPHA 0.5 + +float spr_hudnum[10] = { + 0 / 256, + 24 / 256, + (24*2) / 256, + (24*3) / 256, + (24*4) / 256, + (24*5) / 256, + (24*6) / 256, + (24*7) / 256, + (24*8) / 256, + (24*9) / 256 +}; + +/* pre-calculated sprite definitions */ +float spr_health[4] = { + 80 / 256, // pos x + 24 / 128, // pos u + 32 / 256, // size x + 32 / 128 // size y +}; + +float spr_suit1[4] = { + 0 / 256, // pos x + 24 / 128, // pos u + 40 / 256, // size x + 40 / 128 // size y +}; + +float spr_suit2[4] = { + 40 / 256, // pos x + 24 / 128, // pos u + 40 / 256, // size x + 40 / 128 // size y +}; + +float spr_flash1[4] = { + 160 / 256, // pos x + 24 / 128, // pos u + 32 / 256, // size x + 32 / 128 // size y +}; + +float spr_flash2[4] = { + 112 / 256, // pos x + 24 / 128, // pos u + 48 / 256, // size x + 32 / 128 // size y +}; + +/* precaches */ +void +HUD_Init(void) +{ + g_cross_spr = spriteframe("sprites/crosshairs.spr", 0, 0.0f); + g_laser_spr = spriteframe("sprites/laserdot.spr", 0, 0.0f); + g_hud1_spr = spriteframe("sprites/640hud1.spr", 0, 0.0f); + g_hud2_spr = spriteframe("sprites/640hud2.spr", 0, 0.0f); + g_hud3_spr = spriteframe("sprites/640hud3.spr", 0, 0.0f); + g_hud4_spr = spriteframe("sprites/640hud4.spr", 0, 0.0f); + g_hud5_spr = spriteframe("sprites/640hud5.spr", 0, 0.0f); + g_hud6_spr = spriteframe("sprites/640hud6.spr", 0, 0.0f); + g_hud7_spr = spriteframe("sprites/640hud7.spr", 0, 0.0f); +} + +/* seperator for mainly ammo */ +void +HUD_DrawSeperator(vector pos) +{ + drawsubpic(pos, + [2,24], + g_hud7_spr, + [240/256, 0], + [2/256, 24/128], + g_hud_color, + HUD_ALPHA, + DRAWFLAG_ADDITIVE + ); +} + +/* handle single/multiple digits */ +void +HUD_DrawNumber(int iNumber, vector vecPos, float fAlpha, vector vColor) +{ + drawsubpic(vecPos, + [24,24], + g_hud7_spr, + [spr_hudnum[iNumber], 0], + [NUMSIZE_X, NUMSIZE_Y], + vColor, + fAlpha, + DRAWFLAG_ADDITIVE + ); +} + +void +HUD_DrawNums(float fNumber, vector vecPos, float fAlpha, vector vColor) +{ + int i = fNumber; + if (i > 0) { + while (i > 0) { + HUD_DrawNumber((float)i % 10, vecPos, fAlpha, vColor); + i = i / 10; + vecPos[0] -= 20; + } + } else { + HUD_DrawNumber(0, vecPos, fAlpha, vColor); + } +} + +/* health */ +void +HUD_DrawHealth(void) +{ + vector pos; + player pl = (player)pSeat->m_ePlayer; + + if (pl.health != pSeat->m_iHealthOld) { + pSeat->m_flHealthAlpha = 1.0; + } + + if (pSeat->m_flHealthAlpha >= HUD_ALPHA) { + pSeat->m_flHealthAlpha -= clframetime * 0.5; + } else { + pSeat->m_flHealthAlpha = HUD_ALPHA; + } + + pos = g_hudmins + [88, g_hudres[1] - 42]; + if (pl.health > 25) { + drawsubpic( + pos + [-72,-4], + [32,32], + g_hud7_spr, + [spr_health[0], spr_health[1]], + [spr_health[2], spr_health[3]], + g_hud_color, + pSeat->m_flHealthAlpha, + DRAWFLAG_ADDITIVE + ); + HUD_DrawNums(pl.health, pos, pSeat->m_flHealthAlpha, g_hud_color); + } else { + drawsubpic( + pos + [-72,-4], + [32,32], + g_hud7_spr, + [spr_health[0], spr_health[1]], + [spr_health[2], spr_health[3]], + [1,0,0], + pSeat->m_flHealthAlpha, + DRAWFLAG_ADDITIVE + ); + HUD_DrawNums(pl.health, pos, pSeat->m_flHealthAlpha, [1,0,0]); + } + + pSeat->m_iHealthOld = pl.health; +} + +/* armor/suit charge */ +void +HUD_DrawArmor(void) +{ + vector pos; + player pl = (player)pSeat->m_ePlayer; + + pos = g_hudmins + [198, g_hudres[1] - 42]; + + if (pl.armor != pSeat->m_iArmorOld) { + pSeat->m_flArmorAlpha = 1.0; + } + + if (pSeat->m_flArmorAlpha >= HUD_ALPHA) { + pSeat->m_flArmorAlpha -= clframetime * 0.5; + } else { + pSeat->m_flArmorAlpha = HUD_ALPHA; + } + + drawsubpic( + pos + [-80,-9], + [40,40], + g_hud7_spr, + [spr_suit2[0], spr_suit2[1]], + [spr_suit2[2], spr_suit2[3]], + g_hud_color, + pSeat->m_flArmorAlpha, + DRAWFLAG_ADDITIVE + ); + + if (pl.armor > 0) { + drawsubpic( + pos + [-80,-9], + [40, 40 * (pl.armor / 100)], + g_hud7_spr, + [spr_suit1[0], + spr_suit1[1]], + [spr_suit1[2], spr_suit1[3] * (pl.armor / 100)], + g_hud_color, + pSeat->m_flArmorAlpha, + DRAWFLAG_ADDITIVE + ); + } + + HUD_DrawNums(pl.armor, pos, pSeat->m_flArmorAlpha, g_hud_color); + pSeat->m_iArmorOld = pl.armor; +} + +/* magazine/clip ammo */ +void +HUD_DrawAmmo1(void) +{ + player pl = (player)pSeat->m_ePlayer; + vector pos; + + if (pl.a_ammo1 != pSeat->m_iAmmo1Old) { + pSeat->m_flAmmo1Alpha = 1.0; + pSeat->m_iAmmo1Old = pl.a_ammo1; + } + + if (pSeat->m_flAmmo1Alpha >= HUD_ALPHA) { + pSeat->m_flAmmo1Alpha -= clframetime * 0.5; + } else { + pSeat->m_flAmmo1Alpha = HUD_ALPHA; + } + + pos = g_hudmins + [g_hudres[0] - 152, g_hudres[1] - 42]; + HUD_DrawNums(pl.a_ammo1, pos, pSeat->m_flAmmo1Alpha, g_hud_color); + HUD_DrawSeperator(pos + [30,0]); +} + +/* leftover type ammo */ +void +HUD_DrawAmmo2(void) +{ + player pl = (player)pSeat->m_ePlayer; + vector pos; + + if (pl.a_ammo2 != pSeat->m_iAmmo2Old) { + pSeat->m_flAmmo2Alpha = 1.0; + pSeat->m_iAmmo2Old = pl.a_ammo2; + } + + if (pSeat->m_flAmmo2Alpha >= HUD_ALPHA) { + pSeat->m_flAmmo2Alpha -= clframetime * 0.5; + } else { + pSeat->m_flAmmo2Alpha = HUD_ALPHA; + } + + pos = g_hudmins + [g_hudres[0] - 72, g_hudres[1] - 42]; + HUD_DrawNums(pl.a_ammo2, pos, pSeat->m_flAmmo2Alpha, g_hud_color); +} + +/* special ammo */ +void +HUD_DrawAmmo3(void) +{ + player pl = (player)pSeat->m_ePlayer; + vector pos; + + if (pl.a_ammo3 != pSeat->m_iAmmo3Old) { + pSeat->m_flAmmo3Alpha = 1.0; + pSeat->m_iAmmo3Old = pl.a_ammo3; + } + + if (pSeat->m_flAmmo3Alpha >= HUD_ALPHA) { + pSeat->m_flAmmo3Alpha -= clframetime * 0.5; + } else { + pSeat->m_flAmmo3Alpha = HUD_ALPHA; + } + + pos = g_hudmins + [g_hudres[0] - 72, g_hudres[1] - 74]; + HUD_DrawNums(pl.a_ammo3, pos, pSeat->m_flAmmo3Alpha, g_hud_color); +} + +/* ammo bar */ +void +HUD_DrawAmmoBar(vector pos, float val, float max, float a) +{ + if (val <= 0) + return; + + float perc; + perc = val / max; + drawfill(pos + [10,0], [20,4], g_hud_color, a, DRAWFLAG_NORMAL); + drawfill(pos + [10,0], [20 * perc,4], [0,1,0], a, DRAWFLAG_NORMAL); +} + +/* flashlight/torch indicator */ +void +HUD_DrawFlashlight(void) +{ + vector pos; + player pl = (player)pSeat->m_ePlayer; + pos = g_hudmins + [g_hudres[0] - 48, 16]; + + /* both on, draw both sprites at full intensity */ + if (pl.gflags & GF_FLASHLIGHT) { + drawsubpic( + pos, + [32,32], + g_hud7_spr, + [spr_flash1[0], spr_flash1[1]], + [spr_flash1[2], spr_flash1[3]], + g_hud_color, + 1.0f, + DRAWFLAG_ADDITIVE + ); + + drawsubpic( + pos, + [48,32], + g_hud7_spr, + [spr_flash2[0], spr_flash2[1]], + [spr_flash2[2], spr_flash2[3]], + g_hud_color, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [32,32], + g_hud7_spr, + [spr_flash1[0], spr_flash1[1]], + [spr_flash1[2], spr_flash1[3]], + g_hud_color, + HUD_ALPHA, + DRAWFLAG_ADDITIVE + ); + } +} + +/* logo animation used during e3 1998 */ +void +HUD_DrawLogo(void) +{ + vector pos; + static int f; + static float frame_timer; + + frame_timer -= clframetime; + pos = [g_hudres[0] - 262, 48]; + + drawpic( + pos, + sprintf("sprites/640_logo.spr_%i.tga", f), + [256,48], + [1,1,1], + 1.0f, + DRAWFLAG_ADDITIVE + ); + + if (frame_timer > 0) { + return; + } + + frame_timer = 0.1f; + + f++; + if (f == 31) { + f = 0; + } +} + +/* weapon/ammo pickup notifications */ +void +HUD_DrawNotify(void) +{ + vector pos; + + if (pSeat->m_flPickupAlpha <= 0.0f) { + return; + } + + pos = g_hudmins + [g_hudres[0] - 192, g_hudres[1] - 128]; + Weapons_HUDPic(pSeat->m_iPickupWeapon, 1, pos, pSeat->m_flPickupAlpha); + pSeat->m_flPickupAlpha -= clframetime; +} + +void +HUD_WeaponPickupNotify(int w) +{ + pSeat->m_iPickupWeapon = w; + pSeat->m_flPickupAlpha = 1.0f; +} + +/* main entry */ +void +HUD_Draw(void) +{ + player pl = (player)pSeat->m_ePlayer; + + g_hud_color = autocvar_con_color * (1 / 255); + + /* little point in not drawing these, even if you don't have a suit */ + Weapons_DrawCrosshair(); + HUD_DrawWeaponSelect(); + Obituary_Draw(); + Textmenu_Draw(); + + if (!(pl.g_items & ITEM_SUIT)) { + return; + } + + HUD_DrawHealth(); + HUD_DrawArmor(); + HUD_DrawFlashlight(); + HUD_DrawNotify(); + Damage_Draw(); +} + +/* specatator main entry */ +void +HUD_DrawSpectator(void) +{ + // FIXME + Textmenu_Draw(); +} diff --git a/src/client/hud_weaponselect.qc b/src/client/hud_weaponselect.qc new file mode 100644 index 0000000..9b5b81f --- /dev/null +++ b/src/client/hud_weaponselect.qc @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +vector g_vecHUDNums[6] = +{ + [168 / 256, 72 / 128], + [188 / 256, 72 / 128], + [208 / 256, 72 / 128], + [168 / 256, 92 / 128], + [188 / 256, 92 / 128], + [208 / 256, 92 / 128] +}; + +void +HUD_DrawWeaponSelect_Forward(void) +{ + player pl = (player)pSeat->m_ePlayer; + + if (!pl.activeweapon) { + return; + } + + if (pSeat->m_flHUDWeaponSelectTime < time) { + pSeat->m_iHUDWeaponSelected = pl.activeweapon; + sound(pSeat->m_ePlayer, CHAN_ITEM, "common/wpn_hudon.wav", 0.5, ATTN_NONE); + } else { + sound(pSeat->m_ePlayer, CHAN_ITEM, "common/wpn_moveselect.wav", 0.5, ATTN_NONE); + pSeat->m_iHUDWeaponSelected--; + if (pSeat->m_iHUDWeaponSelected <= 0) { + pSeat->m_iHUDWeaponSelected = g_weapons.length - 1; + } + } + + pSeat->m_flHUDWeaponSelectTime = time + 3; + + if not (pl.g_items & g_weapons[pSeat->m_iHUDWeaponSelected].id) { + HUD_DrawWeaponSelect_Forward(); + } +} + +void +HUD_DrawWeaponSelect_Back(void) +{ + player pl = (player)pSeat->m_ePlayer; + + if (!pl.activeweapon) { + return; + } + + if (pSeat->m_flHUDWeaponSelectTime < time) { + pSeat->m_iHUDWeaponSelected = pl.activeweapon; + sound(pSeat->m_ePlayer, CHAN_ITEM, "common/wpn_hudon.wav", 0.5, ATTN_NONE); + } else { + sound(pSeat->m_ePlayer, CHAN_ITEM, "common/wpn_moveselect.wav", 0.5, ATTN_NONE); + pSeat->m_iHUDWeaponSelected++; + if (pSeat->m_iHUDWeaponSelected >= g_weapons.length) { + pSeat->m_iHUDWeaponSelected = 1; + } + } + + pSeat->m_flHUDWeaponSelectTime = time + 3; + + if not (pl.g_items & g_weapons[pSeat->m_iHUDWeaponSelected].id) { + HUD_DrawWeaponSelect_Back(); + } +} + +void +HUD_DrawWeaponSelect_Trigger(void) +{ + player pl = (player)pSeat->m_ePlayer; + pl.activeweapon = pSeat->m_iHUDWeaponSelected; + sendevent("PlayerSwitchWeapon", "i", pSeat->m_iHUDWeaponSelected); + sound(pSeat->m_ePlayer, CHAN_ITEM, "common/wpn_select.wav", 0.5f, ATTN_NONE); + pSeat->m_iHUDWeaponSelected = pSeat->m_flHUDWeaponSelectTime = 0; +} + +void +HUD_DrawWeaponSelect_Last(void) +{ + player pl = (player)pSeat->m_ePlayer; + if (pl.g_items & g_weapons[pSeat->m_iOldWeapon].id) { + pl.activeweapon = pSeat->m_iOldWeapon; + sendevent("PlayerSwitchWeapon", "i", pSeat->m_iOldWeapon); + } +} + +void +HUD_DrawWeaponSelect_Num(vector vecPos, float fValue) +{ + drawsubpic(vecPos, [20,20], g_hud7_spr, g_vecHUDNums[fValue], [20/256, 20/128], g_hud_color, 1, DRAWFLAG_ADDITIVE); +} + +int +HUD_InSlotPos(int slot, int pos) +{ + player pl = (player)pSeat->m_ePlayer; + for (int i = 1; i < g_weapons.length; i++) { + if (g_weapons[i].slot == slot && g_weapons[i].slot_pos == pos) { + if (pl.g_items & g_weapons[i].id) { + return i; + } else { + return -1; + } + } + } + return -1; +} + +void +HUD_SlotSelect(int slot) +{ + player pl = (player)pSeat->m_ePlayer; + int curslot = g_weapons[pSeat->m_iHUDWeaponSelected].slot; + int i; + + if (g_textmenu != "") { + Textmenu_Input(slot); + return; + } + + /* hack to see if we have ANY weapons at all. */ + if (!pl.activeweapon) { + return; + } + + if (pSeat->m_flHUDWeaponSelectTime < time) { + sound(pSeat->m_ePlayer, CHAN_ITEM, "common/wpn_hudon.wav", 0.5, ATTN_NONE); + } else { + sound(pSeat->m_ePlayer, CHAN_ITEM, "common/wpn_moveselect.wav", 0.5, ATTN_NONE); + } + + /* weren't in that slot? select the first one then */ + if (curslot != slot) { + for (i = 1; i < g_weapons.length; i++) { + if (g_weapons[i].slot == slot && pl.g_items & g_weapons[i].id) { + pSeat->m_iHUDWeaponSelected = i; + pSeat->m_flHUDWeaponSelectTime = time + 3; + break; + } + } + } else { + int first = -1; + for (i = 1; i < g_weapons.length; i++) { + if (g_weapons[i].slot == slot && pl.g_items & g_weapons[i].id) { + if (i < pSeat->m_iHUDWeaponSelected && first == -1) { + first = i; + } else if (i > pSeat->m_iHUDWeaponSelected) { + first = -1; + pSeat->m_iHUDWeaponSelected = i; + pSeat->m_flHUDWeaponSelectTime = time + 3; + break; + } + } + } + + if (first > 0) { + pSeat->m_iHUDWeaponSelected = first; + pSeat->m_flHUDWeaponSelectTime = time + 3; + } + } +} + +void +HUD_DrawWeaponSelect(void) +{ + player pl = (player)pSeat->m_ePlayer; + if (!pl.activeweapon) { + return; + } + if (pSeat->m_flHUDWeaponSelectTime < time) { + if (pSeat->m_iHUDWeaponSelected) { + sound(pSeat->m_ePlayer, CHAN_ITEM, "common/wpn_hudoff.wav", 0.5, ATTN_NONE); + pSeat->m_iHUDWeaponSelected = 0; + } + return; + } + + vector vecPos = g_hudmins + [16,16]; + + int b; + int wantslot = g_weapons[pSeat->m_iHUDWeaponSelected].slot; + int wantpos = g_weapons[pSeat->m_iHUDWeaponSelected].slot_pos; + for (int i = 0; i < 5; i++) { + int slot_selected = 0; + vecPos[1] = g_hudmins[1] + 16; + HUD_DrawWeaponSelect_Num(vecPos, i); + vecPos[1] += 20; + for (int x = 0; x < 32; x++) { + if (i == wantslot) { + slot_selected = TRUE; + if (x == wantpos) { + // Selected Sprite + Weapons_HUDPic(pSeat->m_iHUDWeaponSelected, 1, vecPos, 1.0f); + drawsubpic(vecPos, [170,45], g_hud3_spr, + [0,180/256], [170/256,45/256], g_hud_color, 1, DRAWFLAG_ADDITIVE); + vecPos[1] += 50; + } else if ((b=HUD_InSlotPos(i, x)) != -1) { + // Unselected Sprite + Weapons_HUDPic(b, 0, vecPos, 1.0f); + vecPos[1] += 50; + } + } else if (HUD_InSlotPos(i, x) != -1) { + HUD_DrawWeaponSelect_Num(vecPos, 5); + vecPos[1] += 25; + } + } + + if (slot_selected == TRUE) { + vecPos[0] += 175; + } else { + vecPos[0] += 25; + } + } +} diff --git a/src/client/init.qc b/src/client/init.qc new file mode 100644 index 0000000..4ad56cd --- /dev/null +++ b/src/client/init.qc @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +/* +================= +ClientGame_Init + +Comparable to worldspawn in SSQC in that it's mostly used for precaches +================= +*/ +void +ClientGame_Init(float apilevel, string enginename, float engineversion) +{ + Obituary_Init(); +} + +void +ClientGame_InitDone(void) +{ +} + +void +ClientGame_RendererRestart(string rstr) +{ + Obituary_Precache(); + + FX_Blood_Init(); + FX_BreakModel_Init(); + FX_Explosion_Init(); + FX_GibHuman_Init(); + FX_Spark_Init(); + FX_Impact_Init(); + + BEAM_TRIPMINE = particleeffectnum("weapon_tripmine.beam"); +} diff --git a/src/client/input.qc b/src/client/input.qc new file mode 100644 index 0000000..63e1d2d --- /dev/null +++ b/src/client/input.qc @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +void +Game_Input(void) +{ + if (input_buttons & INPUT_BUTTON0) { + Weapons_Primary(); + } else if (input_buttons & INPUT_BUTTON4) { + Weapons_Reload(); + } else if (input_buttons & INPUT_BUTTON3) { + Weapons_Secondary(); + } else { + Weapons_Release(); + } +} diff --git a/src/client/obituary.h b/src/client/obituary.h new file mode 100644 index 0000000..e6b80c1 --- /dev/null +++ b/src/client/obituary.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +#define OBITUARY_LINES 4 +#define OBITUARY_TIME 5 + +/* imagery */ +typedef struct { + string name; /* name of the weapon/type, e.g. d_crowbar */ + string sprite; /* name of the spritesheet it's from */ + float size[2]; /* on-screen size in pixels */ + float src_pos[2]; /* normalized position in the sprite sheet */ + float src_size[2]; /* normalized size in the sprite sheet */ + string src_sprite; /* precaching reasons */ +} obituaryimg_t; + +obituaryimg_t *g_obtypes; +int g_obtype_count; + +/* actual obituary storage */ +typedef struct +{ + string attacker; + string victim; + int icon; +} obituary_t; + +obituary_t g_obituary[OBITUARY_LINES]; +int g_obituary_count; +float g_obituary_time; + +void Obituary_Init(void); +void Obituary_Precache(void); +void Obituary_Draw(void); +void Obituary_Parse(void); diff --git a/src/client/obituary.qc b/src/client/obituary.qc new file mode 100644 index 0000000..ea61f79 --- /dev/null +++ b/src/client/obituary.qc @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +void +Obituary_Init(void) +{ + int c; + int i; + filestream fh; + string line; + vector tmp; + + if (g_obtype_count > 0) { + return; + } + + g_obtype_count = 0; + i = 0; + + fh = fopen("sprites/hud.txt", FILE_READ); + if (fh < 0) { + return; + } + + /* count valid entries */ + while ((line = fgets(fh))) { + if (substring(line, 0, 2) == "d_") { + c = tokenize(line); + if (c == 7 && argv(1) == "640") { + g_obtype_count++; + } + } + } + + g_obtypes = memalloc(sizeof(obituaryimg_t) * g_obtype_count); + + fseek(fh, 0); + + /* read them in */ + while ((line = fgets(fh))) { + if (substring(line, 0, 2) == "d_") { + c = tokenize(line); + + /* we only care about the high-res (640) variants. the 320 + * HUD is useless to us. Just use the builtin scaler */ + if (c == 7 && argv(1) == "640") { + g_obtypes[i].name = substring(argv(0), 2, -1); + g_obtypes[i].src_sprite = sprintf("sprites/%s.spr", argv(2)); + precache_model(g_obtypes[i].src_sprite); + g_obtypes[i].sprite = spriteframe(sprintf("sprites/%s.spr", argv(2)), 0, 0.0f); + g_obtypes[i].size[0] = stof(argv(5)); + g_obtypes[i].size[1] = stof(argv(6)); + tmp = drawgetimagesize(g_obtypes[i].sprite); + g_obtypes[i].src_pos[0] = stof(argv(3)) / tmp[0]; + g_obtypes[i].src_pos[1] = stof(argv(4)) / tmp[1]; + g_obtypes[i].src_size[0] = g_obtypes[i].size[0] / tmp[0]; + g_obtypes[i].src_size[1] = g_obtypes[i].size[1] / tmp[1]; + i++; + } + } + } + + fclose(fh); +} + +void +Obituary_Precache(void) +{ + for (int i = 0; i < g_obtype_count; i++) + precache_model(g_obtypes[i].src_sprite); +} + +void +Obituary_KillIcon(int id, float w) +{ + if (w > 0) + for (int i = 0; i < g_obtype_count; i++) { + if (g_weapons[w].name == g_obtypes[i].name) { + g_obituary[id].icon = i; + return; + } + } + + /* look for skull instead */ + for (int i = 0; i < g_obtype_count; i++) { + if (g_obtypes[i].name == "skull") { + g_obituary[id].icon = i; + return; + } + } +} + +void +Obituary_Add(string attacker, string victim, float weapon, float flags) +{ + int i; + int x, y; + x = OBITUARY_LINES; + + /* we're not full yet, so fill up the buffer */ + if (g_obituary_count < x) { + y = g_obituary_count; + g_obituary[y].attacker = attacker; + g_obituary[y].victim = victim; + Obituary_KillIcon(y, weapon); + g_obituary_count++; + } else { + for (i = 0; i < (x-1); i++) { + g_obituary[i].attacker = g_obituary[i+1].attacker; + g_obituary[i].victim = g_obituary[i+1].victim; + g_obituary[i].icon = g_obituary[i+1].icon; + } + /* after rearranging, add the newest to the bottom. */ + g_obituary[x-1].attacker = attacker; + g_obituary[x-1].victim = victim; + Obituary_KillIcon(x-1, weapon); + } + + g_obituary_time = OBITUARY_TIME; + + if (g_weapons[weapon].deathmsg) { + string conprint = g_weapons[weapon].deathmsg(); + + if (conprint != "") { + print(sprintf(conprint, attacker, victim)); + print("\n"); + } + } +} + +void +Obituary_Draw(void) +{ + int i; + vector pos; + vector item; + drawfont = FONT_CON; + pos = g_hudmins + [g_hudres[0] - 18, 56]; + + if (g_obituary_time <= 0 && g_obituary_count > 0) { + for (i = 0; i < (OBITUARY_LINES-1); i++) { + g_obituary[i].attacker = g_obituary[i+1].attacker; + g_obituary[i].victim = g_obituary[i+1].victim; + g_obituary[i].icon = g_obituary[i+1].icon; + } + g_obituary[OBITUARY_LINES-1].attacker = ""; + + g_obituary_time = OBITUARY_TIME; + g_obituary_count--; + } + + if (g_obituary_count <= 0) { + return; + } + + item = pos; + for (i = 0; i < OBITUARY_LINES; i++) { + string a, v; + + if (!g_obituary[i].attacker) { + break; + } + + item[0] = pos[0]; + + v = g_obituary[i].victim; + drawstring_r(item + [0,2], v, [12,12], [1,1,1], 1.0f, 0); + item[0] -= stringwidth(v, TRUE, [12,12]) + 4; + item[0] -= g_obtypes[g_obituary[i].icon].size[0]; + + drawsubpic( + item, + [g_obtypes[g_obituary[i].icon].size[0], g_obtypes[g_obituary[i].icon].size[1]], + g_obtypes[g_obituary[i].icon].sprite, + [g_obtypes[g_obituary[i].icon].src_pos[0],g_obtypes[g_obituary[i].icon].src_pos[1]], + [g_obtypes[g_obituary[i].icon].src_size[0],g_obtypes[g_obituary[i].icon].src_size[1]], + g_hud_color, + 1.0f, + DRAWFLAG_ADDITIVE + ); + + a = g_obituary[i].attacker; + drawstring_r(item + [-4,2], a, [12,12], [1,1,1], 1.0f, 0); + item[1] += 18; + } + + g_obituary_time = max(0, g_obituary_time - clframetime); +} + +void +Obituary_Parse(void) +{ + string attacker; + string victim; + float weapon; + float flags; + + attacker = readstring(); + victim = readstring(); + weapon = readbyte(); + flags = readbyte(); + + if (!attacker) { + return; + } + + Obituary_Add(attacker, victim, weapon, flags); +} diff --git a/src/client/particles.h b/src/client/particles.h new file mode 100644 index 0000000..37140d1 --- /dev/null +++ b/src/client/particles.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +var float BEAM_TRIPMINE; diff --git a/src/client/player.qc b/src/client/player.qc new file mode 100644 index 0000000..62453d1 --- /dev/null +++ b/src/client/player.qc @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +void +Player_PreDraw(base_player pl, int thirdperson) +{ + /* Handle the flashlights... */ + if (pl.gflags & GF_FLASHLIGHT) { + vector src; + vector ang; + + if (pl.entnum != player_localentnum) { + src = pl.origin + pl.view_ofs; + ang = [pl.pitch, pl.angles[1], pl.angles[2]]; + } else { + src = pSeat->m_vecPredictedOrigin + [0,0,-8]; + ang = view_angles; + } + + makevectors(ang); + traceline(src, src + (v_forward * 8096), MOVE_NORMAL, pl); + + if (serverkeyfloat("*bspversion") == BSPVER_HL) { + dynamiclight_add(trace_endpos + (v_forward * -2), 128, [1,1,1]); + } else { + float p = dynamiclight_add(src, 512, [1,1,1], 0, "textures/flashlight"); + dynamiclight_set(p, LFIELD_ANGLES, ang); + dynamiclight_set(p, LFIELD_FLAGS, 3); + } + } +} diff --git a/src/client/predict.qc b/src/client/predict.qc new file mode 100644 index 0000000..5aea138 --- /dev/null +++ b/src/client/predict.qc @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +void +GamePredict_PreFrame(player pl) +{ +} + +void +GamePredict_PostFrame(player pl) +{ +} diff --git a/src/client/progs.src b/src/client/progs.src new file mode 100755 index 0000000..bd015f9 --- /dev/null +++ b/src/client/progs.src @@ -0,0 +1,38 @@ +#pragma target fte +#pragma progs_dat "../../csprogs.dat" + +#define CSQC +#define CLIENT +#define VALVE +#define CLASSIC_VGUI +#define GS_RENDERFX + +#includelist +../../../src/shared/fteextensions.qc +../../../src/shared/defs.h +defs.h +../../../src/client/defs.h + +../../../src/vgui/include.src + +../../../src/gs-entbase/client.src +../../../src/gs-entbase/shared.src +../shared/include.src + +predict.qc +init.qc +player.qc +entities.qc +cmds.qc +game_event.qc +view.qc +obituary.qc +hud.qc +hud_weaponselect.qc +scoreboard.qc +input.qc +../../../base/src/client/modelevent.qc + +../../../src/client/include.src +../../../src/shared/include.src +#endlist diff --git a/src/client/scoreboard.qc b/src/client/scoreboard.qc new file mode 100644 index 0000000..2e9c14a --- /dev/null +++ b/src/client/scoreboard.qc @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +#define SCORE_HEADER_C [255/255,156/255,0] +#define SCORE_LINE_C [255/255,200/255,0] + +var int autocvar_cl_centerscores = FALSE; +var int g_scores_teamplay = 0; + +void +Scores_Init(void) +{ + g_scores_teamplay = (int)serverkeyfloat("teamplay"); +} + +void +Scores_DrawTeam(player pl, vector pos) +{ + drawfill(pos, [290, 1], SCORE_LINE_C, 1.0f, DRAWFLAG_ADDITIVE); + + drawfont = FONT_20; + drawstring(pos + [0,-18], "Teams", [20,20], SCORE_HEADER_C, 1.0f, DRAWFLAG_ADDITIVE); + drawstring(pos + [124,-18], "kills / deaths", [20,20], SCORE_HEADER_C, 1.0f, DRAWFLAG_ADDITIVE); + drawstring(pos + [240,-18], "latency", [20,20], SCORE_HEADER_C, 1.0f, DRAWFLAG_ADDITIVE); + + pos[1] += 12; + + for (int t = 1; t <= serverkeyfloat("teams"); t++) { + float l; + string temp; + drawstring(pos, serverkey(sprintf("team_%i", t)), [20,20], SCORE_HEADER_C, 1.0f, DRAWFLAG_ADDITIVE); + temp = serverkey(sprintf("teamscore_%i", t)); + l = stringwidth(temp, FALSE, [20,20]); + drawstring(pos + [150-l, 0], temp, [20,20], SCORE_HEADER_C, 1.0f, DRAWFLAG_ADDITIVE); + drawstring(pos + [158, 0], "wins", [20,20], SCORE_HEADER_C, 1.0f, DRAWFLAG_ADDITIVE); + pos[1] += 16; + + for (int i = -1; i > -32; i--) { + if (getplayerkeyfloat(i, "*team") != t) { + continue; + } + + temp = getplayerkeyvalue(i, "name"); + + /* Out of players */ + if (!temp) { + break; + } else if (temp == getplayerkeyvalue(pl.entnum-1, "name")) { + drawfill(pos, [290, 13], [0,0,1], 0.5f, DRAWFLAG_ADDITIVE); + } + + drawstring(pos + [24,0], getplayerkeyvalue(i, "name"), [20,20], [1,1,1], 1.0f, DRAWFLAG_ADDITIVE); + drawstring(pos + [154,0], "/", [20,20], [1,1,1], 1.0f, DRAWFLAG_ADDITIVE); + + /* Get the kills and align them left to right */ + temp = getplayerkeyvalue(i, "frags"); + l = stringwidth(temp, FALSE, [20,20]); + drawstring(pos + [150 - l,0], temp, [20,20], [1,1,1], 1.0f, DRAWFLAG_ADDITIVE); + + /* Deaths are right to left aligned */ + temp = getplayerkeyvalue(i, "*deaths"); + drawstring(pos + [165,0], temp, [20,20], [1,1,1], 1.0f, DRAWFLAG_ADDITIVE); + + /* Get the latency and align it left to right */ + temp = getplayerkeyvalue(i, "ping"); + l = stringwidth(temp, FALSE, [20,20]); + + if (getplayerkeyfloat(i, "*dead") == 1) { + drawsubpic( + pos - [8,0], + [32,16], + g_hud1_spr, + [224/256, 240/256], + [32/256, 16/256], + [1,0,0], + 1.0f, + DRAWFLAG_ADDITIVE + ); + } + + drawstring(pos + [290 - l,0], temp, [20,20], [1,1,1], 1.0f, DRAWFLAG_ADDITIVE); + pos[1] += 20; + } + pos[1] += 12; + } + + drawfont = FONT_CON; +} + +void +Scores_DrawNormal(player pl, vector pos) +{ + drawfill(pos, [290, 1], SCORE_LINE_C, 1.0f, DRAWFLAG_ADDITIVE); + + drawfont = FONT_20; + drawstring(pos + [0,-18], "Player", [20,20], SCORE_HEADER_C, 1.0f, DRAWFLAG_ADDITIVE); + drawstring(pos + [124,-18], "kills / deaths", [20,20], SCORE_HEADER_C, 1.0f, DRAWFLAG_ADDITIVE); + drawstring(pos + [240,-18], "latency", [20,20], SCORE_HEADER_C, 1.0f, DRAWFLAG_ADDITIVE); + + pos[1] += 12; + for (int i = -1; i > -32; i--) { + float l; + string ping; + string kills; + string deaths; + string name; + + name = getplayerkeyvalue(i, "name"); + + /* Out of players */ + if (!name) { + break; + } else if (name == getplayerkeyvalue(pl.entnum-1, "name")) { + drawfill(pos, [290, 13], [0,0,1], 0.5f, DRAWFLAG_ADDITIVE); + } + + drawstring(pos, getplayerkeyvalue(i, "name"), [20,20], [1,1,1], 1.0f, DRAWFLAG_ADDITIVE); + drawstring(pos + [154,0], "/", [20,20], [1,1,1], 1.0f, DRAWFLAG_ADDITIVE); + + /* Get the kills and align them left to right */ + kills = getplayerkeyvalue(i, "frags"); + l = stringwidth(kills, FALSE, [20,20]); + drawstring(pos + [150 - l,0], kills, [20,20], [1,1,1], 1.0f, DRAWFLAG_ADDITIVE); + + /* Deaths are right to left aligned */ + deaths = getplayerkeyvalue(i, "*deaths"); + drawstring(pos + [165,0], deaths, [20,20], [1,1,1], 1.0f, DRAWFLAG_ADDITIVE); + + /* Get the latency and align it left to right */ + ping = getplayerkeyvalue(i, "ping"); + l = stringwidth(ping, FALSE, [20,20]); + + drawstring(pos + [290 - l,0], ping, [20,20], [1,1,1], 1.0f, DRAWFLAG_ADDITIVE); + pos[1] += 20; + } + + drawfont = FONT_CON; +} + +void +Scores_Draw(void) +{ + vector pos; + player pl; + + pl = (player)pSeat->m_ePlayer; + + if (autocvar_cl_centerscores) { + int c = 10; + + /* calculate all valid entries */ + for (int i = -1; i > -32; i--) { + if (getplayerkeyvalue(i, "name")) { + break; + } + c += 10; + } + pos = video_mins + [(video_res[0] / 2) - 145, (video_res[1] / 2) - c]; + } else { + pos = video_mins + [(video_res[0] / 2) - 145, 30]; + } + + if (serverkeyfloat("teams") > 0) { + Scores_DrawTeam(pl, pos); + } else { + Scores_DrawNormal(pl, pos); + } +} diff --git a/src/client/view.qc b/src/client/view.qc new file mode 100644 index 0000000..5592181 --- /dev/null +++ b/src/client/view.qc @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +void +View_UpdateWeapon(entity vm, entity mflash) +{ + player pl = (player)pSeat->m_ePlayer; + + /* only bother upon change */ + if (pSeat->m_iLastWeapon == pl.activeweapon) { + return; + } + pSeat->m_iOldWeapon = pSeat->m_iLastWeapon; + pSeat->m_iLastWeapon = pl.activeweapon; + + if (!pl.activeweapon) { + return; + } + + /* hack, we changed the wep, move this into Game_Input/PMove */ + Weapons_Draw(); + + /* we forced a weapon call outside the prediction, + * thus we need to update all the net variables to + * make sure these updates are recognized. this is + * vile but it'll have to do for now */ + pl.net_w_attack_next = pl.w_attack_next; + pl.net_w_idle_next = pl.w_idle_next; + pl.net_viewzoom = pl.viewzoom; + pl.net_weapontime = pl.weapontime; + + /* figure out when the attachments start. in FTE attachments for + * HLMDL are treated as bones. they start at numbones + 1 */ + skel_delete(mflash.skeletonindex); + mflash.skeletonindex = skel_create(vm.modelindex); + pSeat->m_iVMBones = skel_get_numbones(mflash.skeletonindex) + 1; + pSeat->m_iVMEjectBone = pSeat->m_iVMBones + 1; +} diff --git a/src/progs.src b/src/progs.src new file mode 100755 index 0000000..2c2a868 --- /dev/null +++ b/src/progs.src @@ -0,0 +1,2 @@ +#pragma sourcefile client/progs.src +#pragma sourcefile server/progs.src diff --git a/src/server/Makefile b/src/server/Makefile new file mode 100644 index 0000000..627019a --- /dev/null +++ b/src/server/Makefile @@ -0,0 +1,4 @@ +CC=fteqcc + +all: + $(CC) progs.src diff --git a/src/server/ammo.qc b/src/server/ammo.qc new file mode 100644 index 0000000..f47920c --- /dev/null +++ b/src/server/ammo.qc @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +class item_ammo:CBaseEntity +{ + void(void) item_ammo; + virtual void(void) Respawn; + virtual void(void) touch; +}; + +void item_ammo::touch(void) +{ + if not (other.flags & FL_CLIENT) { + return; + } + + player pl = (player)other; + Sound_Play(other, CHAN_ITEM, "ammo.pickup"); + Weapons_RefreshAmmo(pl); + Logging_Pickup(other, this, __NULL__); + + if (real_owner || cvar("sv_playerslots") == 1) { + remove(self); + } else { + Hide(); + think = Respawn; + nextthink = time + 20.0f; + } +} + +void item_ammo::Respawn(void) +{ + SetSolid(SOLID_TRIGGER); + SetMovetype(MOVETYPE_TOSS); + SetSize([-16,-16,0],[16,16,16]); + SetOrigin(m_oldOrigin); + SetModel(m_oldModel); + + think = __NULL__; + nextthink = -1; + + if (real_owner) + Sound_Play(this, CHAN_ITEM, "ammo.respawn"); + + droptofloor(); +} + +void item_ammo::item_ammo(void) +{ + precache_model(model); + m_oldModel = model; + SetModel(m_oldModel); + CBaseEntity::CBaseEntity(); +} + +/*QUAKED ammo_357 (0 0 0.8) (-16 -16 0) (16 16 32) + +HALF-LIFE (1998) ENTITY + +Ammo for the .357 Magnum Revolver. +A single ammo_357 will provide 6 bullets. + +*/ +class ammo_357:item_ammo +{ + void(void) ammo_357; + virtual void(void) touch; +}; + +void ammo_357::ammo_357(void) +{ + model = "models/w_357ammobox.mdl"; + item_ammo::item_ammo(); +} +void ammo_357::touch(void) +{ + if not (other.flags & FL_CLIENT) { + return; + } + if (other.classname == "player") { + player pl = (player)other; + if (pl.ammo_357 < MAX_A_357) { + pl.ammo_357 = bound(0, pl.ammo_357 + 6, MAX_A_357); + item_ammo::touch(); + } + } +} + +/*QUAKED ammo_9mmAR (0 0 0.8) (-16 -16 0) (16 16 32) + +HALF-LIFE (1998) ENTITY + +Ammo for the 9mm Handgun and the 9mm AR. +A single ammo_9mmAR will provide 50 bullets. + +*/ +class ammo_9mmAR:item_ammo +{ + void(void) ammo_9mmAR; + virtual void(void) touch; +}; + +void ammo_9mmAR::ammo_9mmAR(void) +{ + model = "models/w_9mmarclip.mdl"; + item_ammo::item_ammo(); +} +void ammo_9mmAR::touch(void) +{ + if not (other.flags & FL_CLIENT) { + return; + } + if (other.classname == "player") { + player pl = (player)other; + if (pl.ammo_9mm < MAX_A_9MM) { + pl.ammo_9mm = bound(0, pl.ammo_9mm + 50, MAX_A_9MM); + item_ammo::touch(); + } + } +} +CLASSEXPORT(ammo_mp5clip, ammo_9mmAR) + +/*QUAKED ammo_9mmbox (0 0 0.8) (-16 -16 0) (16 16 32) + +HALF-LIFE (1998) ENTITY + +Ammo for the 9mm Handgun and the 9mm AR. +A single ammo_9mmbox will provide 200 bullets. + +*/ +class ammo_9mmbox:item_ammo +{ + void(void) ammo_9mmbox; + virtual void(void) touch; +}; + +void ammo_9mmbox::ammo_9mmbox(void) +{ + model = "models/w_chainammo.mdl"; + item_ammo::item_ammo(); +} +void ammo_9mmbox::touch(void) +{ + if not (other.flags & FL_CLIENT) { + return; + } + if (other.classname == "player") { + player pl = (player)other; + if (pl.ammo_9mm < MAX_A_9MM) { + pl.ammo_9mm = bound(0, pl.ammo_9mm + 200, MAX_A_9MM); + item_ammo::touch(); + } + } +} + +/*QUAKED ammo_9mmclip (0 0 0.8) (-16 -16 0) (16 16 32) + +HALF-LIFE (1998) ENTITY + +Ammo for the 9mm Handgun and the 9mm AR. +A single ammo_9mmclip will provide 17 bullets. + +*/ +class ammo_9mmclip:item_ammo +{ + void(void) ammo_9mmclip; + virtual void(void) touch; +}; + +void ammo_9mmclip::ammo_9mmclip(void) +{ + model = "models/w_9mmclip.mdl"; + item_ammo::item_ammo(); +} +void ammo_9mmclip::touch(void) +{ + if not (other.flags & FL_CLIENT) { + return; + } + if (other.classname == "player") { + player pl = (player)other; + if (pl.ammo_9mm < MAX_A_9MM) { + pl.ammo_9mm = bound(0, pl.ammo_9mm + 17, MAX_A_9MM); + item_ammo::touch(); + } + } +} + +/*QUAKED ammo_ARgrenades (0 0 0.8) (-16 -16 0) (16 16 32) + +HALF-LIFE (1998) ENTITY + +Ammo for the 9mm AR's secondary fire. +A single ammo_ARgrenades will provide 2 AR grenades. + +*/ +class ammo_ARgrenades:item_ammo +{ + void(void) ammo_ARgrenades; + virtual void(void) touch; +}; + +void ammo_ARgrenades::ammo_ARgrenades(void) +{ + model = "models/w_argrenade.mdl"; + item_ammo::item_ammo(); +} +void ammo_ARgrenades::touch(void) +{ + if not (other.flags & FL_CLIENT) { + return; + } + if (other.classname == "player") { + player pl = (player)other; + if (pl.ammo_m203_grenade < MAX_A_M203_GRENADE) { + pl.ammo_m203_grenade = bound(0, pl.ammo_m203_grenade + 2, MAX_A_M203_GRENADE); + item_ammo::touch(); + } + } +} +CLASSEXPORT(ammo_mp5grenades, ammo_ARgrenades) + +/*QUAKED ammo_buckshot (0 0 0.8) (-16 -16 0) (16 16 32) + +HALF-LIFE (1998) ENTITY + +Ammo for the Shotgun. +A single ammo_buckshot will provide 12 shells. + +*/ +class ammo_buckshot:item_ammo +{ + void(void) ammo_buckshot; + virtual void(void) touch; +}; + +void ammo_buckshot::ammo_buckshot(void) +{ + model = "models/w_shotbox.mdl"; + item_ammo::item_ammo(); +} +void ammo_buckshot::touch(void) +{ + if not (other.flags & FL_CLIENT) { + return; + } + if (other.classname == "player") { + player pl = (player)other; + if (pl.ammo_buckshot < MAX_A_BUCKSHOT) { + pl.ammo_buckshot = bound(0, pl.ammo_buckshot + 12, MAX_A_BUCKSHOT); + item_ammo::touch(); + } + } +} + +/*QUAKED ammo_crossbow (0 0 0.8) (-16 -16 0) (16 16 32) + +HALF-LIFE (1998) ENTITY + +Ammo for the Crossbow. +A single ammo_crossbow will provide 5 bolts. + +*/ +class ammo_crossbow:item_ammo +{ + void(void) ammo_crossbow; + virtual void(void) touch; +}; + +void ammo_crossbow::ammo_crossbow(void) +{ + model = "models/w_crossbow_clip.mdl"; + item_ammo::item_ammo(); +} +void ammo_crossbow::touch(void) +{ + if not (other.flags & FL_CLIENT) { + return; + } + if (other.classname == "player") { + player pl = (player)other; + if (pl.ammo_bolt < MAX_A_BOLT) { + pl.ammo_bolt = bound(0, pl.ammo_bolt + 5, MAX_A_BOLT); + item_ammo::touch(); + } + } +} + +/*QUAKED ammo_gaussclip (0 0 0.8) (-16 -16 0) (16 16 32) + +HALF-LIFE (1998) ENTITY + +Ammo for the Tau Cannon and the Gluon Gun. +A single ammo_gaussclip will provide 20 cells. + +*/ +class ammo_gaussclip:item_ammo +{ + void(void) ammo_gaussclip; + virtual void(void) touch; +}; + +void ammo_gaussclip::ammo_gaussclip(void) +{ + model = "models/w_gaussammo.mdl"; + item_ammo::item_ammo(); +} +void ammo_gaussclip::touch(void) +{ + if not (other.flags & FL_CLIENT) { + return; + } + + player pl = (player)other; + if (pl.ammo_uranium < MAX_A_URANIUM) { + pl.ammo_uranium = bound(0, pl.ammo_uranium + 20, MAX_A_URANIUM); + item_ammo::touch(); + } +} + +/*QUAKED ammo_rpgclip (0 0 0.8) (-16 -16 0) (16 16 32) + +HALF-LIFE (1998) ENTITY + +Ammo for the RPG. +A single ammo_rpgclip will provide 1 rocket. + +*/ +class ammo_rpgclip:item_ammo +{ + void(void) ammo_rpgclip; + virtual void(void) touch; +}; + +void ammo_rpgclip::ammo_rpgclip(void) +{ + model = "models/w_rpgammo.mdl"; + item_ammo::item_ammo(); +} +void ammo_rpgclip::touch(void) +{ + if not (other.flags & FL_CLIENT) { + return; + } + + player pl = (player)other; + if (pl.ammo_rocket < MAX_A_ROCKET) { + pl.ammo_rocket = bound(0, pl.ammo_rocket + 1, MAX_A_ROCKET); + item_ammo::touch(); + } +} diff --git a/src/server/client.qc b/src/server/client.qc new file mode 100644 index 0000000..107c58e --- /dev/null +++ b/src/server/client.qc @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +/* called every input frame */ +void +Game_RunClientCommand(void) +{ + Footsteps_Update(); + Animation_PlayerUpdate(); + PMove_Run(); +} + +/* custom chat packet */ +void +SV_SendChat(entity sender, string msg, entity eEnt, float fType) +{ + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, fType == 0 ? EV_CHAT:EV_CHAT_TEAM); + WriteByte(MSG_MULTICAST, num_for_edict(sender) - 1); + WriteByte(MSG_MULTICAST, sender.team); + WriteString(MSG_MULTICAST, msg); + + if (eEnt) { + msg_entity = eEnt; + multicast([0,0,0], MULTICAST_ONE); + } else { + multicast([0,0,0], MULTICAST_ALL); + } + + localcmd(sprintf("echo [SERVER] %s: %s\n", sender.netname, msg)); +} + +/* client cmd overrides happen here */ +void +Game_ParseClientCommand(string cmd) +{ + tokenize(cmd); + + if (argv(1) == "timeleft") { + string msg; + string timestring; + float timeleft; + timeleft = cvar("mp_timelimit") - (time / 60); + timestring = Vox_TimeToString(timeleft); + msg = sprintf("we have %s minutes remaining", timestring); + Vox_Singlecast(self, msg); + return; + } + + if (argv(0) == "say") { + SV_SendChat(self, argv(1), world, 0); + return; + } else if (argv(0) == "say_team") { + entity a; + for (a = world; (a = find(a, ::classname, "player"));) { + if (a.team == self.team) { + SV_SendChat(self, argv(1), a, 1); + } + } + return; + } + + clientcommand(self, cmd); +} diff --git a/src/server/damage.qc b/src/server/damage.qc new file mode 100644 index 0000000..73cd380 --- /dev/null +++ b/src/server/damage.qc @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +/* generic function that applies damage, pain and suffering */ +void +Damage_Apply(entity t, entity c, float dmg, int w, int type) +{ + base_player tp = (base_player)t; + + CGameRules rules = (CGameRules)g_grMode; + if (t.flags & FL_GODMODE) { + return; + } + + /* already dead, please avoid recursion */ + if (t.health <= 0) { + return; + } + + /* skip armor */ + if not (type & DMG_SKIP_ARMOR) + if (tp.armor && dmg > 0) { + float flArmor; + float flNewDamage; + + flNewDamage = dmg * 0.2; + flArmor = (dmg - flNewDamage) * 0.5; + + if (flArmor > tp.armor) { + flArmor = tp.armor; + flArmor *= (1/0.5); + flNewDamage = dmg - flArmor; + tp.armor = 0; + } else { + tp.armor -= flArmor; + } + dmg = flNewDamage; + } + + dmg = rint(dmg); + t.health -= dmg; + + /* the globals... */ + g_dmg_eAttacker = c; + g_dmg_eTarget = t; + g_dmg_iDamage = dmg; + g_dmg_iHitBody = trace_surface_id; + g_dmg_iFlags = type; + g_dmg_iWeapon = w; + + if (dmg > 0) { + t.dmg_take = dmg; + t.dmg_inflictor = c; + } else if (t.max_health && t.health > t.max_health) { + t.health = t.max_health; + } + + /* set this global in case we need it later */ + g_eAttacker = c; + + CBaseEntity s = (CBaseEntity)t; + + if (s.health <= 0) { + if (s.flags & FL_CLIENT) { + rules.PlayerDeath((player)s); + } else { + s.Death(); + } + } else { + if (s.flags & FL_CLIENT) { + rules.PlayerPain((player)s); + } else { + s.Pain(); + } + } +} + +/* physical check of whether or not we can trace important parts of an ent */ +float +Damage_CheckTrace(entity t, vector vecHitPos) +{ + /* We're lazy. Who cares */ + if (t.solid == SOLID_BSP) { + return TRUE; + } + + traceline(vecHitPos, t.origin, 1, self); + if (trace_fraction == 1) { + return TRUE; + } + traceline(vecHitPos, t.origin + [15,15,0], 1, self); + if (trace_fraction == 1) { + return TRUE; + } + traceline(vecHitPos, t.origin + [-15,-15,0], 1, self); + if (trace_fraction == 1) { + return TRUE; + } + traceline(vecHitPos, t.origin + [-15,15,0], 1, self); + if (trace_fraction == 1) { + return TRUE; + } + traceline(vecHitPos, t.origin + [15,-15,0], 1, self); + if (trace_fraction == 1) { + return TRUE; + } + + return FALSE; +} + +/* even more pain and suffering, mostly used for explosives */ +void +Damage_Radius(vector org, entity attacker, float dmg, float r, int check, int w) +{ + float new_dmg; + float dist; + float diff; + vector pos; + + for (entity e = world; (e = findfloat(e, ::takedamage, DAMAGE_YES));) { + pos[0] = e.absmin[0] + (0.5 * (e.absmax[0] - e.absmin[0])); + pos[1] = e.absmin[1] + (0.5 * (e.absmax[1] - e.absmin[1])); + pos[2] = e.absmin[2] + (0.5 * (e.absmax[2] - e.absmin[2])); + + /* don't bother if it's not anywhere near us */ + dist = vlen(org - pos); + if (dist > r) { + continue; + } + + /* can we physically hit this thing? */ + if (Damage_CheckTrace(e, org) == FALSE) { + if (check == TRUE) { + continue; + } + } + + /* calculate new damage values */ + diff = vlen(org - pos); + diff = (r - diff) / r; + new_dmg = rint(dmg * diff); + + if (diff > 0) { + Damage_Apply(e, attacker, new_dmg, w, DMG_EXPLODE); + + /* approximate, feel free to tweak */ + if (e.movetype == MOVETYPE_WALK) { + makevectors(vectoangles(e.origin - org)); + e.velocity += v_forward * (new_dmg * 5); + } + } + } +} diff --git a/src/server/defs.h b/src/server/defs.h new file mode 100644 index 0000000..88c3448 --- /dev/null +++ b/src/server/defs.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +#include "gamerules.h" +#include "items.h" diff --git a/src/server/flashlight.qc b/src/server/flashlight.qc new file mode 100644 index 0000000..fbdf827 --- /dev/null +++ b/src/server/flashlight.qc @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +void Flashlight_Toggle(void) +{ + if (cvar("sv_playerslots") != 1) { + if (cvar("mp_flashlight") != 1) { + return; + } + } + +#ifdef VALVE + player pl = (player)self; + if (!(pl.g_items & ITEM_SUIT)) { + return; + } +#endif + + if (self.health <= 0) { + return; + } + + if (self.gflags & GF_FLASHLIGHT) { + self.gflags &= ~GF_FLASHLIGHT; + } else { + self.gflags |= GF_FLASHLIGHT; + } + sound(self, CHAN_ITEM, "items/flashlight1.wav", 1, ATTN_IDLE); +} diff --git a/src/server/gamerules.h b/src/server/gamerules.h new file mode 100644 index 0000000..b25b994 --- /dev/null +++ b/src/server/gamerules.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +class HLGameRules:CGameRules +{ + virtual void(base_player) PlayerConnect; + virtual void(base_player) PlayerDisconnect; + virtual void(base_player) PlayerKill; + virtual void(base_player) PlayerPostFrame; + + virtual void(base_player) LevelDecodeParms; + virtual void(base_player) LevelChangeParms; + virtual void(void) LevelNewParms; +}; + +class HLSingleplayerRules:HLGameRules +{ + /* client */ + virtual void(base_player) PlayerSpawn; + virtual void(base_player) PlayerDeath; +}; + +class HLMultiplayerRules:HLGameRules +{ + int m_iIntermission; + int m_iIntermissionTime; + + virtual void(void) FrameStart; + + /* client */ + virtual void(base_player) PlayerSpawn; + virtual void(base_player) PlayerDeath; + virtual float(base_player, string) ConsoleCommand; +}; diff --git a/src/server/gamerules.qc b/src/server/gamerules.qc new file mode 100644 index 0000000..146d74c --- /dev/null +++ b/src/server/gamerules.qc @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +void +HLGameRules::LevelDecodeParms(base_player pp) +{ + player pl = (player)pp; + g_landmarkpos[0] = parm1; + g_landmarkpos[1] = parm2; + g_landmarkpos[2] = parm3; + pl.angles[0] = parm4; + pl.angles[1] = parm5; + pl.angles[2] = parm6; + pl.velocity[0] = parm7; + pl.velocity[1] = parm8; + pl.velocity[2] = parm9; + pl.g_items = parm10; + pl.activeweapon = parm11; + pl.flags = parm64; + + pl.ammo_9mm = parm12; + pl.ammo_357 = parm13; + pl.ammo_buckshot = parm14; + pl.ammo_m203_grenade = parm15; + pl.ammo_bolt = parm16; + pl.ammo_rocket = parm17; + pl.ammo_uranium = parm18; + pl.ammo_handgrenade = parm19; + pl.ammo_satchel = parm20; + pl.ammo_tripmine = parm21; + pl.ammo_snark = parm22; + pl.ammo_hornet = parm23; + + pl.glock_mag = parm24; + pl.mp5_mag = parm25; + pl.python_mag = parm26; + pl.shotgun_mag = parm27; + pl.crossbow_mag = parm28; + pl.rpg_mag = parm29; + pl.satchel_chg = parm30; + + if (pl.flags & FL_CROUCHING) { + setsize(pl, VEC_CHULL_MIN, VEC_CHULL_MAX); + } else { + setsize(pl, VEC_HULL_MIN, VEC_HULL_MAX); + } +} + +void +HLGameRules::LevelChangeParms(base_player pp) +{ + player pl = (player)pp; + parm1 = g_landmarkpos[0]; + parm2 = g_landmarkpos[1]; + parm3 = g_landmarkpos[2]; + parm4 = pl.angles[0]; + parm5 = pl.angles[1]; + parm6 = pl.angles[2]; + parm7 = pl.velocity[0]; + parm8 = pl.velocity[1]; + parm9 = pl.velocity[2]; + parm64 = pl.flags; + parm10 = pl.g_items; + parm11 = pl.activeweapon; + parm12 = pl.ammo_9mm; + parm13 = pl.ammo_357; + parm14 = pl.ammo_buckshot; + parm15 = pl.ammo_m203_grenade; + parm16 = pl.ammo_bolt; + parm17 = pl.ammo_rocket; + parm18 = pl.ammo_uranium; + parm19 = pl.ammo_handgrenade; + parm20 = pl.ammo_satchel; + parm21 = pl.ammo_tripmine; + parm22 = pl.ammo_snark; + parm23 = pl.ammo_hornet; + parm24 = pl.glock_mag; + parm25 = pl.mp5_mag; + parm26 = pl.python_mag; + parm27 = pl.shotgun_mag; + parm28 = pl.crossbow_mag; + parm29 = pl.rpg_mag; + parm30 = pl.satchel_chg; +} + +void +HLGameRules::LevelNewParms(void) +{ + parm1 = parm2 = parm3 = parm4 = parm5 = parm6 = parm7 = + parm8 = parm9 = parm10 = parm11 = parm12 = parm13 = parm14 = + parm15 = parm16 = parm17 = parm18 = parm19 = parm20 = parm21 = + parm22 = parm23 = parm24 = parm25 = parm26 = parm27 = parm28 = + parm29 = parm30 = 0; + parm64 = FL_CLIENT; +} + +/* we check what fields have changed over the course of the frame and network + * only the ones that have actually changed */ +void +HLGameRules::PlayerPostFrame(base_player pl) +{ +} + +void +HLGameRules::PlayerConnect(base_player pl) +{ + if (Plugin_PlayerConnect(pl) == FALSE) + bprint(PRINT_HIGH, sprintf("%s connected\n", pl.netname)); +} + +void +HLGameRules::PlayerDisconnect(base_player pl) +{ + if (Plugin_PlayerDisconnect(pl) == FALSE) + bprint(PRINT_HIGH, sprintf("%s disconnected\n", pl.netname)); + + /* Make this unusable */ + pl.solid = SOLID_NOT; + pl.movetype = MOVETYPE_NONE; + pl.modelindex = 0; + pl.health = 0; + pl.takedamage = 0; + pl.SendFlags = -1; +} + +void +HLGameRules::PlayerKill(base_player pl) +{ + Damage_Apply(pl, pl, pl.health, WEAPON_NONE, DMG_SKIP_ARMOR); +} diff --git a/src/server/gamerules_multiplayer.qc b/src/server/gamerules_multiplayer.qc new file mode 100644 index 0000000..1ff921a --- /dev/null +++ b/src/server/gamerules_multiplayer.qc @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +void +HLMultiplayerRules::FrameStart(void) +{ + if (cvar("mp_timelimit")) + if (time >= (cvar("mp_timelimit") * 60)) { + IntermissionStart(); + } + + IntermissionCycle(); +} + +void +HLMultiplayerRules::PlayerDeath(base_player pl) +{ + /* obituary networking */ + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EV_OBITUARY); + if (g_dmg_eAttacker.netname) + WriteString(MSG_MULTICAST, g_dmg_eAttacker.netname); + else + WriteString(MSG_MULTICAST, g_dmg_eAttacker.classname); + WriteString(MSG_MULTICAST, pl.netname); + WriteByte(MSG_MULTICAST, g_dmg_iWeapon); + WriteByte(MSG_MULTICAST, 0); + msg_entity = world; + multicast([0,0,0], MULTICAST_ALL); + + Plugin_PlayerObituary(g_dmg_eAttacker, g_dmg_eTarget, g_dmg_iWeapon, g_dmg_iHitBody, g_dmg_iDamage); + + /* death-counter */ + pl.deaths++; + forceinfokey(pl, "*deaths", ftos(pl.deaths)); + + /* update score-counter */ + if (pl.flags & FL_CLIENT || pl.flags & FL_MONSTER) + if (g_dmg_eAttacker.flags & FL_CLIENT) { + if (pl == g_dmg_eAttacker) + g_dmg_eAttacker.frags--; + else + g_dmg_eAttacker.frags++; + } + + /* in DM we only care about the frags */ + if (cvar("mp_fraglimit")) + if (g_dmg_eAttacker.frags >= cvar("mp_fraglimit")) { + IntermissionStart(); + } + + weaponbox_spawn((player)pl); + + /* either gib, or make a corpse */ + if (pl.health < -50) { + FX_GibHuman(pl.origin); + } else { + /* Let's handle corpses on the clientside */ + entity corpse = spawn(); + setorigin(corpse, pl.origin + [0,0,32]); + setmodel(corpse, pl.model); + setsize(corpse, VEC_HULL_MIN, VEC_HULL_MAX); + corpse.movetype = MOVETYPE_TOSS; + corpse.solid = SOLID_TRIGGER; + corpse.modelindex = pl.modelindex; + corpse.frame = ANIM_DIESIMPLE; + corpse.angles = pl.angles; + corpse.velocity = pl.velocity; + } + + /* now let's make the real client invisible */ + pl.SetModelindex(0); + pl.SetMovetype(MOVETYPE_NONE); + pl.SetSolid(SOLID_NOT); + pl.takedamage = DAMAGE_NO; + pl.gflags &= ~GF_FLASHLIGHT; + pl.armor = pl.activeweapon = pl.g_items = 0; + pl.health = 0; + + Sound_Play(pl, CHAN_AUTO, "player.die"); + + /* force respawn */ + pl.think = PutClientInServer; + pl.nextthink = time + 4.0f; +} + +void +HLMultiplayerRules::PlayerSpawn(base_player pp) +{ + player pl = (player)pp; + /* this is where the mods want to deviate */ + entity spot; + + pl.classname = "player"; + pl.health = pl.max_health = 100; + pl.takedamage = DAMAGE_YES; + pl.solid = SOLID_SLIDEBOX; + pl.movetype = MOVETYPE_WALK; + pl.flags = FL_CLIENT; + pl.viewzoom = 1.0; + pl.model = "models/player.mdl"; + string mymodel = infokey(pl, "model"); + + if (mymodel) { + mymodel = sprintf("models/player/%s/%s.mdl", mymodel, mymodel); + if (whichpack(mymodel)) { + pl.model = mymodel; + } + } + setmodel(pl, pl.model); + + setsize(pl, VEC_HULL_MIN, VEC_HULL_MAX); + pl.view_ofs = VEC_PLAYER_VIEWPOS; + pl.velocity = [0,0,0]; + pl.gravity = __NULL__; + pl.frame = 1; + //pl.SendEntity = Player_SendEntity; + pl.SendFlags = UPDATE_ALL; + pl.customphysics = Empty; + pl.iBleeds = TRUE; + forceinfokey(pl, "*spec", "0"); + forceinfokey(pl, "*deaths", ftos(pl.deaths)); + + LevelNewParms(); + LevelDecodeParms(pl); + pl.g_items = ITEM_CROWBAR | ITEM_GLOCK | ITEM_SUIT; + pl.activeweapon = WEAPON_GLOCK; + pl.glock_mag = 18; + pl.ammo_9mm = 44; + + spot = Spawn_SelectRandom("info_player_deathmatch"); + setorigin(pl, spot.origin); + pl.angles = spot.angles; + Weapons_RefreshAmmo(pl); + + Client_FixAngle(pl, pl.angles); +} + +float +HLMultiplayerRules::ConsoleCommand(base_player pp, string cmd) +{ + tokenize(cmd); + + switch (argv(0)) { + case "bot_add": + Bot_AddQuick(); + break; + default: + return FALSE; + } + + return TRUE; +} diff --git a/src/server/gamerules_singleplayer.qc b/src/server/gamerules_singleplayer.qc new file mode 100644 index 0000000..7c967e6 --- /dev/null +++ b/src/server/gamerules_singleplayer.qc @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +void +HLSingleplayerRules::PlayerDeath(base_player pl) +{ + pl.movetype = MOVETYPE_NONE; + pl.solid = SOLID_NOT; + pl.takedamage = DAMAGE_NO; + pl.gflags &= ~GF_FLASHLIGHT; + pl.armor = pl.activeweapon = pl.g_items = pl.weapon = 0; + pl.health = 0; + Sound_Play(pl, CHAN_AUTO, "player.die"); + + if (cvar("coop") == 1) { + pl.think = PutClientInServer; + pl.nextthink = time + 4.0f; + } + + if (pl.health < -50) { + FX_GibHuman(pl.origin); + } + + /* Let's handle corpses on the clientside */ + entity corpse = spawn(); + setorigin(corpse, pl.origin + [0,0,32]); + setmodel(corpse, pl.model); + setsize(corpse, VEC_HULL_MIN, VEC_HULL_MAX); + corpse.movetype = MOVETYPE_TOSS; + corpse.solid = SOLID_TRIGGER; + corpse.modelindex = pl.modelindex; + corpse.frame = ANIM_DIESIMPLE; + corpse.angles = pl.angles; + corpse.velocity = pl.velocity; +} + +void +HLSingleplayerRules::PlayerSpawn(base_player pl) +{ + pl.classname = "player"; + pl.health = pl.max_health = 100; + pl.takedamage = DAMAGE_YES; + pl.solid = SOLID_SLIDEBOX; + pl.movetype = MOVETYPE_WALK; + pl.flags = FL_CLIENT; + pl.viewzoom = 1.0; + pl.model = "models/player.mdl"; + + + if (cvar("coop") == 1) { + string mymodel = infokey(pl, "model"); + if (mymodel) { + mymodel = sprintf("models/player/%s/%s.mdl", mymodel, mymodel); + if (whichpack(mymodel)) { + pl.model = mymodel; + } + } + } + + setmodel(pl, pl.model); + + setsize(pl, VEC_HULL_MIN, VEC_HULL_MAX); + pl.view_ofs = VEC_PLAYER_VIEWPOS; + pl.velocity = [0,0,0]; + pl.gravity = __NULL__; + pl.frame = 1; + //pl.SendEntity = Player_SendEntity; + pl.SendFlags = UPDATE_ALL; + pl.customphysics = Empty; + pl.iBleeds = TRUE; + forceinfokey(pl, "*spec", "0"); + forceinfokey(pl, "*deaths", ftos(pl.deaths)); + + /* this is where the mods want to deviate */ + entity spot; + + if (startspot != "") { + dprint(sprintf("^3Gamerules_Spawn^7: Startspot is %s\n", startspot)); + LevelDecodeParms(pl); + setorigin(pl, Landmark_GetSpot()); + } else { + LevelNewParms(); + spot = find(world, ::classname, "info_player_start"); + setorigin(pl, spot.origin); + pl.angles = spot.angles; + } + + Weapons_RefreshAmmo(pl); + Client_FixAngle(pl, pl.angles); +} diff --git a/src/server/input.qc b/src/server/input.qc new file mode 100644 index 0000000..9f802f7 --- /dev/null +++ b/src/server/input.qc @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +/* +================= +Input_Handle + +Handles impulse and whatnot +================= +*/ +void Game_Input(void) +{ + CGameRules rules = (CGameRules)g_grMode; + + if (rules.m_iIntermission) { + rules.IntermissionEnd(); + return; + } + + if (input_buttons & INPUT_BUTTON0) { + Weapons_Primary(); + } else if (input_buttons & INPUT_BUTTON4) { + Weapons_Reload(); + } else if (input_buttons & INPUT_BUTTON3) { + Weapons_Secondary(); + } else { + Weapons_Release(); + } + + if (input_buttons & INPUT_BUTTON5) { + Player_UseDown(); + } else { + Player_UseUp(); + } + + if (self.impulse == 100) { + Flashlight_Toggle(); + } + + if (cvar("sv_cheats") == 1) { + player pl = (player)self; + if (self.impulse == 101) { + pl.health = 100; + pl.armor = 100; + pl.g_items |= ITEM_SUIT; + Weapons_AddItem(pl, WEAPON_CROWBAR, -1); + Weapons_AddItem(pl, WEAPON_GLOCK, -1); + Weapons_AddItem(pl, WEAPON_PYTHON, -1); + Weapons_AddItem(pl, WEAPON_MP5, -1); + Weapons_AddItem(pl, WEAPON_SHOTGUN, -1); + Weapons_AddItem(pl, WEAPON_CROSSBOW, -1); + Weapons_AddItem(pl, WEAPON_RPG, -1); + Weapons_AddItem(pl, WEAPON_GAUSS, -1); + Weapons_AddItem(pl, WEAPON_EGON, -1); + Weapons_AddItem(pl, WEAPON_HORNETGUN, -1); + Weapons_AddItem(pl, WEAPON_HANDGRENADE, -1); + Weapons_AddItem(pl, WEAPON_SATCHEL, -1); + Weapons_AddItem(pl, WEAPON_TRIPMINE, -1); + Weapons_AddItem(pl, WEAPON_SNARK, -1); +#ifdef GEARBOX + Weapons_AddItem(pl, WEAPON_PIPEWRENCH, -1); + Weapons_AddItem(pl, WEAPON_KNIFE, -1); + Weapons_AddItem(pl, WEAPON_GRAPPLE, -1); + Weapons_AddItem(pl, WEAPON_EAGLE, -1); + Weapons_AddItem(pl, WEAPON_PENGUIN, -1); + Weapons_AddItem(pl, WEAPON_M249, -1); + Weapons_AddItem(pl, WEAPON_DISPLACER, -1); + Weapons_AddItem(pl, WEAPON_SNIPERRIFLE, -1); + Weapons_AddItem(pl, WEAPON_SPORELAUNCHER, -1); + Weapons_AddItem(pl, WEAPON_SHOCKRIFLE, -1); +#endif + } + + if (self.impulse == 102) { + // Respawn all the entities + for (entity a = world; (a = findfloat(a, ::identity, 1));) { + CBaseEntity caw = (CBaseEntity)a; + caw.Respawn(); + caw.SendFlags |= + BASEFL_CHANGED_ORIGIN | + BASEFL_CHANGED_ANGLES | + BASEFL_CHANGED_MODELINDEX | + BASEFL_CHANGED_SIZE | + BASEFL_CHANGED_SOLID | + BASEFL_CHANGED_FRAME | + BASEFL_CHANGED_SKIN | + BASEFL_CHANGED_MOVETYPE | + BASEFL_CHANGED_EFFECTS; + } + bprint(PRINT_HIGH, "Respawning all map entities...\n"); + } + } + + self.impulse = 0; +} diff --git a/src/server/item_battery.qc b/src/server/item_battery.qc new file mode 100644 index 0000000..bb19d99 --- /dev/null +++ b/src/server/item_battery.qc @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 item_battery (0 0 0.8) (-16 -16 0) (16 16 36) + +HALF-LIFE (1998) ENTITY + +HEV Suit energy battery. +It adds the following energy values to the HEV Suit by default: +Skill 1 (Easy): 15 +Skill 2 (Medium): 15 +Skill 3 (Hard): 10 + +The values can be tweaked in the skill.cfg file. + +*/ +class item_battery:CBaseEntity +{ + void(void) item_battery; + virtual void(void) Respawn; + virtual void(void) touch; +}; + +void item_battery::touch(void) +{ + if (other.classname != "player") { + return; + } + + base_player pl = (base_player)other; + + if (pl.armor >= 100) { + return; + } + /* Move this somewhere else? */ + pl.armor += Skill_GetValue("battery", 15); + if (pl.armor > 100) { + pl.armor = 100; + } + + Logging_Pickup(other, this, __NULL__); + Sound_Play(other, CHAN_ITEM, "item.battery"); + + if (real_owner || cvar("sv_playerslots") == 1) { + remove(self); + } else { + Hide(); + think = Respawn; + nextthink = time + 20.0f; + } +} + +void item_battery::Respawn(void) +{ + SetSolid(SOLID_TRIGGER); + SetMovetype(MOVETYPE_TOSS); + SetSize([-16,-16,0],[16,16,16]); + SetOrigin(m_oldOrigin); + SetModel(m_oldModel); +// botinfo = BOTINFO_ARMOR; + + think = __NULL__; + nextthink = -1; + + if (!real_owner) + Sound_Play(this, CHAN_ITEM, "item.respawn"); + + droptofloor(); +} + +void item_battery::item_battery(void) +{ + Sound_Precache("item.battery"); + Sound_Precache("item.respawn"); + model = "models/w_battery.mdl"; + CBaseEntity::CBaseEntity(); + item_healthkit::Respawn(); +} diff --git a/src/server/item_healthkit.qc b/src/server/item_healthkit.qc new file mode 100644 index 0000000..a345bc9 --- /dev/null +++ b/src/server/item_healthkit.qc @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 item_healthkit (0 0 0.8) (-16 -16 0) (16 16 36) + +HALF-LIFE (1998) ENTITY + +Healthkit item. +Adds 20 of health to the player. + +*/ +class item_healthkit:CBaseEntity +{ + void(void) item_healthkit; + virtual void(void) Respawn; + virtual void(void) touch; +}; + +void item_healthkit::touch(void) +{ + if (other.classname != "player") { + return; + } + + if (other.health >= other.max_health) { + return; + } + + Damage_Apply(other, this, -20, 0, DMG_GENERIC); + Sound_Play(this, CHAN_ITEM, "item.healthkit"); + Logging_Pickup(other, this, __NULL__); + + if (real_owner || cvar("sv_playerslots") == 1) { + remove(self); + } else { + Hide(); + think = Respawn; + nextthink = time + 20.0f; + } +} + +void item_healthkit::Respawn(void) +{ + SetSolid(SOLID_TRIGGER); + SetMovetype(MOVETYPE_TOSS); + SetSize([-16,-16,0],[16,16,16]); + SetOrigin(m_oldOrigin); + SetModel(m_oldModel); + //botinfo = BOTINFO_HEALTH; + + think = __NULL__; + nextthink = -1; + + if (!real_owner) + Sound_Play(this, CHAN_ITEM, "item.respawn"); + + droptofloor(); +} + +void item_healthkit::item_healthkit(void) +{ + Sound_Precache("item.healthkit"); + Sound_Precache("item.respawn"); + model = "models/w_medkit.mdl"; + CBaseEntity::CBaseEntity(); + item_healthkit::Respawn(); +} diff --git a/src/server/item_longjump.qc b/src/server/item_longjump.qc new file mode 100644 index 0000000..38a729e --- /dev/null +++ b/src/server/item_longjump.qc @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 item_longjump (0 0 0.8) (-16 -16 0) (16 16 36) + +HALF-LIFE (1998) ENTITY + +Longjump module. +Allows the player to jump longer distance by holding crouch +and pressing jump. + +*/ +class item_longjump:CBaseTrigger +{ + string m_strOnPlayerTouch; + + void(void) item_longjump; + + virtual void(void) touch; + virtual void(void) Respawn; + virtual void(string, string) SpawnKey; +}; + +void +item_longjump::touch(void) +{ + if (other.classname != "player") { + return; + } + + player pl = (player)other; + if (pl.g_items & ITEM_LONGJUMP) { + return; + } + + Logging_Pickup(other, this, __NULL__); + sound(other, CHAN_ITEM, "fvox/blip.wav", 1, ATTN_NORM); + sound(other, CHAN_VOICE, "fvox/powermove_on.wav", 1, ATTN_NORM); + pl.g_items |= ITEM_LONGJUMP; + + if (!target) { + UseOutput(other, m_strOnPlayerTouch); + } else { + UseTargets(other, TRIG_TOGGLE, m_flDelay); + } + + if (real_owner || cvar("sv_playerslots") == 1) { + remove(self); + } else { + Hide(); + think = Respawn; + nextthink = time + 30.0f; + } +} + +void +item_longjump::Respawn(void) +{ + SetSolid(SOLID_TRIGGER); + SetMovetype(MOVETYPE_TOSS); + SetSize([-16,-16,0],[16,16,16]); + SetOrigin(m_oldOrigin); + SetModel(m_oldModel); + + think = __NULL__; + nextthink = -1; + + if (!real_owner) + Sound_Play(this, CHAN_ITEM, "item.respawn"); +} + +void +item_longjump::SpawnKey(string strKey, string strValue) +{ + switch (strKey) { + case "OnPlayerTouch": + strValue = strreplace(",", ",_", strValue); + m_strOnPlayerTouch = strcat(m_strOnPlayerTouch, ",_", strValue); + break; + default: + CBaseTrigger::SpawnKey(strKey, strValue); + break; + } +} + +void +item_longjump::item_longjump(void) +{ + model = "models/w_longjump.mdl"; + precache_sound("items/suitchargeok1.wav"); + precache_sound("fvox/powermove_on.wav"); + precache_sound("fvox/blip.wav"); + CBaseTrigger::CBaseTrigger(); + Respawn(); +} diff --git a/src/server/item_suit.qc b/src/server/item_suit.qc new file mode 100644 index 0000000..5283317 --- /dev/null +++ b/src/server/item_suit.qc @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 item_suit (0 0 0.8) (-16 -16 0) (16 16 36) SUIT_LONGINTRO + +HALF-LIFE (1998) ENTITY + +HEV Suit +Provides the player with armor, a flashlight and a Heads-Up-Display. + +When SUIT_LONGINTRO is set, the intro dialog will be longer. + +*/ + +class item_suit:CBaseTrigger +{ + string m_strOnPlayerTouch; + + void(void) item_suit; + + virtual void(void) touch; + virtual void(void) Respawn; + virtual void(string, string) SpawnKey; +}; + +void +item_suit::touch(void) +{ + if (other.classname != "player") { + return; + } + + player pl = (player)other; + if (pl.g_items & ITEM_SUIT) { + return; + } + + Logging_Pickup(other, this, __NULL__); + sound(other, CHAN_ITEM, "fvox/bell.wav", 1, ATTN_NORM); + sound(other, CHAN_VOICE, "fvox/hev_logon.wav", 1, ATTN_NORM); + pl.g_items |= ITEM_SUIT; + m_iValue = TRUE; + + if (!target) { + UseOutput(other, m_strOnPlayerTouch); + } else { + UseTargets(other, TRIG_TOGGLE, m_flDelay); + } + + if (real_owner || cvar("sv_playerslots") == 1) { + remove(self); + } else { + Hide(); + think = Respawn; + nextthink = time + 30.0f; + } +} + +void +item_suit::Respawn(void) +{ + SetSolid(SOLID_TRIGGER); + SetMovetype(MOVETYPE_TOSS); + SetSize(VEC_HULL_MIN, VEC_HULL_MAX); + SetOrigin(m_oldOrigin); + SetModel(m_oldModel); + m_iValue = FALSE; + + think = __NULL__; + nextthink = -1; + + if (!real_owner) + Sound_Play(this, CHAN_ITEM, "item.respawn"); +} + +void +item_suit::SpawnKey(string strKey, string strValue) +{ + switch (strKey) { + case "OnPlayerTouch": + strValue = strreplace(",", ",_", strValue); + m_strOnPlayerTouch = strcat(m_strOnPlayerTouch, ",_", strValue); + break; + default: + CBaseTrigger::SpawnKey(strKey, strValue); + break; + } +} + +void +item_suit::item_suit(void) +{ + model = "models/w_suit.mdl"; + precache_sound("items/suitchargeok1.wav"); + precache_sound("fvox/hev_logon.wav"); + precache_sound("fvox/bell.wav"); + CBaseTrigger::CBaseTrigger(); + + m_strOnPlayerTouch = CreateOutput(m_strOnPlayerTouch); +} diff --git a/src/server/item_weaponbox.qc b/src/server/item_weaponbox.qc new file mode 100644 index 0000000..60bec04 --- /dev/null +++ b/src/server/item_weaponbox.qc @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +class item_weaponbox:CBaseEntity +{ + int ammo_9mm; + int ammo_357; + int ammo_buckshot; + int ammo_m203_grenade; + int ammo_bolt; + int ammo_rocket; + int ammo_uranium; + int ammo_handgrenade; + int ammo_satchel; + int ammo_tripmine; + int ammo_snark; + int ammo_hornet; + int weapon_items; + + void(void) item_weaponbox; + virtual void(void) touch; + virtual void(player) setup; +}; + +void item_weaponbox::touch(void) +{ + if (other.classname != "player") { + return; + } + + player pl = (player)other; + Logging_Pickup(other, this, __NULL__); + sound(pl, CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_NORM); + + pl.ammo_9mm += ammo_9mm; + pl.ammo_357 += ammo_357; + pl.ammo_buckshot += ammo_buckshot; + pl.ammo_m203_grenade += ammo_m203_grenade; + pl.ammo_bolt += ammo_bolt; + pl.ammo_rocket += ammo_rocket; + pl.ammo_uranium += ammo_uranium; + pl.ammo_handgrenade += ammo_handgrenade; + pl.ammo_satchel += ammo_satchel; + pl.ammo_tripmine += ammo_tripmine; + pl.ammo_snark += ammo_snark; + pl.ammo_hornet += ammo_hornet; + + /* cull */ + pl.ammo_9mm = min(pl.ammo_9mm, MAX_A_9MM); + pl.ammo_357 = min(pl.ammo_357, MAX_A_357); + pl.ammo_buckshot = min(pl.ammo_buckshot, MAX_A_BUCKSHOT); + pl.ammo_m203_grenade = min(pl.ammo_m203_grenade, MAX_A_M203_GRENADE); + pl.ammo_bolt = min(pl.ammo_bolt, MAX_A_BOLT); + pl.ammo_rocket = min(pl.ammo_rocket, MAX_A_ROCKET); + pl.ammo_uranium = min(pl.ammo_uranium, MAX_A_URANIUM); + pl.ammo_handgrenade = min(pl.ammo_handgrenade, MAX_A_HANDGRENADE); + pl.ammo_satchel = min(pl.ammo_satchel, MAX_A_SATCHEL); + pl.ammo_tripmine = min(pl.ammo_tripmine, MAX_A_TRIPMINE); + pl.ammo_snark = min(pl.ammo_snark, MAX_A_SNARK); + pl.ammo_hornet = min(pl.ammo_hornet, MAX_A_HORNET); + + pl.g_items |= weapon_items; + Weapons_RefreshAmmo(pl); + + remove(this); +} + +void item_weaponbox::setup(player pl) +{ + /* TODO: Should the magazine bits be transferred too? */ + ammo_9mm = pl.ammo_9mm; + ammo_357 = pl.ammo_357; + ammo_buckshot = pl.ammo_buckshot; + ammo_m203_grenade = pl.ammo_m203_grenade; + ammo_bolt = pl.ammo_bolt; + ammo_rocket = pl.ammo_rocket; + ammo_uranium = pl.ammo_uranium; + ammo_handgrenade = pl.ammo_handgrenade; + ammo_satchel = pl.ammo_satchel; + ammo_tripmine = pl.ammo_tripmine; + ammo_snark = pl.ammo_snark; + ammo_hornet = pl.ammo_hornet; + weapon_items = pl.g_items; +} + +void item_weaponbox::item_weaponbox(void) +{ + SetModel("models/w_weaponbox.mdl"); + SetSize([-16,-16,0], [16,16,16]); + SetSolid(SOLID_TRIGGER); + SetMovetype(MOVETYPE_TOSS); +} + +void weaponbox_spawn(player spawner) +{ + item_weaponbox weaponbox = spawn(item_weaponbox); + weaponbox.SetOrigin(spawner.origin); + weaponbox.setup(spawner); +} diff --git a/src/server/items.h b/src/server/items.h new file mode 100644 index 0000000..dee59bd --- /dev/null +++ b/src/server/items.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +/* PICKUP ITEMS */ +class item_pickup:CBaseTrigger +{ + int m_bFloating; + int m_iClip; + int m_iWasDropped; + int id; + void(void) item_pickup; + + virtual void(void) touch; + virtual void(int i) SetItem; + virtual void(void) Respawn; + virtual void(int) SetFloating; +}; diff --git a/src/server/items.qc b/src/server/items.qc new file mode 100644 index 0000000..b635066 --- /dev/null +++ b/src/server/items.qc @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +void item_pickup::touch(void) +{ + if (other.classname != "player") { + return; + } + + /* don't remove if AddItem fails */ + if (Weapons_AddItem((player)other, id, m_iClip) == FALSE) { + return; + } + + Logging_Pickup(other, this, __NULL__); + Sound_Play(other, CHAN_ITEM, "weapon.pickup"); + + UseTargets(other, TRIG_TOGGLE, m_flDelay); + + if (real_owner || m_iWasDropped == 1 || cvar("sv_playerslots") == 1) { + remove(self); + } else { + Hide(); + think = Respawn; + nextthink = time + 30.0f; + } +} + +void item_pickup::SetItem(int i) +{ + id = i; + m_oldModel = Weapons_GetWorldmodel(id); + SetModel(m_oldModel); +} + +void item_pickup::SetFloating(int i) +{ + m_bFloating = rint(bound(0, m_bFloating, 1)); +} + +void item_pickup::Respawn(void) +{ + SetSolid(SOLID_TRIGGER); + SetOrigin(m_oldOrigin); + + /* At some points, the item id might not yet be set */ + if (m_oldModel) { + SetModel(m_oldModel); + } + + SetSize([-16,-16,0], [16,16,16]); + + think = __NULL__; + nextthink = -1; + + if (!m_iWasDropped && cvar("sv_playerslots") > 1) { + if (!real_owner) + Sound_Play(this, CHAN_ITEM, "item.respawn"); + + m_iClip = -1; + } + + if (!m_bFloating) { + droptofloor(); + SetMovetype(MOVETYPE_TOSS); + } +} + +void item_pickup::item_pickup(void) +{ + Sound_Precache("item.respawn"); + Sound_Precache("weapon.pickup"); + CBaseTrigger::CBaseTrigger(); + Respawn(); +} diff --git a/src/server/monster_alien_controller.qc b/src/server/monster_alien_controller.qc new file mode 100644 index 0000000..5ef43e7 --- /dev/null +++ b/src/server/monster_alien_controller.qc @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_alien_controller (0 0.8 0.8) (-16 -16 0) (16 16 72) + +HALF-LIFE (1998) ENTITY + +Alien Controller + +*/ + +enum +{ + CON_ATTACK, + CON_ATTACK2, + CON_THROW, + CON_IDLE2, + CON_BLOCK, + CON_SHOOT, + CON_FLINCH, + CON_FLINCH2, + CON_FALL, + CON_FORWARD, + CON_BACKWARD, + CON_UP, + CON_DOWN, + CON_RIGHT, + CON_LEFT, + CON_IDLE, + CON_UNUSED, + CON_UNUSED2, + CON_DIE +}; + +class monster_alien_controller:CBaseMonster +{ + float m_flIdleTime; + float m_flPainTime; + + void(void) monster_alien_controller; + + virtual void(void) Pain; + virtual void(void) Death; + virtual void(void) IdleNoise; + virtual void(void) Respawn; +}; + +void +monster_alien_controller::Pain(void) +{ + CBaseMonster::Pain(); + + if (m_flPainTime > time) { + return; + } + + if (random() < 0.25f) { + return; + } + + Sound_Play(this, CHAN_VOICE, "monster_alien_controller.die"); + SetFrame(CON_FLINCH + floor(random(0, 2))); + m_flPainTime = time + 0.25f; +} + +void +monster_alien_controller::Death(void) +{ + /* if we're already dead (corpse) don't change animations */ + if (style != MONSTER_DEAD) { + SetFrame(CON_DIE); + Sound_Play(this, CHAN_VOICE, "monster_alien_controller.die"); + } + + /* set the functional differences */ + CBaseMonster::Death(); +} + +void +monster_alien_controller::IdleNoise(void) +{ + /* don't make noise if we're dead (corpse) */ + if (style == MONSTER_DEAD) { + return; + } + + if (m_flIdleTime > time) { + return; + } + m_flIdleTime = time + random(2,10); + + Sound_Play(this, CHAN_VOICE, "monster_alien_controller.idle"); +} + +void +monster_alien_controller::Respawn(void) +{ + CBaseMonster::Respawn(); + SetFrame(CON_IDLE); +} + +void +monster_alien_controller::monster_alien_controller(void) +{ + Sound_Precache("monster_alien_controller.alert"); + Sound_Precache("monster_alien_controller.attack"); + Sound_Precache("monster_alien_controller.die"); + Sound_Precache("monster_alien_controller.idle"); + Sound_Precache("monster_alien_controller.pain"); + netname = "Alien Controller"; + model = "models/controller.mdl"; + base_mins = [-16,-16,0]; + base_maxs = [16,16,72]; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_alien_grunt.qc b/src/server/monster_alien_grunt.qc new file mode 100644 index 0000000..64f7e45 --- /dev/null +++ b/src/server/monster_alien_grunt.qc @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_alien_grunt (0 0.8 0.8) (-32 -32 0) (32 32 64) + +HALF-LIFE (1998) ENTITY + +Alien Grunt + +*/ + +enum +{ + AG_IDLE, + AG_THREAT, + AG_WALK, + AG_RUN, + AG_LEFT, + AG_RIGHT, + AG_FLINCH, + AG_FLINCHBIG, + AG_ATTACK, + AG_ATTACK2, + AG_VICTORYSQUAT, + AG_VICTORYEAT, + AG_VICTORYSTAND, + AG_FLINCHARML, + AG_FLINCHLEGL, + AG_FLINCHARMR, + AG_FLINCHLEGR, + AG_SHOOTUP, + AG_SHOOTDOWN, + AG_SHOOT, + AG_SHOOTQUICK, + AG_SHOOTLONG, + AG_DIEHS, + AG_DIEGUT, + AG_DIEFORWARD, + AG_DIE, + AG_DIEBACK, + AG_FLOAT, + AG_SCARE, + AG_OPEN, + AG_SMASHRAIL, + AG_LAND +}; + +class monster_alien_grunt:CBaseMonster +{ + float m_flIdleTime; + float m_flPainTime; + + void(void) monster_alien_grunt; + + virtual void(void) Pain; + virtual void(void) Death; + virtual void(void) IdleNoise; + virtual void(void) Respawn; +}; + +void +monster_alien_grunt::Pain(void) +{ + CBaseMonster::Pain(); + + if (m_flPainTime > time) { + return; + } + + if (random() < 0.25f) { + return; + } + + Sound_Play(this, CHAN_VOICE, "monster_alien_grunt.pain"); + SetFrame(AG_FLINCH + floor(random(0, 2))); + m_flPainTime = time + 0.25f; +} + +void +monster_alien_grunt::Death(void) +{ + /* if we're already dead (corpse) don't change animations */ + if (style != MONSTER_DEAD) { + /* headshots == different animation */ + if (g_dmg_iHitBody == BODY_HEAD) { + if (random() < 0.5) { + SetFrame(AG_DIEHS); + } else { + SetFrame(AG_DIEFORWARD); + } + } else { + SetFrame(AG_DIE + floor(random(0, 2))); + } + + Sound_Play(this, CHAN_VOICE, "monster_alien_grunt.die"); + } + + /* set the functional differences */ + CBaseMonster::Death(); +} + +void +monster_alien_grunt::IdleNoise(void) +{ + /* don't make noise if we're dead (corpse) */ + if (style == MONSTER_DEAD) { + return; + } + + if (m_flIdleTime > time) { + return; + } + m_flIdleTime = time + random(2,10); + + Sound_Play(this, CHAN_VOICE, "monster_alien_grunt.idle"); +} + +void +monster_alien_grunt::Respawn(void) +{ + CBaseMonster::Respawn(); + SetFrame(AG_IDLE); +} + +void +monster_alien_grunt::monster_alien_grunt(void) +{ + Sound_Precache("monster_alien_grunt.alert"); + Sound_Precache("monster_alien_grunt.attack"); + Sound_Precache("monster_alien_grunt.die"); + Sound_Precache("monster_alien_grunt.idle"); + Sound_Precache("monster_alien_grunt.pain"); + + netname = "Alien Grunt"; + model = "models/agrunt.mdl"; + base_mins = [-32,-32,0]; + base_maxs = [32,32,64]; + base_health = Skill_GetValue("agrunt_health", 90); + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_alien_slave.qc b/src/server/monster_alien_slave.qc new file mode 100644 index 0000000..7dea1a5 --- /dev/null +++ b/src/server/monster_alien_slave.qc @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_alien_slave (0 0.8 0.8) (-16 -16 0) (16 16 72) + +HALF-LIFE (1998) ENTITY + +Alien Slave + +*/ + +enum +{ + SLV_IDLE, + SLV_IDLE2, + SLV_IDLE3, + SLV_CROUCH, + SLV_WALK, + SLV_WALK2, + SLV_RUN, + SLV_RIGHT, + SLV_LEFT, + SLV_JUMP, + SLV_STAIRUP, + SLV_ATTACK, + SLV_ATTACKZAP, + SLV_FLINCH, + SLV_FLINCHLA, + SLV_FLINCHRA, + SLV_FLINCHL, + SLV_FLINCHR, + SLV_DIEHS, + SLV_DIE, + SLV_DIEBACK, + SLV_DIEFORWARD, + SLV_COLLAR, + SLV_COLLAR2, + SLV_PUSHUP, + SLV_GRAB, + SLV_UPDOWN, + SLV_DOWNUP, + SLV_JIBBER, + SLV_JABBER +}; + +class monster_alien_slave:CBaseNPC +{ + float m_flIdleTime; + float m_flPainTime; + + void(void) monster_alien_slave; + + virtual void(void) Death; + virtual void(void) Pain; + virtual void(void) IdleChat; + virtual void(void) Respawn; + + virtual int(void) AnimIdle; + virtual int(void) AnimWalk; + virtual int(void) AnimRun; + + virtual int(void) AttackMelee; + virtual void(void) AttackFlail; + + virtual int(void) AttackRanged; + virtual void(void) AttackBeam; +}; + +int +monster_alien_slave::AnimIdle(void) +{ + return SLV_IDLE; +} + +int +monster_alien_slave::AnimWalk(void) +{ + return SLV_WALK; +} + +int +monster_alien_slave::AnimRun(void) +{ + return SLV_RUN; +} + +int +monster_alien_slave::AttackMelee(void) +{ + /* visual */ + AnimPlay(SLV_ATTACK); + + m_flAttackThink = m_flAnimTime; + + /* functional */ + think = AttackFlail; + nextthink = 0.25f; + return TRUE; +} + +void +monster_alien_slave::AttackFlail(void) +{ + traceline(origin, m_eEnemy.origin, FALSE, this); + + if (trace_fraction >= 1.0 || trace_ent.takedamage != DAMAGE_YES) { + Sound_Play(this, CHAN_WEAPON, "monster_zombie.attackmiss"); + return; + } + + Damage_Apply(trace_ent, this, 25, 0, 0); + Sound_Play(this, CHAN_WEAPON, "monster_zombie.attackhit"); +} + +int +monster_alien_slave::AttackRanged(void) +{ + /* visual */ + AnimPlay(SLV_ATTACKZAP); + + m_flAttackThink = m_flAnimTime; + Sound_Play(this, CHAN_VOICE, "monster_alien_slave.attack_charge"); + + /* functional */ + think = AttackBeam; + nextthink = time + 1.5f; + return TRUE; +} + +void +monster_alien_slave::AttackBeam(void) +{ + traceline(origin, m_eEnemy.origin, FALSE, this); + Sound_Play(this, CHAN_WEAPON, "monster_alien_slave.attack_shoot"); + + if (trace_fraction >= 1.0 || trace_ent.takedamage != DAMAGE_YES) { + //Sound_Play(this, CHAN_WEAPON, "monster_zombie.attackmiss"); + return; + } + + Damage_Apply(trace_ent, this, 100, 0, 0); +} + +void +monster_alien_slave::IdleChat(void) +{ + if (m_flIdleTime > time) { + return; + } + + Sentence(m_talkIdle); + + m_flIdleTime = time + 5.0f + random(0,20); +} + +void +monster_alien_slave::Pain(void) +{ + CBaseNPC::Pain(); + + if (m_flPainTime > time) { + return; + } + + if (random() < 0.25f) { + return; + } + + Sound_Play(this, CHAN_VOICE, "monster_alien_slave.pain"); + SetFrame(SLV_FLINCH + floor(random(0, 2))); + m_flPainTime = time + 0.25f; +} + +void +monster_alien_slave::Death(void) +{ + /* if we're already dead (corpse) don't change animations */ + if (style != MONSTER_DEAD) { + /* headshots == different animation */ + if (g_dmg_iHitBody == BODY_HEAD) { + if (random() < 0.5) { + SetFrame(SLV_DIEHS); + } else { + SetFrame(SLV_DIEBACK); + } + } else { + SetFrame(SLV_DIE + floor(random(0, 3))); + } + + Sound_Play(this, CHAN_VOICE, "monster_alien_slave.die"); + } + + /* set the functional differences */ + CBaseNPC::Death(); +} + +void +monster_alien_slave::Respawn(void) +{ + CBaseNPC::Respawn(); + SetFrame(SLV_IDLE); +} + +void +monster_alien_slave::monster_alien_slave(void) +{ + Sound_Precache("monster_alien_slave.die"); + Sound_Precache("monster_alien_slave.pain"); + Sound_Precache("monster_alien_slave.attack_charge"); + Sound_Precache("monster_alien_slave.attack_shoot"); + Sound_Precache("monster_zombie.attackhit"); + Sound_Precache("monster_zombie.attackmiss"); + + m_talkAnswer = ""; + m_talkAsk = ""; + m_talkAllyShot = ""; + m_talkGreet = "SLV_ALERT"; + m_talkIdle = "!SLV_IDLE"; + m_talkSmelling = ""; + m_talkStare = ""; + m_talkSurvived = ""; + m_talkWounded = ""; + + m_talkPlayerAsk = ""; + m_talkPlayerGreet = "!SLV_ALERT"; + m_talkPlayerIdle = ""; + m_talkPlayerWounded1 = ""; + m_talkPlayerWounded2 = ""; + m_talkPlayerWounded3 = ""; + m_talkUnfollow = ""; + m_talkFollow = ""; + m_talkStopFollow = ""; + + netname = "Alien Slave"; + model = "models/islave.mdl"; + base_health = Skill_GetValue("islave_health", 30); + base_mins = [-16,-16,0]; + base_maxs = [16,16,72]; + m_iAlliance = MAL_ALIEN; + CBaseNPC::CBaseNPC(); +} diff --git a/src/server/monster_apache.qc b/src/server/monster_apache.qc new file mode 100644 index 0000000..ff09a44 --- /dev/null +++ b/src/server/monster_apache.qc @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_apache (0 0.8 0.8) (-300 -300 -172) (300 300 8) + +HALF-LIFE (1998) ENTITY + +Boeing AH-64 Apache + +*/ + +class monster_apache:CBaseMonster +{ + void(void) monster_apache; + virtual void(void) Respawn; +}; + +void monster_apache::Respawn(void) +{ + CBaseMonster::Respawn(); + movetype = MOVETYPE_NONE; + takedamage = DAMAGE_NO; + iBleeds = FALSE; + setsize(this, [-300,-300,-172], [300, 300, 8]); +} + +void monster_apache::monster_apache(void) +{ + netname = "Apache"; + model = "models/apache.mdl"; + base_mins = [-16,-16,0]; + base_maxs = [16,16,72]; + base_health = Skill_GetValue("apache_health", 250); + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_babycrab.qc b/src/server/monster_babycrab.qc new file mode 100644 index 0000000..299ccc3 --- /dev/null +++ b/src/server/monster_babycrab.qc @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_babycrab (0 0.8 0.8) (-16 -16 0) (16 16 36) + +HALF-LIFE (1998) ENTITY + +Baby Headcrab + +*/ + +class monster_babycrab:monster_headcrab +{ + void(void) monster_babycrab; +}; + +void +monster_babycrab::monster_babycrab(void) +{ + monster_headcrab::monster_headcrab(); +} diff --git a/src/server/monster_barnacle.qc b/src/server/monster_barnacle.qc new file mode 100644 index 0000000..5dbaab9 --- /dev/null +++ b/src/server/monster_barnacle.qc @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_barnacle (0 0.8 0.8) (-16 -16 -36) (16 16 0) + +HALF-LIFE (1998) ENTITY + +Barnacle + +*/ + +enum +{ + BCL_IDLE, + BCL_IDLE2, + BCL_IDLE3, + BCL_FLINCH, + BCL_ATTACK, + BCL_CHEW, + BCL_DIE +}; + +class monster_barnacle:CBaseMonster +{ + void(void) monster_barnacle; + + virtual void(void) Death; + virtual void(void) Respawn; + virtual void(void) Physics; +}; + +void +monster_barnacle::Physics(void) +{ + movetype = MOVETYPE_NONE; +} + +void +monster_barnacle::Death(void) +{ + /* if we're already dead (corpse) don't change animations */ + if (style != MONSTER_DEAD) { + SetFrame(BCL_DIE); + Sound_Play(this, CHAN_VOICE, "monster_barnacle.die"); + } + + /* set the functional differences */ + CBaseMonster::Death(); +} + +void +monster_barnacle::Respawn(void) +{ + CBaseMonster::Respawn(); + SetFrame(BCL_IDLE); +} + +void monster_barnacle::monster_barnacle(void) +{ + Sound_Precache("monster_barnacle.attackchew"); + Sound_Precache("monster_barnacle.attackpull"); + Sound_Precache("monster_barnacle.die"); + + netname = "Barnacle"; + model = "models/barnacle.mdl"; + base_mins = [-16,-16,-36]; + base_maxs = [16,16,0]; + base_health = Skill_GetValue("barnacle_health", 25); + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_barney.qc b/src/server/monster_barney.qc new file mode 100644 index 0000000..67e37c6 --- /dev/null +++ b/src/server/monster_barney.qc @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_barney (0 0.8 0.8) (-16 -16 0) (16 16 72) + +HALF-LIFE (1998) ENTITY + +Barney Calhoun + +*/ + +enum +{ + BA_IDLE1, + BA_IDLE2, + BA_IDLE3, + BA_IDLE4, + BA_WALK, + BA_RUN, + BA_SHOOT1, + BA_SHOOT2, + BA_DRAW, + BA_HOLSTER, + BA_RELOAD, + BA_TURNLEFT, + BA_TURNRIGHT, + BA_FLINCH_LA, + BA_FLINCH_RA, + BA_FLINCH_LL, + BA_FLINCH_RL, + BA_FLINCH_SML +}; + +class monster_barney:CBaseNPC +{ + void(void) monster_barney; + + virtual void(void) Respawn; + virtual void(void) OnPlayerUse; + virtual void(void) Pain; + virtual void(void) Death; + virtual int(void) AnimIdle; + virtual int(void) AnimWalk; + virtual int(void) AnimRun; + + virtual void(void) AttackDraw; + virtual void(void) AttackHolster; + virtual int(void) AttackMelee; + virtual int(void) AttackRanged; +}; + +int +monster_barney::AnimIdle(void) +{ + return BA_IDLE1; +} + +int +monster_barney::AnimWalk(void) +{ + return BA_WALK; +} + +int +monster_barney::AnimRun(void) +{ + return BA_RUN; +} + +void +monster_barney::AttackDraw(void) +{ + AnimPlay(BA_DRAW); + m_flAttackThink = m_flAnimTime; +} + +void +monster_barney::AttackHolster(void) +{ + AnimPlay(BA_HOLSTER); + m_flAttackThink = m_flAnimTime; +} + +int +monster_barney::AttackMelee(void) +{ + return AttackRanged(); +} + +int +monster_barney::AttackRanged(void) +{ + /* visual */ + AnimPlay(BA_SHOOT1); + m_flAttackThink = time + 0.4f; + + /* functional */ + v_angle = vectoangles(m_eEnemy.origin - origin); + TraceAttack_FireBullets(1, origin + [0,0,16], 8, [0.01,0.01], 2); + Sound_Play(this, CHAN_WEAPON, "weapon_glock.fire"); + return TRUE; +} + +void +monster_barney::OnPlayerUse(void) +{ + if (spawnflags & MSF_PREDISASTER) { + Sentence("!BA_POK"); + return; + } + + CBaseNPC::OnPlayerUse(); +} + +void +monster_barney::Pain(void) +{ + CBaseNPC::Pain(); + + WarnAllies(); + + if (m_flAnimTime > time) { + return; + } + + if (random() < 0.25f) { + return; + } + + Sound_Speak(this, "monster_barney.pain"); + + AnimPlay(BA_FLINCH_LA + floor(random(0, 5))); + m_flAttackThink = m_flAnimTime; + m_iFlags |= MONSTER_FEAR; +} + +void +monster_barney::Death(void) +{ + WarnAllies(); + + if (style != MONSTER_DEAD) { + SetFrame(25 + floor(random(0, 6))); + Sound_Speak(this, "monster_barney.die"); + } + + /* now mark our state as 'dead' */ + CBaseNPC::Death(); +} + +void +monster_barney::Respawn(void) +{ + CBaseNPC::Respawn(); + m_iFlags |= MONSTER_CANFOLLOW; + PlayerUse = OnPlayerUse; +} + +void +monster_barney::monster_barney(void) +{ + Sound_Precache("monster_barney.die"); + Sound_Precache("monster_barney.pain"); + + /* TODO + * BA_MAD - When player gets too naughty + * */ + m_talkAnswer = "!BA_ANSWER"; + m_talkAsk = "!BA_QUESTION"; + m_talkAllyShot = "!BA_SHOOT"; + m_talkGreet = ""; + m_talkIdle = "!BA_IDLE"; + m_talkHearing = "!BA_HEAR"; + m_talkSmelling = "!BA_SMELL"; + m_talkStare = "!BA_STARE"; + m_talkSurvived = "!BA_WOUND"; + m_talkWounded = "!BA_WOUND"; + + m_talkPlayerAsk = "!BA_QUESTION"; + m_talkPlayerGreet = "!BA_HELLO"; + m_talkPlayerIdle = "!BA_IDLE"; + m_talkPlayerWounded1 = "!BA_CUREA"; + m_talkPlayerWounded2 = "!BA_CUREB"; + m_talkPlayerWounded3 = "!BA_CUREC"; + m_talkUnfollow = "!BA_WAIT"; + m_talkFollow = "!BA_OK"; + m_talkStopFollow = "!BA_STOP"; + + model = "models/barney.mdl"; + netname = "Barney"; + base_health = Skill_GetValue("barney_health", 35); + base_mins = [-16,-16,0]; + base_maxs = [16,16,72]; + m_iAlliance = MAL_FRIEND; + CBaseNPC::CBaseNPC(); +} diff --git a/src/server/monster_barney_dead.qc b/src/server/monster_barney_dead.qc new file mode 100644 index 0000000..15acce9 --- /dev/null +++ b/src/server/monster_barney_dead.qc @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_barney_dead (0 0.8 0.8) (-16 -16 0) (16 16 72) + +HALF-LIFE (1998) ENTITY + +Barney's corpse + +*/ + +class monster_barney_dead:CBaseEntity +{ + int m_iPose; + void(void) monster_barney_dead; + + virtual void(void) Hide; + virtual void(void) Respawn; + virtual void(void) Gib; + virtual void(string, string) SpawnKey; +}; + +void +monster_barney_dead::Gib(void) +{ + takedamage = DAMAGE_NO; + FX_GibHuman(this.origin); + Hide(); +} + +void +monster_barney_dead::Hide(void) +{ + SetModel(""); + solid = SOLID_NOT; + movetype = MOVETYPE_NONE; +} + +void +monster_barney_dead::Respawn(void) +{ + v_angle[0] = Math_FixDelta(m_oldAngle[0]); + v_angle[1] = Math_FixDelta(m_oldAngle[1]); + v_angle[2] = Math_FixDelta(m_oldAngle[2]); + + SetOrigin(m_oldOrigin); + angles = v_angle; + solid = SOLID_CORPSE; + movetype = MOVETYPE_NONE; + SetModel(m_oldModel); + setsize(this, VEC_HULL_MIN + [0,0,36], VEC_HULL_MAX + [0,0,36]); + takedamage = DAMAGE_YES; + health = 0; + velocity = [0,0,0]; + iBleeds = TRUE; + SetFrame(35 + m_iPose); +} + +void +monster_barney_dead::SpawnKey(string strKey, string strValue) +{ + switch (strKey) { + case "pose": + m_iPose = stoi(strValue); + break; + default: + CBaseMonster::SpawnKey(strKey, strValue); + } +} + +void +monster_barney_dead::monster_barney_dead(void) +{ + model = "models/barney.mdl"; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_bigmomma.qc b/src/server/monster_bigmomma.qc new file mode 100644 index 0000000..705f45d --- /dev/null +++ b/src/server/monster_bigmomma.qc @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_bigmomma (0 0.8 0.8) (-95 -95 0) (95 95 190) + +HALF-LIFE (1998) ENTITY + +Gonarch + +*/ + +enum +{ + GON_IDLE, + GON_IDLE2, + GON_WALK, + GON_RUN, + GON_DIE, + GON_CLAW, + GON_CLAW2, + GON_CLAW3, + GON_SPAWN, + GON_SHOOT, + GON_FLINCH, + GON_DEFEND, + GON_JUMP, + GON_ANGRY, + GON_ANGRY2, + GON_ANGRY3, + GON_BREAKWALL, + GON_FALL, + GON_FALL2, + GON_FALLDIE +}; + +class monster_bigmomma:CBaseMonster +{ + float m_flIdleTime; + + void(void) monster_bigmomma; + + virtual void(void) Death; + virtual void(void) Pain; + virtual void(void) IdleNoise; + virtual void(void) Respawn; +}; + +void +monster_bigmomma::IdleNoise(void) +{ + /* don't make noise if we're dead (corpse) */ + if (style == MONSTER_DEAD) { + return; + } + + if (m_flIdleTime > time) { + return; + } + /* timing needs to adjusted as sounds conflict */ + m_flIdleTime = time + random(2,10); + + Sound_Play(this, CHAN_VOICE, "monster_bigmomma.idle"); +} + +void +monster_bigmomma::Pain(void) +{ + CBaseMonster::Pain(); + + if (m_flAnimTime > time) { + return; + } + + if (random() < 0.25f) { + return; + } + + Sound_Play(this, CHAN_VOICE, "monster_bigmomma.pain"); + SetFrame(GON_FLINCH); + m_flAnimTime = time + 0.25f; +} + +void +monster_bigmomma::Death(void) +{ + /* if we're already dead (corpse) don't change animations */ + if (style != MONSTER_DEAD) { + SetFrame(GON_DIE); + Sound_Play(this, CHAN_VOICE, "monster_bigmomma.die"); + } + + /* set the functional differences */ + CBaseMonster::Death(); +} + +void +monster_bigmomma::Respawn(void) +{ + CBaseMonster::Respawn(); + SetFrame(GON_IDLE); +} + +void monster_bigmomma::monster_bigmomma(void) +{ + Sound_Precache("monster_bigmomma.alert"); + Sound_Precache("monster_bigmomma.attack"); + Sound_Precache("monster_bigmomma.child"); + Sound_Precache("monster_bigmomma.die"); + Sound_Precache("monster_bigmomma.idle"); + Sound_Precache("monster_bigmomma.pain"); + Sound_Precache("monster_bigmomma.step"); + netname = "Gonarch"; + model = "models/big_mom.mdl"; + /* health is based on factor, for it's not killable until last stage */ + base_health = Skill_GetValue("bigmomma_health_factor", 1.5) * 300; + base_mins = [-95,-95,0]; + base_maxs = [95,95,190]; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_bloater.qc b/src/server/monster_bloater.qc new file mode 100644 index 0000000..9ed3b7b --- /dev/null +++ b/src/server/monster_bloater.qc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_bloater (0 0.8 0.8) (-16 -16 0) (16 16 72) + +HALF-LIFE (1998) ENTITY + +Flocking Floater + +*/ + +class monster_bloater:CBaseMonster +{ + void(void) monster_bloater; +}; + +void monster_bloater::monster_bloater(void) +{ + netname = "Floater"; + model = "models/floater.mdl"; + base_mins = [-16,-16,0]; + base_maxs = [16,16,72]; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_bullchicken.qc b/src/server/monster_bullchicken.qc new file mode 100644 index 0000000..2ab5ae2 --- /dev/null +++ b/src/server/monster_bullchicken.qc @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_bullchicken (0 0.8 0.8) (-32 -32 0) (32 32 64) + +HALF-LIFE (1998) ENTITY + +Bullsquid + +*/ + +enum +{ + BULL_WALK, + BULL_RUN, + BULL_SURPIRSE, + BULL_FLINCH, + BULL_FLINCH2, + BULL_LEFT, + BULL_RIGHT, + BULL_IDLE, + BULL_WHIP, + BULL_BITE, + BULL_RANGE, + BULL_LOOK, + BULL_SEECRAB, + BULL_EAT, + BULL_INSPECT, + BULL_SNIFF, + BULL_DIE, + BULL_DIE2, + BULL_JUMP, + BULL_DRAGIDLE, + BULL_DRAG, + BULL_SCARE, + BULL_FALLIDLE, + BULL_FALL +}; + +/* the growls are used in combination with the bite sounds + * for close range attacks + */ + +class monster_bullchicken:CBaseMonster +{ + float m_flIdleTime; + + void(void) monster_bullchicken; + + virtual void(void) Death; + virtual void(void) Pain; + virtual void(void) IdleNoise; + virtual int(void) AnimIdle; + virtual int(void) AnimWalk; + virtual int(void) AnimRun; +}; + +int +monster_bullchicken::AnimIdle(void) +{ + return BULL_IDLE; +} + +int +monster_bullchicken::AnimWalk(void) +{ + return BULL_WALK; +} + +int +monster_bullchicken::AnimRun(void) +{ + return BULL_RUN; +} + +void +monster_bullchicken::IdleNoise(void) +{ + /* don't make noise if we're dead (corpse) */ + if (style == MONSTER_DEAD) { + return; + } + + if (m_flIdleTime > time) { + return; + } + /* timing needs to adjusted as sounds conflict */ + m_flIdleTime = time + random(2,10); + + Sound_Play(this, CHAN_VOICE, "monster_bullchicken.idle"); +} + +void +monster_bullchicken::Pain(void) +{ + CBaseMonster::Pain(); + + if (m_flAnimTime > time) { + return; + } + + if (random() < 0.25f) { + return; + } + + Sound_Play(this, CHAN_VOICE, "monster_bullchicken.pain"); + SetFrame((random() < 0.5) ? BULL_FLINCH : BULL_FLINCH2); + m_flAnimTime = time + 0.25f; +} + +void +monster_bullchicken::Death(void) +{ + /* if we're already dead (corpse) don't change animations */ + if (style != MONSTER_DEAD) { + + /* two different animations */ + SetFrame((random() < 0.5) ? BULL_DIE : BULL_DIE2); + + Sound_Play(this, CHAN_VOICE, "monster_bullchicken.die"); + } + + /* set the functional differences */ + CBaseMonster::Death(); +} + +void monster_bullchicken::monster_bullchicken(void) +{ + Sound_Precache("monster_bullchicken.alert"); + Sound_Precache("monster_bullchicken.attack"); + Sound_Precache("monster_bullchicken.attackbite"); + Sound_Precache("monster_bullchicken.attackshoot"); + Sound_Precache("monster_bullchicken.die"); + Sound_Precache("monster_bullchicken.idle"); + Sound_Precache("monster_bullchicken.pain"); + netname = "Bullsquid"; + model = "models/bullsquid.mdl"; + base_health = Skill_GetValue("bullsquid_health", 40); + base_mins = [-32,-32,0]; + base_maxs = [32,32,64]; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_cockroach.qc b/src/server/monster_cockroach.qc new file mode 100644 index 0000000..333f8f6 --- /dev/null +++ b/src/server/monster_cockroach.qc @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_cockroach (0 0.8 0.8) (-4 -4 0) (4 4 4) + +HALF-LIFE (1998) ENTITY + +Cockroach + +*/ + +class monster_cockroach:CBaseMonster +{ + void(void) monster_cockroach; + virtual void(void) Death; +}; + +void +monster_cockroach::Death(void) +{ + /* if we're already dead (corpse) don't change animations */ + if (style != MONSTER_DEAD) { + Sound_Play(this, CHAN_VOICE, "monster_cockroach.die"); + } + + /* make sure we gib this thing */ + health = -100; + + /* set the functional differences */ + CBaseMonster::Death(); +} + +void monster_cockroach::monster_cockroach(void) +{ + Sound_Precache("monster_cockroach.die"); + netname = "Cockroach"; + model = "models/roach.mdl"; + base_mins = [-1,-1,0]; + base_maxs = [1,1,1]; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_flyer_flock.qc b/src/server/monster_flyer_flock.qc new file mode 100644 index 0000000..9b4305e --- /dev/null +++ b/src/server/monster_flyer_flock.qc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_flyer_flock (0 0.8 0.8) (-16 -16 0) (16 16 16) + +HALF-LIFE (1998) ENTITY + +Boid + +*/ + +class monster_flyer_flock:CBaseMonster +{ + void(void) monster_flyer_flock; +}; + +void monster_flyer_flock::monster_flyer_flock(void) +{ + netname = "Boid"; + model = "models/aflock.mdl"; + base_mins = [-16,-16,0]; + base_maxs = [16,16,16]; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_gargantua.qc b/src/server/monster_gargantua.qc new file mode 100644 index 0000000..736d000 --- /dev/null +++ b/src/server/monster_gargantua.qc @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_gargantua (0 0.8 0.8) (-32 -32 0) (32 32 128) + +HALF-LIFE (1998) ENTITY + +Gargantua + +*/ + +enum +{ + GARG_IDLE, + GARG_IDLE2, + GARG_IDLE3, + GARG_IDLE4, + GARG_WALK, + GARG_RUN, + GARG_SHOOT, + GARG_SHOOT2, + GARG_ATTACK, + GARG_STOMP, + GARG_LEFT, + GARG_RIGHT, + GARG_FLINCH, + GARG_FLINCH2, + GARG_DIE, + GARG_BITEHEAD, + GARG_THROW, + GARG_SMASH, + GARG_ROLLCAR, + GARG_KICKCAR, + GARG_PUSHCAR, + GARG_BUST +}; + +class monster_gargantua:CBaseMonster +{ + float m_flIdleTime; + + void(void) monster_gargantua; + + virtual void(void) Death; + virtual void(void) Pain; + virtual void(void) IdleNoise; + virtual void(void) Respawn; +}; + +void +monster_gargantua::IdleNoise(void) +{ + /* don't make noise if we're dead (corpse) */ + if (style == MONSTER_DEAD) { + return; + } + + if (m_flIdleTime > time) { + return; + } + /* timing needs to adjusted as sounds conflict */ + m_flIdleTime = time + random(2,10); + + Sound_Play(this, CHAN_VOICE, "monster_gargantua.idle"); +} + +void +monster_gargantua::Pain(void) +{ + CBaseMonster::Pain(); + + if (m_flAnimTime > time) { + return; + } + + if (random() < 0.25f) { + return; + } + + Sound_Play(this, CHAN_VOICE, "monster_gargantua.pain"); + SetFrame((random() < 0.5) ? GARG_FLINCH : GARG_FLINCH2); + m_flAnimTime = time + 0.25f; +} + +void +monster_gargantua::Death(void) +{ + /* if we're already dead (corpse) don't change animations */ + if (style != MONSTER_DEAD) { + SetFrame(GARG_DIE); + Sound_Play(this, CHAN_VOICE, "monster_gargantua.die"); + } + + /* set the functional differences */ + CBaseMonster::Death(); +} + +void +monster_gargantua::Respawn(void) +{ + CBaseMonster::Respawn(); + SetFrame(GARG_IDLE); + /* takes damage from explosives only + * takedamage = DAMAGE_NO; */ + iBleeds = FALSE; +} + +void monster_gargantua::monster_gargantua(void) +{ + Sound_Precache("monster_gargantua.alert"); + Sound_Precache("monster_gargantua.attack"); + Sound_Precache("monster_gargantua.attackflame"); + Sound_Precache("monster_gargantua.attackflameon"); + Sound_Precache("monster_gargantua.attackflameoff"); + Sound_Precache("monster_gargantua.die"); + Sound_Precache("monster_gargantua.idle"); + Sound_Precache("monster_gargantua.pain"); + Sound_Precache("monster_gargantua.step"); + netname = "Gargantua"; + model = "models/garg.mdl"; + base_health = Skill_GetValue("gargantua_health", 800); + base_mins = [-32,-32,0]; + base_maxs = [32,32,128]; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_gman.qc b/src/server/monster_gman.qc new file mode 100644 index 0000000..d1fff66 --- /dev/null +++ b/src/server/monster_gman.qc @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_gman (0 0.8 0.8) (-16 -16 0) (16 16 72) + +HALF-LIFE (1998) ENTITY + +G-Man + +*/ + +enum +{ + GMAN_IDLE, + GMAN_IDLETIE, + GMAN_IDLELOOK, + GMAN_IDLE2, + GMAN_OPEN, + GMAN_STAND, + GMAN_WALK, + GMAN_YES, + GMAN_NO, + GMAN_NOBIG, + GMAN_YESBIG, + GMAN_LISTEN, + GMAN_LOOKDOWN, + GMAN_LOOKDOWN2 +}; + +class monster_gman:CBaseMonster +{ + void(void) monster_gman; + + virtual void(void) Respawn; + virtual int(void) AnimIdle; + virtual int(void) AnimWalk; + virtual int(void) AnimRun; +}; + +int +monster_gman::AnimIdle(void) +{ + return GMAN_IDLE; +} + +int +monster_gman::AnimWalk(void) +{ + return GMAN_WALK; +} + +int +monster_gman::AnimRun(void) +{ + return GMAN_WALK; +} + +void monster_gman::Respawn(void) +{ + /* he can't die, he's the G-Man! */ + CBaseMonster::Respawn(); + SetFrame(GMAN_IDLE); + takedamage = DAMAGE_NO; + iBleeds = FALSE; +} + +void monster_gman::monster_gman(void) +{ + netname = "G-Man"; + model = "models/gman.mdl"; + base_mins = [-16,-16,0]; + base_maxs = [16,16,72]; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_headcrab.qc b/src/server/monster_headcrab.qc new file mode 100644 index 0000000..00f0cd2 --- /dev/null +++ b/src/server/monster_headcrab.qc @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_headcrab (0 0.8 0.8) (-16 -16 0) (16 16 36) + +HALF-LIFE (1998) ENTITY + +Headcrab + +*/ + +enum +{ + HC_IDLE1, + HC_IDLE2, + HC_IDLE3, + HC_WALK, + HC_RUN, + HC_ANGRY, + HC_FLINCH, + HC_DIE, + HC_180_LEFT, + HC_180_RIGHT, + HC_JUMP, + HC_JUMP_VARIATION1, + HC_JUMP_VARIATION2, + HC_YAW_ADJUSTMENT, + HC_HEADCRABBED1, + HC_HEADCRABBED2, + HC_HEADIDLE, + HC_CRASHIDLE, + HC_CRASH, + HC_STRUGGLEIDLE, + HC_STRUGGLE +}; + +class monster_headcrab:CBaseMonster +{ + float m_flIdleTime; + + void(void) monster_headcrab; + + virtual void(void) Pain; + virtual void(void) Death; + virtual void(void) IdleNoise; + virtual int(void) AnimIdle; + virtual int(void) AnimWalk; + virtual int(void) AnimRun; + virtual int(void) AttackRanged; + virtual void(void) touch; +}; + +int +monster_headcrab::AnimIdle(void) +{ + return HC_IDLE1; +} + +int +monster_headcrab::AnimWalk(void) +{ + return HC_WALK; +} + +int +monster_headcrab::AnimRun(void) +{ + return HC_RUN; +} + +int +monster_headcrab::AttackRanged(void) +{ + /* visual */ + if (random() < 0.5) + AnimPlay(HC_JUMP); + else + AnimPlay(HC_JUMP_VARIATION1); + + m_flAttackThink = m_flAnimTime; + Sound_Play(this, CHAN_VOICE, "monster_headcrab.attack"); + + /* functional */ + makevectors(vectoangles(m_eEnemy.origin - origin)); + velocity = v_forward * 512 + [0,0,250]; + return TRUE; +} + +void +monster_headcrab::touch(void) +{ + if (other.takedamage == DAMAGE_YES) + if (frame == HC_JUMP || frame == HC_JUMP_VARIATION1) + Damage_Apply(other, this, 500, 0, 0); +} + +void +monster_headcrab::Pain(void) +{ + CBaseMonster::Pain(); + + if (m_flAnimTime > time) { + return; + } + + if (random() < 0.25f) { + return; + } + + Sound_Play(this, CHAN_VOICE, "monster_headcrab.pain"); + SetFrame(HC_FLINCH); + m_flAnimTime = time + 0.25f; +} + +void +monster_headcrab::Death(void) +{ + /* if we're already dead (corpse) don't change animations */ + if (style != MONSTER_DEAD) { + SetFrame(HC_DIE); + Sound_Play(this, CHAN_VOICE, "monster_headcrab.die"); + } + + /* set the functional differences */ + CBaseMonster::Death(); +} + +void +monster_headcrab::IdleNoise(void) +{ + /* don't make noise if we're dead (corpse) */ + if (style == MONSTER_DEAD) { + return; + } + + if (m_flIdleTime > time) { + return; + } + m_flIdleTime = time + random(2,10); + + Sound_Play(this, CHAN_VOICE, "monster_headcrab.idle"); +} + +void +monster_headcrab::monster_headcrab(void) +{ + Sound_Precache("monster_headcrab.alert"); + Sound_Precache("monster_headcrab.attack"); + Sound_Precache("monster_headcrab.attackhit"); + Sound_Precache("monster_headcrab.die"); + Sound_Precache("monster_headcrab.idle"); + Sound_Precache("monster_headcrab.pain"); + + if (classname == "monster_babycrab") { + netname = "Baby Headcrab"; + model = "models/baby_headcrab.mdl"; + base_health = Skill_GetValue ("headcrab_health", 10) /4; + } else { + netname = "Headcrab"; + model = "models/headcrab.mdl"; + base_health = Skill_GetValue("headcrab_health", 10); + } + + base_mins = [-16,-16,0]; + base_maxs = [16,16,36]; + m_iAlliance = MAL_ALIEN; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_hevsuit_dead.qc b/src/server/monster_hevsuit_dead.qc new file mode 100644 index 0000000..f99937a --- /dev/null +++ b/src/server/monster_hevsuit_dead.qc @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_hevsuit_dead (0 0.8 0.8) (-16 -16 0) (16 16 72) + +HALF-LIFE (1998) ENTITY + +HEV-Suit/Player's corpse + +*/ + +class monster_hevsuit_dead:CBaseMonster +{ + int m_iPose; + void(void) monster_hevsuit_dead; + + virtual void(void) Hide; + virtual void(void) Respawn; + virtual void(void) Gib; + virtual void(string, string) SpawnKey; +}; + +void +monster_hevsuit_dead::Gib(void) +{ + takedamage = DAMAGE_NO; + FX_GibHuman(this.origin); + Hide(); +} + +void +monster_hevsuit_dead::Hide(void) +{ + SetModel(""); + solid = SOLID_NOT; + movetype = MOVETYPE_NONE; +} + +void +monster_hevsuit_dead::Respawn(void) +{ + v_angle[0] = Math_FixDelta(m_oldAngle[0]); + v_angle[1] = Math_FixDelta(m_oldAngle[1]); + v_angle[2] = Math_FixDelta(m_oldAngle[2]); + + SetOrigin(m_oldOrigin); + angles = v_angle; + solid = SOLID_CORPSE; + movetype = MOVETYPE_NONE; + SetModel(m_oldModel); + setsize(this, VEC_HULL_MIN + [0,0,36], VEC_HULL_MAX + [0,0,36]); + takedamage = DAMAGE_YES; + health = 0; + velocity = [0,0,0]; + iBleeds = TRUE; + SetFrame(73 + m_iPose); + SendFlags |= NPC_BODY; +} + +void +monster_hevsuit_dead::SpawnKey(string strKey, string strValue) +{ + switch (strKey) { + case "pose": + m_iPose = stoi(strValue); + break; + case "body": + SetBody(stoi(strValue) + 1); + break; + case "skin": + SetSkin(stoi(strValue)); + break; + default: + CBaseMonster::SpawnKey(strKey, strValue); + } +} + +void +monster_hevsuit_dead::monster_hevsuit_dead(void) +{ + model = "models/player.mdl"; + m_iBody = 2; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_hgrunt_dead.qc b/src/server/monster_hgrunt_dead.qc new file mode 100644 index 0000000..77d6a1c --- /dev/null +++ b/src/server/monster_hgrunt_dead.qc @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_hgrunt_dead (0 0.8 0.8) (-16 -16 0) (16 16 72) + +HALF-LIFE (1998) ENTITY + +Human Grunt's corpse + +*/ + +class monster_hgrunt_dead:CBaseMonster +{ + int m_iPose; + void(void) monster_hgrunt_dead; + + virtual void(void) Hide; + virtual void(void) Respawn; + virtual void(void) Gib; + virtual void(string, string) SpawnKey; +}; + +void +monster_hgrunt_dead::Gib(void) +{ + takedamage = DAMAGE_NO; + FX_GibHuman(this.origin); + Hide(); +} + +void +monster_hgrunt_dead::Hide(void) +{ + SetModel(""); + solid = SOLID_NOT; + movetype = MOVETYPE_NONE; +} + +void +monster_hgrunt_dead::Respawn(void) +{ + v_angle[0] = Math_FixDelta(m_oldAngle[0]); + v_angle[1] = Math_FixDelta(m_oldAngle[1]); + v_angle[2] = Math_FixDelta(m_oldAngle[2]); + + SetOrigin(m_oldOrigin); + angles = v_angle; + solid = SOLID_CORPSE; + movetype = MOVETYPE_NONE; + SetModel(m_oldModel); + setsize(this, VEC_HULL_MIN + [0,0,36], VEC_HULL_MAX + [0,0,36]); + takedamage = DAMAGE_YES; + health = 0; + velocity = [0,0,0]; + iBleeds = TRUE; + SetFrame(44 + m_iPose); + SendFlags |= NPC_BODY; +} + +void +monster_hgrunt_dead::SpawnKey(string strKey, string strValue) +{ + switch (strKey) { + case "pose": + m_iPose = stoi(strValue); + break; + case "body": + m_iBody = stoi(strValue) + 1; + break; + case "skin": + skin = stoi(strValue); + break; + default: + CBaseMonster::SpawnKey(strKey, strValue); + } +} + +void +monster_hgrunt_dead::monster_hgrunt_dead(void) +{ + model = "models/hgrunt.mdl"; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_houndeye.qc b/src/server/monster_houndeye.qc new file mode 100644 index 0000000..7a6cc0b --- /dev/null +++ b/src/server/monster_houndeye.qc @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_houndeye (0 0.8 0.8) (-16 -16 0) (16 16 36) + +HALF-LIFE (1998) ENTITY + +Houndeye + +*/ + +#define HE_BLAST_RADIUS 384 + +enum +{ + HE_IDLE, + HE_IDLE2, + HE_IDLE3, + HE_RUN, + HE_RUN2, + HE_RUN3, + HE_DIE, + HE_DIE2, + HE_DIE3, + HE_DIE4, + HE_ATTACK, + HE_FLINCH, + HE_FLINCH2, + HE_DIE5, + HE_WALKLIMP, + HE_WALK2, + HE_LEADERLOOK, + HE_SLEEP, + HE_GOTOSLEEP, + HE_WAKE, + HE_IDLEMAD, + HE_IDLEMAD2, + HE_IDLEMAD3, + HE_INSPECT, + HE_EAT, + HE_LEFT, + HE_RIGHT, + HE_JUMPBACK, + HE_WAKEFAST, + HE_WHIMPER, + HE_JUMPWINDOW +}; + +class monster_houndeye:CBaseMonster +{ + float m_flIdleTime; + + void(void) monster_houndeye; + + virtual void(void) Pain; + virtual void(void) Death; + virtual void(void) IdleNoise; + virtual void(void) Respawn; + + virtual int(void) AttackMelee; + virtual void(void) AttackBlast; +}; + +int +monster_houndeye::AttackMelee(void) +{ + AnimPlay(HE_ATTACK); + Sound_Play(this, CHAN_WEAPON, "monster_houndeye.attack"); + m_flAttackThink = m_flAnimTime + 0.5f; + + think = AttackBlast; + nextthink = m_flAnimTime; + return TRUE; +} + +void +monster_houndeye::AttackBlast(void) +{ + float new_dmg; + float dist; + float diff; + vector pos; + float dmg = 50; /* TODO: set proper damage */ + + for (entity e = world; (e = findfloat(e, ::takedamage, DAMAGE_YES));) { + pos[0] = e.absmin[0] + (0.5 * (e.absmax[0] - e.absmin[0])); + pos[1] = e.absmin[1] + (0.5 * (e.absmax[1] - e.absmin[1])); + pos[2] = e.absmin[2] + (0.5 * (e.absmax[2] - e.absmin[2])); + + if (e.classname == "monster_houndeye") + continue; + + /* don't bother if it's not anywhere near us */ + dist = vlen(origin - pos); + if (dist > HE_BLAST_RADIUS) { + continue; + } + + /* can we physically hit this thing? */ + other = world; + traceline(e.origin, origin, MOVE_OTHERONLY, this); + + if (trace_fraction < 1.0f) + dmg *= 0.5f; + + /* calculate new damage values */ + diff = vlen(origin - pos); + diff = (HE_BLAST_RADIUS - diff) / HE_BLAST_RADIUS; + new_dmg = rint(dmg * diff); + + if (diff > 0) { + Damage_Apply(e, this, new_dmg, 0, DMG_EXPLODE); + } + } + Sound_Play(this, CHAN_WEAPON, "monster_houndeye.blast"); +} + +void +monster_houndeye::Pain(void) +{ + CBaseMonster::Pain(); + + if (m_flAnimTime > time) { + return; + } + + if (random() < 0.25f) { + return; + } + + Sound_Play(this, CHAN_VOICE, "monster_houndeye.pain"); + SetFrame(HE_FLINCH + floor(random(0, 2))); + m_flAnimTime = time + 0.25f; +} + +void +monster_houndeye::Death(void) +{ + /* if we're already dead (corpse) don't change animations */ + if (style != MONSTER_DEAD) { + SetFrame(HE_DIE + floor(random(0, 4))); + + Sound_Play(this, CHAN_VOICE, "monster_houndeye.die"); + } + + /* set the functional differences */ + CBaseMonster::Death(); +} + +void +monster_houndeye::IdleNoise(void) +{ + /* don't make noise if we're dead (corpse) */ + if (style == MONSTER_DEAD) { + return; + } + + if (m_flIdleTime > time) { + return; + } + m_flIdleTime = time + random(2,10); + + Sound_Play(this, CHAN_VOICE, "monster_houndeye.idle"); +} + +void +monster_houndeye::Respawn(void) +{ + CBaseMonster::Respawn(); + SetFrame(HE_IDLE); +} + +void +monster_houndeye::monster_houndeye(void) +{ + Sound_Precache("monster_houndeye.alert"); + Sound_Precache("monster_houndeye.attack"); + Sound_Precache("monster_houndeye.blast"); + Sound_Precache("monster_houndeye.die"); + Sound_Precache("monster_houndeye.idle"); + Sound_Precache("monster_houndeye.pain"); + netname = "Houndeye"; + model = "models/houndeye.mdl"; + base_health = Skill_GetValue("houndeye_health", 20); + base_mins = [-16,-16,0]; + base_maxs = [16,16,36]; + m_iAlliance = MAL_ALIEN; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_human_assassin.qc b/src/server/monster_human_assassin.qc new file mode 100644 index 0000000..bda6378 --- /dev/null +++ b/src/server/monster_human_assassin.qc @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_human_assassin (0 0.8 0.8) (-16 -16 0) (16 16 72) + +HALF-LIFE (1998) ENTITY + +Black Ops - Assassin + +*/ + +enum +{ + HAS_IDLE, + HAS_IDLE3, + HAS_IDLE2, + HAS_RUN, + HAS_WALK, + HAS_SHOOT, + HAS_NADETHROW, + HAS_KICK, + HAS_KICK2, + HAS_DIERUN, + HAS_DIEBACK, + HAS_DIE, + HAS_JUMP, + HAS_UP, + HAS_UNUSED, + HAS_ATTACKDOWN, + HAS_LAND +}; + +class monster_human_assassin:CBaseMonster +{ + float m_flIdleTime; + + void(void) monster_human_assassin; + + virtual void(void) Death; + virtual void(void) Respawn; +}; + +void +monster_human_assassin::Death(void) +{ + /* if we're already dead (corpse) don't change animations */ + if (style != MONSTER_DEAD) { + /* headshots == different animation */ + /* this animation may not have been used, but it looks cool */ + if (g_dmg_iHitBody == BODY_HEAD) { + if (random() < 0.5) { + SetFrame(HAS_DIERUN); + } else { + SetFrame(HAS_DIEBACK); + } + } else { + SetFrame(HAS_DIE); + } + } + + /* set the functional differences */ + CBaseMonster::Death(); +} + +void +monster_human_assassin::Respawn(void) +{ + CBaseMonster::Respawn(); + frame = HAS_IDLE; +} + +void +monster_human_assassin::monster_human_assassin(void) +{ + netname = "Assassin"; + model = "models/hassassin.mdl"; + base_health = Skill_GetValue("hassassin_health", 50); + base_mins = [-16,-16,0]; + base_maxs = [16,16,72]; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_human_grunt.qc b/src/server/monster_human_grunt.qc new file mode 100644 index 0000000..dd6981b --- /dev/null +++ b/src/server/monster_human_grunt.qc @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_human_grunt (0 0.8 0.8) (-16 -16 0) (16 16 72) + +HALF-LIFE (1998) ENTITY + +HECU - Human Grunt + +*/ + +enum +{ + GR_WALK, + GR_RUN, + GR_VICTORYDANCE, + GR_COWER, + GR_FLINCH, + GR_LEFTLEGFLINCH, + GR_RIGHTLEGFLINCH, + GR_RIGHTARMFLINCH, + GR_LEFTARMFLINCH, + GR_LAUNCHNADE, + GR_THROWNADE, + GR_IDLE, + GR_IDLE2, + GR_COMBATIDLE, + GR_FRONTKICK, + GR_CROUCHIDLE, + GR_CROUCHWAIT, + GR_CROUCHSHOOTMP5, + GR_STANDSHOOTMP5, + GR_RELOADMP5, + GR_CROUCHSHOOTSG, + GR_STANDSHOOTSG, + GR_RELOADSG, + GR_SIGNALADV, + GR_SIGNALFLANK, + GR_SIGNALRETREAT, + GR_DROPNADE, + GR_LIMPWALK, + GR_LIMPRUN, + GR_TURNLEFT, + GR_TURNRIGHT, + GR_STRAFELEFT, + GR_STRAFERIGHT, + GR_DIEBACK, + GR_DIEFORWARD, + GR_DIE, + GR_DIEBACK2, + GR_DIEHS, + GR_DIEGUT, + GR_BARNACLE1, + GR_BARNACLE2, + GR_BARNACLE3, + GR_BARNACLE4, + GR_DEADSTOMACH, + GR_DEADSTOMACH2, + GR_DEADSIDE, + GR_DEADSITTING, + GR_REPELJUMP, + GR_REPEL, + GR_REPELSHOOT, + GR_REPELLAND, + GR_REPELDIE, + GR_DRAGHOLEIDLE, + GR_DRAGHOLE, + GR_BUSTWALL, + GR_HOPRAIL, + GR_CONVERSE1, + GR_CONVERSE2, + GR_STARTLELEFT, + GR_STRRTLERIGHT, + GR_DIVE, + GR_DEFUSE, + GR_CORNER1, + GR_CORNER2, + GR_STONETOSS, + GR_CLIFFDIE, + GR_DIVESIDEIDLE, + GR_DIVESIDE, + GR_DIVEKNEELIDLE, + GR_DIVEKNEEL, + GR_WMBUTTON, + GR_WM, + GR_WMJUMP, + GR_BUSTWINDOW, + GR_DRAGLEFT, + GR_DRAGRIGHT, + GR_TRACKWAVE, + GR_TRACKDIVE, + GR_FLYBACK, + GR_IMPALED, + GR_JUMPTRACKS, + GR_THROWPIPE, + GR_PLUNGER +}; + +class monster_human_grunt:CBaseNPC +{ + float m_flIdleTime; + int m_iMP5Burst; + + void(void) monster_human_grunt; + + virtual void(void) Scream; + virtual void(void) IdleChat; + virtual void(void) Respawn; + virtual void(void) Pain; + virtual void(void) Death; + + virtual int(void) AnimIdle; + virtual int(void) AnimWalk; + virtual int(void) AnimRun; + + virtual int(void) AttackRanged; + virtual int(void) AttackMelee; + virtual void(void) AttackKick; + +}; + +int +monster_human_grunt::AnimIdle(void) +{ + return GR_IDLE; +} + +int +monster_human_grunt::AnimWalk(void) +{ + return GR_WALK; +} + +int +monster_human_grunt::AnimRun(void) +{ + return GR_RUN; +} + +int +monster_human_grunt::AttackMelee(void) +{ + /* visual */ + AnimPlay(GR_FRONTKICK); + + m_flAttackThink = m_flAnimTime; + Sound_Play(this, CHAN_VOICE, "monster_zombie.attack"); + + /* functional */ + think = AttackKick; + nextthink = 0.25f; + return TRUE; +} + +void +monster_human_grunt::AttackKick(void) +{ + traceline(origin, m_eEnemy.origin, FALSE, this); + + if (trace_fraction >= 1.0 || trace_ent.takedamage != DAMAGE_YES) { + //Sound_Play(this, CHAN_WEAPON, "monster_zombie.attackmiss"); + return; + } + + Damage_Apply(trace_ent, this, 25, 0, 0); + //Sound_Play(this, CHAN_WEAPON, "monster_zombie.attackhit"); +} + +int +monster_human_grunt::AttackRanged(void) +{ + /* visual */ + AnimPlay(GR_STANDSHOOTMP5); + Sound_Play(this, CHAN_WEAPON, "weapon_mp5.shoot"); + + if (m_iMP5Burst >= 2) { + m_iMP5Burst = 0; + m_flAttackThink = time + 0.4f; + } else { + m_iMP5Burst++; + m_flAttackThink = time + 0.1f; + } + + /* functional */ + v_angle = vectoangles(m_eEnemy.origin - origin); + TraceAttack_FireBullets(1, origin + [0,0,16], 8, [0.01,0.01], 2); + return TRUE; +} + +void monster_human_grunt::Scream(void) +{ + if (m_flIdleTime > time) { + return; + } + + Sentence(m_talkAllyShot); + m_flIdleTime = time + 5.0f; +} + +void monster_human_grunt::IdleChat(void) +{ + if (m_flIdleTime > time) { + return; + } + + Sentence(m_talkIdle); + + /* Sentence(m_talkPlayerIdle); */ + /* come up with logic to make them repsone to questions + * Sentence(m_talkAsk); + * Sentence(m_talkAnswer); + */ + m_flIdleTime = time + 5.0f + random(0,20); +} + +void +monster_human_grunt::Pain(void) +{ + CBaseMonster::Pain(); + + if (m_flAnimTime > time) { + return; + } + + if (random() < 0.25f) { + return; + } + + Sound_Play(this, CHAN_VOICE, "monster_human_grunt.pain"); + SetFrame(GR_FLINCH); + m_flAnimTime = time + 0.25f; +} + +void +monster_human_grunt::Death(void) +{ + /* if we're already dead (corpse) don't change animations */ + if (style != MONSTER_DEAD) { + /* headshots == different animation */ + /* this animation may not have been used, but it looks cool */ + if (g_dmg_iHitBody == BODY_HEAD) { + if (random() < 0.5) { + SetFrame(GR_DIEHS); + } else { + SetFrame(GR_DIEBACK); + } + } else { + SetFrame(GR_DIE); + } + } + + Sound_Play(this, CHAN_VOICE, "monster_human_grunt.die"); + + /* set the functional differences */ + CBaseMonster::Death(); +} + +void +monster_human_grunt::Respawn(void) +{ + CBaseMonster::Respawn(); + SetFrame(GR_IDLE); +} + + +void monster_human_grunt::monster_human_grunt(void) +{ + Sound_Precache("monster_human_grunt.die"); + Sound_Precache("monster_human_grunt.pain"); + + /* Adding some into other slots in hopes it feels right + * listed below are other setences that might need their own: + * !HG_MONST - Monster HG_ALERT + * !HG_GREN - Grenade toss + * !HG_CHECK - Sector check question + * !HG_CLEAR - Sector clear response */ + + m_talkAnswer = "!HG_ANSWER"; + m_talkAsk = "!HG_QUEST"; + m_talkAllyShot = "!HG_COVER"; + m_talkGreet = ""; + m_talkIdle = "!HG_IDLE"; + m_talkSmelling = ""; + m_talkStare = ""; + m_talkSurvived = "!HG_CLEAR"; + m_talkWounded = "!HG_CHECK"; + + m_talkPlayerAsk = ""; + m_talkPlayerGreet = "!HG_ALERT"; + m_talkPlayerIdle = "!HG_CHARGE"; + m_talkPlayerWounded1 = ""; + m_talkPlayerWounded2 = ""; + m_talkPlayerWounded3 = ""; + m_talkUnfollow = ""; + m_talkFollow = ""; + m_talkStopFollow = ""; + + + netname = "Grunt"; + model = "models/hgrunt.mdl"; + base_health = Skill_GetValue("hgrunt_health", 50); + base_mins = [-16,-16,0]; + base_maxs = [16,16,72]; + m_iAlliance = MAL_ENEMY; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_ichthyosaur.qc b/src/server/monster_ichthyosaur.qc new file mode 100644 index 0000000..eb30588 --- /dev/null +++ b/src/server/monster_ichthyosaur.qc @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_ichthyosaur (0 0.8 0.8) (-32 -32 0) (32 32 64) + +HALF-LIFE (1998) ENTITY + +Ichthyosaur + +*/ + +enum +{ + ICHY_IDLE, + ICHY_SWIM, + ICHY_THRUST, + ICHY_DIE1, + ICHY_DIE2, + ICHY_FLINCH, + ICHY_FLINCH2, + ICHY_DIE3, + ICHY_BITER, + ICHY_BITEL, + ICHY_ATTACK, + ICHY_RIGHT, + ICHY_LEFT, + ICHY_180, + ICHY_HITCAGE, + ICHY_JUMP +}; + +class monster_ichthyosaur:CBaseMonster +{ + float m_flIdleTime; + + void(void) monster_ichthyosaur; + + virtual void(void) Pain; + virtual void(void) Death; + virtual void(void) IdleNoise; + virtual void(void) Respawn; +}; + +void +monster_ichthyosaur::Pain(void) +{ + CBaseMonster::Pain(); + + if (m_flAnimTime > time) { + return; + } + + if (random() < 0.25f) { + return; + } + + Sound_Play(this, CHAN_VOICE, "monster_ichthyosaur.pain"); + SetFrame(ICHY_FLINCH + floor(random(0, 2))); + m_flAnimTime = time + 0.25f; +} + +void +monster_ichthyosaur::Death(void) +{ + /* if we're already dead (corpse) don't change animations */ + if (style != MONSTER_DEAD) { + int r = floor(random(0,3)); + + switch (r) { + case 1: + SetFrame(ICHY_DIE2); + break; + case 2: + SetFrame(ICHY_DIE3); + break; + default: + SetFrame(ICHY_DIE1); + break; + } + + Sound_Play(this, CHAN_VOICE, "monster_ichthyosaur.die"); + } + + /* set the functional differences */ + CBaseMonster::Death(); +} + +void +monster_ichthyosaur::IdleNoise(void) +{ + /* don't make noise if we're dead (corpse) */ + if (style == MONSTER_DEAD) { + return; + } + + if (m_flIdleTime > time) { + return; + } + m_flIdleTime = time + random(2,10); + + Sound_Play(this, CHAN_VOICE, "monster_ichthyosaur.idle"); +} + +void +monster_ichthyosaur::Respawn(void) +{ + CBaseMonster::Respawn(); + SetFrame(ICHY_IDLE); +} + +void +monster_ichthyosaur::monster_ichthyosaur(void) +{ + Sound_Precache("monster_ichthyosaur.alert"); + Sound_Precache("monster_ichthyosaur.attack"); + Sound_Precache("monster_ichthyosaur.die"); + Sound_Precache("monster_ichthyosaur.idle"); + Sound_Precache("monster_ichthyosaur.pain"); + netname = "Ichthyosaur"; + model = "models/icky.mdl"; + base_mins = [-32,-32,0]; + base_maxs = [32,32,64]; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_leech.qc b/src/server/monster_leech.qc new file mode 100644 index 0000000..a37e270 --- /dev/null +++ b/src/server/monster_leech.qc @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_leech (0 0.8 0.8) (-6 -6 0) (6 6 6) + +HALF-LIFE (1998) ENTITY + +Leech + +*/ + +enum +{ + LEECH_SWIM, + LEECH_SWIM2, + LEECH_ATTACK, + LEECH_HOVER, + LEECH_LEFT, + LEECH_RIGHT, + LEECH_DIE, + LEECH_DIEEND +}; + +class monster_leech:CBaseMonster +{ + float m_flIdleTime; + + void(void) monster_leech; + + virtual void(void) Death; + virtual void(void) DeathEnd; + virtual void(void) Respawn; +}; + +void +monster_leech::DeathEnd(void) +{ + SetFrame(LEECH_DIEEND); +} + +void +monster_leech::Death(void) +{ + /* if we're already dead (corpse) don't change animations */ + if (style != MONSTER_DEAD) { + SetFrame(LEECH_DIE); + think = DeathEnd; + nextthink = time + 1.0f; + } + + /* set the functional differences */ + CBaseMonster::Death(); +} + +void +monster_leech::Respawn(void) +{ + CBaseMonster::Respawn(); + SetFrame(LEECH_SWIM); +} + +void monster_leech::monster_leech(void) +{ + Sound_Precache("monster_leech.alert"); + Sound_Precache("monster_leech.attack"); + netname = "Leech"; + model = "models/leech.mdl"; + base_mins = [-6,-6,0]; + base_maxs = [6,6,6]; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_miniturret.qc b/src/server/monster_miniturret.qc new file mode 100644 index 0000000..b952d73 --- /dev/null +++ b/src/server/monster_miniturret.qc @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_miniturret (0 0.8 0.8) (-16 -16 -32) (16 16 32) + +HALF-LIFE (1998) ENTITY + +Automatic Turret (small) + +*/ + +enum +{ + TUR_IDLE, + TUR_FIRE, + TUR_SPIN, + TUR_DEPLOY, + TUR_RETIRE, + TUR_DIE +}; + +class monster_miniturret:CBaseMonster +{ + void(void) monster_miniturret; +}; + +void monster_miniturret::monster_miniturret(void) +{ + netname = "Mini-Turret"; + model = "models/miniturret.mdl"; + base_mins = [-16,-16,-32]; + base_maxs = [16,16,32]; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_nihilanth.qc b/src/server/monster_nihilanth.qc new file mode 100644 index 0000000..0e8c620 --- /dev/null +++ b/src/server/monster_nihilanth.qc @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_nihilanth (0 0.8 0.8) (-192 -192 0) (192 192 384) + +HALF-LIFE (1998) ENTITY + +Nihilanth + +*/ + +enum +{ + NIL_IDLE, + NIL_ATTACK, + NIL_ATTACK2, + NIL_THROW, + NIL_BLOCK, + NIL_RECHARGE, + NIL_IDLEOPEN, + NIL_ATTACKOPEN, + NIL_ATTACKOPEN2, + NIL_FLINCH, + NIL_FLINCH2, + NIL_FALL, + NIL_DIE, + NIL_FORWARD, + NIL_BACK, + NIL_UP, + NIL_DOWN, + NIL_RIGHT, + NIL_LEFT, + NIL_WALK2, + NIL_SHOOT +}; + +class monster_nihilanth:CBaseMonster +{ + float m_flIdleTime; + + void(void) monster_nihilanth; + + virtual void(void) Death; + virtual void(void) Pain; + virtual void(void) IdleNoise; + virtual void(void) Respawn; +}; + +void +monster_nihilanth::IdleNoise(void) +{ + /* don't make noise if we're dead (corpse) */ + if (style == MONSTER_DEAD) { + return; + } + + if (m_flIdleTime > time) { + return; + } + + /* timing needs to adjusted as sounds conflict */ + m_flIdleTime = time + random(2,10); + + Sound_Play(this, CHAN_VOICE, "monster_nihilanth.idle"); +} + +void +monster_nihilanth::Pain(void) +{ + CBaseMonster::Pain(); + + if (m_flAnimTime > time) { + return; + } + + if (random() < 0.25f) { + return; + } + + Sound_Play(this, CHAN_VOICE, "monster_nihilanth.pain"); + + SetFrame((random() < 0.5) ? NIL_FLINCH : NIL_FLINCH2); + m_flAnimTime = time + 0.25f; +} + +void +monster_nihilanth::Death(void) +{ + /* if we're already dead (corpse) don't change animations */ + if (style != MONSTER_DEAD) { + SetFrame(NIL_DIE); + Sound_Play(this, CHAN_VOICE, "monster_nihilanth.die"); + } + + /* set the functional differences */ + CBaseMonster::Death(); +} + +void +monster_nihilanth::Respawn(void) +{ + CBaseMonster::Respawn(); + SetFrame(NIL_IDLE); +} + +void monster_nihilanth::monster_nihilanth(void) +{ + Sound_Precache("monster_nihilanth.attack"); + Sound_Precache("monster_nihilanth.attackball"); + Sound_Precache("monster_nihilanth.attackballmove"); + Sound_Precache("monster_nihilanth.die"); + Sound_Precache("monster_nihilanth.idle"); + Sound_Precache("monster_nihilanth.pain"); + Sound_Precache("monster_nihilanth.recharge"); + netname = "Nihilanth"; + model = "models/nihilanth.mdl"; + base_health = Skill_GetValue("nihilanth_health", 800); + base_mins = [-192,-192,-32]; + base_maxs = [192,192,384]; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_osprey.qc b/src/server/monster_osprey.qc new file mode 100644 index 0000000..cbc635a --- /dev/null +++ b/src/server/monster_osprey.qc @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_osprey (0 0.8 0.8) (-480 -480 -112) (480 480 24) + +HALF-LIFE (1998) ENTITY + +Bell Boeing VV-22 Osprey + +*/ + +class monster_osprey:CBaseMonster +{ + void(void) monster_osprey; + virtual void(void) Respawn; +}; + +void monster_osprey::Respawn(void) +{ + CBaseMonster::Respawn(); + takedamage = DAMAGE_NO; + iBleeds = FALSE; +} + +void monster_osprey::monster_osprey(void) +{ + netname = "Osprey"; + model = "models/osprey.mdl"; + base_mins = [-480,-480,-112]; + base_maxs = [480,480,24]; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_rat.qc b/src/server/monster_rat.qc new file mode 100644 index 0000000..57cee12 --- /dev/null +++ b/src/server/monster_rat.qc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_rat (0 0.8 0.8) (-6 -6 0) (6 6 6) + +HALF-LIFE (1998) ENTITY + +Rat + +*/ + +class monster_rat:CBaseMonster +{ + void(void) monster_rat; +}; + +void monster_rat::monster_rat(void) +{ + netname = "Rat"; + model = "models/bigrat.mdl"; + base_mins = [-6,-6,-0]; + base_maxs = [6,6,6]; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_scientist.qc b/src/server/monster_scientist.qc new file mode 100644 index 0000000..25d9ae2 --- /dev/null +++ b/src/server/monster_scientist.qc @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_scientist (0 0.8 0.8) (-16 -16 0) (16 16 72) + +HALF-LIFE (1998) ENTITY + +Scientist + +*/ + +enum +{ + SCIA_WALK, + SCIA_WALKSCARED, + SCIA_RUN, + SCIA_RUNSCARED, + SCIA_RUNLOOK, + SCIA_180LEFT, + SCIA_180RIGHT, + SCIA_FLINCH, + SCIA_PAIN, + SCIA_PAINLEFT, + SCIA_PAINRIGHT, + SCIA_PAINLEGLEFT, + SCIA_PAINLEGRIGHT, + SCIA_IDLE1, + SCIA_IDLE2, + SCIA_IDLE3, + SCIA_IDLE4, + SCIA_IDLE5, + SCIA_IDLE6, + SCIA_SCARED_END, + SCIA_SCARED1, + SCIA_SCARED2, + SCIA_SCARED3, + SCIA_SCARED4, + SCIA_PANIC, + SCIA_FEAR1, + SCIA_FEAR2, + SCIA_CRY, + SCIA_SCI1, + SCIA_SCI2, + SCIA_SCI3, + SCIA_DIE_SIMPLE, + SCIA_DIE_FORWARD1, + SCIA_DIE_FORWARD2, + SCIA_DIE_BACKWARD, + SCIA_DIE_HEADSHOT, + SCIA_DIE_GUTSHOT, + SCIA_LYING1, + SCIA_LYING2, + SCIA_DEADSIT, + SCIA_DEADTABLE1, + SCIA_DEADTABLE2, + SCIA_DEADTABLE3 +}; + +class monster_scientist:CBaseNPC +{ + void(void) monster_scientist; + + virtual void(void) Respawn; + virtual void(void) OnPlayerUse; + virtual void(void) Pain; + virtual void(void) Death; + virtual int(void) AnimIdle; + virtual int(void) AnimWalk; + virtual int(void) AnimRun; + virtual void(string, string) SpawnKey; +}; + +int +monster_scientist::AnimIdle(void) +{ + return SCIA_IDLE1; +} + +int +monster_scientist::AnimWalk(void) +{ + return SCIA_WALK; +} + +int +monster_scientist::AnimRun(void) +{ + return SCIA_RUN; +} + +void +monster_scientist::OnPlayerUse(void) +{ + if (spawnflags & MSF_PREDISASTER) { + Sentence("!SC_POK"); + return; + } + + CBaseNPC::OnPlayerUse(); +} + +void +monster_scientist::Pain(void) +{ + WarnAllies(); + + if (m_flAnimTime > time) { + return; + } + + if (random() < 0.25f) { + return; + } + + Sound_Speak(this, "monster_scientist.pain"); + + frame = SCIA_FLINCH + floor(random(0, 6)); + m_iFlags |= MONSTER_FEAR; + m_flAnimTime = time + 0.25f; +} + +void +monster_scientist::Death(void) +{ + WarnAllies(); + + if (style != MONSTER_DEAD) { + SetFrame(SCIA_DIE_SIMPLE + floor(random(0, 6))); + Sound_Speak(this, "monster_scientist.die"); + } + + /* now mark our state as 'dead' */ + CBaseNPC::Death(); +} + +void +monster_scientist::Respawn(void) +{ + CBaseNPC::Respawn(); + m_iFlags |= MONSTER_CANFOLLOW; + PlayerUse = OnPlayerUse; +} + +void +monster_scientist::SpawnKey(string strKey, string strValue) +{ + switch (strKey) { + case "body": + SetBody(stoi(strValue) + 1); + break; + default: + CBaseEntity::SpawnKey(strKey, strValue); + } +} + +void +monster_scientist::monster_scientist(void) +{ + Sound_Precache("monster_scientist.die"); + Sound_Precache("monster_scientist.pain"); + + if (spawnflags & MSF_PREDISASTER) { + m_talkAsk = ""; + m_talkPlayerAsk = "!SC_PQUEST"; + m_talkPlayerGreet = "!SC_PHELLO"; + m_talkPlayerIdle = "!SC_PIDLE"; + } else { + m_talkAsk = "!SC_QUESTION"; + m_talkPlayerAsk = "!SC_QUESTION"; + m_talkPlayerGreet = "!SC_HELLO"; + m_talkPlayerIdle = "!SC_PIDLE"; + } + + m_talkAnswer = "!SC_ANSWER"; + m_talkAllyShot = "!SC_PLFEAR"; + m_talkGreet = ""; + m_talkIdle = "!SC_IDLE"; + m_talkHearing = "!SC_HEAR"; + m_talkSmelling = "!SC_SMELL"; + m_talkStare = "!SC_STARE"; + m_talkSurvived = "!SC_WOUND"; + m_talkWounded = "!SC_MORTAL"; + + /* they seem to use predisaster lines regardless of disaster state */ + m_talkPlayerWounded1 = "!SC_CUREA"; + m_talkPlayerWounded2 = "!SC_CUREB"; + m_talkPlayerWounded3 = "!SC_CUREC"; + m_talkUnfollow = "!SC_WAIT"; + m_talkFollow = "!SC_OK"; + m_talkStopFollow = "!SC_STOP"; + m_iBody = -1; + + model = "models/scientist.mdl"; + base_mins = [-16,-16,0]; + base_maxs = [16,16,72]; + base_health = Skill_GetValue("scientist_health", 20); + + CBaseNPC::CBaseNPC(); + + /* has the body not been overriden, etc. choose a character for us */ + if (m_iBody == -1) { + SetBody((int)floor(random(1,5))); + } + + switch (m_iBody) { + case 1: + m_flPitch = 105; + netname = "Walter"; + break; + case 2: + m_flPitch = 100; + netname = "Einstein"; + break; + case 3: + m_flPitch = 95; + netname = "Luther"; + SetSkin(1); + break; + default: + m_flPitch = 100; + netname = "Slick"; + } +} diff --git a/src/server/monster_scientist_dead.qc b/src/server/monster_scientist_dead.qc new file mode 100644 index 0000000..8e82379 --- /dev/null +++ b/src/server/monster_scientist_dead.qc @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_scientist_dead (0 0.8 0.8) (-16 -16 0) (16 16 72) + +HALF-LIFE (1998) ENTITY + +Scientists' corpse + +*/ + +enum +{ + DSCIA_LYING1 = 37, + DSCIA_LYING2, + DSCIA_DEADSIT, + DSCIA_DEADTABLE1, + DSCIA_DEADTABLE2, + DSCIA_DEADTABLE3, + DSCIA_DEADHANG +}; + +class monster_scientist_dead:CBaseMonster +{ + int m_iPose; + void(void) monster_scientist_dead; + + virtual void(void) Hide; + virtual void(void) Respawn; + virtual void(void) Gib; + virtual void(string, string) SpawnKey; +}; + +void +monster_scientist_dead::Gib(void) +{ + takedamage = DAMAGE_NO; + FX_GibHuman(this.origin); + Hide(); +} + +void +monster_scientist_dead::Hide(void) +{ + SetModel(""); + solid = SOLID_NOT; + movetype = MOVETYPE_NONE; +} + +void +monster_scientist_dead::Respawn(void) +{ + v_angle[0] = Math_FixDelta(m_oldAngle[0]); + v_angle[1] = Math_FixDelta(m_oldAngle[1]); + v_angle[2] = Math_FixDelta(m_oldAngle[2]); + + SetOrigin(m_oldOrigin); + angles = v_angle; + solid = SOLID_CORPSE; + movetype = MOVETYPE_NONE; + SetModel(m_oldModel); + setsize(this, VEC_HULL_MIN + [0,0,36], VEC_HULL_MAX + [0,0,36]); + takedamage = DAMAGE_YES; + health = 0; + velocity = [0,0,0]; + iBleeds = TRUE; + SendFlags |= NPC_BODY; + + switch (m_iPose) { + case 1: + SetFrame(DSCIA_LYING2); + break; + case 2: + SetFrame(DSCIA_DEADSIT); + break; + case 3: + SetFrame(DSCIA_DEADHANG); + break; + case 4: + SetFrame(DSCIA_DEADTABLE1); + break; + case 5: + SetFrame(DSCIA_DEADTABLE2); + break; + case 6: + SetFrame(DSCIA_DEADTABLE3); + break; + default: + SetFrame(DSCIA_LYING1); + } + + droptofloor(); +} + +void +monster_scientist_dead::SpawnKey(string strKey, string strValue) +{ + switch (strKey) { + case "pose": + m_iPose = stoi(strValue); + break; + case "body": + SetBody(stoi(strValue) + 1); + break; + case "skin": + SetSkin(stoi(strValue)); + break; + default: + CBaseMonster::SpawnKey(strKey, strValue); + } +} + +void +monster_scientist_dead::monster_scientist_dead(void) +{ + model = "models/scientist.mdl"; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_sentry.qc b/src/server/monster_sentry.qc new file mode 100644 index 0000000..8925386 --- /dev/null +++ b/src/server/monster_sentry.qc @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_sentry (0 0.8 0.8) (-16 -16 0) (16 16 72) + +HALF-LIFE (1998) ENTITY + +Sentry Gun + +*/ + +enum +{ + SENT_IDLE, + SENT_FIRE, + SENT_SPIN, + SENT_DEPLOY, + SENT_RETIRE, + SENT_DIE +}; + + +class monster_sentry:CBaseMonster +{ + void(void) monster_sentry; + + virtual void(void) Death; + virtual void(void) Respawn; + +}; + +void +monster_sentry::Death(void) +{ + /* if we're already dead (corpse) don't change animations */ + if (style != MONSTER_DEAD) { + SetFrame(SENT_DIE); + Sound_Play(this, CHAN_VOICE, "monster_sentry.die"); + } + + /* set the functional differences */ + CBaseMonster::Death(); +} + +void +monster_sentry::Respawn(void) +{ + CBaseMonster::Respawn(); + SetFrame(SENT_IDLE); + iBleeds = FALSE; +} + +void monster_sentry::monster_sentry(void) +{ + Sound_Precache("monster_sentry.alert"); + Sound_Precache("monster_sentry.die"); + Sound_Precache("monster_sentry.idle"); + Sound_Precache("monster_sentry.retract"); + netname = "Sentry"; + model = "models/sentry.mdl"; + base_health = Skill_GetValue("sentry_health", 40); + base_mins = [-16,-16,0]; + base_maxs = [16,16,72]; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_sitting_scientist.qc b/src/server/monster_sitting_scientist.qc new file mode 100644 index 0000000..b4aff43 --- /dev/null +++ b/src/server/monster_sitting_scientist.qc @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_sitting_scientist (0 0.8 0.8) (-14 -14 0) (14 14 36) + +HALF-LIFE (1998) ENTITY + +Sitting scientists + +*/ + +enum +{ + DSCIA_LYING1 = 37, + DSCIA_LYING2, + DSCIA_DEADSIT, + DSCIA_DEADTABLE1, + DSCIA_DEADTABLE2, + DSCIA_DEADTABLE3, + DSCIA_DEADHANG +}; + +class monster_sitting_scientist:CBaseMonster +{ + int m_iPose; + void(void) monster_sitting_scientist; + + virtual void(void) Hide; + virtual void(void) Respawn; + virtual void(void) Death; + virtual void(void) Gib; + virtual void(string, string) SpawnKey; +}; + +void +monster_sitting_scientist::Gib(void) +{ + takedamage = DAMAGE_NO; + FX_GibHuman(this.origin); + Hide(); +} + +void +monster_sitting_scientist::Death(void) +{ + if (health < -50) { + Gib(); + return; + } +} + +void +monster_sitting_scientist::Hide(void) +{ + SetModel(""); + solid = SOLID_NOT; + movetype = MOVETYPE_NONE; +} + +void +monster_sitting_scientist::Respawn(void) +{ + v_angle[0] = Math_FixDelta(m_oldAngle[0]); + v_angle[1] = Math_FixDelta(m_oldAngle[1]); + v_angle[2] = Math_FixDelta(m_oldAngle[2]); + + SetOrigin(m_oldOrigin); + angles = v_angle; + solid = SOLID_BBOX; + movetype = MOVETYPE_NONE; + SetModel(m_oldModel); + setsize(this, [-14,-14,0],[14,14,36]); + takedamage = DAMAGE_YES; + health = 0; + velocity = [0,0,0]; + iBleeds = TRUE; + SendFlags |= NPC_BODY; + frame = 74; + droptofloor(); +} + +void +monster_sitting_scientist::SpawnKey(string strKey, string strValue) +{ + switch (strKey) { + case "pose": + m_iPose = stoi(strValue); + break; + case "body": + SetBody(stoi(strValue) + 1); + break; + case "skin": + SetSkin(stoi(strValue)); + break; + default: + CBaseMonster::SpawnKey(strKey, strValue); + } +} + +void +monster_sitting_scientist::monster_sitting_scientist(void) +{ + model = "models/scientist.mdl"; + CBaseMonster::CBaseMonster(); + + /* has the body not been overriden, etc. choose a character for us */ + if (m_iBody == -1) { + SetBody((int)floor(random(1,5))); + } + + switch (m_iBody) { + case 1: + m_flPitch = 105; + netname = "Walter"; + break; + case 2: + m_flPitch = 100; + netname = "Einstein"; + break; + case 3: + m_flPitch = 95; + netname = "Luther"; + SetSkin(1); + break; + default: + m_flPitch = 100; + netname = "Slick"; + } +} diff --git a/src/server/monster_tentacle.qc b/src/server/monster_tentacle.qc new file mode 100644 index 0000000..0afe7db --- /dev/null +++ b/src/server/monster_tentacle.qc @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_tentacle (0 0.8 0.8) (-32 -32 0) (32 32 64) + +HALF-LIFE (1998) ENTITY + +Tentacle + +*/ + +enum +{ + TE_IDLEPIT, + TE_RISE, + TE_TOFLOOR1, + TE_IDLE, + TE_PISSED, + TE_SMALLRISE, + TE_WAVE, + TE_STRIKE, + TE_TAP, + TE_ROTATE, + TE_REAR, + TE_REARIDLE, + TE_TOLEV1, + TE_IDELLEV1, + TE_FIDGETLEV1, + TE_SNAPLEV1, + TE_STRIKELEV1, + TE_TAPLEV1, + TE_ROTATELEV1, + TE_REARLEV1, + TE_REARIDELLEV1, + TE_TOLEV2, + TE_IDLELEV2, + TE_FIDGETLEV2, + TE_SNAPLEV2, + TE_SWINGLEV2, + TE_TUTLEV2, + TE_STRIKELEV2, + TE_TAPLEV2, + TE_ROTATELEV2, + TE_REARLEV2, + TE_FREAKDIE, + TE_REARIDLE2, + TE_TOLEV3, + TE_IDLELEV3, + TE_FIDGETLEV3, + TE_SIDELEV3, + TE_SWIPELEV3, + TE_STRIKELEV3, + TE_TAPLEV3, + TE_ROTATELEV3, + TE_REARLEV3, + TE_REARIDLELEV3, + TE_DOORLEV1, + TE_ENGINELEV3, + TE_ENGINEIDLE, + TE_ENGINESWAY, + TE_ENGINESWAT, + TE_ENGINEBOB, + TE_ENGINEDEATH, + TE_ENGINEDEATH2, + TE_ENGINEDEATH3, + TE_GRABIDLE, + TE_GRAB +}; + +class monster_tentacle:CBaseMonster +{ + float m_flIdleTime; + + void(void) monster_tentacle; + + virtual void(void) IdleNoise; + virtual void(void) Respawn; +}; + +void +monster_tentacle::IdleNoise(void) +{ + /* don't make noise if we're dead (corpse) */ + if (style == MONSTER_DEAD) { + return; + } + + if (m_flIdleTime > time) { + return; + } + /* timing needs to adjusted as sounds conflict */ + m_flIdleTime = time + random(2,10); + + Sound_Play(this, CHAN_VOICE, "monster_tentacle.idle"); +} + +void +monster_tentacle::Respawn(void) +{ + CBaseMonster::Respawn(); + /* not entirely true, takes damage then retreats and reheals */ + takedamage = DAMAGE_NO; + iBleeds = FALSE; + SetFrame(TE_IDLE); +} + +void +monster_tentacle::monster_tentacle(void) +{ + Sound_Precache("monster_tentacle.alert"); + Sound_Precache("monster_tentacle.attack"); + Sound_Precache("monster_tentacle.die"); + Sound_Precache("monster_tentacle.idle"); + netname = "Tentacle"; + model = "models/tentacle2.mdl"; + base_mins = [-32,-32,0]; + base_maxs = [32,32,64]; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_turret.qc b/src/server/monster_turret.qc new file mode 100644 index 0000000..026706a --- /dev/null +++ b/src/server/monster_turret.qc @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_turret (0 0.8 0.8) (-16 -16 0) (16 16 72) + +HALF-LIFE (1998) ENTITY + +Automatic Turret (large) + +*/ + +enum +{ + TUR_IDLE, + TUR_FIRE, + TUR_SPIN, + TUR_DEPLOY, + TUR_RETIRE, + TUR_DIE +}; + +class monster_turret:CBaseMonster +{ + void(void) monster_turret; +}; + +void monster_turret::monster_turret(void) +{ + netname = "Turret"; + model = "models/turret.mdl"; + base_mins = [-16,-16,0]; + base_maxs = [16,16,72]; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/monster_zombie.qc b/src/server/monster_zombie.qc new file mode 100644 index 0000000..9806531 --- /dev/null +++ b/src/server/monster_zombie.qc @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 monster_zombie (0 0.8 0.8) (-16 -16 0) (16 16 72) + +HALF-LIFE (1998) ENTITY + +Zombie + +*/ + +enum +{ + ZO_IDLE, + ZO_TURNLEFT, + ZO_TURNRIGHT, + ZO_FLINCHSM, + ZO_FLINCH, + ZO_FLINCHBIG, + ZO_RISE, + ZO_FALLING, + ZO_ATTACK1, + ZO_ATTACK2, + ZO_WALK, + ZO_FLINCHLA, + ZO_FLINCHRA, + ZO_FLINCHLEFT, + ZO_FLINCHRIGHT, + ZO_DIEHS, + ZO_DIEHS2, + ZO_DIE, + ZO_DIE2, + ZO_DIE3, + ZO_PAUSE, + ZO_WALLBUST, + ZO_WALLKICK, + ZO_WINDOWBUST, + ZO_SODA, + ZO_SLIDEIDLE, + ZO_SLIDE, + ZO_VENTIDLE, + ZO_VENT, + ZO_DEADIDLE, + ZO_DEAD, + ZO_FREAKDIE, + ZO_FREAK, + ZO_EATTABLE, + ZO_EAT, + ZO_EATSTAND, + ZO_DOORIP, + ZO_PULLSCI, + ZO_EAT2, + ZO_EAT2STAND, + ZO_VENT2IDLE, + ZO_VENT2, + ZO_HAUL, + ZO_RISESNACK +}; + +class monster_zombie:CBaseMonster +{ + float m_flIdleTime; + + void(void) monster_zombie; + + virtual void(void) Pain; + virtual void(void) Death; + virtual void(void) IdleNoise; + virtual void(void) Respawn; + + virtual int(void) AnimIdle; + virtual int(void) AnimWalk; + virtual int(void) AnimRun; + + virtual int(void) AttackMelee; + virtual void(void) AttackFlail; +}; + +int +monster_zombie::AnimIdle(void) +{ + return ZO_IDLE; +} + +int +monster_zombie::AnimWalk(void) +{ + return ZO_WALK; +} + +int +monster_zombie::AnimRun(void) +{ + return ZO_WALK; +} + +int +monster_zombie::AttackMelee(void) +{ + /* visual */ + if (random() < 0.5) + AnimPlay(ZO_ATTACK1); + else + AnimPlay(ZO_ATTACK2); + + m_flAttackThink = m_flAnimTime; + Sound_Play(this, CHAN_VOICE, "monster_zombie.attack"); + + /* functional */ + think = AttackFlail; + nextthink = 0.25f; + return TRUE; +} + +void +monster_zombie::AttackFlail(void) +{ + traceline(origin, m_eEnemy.origin, FALSE, this); + + if (trace_fraction >= 1.0 || trace_ent.takedamage != DAMAGE_YES) { + Sound_Play(this, CHAN_WEAPON, "monster_zombie.attackmiss"); + return; + } + + Damage_Apply(trace_ent, this, 25, 0, 0); + Sound_Play(this, CHAN_WEAPON, "monster_zombie.attackhit"); +} + +void +monster_zombie::Pain(void) +{ + CBaseMonster::Pain(); + + if (m_flAnimTime > time) { + return; + } + + if (random() < 0.25f) { + return; + } + + Sound_Play(this, CHAN_VOICE, "monster_zombie.pain"); + SetFrame(ZO_FLINCH + floor(random(0, 2))); + m_flAnimTime = time + 0.25f; +} + +void +monster_zombie::Death(void) +{ + /* if we're already dead (corpse) don't change animations */ + if (style != MONSTER_DEAD) { + /* headshots == different animation */ + if (g_dmg_iHitBody == BODY_HEAD) { + if (random() < 0.5) { + SetFrame(ZO_DIEHS); + } else { + SetFrame(ZO_DIEHS2); + } + } else { + SetFrame(ZO_DIE + floor(random(0, 3))); + } + + Sound_Play(this, CHAN_VOICE, "monster_zombie.pain"); + } + + /* set the functional differences */ + CBaseMonster::Death(); +} + +void +monster_zombie::IdleNoise(void) +{ + /* don't make noise if we're dead (corpse) */ + if (style == MONSTER_DEAD) { + return; + } + + if (m_flIdleTime > time) { + return; + } + m_flIdleTime = time + random(2,10); + + Sound_Play(this, CHAN_VOICE, "monster_zombie.idle"); +} + +void +monster_zombie::Respawn(void) +{ + CBaseMonster::Respawn(); + SetFrame(ZO_IDLE); +} + +void +monster_zombie::monster_zombie(void) +{ + Sound_Precache("monster_zombie.alert"); + Sound_Precache("monster_zombie.attack"); + Sound_Precache("monster_zombie.attackhit"); + Sound_Precache("monster_zombie.attackmiss"); + Sound_Precache("monster_zombie.idle"); + Sound_Precache("monster_zombie.pain"); + netname = "Zombie"; + model = "models/zombie.mdl"; + base_health = Skill_GetValue("zombie_health", 50); + base_mins = [-16,-16,0]; + base_maxs = [16,16,72]; + m_iAlliance = MAL_ALIEN; + CBaseMonster::CBaseMonster(); +} diff --git a/src/server/player.qc b/src/server/player.qc new file mode 100644 index 0000000..2b8b170 --- /dev/null +++ b/src/server/player.qc @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +/* +==================== +UseWorkaround +==================== +*/ +void UseWorkaround(entity eTarget) +{ + eActivator = self; + entity eOldSelf = self; + self = eTarget; + self.PlayerUse(); + self = eOldSelf; +} + +/* +==================== +Player_UseDown +==================== +*/ +void Player_UseDown(void) +{ + vector vecSrc; + + if (self.health <= 0) { + return; + } else if (!(self.flags & FL_USE_RELEASED)) { + return; + } + + makevectors(self.v_angle); + vecSrc = self.origin + self.view_ofs; + + self.hitcontentsmaski = CONTENTBITS_POINTSOLID; + traceline(vecSrc, vecSrc + (v_forward * 64), MOVE_HITMODEL, self); + + + if (trace_ent.PlayerUse) { + self.flags &= ~FL_USE_RELEASED; + + UseWorkaround(trace_ent); + + /* Some entities want to support Use spamming */ + if (!(self.flags & FL_USE_RELEASED)) { + sound(self, CHAN_ITEM, "common/wpn_select.wav", 0.25, ATTN_IDLE); + } + } else { + sound(self, CHAN_ITEM, "common/wpn_denyselect.wav", 0.25, ATTN_IDLE); + self.flags &= ~FL_USE_RELEASED; + } +} + +/* +==================== +Player_UseUp +==================== +*/ +void Player_UseUp(void) { + if (!(self.flags & FL_USE_RELEASED)) { + self.flags |= FL_USE_RELEASED; + } +} + +void Weapons_Draw(void); +void CSEv_PlayerSwitchWeapon_i(int w) +{ + player pl = (player)self; + pl.activeweapon = w; + Weapons_Draw(); +} + +void +Player_Precache(void) +{ + searchhandle pm; + pm = search_begin("models/player/*/*.mdl", TRUE, TRUE); + for (int i = 0; i < search_getsize(pm); i++) { + precache_model(search_getfilename(pm, i)); + } + search_end(pm); +} diff --git a/src/server/progs.src b/src/server/progs.src new file mode 100755 index 0000000..a3d7cb8 --- /dev/null +++ b/src/server/progs.src @@ -0,0 +1,89 @@ +#pragma target fte +#pragma progs_dat "../../progs.dat" + +#define QWSSQC +#define SERVER +#define VALVE +#define GS_RENDERFX + +#includelist +../../../src/shared/fteextensions.qc +../../../src/gs-entbase/server/defs.h +../../../src/shared/defs.h +../../../src/server/defs.h + +../../../src/gs-entbase/server.src +../../../src/gs-entbase/shared.src +../shared/include.src + +defs.h + +monster_apache.qc +monster_alien_controller.qc +monster_alien_grunt.qc +monster_alien_slave.qc +monster_barnacle.qc +monster_barney.qc +monster_barney_dead.qc +monster_bigmomma.qc +monster_bloater.qc +monster_bullchicken.qc +monster_cockroach.qc +monster_flyer_flock.qc +monster_gargantua.qc +monster_gman.qc +monster_headcrab.qc +monster_babycrab.qc +monster_hevsuit_dead.qc +monster_houndeye.qc +monster_human_grunt.qc +monster_hgrunt_dead.qc +monster_human_assassin.qc +monster_ichthyosaur.qc +monster_leech.qc +monster_miniturret.qc +monster_nihilanth.qc +monster_osprey.qc +monster_rat.qc +monster_scientist_dead.qc +monster_sitting_scientist.qc +monster_scientist.qc +monster_sentry.qc +monster_tentacle.qc +monster_turret.qc +monster_zombie.qc + +player.qc +spectator.qc +items.qc +item_longjump.qc +item_suit.qc +item_healthkit.qc +item_battery.qc +item_weaponbox.qc +world_items.qc +xen_spore_small.qc +xen_spore_medium.qc +xen_spore_large.qc +xen_hair.qc +xen_plantlight.qc +ammo.qc + +../../../src/botlib/include.src + +gamerules.qc +gamerules_singleplayer.qc +gamerules_multiplayer.qc +client.qc +server.qc +damage.qc +rules.qc +flashlight.qc +../../../base/src/server/modelevent.qc + +input.qc +spawn.qc + +../../../src/server/include.src +../../../src/shared/include.src +#endlist diff --git a/src/server/rules.qc b/src/server/rules.qc new file mode 100644 index 0000000..e6b76f0 --- /dev/null +++ b/src/server/rules.qc @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +int Rules_IsTeamPlay(void) +{ + return cvar("teamplay"); +} diff --git a/src/server/server.qc b/src/server/server.qc new file mode 100644 index 0000000..d923dc0 --- /dev/null +++ b/src/server/server.qc @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +void +Game_InitRules(void) +{ + if (cvar("sv_playerslots") == 1 || cvar("coop") == 1) { + g_grMode = spawn(HLSingleplayerRules); + } else { + g_grMode = spawn(HLMultiplayerRules); + } +} + +void +Game_Worldspawn(void) +{ + Sound_Precache("ammo.pickup"); + Sound_Precache("ammo.respawn"); + Sound_Precache("player.die"); + Sound_Precache("player.fall"); + Sound_Precache("player.lightfall"); + + precache_model("models/player.mdl"); + precache_model("models/w_weaponbox.mdl"); + Weapons_Init(); + Player_Precache(); +} diff --git a/src/server/spawn.qc b/src/server/spawn.qc new file mode 100644 index 0000000..b60cc9e --- /dev/null +++ b/src/server/spawn.qc @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +void info_player_start(void) +{ + self.solid = SOLID_TRIGGER; + setsize(self, VEC_HULL_MIN, VEC_HULL_MAX); + self.botinfo = BOTINFO_SPAWNPOINT; +} + +void info_player_deathmatch(void) +{ + self.solid = SOLID_TRIGGER; + setsize(self, VEC_HULL_MIN, VEC_HULL_MAX); + self.botinfo = BOTINFO_SPAWNPOINT; +} + +void info_player_team1(void) +{ + self.classname = "info_player_deathmatch"; + self.botinfo = BOTINFO_SPAWNPOINT; +} + +void info_player_team2(void) +{ + self.classname = "info_player_deathmatch"; + self.botinfo = BOTINFO_SPAWNPOINT; +} diff --git a/src/server/spectator.qc b/src/server/spectator.qc new file mode 100644 index 0000000..01ce53f --- /dev/null +++ b/src/server/spectator.qc @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +void Game_SpectatorThink(void) +{ + +} +void Game_SpectatorConnect(void) +{ + +} +void Game_SpectatorDisconnect(void) +{ + +} diff --git a/src/server/world_items.qc b/src/server/world_items.qc new file mode 100644 index 0000000..9efa058 --- /dev/null +++ b/src/server/world_items.qc @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +/* This is one of those leftovers from trying to get a game out in time */ +class world_items:CBaseTrigger +{ + void(void) world_items; +}; + +void world_items::world_items(void) +{ + int nfields = tokenize(__fullspawndata); + for (int i = 1; i < (nfields - 1); i += 2) { + switch (argv(i)) { + case "type": + float type = stof(argv(i+1)); + switch (type) { + case 44: + spawnfunc_item_battery(); + break; + case 45: + spawnfunc_item_suit(); + break; + default: + } + default: + break; + } + } +} diff --git a/src/server/xen_hair.qc b/src/server/xen_hair.qc new file mode 100644 index 0000000..eb1afe6 --- /dev/null +++ b/src/server/xen_hair.qc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 xen_hair (0 0.8 0.8) (-16 -16 0) (16 16 72) + +HALF-LIFE (1998) ENTITY + +Xen Hair + +*/ +class xen_hair:CBaseEntity +{ + void(void) xen_hair; +}; + +void xen_hair::xen_hair(void) +{ + CBaseEntity::CBaseEntity(); + precache_model("models/hair.mdl"); + solid = SOLID_SLIDEBOX; + movetype = MOVETYPE_WALK; + SetModel("models/hair.mdl"); + SetOrigin(origin); +} diff --git a/src/server/xen_plantlight.qc b/src/server/xen_plantlight.qc new file mode 100644 index 0000000..17a0118 --- /dev/null +++ b/src/server/xen_plantlight.qc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 xen_plantlight (0 0.8 0.8) (-16 -16 0) (16 16 72) + +HALF-LIFE (1998) ENTITY + +Xen Plant Light + +*/ +class xen_plantlight:CBaseEntity +{ + void(void) xen_plantlight; +}; + +void xen_plantlight::xen_plantlight(void) +{ + CBaseEntity::CBaseEntity(); + precache_model("models/light.mdl"); + solid = SOLID_SLIDEBOX; + movetype = MOVETYPE_WALK; + SetModel("models/light.mdl"); + SetOrigin(origin); +} diff --git a/src/server/xen_spore_large.qc b/src/server/xen_spore_large.qc new file mode 100644 index 0000000..be5bdec --- /dev/null +++ b/src/server/xen_spore_large.qc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 xen_spore_large (0 0.8 0.8) (-16 -16 0) (16 16 72) + +HALF-LIFE (1998) ENTITY + +Large Xen Spore + +*/ +class xen_spore_large:CBaseEntity +{ + void(void) xen_spore_large; +}; + +void xen_spore_large::xen_spore_large(void) +{ + CBaseEntity::CBaseEntity(); + precache_model("models/fungus(large).mdl"); + solid = SOLID_SLIDEBOX; + movetype = MOVETYPE_WALK; + SetModel("models/fungus(large).mdl"); + SetOrigin(origin); +} diff --git a/src/server/xen_spore_medium.qc b/src/server/xen_spore_medium.qc new file mode 100644 index 0000000..41ab34a --- /dev/null +++ b/src/server/xen_spore_medium.qc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 xen_spore_medium (0 0.8 0.8) (-16 -16 0) (16 16 72) + +HALF-LIFE (1998) ENTITY + +Medium Xen Spore + +*/ +class xen_spore_medium:CBaseEntity +{ + void(void) xen_spore_medium; +}; + +void xen_spore_medium::xen_spore_medium(void) +{ + CBaseEntity::CBaseEntity(); + precache_model("models/fungus.mdl"); + solid = SOLID_SLIDEBOX; + movetype = MOVETYPE_WALK; + SetModel("models/fungus.mdl"); + SetOrigin(origin); +} diff --git a/src/server/xen_spore_small.qc b/src/server/xen_spore_small.qc new file mode 100644 index 0000000..3698282 --- /dev/null +++ b/src/server/xen_spore_small.qc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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 xen_spore_small (0 0.8 0.8) (-16 -16 0) (16 16 72) + +HALF-LIFE (1998) ENTITY + +Small Xen Spore + +*/ +class xen_spore_small:CBaseEntity +{ + void(void) xen_spore_small; +}; + +void xen_spore_small::xen_spore_small(void) +{ + CBaseEntity::CBaseEntity(); + precache_model("models/fungus(small).mdl"); + solid = SOLID_SLIDEBOX; + movetype = MOVETYPE_WALK; + SetModel("models/fungus(small).mdl"); + SetOrigin(origin); +} diff --git a/src/shared/animations.h b/src/shared/animations.h new file mode 100644 index 0000000..d7b9812 --- /dev/null +++ b/src/shared/animations.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +enum +{ + ANIM_LOOKIDLE, + ANIM_IDLE, + ANIM_DEEPIDLE, + ANIM_RUN2, + ANIM_WALK2HANDED, + ANIM_2HANDSHOT, + ANIM_CRAWL, + ANIM_CROUCHIDLE, + ANIM_JUMP, + ANIM_LONGJUMP, + ANIM_SWIM, + ANIM_TREADWATER, + ANIM_RUN, + ANIM_WALK, + ANIM_AIM2, + ANIM_SHOOT2, + ANIM_AIM1, + ANIM_SHOOT1, + ANIM_DIESIMPLE, + ANIM_DIEBACKWARDS1, + ANIM_DIEBACKWARDS2, + ANIM_DIEFORWARD, + ANIM_DIEHEADSHOT, + ANIM_DIESPIN, + ANIM_DIEGUTSHOT, + ANIM_AIMCROWBAR, + ANIM_SHOOTCROWBAR, + ANIM_CR_AIMCROWBAR, + ANIM_CR_SHOOTCROWBAR, + ANIM_AIMTRIPMINE, + ANIM_SHOOTTRIPMINE, + ANIM_CR_AIMTRIPMINE, + ANIM_CR_SHOOTTRIPMINE, + ANIM_AIM1HAND, + ANIM_SHOOT1HAND, + ANIM_CR_AIM1HAND, + ANIM_CR_SHOOT1HAND, + ANIM_AIMPYTHON, + ANIM_SHOOTPYTHON, + ANIM_CR_AIMPYTHON, + ANIM_CR_SHOOTPYTHON, + ANIM_AIMSHOTGUN, + ANIM_SHOOTSHOTGUN, + ANIM_CR_AIMSHOTGUN, + ANIM_CR_SHOOTSHOTGUN, + ANIM_AIMGAUSS, + ANIM_SHOOTGAUSS, + ANIM_CR_AIMGAUSS, + ANIM_CR_SHOOTGAUSS, + ANIM_AIMMP5, + ANIM_SHOOTMP5, + ANIM_CR_AIMMP5, + ANIM_CR_SHOOTMP5, + ANIM_AIMRPG, + ANIM_SHOOTRPG, + ANIM_CR_AIMRPG, + ANIM_CR_SHOOTRPG, + ANIM_AIMEGON, + ANIM_SHOOTEGON, + ANIM_CR_AIMEGON, + ANIM_CR_SHOOTEGON, + ANIM_AIMSQUEAK, + ANIM_SHOOTSQUEAK, + ANIM_CR_AIMSQUEAK, + ANIM_CR_SHOOTSQUEAK, + ANIM_AIMHIVE, + ANIM_SHOOTHIVE, + ANIM_CR_AIMHIVE, + ANIM_CR_SHOOTHIVE, + ANIM_AIMBOW, + ANIM_SHOOTBOW, + ANIM_CR_AIMBOW, + ANIM_CR_SHOOTBOW +}; + +void Animation_PlayerTop(float); +void Animation_PlayerTopTemp(float, float); diff --git a/src/shared/animations.qc b/src/shared/animations.qc new file mode 100755 index 0000000..59dd1c5 --- /dev/null +++ b/src/shared/animations.qc @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +.float baselerpfrac; +.float lerpfrac; +.float frame_time; +.float frame_old; +.float fWasCrouching; +.float frame2time; +.float frame2; +.float baseframe2time; +.float baseframe1time; +.float baseframe2; + +// For lerping, sigh +.float frame_last; +.float baseframe_last; +.float subblendfrac; +.float subblend2frac; + +void Animation_Print(string sWow) { +#ifdef CLIENT + print(sprintf("[DEBUG] %s", sWow)); +#else + bprint(PRINT_HIGH, sprintf("SSQC: %s", sWow) ); +#endif +} + +var int autocvar_bone_spinebone = 0; +var int autocvar_bone_baseframe = 0; +var int autocvar_bone_frame = 0; +/* +================= +Animation_PlayerUpdate + +Called every frame to update the animation sequences +depending on what the player is doing +================= +*/ +void +Animation_PlayerUpdate(void) +{ + self.basebone = gettagindex(self, "Bip01 Spine1"); + + // TODO: Make this faster + if (self.frame_time < time) { + player pl = (player)self; + self.frame = Weapons_GetAim(pl.activeweapon); + self.frame_old = self.frame; + } + + /* in order to appear jumping, we want to not be on ground, + * but also make sure we're not just going down a ramp */ + if (!(self.flags & FL_ONGROUND) && (self.velocity[2] > 0 || self.baseframe == ANIM_JUMP)) { + self.baseframe = ANIM_JUMP; + } else if (vlen(self.velocity) == 0) { + if (self.flags & FL_CROUCHING) { + self.baseframe = ANIM_CROUCHIDLE; + } else { + self.baseframe = ANIM_IDLE; + } + } else if (vlen(self.velocity) < 150) { + if (self.flags & FL_CROUCHING) { + self.baseframe = ANIM_CRAWL; + } else { + self.baseframe = ANIM_WALK; + } + } else if (vlen(self.velocity) > 150) { + if (self.flags & FL_CROUCHING) { + self.baseframe = ANIM_CRAWL; + } else { + self.baseframe = ANIM_RUN; + } + } + + // Lerp it down! + if (self.lerpfrac > 0) { + self.lerpfrac -= frametime * 5; + if (self.lerpfrac < 0) { + self.lerpfrac = 0; + } + } + + if (self.baselerpfrac > 0) { + self.baselerpfrac -= frametime * 5; + if (self.baselerpfrac < 0) { + self.baselerpfrac = 0; + } + } + + if (self.frame != self.frame_last) { + //Animation_Print(sprintf("New Frame: %d, Last Frame: %d\n", self.frame, self.frame_last)); + + // Move everything over to frame 2 + self.frame2time = self.frame1time; + self.frame2 = self.frame_last; + + // Set frame_last to avoid this being called again + self.frame_last = self.frame; + + self.lerpfrac = 1.0f; + self.frame1time = 0.0f; + } + + if (self.baseframe != self.baseframe_last) { + //Animation_Print(sprintf("New Baseframe: %d, Last Baseframe: %d\n", self.baseframe, self.baseframe_last)); + + // Move everything over to frame 2 + self.baseframe2time = self.baseframe1time; + self.baseframe2 = self.baseframe_last; + + // Set frame_last to avoid this being called again + self.baseframe_last = self.baseframe; + + self.baselerpfrac = 1.0f; + self.baseframe1time = 0.0f; + } + + self.subblend2frac = self.angles[0]; + + self.angles[0] = self.angles[2] = 0; + + if (!(self.flags & FL_ONGROUND)) { + /*self.frame = ANIM_JUMP;*/ + } + + // Force the code above to update if we switched positions + if (self.fWasCrouching != (self.flags & FL_CROUCHING)) { + self.frame_old = 0; + self.frame_time = 0; + self.fWasCrouching = (self.flags & FL_CROUCHING); + } + +#ifndef CLIENT + // On the CSQC it's done in Player.c + self.subblendfrac = + self.subblend2frac = self.v_angle[0] / 90; +#endif +} + +/* +================= +Animation_PlayerTop + +Changes the animation sequence for the upper body part +================= +*/ +void +Animation_PlayerTop(float fFrame) +{ +#ifndef CLIENT + self.frame = fFrame; + self.frame_old = fFrame; +#endif +} + +void +Animation_PlayerTopTemp(float fFrame, float fTime) +{ +#ifndef CLIENT + self.frame = fFrame; + self.frame_time = time + fTime; + self.SendFlags |= PLAYER_FRAME; +#endif +} diff --git a/src/shared/entities.h b/src/shared/entities.h new file mode 100644 index 0000000..9d2beb5 --- /dev/null +++ b/src/shared/entities.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +enum +{ + ENT_TRIPMINE = ENT_SEPARATOR +}; diff --git a/src/shared/flags.h b/src/shared/flags.h new file mode 100644 index 0000000..87c3e9a --- /dev/null +++ b/src/shared/flags.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +/* game flags */ +#define GF_SEMI_TOGGLED (1<<0) +#define GF_FLASHLIGHT (1<<1) +#define GF_UNUSED3 (1<<2) +#define GF_UNUSED4 (1<<3) +#define GF_UNUSED5 (1<<4) +#define GF_UNUSED6 (1<<5) +#define GF_UNUSED7 (1<<6) +#define GF_UNUSED8 (1<<7) +#define GF_UNUSED9 (1<<8) +#define GF_UNUSED10 (1<<9) +#define GF_UNUSED11 (1<<10) +#define GF_UNUSED12 (1<<11) +#define GF_UNUSED13 (1<<12) +#define GF_UNUSED14 (1<<14) +#define GF_UNUSED15 (1<<16) +#define GF_UNUSED16 (1<<13) +#define GF_UNUSED17 (1<<17) +#define GF_UNUSED18 (1<<18) +#define GF_UNUSED19 (1<<19) +#define GF_UNUSED20 (1<<20) +#define GF_UNUSED21 (1<<21) +#define GF_UNUSED22 (1<<22) +#define GF_UNUSED23 (1<<23) diff --git a/src/shared/fx_blood.qc b/src/shared/fx_blood.qc new file mode 100644 index 0000000..fe0a149 --- /dev/null +++ b/src/shared/fx_blood.qc @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +#ifdef CLIENT +var float PARTICLE_BLOOD; +var int DECAL_BLOOD; + +void +FX_Blood_Init(void) +{ + precache_model("sprites/bloodspray.spr"); + precache_model("sprites/blood.spr"); + PARTICLE_BLOOD = particleeffectnum("part_blood"); + DECAL_BLOOD = particleeffectnum("decal_blood.effect"); +} +#endif + +void +FX_Blood(vector pos, vector color) +{ +#ifdef SERVER + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EV_BLOOD); + WriteCoord(MSG_MULTICAST, pos[0]); + WriteCoord(MSG_MULTICAST, pos[1]); + WriteCoord(MSG_MULTICAST, pos[2]); + WriteByte(MSG_MULTICAST, color[0] * 255); + WriteByte(MSG_MULTICAST, color[1] * 255); + WriteByte(MSG_MULTICAST, color[2] * 255); + msg_entity = self; + multicast(pos, MULTICAST_PVS); +#else + static void Blood_Touch(void) + { + if (serverkeyfloat("*bspversion") == BSPVER_HL) + Decals_Place(self.origin, sprintf("{blood%d", floor(random(1,9)))); + else { + decal_pickwall(self, self.origin); + pointparticles(DECAL_BLOOD, g_tracedDecal.endpos, g_tracedDecal.normal, 1); + } + } + + if (cvar("violence_hblood") <= 0) { + return; + } + + env_sprite eBlood = spawn(env_sprite); + setorigin(eBlood, pos); + setmodel(eBlood, "sprites/bloodspray.spr"); + + eBlood.drawmask = MASK_ENGINE; + eBlood.maxframe = modelframecount(eBlood.modelindex); + eBlood.loops = 0; + eBlood.scale = 1.0f; + +#ifdef GS_RENDERFX + eBlood.m_vecRenderColor = color; +#else + eBlood.colormod = color; +#endif + + eBlood.framerate = 20; + eBlood.nextthink = time + 0.05f; + + for (int i = 0; i < 3; i++) { + env_sprite ePart = spawn(env_sprite); + setorigin(ePart, pos); + setmodel(ePart, "sprites/blood.spr"); + ePart.movetype = MOVETYPE_BOUNCE; + ePart.gravity = 0.5f; + ePart.scale = 0.5f; + ePart.drawmask = MASK_ENGINE; + ePart.maxframe = modelframecount(ePart.modelindex); + ePart.loops = 0; + +#ifdef GS_RENDERFX + ePart.m_vecRenderColor = color; +#else + ePart.colormod = color; +#endif + ePart.framerate = 15; + ePart.nextthink = time + 0.1f; + ePart.velocity = randomvec() * 64; + ePart.touch = Blood_Touch; + ePart.solid = SOLID_BBOX; + /* ignore player physics */ + ePart.dimension_solid = 1; + ePart.dimension_hit = 1; + setsize(ePart, [0,0,0], [0,0,0]); + } +#endif +} diff --git a/src/shared/fx_breakmodel.qc b/src/shared/fx_breakmodel.qc new file mode 100644 index 0000000..4f80f19 --- /dev/null +++ b/src/shared/fx_breakmodel.qc @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +#ifdef CLIENT +void +FX_BreakModel_Init(void) +{ + precache_model("models/glassgibs.mdl"); + precache_model("models/woodgibs.mdl"); + precache_model("models/metalplategibs.mdl"); + precache_model("models/fleshgibs.mdl"); + precache_model("models/ceilinggibs.mdl"); + precache_model("models/computergibs.mdl"); + precache_model("models/rockgibs.mdl"); + precache_model("models/cindergibs.mdl"); + precache_sound("debris/bustglass1.wav"); + precache_sound("debris/bustglass2.wav"); + precache_sound("debris/bustglass3.wav"); + precache_sound("debris/bustcrate1.wav"); + precache_sound("debris/bustcrate2.wav"); + precache_sound("debris/bustcrate3.wav"); + precache_sound("debris/bustmetal1.wav"); + precache_sound("debris/bustmetal2.wav"); + precache_sound("debris/bustflesh1.wav"); + precache_sound("debris/bustflesh2.wav"); + precache_sound("debris/bustconcrete1.wav"); + precache_sound("debris/bustconcrete2.wav"); + precache_sound("debris/bustceiling.wav"); +} +#endif + +void +FX_BreakModel(int count, vector vMins, vector vMaxs, vector vVel, float fStyle) +{ +#ifdef SERVER + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EV_MODELGIB); + WriteCoord(MSG_MULTICAST, vMins[0]); + WriteCoord(MSG_MULTICAST, vMins[1]); + WriteCoord(MSG_MULTICAST, vMins[2]); + WriteCoord(MSG_MULTICAST, vMaxs[0]); + WriteCoord(MSG_MULTICAST, vMaxs[1]); + WriteCoord(MSG_MULTICAST, vMaxs[2]); + WriteByte(MSG_MULTICAST, fStyle); + WriteByte(MSG_MULTICAST, count); + + msg_entity = self; + + vector vWorldPos; + vWorldPos[0] = vMins[0] + (0.5 * (vMaxs[0] - vMins[0])); + vWorldPos[1] = vMins[1] + (0.5 * (vMaxs[1] - vMins[1])); + vWorldPos[2] = vMins[2] + (0.5 * (vMaxs[2] - vMins[2])); + multicast(vWorldPos, MULTICAST_PVS); +#else + static void FX_BreakModel_Remove(void) { remove(self) ; } + + float fModelCount = 0; + vector vecPos; + string sModel = ""; + + switch (fStyle) { + case GSMATERIAL_GLASS: + case GSMATERIAL_GLASS_UNBREAKABLE: + sModel = "models/glassgibs.mdl"; + fModelCount = 8; + break; + case GSMATERIAL_WOOD: + sModel = "models/woodgibs.mdl"; + fModelCount = 3; + break; + case GSMATERIAL_METAL: + sModel = "models/metalplategibs.mdl"; + fModelCount = 13; + break; + case GSMATERIAL_FLESH: + sModel = "models/fleshgibs.mdl"; + fModelCount = 4; + break; + case GSMATERIAL_TILE: + sModel = "models/ceilinggibs.mdl"; + fModelCount = 4; + break; + case GSMATERIAL_COMPUTER: + sModel = "models/computergibs.mdl"; + fModelCount = 15; + break; + case GSMATERIAL_ROCK: + sModel = "models/rockgibs.mdl"; + fModelCount = 3; + break; + default: + case GSMATERIAL_CINDER: + sModel = "models/cindergibs.mdl"; + fModelCount = 9; + break; + } + + vector vWorldPos; + vWorldPos = vMins + (0.5 * (vMaxs - vMins)); + + switch (fStyle) { + case GSMATERIAL_GLASS: + pointsound(vWorldPos, sprintf("debris/bustglass%d.wav", random(1, 4)), 1.0f, ATTN_NORM); + break; + case GSMATERIAL_WOOD: + pointsound(vWorldPos, sprintf("debris/bustcrate%d.wav", random(1, 4)), 1.0f, ATTN_NORM); + break; + case GSMATERIAL_METAL: + case GSMATERIAL_COMPUTER: + pointsound(vWorldPos, sprintf("debris/bustmetal%d.wav", random(1, 3)), 1.0f, ATTN_NORM); + break; + case GSMATERIAL_FLESH: + pointsound(vWorldPos, sprintf("debris/bustflesh%d.wav", random(1, 3)), 1.0f, ATTN_NORM); + break; + case GSMATERIAL_CINDER: + case GSMATERIAL_ROCK: + pointsound(vWorldPos, sprintf("debris/bustconcrete%d.wav", random(1, 4)), 1.0f, ATTN_NORM); + break; + case GSMATERIAL_TILE: + pointsound(vWorldPos, "debris/bustceiling.wav", 1.0f, ATTN_NORM); + break; + } + + for (int i = 0; i < count; i++) { + entity eGib = spawn(); + eGib.classname = "gib"; + + vecPos[0] = vMins[0] + (random() * (vMaxs[0] - vMins[0])); + vecPos[1] = vMins[1] + (random() * (vMaxs[1] - vMins[1])); + vecPos[2] = vMins[2] + (random() * (vMaxs[2] - vMins[2])); + + setorigin(eGib, vecPos); + setmodel(eGib, sModel); + setcustomskin(eGib, "", sprintf("geomset 0 %f\n", random(1, fModelCount + 1))); + eGib.movetype = MOVETYPE_BOUNCE; + eGib.solid = SOLID_NOT; + + eGib.avelocity[0] = random()*600; + eGib.avelocity[1] = random()*600; + eGib.avelocity[2] = random()*600; + eGib.think = FX_BreakModel_Remove; + eGib.nextthink = time + 10; + + if ((fStyle == GSMATERIAL_GLASS) || (fStyle == GSMATERIAL_GLASS_UNBREAKABLE)) { + eGib.alpha = 0.5f; + } + + eGib.drawmask = MASK_ENGINE; + } +#endif +} diff --git a/src/shared/fx_explosion.qc b/src/shared/fx_explosion.qc new file mode 100755 index 0000000..17df90f --- /dev/null +++ b/src/shared/fx_explosion.qc @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +#ifdef CLIENT +var int FX_EXPLOSION_MAIN; +var int FX_EXPLOSION_BS; + +void +FX_Explosion_Init(void) +{ + Sound_Precache("fx.explosion"); + precache_model("sprites/fexplo.spr"); + FX_EXPLOSION_MAIN = particleeffectnum("fx_explosion.main"); + FX_EXPLOSION_BS = particleeffectnum("fx_explosion.blacksmoke"); +} +#endif + +void +FX_Explosion(vector vecPos) +{ +#ifdef SERVER + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EV_EXPLOSION); + WriteCoord(MSG_MULTICAST, vecPos[0]); + WriteCoord(MSG_MULTICAST, vecPos[1]); + WriteCoord(MSG_MULTICAST, vecPos[2]); + msg_entity = self; + multicast(vecPos, MULTICAST_PVS); +#else + Decals_Place(vecPos, sprintf("{scorch%d", floor(random(1,4)))); + vecPos[2] += 48; + env_sprite eExplosion = spawn(env_sprite); + setorigin(eExplosion, vecPos); + setmodel(eExplosion, "sprites/fexplo.spr"); + Sound_Play(eExplosion, CHAN_WEAPON, "fx.explosion"); + + //eExplosion.think = FX_Explosion_Animate; + eExplosion.effects = EF_ADDITIVE; + eExplosion.drawmask = MASK_ENGINE; + eExplosion.maxframe = modelframecount(eExplosion.modelindex); + eExplosion.loops = 0; + eExplosion.framerate = 20; + eExplosion.nextthink = time + 0.05f; + + pointparticles(FX_EXPLOSION_MAIN, vecPos, [0,0,0], 1); + pointparticles(FX_EXPLOSION_BS, vecPos, [0,0,0], 1); +#endif +} + diff --git a/src/shared/fx_gibhuman.qc b/src/shared/fx_gibhuman.qc new file mode 100644 index 0000000..25bd919 --- /dev/null +++ b/src/shared/fx_gibhuman.qc @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +#ifdef CLIENT +string g_hgibs[] = { + "models/gib_b_bone.mdl", + "models/gib_legbone.mdl", + "models/gib_lung.mdl", + "models/gib_skull.mdl", + "models/gib_b_gib.mdl" +}; + +void +FX_GibHuman_Init(void) +{ + precache_model("models/gib_b_bone.mdl"); + precache_model("models/gib_legbone.mdl"); + precache_model("models/gib_lung.mdl"); + precache_model("models/gib_skull.mdl"); + precache_model("models/gib_b_gib.mdl"); + precache_sound("common/bodysplat.wav"); +} +#endif + +void +FX_GibHuman(vector pos) +{ +#ifdef SERVER + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EV_GIBHUMAN); + WriteCoord(MSG_MULTICAST, pos[0]); + WriteCoord(MSG_MULTICAST, pos[1]); + WriteCoord(MSG_MULTICAST, pos[2]); + msg_entity = __NULL__; + multicast(pos, MULTICAST_PVS); +#else + static void Gib_Remove(void) { + remove(self); + } + static void Gib_Touch(void) + { + if (serverkeyfloat("*bspversion") == BSPVER_HL) + Decals_Place(self.origin, sprintf("{blood%d", floor(random(1,9)))); + else { + decal_pickwall(self, self.origin); + pointparticles(DECAL_BLOOD, g_tracedDecal.endpos, g_tracedDecal.normal, 1); + } + } + + if (cvar("violence_hgibs") <= 0) { + return; + } + + for (int i = 0; i < 5; i++) { + vector vel; + vel[0] = random(-128,128); + vel[1] = random(-128,128); + vel[2] = (300 + random() * 64); + + entity gibb = spawn(); + setmodel(gibb, g_hgibs[i]); + setorigin(gibb, pos); + gibb.movetype = MOVETYPE_BOUNCE; + gibb.solid = SOLID_BBOX; + setsize(gibb, [0,0,0], [0,0,0]); + gibb.velocity = vel; + gibb.avelocity = vectoangles(gibb.velocity); + gibb.think = Gib_Remove; + gibb.touch = Gib_Touch; + gibb.nextthink = time + 5.0f; + gibb.drawmask = MASK_ENGINE; + } + pointsound(pos, "common/bodysplat.wav", 1, ATTN_NORM); +#endif +} diff --git a/src/shared/fx_impact.qc b/src/shared/fx_impact.qc new file mode 100644 index 0000000..7ff012b --- /dev/null +++ b/src/shared/fx_impact.qc @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +#ifdef CLIENT +var int FX_IMPACT_BLACKBITS; +var int FX_IMPACT_SMOKE_BROWN; +var int FX_IMPACT_SMOKE_GREY; +var int FX_IMPACT_SPARK; + +var int DECAL_IMPACT_DEFAULT; +var int DECAL_IMPACT_ALIEN; +var int DECAL_IMPACT_FLESH; +var int DECAL_IMPACT_FOLIAGE; +var int DECAL_IMPACT_COMPUTER; +var int DECAL_IMPACT_DIRT; +var int DECAL_IMPACT_VENT; +var int DECAL_IMPACT_GRATE; +var int DECAL_IMPACT_METAL; +var int DECAL_IMPACT_GLASS; +var int DECAL_IMPACT_SAND; +var int DECAL_IMPACT_SLOSH; +var int DECAL_IMPACT_SNOW; +var int DECAL_IMPACT_TILE; +var int DECAL_IMPACT_WOOD; +var int DECAL_IMPACT_CONCRETE; + +void +FX_Impact_Init(void) +{ + Sound_Precache("sfx_impact.default"); + Sound_Precache("sfx_impact.alien"); + Sound_Precache("sfx_impact.flesh"); + Sound_Precache("sfx_impact.foliage"); + Sound_Precache("sfx_impact.computer"); + Sound_Precache("sfx_impact.dirt"); + Sound_Precache("sfx_impact.vent"); + Sound_Precache("sfx_impact.grate"); + Sound_Precache("sfx_impact.metal"); + Sound_Precache("sfx_impact.glass"); + Sound_Precache("sfx_impact.sand"); + Sound_Precache("sfx_impact.slosh"); + Sound_Precache("sfx_impact.snow"); + Sound_Precache("sfx_impact.tile"); + Sound_Precache("sfx_impact.wood"); + Sound_Precache("sfx_impact.concrete"); + + FX_IMPACT_BLACKBITS = particleeffectnum("fx_impact.blackbits"); + FX_IMPACT_SMOKE_GREY = particleeffectnum("fx_impact.smoke_grey"); + FX_IMPACT_SMOKE_BROWN = particleeffectnum("fx_impact.smoke_brown"); + FX_IMPACT_SPARK = particleeffectnum("fx_impact.spark"); + + /* engine-side particle system decals for non HL1 BSP */ + DECAL_IMPACT_DEFAULT = particleeffectnum("decal_impact.default"); + DECAL_IMPACT_ALIEN = particleeffectnum("decal_impact.alien"); + DECAL_IMPACT_FLESH = particleeffectnum("decal_impact.flesh"); + DECAL_IMPACT_FOLIAGE = particleeffectnum("decal_impact.foliage"); + DECAL_IMPACT_COMPUTER = particleeffectnum("decal_impact.computer"); + DECAL_IMPACT_DIRT = particleeffectnum("decal_impact.dirt"); + DECAL_IMPACT_VENT = particleeffectnum("decal_impact.vent"); + DECAL_IMPACT_GRATE = particleeffectnum("decal_impact.grate"); + DECAL_IMPACT_METAL = particleeffectnum("decal_impact.metal"); + DECAL_IMPACT_GLASS = particleeffectnum("decal_impact.glass"); + DECAL_IMPACT_SAND = particleeffectnum("decal_impact.sand"); + DECAL_IMPACT_SLOSH = particleeffectnum("decal_impact.slosh"); + DECAL_IMPACT_SNOW = particleeffectnum("decal_impact.snow"); + DECAL_IMPACT_TILE = particleeffectnum("decal_impact.tile"); + DECAL_IMPACT_WOOD = particleeffectnum("decal_impact.wood"); + DECAL_IMPACT_CONCRETE = particleeffectnum("decal_impact.concrete"); +} +#endif + +void +FX_Impact(int iType, vector vecPos, vector vNormal) +{ +#ifdef SERVER + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EV_IMPACT); + WriteByte(MSG_MULTICAST, (float)iType); + WriteCoord(MSG_MULTICAST, vecPos[0]); + WriteCoord(MSG_MULTICAST, vecPos[1]); + WriteCoord(MSG_MULTICAST, vecPos[2]); + WriteCoord(MSG_MULTICAST, vNormal[0]); + WriteCoord(MSG_MULTICAST, vNormal[1]); + WriteCoord(MSG_MULTICAST, vNormal[2]); + msg_entity = self; + multicast(vecPos, MULTICAST_PVS); +#else + /* decals */ + if (serverkeyfloat("*bspversion") == BSPVER_HL) { + switch (iType) { + case IMPACT_GLASS: + Decals_Place(vecPos, sprintf("{break%d", floor(random(1,4)))); + break; + case IMPACT_MELEE: + Decals_Place(vecPos, sprintf("{shot%d", floor(random(1,6)))); + break; + default: + Decals_Place(vecPos, sprintf("{bigshot%d", floor(random(1,6)))); + break; + } + } else { + switch (iType) { + case IMPACT_GLASS: + pointparticles(DECAL_IMPACT_GLASS, vecPos, vNormal, 1); + break; + case IMPACT_WOOD: + pointparticles(DECAL_IMPACT_WOOD, vecPos, vNormal, 1); + break; + case IMPACT_METAL: + pointparticles(DECAL_IMPACT_METAL, vecPos, vNormal, 1); + break; + case IMPACT_FLESH: + pointparticles(DECAL_IMPACT_FLESH, vecPos, vNormal, 1); + break; + default: + pointparticles(DECAL_IMPACT_DEFAULT, vecPos, vNormal, 1); + break; + } + } + + switch (iType) { + case IMPACT_MELEE: + case IMPACT_EXPLOSION: + break; + case IMPACT_GLASS: + pointparticles(FX_IMPACT_BLACKBITS, vecPos, vNormal, 1); + break; + case IMPACT_WOOD: + pointparticles(FX_IMPACT_SPARK, vecPos, vNormal, 1); + pointparticles(FX_IMPACT_BLACKBITS, vecPos, vNormal, 1); + pointparticles(FX_IMPACT_SMOKE_BROWN, vecPos, vNormal, 1); + break; + case IMPACT_METAL: + pointparticles(FX_IMPACT_SPARK, vecPos, vNormal, 1); + pointparticles(FX_IMPACT_BLACKBITS, vecPos, vNormal, 1); + break; + case IMPACT_FLESH: + FX_Blood(vecPos, vNormal); + break; + default: + pointparticles(FX_IMPACT_SPARK, vecPos, vNormal, 1); + pointparticles(FX_IMPACT_BLACKBITS, vecPos, vNormal, 1); + pointparticles(FX_IMPACT_SMOKE_GREY, vecPos, vNormal, 1); + break; + } + + switch (iType) { + case IMPACT_ALIEN: + Sound_PlayAt(vecPos, "sfx_impact.alien"); + break; + case IMPACT_COMPUTER: + Sound_PlayAt(vecPos, "sfx_impact.computer"); + break; + case IMPACT_CONCRETE: + Sound_PlayAt(vecPos, "sfx_impact.concrete"); + break; + case IMPACT_DIRT: + Sound_PlayAt(vecPos, "sfx_impact.dirt"); + break; + case IMPACT_FLESH: + Sound_PlayAt(vecPos, "sfx_impact.flesh"); + break; + case IMPACT_FOLIAGE: + Sound_PlayAt(vecPos, "sfx_impact.foliage"); + break; + case IMPACT_GLASS: + Sound_PlayAt(vecPos, "sfx_impact.glass"); + break; + case IMPACT_GRATE: + Sound_PlayAt(vecPos, "sfx_impact.grate"); + break; + case IMPACT_METAL: + Sound_PlayAt(vecPos, "sfx_impact.metal"); + break; + case IMPACT_SLOSH: + Sound_PlayAt(vecPos, "sfx_impact.slosh"); + break; + case IMPACT_SNOW: + Sound_PlayAt(vecPos, "sfx_impact.snow"); + break; + case IMPACT_TILE: + Sound_PlayAt(vecPos, "sfx_impact.tile"); + break; + case IMPACT_VENT: + Sound_PlayAt(vecPos, "sfx_impact.vent"); + break; + case IMPACT_WOOD: + Sound_PlayAt(vecPos, "sfx_impact.wood"); + break; + default: + Sound_PlayAt(vecPos, "sfx_impact.default"); + break; + } +#endif +} diff --git a/src/shared/fx_spark.qc b/src/shared/fx_spark.qc new file mode 100644 index 0000000..b326fd8 --- /dev/null +++ b/src/shared/fx_spark.qc @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +#ifdef CLIENT +var float PARTICLE_SPARK; + +void +FX_Spark_Init(void) +{ + precache_sound("buttons/spark1.wav"); + precache_sound("buttons/spark2.wav"); + precache_sound("buttons/spark3.wav"); + precache_sound("buttons/spark4.wav"); + precache_sound("buttons/spark5.wav"); + precache_sound("buttons/spark6.wav"); + PARTICLE_SPARK = particleeffectnum("fx_spark.effect"); +} +#endif + +void +FX_Spark(vector pos, vector ang) +{ +#ifdef SERVER + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EV_SPARK); + WriteCoord(MSG_MULTICAST, pos[0]); + WriteCoord(MSG_MULTICAST, pos[1]); + WriteCoord(MSG_MULTICAST, pos[2]); + WriteCoord(MSG_MULTICAST, ang[0]); + WriteCoord(MSG_MULTICAST, ang[1]); + WriteCoord(MSG_MULTICAST, ang[2]); + msg_entity = self; + multicast(pos, MULTICAST_PVS); +#else + pointparticles(PARTICLE_SPARK, pos, ang, 1); + pointsound(pos, sprintf("buttons/spark%d.wav", floor(random() * 6) + 1), 1, ATTN_STATIC); +#endif +} diff --git a/src/shared/include.src b/src/shared/include.src new file mode 100644 index 0000000..b454be1 --- /dev/null +++ b/src/shared/include.src @@ -0,0 +1,36 @@ + #includelist +entities.h +flags.h +player.qc +weapon_common.h +animations.h +animations.qc +pmove.qc +pmove_water.qc + +fx_blood.qc +fx_breakmodel.qc +fx_explosion.qc +fx_gibhuman.qc +fx_spark.qc +fx_impact.qc + +items.h +weapons.h +w_crossbow.qc +w_crowbar.qc +w_egon.qc +w_gauss.qc +w_glock.qc +w_handgrenade.qc +w_hornetgun.qc +w_mp5.qc +w_python.qc +w_rpg.qc +w_satchel.qc +w_shotgun.qc +w_snark.qc +w_tripmine.qc +weapons.qc +weapon_common.qc +#endlist diff --git a/src/shared/items.h b/src/shared/items.h new file mode 100644 index 0000000..f6e6b37 --- /dev/null +++ b/src/shared/items.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +#define ITEM_CROWBAR 0x00000001i +#define ITEM_GLOCK 0x00000002i +#define ITEM_PYTHON 0x00000004i +#define ITEM_MP5 0x00000008i +#define ITEM_CROSSBOW 0x00000010i +#define ITEM_SHOTGUN 0x00000020i +#define ITEM_RPG 0x00000040i +#define ITEM_GAUSS 0x00000080i + +#define ITEM_EGON 0x00000100i +#define ITEM_HORNETGUN 0x00000200i +#define ITEM_HANDGRENADE 0x00000400i +#define ITEM_TRIPMINE 0x00000800i +#define ITEM_SATCHEL 0x00001000i +#define ITEM_SNARK 0x00002000i +#define ITEM_SUIT 0x00004000i +#define ITEM_LONGJUMP 0x00008000i + +#define ITEM_UNUSED17 0x00010000i +#define ITEM_UNUSED18 0x00020000i +#define ITEM_UNUSED19 0x00040000i +#define ITEM_UNUSED20 0x00080000i +#define ITEM_UNUSED21 0x00100000i +#define ITEM_UNUSED22 0x00200000i +#define ITEM_UNUSED23 0x00400000i +#define ITEM_UNUSED24 0x00800000i + +#define ITEM_UNUSED25 0x01000000i +#define ITEM_UNUSED26 0x02000000i +#define ITEM_UNUSED27 0x04000000i +#define ITEM_UNUSED28 0x08000000i +#define ITEM_UNUSED29 0x10000000i +#define ITEM_UNUSED30 0x20000000i +#define ITEM_UNUSED31 0x40000000i +#define ITEM_UNUSED32 0x80000000i diff --git a/src/shared/player.qc b/src/shared/player.qc new file mode 100644 index 0000000..5b36c3f --- /dev/null +++ b/src/shared/player.qc @@ -0,0 +1,642 @@ +/* + * Copyright (c) 2016-2021 Marco Hladik + * + * 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. + */ + +/* all potential SendFlags bits we can possibly send */ +enumflags +{ + PLAYER_KEEPALIVE, + PLAYER_MODELINDEX, + PLAYER_ORIGIN, + PLAYER_ORIGIN_Z, + PLAYER_ANGLES_X, + PLAYER_ANGLES_Y, + PLAYER_ANGLES_Z, + PLAYER_VELOCITY, + PLAYER_VELOCITY_Z, + PLAYER_FLAGS, + PLAYER_WEAPON, + PLAYER_ITEMS, + PLAYER_HEALTH, + PLAYER_ARMOR, + PLAYER_MOVETYPE, + PLAYER_VIEWOFS, + PLAYER_BASEFRAME, + PLAYER_FRAME, + PLAYER_AMMO1, + PLAYER_AMMO2, + PLAYER_AMMO3, + PLAYER_UNUSED1, + PLAYER_UNUSED2 +}; + +/* ammo 1 type updates */ +enumflags +{ + AMMO1_GLOCK, + AMMO1_MP5, + AMMO1_PYTHON, + AMMO1_SHOTGUN, + AMMO1_CROSSBOW, + AMMO1_RPG, + AMMO1_SATCHEL +}; + +/* ammo 2 type updates */ +enumflags +{ + AMMO2_9MM, + AMMO2_357, + AMMO2_BUCKSHOT, + AMMO2_BOLT, + AMMO2_ROCKET, + AMMO2_URANIUM, + AMMO2_HANDGRENADE, + AMMO2_SATCHEL, + AMMO2_TRIPMINE, + AMMO2_SNARK, + AMMO2_HORNET, +}; + +enumflags +{ + AMMO3_M203_GRENADE, + AMMO3_SHOTGUN_STATE, + AMMO3_GAUSS_STATE, + AMMO3_GAUSS_VOLUME, + AMMO3_EGON_STATE, + AMMO3_RPG_STATE, + AMMO3_HANDGRENADE_STATE +}; + +noref int input_sequence; +class player:base_player +{ + /* Weapon specific */ + int glock_mag; + int glock_mag_net; + int mp5_mag; + int mp5_mag_net; + int python_mag; + int python_mag_net; + int shotgun_mag; + int shotgun_mag_net; + int crossbow_mag; + int crossbow_mag_net; + int rpg_mag; + int rpg_mag_net; + int satchel_chg; + int satchel_chg_net; + + int ammo_9mm; + int ammo_9mm_net; + int ammo_357; + int ammo_357_net; + int ammo_buckshot; + int ammo_buckshot_net; + int ammo_bolt; + int ammo_bolt_net; + int ammo_rocket; + int ammo_rocket_net; + int ammo_uranium; + int ammo_uranium_net; + int ammo_handgrenade; + int ammo_handgrenade_net; + int ammo_satchel; + int ammo_satchel_net; + int ammo_tripmine; + int ammo_tripmine_net; + int ammo_snark; + int ammo_snark_net; + int ammo_hornet; + int ammo_hornet_net; + + int ammo_m203_grenade; + int ammo_m203_grenade_net; + int ammo_gauss_volume; + int ammo_gauss_volume_net; + int ammo_rpg_state; + int ammo_rpg_state_net; + int mode_tempstate; + int mode_tempstate_net; + +#ifdef CLIENT + /* External model */ + entity p_model; + int p_hand_bone; + int p_model_bone; + float lastweapon; + + virtual void(void) gun_offset; + virtual void(void) draw; + virtual float() predraw; + virtual void(void) postdraw; + virtual void(float) ReceiveEntity; + virtual void(void) PredictPreFrame; + virtual void(void) PredictPostFrame; +#else + virtual void(void) EvaluateEntity; + virtual float(entity, float) SendEntity; +#endif +}; + +#ifdef CLIENT +void Weapons_AmmoUpdate(entity); +/* +================= +player::ReceiveEntity +================= +*/ +void +player::ReceiveEntity(float new) +{ + float fl; + if (new == FALSE) { + /* Go through all the physics code between the last received frame + * and the newest frame and keep the changes this time around instead + * of rolling back, because we'll apply the new server-verified values + * right after anyway. */ + /* FIXME: splitscreen */ + if (entnum == player_localentnum) { + /* FIXME: splitscreen */ + pSeat = &g_seats[0]; + + for (int i = sequence+1; i <= servercommandframe; i++) { + /* ...maybe the input state is too old? */ + if (!getinputstate(i)) { + break; + } + input_sequence = i; + PMove_Run(); + } + + /* any differences in things that are read below are now + * officially from prediction misses. */ + } + } + + /* seed for our prediction table */ + sequence = servercommandframe; + + fl = readfloat(); + + /* HACK: we need to make this more reliable */ + if (fl == UPDATE_ALL) { + /* we respawned */ + gravity = __NULL__; + } + + if (fl & PLAYER_MODELINDEX) + modelindex = readshort(); + + if (fl & PLAYER_ORIGIN) { + origin[0] = readcoord(); + origin[1] = readcoord(); + } + + if (fl & PLAYER_ORIGIN_Z) + origin[2] = readcoord(); + if (fl & PLAYER_ANGLES_X) + pitch = readfloat(); + if (fl & PLAYER_ANGLES_Y) + angles[1] = readfloat(); + if (fl & PLAYER_ANGLES_Z) + angles[2] = readfloat(); + + if (fl & PLAYER_VELOCITY) { + velocity[0] = readcoord(); + velocity[1] = readcoord(); + } + + if (fl & PLAYER_VELOCITY_Z) + velocity[2] = readcoord(); + if (fl & PLAYER_FLAGS) { + flags = readfloat(); + gflags = readfloat(); + } + if (fl & PLAYER_WEAPON) + activeweapon = readbyte(); + if (fl & PLAYER_ITEMS) + g_items = (__variant)readfloat(); + if (fl & PLAYER_HEALTH) + health = readbyte(); + if (fl & PLAYER_ARMOR) + armor = readbyte(); + if (fl & PLAYER_MOVETYPE) + movetype = readbyte(); + if (fl & PLAYER_VIEWOFS) + view_ofs[2] = readfloat(); + if (fl & PLAYER_BASEFRAME) + baseframe = readbyte(); + if (fl & PLAYER_FRAME) { + frame = readbyte(); + frame1time = 0.0f; + frame2time = 0.0f; + } + + if (fl & PLAYER_AMMO1) { + glock_mag = readbyte(); + mp5_mag = readbyte(); + python_mag = readbyte(); + shotgun_mag = readbyte(); + crossbow_mag = readbyte(); + rpg_mag = readbyte(); + satchel_chg = readbyte(); + } + + if (fl & PLAYER_AMMO2) { + ammo_9mm = readbyte(); + ammo_357 = readbyte(); + ammo_buckshot = readbyte(); + ammo_bolt = readbyte(); + ammo_rocket = readbyte(); + ammo_uranium = readbyte(); + ammo_handgrenade = readbyte(); + ammo_satchel = readbyte(); + ammo_tripmine = readbyte(); + ammo_snark = readbyte(); + ammo_hornet = readbyte(); + } + + if (fl & PLAYER_AMMO3) { + ammo_m203_grenade = readbyte(); + ammo_gauss_volume = readbyte(); + ammo_rpg_state = readbyte(); + mode_tempstate = readbyte(); + } + + if (fl & PLAYER_AMMO1 || fl & PLAYER_AMMO2 || fl & PLAYER_AMMO3) + Weapons_AmmoUpdate(this); + + setorigin(this, origin); +} + +/* +================= +player::PredictPostFrame + +Save the last valid server values away in the _net variants of each field +so we can roll them back later. +================= +*/ +void +player::PredictPreFrame(void) +{ + glock_mag_net = glock_mag; + mp5_mag_net = mp5_mag; + python_mag_net = python_mag; + shotgun_mag_net = shotgun_mag; + crossbow_mag_net = crossbow_mag; + rpg_mag_net = rpg_mag; + satchel_chg_net = satchel_chg; + ammo_9mm_net = ammo_9mm; + ammo_357_net = ammo_357; + ammo_buckshot_net = ammo_buckshot; + ammo_bolt_net = ammo_bolt; + ammo_rocket_net = ammo_rocket; + ammo_uranium_net = ammo_uranium; + ammo_handgrenade_net = ammo_handgrenade; + ammo_satchel_net = ammo_satchel; + ammo_tripmine_net = ammo_tripmine; + ammo_snark_net = ammo_snark; + ammo_hornet_net = ammo_hornet; + + ammo_m203_grenade_net = ammo_m203_grenade; + ammo_gauss_volume_net = ammo_gauss_volume; + ammo_rpg_state_net = ammo_rpg_state; + mode_tempstate_net = mode_tempstate; +} + +/* +================= +player::PredictPostFrame + +Where we roll back our values to the ones last sent/verified by the server. +================= +*/ +void +player::PredictPostFrame(void) +{ + glock_mag = glock_mag_net; + mp5_mag = mp5_mag_net; + python_mag = python_mag_net; + shotgun_mag = shotgun_mag_net; + crossbow_mag = crossbow_mag_net; + rpg_mag = rpg_mag_net; + satchel_chg = satchel_chg_net; + ammo_9mm = ammo_9mm_net; + ammo_357 = ammo_357_net; + ammo_buckshot = ammo_buckshot_net; + ammo_m203_grenade = ammo_m203_grenade_net; + ammo_bolt = ammo_bolt_net; + ammo_rocket = ammo_rocket_net; + ammo_uranium = ammo_uranium_net; + ammo_handgrenade = ammo_handgrenade_net; + ammo_satchel = ammo_satchel_net; + ammo_tripmine = ammo_tripmine_net; + ammo_snark = ammo_snark_net; + ammo_hornet = ammo_hornet_net; + + ammo_m203_grenade = ammo_m203_grenade_net; + ammo_gauss_volume = ammo_gauss_volume_net; + ammo_rpg_state = ammo_rpg_state_net; + mode_tempstate = mode_tempstate_net; +} + +#else +void +player::EvaluateEntity(void) +{ + SendFlags |= PLAYER_KEEPALIVE; + + if (old_modelindex != modelindex) + SendFlags |= PLAYER_MODELINDEX; + + if (old_origin[0] != origin[0]) + SendFlags |= PLAYER_ORIGIN; + + if (old_origin[1] != origin[1]) + SendFlags |= PLAYER_ORIGIN; + + if (old_origin[2] != origin[2]) + SendFlags |= PLAYER_ORIGIN_Z; + + if (old_angles[0] != v_angle[0]) + SendFlags |= PLAYER_ANGLES_X; + + if (old_angles[1] != angles[1]) + SendFlags |= PLAYER_ANGLES_Y; + + if (old_angles[2] != angles[2]) + SendFlags |= PLAYER_ANGLES_Z; + + if (old_velocity[0] != velocity[0]) + SendFlags |= PLAYER_VELOCITY; + + if (old_velocity[1] != velocity[1]) + SendFlags |= PLAYER_VELOCITY; + + if (old_velocity[2] != velocity[2]) + SendFlags |= PLAYER_VELOCITY_Z; + + if (old_flags != flags) + SendFlags |= PLAYER_FLAGS; + + if (old_gflags != gflags) + SendFlags |= PLAYER_FLAGS; + + if (old_activeweapon != activeweapon) + SendFlags |= PLAYER_WEAPON; + + if (old_items != g_items) + SendFlags |= PLAYER_ITEMS; + + if (old_health != health) + SendFlags |= PLAYER_HEALTH; + + if (old_armor != armor) + SendFlags |= PLAYER_ARMOR; + + if (old_movetype != movetype) + SendFlags |= PLAYER_MOVETYPE; + + if (old_viewofs != view_ofs[2]) + SendFlags |= PLAYER_VIEWOFS; + + if (old_baseframe != baseframe) + SendFlags |= PLAYER_BASEFRAME; + + if (old_frame != frame) + SendFlags |= PLAYER_FRAME; + + /* ammo 1 type updates */ + if (glock_mag != glock_mag_net) { + SendFlags |= PLAYER_AMMO1; + } + if (mp5_mag != mp5_mag_net) { + SendFlags |= PLAYER_AMMO1; + } + if (python_mag != python_mag_net) { + SendFlags |= PLAYER_AMMO1; + } + if (shotgun_mag != shotgun_mag_net) { + SendFlags |= PLAYER_AMMO1; + } + if (crossbow_mag != crossbow_mag_net) { + SendFlags |= PLAYER_AMMO1; + } + if (rpg_mag != rpg_mag_net) { + SendFlags |= PLAYER_AMMO1; + } + if (satchel_chg != satchel_chg_net) { + SendFlags |= PLAYER_AMMO1; + } + + /* ammo 2 type updates */ + if (ammo_9mm != ammo_9mm_net) { + SendFlags |= PLAYER_AMMO2; + } + if (ammo_357 != ammo_357_net) { + SendFlags |= PLAYER_AMMO2; + } + if (ammo_buckshot != ammo_buckshot_net) { + SendFlags |= PLAYER_AMMO2; + } + if (ammo_bolt != ammo_bolt_net) { + SendFlags |= PLAYER_AMMO2; + } + if (ammo_rocket != ammo_rocket_net) { + SendFlags |= PLAYER_AMMO2; + } + if (ammo_uranium != ammo_uranium_net) { + SendFlags |= PLAYER_AMMO2; + } + if (ammo_handgrenade != ammo_handgrenade_net) { + SendFlags |= PLAYER_AMMO2; + } + if (ammo_satchel != ammo_satchel_net) { + SendFlags |= PLAYER_AMMO2; + } + if (ammo_tripmine != ammo_tripmine_net) { + SendFlags |= PLAYER_AMMO2; + } + if (ammo_snark != ammo_snark_net) { + SendFlags |= PLAYER_AMMO2; + } + if (ammo_hornet != ammo_hornet_net) { + SendFlags |= PLAYER_AMMO2; + } + + if (ammo_m203_grenade != ammo_m203_grenade_net) { + SendFlags |= PLAYER_AMMO3; + } + if (ammo_gauss_volume != ammo_gauss_volume_net) { + SendFlags |= PLAYER_AMMO3; + } + if (ammo_rpg_state != ammo_rpg_state_net) { + SendFlags |= PLAYER_AMMO3; + } + if (mode_tempstate != mode_tempstate_net) { + SendFlags |= PLAYER_AMMO3; + } + + old_modelindex = modelindex; + old_origin = origin; + old_angles = angles; + old_angles[0] = v_angle[0]; + old_velocity = velocity; + old_flags = flags; + old_gflags = gflags; + old_activeweapon = activeweapon; + old_items = g_items; + old_health = health; + old_armor = armor; + old_movetype = movetype; + old_viewofs = view_ofs[2]; + old_baseframe = baseframe; + old_frame = frame; + + glock_mag_net = glock_mag; + mp5_mag_net = mp5_mag; + python_mag_net = python_mag; + shotgun_mag_net = shotgun_mag; + crossbow_mag_net = crossbow_mag; + rpg_mag_net = rpg_mag; + satchel_chg_net = satchel_chg; + + ammo_9mm_net = ammo_9mm; + ammo_357_net = ammo_357; + ammo_buckshot_net = ammo_buckshot; + ammo_m203_grenade_net = ammo_m203_grenade; + ammo_bolt_net = ammo_bolt; + ammo_rocket_net = ammo_rocket; + ammo_uranium_net = ammo_uranium; + ammo_handgrenade_net = ammo_handgrenade; + ammo_satchel_net = ammo_satchel; + ammo_tripmine_net = ammo_tripmine; + ammo_snark_net = ammo_snark; + ammo_hornet_net = ammo_hornet; + + ammo_m203_grenade_net = ammo_m203_grenade; + ammo_gauss_volume_net = ammo_gauss_volume; + ammo_rpg_state_net = ammo_rpg_state; + mode_tempstate_net = mode_tempstate; +} + +/* +================= +player::SendEntity +================= +*/ +float +player::SendEntity(entity ePEnt, float fChanged) +{ + if (health <= 0 && ePEnt != this) { + return FALSE; + } + + if (clienttype(ePEnt) != CLIENTTYPE_REAL) { + return FALSE; + } + + if (ePEnt != self) { + fChanged &= ~PLAYER_ITEMS; + fChanged &= ~PLAYER_HEALTH; + fChanged &= ~PLAYER_ARMOR; + fChanged &= ~PLAYER_VIEWOFS; + fChanged &= ~PLAYER_AMMO1; + fChanged &= ~PLAYER_AMMO2; + fChanged &= ~PLAYER_AMMO3; + } + + WriteByte(MSG_ENTITY, ENT_PLAYER); + WriteFloat(MSG_ENTITY, fChanged); + + /* really trying to get our moneys worth with 23 bits of mantissa */ + if (fChanged & PLAYER_MODELINDEX) + WriteShort(MSG_ENTITY, modelindex); + if (fChanged & PLAYER_ORIGIN) { + WriteCoord(MSG_ENTITY, origin[0]); + WriteCoord(MSG_ENTITY, origin[1]); + } + if (fChanged & PLAYER_ORIGIN_Z) + WriteCoord(MSG_ENTITY, origin[2]); + if (fChanged & PLAYER_ANGLES_X) + WriteFloat(MSG_ENTITY, v_angle[0]); + if (fChanged & PLAYER_ANGLES_Y) + WriteFloat(MSG_ENTITY, angles[1]); + if (fChanged & PLAYER_ANGLES_Z) + WriteFloat(MSG_ENTITY, angles[2]); + if (fChanged & PLAYER_VELOCITY) { + WriteCoord(MSG_ENTITY, velocity[0]); + WriteCoord(MSG_ENTITY, velocity[1]); + } + if (fChanged & PLAYER_VELOCITY_Z) + WriteCoord(MSG_ENTITY, velocity[2]); + if (fChanged & PLAYER_FLAGS) { + WriteFloat(MSG_ENTITY, flags); + WriteFloat(MSG_ENTITY, gflags); + } + if (fChanged & PLAYER_WEAPON) + WriteByte(MSG_ENTITY, activeweapon); + if (fChanged & PLAYER_ITEMS) + WriteFloat(MSG_ENTITY, (__variant)g_items); + if (fChanged & PLAYER_HEALTH) + WriteByte(MSG_ENTITY, bound(0, health, 255)); + if (fChanged & PLAYER_ARMOR) + WriteByte(MSG_ENTITY, armor); + if (fChanged & PLAYER_MOVETYPE) + WriteByte(MSG_ENTITY, movetype); + if (fChanged & PLAYER_VIEWOFS) + WriteFloat(MSG_ENTITY, view_ofs[2]); + if (fChanged & PLAYER_BASEFRAME) + WriteByte(MSG_ENTITY, baseframe); + if (fChanged & PLAYER_FRAME) + WriteByte(MSG_ENTITY, frame); + + if (fChanged & PLAYER_AMMO1) { + WriteByte(MSG_ENTITY, glock_mag); + WriteByte(MSG_ENTITY, mp5_mag); + WriteByte(MSG_ENTITY, python_mag); + WriteByte(MSG_ENTITY, shotgun_mag); + WriteByte(MSG_ENTITY, crossbow_mag); + WriteByte(MSG_ENTITY, rpg_mag); + WriteByte(MSG_ENTITY, satchel_chg); + } + + if (fChanged & PLAYER_AMMO2) { + WriteByte(MSG_ENTITY, ammo_9mm); + WriteByte(MSG_ENTITY, ammo_357); + WriteByte(MSG_ENTITY, ammo_buckshot); + WriteByte(MSG_ENTITY, ammo_bolt); + WriteByte(MSG_ENTITY, ammo_rocket); + WriteByte(MSG_ENTITY, ammo_uranium); + WriteByte(MSG_ENTITY, ammo_handgrenade); + WriteByte(MSG_ENTITY, ammo_satchel); + WriteByte(MSG_ENTITY, ammo_tripmine); + WriteByte(MSG_ENTITY, ammo_snark); + WriteByte(MSG_ENTITY, ammo_hornet); + } + + if (fChanged & PLAYER_AMMO3) { + WriteByte(MSG_ENTITY, ammo_m203_grenade); + WriteByte(MSG_ENTITY, ammo_gauss_volume); + WriteByte(MSG_ENTITY, ammo_rpg_state); + WriteByte(MSG_ENTITY, mode_tempstate); + } + + return TRUE; +} +#endif diff --git a/src/shared/pmove.qc b/src/shared/pmove.qc new file mode 100644 index 0000000..9656ee5 --- /dev/null +++ b/src/shared/pmove.qc @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +#define PHY_JUMP_CHAINWINDOW 0.5 +#define PHY_JUMP_CHAIN 100 +#define PHY_JUMP_CHAINDECAY 50 + +.float waterlevel; +.float watertype; + +float GamePMove_Maxspeed(player target) +{ + return (target.flags & FL_CROUCHING) ? 135 : 270; +} + +void GamePMove_Fall(player target, float impactspeed) +{ + if (impactspeed > 580) { +#ifdef SERVER + float fFallDamage = (impactspeed - 580) * (100 / (1024 - 580)); + Damage_Apply(target, world, fFallDamage, 0, DMG_FALL); + Sound_Play(target, CHAN_VOICE, "player.fall"); +#endif + target.punchangle += [15,0,(input_sequence & 1) ? 15 : -15]; + } else if (impactspeed > 400) { + target.punchangle += [15,0,0]; +#ifdef SERVER + Sound_Play(target, CHAN_VOICE, "player.lightfall"); +#endif + } +} + +void GamePMove_Jump(player target) +{ + float flJumptimeDelta; + float flChainBonus; + + if (target.waterlevel >= 2) { + if (target.watertype == CONTENT_WATER) { + target.velocity[2] = 100; + } else if (target.watertype == CONTENT_SLIME) { + target.velocity[2] = 80; + } else { + target.velocity[2] = 50; + } + } else { + /* Half-Life: Longjump module */ + if (target.flags & FL_CROUCHING && target.g_items & 0x00008000i) { + target.velocity = v_forward * 512; + target.velocity[2] += 100; + } + target.velocity[2] += 240; + } + + if (target.jumptime > 0) { + flJumptimeDelta = 0 - (target.jumptime - PHY_JUMP_CHAINWINDOW); + flChainBonus = PHY_JUMP_CHAIN - (((PHY_JUMP_CHAINWINDOW - (PHY_JUMP_CHAINWINDOW - flJumptimeDelta)) * 2) * PHY_JUMP_CHAINDECAY); + target.velocity[2] += flChainBonus; + } + target.jumptime = PHY_JUMP_CHAINWINDOW; +} diff --git a/src/shared/pmove_water.qc b/src/shared/pmove_water.qc new file mode 100644 index 0000000..cec6cab --- /dev/null +++ b/src/shared/pmove_water.qc @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +void +GamePMove_WaterMove(player target) +{ + if (target.movetype == MOVETYPE_NOCLIP) { + return; + } + +#ifdef SERVER + if (target.health < 0) { + return; + } + + /* we've just exited water */ + if (target.waterlevel != 3) { + if (target.underwater_time < time) { + Sound_Play(target, CHAN_BODY, "player.gasplight"); + } else if (target.underwater_time < time + 9) { + Sound_Play(target, CHAN_BODY, "player.gaspheavy"); + } + target.underwater_time = time + 12; + } else if (target.underwater_time < time) { + /* we've been underwater... for too long. */ + if (target.pain_time < time) { + Damage_Apply(target, world, 5, DMG_DROWN, 0); + target.pain_time = time + 1; + } + } +#endif + + if (!target.waterlevel){ + if (target.flags & FL_INWATER) { +#ifdef SERVER + Sound_Play(target, CHAN_BODY, "player.waterexit"); +#endif + target.flags &= ~FL_INWATER; + } + return; + } + +#ifdef SERVER + if (target.watertype == CONTENT_LAVA) { + if (target.pain_time < time) { + target.pain_time = time + 0.2; + Damage_Apply(target, world, 10*target.waterlevel, DMG_BURN, 0); + } + } else if (target.watertype == CONTENT_SLIME) { + if (target.pain_time < time) { + target.pain_time = time + 1; + Damage_Apply(target, world, 4*target.waterlevel, DMG_ACID, 0); + } + } +#endif + + if (!(target.flags & FL_INWATER)) { +#ifdef SERVER + Sound_Play(target, CHAN_BODY, "player.waterenter"); + target.pain_time = 0; +#endif + target.flags |= FL_INWATER; + } +} + diff --git a/src/shared/w_crossbow.qc b/src/shared/w_crossbow.qc new file mode 100644 index 0000000..d86ef62 --- /dev/null +++ b/src/shared/w_crossbow.qc @@ -0,0 +1,396 @@ +/* + * Copyright (c) 2016-2021 Marco Hladik + * + * 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/w_crossbow.mdl" + +HALF-LIFE (1998) ENTITY + +Crossbow Weapon + +*/ + +enum +{ + CROSSBOW_IDLE1, + CROSSBOW_IDLE2, + CROSSBOW_FIDGET1, + CROSSBOW_FIDGET2, + CROSSBOW_FIRE1, + CROSSBOW_FIRE2, + CROSSBOW_FIRE3, + CROSSBOW_RELOAD, + CROSSBOW_DRAW1, + CROSSBOW_DRAW2, + CROSSBOW_HOLSTER1, + CROSSBOW_HOLSTER2 +}; + +void +w_crossbow_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_crossbow.fire"); + Sound_Precache("weapon_crossbow.hit"); + Sound_Precache("weapon_crossbow.hitbody"); + Sound_Precache("weapon_crossbow.reload"); + precache_model("models/crossbow_bolt.mdl"); + precache_model("models/w_crossbow.mdl"); +#else + precache_model("models/v_crossbow.mdl"); + precache_model("models/p_crossbow.mdl"); +#endif +} + +void +w_crossbow_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.crossbow_mag, pl.ammo_bolt, -1); +} + +string +w_crossbow_wmodel(void) +{ + return "models/w_crossbow.mdl"; +} + +string +w_crossbow_pmodel(void) +{ + return "models/p_crossbow.mdl"; +} + +string +w_crossbow_deathmsg(void) +{ + return ""; +} + +int +w_crossbow_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + pl.crossbow_mag = 5; + } else { + if (pl.ammo_bolt < MAX_A_BOLT) { + pl.ammo_bolt = bound(0, pl.ammo_bolt + 5, MAX_A_BOLT); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_crossbow_draw(void) +{ + player pl = (player)self; + Weapons_SetModel("models/v_crossbow.mdl"); + + if (pl.crossbow_mag <= 0) + Weapons_ViewAnimation(CROSSBOW_DRAW2); + else + Weapons_ViewAnimation(CROSSBOW_DRAW1); +} + +void +w_crossbow_holster(void) +{ + Weapons_ViewAnimation(CROSSBOW_HOLSTER1); +} + +#ifdef SERVER +void Crossbolt_Touch(void) { + /* explode mode, multiplayer */ + if (self.weapon) { + float dmg = Skill_GetValue("plr_xbow_bolt_monster", 50); + FX_Explosion(self.origin); + Damage_Radius(self.origin, self.owner, dmg, dmg * 2.5f, TRUE, WEAPON_CROSSBOW); + if (random() < 0.5) { + sound(self, 1, "weapons/explode3.wav", 1.0f, ATTN_NORM); + } else { + sound(self, 1, "weapons/explode4.wav", 1.0f, ATTN_NORM); + } + remove(self); + return; + } + + /* walls, etc. */ + if (other.takedamage != DAMAGE_YES) { + FX_Spark(self.origin, trace_plane_normal); + Sound_Play(self, 1, "weapon_crossbow.hit"); + remove(self); + return; + } + + /* anything else that can take damage */ + Damage_Apply(other, self.owner, Skill_GetValue("plr_xbow_bolt_monster", 50), WEAPON_CROSSBOW, DMG_BLUNT); + Sound_Play(self, 1, "weapon_crossbow.hitbody"); + + if (other.iBleeds == FALSE) { + FX_Spark(self.origin, trace_plane_normal); + } else { + FX_Blood(self.origin, [1,0,0]); + } + remove(self); +} +#endif + +void +w_crossbow_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + + /* ammo check */ + if (pl.crossbow_mag <= 0) { + return; + } + + pl.crossbow_mag--; + +#ifndef CLIENT + Weapons_MakeVectors(); + entity bolt = spawn(); + setmodel(bolt, "models/crossbow_bolt.mdl"); + setorigin(bolt, Weapons_GetCameraPos() + (v_forward * 16)); + bolt.owner = self; + bolt.velocity = v_forward * 2000; + bolt.movetype = MOVETYPE_FLYMISSILE; + bolt.solid = SOLID_BBOX; + //bolt.flags |= FL_LAGGEDMOVE; + bolt.gravity = 0.5f; + bolt.angles = vectoangles(bolt.velocity); + bolt.avelocity[2] = 10; + bolt.touch = Crossbolt_Touch; + bolt.weapon = pl.viewzoom == 1.0 ? 1 : 0; + setsize(bolt, [0,0,0], [0,0,0]); + + if (pl.crossbow_mag > 0) { + Sound_Play(pl, 8, "weapon_crossbow.hitbody"); + } + + Sound_Play(pl, CHAN_WEAPON, "weapon_crossbow.fire"); +#endif + + if (pl.crossbow_mag) { + Weapons_ViewAnimation(CROSSBOW_FIRE1); + } else { + Weapons_ViewAnimation(CROSSBOW_FIRE3); + } + + Weapons_ViewPunchAngle([-2,0,0]); + + pl.w_attack_next = 0.75f; + pl.w_idle_next = 10.0f; +} + +void +w_crossbow_secondary(void) +{ + player pl = (player)self; + if (pl.w_attack_next) { + return; + } + /* Simple toggle of fovs */ + if (pl.viewzoom == 1.0f) { + pl.viewzoom = 0.2f; + } else { + pl.viewzoom = 1.0f; + } + pl.w_attack_next = 0.5f; +} + +void +w_crossbow_reload(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + + if (pl.ammo_bolt <= 0) { + return; + } + if (pl.crossbow_mag >= 5) { + return; + } + +#ifdef SERVER + Weapons_ReloadWeapon(pl, player::crossbow_mag, player::ammo_bolt, 5); + Sound_Play(pl, CHAN_ITEM, "weapon_crossbow.reload"); +#endif + + Weapons_ViewAnimation(CROSSBOW_RELOAD); + + pl.w_attack_next = 4.5f; + pl.w_idle_next = 10.0f; +} + +void +w_crossbow_release(void) +{ + player pl = (player)self; + + /* auto-reload if need be */ + if (pl.w_attack_next <= 0.0) + if (pl.crossbow_mag == 0 && pl.ammo_bolt > 0) { + Weapons_Reload(); + return; + } + + if (pl.w_idle_next > 0.0) { + return; + } + + int r = (float)input_sequence % 2; + if (r == 1) { + if (pl.ammo_bolt) { + Weapons_ViewAnimation(CROSSBOW_IDLE1); + } else { + Weapons_ViewAnimation(CROSSBOW_IDLE2); + } + } else { + if (pl.ammo_bolt) { + Weapons_ViewAnimation(CROSSBOW_FIDGET1); + } else { + Weapons_ViewAnimation(CROSSBOW_FIDGET2); + } + } + + pl.w_idle_next = 3.0f; +} + +void +w_crossbow_crosshair(void) +{ +#ifdef CLIENT + vector cross_pos; + vector aicon_pos; + + cross_pos = g_hudmins + (g_hudres / 2) + [-12,-12]; + drawsubpic( + cross_pos, + [24,24], + g_cross_spr, + [72/128,0], + [0.1875, 0.1875], + [1,1,1], + 1, + DRAWFLAG_NORMAL + ); + + HUD_DrawAmmo1(); + HUD_DrawAmmo2(); + + aicon_pos = g_hudmins + [g_hudres[0] - 48, g_hudres[1] - 42]; + drawsubpic( + aicon_pos, + [24,24], + g_hud7_spr, + [96/256,72/128], + [24/256, 24/128], + g_hud_color, + pSeat->m_flAmmo2Alpha, + DRAWFLAG_ADDITIVE + ); +#endif +} + +float +w_crossbow_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CR_AIMBOW : ANIM_AIMBOW; +} + +void +w_crossbow_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.crossbow_mag == 0 && pl.ammo_bolt == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud5_spr, + [0,0], + [170/256,45/256], + hud_col, + a, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud2_spr, + [0,0], + [170/256,45/256], + hud_col, + a, + DRAWFLAG_ADDITIVE + ); + } + + HUD_DrawAmmoBar(pos, pl.ammo_bolt, MAX_A_BOLT, a); +#endif +} + +weapon_t w_crossbow = +{ + .name = "crossbow", + .id = ITEM_CROSSBOW, + .slot = 2, + .slot_pos = 2, + .draw = w_crossbow_draw, + .holster = w_crossbow_holster, + .primary = w_crossbow_primary, + .secondary = w_crossbow_secondary, + .reload = w_crossbow_reload, + .release = w_crossbow_release, + .crosshair = w_crossbow_crosshair, + .precache = w_crossbow_precache, + .pickup = w_crossbow_pickup, + .updateammo = w_crossbow_updateammo, + .wmodel = w_crossbow_wmodel, + .pmodel = w_crossbow_pmodel, + .deathmsg = w_crossbow_deathmsg, + .aimanim = w_crossbow_aimanim, + .hudpic = w_crossbow_hudpic +}; + +#ifdef SERVER +void +weapon_crossbow(void) +{ + Weapons_InitItem(WEAPON_CROSSBOW); +} +#endif diff --git a/src/shared/w_crowbar.qc b/src/shared/w_crowbar.qc new file mode 100644 index 0000000..86dd56b --- /dev/null +++ b/src/shared/w_crowbar.qc @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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_crowbar (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_crowbar.mdl" + +HALF-LIFE (1998) ENTITY + +Crowbar Weapon + +*/ + +enum +{ + CBAR_IDLE, + CBAR_DRAW, + CBAR_HOLSTER, + CBAR_ATTACK1HIT, + CBAR_ATTACK1MISS, + CBAR_ATTACK2MISS, + CBAR_ATTACK2HIT, + CBAR_ATTACK3MISS, + CBAR_ATTACK3HIT +}; + +void +w_crowbar_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_crowbar.hit"); + Sound_Precache("weapon_crowbar.miss"); + Sound_Precache("weapon_crowbar.hitbody"); + precache_model("models/w_crowbar.mdl"); +#else + precache_model("models/v_crowbar.mdl"); + precache_model("models/p_crowbar.mdl"); +#endif +} + +void +w_crowbar_updateammo(player pl) +{ + +} + +string +w_crowbar_wmodel(void) +{ + return "models/w_crowbar.mdl"; +} +string +w_crowbar_pmodel(void) +{ + return "models/p_crowbar.mdl"; +} + +string +w_crowbar_deathmsg(void) +{ + return "%s was assaulted by %s's Crowbar."; +} + +void +w_crowbar_draw(void) +{ + Weapons_SetModel("models/v_crowbar.mdl"); + Weapons_ViewAnimation(CBAR_DRAW); +} + +void +w_crowbar_holster(void) +{ + Weapons_ViewAnimation(CBAR_HOLSTER); +} + +void +w_crowbar_primary(void) +{ + int anim = 0; + vector src; + player pl = (player)self; + + if (pl.w_attack_next) { + return; + } + + Weapons_MakeVectors(); + src = pl.origin + pl.view_ofs; + + /* make sure we can gib corpses */ + int oldhitcontents = self.hitcontentsmaski; + self.hitcontentsmaski = CONTENTBITS_POINTSOLID | CONTENTBIT_CORPSE; + traceline(src, src + (v_forward * 32), FALSE, pl); + self.hitcontentsmaski = oldhitcontents; + + if (trace_fraction >= 1.0) { + pl.w_attack_next = 0.5f; + } else { + pl.w_attack_next = 0.25f; + } + pl.w_idle_next = 2.5f; + + int r = (float)input_sequence % 3; + switch (r) { + case 0: + Weapons_ViewAnimation(trace_fraction >= 1 ? CBAR_ATTACK1MISS:CBAR_ATTACK1HIT); + break; + case 1: + Weapons_ViewAnimation(trace_fraction >= 1 ? CBAR_ATTACK2MISS:CBAR_ATTACK2HIT); + break; + default: + Weapons_ViewAnimation(trace_fraction >= 1 ? CBAR_ATTACK3MISS:CBAR_ATTACK3HIT); + } + + if (pl.flags & FL_CROUCHING) { + Animation_PlayerTopTemp(ANIM_SHOOTCROWBAR, 0.5f); + } else { + Animation_PlayerTopTemp(ANIM_CR_SHOOTCROWBAR, 0.42f); + } + +#ifdef SERVER + Sound_Play(self, CHAN_WEAPON, "weapon_crowbar.miss"); + + if (trace_fraction >= 1.0) { + return; + } + + /* don't bother with decals, we got squibs */ + if (trace_ent.iBleeds) { + FX_Blood(trace_endpos, [1,0,0]); + } else { + FX_Impact(IMPACT_MELEE, trace_endpos, trace_plane_normal); + } + + if (trace_ent.takedamage) { + Damage_Apply(trace_ent, pl, Skill_GetValue("plr_crowbar", 10), WEAPON_CROWBAR, DMG_BLUNT); + if (trace_ent.iBleeds) { + Sound_Play(self, CHAN_WEAPON, "weapon_crowbar.hitbody"); + } + } else { + Sound_Play(self, CHAN_WEAPON, "weapon_crowbar.hit"); + } +#endif +} + +void +w_crowbar_release(void) +{ + player pl = (player)self; + + if (pl.w_idle_next) { + return; + } + + Weapons_ViewAnimation(CBAR_IDLE); + pl.w_idle_next = 15.0f; +} + +float +w_crowbar_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CR_AIMCROWBAR : ANIM_AIMCROWBAR; +} + +void +w_crowbar_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud4_spr, + [0,0], + [170/256,45/256], + g_hud_color, + a, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud1_spr, + [0,0], + [170/256,45/256], + g_hud_color, + a, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_crowbar = +{ + .name = "crowbar", + .id = ITEM_CROWBAR, + .slot = 0, + .slot_pos = 0, + .draw = w_crowbar_draw, + .holster = w_crowbar_holster, + .primary = w_crowbar_primary, + .secondary = __NULL__, + .reload = __NULL__, + .release = w_crowbar_release, + .crosshair = __NULL__, + .precache = w_crowbar_precache, + .pickup = __NULL__, + .updateammo = w_crowbar_updateammo, + .wmodel = w_crowbar_wmodel, + .pmodel = w_crowbar_pmodel, + .deathmsg = w_crowbar_deathmsg, + .aimanim = w_crowbar_aimanim, + .hudpic = w_crowbar_hudpic +}; + +/* entity definitions for pickups */ +#ifdef SERVER +void +weapon_crowbar(void) +{ + Weapons_InitItem(WEAPON_CROWBAR); +} +#endif diff --git a/src/shared/w_egon.qc b/src/shared/w_egon.qc new file mode 100644 index 0000000..6adc26c --- /dev/null +++ b/src/shared/w_egon.qc @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2016-2021 Marco Hladik + * + * 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_egon (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_egon.mdl" + +HALF-LIFE (1998) ENTITY + +Egon/Gluon-Gun Weapon + +*/ + +#ifdef CLIENT +var float TRAIL_EGONBEAM; + +#define FXGAUSS_BEAMCOLOR [1,0.5,0] +class FXEgon:CBaseFX +{ + int m_iBeams; + vector m_vecStart; + vector m_vecAngle; + + void(void) FXEgon; + virtual void(void) Draw; +}; + +void +FXEgon::Animate_Next(void) +{ + frame++; + + if (frame == 11) + frame = 0; + + nextthink = time + 0.1f; +} + +void +FXEgon::Draw(void) +{ + player pl = (player)self; + int iLoop = 6; + vector src, endpos; + vector gunpos = gettaginfo(pSeat->m_eViewModel, 33); + + if (alpha <= 0.0f) { + modelindex = 0; + return; + } + + src = gettaginfo(pSeat->m_eViewModel, 0); + makevectors(input_angles); + endpos = src + v_forward * 1024; + traceline(src, endpos, FALSE, pl); + + dynamiclight_add(trace_endpos + v_forward * -16, 128, [0.5, 0.5, 1.0]); + trailparticles(TRAIL_EGONBEAM, pl, gunpos, trace_endpos); + setorigin(this, trace_endpos + v_forward * -16); + + setmodel(this, "sprites/xspark1.spr"); + effects = EF_ADDITIVE; +} + +void +FXEgon::FXEgon(void) +{ + CBaseFX::CBaseFX(); + think = Animate_Next; + nextthink = time + 0.1f; +} +#endif + +enum +{ + EGON_IDLE1, + EGON_FIDGET1, + EGON_ALTFIREON, + EGON_ALTFIRECYCLE, + EGON_ALTFIREOFF, + EGON_FIRE1, + EGON_FIRE2, + EGON_FIRE3, + EGON_FIRE4, + EGON_DRAW, + EGON_HOLSTER +}; + +void w_egon_precache(void) +{ +#ifdef SERVER + precache_sound("weapons/egon_windup2.wav"); + precache_sound("weapons/egon_run3.wav"); + precache_sound("weapons/egon_off1.wav"); + precache_model("models/w_egon.mdl"); +#else + TRAIL_EGONBEAM = particleeffectnum("weapon_egon.beam"); + precache_model("models/v_egon.mdl"); + precache_model("models/p_egon.mdl"); + precache_model("sprites/xspark1.spr"); +#endif +} +void w_egon_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, -1, pl.ammo_uranium, -1); +} +string w_egon_wmodel(void) +{ + return "models/w_egon.mdl"; +} +string w_egon_pmodel(void) +{ + return "models/p_egon.mdl"; +} +string w_egon_deathmsg(void) +{ + return ""; +} + +int w_egon_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (pl.ammo_uranium < MAX_A_URANIUM) { + pl.ammo_uranium = bound(0, pl.ammo_uranium + 20, MAX_A_URANIUM); + } else { + return FALSE; + } +#endif + return TRUE; +} + +void w_egon_draw(void) +{ + player pl = (player)self; + pl.mode_tempstate = 0; + + /* link the FX class */ +#ifdef CLIENT + Weapons_SetModel("models/v_egon.mdl"); + Weapons_ViewAnimation(EGON_DRAW); + + entity eold = self; + self = pSeat->m_pWeaponFX; + spawnfunc_FXEgon(); + self = eold; +#endif +} + +void w_egon_holster(void) +{ + Weapons_ViewAnimation(EGON_HOLSTER); +} + +void w_egon_release(void); + +void w_egon_primary(void) +{ + player pl = (player)self; + if (pl.w_attack_next > 0.0) { + return; + } + + /* Ammo check */ + if (pl.ammo_uranium <= 0) { + w_egon_release(); + return; + } + +#ifdef CLIENT + if (Weapons_GetAnimation() == EGON_IDLE1) + Weapons_ViewAnimation(EGON_ALTFIREON); + else if (Weapons_GetAnimation() == EGON_ALTFIREON) + Weapons_ViewAnimation(EGON_ALTFIRECYCLE); + + FXEgon p = (FXEgon)pSeat->m_pWeaponFX; + p.m_vecAngle = input_angles; + p.m_vecStart = pl.origin + pl.view_ofs; + p.alpha = 1.0f; +#else + Weapons_MakeVectors(); + vector src = Weapons_GetCameraPos(); + vector endpos = src + v_forward * 1024; + traceline(src, endpos, FALSE, pl); + Damage_Radius(trace_endpos, pl, 14, 64, TRUE, DMG_ELECTRO); +#endif + + pl.ammo_uranium--; + + if (pl.mode_tempstate == 0) { +#ifdef SERVER + sound(pl, CHAN_WEAPON, "weapons/egon_windup2.wav", 1, ATTN_NORM, 100, 0); +#endif + pl.mode_tempstate = 1; + Weapons_ViewPunchAngle([-5,0,0]); + pl.w_idle_next = 3.0f; + } else if not (pl.w_idle_next > 0.0f) { + /* wait 3 seconds (idle next) */ + if (pl.mode_tempstate == 1) { +#ifdef SERVER + sound(pl, CHAN_WEAPON, "weapons/egon_run3.wav", 1, ATTN_NORM, 100); +#endif + pl.mode_tempstate = 2; + } + pl.w_idle_next = 3.0f; + } + + pl.w_attack_next = 0.2f; +} + +void w_egon_reload(void) +{ + +} + +void w_egon_release(void) +{ + player pl = (player)self; + +#ifdef CLIENT + FXEgon p = (FXEgon)pSeat->m_pWeaponFX; + p.m_vecAngle = input_angles; + p.m_vecStart = pl.origin + pl.view_ofs; + p.alpha = 0.0f; +#endif + + if (pl.mode_tempstate != 0) { +#ifdef SERVER + sound(pl, CHAN_WEAPON, "weapons/egon_off1.wav", 1, ATTN_NORM, 100, 0); +#endif + pl.mode_tempstate = 0; + Weapons_ViewPunchAngle([-2,0,0]); + } + +#ifndef SERVER + if (Weapons_GetAnimation() == EGON_ALTFIRECYCLE) { + Weapons_ViewAnimation(EGON_ALTFIREOFF); + pl.w_idle_next = 1.0f; + } else { + if (pl.w_idle_next > 0.0f) { + return; + } + int r = (float)input_sequence % 3; + if (r == 1) { + Weapons_ViewAnimation(EGON_FIDGET1); + pl.w_idle_next = 2.666667f; + } else { + Weapons_ViewAnimation(EGON_IDLE1); + pl.w_idle_next = 2.0f; + } + } +#endif +} +void w_egon_crosshair(void) +{ +#ifdef CLIENT + static vector cross_pos; + cross_pos = g_hudmins + (g_hudres / 2) + [-12,-12]; + drawsubpic(cross_pos, [24,24], g_cross_spr, [72/128,48/128], [0.1875, 0.1875], [1,1,1], 1, DRAWFLAG_NORMAL); + HUD_DrawAmmo2(); + vector aicon_pos = g_hudmins + [g_hudres[0] - 48, g_hudres[1] - 42]; + drawsubpic(aicon_pos, [24,24], g_hud7_spr, [0,96/128], [24/256, 24/128], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +float w_egon_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CR_AIMEGON : ANIM_AIMEGON; +} + +void w_egon_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.ammo_uranium == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + if (selected) { + drawsubpic(pos, [170,45], g_hud5_spr, [0,135/256], [170/256,45/256], hud_col, a, DRAWFLAG_ADDITIVE); + } else { + drawsubpic(pos, [170,45], g_hud2_spr, [0,135/256], [170/256,45/256], hud_col, a, DRAWFLAG_ADDITIVE); + } + + HUD_DrawAmmoBar(pos, pl.ammo_uranium, MAX_A_URANIUM, a); +#endif +} + +weapon_t w_egon = +{ + .name = "egon", + .id = ITEM_EGON, + .slot = 3, + .slot_pos = 2, + .draw = w_egon_draw, + .holster = w_egon_holster, + .primary = w_egon_primary, + .secondary = w_egon_release, + .reload = w_egon_reload, + .release = w_egon_release, + .crosshair = w_egon_crosshair, + .precache = w_egon_precache, + .pickup = w_egon_pickup, + .updateammo = w_egon_updateammo, + .wmodel = w_egon_wmodel, + .pmodel = w_egon_pmodel, + .deathmsg = w_egon_deathmsg, + .aimanim = w_egon_aimanim, + .hudpic = w_egon_hudpic +}; + +#ifdef SERVER +void weapon_egon(void) { + Weapons_InitItem(WEAPON_EGON); +} +#endif diff --git a/src/shared/w_gauss.qc b/src/shared/w_gauss.qc new file mode 100644 index 0000000..ec1c407 --- /dev/null +++ b/src/shared/w_gauss.qc @@ -0,0 +1,506 @@ +/* + * Copyright (c) 2016-2021 Marco Hladik + * + * 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_gauss (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_gauss.mdl" + +HALF-LIFE (1998) ENTITY + +Gauss Weapon + +*/ + +#ifdef CLIENT +#define FXGAUSS_BEAMCOLOR [1,0.5,0] +class FXGauss:CBaseFX +{ + int m_iBeams; + vector m_vecStart; + vector m_vecAngle; + + void(void) FXGauss; + virtual void(void) Draw; +}; + +void +FXGauss::Draw(void) +{ + player pl = (player)self; + int iLoop = 6; + vector src, endpos; + vector gunpos = gettaginfo(pSeat->m_eViewModel, 33); + + if (alpha <= 0.0f) { + return; + } + + src = m_vecStart; + makevectors(input_angles); + endpos = src + v_forward * 1024; + traceline(src, endpos, FALSE, pl); + + /* drawing the first bit */ + vector fsize = [3,3]; + makevectors(view_angles); + R_BeginPolygon("sprites/xbeam1.spr_0.tga", 1, 0); + R_PolygonVertex(gunpos + v_right * fsize[0] - v_up * fsize[1], + [1,1], FXGAUSS_BEAMCOLOR, alpha); + R_PolygonVertex(gunpos - v_right * fsize[0] - v_up * fsize[1], + [0,1], FXGAUSS_BEAMCOLOR, alpha); + R_PolygonVertex(trace_endpos - v_right * fsize[0] + v_up * fsize[1], + [0,0], FXGAUSS_BEAMCOLOR, alpha); + R_PolygonVertex(trace_endpos + v_right * fsize[0] + v_up * fsize[1], + [1,0], FXGAUSS_BEAMCOLOR, alpha); + R_EndPolygon(); + + if (m_iBeams == 0) { + alpha -= clframetime * 3; + return; + } + + // reflection equation: + vector x = v_forward; + while (iLoop > 0) { + float n; + vector r; + n = -dotproduct(trace_plane_normal, x); + r = 2 * trace_plane_normal * n + x; + x = r; + src = trace_endpos + (x * 1); + endpos = trace_endpos + (x * 8192); + traceline(src, endpos, FALSE, pl); + + makevectors(view_angles); + R_BeginPolygon("sprites/xbeam1.spr_0.tga", 1, 0); + R_PolygonVertex(src + v_right * fsize[0] - v_up * fsize[1], + [1,1], FXGAUSS_BEAMCOLOR, alpha); + R_PolygonVertex(src - v_right * fsize[0] - v_up * fsize[1], + [0,1], FXGAUSS_BEAMCOLOR, alpha); + R_PolygonVertex(trace_endpos - v_right * fsize[0] + v_up * fsize[1], + [0,0], FXGAUSS_BEAMCOLOR, alpha); + R_PolygonVertex(trace_endpos + v_right * fsize[0] + v_up * fsize[1], + [1,0], FXGAUSS_BEAMCOLOR, alpha); + R_EndPolygon(); + + iLoop--; + } + alpha -= clframetime * 3; +} + +void +FXGauss::FXGauss(void) +{ + CBaseFX::CBaseFX(); +} +#endif + +enum +{ + GAUSS_IDLE1, + GAUSS_IDLE2, + GAUSS_FIDGET, + GAUSS_SPINUP, + GAUSS_SPIN, + GAUSS_FIRE1, + GAUSS_FIRE2, + GAUSS_HOLSTER, + GAUSS_DRAW +}; + +void w_gauss_release(void); + +void w_gauss_precache(void) +{ +#ifdef SERVER + precache_model("models/w_gauss.mdl"); + precache_model("sprites/yelflare1.spr"); + precache_model("sprites/xbeam1.spr"); + precache_sound("weapons/electro4.wav"); + precache_sound("weapons/electro5.wav"); + precache_sound("weapons/electro6.wav"); + precache_sound("weapons/gauss2.wav"); +#else + precache_sound("ambience/pulsemachine.wav"); + precache_model("models/v_gauss.mdl"); + precache_model("models/p_gauss.mdl"); +#endif +} +void w_gauss_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, -1, pl.ammo_uranium, -1); +} +string w_gauss_wmodel(void) +{ + return "models/w_gauss.mdl"; +} +string w_gauss_pmodel(void) +{ + return "models/p_gauss.mdl"; +} +string w_gauss_deathmsg(void) +{ + return ""; +} + +int w_gauss_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (pl.ammo_uranium < MAX_A_URANIUM) { + pl.ammo_uranium = bound(0, pl.ammo_uranium + 20, MAX_A_URANIUM); + } else { + return FALSE; + } +#endif + return TRUE; +} + +void w_gauss_draw(void) +{ + player pl = (player)self; + pl.mode_tempstate = 0; + + /* link the FX class */ +#ifdef CLIENT + Weapons_SetModel("models/v_gauss.mdl"); + Weapons_ViewAnimation(GAUSS_DRAW); + + entity eold = self; + self = pSeat->m_pWeaponFX; + spawnfunc_FXGauss(); + self = eold; +#endif +} + +void w_gauss_holster(void) +{ + Weapons_ViewAnimation(GAUSS_HOLSTER); +} + +#ifdef SERVER +void w_gauss_fire(int one) +{ + player pl = (player)self; + int iLoop = 6; + + Weapons_MakeVectors(); + vector src = Weapons_GetCameraPos(); + vector endpos = src + v_forward * 1024; + traceline(src, endpos, FALSE, pl); + sound(pl, CHAN_WEAPON, "weapons/gauss2.wav", 1, ATTN_NORM); + int iDamage = one ? 20 : 200; + + if (getsurfacetexture(trace_ent, getsurfacenearpoint(trace_ent, trace_endpos)) == "sky") { + return; + } + + if (trace_ent.takedamage == DAMAGE_YES) { + Damage_Apply(trace_ent, self, iDamage, WEAPON_GAUSS, DMG_ELECTRO); + sound(trace_ent, CHAN_ITEM, sprintf("weapons/electro%d.wav", random(0,3)+4), 1, ATTN_NORM); + } + if (one) { + return; + } else { + /* Apply force */ + if (pl.flags & FL_ONGROUND) { + pl.velocity += v_forward * -400; + } else { + pl.velocity += v_forward * -800; + } + } + + // reflection equation: + vector dir = v_forward; + while (iLoop > 0) { + float n; + vector r; + n = -dotproduct(trace_plane_normal, dir); + r = 2 * trace_plane_normal * n + dir; + dir = r; + src = trace_endpos + (dir * 1); + endpos = trace_endpos + (dir * 8192); + traceline(src, endpos, FALSE, pl); + te_beam(world, src, trace_endpos); + iLoop--; + if (trace_ent.takedamage == DAMAGE_YES) { + Damage_Apply(trace_ent, self, iDamage, WEAPON_GAUSS, DMG_ELECTRO); + sound(trace_ent, CHAN_ITEM, sprintf("weapons/electro%d.wav", random(0,3)+4), 1, ATTN_NORM); + } + + if (getsurfacetexture(trace_ent, getsurfacenearpoint(trace_ent, trace_endpos)) != "sky") { + Decals_Place(trace_endpos, "{gaussshot1"); + } else { + break; + } + } +} +#endif + +void w_gauss_primary(void) +{ + player pl = (player)self; + if (pl.w_attack_next > 0.0) { + return; + } + + /* Ammo check */ +#ifdef CLIENT + if (pl.a_ammo2 < 2) { + return; + } +#else + if (pl.ammo_uranium < 2) { + return; + } +#endif + + Weapons_ViewAnimation(GAUSS_FIRE2); +#ifdef CLIENT + FXGauss p = (FXGauss)pSeat->m_pWeaponFX; + p.m_iBeams = 0; + p.m_vecAngle = input_angles; + p.m_vecStart = pl.origin + pl.view_ofs; + p.alpha = 1.0f; + pl.a_ammo2 -= 2; + Weapons_ViewPunchAngle([-2,0,0]); +#else + pl.ammo_uranium -= 2; + w_gauss_fire(1); +#endif + + pl.w_attack_next = 0.2f; + pl.w_idle_next = 2.5f; +} +void w_gauss_secondary(void) +{ + player pl = (player)self; + +#ifdef CLIENT + if (pl.mode_tempstate) + soundupdate(pl, CHAN_WEAPON, "", 2, ATTN_NORM, 100 + (200 * (pl.ammo_gauss_volume/255)), 0, 0); +#endif + + if (pl.w_attack_next) { + return; + } + pl.w_attack_next = 0.1f; + + /* Ammo check */ +#ifdef CLIENT + if (pl.a_ammo2 <= 0) { + if (pl.mode_tempstate > 0) { + w_gauss_release(); + } + return; + } +#else + if (pl.ammo_uranium <= 0) { + if (pl.mode_tempstate > 0) { + w_gauss_release(); + } + return; + } +#endif + +#ifdef CLIENT + if (pl.ammo_gauss_volume < 255) + pl.a_ammo2--; +#else + if (pl.ammo_gauss_volume < 255) + pl.ammo_uranium--; +#endif + + /* Set pitch sound shift */ + pl.ammo_gauss_volume += 16; + if (pl.ammo_gauss_volume > 255) { + pl.ammo_gauss_volume = 255; + } + + if (pl.mode_tempstate == 1) { + Weapons_ViewAnimation(GAUSS_SPIN); + pl.mode_tempstate = 2; + pl.w_idle_next = 0.0f; + } else if (!pl.mode_tempstate) { + Weapons_ViewAnimation(GAUSS_SPINUP); +#ifdef CLIENT + sound(pl, CHAN_WEAPON, "ambience/pulsemachine.wav", 2, ATTN_NORM); +#endif + pl.mode_tempstate = 1; + } +} + +void w_gauss_release(void) +{ + player pl = (player)self; + if (pl.w_idle_next > 0.0) { + return; + } + + /* Reset the pitch sound shift */ + pl.ammo_gauss_volume = 0; + + if (pl.mode_tempstate == 1) { + pl.w_attack_next = 0.0f; + pl.w_idle_next = 4.0f; + w_gauss_primary(); + pl.mode_tempstate = 0; + return; + } else if (pl.mode_tempstate == 2) { + Weapons_ViewAnimation(GAUSS_FIRE1); +#ifdef CLIENT + FXGauss p = (FXGauss)pSeat->m_pWeaponFX; + p.m_iBeams = 1; + p.m_vecAngle = input_angles; + p.m_vecStart = pl.origin + pl.view_ofs; + p.alpha = 1.0f; + soundupdate(pl, CHAN_WEAPON, "", -1, ATTN_NORM, 0, 0, 0); + Weapons_ViewPunchAngle([-5,0,0]); +#else + w_gauss_fire(0); +#endif + pl.w_attack_next = 1.5f; + pl.w_idle_next = 4.0f; + pl.mode_tempstate = 0; + return; + } + + int r = (float)input_sequence % 3; + switch (r) { + case 1: + Weapons_ViewAnimation(GAUSS_IDLE2); + pl.w_idle_next = 4.0f; + break; +#ifndef GEARBOX + case 2: + Weapons_ViewAnimation(GAUSS_FIDGET); + pl.w_idle_next = 3.0f; + break; +#endif + default: + Weapons_ViewAnimation(GAUSS_IDLE1); + pl.w_idle_next = 4.0f; + break; + } +} + +void w_gauss_crosshair(void) +{ +#ifdef CLIENT + vector cross_pos; + vector aicon_pos; + + cross_pos = g_hudmins + (g_hudres / 2) + [-12,-12]; + aicon_pos = g_hudmins + [g_hudres[0] - 48, g_hudres[1] - 42]; + + drawsubpic( + cross_pos, + [24,24], + g_cross_spr, + [48/128,48/128], + [0.1875, 0.1875], + [1,1,1], + 1, + DRAWFLAG_NORMAL + ); + + drawsubpic( + aicon_pos, + [24,24], + g_hud7_spr, + [0,96/128], + [24/256,24/128], + g_hud_color, + pSeat->m_flAmmo2Alpha, + DRAWFLAG_ADDITIVE + ); + + HUD_DrawAmmo2(); +#endif +} + +float w_gauss_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CR_AIMGAUSS : ANIM_AIMGAUSS; +} + +void w_gauss_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.ammo_uranium == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud5_spr, + [0,90/256], + [170/256,45/256], + hud_col, + a, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud2_spr, + [0,90/256], + [170/256,45/256], + hud_col, + a, + DRAWFLAG_ADDITIVE + ); + } + + HUD_DrawAmmoBar(pos, pl.ammo_uranium, MAX_A_URANIUM, a); +#endif +} + +weapon_t w_gauss = +{ + .name = "gauss", + .id = ITEM_GAUSS, + .slot = 3, + .slot_pos = 1, + .draw = w_gauss_draw, + .holster = w_gauss_holster, + .primary = w_gauss_primary, + .secondary = w_gauss_secondary, + .reload = __NULL__, + .release = w_gauss_release, + .crosshair = w_gauss_crosshair, + .precache = w_gauss_precache, + .pickup = w_gauss_pickup, + .updateammo = w_gauss_updateammo, + .wmodel = w_gauss_wmodel, + .pmodel = w_gauss_pmodel, + .deathmsg = w_gauss_deathmsg, + .aimanim = w_gauss_aimanim, + .hudpic = w_gauss_hudpic +}; + +#ifdef SERVER +void weapon_gauss(void) { + Weapons_InitItem(WEAPON_GAUSS); +} +#endif diff --git a/src/shared/w_glock.qc b/src/shared/w_glock.qc new file mode 100644 index 0000000..d6918cf --- /dev/null +++ b/src/shared/w_glock.qc @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2016-2021 Marco Hladik + * + * 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_9mmhandgun (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_9mmhandgun.mdl" + +HALF-LIFE (1998) ENTITY + +9mm Handgun/Glock Weapon +Same as weapon_glock + +*/ + +/*QUAKED weapon_glock (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_9mmhandgun.mdl" + +HALF-LIFE (1998) ENTITY + +9mm Handgun/Glock Weapon +Same as weapon_9mmhandgun + +*/ + +enum +{ + GLOCK_IDLE1, + GLOCK_IDLE2, + GLOCK_IDLE3, + GLOCK_SHOOT, + GLOCK_SHOOT_EMPTY, + GLOCK_RELOAD_EMPTY, + GLOCK_RELOAD, + GLOCK_DRAW, + GLOCK_HOLSTER +}; + +void +w_glock_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_glock.fire"); + precache_model("models/w_9mmhandgun.mdl"); +#else + precache_model("models/v_9mmhandgun.mdl"); + precache_model("models/p_9mmhandgun.mdl"); +#endif +} + +void +w_glock_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.glock_mag, pl.ammo_9mm, -1); +} + +string +w_glock_wmodel(void) +{ + return "models/w_9mmhandgun.mdl"; +} + +string +w_glock_pmodel(void) +{ + return "models/p_9mmhandgun.mdl"; +} + +string +w_glock_deathmsg(void) +{ + return ""; +} + +int +w_glock_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + pl.glock_mag = 18; + } else { + if (pl.ammo_9mm < MAX_A_9MM) { + pl.ammo_9mm = bound(0, pl.ammo_9mm + 18, MAX_A_9MM); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_glock_draw(void) +{ +#ifdef CLIENT + Weapons_SetModel("models/v_9mmhandgun.mdl"); + Weapons_ViewAnimation(GLOCK_DRAW); +#endif +} + +void +w_glock_holster(void) +{ +#ifdef CLIENT + Weapons_ViewAnimation(GLOCK_HOLSTER); +#endif +} + +void +w_glock_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + + /* ammo check */ + if (!pl.glock_mag) { + return; + } + + /* actual firing */ + pl.glock_mag--; + +#ifdef CLIENT + View_SetMuzzleflash(MUZZLE_SMALL); + Weapons_ViewPunchAngle([-2,0,0]); + + if (pl.glock_mag) { + Weapons_ViewAnimation(GLOCK_SHOOT); + } else { + Weapons_ViewAnimation(GLOCK_SHOOT_EMPTY); + } +#else + TraceAttack_FireBullets(1, pl.origin + pl.view_ofs, Skill_GetValue("plr_9mm_bullet", 8), [0.01,0.01], WEAPON_GLOCK); + Sound_Play(pl, CHAN_WEAPON, "weapon_glock.fire"); + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_SHOOT1HAND, 0.45f); + else + Animation_PlayerTopTemp(ANIM_CR_SHOOT1HAND, 0.45f); +#endif + + pl.w_attack_next = 0.3f; + pl.w_idle_next = 5.0f; +} + +void +w_glock_secondary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0) { + return; + } + + /* ammo check */ + if (!pl.glock_mag) { + return; + } + + pl.glock_mag--; + +#ifdef CLIENT + View_SetMuzzleflash(MUZZLE_SMALL); + Weapons_ViewPunchAngle([-2,0,0]); + + if (pl.glock_mag) { + Weapons_ViewAnimation(GLOCK_SHOOT); + } else { + Weapons_ViewAnimation(GLOCK_SHOOT_EMPTY); + } +#else + TraceAttack_FireBullets(1, pl.origin + pl.view_ofs, Skill_GetValue("plr_9mm_bullet", 8), [0.1,0.1], WEAPON_GLOCK); + Sound_Play(pl, CHAN_WEAPON, "weapon_glock.fire"); + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_SHOOT1HAND, 0.45f); + else + Animation_PlayerTopTemp(ANIM_CR_SHOOT1HAND, 0.45f); +#endif + + pl.w_attack_next = 0.2f; + pl.w_idle_next = 5.0f; +} + +void +w_glock_reload(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + + if (pl.glock_mag >= 18) { + return; + } + if (pl.ammo_9mm <= 0) { + return; + } + + if (pl.glock_mag) { + Weapons_ViewAnimation(GLOCK_RELOAD); + } else { + Weapons_ViewAnimation(GLOCK_RELOAD_EMPTY); + } + +#ifdef SERVER + Weapons_ReloadWeapon(pl, player::glock_mag, player::ammo_9mm, 18); +#endif + + pl.w_attack_next = 2.0f; + pl.w_idle_next = 10.0f; +} + +void +w_glock_release(void) +{ + player pl = (player)self; + int r; + + /* auto-reload if need be */ + if (pl.w_attack_next <= 0.0) + if (pl.glock_mag == 0 && pl.ammo_9mm > 0) { + Weapons_Reload(); + return; + } + + if (pl.w_idle_next > 0.0) { + return; + } + + r = (float)input_sequence % 3; + switch (r) { + case 1: + Weapons_ViewAnimation(GLOCK_IDLE2); + pl.w_idle_next = 2.5f; + break; + case 2: + Weapons_ViewAnimation(GLOCK_IDLE3); + pl.w_idle_next = 3.5f; + break; + default: + Weapons_ViewAnimation(GLOCK_IDLE1); + pl.w_idle_next = 3.75f; + break; + } +} + +float +w_glock_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CR_AIM1HAND : ANIM_AIM1HAND; +} + +void +w_glock_hud(void) +{ +#ifdef CLIENT + vector cross_pos; + vector aicon_pos; + + cross_pos = g_hudmins + (g_hudres / 2) + [-12,-12]; + aicon_pos = g_hudmins + [g_hudres[0] - 48, g_hudres[1] - 42]; + + drawsubpic( + cross_pos, + [24,24], + g_cross_spr, + [0.1875,0], + [0.1875, 0.1875], + [1,1,1], + 1.0f, + DRAWFLAG_NORMAL + ); + + HUD_DrawAmmo1(); + HUD_DrawAmmo2(); + + drawsubpic( + aicon_pos, + [24,24], + g_hud7_spr, + [0,72/128], + [24/256, 24/128], + g_hud_color, + pSeat->m_flAmmo2Alpha, + DRAWFLAG_ADDITIVE + ); +#endif +} + +void +w_glock_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.glock_mag == 0 && pl.ammo_9mm == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud4_spr, + [0,45/256], + [170/256,45/256], + hud_col, + a, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud1_spr, + [0,45/256], + [170/256,45/256], + hud_col, + a, + DRAWFLAG_ADDITIVE + ); + } + + HUD_DrawAmmoBar(pos, pl.ammo_9mm, MAX_A_9MM, a); +#endif +} + +weapon_t w_glock = +{ + .name = "9mmhandgun", + .id = ITEM_GLOCK, + .slot = 1, + .slot_pos = 0, + .draw = w_glock_draw, + .holster = w_glock_holster, + .primary = w_glock_primary, + .secondary = w_glock_secondary, + .reload = w_glock_reload, + .release = w_glock_release, + .crosshair = w_glock_hud, + .precache = w_glock_precache, + .pickup = w_glock_pickup, + .updateammo = w_glock_updateammo, + .wmodel = w_glock_wmodel, + .pmodel = w_glock_pmodel, + .deathmsg = w_glock_deathmsg, + .aimanim = w_glock_aimanim, + .hudpic = w_glock_hudpic +}; + +/* pickups */ +#ifdef SERVER +void +weapon_9mmhandgun(void) +{ + Weapons_InitItem(WEAPON_GLOCK); +} + +void +weapon_glock(void) +{ + Weapons_InitItem(WEAPON_GLOCK); +} +#endif diff --git a/src/shared/w_handgrenade.qc b/src/shared/w_handgrenade.qc new file mode 100644 index 0000000..93cedef --- /dev/null +++ b/src/shared/w_handgrenade.qc @@ -0,0 +1,274 @@ +/* + * Copyright (c) 2016-2021 Marco Hladik + * + * 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_handgrenade (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_grenade.mdl" + +HALF-LIFE (1998) ENTITY + +Handgrenade Weapon + +*/ + +enum +{ + HANDGRENADE_IDLE, + HANDGRENADE_FIDGET, + HANDGRENADE_PULLPIN, + HANDGRENADE_THROW1, + HANDGRENADE_THROW2, + HANDGRENADE_THROW3, + HANDGRENADE_HOLSTER, + HANDGRENADE_DRAW +}; + +void w_handgrenade_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_handgrenade.bounce"); + precache_model("models/w_grenade.mdl"); +#else + precache_model("models/v_grenade.mdl"); + precache_model("models/p_grenade.mdl"); +#endif +} +void w_handgrenade_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, -1, pl.ammo_handgrenade, -1); +} +string w_handgrenade_wmodel(void) +{ + return "models/w_grenade.mdl"; +} +string w_handgrenade_pmodel(void) +{ + return "models/p_grenade.mdl"; +} +string w_handgrenade_deathmsg(void) +{ + return ""; +} + +int w_handgrenade_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (pl.ammo_handgrenade < MAX_A_HANDGRENADE) { + pl.ammo_handgrenade = bound(0, pl.ammo_handgrenade + 1, MAX_A_HANDGRENADE); + } else { + return FALSE; + } +#endif + return TRUE; +} + +#ifdef SERVER +void w_handgrenade_throw(void) +{ + static void WeaponFrag_Throw_Explode(void) + { + float dmg = Skill_GetValue("plr_hand_grenade", 150); + FX_Explosion(self.origin); + Damage_Radius(self.origin, self.owner, dmg, dmg * 2.5f, TRUE, WEAPON_HANDGRENADE); + sound(self, CHAN_WEAPON, sprintf("weapons/explode%d.wav", floor(random() * 2) + 3), 1, ATTN_NORM); + remove(self); + } + + static void WeaponFrag_Throw_Touch(void) + { + if (other.takedamage == DAMAGE_YES) { + Damage_Apply(other, self.owner, 15, WEAPON_HANDGRENADE, DMG_BLUNT); + } else { + Sound_Play(self, CHAN_BODY, "weapon_handgrenade.bounce"); + } + self.frame = 0; + } + + player pl = (player)self; + vector vPLAngle = pl.v_angle; + if (vPLAngle[0] < 0) { + vPLAngle[0] = -10 + vPLAngle[0] * ((90 - 10) / 90.0); + } else { + vPLAngle[0] = -10 + vPLAngle[0] * ((90 + 10) / 90.0); + } + + float flVel = (90 - vPLAngle[0]) * 5; + if (flVel > 1000) { + flVel = 1000; + } + + makevectors(vPLAngle); + vector vecSrc = pl.origin + pl.view_ofs + v_forward * 16; + vector vecThrow = v_forward * flVel + pl.velocity; + + entity eGrenade = spawn(); + eGrenade.owner = pl; + eGrenade.classname = "remove_me"; + eGrenade.solid = SOLID_BBOX; + eGrenade.frame = 1; + eGrenade.velocity = vecThrow; + eGrenade.movetype = MOVETYPE_BOUNCE; + eGrenade.think = WeaponFrag_Throw_Explode; + eGrenade.touch = WeaponFrag_Throw_Touch; + eGrenade.nextthink = time + 4.0f; + setmodel(eGrenade, "models/w_grenade.mdl"); + setsize(eGrenade, [0,0,0], [0,0,0]); + setorigin(eGrenade, vecSrc); +} +#endif + +void w_handgrenade_draw(void) +{ + player pl = (player)self; + pl.mode_tempstate = 0; + +#ifdef CLIENT + Weapons_SetModel("models/v_grenade.mdl"); + Weapons_ViewAnimation(HANDGRENADE_DRAW); +#endif +} + +void w_handgrenade_holster(void) +{ + +} +void w_handgrenade_primary(void) +{ + player pl = (player)self; + if (pl.w_attack_next > 0.0) { + return; + } + + /* We're abusing this network variable for the holding check */ + if (pl.mode_tempstate > 0) { + return; + } + + /* Ammo check */ + if (pl.ammo_handgrenade <= 0) { + return; + } + +#ifdef CLIENT + Weapons_ViewAnimation(HANDGRENADE_PULLPIN); +#endif + + pl.mode_tempstate = 1; + pl.w_attack_next = 0.5f; + pl.w_idle_next = 0.5f; +} + +void w_handgrenade_hud(void) +{ +#ifdef CLIENT + HUD_DrawAmmo2(); + vector aicon_pos = g_hudmins + [g_hudres[0] - 48, g_hudres[1] - 42]; + drawsubpic(aicon_pos, [24,24], g_hud7_spr, [48/256,96/128], [24/256, 24/128], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void w_handgrenade_release(void) +{ + player pl = (player)self; + + if (pl.w_idle_next > 0.0) { + return; + } + + if (pl.mode_tempstate == 1) { +#ifdef CLIENT + Weapons_ViewAnimation(HANDGRENADE_THROW1); +#else + w_handgrenade_throw(); +#endif + pl.ammo_handgrenade--; + pl.mode_tempstate = 2; + pl.w_attack_next = 1.0f; + pl.w_idle_next = 0.5f; + } else if (pl.mode_tempstate == 2) { +#ifdef CLIENT + Weapons_ViewAnimation(HANDGRENADE_DRAW); +#else + if (!pl.ammo_handgrenade) { + Weapons_RemoveItem(pl, WEAPON_HANDGRENADE); + } +#endif + pl.w_attack_next = 0.5f; + pl.w_idle_next = 0.5f; + pl.mode_tempstate = 0; + } else { + int r = (float)input_sequence % 8; + if (r == 1) { + Weapons_ViewAnimation(HANDGRENADE_FIDGET); + pl.w_idle_next = 2.5f; + } else { + Weapons_ViewAnimation(HANDGRENADE_IDLE); + pl.w_idle_next = 3.0f; + } + } +} + +float +w_handgrenade_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CR_AIMCROWBAR : ANIM_AIMCROWBAR; +} + +void +w_handgrenade_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + + if (selected) { + drawsubpic(pos, [170,45], g_hud6_spr, [0,0], [170/256,45/256], g_hud_color, a, DRAWFLAG_ADDITIVE); + } else { + drawsubpic(pos, [170,45], g_hud3_spr, [0,0], [170/256,45/256], g_hud_color, a, DRAWFLAG_ADDITIVE); + } + + HUD_DrawAmmoBar(pos, pl.ammo_handgrenade, MAX_A_HANDGRENADE, a); +#endif +} + +weapon_t w_handgrenade = +{ + .name = "grenade", + .id = ITEM_HANDGRENADE, + .slot = 4, + .slot_pos = 0, + .draw = w_handgrenade_draw, + .holster = w_handgrenade_holster, + .primary = w_handgrenade_primary, + .secondary = __NULL__, + .reload = __NULL__, + .release = w_handgrenade_release, + .crosshair = w_handgrenade_hud, + .precache = w_handgrenade_precache, + .pickup = w_handgrenade_pickup, + .updateammo = w_handgrenade_updateammo, + .wmodel = w_handgrenade_wmodel, + .pmodel = w_handgrenade_pmodel, + .deathmsg = w_handgrenade_deathmsg, + .aimanim = w_handgrenade_aimanim, + .hudpic = w_handgrenade_hudpic +}; + +#ifdef SERVER +void weapon_handgrenade(void) { + Weapons_InitItem(WEAPON_HANDGRENADE); +} +#endif diff --git a/src/shared/w_hornetgun.qc b/src/shared/w_hornetgun.qc new file mode 100644 index 0000000..1721447 --- /dev/null +++ b/src/shared/w_hornetgun.qc @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2016-2021 Marco Hladik + * + * 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_hornetgun (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_hgun.mdl" + +HALF-LIFE (1998) ENTITY + +Hornetgun Weapon + +*/ + +enum +{ + HORNETGUN_IDLE, + HORNETGUN_FIDGET1, + HORNETGUN_FIDGET2, + HORNETGUN_HOLSTER, + HORNETGUN_DRAW, + HORNETGUN_SHOOT +}; + +void +w_hornetgun_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_hornetgun.fire"); + Sound_Precache("weapon_hornetgun.buzz"); + Sound_Precache("weapon_hornetgun.hit"); + precache_model("models/w_hgun.mdl"); + precache_model("models/hornet.mdl"); +#else + precache_model("models/v_hgun.mdl"); + precache_model("models/p_hgun.mdl"); +#endif +} + +int +w_hornetgun_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + /* only pick it up once */ + if (new) { + pl.ammo_hornet = MAX_A_HORNET; + return TRUE; + } +#endif + return FALSE; +} + +void +w_hornetgun_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, -1, pl.ammo_hornet, -1); +} +string w_hornetgun_wmodel(void) +{ + return "models/w_hgun.mdl"; +} +string w_hornetgun_pmodel(void) +{ + return "models/p_hgun.mdl"; +} +string w_hornetgun_deathmsg(void) +{ + return ""; +} + +void +w_hornetgun_draw(void) +{ + Weapons_SetModel("models/v_hgun.mdl"); + Weapons_ViewAnimation(HORNETGUN_DRAW); +} + +void +w_hornetgun_holster(void) +{ + +} + +#ifdef SERVER +void +w_hornetgun_shoothornet(void) +{ + static void Hornet_Touch(void) { + if (other.takedamage == DAMAGE_YES) { + Damage_Apply(other, self.owner, Skill_GetValue("plr_hornet", 10), WEAPON_HORNETGUN, DMG_GENERIC); + Sound_Play(other, CHAN_VOICE, "weapon_hornetgun.hit"); + } else { + Sound_Play(self, CHAN_VOICE, "weapon_hornetgun.buzz"); + } + remove(self); + } + + Weapons_MakeVectors(); + entity bolt = spawn(); + setmodel(bolt, "models/hornet.mdl"); + setorigin(bolt, Weapons_GetCameraPos() + (v_forward * 16) + (v_up * -8)); + bolt.owner = self; + bolt.velocity = v_forward * 1000; + bolt.movetype = MOVETYPE_FLY; + bolt.solid = SOLID_BBOX; + //bolt.flags |= FL_LAGGEDMOVE; + bolt.gravity = 0.5f; + bolt.angles = vectoangles(bolt.velocity); + bolt.touch = Hornet_Touch; + bolt.traileffectnum = particleeffectnum("weapon_hornet.trail"); + setsize(bolt, [0,0,0], [0,0,0]); +} +#endif + +void +w_hornetgun_release(void) +{ + player pl = (player)self; + + if (pl.w_idle_next > 0.0) { + return; + } + + /* slow regeneration of ammunition */ + if (pl.ammo_hornet < MAX_A_HORNET) { + pl.ammo_hornet = bound(0, pl.ammo_hornet + 1, MAX_A_HORNET); + Weapons_UpdateAmmo(pl, -1, pl.ammo_hornet, -1); + pl.w_idle_next = 0.35f; + return; + } + + if (pl.w_idle_next > 0.0) { + return; + } + + int r; + r = (float)input_sequence % 3; + switch (r) { + case 1: + Weapons_ViewAnimation(HORNETGUN_FIDGET1); + pl.w_idle_next = 2.5f; + break; + case 2: + Weapons_ViewAnimation(HORNETGUN_FIDGET2); + pl.w_idle_next = 2.1875f; + break; + default: + Weapons_ViewAnimation(HORNETGUN_IDLE); + pl.w_idle_next = 1.875f; + } +} + +void +w_hornetgun_primary(void) +{ + player pl = (player)self; + if (pl.w_attack_next > 0.0) { + return; + } + + /* Ammo check */ + if (pl.ammo_hornet <= 0) { + w_hornetgun_release(); + return; + } + +#ifdef SERVER + w_hornetgun_shoothornet(); + Sound_Play(pl, CHAN_WEAPON, "weapon_hornetgun.fire"); +#endif + + pl.ammo_hornet--; + Weapons_ViewAnimation(HORNETGUN_SHOOT); + + pl.w_attack_next = 0.25; + pl.w_idle_next = 1.0f; +} + +void +w_hornetgun_secondary(void) +{ + player pl = (player)self; + if (pl.w_attack_next) { + return; + } + + /* Ammo check */ + if (pl.ammo_hornet <= 0) { + w_hornetgun_release(); + return; + } + +#ifdef SERVER + w_hornetgun_shoothornet(); + Weapons_PlaySound(pl, CHAN_WEAPON, sprintf("agrunt/ag_fire%d.wav", floor(random(1,4))), 1, ATTN_NORM); +#endif + + pl.ammo_hornet--; + Weapons_ViewAnimation(HORNETGUN_SHOOT); + + pl.w_attack_next = 0.1; + pl.w_idle_next = 1.0f; +} + +void +w_hornetgun_crosshair(void) +{ +#ifdef CLIENT + vector cross_pos; + vector aicon_pos; + + cross_pos = g_hudmins + (g_hudres / 2) + [-12,-12]; + aicon_pos = g_hudmins + [g_hudres[0] - 48, g_hudres[1] - 42]; + + drawsubpic( + cross_pos, + [24,24], + g_cross_spr, + [72/128,24/128], + [0.1875, 0.1875], + [1,1,1], + 1.0f, + DRAWFLAG_NORMAL + ); + + HUD_DrawAmmo2(); + + drawsubpic( + aicon_pos, + [24,24], + g_hud7_spr, + [24/256,96/128], + [24/256, 24/128], + g_hud_color, + pSeat->m_flAmmo2Alpha, + DRAWFLAG_ADDITIVE + ); +#endif +} + +float +w_hornetgun_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CR_AIMHIVE : ANIM_AIMHIVE; +} + +void +w_hornetgun_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud5_spr, + [0,180/256], + [170/256,45/256], + g_hud_color, + a, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud2_spr, + [0,180/256], + [170/256,45/256], + g_hud_color, + a, + DRAWFLAG_ADDITIVE + ); + } + + HUD_DrawAmmoBar(pos, pl.ammo_hornet, MAX_A_HORNET, a); +#endif +} + +weapon_t w_hornetgun = +{ + .name = "hornet", + .id = ITEM_HORNETGUN, + .slot = 3, + .slot_pos = 3, + .draw = w_hornetgun_draw, + .holster = w_hornetgun_holster, + .primary = w_hornetgun_primary, + .secondary = w_hornetgun_secondary, + .reload = __NULL__, + .release = w_hornetgun_release, + .crosshair = w_hornetgun_crosshair, + .precache = w_hornetgun_precache, + .pickup = w_hornetgun_pickup, + .updateammo = w_hornetgun_updateammo, + .wmodel = w_hornetgun_wmodel, + .pmodel = w_hornetgun_pmodel, + .deathmsg = w_hornetgun_deathmsg, + .aimanim = w_hornetgun_aimanim, + .hudpic = w_hornetgun_hudpic +}; + +#ifdef SERVER +void +weapon_hornetgun(void) +{ + Weapons_InitItem(WEAPON_HORNETGUN); +} +#endif diff --git a/src/shared/w_mp5.qc b/src/shared/w_mp5.qc new file mode 100644 index 0000000..6f8a882 --- /dev/null +++ b/src/shared/w_mp5.qc @@ -0,0 +1,405 @@ +/* + * Copyright (c) 2016-2021 Marco Hladik + * + * 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_9mmAR (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_9mmar.mdl" + +HALF-LIFE (1998) ENTITY + +MP5/9mmAR Weapon +Same as weapon_mp5 + +*/ + +/*QUAKED weapon_mp5 (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_9mmar.mdl" + +HALF-LIFE (1998) ENTITY + +MP5/9mmAR Weapon +Same as weapon_9mmAR + +*/ + +/* Animations */ +enum +{ + MP5_IDLE1, + MP5_IDLE2, + MP5_GRENADE, + MP5_RELOAD, + MP5_DRAW, +#ifdef GEARBOX + MP5_DEPLOY, /* not sure what this is about */ +#endif + MP5_FIRE1, + MP5_FIRE2, + MP5_FIRE3 +}; + +void +w_mp5_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_mp5.shoot"); + Sound_Precache("weapon_mp5.gl"); + precache_model("models/w_9mmar.mdl"); + precache_model("models/grenade.mdl"); +#else + precache_model("models/v_9mmar.mdl"); + precache_model("models/p_9mmar.mdl"); +#endif +} + +int +w_mp5_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + pl.mp5_mag = 25; + } else { + if (pl.ammo_9mm < MAX_A_9MM) { + pl.ammo_9mm = bound(0, pl.ammo_9mm + 25, MAX_A_9MM); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_mp5_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.mp5_mag, pl.ammo_9mm, pl.ammo_m203_grenade); +} + +string +w_mp5_wmodel(void) +{ + return "models/w_9mmar.mdl"; +} + +string +w_mp5_pmodel(void) +{ + return "models/p_9mmar.mdl"; +} + +string +w_mp5_deathmsg(void) +{ + return ""; +} + +void +w_mp5_draw(void) +{ +#ifdef CLIENT + Weapons_SetModel("models/v_9mmar.mdl"); + Weapons_ViewAnimation(MP5_DRAW); +#endif +} + +void +w_mp5_holster(void) +{ + Weapons_ViewAnimation(MP5_DRAW); +} + +void +w_mp5_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + + /* Ammo check */ + if (pl.mp5_mag <= 0) { + return; + } + +#ifdef CLIENT + View_SetMuzzleflash(MUZZLE_RIFLE); +#else + /* singleplayer is more accurate */ + if (cvar("sv_playerslots") == 1) { + TraceAttack_FireBullets(1, Weapons_GetCameraPos(), Skill_GetValue("plr_9mmAR_bullet", 5), [0.025,0.025], WEAPON_MP5); + } else { + TraceAttack_FireBullets(1, Weapons_GetCameraPos(), Skill_GetValue("plr_9mmAR_bullet", 5), [0.05,0.05], WEAPON_MP5); + } + + Sound_Play(pl, CHAN_WEAPON, "weapon_mp5.shoot"); +#endif + + pl.mp5_mag--; + + /* Actual firing */ + int r = (float)input_sequence % 2; + if (r) { + Weapons_ViewAnimation(MP5_FIRE1); + } else { + Weapons_ViewAnimation(MP5_FIRE2); + } + + Weapons_ViewPunchAngle([-2,0,0]); + + pl.w_attack_next = 0.1f; + pl.w_idle_next = 10.0f; +} + +void +w_mp5_secondary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + + + if (pl.ammo_m203_grenade <= 0) { + return; + } + + pl.ammo_m203_grenade--; + +#ifdef CLIENT + Weapons_ViewPunchAngle([-10,0,0]); + Weapons_ViewAnimation(MP5_GRENADE); +#else + static void Grenade_ExplodeTouch(void) { + float dmg = Skill_GetValue("plr_9mmAR_grenade", 100); + FX_Explosion(self.origin); + Damage_Radius(self.origin, self.owner, dmg, dmg * 2.5f, TRUE, WEAPON_MP5); + + if (random() < 0.5) { + sound(self, 1, "weapons/explode3.wav", 1, ATTN_NORM); + } else { + sound(self, 1, "weapons/explode4.wav", 1, ATTN_NORM); + } + remove(self); + } + + Weapons_MakeVectors(); + entity gren = spawn(); + setmodel(gren, "models/grenade.mdl"); + setorigin(gren, Weapons_GetCameraPos() + (v_forward * 16)); + gren.owner = self; + gren.velocity = v_forward * 800; + gren.angles = vectoangles(gren.velocity); + gren.avelocity[0] = random(-100, -500); + gren.gravity = 0.5f; + gren.movetype = MOVETYPE_BOUNCE; + //gren.flags |= FL_LAGGEDMOVE; + gren.solid = SOLID_BBOX; + setsize(gren, [0,0,0], [0,0,0]); + gren.touch = Grenade_ExplodeTouch; + Sound_Play(pl, CHAN_WEAPON, "weapon_mp5.gl"); +#endif + + pl.w_attack_next = 1.0f; + pl.w_idle_next = 10.0f; +} + +void +w_mp5_reload(void) +{ + player pl = (player)self; + if (pl.w_attack_next) { + return; + } + + /* Ammo check */ + if (pl.mp5_mag >= 50) { + return; + } + if (pl.ammo_9mm <= 0) { + return; + } + +#ifdef CLIENT + Weapons_ViewAnimation(MP5_RELOAD); +#else + Weapons_ReloadWeapon(pl, player::mp5_mag, player::ammo_9mm, 50); +#endif + + pl.w_attack_next = 1.5f; + pl.w_idle_next = 10.0f; +} + +void +w_mp5_release(void) +{ + player pl = (player)self; + + /* auto-reload if need be */ + if (pl.w_attack_next <= 0.0) + if (pl.mp5_mag == 0 && pl.ammo_9mm > 0) { + Weapons_Reload(); + return; + } + if (pl.w_idle_next > 0.0) { + return; + } + + int r = (float)input_sequence % 3; + if (r == 1) { + Weapons_ViewAnimation(MP5_IDLE1); + } else { + Weapons_ViewAnimation(MP5_IDLE2); + } + + pl.w_idle_next = 15.0f; +} + +void +w_mp5_crosshair(void) +{ +#ifdef CLIENT + vector cross_pos; + vector aicon_pos; + + cross_pos = g_hudmins + (g_hudres / 2) + [-12,-12]; + + drawsubpic( + cross_pos, + [24,24], + g_cross_spr, + [24/128,48/128], + [0.1875, 0.1875], + [1,1,1], + 1.0f, + DRAWFLAG_NORMAL + ); + + HUD_DrawAmmo1(); + HUD_DrawAmmo2(); + HUD_DrawAmmo3(); + + aicon_pos = g_hudmins + [g_hudres[0] - 48, g_hudres[1] - 42]; + drawsubpic( + aicon_pos, + [24,24], + g_hud7_spr, + [0,72/128], + [24/256, 24/128], + g_hud_color, + pSeat->m_flAmmo2Alpha, + DRAWFLAG_ADDITIVE + ); + + aicon_pos = g_hudmins + [g_hudres[0] - 48, g_hudres[1] - 74]; + drawsubpic( + aicon_pos, + [24,24], + g_hud7_spr, + [48/256,72/128], + [24/256, 24/128], + g_hud_color, + pSeat->m_flAmmo3Alpha, + DRAWFLAG_ADDITIVE + ); +#endif +} + +float +w_mp5_aimanim(void) +{ + return self.flags & ANIM_CR_AIMMP5 ? ANIM_CR_AIMCROWBAR : ANIM_AIMMP5; +} + +void +w_mp5_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.mp5_mag == 0 && pl.ammo_9mm == 0 && pl.ammo_m203_grenade == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud4_spr, + [0,135/256], + [170/256,45/256], + hud_col, + a, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud1_spr, + [0,135/256], + [170/256,45/256], + hud_col, + a, + DRAWFLAG_ADDITIVE + ); + } + + HUD_DrawAmmoBar(pos, pl.ammo_9mm, MAX_A_9MM, a); + HUD_DrawAmmoBar(pos + [25, 0], pl.ammo_m203_grenade, MAX_A_M203_GRENADE, a); +#endif +} + +weapon_t w_mp5 = +{ + .name = "9mmAR", + .id = ITEM_MP5, + .slot = 2, + .slot_pos = 0, + .draw = w_mp5_draw, + .holster = w_mp5_holster, + .primary = w_mp5_primary, + .secondary = w_mp5_secondary, + .reload = w_mp5_reload, + .release = w_mp5_release, + .crosshair = w_mp5_crosshair, + .precache = w_mp5_precache, + .pickup = w_mp5_pickup, + .updateammo = w_mp5_updateammo, + .wmodel = w_mp5_wmodel, + .pmodel = w_mp5_pmodel, + .deathmsg = w_mp5_deathmsg, + .aimanim = w_mp5_aimanim, + .hudpic = w_mp5_hudpic +}; + +#ifdef SERVER +void +weapon_9mmAR(void) +{ + Weapons_InitItem(WEAPON_MP5); +} + +void +weapon_mp5(void) +{ + Weapons_InitItem(WEAPON_MP5); +} +#endif diff --git a/src/shared/w_python.qc b/src/shared/w_python.qc new file mode 100644 index 0000000..a475116 --- /dev/null +++ b/src/shared/w_python.qc @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2016-2021 Marco Hladik + * + * 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_357 (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_357.mdl" + +HALF-LIFE (1998) ENTITY + +357/Python Weapon +Same as weapon_python + +*/ + +/*QUAKED weapon_python (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_357.mdl" + +HALF-LIFE (1998) ENTITY + +357/Python Weapon +Same as weapon_357 + +*/ + +enum +{ + PYTHON_IDLE1, + PYTHON_FIDGET, + PYTHON_FIRE1, + PYTHON_RELOAD, + PYTHON_HOLSTER, + PYTHON_DRAW, + PYTHON_IDLE2, + PYTHON_IDLE3 +}; + +void +w_python_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_357.shoot"); + Sound_Precache("weapon_357.reload"); + precache_model("models/w_357.mdl"); +#else + precache_model("models/v_357.mdl"); + precache_model("models/p_357.mdl"); +#endif +} + +int +w_python_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + pl.python_mag = 6; + } else { + if (pl.ammo_357 < MAX_A_357) { + pl.ammo_357 = bound(0, pl.ammo_357 + 6, MAX_A_357); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_python_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.python_mag, pl.ammo_357, -1); +} + +string +w_python_wmodel(void) +{ + return "models/w_357.mdl"; +} + +string +w_python_pmodel(void) +{ + return "models/p_357.mdl"; +} + +string +w_python_deathmsg(void) +{ + return ""; +} + +void +w_python_draw(void) +{ +#ifdef CLIENT + Weapons_SetModel("models/v_357.mdl"); + + /* singleplayer doesn't do scope */ + if (cvar("sv_playerslots") == 1) { + Weapons_SetGeomset("geomset 4 1\n"); + } else { + Weapons_SetGeomset("geomset 4 2\n"); + } + Weapons_ViewAnimation(PYTHON_DRAW); +#endif +} + +void +w_python_holster(void) +{ +#ifdef CLIENT + Weapons_ViewAnimation(PYTHON_HOLSTER); +#endif +} + +void +w_python_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + + /* Ammo check */ + if (pl.python_mag <= 0) { + return; + } + + pl.python_mag--; + + /* Actual firing */ +#ifdef CLIENT + View_SetMuzzleflash(MUZZLE_SMALL); + Weapons_ViewPunchAngle([-10,0,0]); + Weapons_ViewAnimation(PYTHON_FIRE1); +#else + TraceAttack_FireBullets(1, pl.origin + pl.view_ofs, Skill_GetValue("plr_357_bullet", 40), [0.008, 0.008], WEAPON_PYTHON); + Sound_Play(pl, CHAN_WEAPON, "weapon_357.shoot"); +#endif + + pl.w_attack_next = 0.75f; + pl.w_idle_next = 10.0f; +} + +void +w_python_secondary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + + /* singleplayer doesn't do scope */ + if (cvar("sv_playerslots") == 1) { + return; + } + + /* Simple toggle of fovs */ + if (pl.viewzoom == 1.0f) { + pl.viewzoom = 0.5f; + } else { + pl.viewzoom = 1.0f; + } + + pl.w_attack_next = 0.25f; +} + +void +w_python_reload(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + + /* Ammo check */ + if (pl.python_mag >= 6) { + return; + } + if (pl.ammo_357 <= 0) { + return; + } + + /* Audio-Visual bit */ +#ifdef CLIENT + Weapons_ViewAnimation(PYTHON_RELOAD); +#else + Sound_Play(pl, CHAN_WEAPON, "weapon_357.reload"); + Weapons_ReloadWeapon(pl, player::python_mag, player::ammo_357, 6); +#endif + + pl.w_attack_next = 3.25f; + pl.w_idle_next = 10.0f; +} + +void +w_python_release(void) +{ + player pl = (player)self; + + /* auto-reload if need be */ + if (pl.w_attack_next <= 0.0) + if (pl.python_mag == 0 && pl.ammo_357 > 0) { + Weapons_Reload(); + return; + } + + if (pl.w_idle_next > 0.0) { + return; + } + + int r = (float)input_sequence % 4; + + switch (r) { + case 1: + Weapons_ViewAnimation(PYTHON_IDLE1); + pl.w_idle_next = 2.33f; + break; + case 2: + Weapons_ViewAnimation(PYTHON_FIDGET); + pl.w_idle_next = 5.66f; + break; + case 3: + Weapons_ViewAnimation(PYTHON_IDLE2); + pl.w_idle_next = 2.0f; + break; + default: + Weapons_ViewAnimation(PYTHON_IDLE3); + pl.w_idle_next = 2.93f; + break; + } +} + +void +w_python_crosshair(void) +{ +#ifdef CLIENT + vector cross_pos; + vector aicon_pos; + + cross_pos = g_hudmins + (g_hudres / 2) + [-12,-12]; + drawsubpic( + cross_pos, + [24,24], + g_cross_spr, + [48/128,0], + [0.1875, 0.1875], + [1,1,1], + 1, + DRAWFLAG_NORMAL + ); + + HUD_DrawAmmo1(); + HUD_DrawAmmo2(); + + aicon_pos = g_hudmins + [g_hudres[0] - 48, g_hudres[1] - 42]; + drawsubpic( + aicon_pos, + [24,24], + g_hud7_spr, + [24/256,72/128], + [24/256, 24/128], + g_hud_color, + pSeat->m_flAmmo2Alpha, + DRAWFLAG_ADDITIVE + ); +#endif +} + +float +w_python_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CR_AIMPYTHON : ANIM_AIMPYTHON; +} + +void +w_python_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.python_mag == 0 && pl.ammo_357 == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud4_spr, + [0,90/256], + [170/256,45/256], + hud_col, + a, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud1_spr, + [0,90/256], + [170/256,45/256], + hud_col, + a, + DRAWFLAG_ADDITIVE + ); + } + + HUD_DrawAmmoBar(pos, pl.ammo_357, MAX_A_357, a); +#endif +} + +weapon_t w_python = +{ + .name = "357", + .id = ITEM_PYTHON, + .slot = 1, + .slot_pos = 1, + .draw = w_python_draw, + .holster = w_python_holster, + .primary = w_python_primary, + .secondary = w_python_secondary, + .reload = w_python_reload, + .release = w_python_release, + .crosshair = w_python_crosshair, + .precache = w_python_precache, + .pickup = w_python_pickup, + .updateammo = w_python_updateammo, + .wmodel = w_python_wmodel, + .pmodel = w_python_pmodel, + .deathmsg = w_python_deathmsg, + .aimanim = w_python_aimanim, + .hudpic = w_python_hudpic +}; + +/* pickups */ +#ifdef SERVER +void +weapon_357(void) +{ + Weapons_InitItem(WEAPON_PYTHON); +} + +void +weapon_python(void) +{ + Weapons_InitItem(WEAPON_PYTHON); +} +#endif diff --git a/src/shared/w_rpg.qc b/src/shared/w_rpg.qc new file mode 100644 index 0000000..18904f4 --- /dev/null +++ b/src/shared/w_rpg.qc @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2016-2021 Marco Hladik + * + * 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_rpg (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_rpg.mdl" + +HALF-LIFE (1998) ENTITY + +RPG Weapon + +*/ + +enum +{ + RPG_IDLE, + RPG_FIDGET, + RPG_RELOAD, + RPG_FIRE2, + RPG_HOLSTER1, + RPG_DRAW1, + RPG_HOLSTER2, + RPG_DRAW_UL, + RPG_IDLE_UL, + RPG_FIDGET_UL, +}; + +void w_rpg_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_rpg.shoot"); + precache_model("models/w_rpg.mdl"); + precache_model("models/rpgrocket.mdl"); +#else + precache_model("models/v_rpg.mdl"); + precache_model("models/p_rpg.mdl"); + precache_model("sprites/laserdot.spr"); +#endif +} + +void w_rpg_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.rpg_mag, pl.ammo_rocket, -1); +} +string w_rpg_wmodel(void) +{ + return "models/w_rpg.mdl"; +} +string w_rpg_pmodel(void) +{ + return "models/p_rpg.mdl"; +} +string w_rpg_deathmsg(void) +{ + return ""; +} + +int w_rpg_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + pl.rpg_mag = 1; + } else { + if (pl.ammo_rocket < MAX_A_ROCKET) { + pl.ammo_rocket = bound(0, pl.ammo_rocket + 1, MAX_A_ROCKET); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void w_rpg_draw(void) +{ +#ifdef CLIENT + Weapons_SetModel("models/v_rpg.mdl"); + Weapons_ViewAnimation(RPG_DRAW1); +#endif +} + +void w_rpg_holster(void) +{ + +} + +void w_rpg_primary(void) +{ + player pl = (player)self; + if (pl.w_attack_next > 0.0) { + return; + } + + /* Ammo check */ + if (pl.rpg_mag <= 0) { + return; + } + + pl.rpg_mag--; + +#ifdef CLIENT + Weapons_ViewAnimation(RPG_FIRE2); + Weapons_ViewPunchAngle([-10,0,0]); +#else + static void Rocket_Touch(void) { + float dmg = Skill_GetValue("plr_rpg", 100); + FX_Explosion(self.origin); + Damage_Radius(self.origin, self.owner, dmg, dmg * 2.5f, TRUE, WEAPON_RPG); + sound(self, CHAN_WEAPON, sprintf("weapons/explode%d.wav", floor(random() * 2) + 3), 1, ATTN_NORM); + remove(self); + } + static void Rocket_BuildSpeed(void){ + /* Calculate new direction */ + if (self.weapon) { + makevectors(self.owner.v_angle); + traceline(self.owner.origin, self.owner.origin + v_forward * 8096, FALSE, self.owner); + self.angles = vectoangles(trace_endpos - self.origin); + } + + /* Increase speed towards it */ + makevectors(self.angles); + self.velocity += (v_forward * 2000) * frametime; + self.nextthink = time; + } + + Weapons_MakeVectors(); + entity rocket = spawn(); + setmodel(rocket, "models/rpgrocket.mdl"); + setorigin(rocket, Weapons_GetCameraPos() + (v_forward * 16)); + rocket.owner = self; + rocket.movetype = MOVETYPE_FLY; + rocket.solid = SOLID_BBOX; + //bolt.flags |= FL_LAGGEDMOVE; + rocket.gravity = 0.5f; + rocket.velocity = (v_forward * 250); + rocket.angles = vectoangles(rocket.velocity); + rocket.avelocity[2] = 10; + rocket.touch = Rocket_Touch; + rocket.think = Rocket_BuildSpeed; + rocket.nextthink = time + 0.15f; + rocket.traileffectnum = particleeffectnum("weapon_rpg.trail"); + + if (pl.ammo_rpg_state > 0) { + rocket.weapon = 1; + } + + setsize(rocket, [0,0,0], [0,0,0]); + Sound_Play(pl, CHAN_WEAPON, "weapon_rpg.shoot"); +#endif + + pl.w_attack_next = + pl.w_idle_next = 2.5f; +} + +void w_rpg_reload(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0) { + return; + } + + /* Ammo check */ + if (pl.rpg_mag >= 1) { + return; + } + if (pl.ammo_rocket <= 0) { + return; + } + + /* Audio-Visual Bit */ +#ifdef CLIENT + Weapons_ViewAnimation(RPG_RELOAD); +#else + Weapons_ReloadWeapon(pl, player::rpg_mag, player::ammo_rocket, 1); +#endif + + pl.w_attack_next = 2.25f; + pl.w_idle_next = 10.0f; +} + +void w_rpg_release(void) +{ + player pl = (player)self; + + /* auto-reload if need be */ + if (pl.w_attack_next <= 0.0) + if (pl.rpg_mag == 0 && pl.ammo_rocket > 0) { + Weapons_Reload(); + return; + } + + if (pl.w_idle_next > 0.0) { + return; + } + + int r = (float)input_sequence % 3; + + if (pl.a_ammo1 > 0) { + if (r == 1) { + Weapons_ViewAnimation(RPG_FIDGET); + } else { + Weapons_ViewAnimation(RPG_IDLE); + } + } else { + if (r == 1) { + Weapons_ViewAnimation(RPG_FIDGET_UL); + } else { + Weapons_ViewAnimation(RPG_IDLE_UL); + } + } + + pl.w_idle_next = 6.0f; +} + +void w_rpg_secondary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + + /* toggle laser */ + pl.ammo_rpg_state = 1 - pl.ammo_rpg_state; + + pl.w_attack_next = 0.25f; + w_rpg_release(); +} + +float w_rpg_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CR_AIMRPG : ANIM_AIMRPG; +} + +void w_rpg_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.rpg_mag == 0 && pl.ammo_rocket == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + if (selected) { + drawsubpic(pos, [170,45], g_hud5_spr, [0,45/256], [170/256,45/256], hud_col, a, DRAWFLAG_ADDITIVE); + } else { + drawsubpic(pos, [170,45], g_hud2_spr, [0,45/256], [170/256,45/256], hud_col, a, DRAWFLAG_ADDITIVE); + } + + HUD_DrawAmmoBar(pos, pl.ammo_rocket, MAX_A_ROCKET, a); +#endif +} + +void w_rpg_hud(void) +{ +#ifdef CLIENT + player pl = (player)self; + vector cross_pos; + vector aicon_pos; + + /* crosshair/laser */ + if (pl.ammo_rpg_state == 1) { + float lerp; + vector jitter; + Weapons_MakeVectors(); + vector src = pl.origin + pl.view_ofs; + traceline(src, src + (v_forward * 256), FALSE, pl); + lerp = Math_Lerp(18,6, trace_fraction); + jitter[0] = (random(0,2) - 2) * (1 - trace_fraction); + jitter[1] = (random(0,2) - 2) * (1 - trace_fraction); + cross_pos = g_hudmins + (g_hudres / 2) + ([-lerp,-lerp] / 2); + drawsubpic( + cross_pos + jitter, + [lerp,lerp], + g_laser_spr, + [0,0], + [1.0, 1.0], + [1,1,1], + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + cross_pos = g_hudmins + (g_hudres / 2) + [-12,-12]; + drawsubpic( + cross_pos, + [24,24], + g_cross_spr, + [0,0], + [0.1875, 0.1875], + [1,1,1], + 1, + DRAWFLAG_NORMAL + ); + } + + /* ammo counters */ + HUD_DrawAmmo1(); + HUD_DrawAmmo2(); + + /* ammo icon */ + aicon_pos = g_hudmins + [g_hudres[0] - 48, g_hudres[1] - 42]; + drawsubpic( + aicon_pos, + [24,24], + "sprites/640hud7.spr_0.tga", + [120/256,72/128], + [24/256, 24/128], + g_hud_color, + pSeat->m_flAmmo2Alpha, + DRAWFLAG_ADDITIVE + ); +#endif +} + +weapon_t w_rpg = +{ + .name = "rpg_rocket", + .id = ITEM_RPG, + .slot = 3, + .slot_pos = 0, + .draw = w_rpg_draw, + .holster = w_rpg_holster, + .primary = w_rpg_primary, + .secondary = w_rpg_secondary, + .reload = w_rpg_reload, + .release = w_rpg_release, + .crosshair = w_rpg_hud, + .precache = w_rpg_precache, + .pickup = w_rpg_pickup, + .updateammo = w_rpg_updateammo, + .wmodel = w_rpg_wmodel, + .pmodel = w_rpg_pmodel, + .deathmsg = w_rpg_deathmsg, + .aimanim = w_rpg_aimanim, + .hudpic = w_rpg_hudpic +}; + +#ifdef SERVER +void weapon_rpg(void) { + Weapons_InitItem(WEAPON_RPG); +} +#endif diff --git a/src/shared/w_satchel.qc b/src/shared/w_satchel.qc new file mode 100644 index 0000000..348e80e --- /dev/null +++ b/src/shared/w_satchel.qc @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2016-2021 Marco Hladik + * + * 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_satchel (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_satchel.mdl" + +HALF-LIFE (1998) ENTITY + +Satchel Weapon + +*/ + +enum +{ + SATCHEL_IDLE, + SATCHEL_FIDGET, + SATCHEL_DRAW, + SATCHEL_THROW +}; + +enum +{ + RADIO_IDLE, + RADIO_FIDGET, + RADIO_DRAW, + RADIO_USE, + RADIO_HOLSTER +}; + + + +#ifdef SERVER +void +s_satchel_drop(entity master, vector src, vector vel) +{ + static void s_satchel_touch(void) + { + if (other != world) + return; + + Sound_Play(self, CHAN_BODY, "weapon_satchel.bounce"); + } + entity satch; + satch = spawn(); + satch.owner = master; + satch.classname = "satchel"; + satch.movetype = MOVETYPE_BOUNCE; + satch.solid = SOLID_BBOX; + satch.frame = 1; + satch.gravity = 0.5f; + satch.friction = 0.8f; + satch.velocity = vel; + satch.avelocity = [0,400,0]; + satch.touch = s_satchel_touch; + setmodel(satch, "models/w_satchel.mdl"); + setsize(satch, [-4,-4,-4], [4,4,4]); + setorigin(satch, src); +} + +void +s_satchel_detonate(entity master) +{ + for (entity b = world; (b = find(b, ::classname, "satchel"));) { + if (b.owner != master) + continue; + + float dmg = Skill_GetValue("plr_satchel", 150); + FX_Explosion(b.origin); + Damage_Radius(b.origin, master, dmg, dmg * 2.5f, TRUE, WEAPON_SATCHEL); + sound(b, CHAN_WEAPON, sprintf("weapons/explode%d.wav", floor(random() * 2) + 3), 1, ATTN_NORM); + remove(b); + } +} +#endif + +void +w_satchel_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.satchel_chg, pl.ammo_satchel, -1); +} + +string +w_satchel_wmodel(void) +{ + return "models/w_satchel.mdl"; +} + +string +w_satchel_pmodel(void) +{ + return "models/p_satchel.mdl"; +} + +string +w_satchel_deathmsg(void) +{ + return ""; +} + +void +w_satchel_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_satchel.bounce"); + precache_model("models/w_satchel.mdl"); +#else + precache_model("models/v_satchel.mdl"); + precache_model("models/v_satchel_radio.mdl"); + precache_model("models/p_satchel.mdl"); +#endif +} + +int +w_satchel_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (pl.ammo_satchel < MAX_A_SATCHEL) { + pl.ammo_satchel = bound(0, pl.ammo_satchel + 1, MAX_A_SATCHEL); + } else { + return FALSE; + } +#endif + return TRUE; +} + +void +w_satchel_draw(void) +{ +#ifdef CLIENT + player pl = (player)self; + if (pl.satchel_chg > 0) { + Weapons_SetModel("models/v_satchel_radio.mdl"); + Weapons_ViewAnimation(RADIO_DRAW); + + } else { + Weapons_SetModel("models/v_satchel.mdl"); + Weapons_ViewAnimation(SATCHEL_DRAW); + } +#endif +} + +void +w_satchel_holster(void) +{ + +} + +void +w_satchel_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next) { + return; + } + + /* Ammo check */ + if (pl.satchel_chg <= 0 && pl.ammo_satchel <= 0) { + return; + } + + if (pl.satchel_chg <= 0) { + Weapons_ViewAnimation(RADIO_DRAW); + } else { + Weapons_ViewAnimation(RADIO_USE); + } + +#ifdef SERVER + /* if we don't have any satchels placed yet, place one */ + if (!pl.satchel_chg) { + vector throw; + + Weapons_MakeVectors(); + throw = pl.velocity + (v_forward * 274); + s_satchel_drop(self, pl.origin, throw); + pl.satchel_chg++; + pl.ammo_satchel--; + } else { + /* detonate all we have */ + s_satchel_detonate(pl); + pl.satchel_chg = 0; + + /* no satchels left to place? just get rid of this thing */ + if (pl.ammo_satchel <= 0) { + Weapons_RemoveItem(pl, WEAPON_SATCHEL); + } + } +#else + Weapons_SetModel("models/v_satchel_radio.mdl"); + + /* same thing as the SERVER ifdef above... */ + if (!pl.satchel_chg) { + pl.satchel_chg++; + pl.ammo_satchel--; + } else { + pl.satchel_chg = 0; + } +#endif + + pl.w_attack_next = 1.0f; + pl.w_idle_next = 1.0f; +} + +void +w_satchel_secondary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next) { + return; + } + + /* Ammo check */ + if (pl.ammo_satchel <= 0) { + return; + } + +#ifdef SERVER + vector throw; + Weapons_MakeVectors(); + throw = pl.velocity + (v_forward * 274); + s_satchel_drop(self, pl.origin, throw); +#else + Weapons_SetModel("models/v_satchel_radio.mdl"); + Weapons_ViewAnimation(RADIO_DRAW); +#endif + + pl.satchel_chg++; + pl.ammo_satchel--; + + pl.w_attack_next = 1.0f; + pl.w_idle_next = 2.5f; +} + +void +w_satchel_reload(void) +{ + +} + +void +w_satchel_release(void) +{ + player pl = (player)self; + + if (pl.w_idle_next > 0.0) { + return; + } + + if (pl.satchel_chg <= 0) { + Weapons_ViewAnimation(SATCHEL_FIDGET); + } else { + Weapons_ViewAnimation(RADIO_FIDGET); + } + pl.w_idle_next = 15.0f; +} + +float +w_satchel_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CR_AIMSQUEAK : ANIM_AIMSQUEAK; +} + +void +w_satchel_hud(void) +{ +#ifdef CLIENT + HUD_DrawAmmo2(); + vector aicon_pos = g_hudmins + [g_hudres[0] - 48, g_hudres[1] - 42]; + drawsubpic(aicon_pos, [24,24], g_hud7_spr, [72/256,96/128], [24/256, 24/128], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_satchel_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + + if (selected) { + drawsubpic(pos, [170,45], g_hud6_spr, [0,45/256], [170/256,45/256], g_hud_color, a, DRAWFLAG_ADDITIVE); + } else { + drawsubpic(pos, [170,45], g_hud3_spr, [0,45/256], [170/256,45/256], g_hud_color, a, DRAWFLAG_ADDITIVE); + } + + HUD_DrawAmmoBar(pos, pl.ammo_satchel, MAX_A_SATCHEL, a); +#endif +} + +weapon_t w_satchel = +{ + .name = "satchel", + .id = ITEM_SATCHEL, + .slot = 4, + .slot_pos = 1, + .draw = w_satchel_draw, + .holster = w_satchel_holster, + .primary = w_satchel_primary, + .secondary = w_satchel_secondary, + .reload = w_satchel_reload, + .release = w_satchel_release, + .crosshair = w_satchel_hud, + .precache = w_satchel_precache, + .pickup = w_satchel_pickup, + .updateammo = w_satchel_updateammo, + .wmodel = w_satchel_wmodel, + .pmodel = w_satchel_pmodel, + .deathmsg = w_satchel_deathmsg, + .aimanim = w_satchel_aimanim, + .hudpic = w_satchel_hudpic +}; + +#ifdef SERVER +void +weapon_satchel(void) +{ + Weapons_InitItem(WEAPON_SATCHEL); +} +#endif diff --git a/src/shared/w_shotgun.qc b/src/shared/w_shotgun.qc new file mode 100644 index 0000000..4471991 --- /dev/null +++ b/src/shared/w_shotgun.qc @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2016-2021 Marco Hladik + * + * 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_shotgun (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_shotgun.mdl" + +HALF-LIFE (1998) ENTITY + +Shotgun Weapon + +*/ + +enum +{ + SHOTGUN_IDLE1, + SHOTGUN_FIRE1, + SHOTGUN_FIRE2, + SHOTGUN_RELOAD, + SHOTGUN_PUMP, + SHOTGUN_START_RELOAD, + SHOTGUN_DRAW, + SHOTGUN_HOLSTER, + SHOTGUN_IDLE2, + SHOTGUN_IDLE3 +}; + +enum +{ + SHOTTY_IDLE, + SHOTTY_RELOAD_START, + SHOTTY_RELOAD, + SHOTTY_RELOAD_END +}; + +void w_shotgun_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_shotgun.single"); + Sound_Precache("weapon_shotgun.double"); + Sound_Precache("weapon_shotgun.reload"); + Sound_Precache("weapon_shotgun.cock"); + precache_model("models/w_shotgun.mdl"); +#else + precache_model("models/v_shotgun.mdl"); + precache_model("models/p_shotgun.mdl"); +#endif +} + +void +w_shotgun_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.shotgun_mag, pl.ammo_buckshot, -1); +} + +string +w_shotgun_wmodel(void) +{ + return "models/w_shotgun.mdl"; +} + +string +w_shotgun_pmodel(void) +{ + return "models/p_shotgun.mdl"; +} + +string +w_shotgun_deathmsg(void) +{ + return ""; +} + +int +w_shotgun_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + pl.shotgun_mag = 8; + } else { + if (pl.ammo_buckshot < MAX_A_BUCKSHOT) { + pl.ammo_buckshot = bound(0, pl.ammo_buckshot + 8, MAX_A_BUCKSHOT); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_shotgun_draw(void) +{ + player pl = (player)self; + pl.mode_tempstate = 0; + +#ifdef CLIENT + Weapons_SetModel("models/v_shotgun.mdl"); + Weapons_ViewAnimation(SHOTGUN_DRAW); +#endif +} + +void +w_shotgun_holster(void) +{ + Weapons_ViewAnimation(SHOTGUN_HOLSTER); +} + +void +w_shotgun_primary(void) +{ + player pl = (player)self; + if (pl.w_attack_next) { + return; + } + + if (pl.mode_tempstate > SHOTTY_IDLE) { + return; + } + + /* Ammo check */ + if (pl.shotgun_mag <= 0) { + return; + } + +#ifdef SERVER + /* Singleplayer is more accurate */ + if (cvar("sv_playerslots") == 1) { + TraceAttack_FireBullets(6, pl.origin + pl.view_ofs, Skill_GetValue("plr_buckshot", 5), [0.08716,0.08716], WEAPON_SHOTGUN); + } else { + TraceAttack_FireBullets(4, pl.origin + pl.view_ofs, Skill_GetValue("plr_buckshot", 5), [0.08716,0.04362], WEAPON_SHOTGUN); + } + Sound_Play(pl, CHAN_WEAPON, "weapon_shotgun.single"); +#else + View_SetMuzzleflash(MUZZLE_WEIRD); + Weapons_ViewAnimation(SHOTGUN_FIRE1); + Weapons_ViewPunchAngle([-5,0,0]); +#endif + + pl.shotgun_mag--; + + pl.w_attack_next = 0.75; + pl.w_idle_next = 2.5f; +} + +void +w_shotgun_secondary(void) +{ + player pl = (player)self; + if (pl.w_attack_next) { + return; + } + + if (pl.mode_tempstate > SHOTTY_IDLE) { + return; + } + + /* Ammo check */ + if (pl.shotgun_mag <= 1) { + return; + } + +#ifdef SERVER + /* Singleplayer is more accurate */ + if (cvar("sv_playerslots") == 1) { + TraceAttack_FireBullets(12, pl.origin + pl.view_ofs, 5, [0.08716,0.08716], WEAPON_SHOTGUN); + } else { + TraceAttack_FireBullets(8, pl.origin + pl.view_ofs, 5, [0.17365,0.04362], WEAPON_SHOTGUN); + } + Sound_Play(pl, CHAN_WEAPON, "weapon_shotgun.double"); + Weapons_UpdateAmmo(pl, pl.shotgun_mag, pl.ammo_buckshot, __NULL__); +#else + Weapons_ViewAnimation(SHOTGUN_FIRE2); + Weapons_ViewPunchAngle([-10,0,0]); +#endif + + pl.shotgun_mag -= 2; + pl.w_attack_next = 1.5f; + pl.w_idle_next = 2.5f; +} + +void +w_shotgun_reload(void) +{ + player pl = (player)self; + + if (pl.shotgun_mag >= 8) { + return; + } + if (pl.ammo_buckshot <= 0) { + return; + } + + if (pl.mode_tempstate > SHOTTY_IDLE) { + return; + } + pl.mode_tempstate = SHOTTY_RELOAD_START; + pl.w_idle_next = 0.0f; +} + +void +w_shotgun_release(void) +{ + player pl = (player)self; + + /* auto-reload if need be */ + if (pl.w_attack_next <= 0.0) + if (pl.mode_tempstate == SHOTTY_IDLE && pl.shotgun_mag == 0 && pl.ammo_buckshot > 0) { + Weapons_Reload(); + return; + } + + if (pl.w_idle_next > 0.0) { + return; + } + + if (pl.mode_tempstate == SHOTTY_IDLE) { + int r = (float)input_sequence % 3; + switch (r) { + case 1: + Weapons_ViewAnimation(SHOTGUN_IDLE2); + pl.w_idle_next = 2.222222f; + break; + case 2: + Weapons_ViewAnimation(SHOTGUN_IDLE3); + pl.w_idle_next = 5.0f; + break; + default: + Weapons_ViewAnimation(SHOTGUN_IDLE1); + pl.w_idle_next = 2.222222f; + break; + } + } else if (pl.mode_tempstate == SHOTTY_RELOAD_START) { + Weapons_ViewAnimation(SHOTGUN_START_RELOAD); + pl.mode_tempstate = SHOTTY_RELOAD; + pl.w_idle_next = 0.65f; + } else if (pl.mode_tempstate == SHOTTY_RELOAD) { + Weapons_ViewAnimation(SHOTGUN_RELOAD); + pl.shotgun_mag++; + pl.ammo_buckshot--; +#ifdef SERVER + Sound_Play(pl, CHAN_WEAPON, "weapon_shotgun.reload"); +#endif + if (pl.ammo_buckshot <= 0 || pl.shotgun_mag >= 8) { + pl.mode_tempstate = SHOTTY_RELOAD_END; + } + Weapons_UpdateAmmo(pl, pl.shotgun_mag, pl.ammo_buckshot, pl.mode_tempstate); + pl.w_idle_next = 0.5f; + } else if (pl.mode_tempstate == SHOTTY_RELOAD_END) { + Weapons_ViewAnimation(SHOTGUN_PUMP); +#ifdef SERVER + Sound_Play(pl, CHAN_WEAPON, "weapon_shotgun.cock"); +#endif + pl.mode_tempstate = SHOTTY_IDLE; + pl.w_idle_next = 10.0f; + pl.w_attack_next = 0.5f; + } +} + +void +w_shotgun_crosshair(void) +{ +#ifdef CLIENT + static vector cross_pos; + cross_pos = g_hudmins + (g_hudres / 2) + [-12,-12]; + drawsubpic(cross_pos, [24,24], g_cross_spr, [48/128,24/128], [0.1875, 0.1875], [1,1,1], 1, DRAWFLAG_NORMAL); + HUD_DrawAmmo1(); + HUD_DrawAmmo2(); + vector aicon_pos = g_hudmins + [g_hudres[0] - 48, g_hudres[1] - 42]; + drawsubpic(aicon_pos, [24,24], g_hud7_spr, [72/256,72/128], [24/256, 24/128], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +float +w_shotgun_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CR_AIMSHOTGUN : ANIM_AIMSHOTGUN; +} + +void +w_shotgun_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.shotgun_mag == 0 && pl.ammo_buckshot == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + if (selected) { + drawsubpic(pos, [170,45], g_hud4_spr, [0,180/256], [170/256,45/256], hud_col, a, DRAWFLAG_ADDITIVE); + } else { + drawsubpic(pos, [170,45], g_hud1_spr, [0,180/256], [170/256,45/256], hud_col, a, DRAWFLAG_ADDITIVE); + } + + HUD_DrawAmmoBar(pos, pl.ammo_buckshot, MAX_A_BUCKSHOT, a); +#endif +} + +weapon_t w_shotgun = +{ + .name = "shotgun", + .id = ITEM_SHOTGUN, + .slot = 2, + .slot_pos = 1, + .draw = w_shotgun_draw, + .holster = w_shotgun_holster, + .primary = w_shotgun_primary, + .secondary = w_shotgun_secondary, + .reload = w_shotgun_reload, + .release = w_shotgun_release, + .crosshair = w_shotgun_crosshair, + .precache = w_shotgun_precache, + .pickup = w_shotgun_pickup, + .updateammo = w_shotgun_updateammo, + .wmodel = w_shotgun_wmodel, + .pmodel = w_shotgun_pmodel, + .deathmsg = w_shotgun_deathmsg, + .aimanim = w_shotgun_aimanim, + .hudpic = w_shotgun_hudpic +}; + +#ifdef SERVER +void +weapon_shotgun(void) +{ + Weapons_InitItem(WEAPON_SHOTGUN); +} +#endif diff --git a/src/shared/w_snark.qc b/src/shared/w_snark.qc new file mode 100644 index 0000000..4b7c15d --- /dev/null +++ b/src/shared/w_snark.qc @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2016-2021 Marco Hladik + * + * 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_snark (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_squeak.mdl" + +HALF-LIFE (1998) ENTITY + +Snark/Squeakgrenade Weapon + +*/ + +enum +{ + SNARK_IDLE, + SNARK_FIDGET1, + SNARK_FIDGET2, + SNARK_HOLSTER, + SNARK_DRAW, + SNARK_THROW +}; + +#ifdef SERVER +class monster_snark:CBaseMonster +{ + float m_flJump; + entity m_eTarget; + + void(void) monster_snark; + + virtual void(void) customphysics; + virtual void(void) Death; + virtual void(void) Respawn; +}; + +void +monster_snark::customphysics(void) +{ + input_movevalues = [250,0,0]; + input_buttons = 0; + input_impulse = 0; + input_angles = angles; + input_timelength = frametime; + + if (health <= 0) { + return; + } + + if (m_flJump <= 0.0 && m_eTarget == __NULL__) { + float shortest = 999999; + for (entity ef = world; (ef = findfloat(ef, ::movetype, MOVETYPE_WALK));) { + float len = vlen(ef.origin - origin); + if (ef.classname != "monster_snark" && len < shortest && ef.health > 0) { + owner = __NULL__; + m_eTarget = ef; + shortest = len; + } + } + } + + if (m_eTarget) { + angles = input_angles = vectoangles(m_eTarget.origin - origin); + } + + if (m_eTarget && m_flJump <= 0.0) { + m_flJump = 0.5f + random(); + Sound_Play(this, CHAN_VOICE, "weapon_snark.hunt"); + input_buttons = 2; + Damage_Apply(this, world, 1, 0, DMG_GENERIC); + + makevectors(angles); + traceline(origin, origin + (v_forward * 128), 0, this); + + if (trace_ent.takedamage == DAMAGE_YES) { + Sound_Play(this, CHAN_BODY, "weapon_snark.deploy"); + Damage_Apply(trace_ent, real_owner, Skill_GetValue("snark_dmg_bite", 10), WEAPON_SNARK, DMG_GENERIC); + FX_Blood(origin + [0,0,16], [1,0,0]); + } + + if (m_eTarget.health <= 0) { + m_eTarget = __NULL__; + } + } + + m_flJump -= frametime; + runstandardplayerphysics(this); +} + +void +monster_snark::Death(void) +{ + float dmg = Skill_GetValue("snark_dmg_pop", 5); + Damage_Radius(origin, goalentity, dmg, dmg * 2.5f, TRUE, WEAPON_SNARK); + FX_Blood(origin + [0,0,16], [203,183,15] / 255); + Sound_Play(this, CHAN_VOICE, "weapon_snark.die"); + Sound_Play(this, CHAN_BODY, "weapon_snark.blast"); + customphysics = __NULL__; + remove(this); +} + +void +monster_snark::Respawn(void) +{ + netname = "Snark"; + SetModel("models/w_squeak.mdl"); + flags |= FL_MONSTER; + SetSolid(SOLID_BBOX); + SetMovetype(MOVETYPE_WALK); + SetFrame(3); /* running like crazy. */ + angles = goalentity.angles; + health = 20; + takedamage = DAMAGE_YES; + m_eTarget = __NULL__; + m_flJump = 1.0f; +} + +void +monster_snark::monster_snark(void) +{ + Respawn(); +} +#endif + +int w_snark_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (pl.ammo_snark < MAX_A_SNARK) { + pl.ammo_snark = bound(0, pl.ammo_snark + 5, MAX_A_SNARK); + } else { + return FALSE; + } +#endif + return TRUE; +} + +void w_snark_draw(void) +{ + Weapons_SetModel("models/v_squeak.mdl"); + Weapons_ViewAnimation(SNARK_DRAW); +} + +void w_snark_holster(void) +{ + +} + +#ifdef SERVER +void w_snark_deploy(void) +{ + monster_snark snark = spawn(monster_snark, real_owner: self, goalentity: self, spawnflags: MSF_MULTIPLAYER); + makevectors(self.v_angle); + snark.SetOrigin(self.origin + v_forward * 32); +} +#endif + +void w_snark_primary(void) +{ + player pl = (player)self; + if (pl.w_attack_next > 0.0) { + return; + } + + /* Ammo check */ + if (pl.ammo_snark <= 0) { + return; + } + + pl.ammo_snark--; + + /* Audio-Visual Bit */ +#ifdef CLIENT + Weapons_ViewAnimation(SNARK_THROW); +#else + w_snark_deploy(); + + if (pl.ammo_snark <= 0) { + Weapons_RemoveItem(pl, WEAPON_SNARK); + } +#endif + + pl.w_idle_next = 1.0f; + pl.w_attack_next = 0.25f; + +} +void w_snark_secondary(void) +{ + +} +void w_snark_reload(void) +{ + +} +void w_snark_release(void) +{ + int r; + player pl = (player)self; + if (pl.w_idle_next > 0.0) { + return; + } + + r = (float)input_sequence % 3; + switch (r) { + case 0: + Weapons_ViewAnimation(SNARK_IDLE); + pl.w_idle_next = 1.875f; + break; + case 1: + Weapons_ViewAnimation(SNARK_FIDGET1); + pl.w_idle_next = 4.375f; + break; + default: + Weapons_ViewAnimation(SNARK_FIDGET2); + pl.w_idle_next = 5.0f; + break; + } +} +void w_snark_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_snark.deploy"); + Sound_Precache("weapon_snark.die"); + Sound_Precache("weapon_snark.blast"); + Sound_Precache("weapon_snark.hunt"); + precache_model("models/w_sqknest.mdl"); + precache_model("models/w_squeak.mdl"); +#else + precache_model("models/p_squeak.mdl"); + precache_model("models/v_squeak.mdl"); +#endif +} +void w_snark_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, -1, pl.ammo_snark, -1); +} +string w_snark_wmodel(void) +{ + return "models/w_sqknest.mdl"; +} +string w_snark_pmodel(void) +{ + return "models/p_squeak.mdl"; +} +string w_snark_deathmsg(void) +{ + return ""; +} + + +float w_snark_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CR_AIMSQUEAK : ANIM_AIMSQUEAK; +} + +void w_snark_hud(void) +{ +#ifdef CLIENT + HUD_DrawAmmo2(); + vector aicon_pos = g_hudmins + [g_hudres[0] - 48, g_hudres[1] - 42]; + drawsubpic(aicon_pos, [24,24], g_hud7_spr, [96/256,96/128], [24/256, 24/128], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void w_snark_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + + if (selected) { + drawsubpic(pos, [170,45], g_hud6_spr, + [0,135/256], [170/256,45/256], + g_hud_color, a, DRAWFLAG_ADDITIVE); + } else { + drawsubpic(pos, [170,45], g_hud3_spr, + [0,135/256], [170/256,45/256], + g_hud_color, a, DRAWFLAG_ADDITIVE); + } + + HUD_DrawAmmoBar(pos, pl.ammo_snark, MAX_A_SNARK, a); +#endif +} + +weapon_t w_snark = +{ + .name = "snark", + .id = ITEM_SNARK, + .slot = 4, + .slot_pos = 3, + .draw = w_snark_draw, + .holster = w_snark_holster, + .primary = w_snark_primary, + .secondary = w_snark_secondary, + .reload = w_snark_reload, + .release = w_snark_release, + .crosshair = w_snark_hud, + .precache = w_snark_precache, + .pickup = w_snark_pickup, + .updateammo = w_snark_updateammo, + .wmodel = w_snark_wmodel, + .pmodel = w_snark_pmodel, + .deathmsg = w_snark_deathmsg, + .aimanim = w_snark_aimanim, + .hudpic = w_snark_hudpic +}; + +#ifdef SERVER +void weapon_snark(void) { + Weapons_InitItem(WEAPON_SNARK); +} +#endif diff --git a/src/shared/w_tripmine.qc b/src/shared/w_tripmine.qc new file mode 100644 index 0000000..170c8ed --- /dev/null +++ b/src/shared/w_tripmine.qc @@ -0,0 +1,400 @@ +/* + * Copyright (c) 2016-2021 Marco Hladik + * + * 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_tripmine (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/v_tripmine.mdl" + +HALF-LIFE (1998) ENTITY + +Tripmine Weapon + +*/ + +enum +{ + TRIPMINE_IDLE1, + TRIPMINE_IDLE2, + TRIPMINE_FIRE1, + TRIPMINE_FIRE2, + TRIPMINE_FIDGET, + TRIPMINE_HOLSTER, + TRIPMINE_DRAW, + TRIPMINE_WORLD, + TRIPMINE_GROUND, +}; + +/* MONSTER_TRIPMINE SEGMENT + * + * Because not being able to place it around levels would be boring. + * Some maps, such as subtransit and a few singleplayer chapters have this. */ + +#ifdef SERVER +class monster_tripmine:CBaseMonster +{ + int m_iDist; + void(void) monster_tripmine; + + virtual float(entity, float) SendEntity; + virtual void(int) Trip; + virtual void(void) Damaged; + virtual void(void) Ready; + virtual void(void) Respawn; +}; + +float +monster_tripmine::SendEntity(entity pvsent, float flags) +{ + WriteByte(MSG_ENTITY, ENT_TRIPMINE); + WriteCoord(MSG_ENTITY, origin[0]); + WriteCoord(MSG_ENTITY, origin[1]); + WriteCoord(MSG_ENTITY, origin[2]); + WriteCoord(MSG_ENTITY, angles[0]); + WriteCoord(MSG_ENTITY, angles[1]); + WriteCoord(MSG_ENTITY, angles[2]); + WriteByte(MSG_ENTITY, health); + WriteShort(MSG_ENTITY, modelindex); + return TRUE; +} + +void +monster_tripmine::Trip(int walkthrough) +{ + float dmg; + if (!walkthrough) { + real_owner = g_eAttacker; + } + + /* This is to prevent infinite loops in Damage_Radius */ + Death = + Pain = __NULL__; + takedamage = DAMAGE_NO; + dmg = Skill_GetValue("plr_tripmine", 150); + FX_Explosion(origin); + Damage_Radius(origin, real_owner, dmg, dmg * 2.5f, TRUE, WEAPON_TRIPMINE); + sound(this, CHAN_WEAPON, sprintf("weapons/explode%d.wav", floor(random() * 2) + 3), 1, ATTN_NORM); + remove(this); +} + +void +monster_tripmine::Damaged(void) +{ + Trip(0); +} + +void +monster_tripmine::Ready(void) +{ + makevectors(angles); + traceline(origin, origin + v_forward * 2048, FALSE, this); + SetSolid(SOLID_BBOX); + + /* first time we're marked as ready, we play a sound and set the distance */ + if (!health) { + SendFlags = -1; + health = 1; + Death = + Pain = Damaged; + takedamage = DAMAGE_YES; + m_iDist = (int)trace_plane_dist; + Sound_Play(this, CHAN_WEAPON, "weapon_tripmine.activate"); + } + + if ((int)trace_plane_dist != m_iDist) { + Trip(1); + } + nextthink = time; +} + +void +monster_tripmine::Respawn(void) +{ + SetModel("models/v_tripmine.mdl"); + SetSolid(SOLID_NOT); + SetMovetype(MOVETYPE_NONE); + SetSize([-8,-8,-8], [8,8,8]); + SetOrigin(origin); + SendFlags = 1; /* force update */ + + /* ready in 4 seconds flat */ + think = Ready; + + /* fast beam */ + if (spawnflags & 1) { + nextthink = time; + } else { + nextthink = time + 4.0f; + } +} + +void +monster_tripmine::monster_tripmine(void) +{ + Respawn(); +} +#else +class csitem_tripmine +{ + int m_iActive; + + void(void) csitem_tripmine; + virtual float(void) predraw; +}; + +float csitem_tripmine::predraw(void) +{ + if (m_iActive) { + makevectors(angles); + traceline(origin, origin + v_forward * 8196, FALSE, this); + trailparticles(BEAM_TRIPMINE, this, origin, trace_endpos); + } + + addentity(this); + return PREDRAW_NEXT; +} + +void +csitem_tripmine::csitem_tripmine(void) +{ + solid = SOLID_BBOX; + movetype = MOVETYPE_NONE; + drawmask = MASK_ENGINE; + frame = TRIPMINE_WORLD; +} + +void w_tripmine_parse(void) +{ + csitem_tripmine tm = (csitem_tripmine)self; + spawnfunc_csitem_tripmine(); + + tm.origin[0] = readcoord(); + tm.origin[1] = readcoord(); + tm.origin[2] = readcoord(); + tm.angles[0] = readcoord(); + tm.angles[1] = readcoord(); + tm.angles[2] = readcoord(); + tm.m_iActive = readbyte(); + tm.modelindex = readshort(); + + setcustomskin(tm, "", "geomset 0 2\ngeomset 1 2\n"); + setorigin(tm, tm.origin); + setsize(tm, [-8,-8,-8], [8,8,8]); +} +#endif + +/* The WEAPON_TRIPMINE code + * + * Here is where the actual 'weapon' logic happens that the player itself + * runs. It obviously won't work without MONSTER_TRIPMINE */ + +void w_tripmine_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_tripmine.deploy"); + Sound_Precache("weapon_tripmine.charge"); + Sound_Precache("weapon_tripmine.activate"); + precache_model("models/v_tripmine.mdl"); +#else + precache_model("models/p_tripmine.mdl"); +#endif +} + +void w_tripmine_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, -1, pl.ammo_tripmine, -1); +} + +string w_tripmine_wmodel(void) +{ + return "models/v_tripmine.mdl"; +} + +string w_tripmine_pmodel(void) +{ + return "models/p_tripmine.mdl"; +} + +string w_tripmine_deathmsg(void) +{ + return ""; +} + +int w_tripmine_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (pl.ammo_tripmine < MAX_A_TRIPMINE) { + pl.ammo_tripmine = bound(0, pl.ammo_tripmine + 1, MAX_A_TRIPMINE); + } else { + return FALSE; + } +#endif + return TRUE; +} + +void w_tripmine_draw(void) +{ +#ifdef CLIENT + Weapons_SetModel("models/v_tripmine.mdl"); + Weapons_ViewAnimation(TRIPMINE_DRAW); +#endif +} + +void w_tripmine_holster(void) +{ + +} + +void +w_tripmine_primary(void) +{ + player pl = (player)self; + vector src; + + if (pl.w_attack_next > 0.0) { + return; + } + + if (pl.ammo_tripmine <= 0) { + return; + } + + src = Weapons_GetCameraPos(); + Weapons_MakeVectors(); + traceline(src, src + v_forward * 64, FALSE, pl); + + if (trace_fraction >= 1.0) { + return; + } + + pl.ammo_tripmine--; + +#ifdef CLIENT + Weapons_ViewAnimation(TRIPMINE_FIRE2); +#else + vector ang = vectoangles(trace_plane_normal); + monster_tripmine mine = spawn(monster_tripmine, real_owner: pl, angles: ang, spawnflags: MSF_MULTIPLAYER); + mine.SetOrigin(trace_endpos - (v_forward * 8)); + + Sound_Play(pl, CHAN_WEAPON, "weapon_tripmine.deploy"); + Sound_Play(mine, CHAN_WEAPON, "weapon_tripmine.charge"); +#endif + + pl.a_ammo3 = 1; + pl.w_attack_next = + pl.w_idle_next = 0.5f; +} + +void +w_tripmine_release(void) +{ + player pl = (player)self; + + if (pl.w_idle_next > 0.0) { + return; + } + + if (pl.a_ammo3 == 1) { + Weapons_ViewAnimation(TRIPMINE_DRAW); +#ifdef SERVER + if (pl.ammo_tripmine <= 0) { + Weapons_RemoveItem(pl, WEAPON_TRIPMINE); + } +#endif + pl.a_ammo3 = 0; + pl.w_attack_next = 0.5f; + pl.w_idle_next = 3.0f; + return; + } + + int r = (float)input_sequence % 3; + switch (r) { + case 1: + Weapons_ViewAnimation(TRIPMINE_IDLE1); + pl.w_idle_next = 3.0f; + break; + case 2: + Weapons_ViewAnimation(TRIPMINE_IDLE2); + pl.w_idle_next = 2.0f; + break; + default: + Weapons_ViewAnimation(TRIPMINE_FIDGET); + pl.w_idle_next = 3.34f; + break; + } +} + +float +w_tripmine_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CR_AIMTRIPMINE : ANIM_AIMTRIPMINE; +} + +void +w_tripmine_hud(void) +{ +#ifdef CLIENT + HUD_DrawAmmo2(); + vector aicon_pos = g_hudmins + [g_hudres[0] - 48, g_hudres[1] - 42]; + drawsubpic(aicon_pos, [24,24], g_hud7_spr, [120/256,96/128], [24/256, 24/128], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_tripmine_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + + if (selected) { + drawsubpic(pos, [170,45], g_hud6_spr, [0,90/256], [170/256,45/256], g_hud_color, a, DRAWFLAG_ADDITIVE); + } else { + drawsubpic(pos, [170,45], g_hud3_spr, [0,90/256], [170/256,45/256], g_hud_color, a, DRAWFLAG_ADDITIVE); + } + + HUD_DrawAmmoBar(pos, pl.ammo_tripmine, MAX_A_TRIPMINE, a); +#endif +} + +weapon_t w_tripmine = +{ + .name = "tripmine", + .id = ITEM_TRIPMINE, + .slot = 4, + .slot_pos = 2, + .draw = w_tripmine_draw, + .holster = w_tripmine_holster, + .primary = w_tripmine_primary, + .secondary = __NULL__, + .reload = __NULL__, + .release = w_tripmine_release, + .crosshair = w_tripmine_hud, + .precache = w_tripmine_precache, + .pickup = w_tripmine_pickup, + .updateammo = w_tripmine_updateammo, + .wmodel = w_tripmine_wmodel, + .pmodel = w_tripmine_pmodel, + .deathmsg = w_tripmine_deathmsg, + .aimanim = w_tripmine_aimanim, + .hudpic = w_tripmine_hudpic +}; + +#ifdef SERVER +void +weapon_tripmine(void) { + Weapons_InitItem(WEAPON_TRIPMINE); +} +#endif diff --git a/src/shared/weapon_common.h b/src/shared/weapon_common.h new file mode 100644 index 0000000..9aafd65 --- /dev/null +++ b/src/shared/weapon_common.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +typedef struct +{ + string name; + int id; /* bitflag id */ + int slot; + int slot_pos; + int allow_drop; + + void(void) draw; + void(void) holster; + void(void) primary; + void(void) secondary; + void(void) reload; + void(void) release; + void(void) crosshair; + + void(void) precache; + int(int, int) pickup; + void(player) updateammo; + string() wmodel; + string() pmodel; + string() deathmsg; + float() aimanim; + void(int, vector, float) hudpic; +} weapon_t; + +float Weapons_GetAim(int); +void Weapons_Reload(void); +void Weapons_DrawCrosshair(void); +void Weapons_MakeVectors(void); +vector Weapons_GetCameraPos(void); +void Weapons_ViewAnimation(int); +void Weapons_ViewPunchAngle(vector); +void Weapons_PlaySound(entity, float, string, float, float); +int Weapons_IsPresent(player, int); +void Weapons_SetModel(string); +void Weapons_SetGeomset(string); +void Weapons_UpdateAmmo(base_player, int, int, int); + +#ifdef CLIENT +string Weapons_GetPlayermodel(int); +int Weapons_GetAnimation(void); +void Weapons_HUDPic(int, int, vector, float); +#endif diff --git a/src/shared/weapon_common.qc b/src/shared/weapon_common.qc new file mode 100644 index 0000000..8d0b9cd --- /dev/null +++ b/src/shared/weapon_common.qc @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +#ifdef SERVER +void Decals_Init(void); +#endif + +void Weapons_Init(void) +{ + /* in the future we'll have no internal weapon table, then this will fill + * one up... */ + /*searchhandle sh; + filestream fh; + string line; + sh = search_begin("scripts/weapon_*.txt", TRUE, TRUE); + for (int i = 0; i < search_getsize(sh); i++) { + fh = fopen(search_getfilename(sh, i), FILE_READ); + if (fh < 0) { + continue; + } + + while ((line = fgets(fh))) { + int w = tokenize(line); + switch (argv(0)) { + case "name": + break; + case "slot": + break; + case "slot_pos": + break; + } + } + fclose(fh); + }*/ + + for (int i = 0; i < g_weapons.length; i++) { + if (g_weapons[i].precache != __NULL__) { + g_weapons[i].precache(); + } + } +} + +void Weapons_SetModel(string mdl) +{ +#ifdef CLIENT + setmodel(pSeat->m_eViewModel, mdl); +#endif +} + +void Weapons_SetGeomset(string set) +{ +#ifdef CLIENT + setcustomskin(pSeat->m_eViewModel, "", set); +#endif +} + +void Weapons_Draw(void) +{ + player pl = (player)self; + int i = pl.activeweapon; + + pl.w_attack_next = 0.5f; + pl.w_idle_next = 2.5f; + pl.viewzoom = 1.0f; + +#ifdef CLIENT + entity eold = self; + self = pSeat->m_pWeaponFX; + spawnfunc_CBaseFX(); + self = eold; +#endif + + /* make sure this is all wiped */ + pl.a_ammo1 = pl.a_ammo2 = pl.a_ammo3 = 0; + + if (g_weapons[i].draw != __NULL__) { + g_weapons[i].draw(); + } + + if (g_weapons[i].updateammo != __NULL__) { + g_weapons[i].updateammo(pl); + } + +} + +void Weapons_Holster(void) +{ + player pl = (player)self; + int i = pl.activeweapon; + if (g_weapons[i].holster != __NULL__) { + g_weapons[i].holster(); + } +} + +void Weapons_Primary(void) +{ + player pl = (player)self; + int i = pl.activeweapon; + + if (pl.flags & FL_NOATTACK) + return; + + if (g_weapons[i].primary != __NULL__) { + g_weapons[i].primary(); + } + + if (g_weapons[i].updateammo != __NULL__) { + g_weapons[i].updateammo(pl); + } +} + +void Weapons_AmmoUpdate(entity target) +{ + player pl = (player)target; + int i = pl.activeweapon; + + if (g_weapons[i].updateammo != __NULL__) { + g_weapons[i].updateammo(pl); + } +} + +void Weapons_Secondary(void) +{ + player pl = (player)self; + int i = pl.activeweapon; + + if (pl.flags & FL_NOATTACK) + return; + + if (g_weapons[i].secondary != __NULL__) { + g_weapons[i].secondary(); + } + + if (g_weapons[i].updateammo != __NULL__) { + g_weapons[i].updateammo(pl); + } + +} + +void Weapons_Reload(void) +{ + player pl = (player)self; + int i = pl.activeweapon; + + if (pl.flags & FL_NOATTACK) + return; + + if (g_weapons[i].reload != __NULL__) { + g_weapons[i].reload(); + } + + if (g_weapons[i].updateammo != __NULL__) { + g_weapons[i].updateammo(pl); + } +} + +void Weapons_Release(void) +{ + player pl = (player)self; + int i = pl.activeweapon; + if (g_weapons[i].release != __NULL__) { + g_weapons[i].release(); + } + + pl.gflags &= ~GF_SEMI_TOGGLED; +} + +void Weapons_DrawCrosshair(void) +{ + player pl = (player)self; + int i = pl.activeweapon; + if (g_weapons[i].crosshair != __NULL__) { + g_weapons[i].crosshair(); + } +} + +string Weapons_GetWorldmodel(int id) +{ + if (g_weapons[id].wmodel != __NULL__) { + return g_weapons[id].wmodel(); + } + + return ""; +} + +string Weapons_GetPlayermodel(int id) +{ + if (g_weapons[id].pmodel != __NULL__) { + return g_weapons[id].pmodel(); + } + + return ""; +} + +string Weapons_GetDeathmessage(int id) +{ + if (g_weapons[id].deathmsg != __NULL__) { + return g_weapons[id].deathmsg(); + } + + return ""; +} + +float Weapons_GetAim(int id) +{ + if (g_weapons[id].aimanim != __NULL__) { + return g_weapons[id].aimanim(); + } + + return 0; +} + +#ifdef CLIENT +void Weapons_HUDPic(int id, int s, vector pos, float a) +{ + if (g_weapons[id].hudpic != __NULL__) { + g_weapons[id].hudpic(s, pos, a); + } +} +#endif + +void Weapons_MakeVectors(void) +{ +#ifdef SERVER + player pl = (player)self; + makevectors(pl.v_angle); +#else + makevectors(view_angles); +#endif +} + +vector Weapons_GetCameraPos(void) +{ +#ifdef SERVER + return self.origin + self.view_ofs; +#else + return getproperty(VF_ORIGIN); +#endif +} + +void Weapons_ViewAnimation(int i) +{ +#ifdef CLIENT + player pl = (player)pSeat->m_ePlayer; + View_PlayAnimation(i); +#else + player pl = (player)self; +#endif + pl.weapontime = 0.0f; +} + +#ifdef CLIENT +int View_GetAnimation(void); +int Weapons_GetAnimation(void) +{ + return View_GetAnimation(); +} +#endif + +void Weapons_ViewPunchAngle(vector add) +{ +#ifdef CLIENT + player pl = (player)self; + pl.punchangle += add; +#endif +} + +void Weapons_PlaySound(entity t, float ch, string s, float vol, float at) +{ +#ifdef SERVER + sound(t, ch, s, vol, at); +#endif +} + +int Weapons_IsPresent(player pl, int w) +{ + if (pl.g_items & g_weapons[w].id) { + return TRUE; + } else { + return FALSE; + } +} +/* +================= +Weapons_UpdateAmmo + +Sets .a_ammoX fields and clamps them so they can be networked as a single byte. +================= +*/ +void +Weapons_UpdateAmmo(base_player pl, int a1, int a2, int a3) +{ + /* no change */ + if (a1 == -1) { + a1 = pl.a_ammo1; + } + if (a2 == -1) { + a2 = pl.a_ammo2; + } + if (a3 == -1) { + a3 = pl.a_ammo3; + } + + /* networked as bytes, since we don't need more. Clamp to avoid errors */ + pl.a_ammo1 = bound(0, a1, 255); + pl.a_ammo2 = bound(0, a2, 255); + pl.a_ammo3 = bound(0, a3, 255); +} diff --git a/src/shared/weapons.h b/src/shared/weapons.h new file mode 100644 index 0000000..6163f2b --- /dev/null +++ b/src/shared/weapons.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +/* weapon Indices for the weapon table */ +enum +{ + WEAPON_NONE, + WEAPON_CROWBAR, + WEAPON_GLOCK, + WEAPON_PYTHON, + WEAPON_MP5, + WEAPON_SHOTGUN, + WEAPON_CROSSBOW, + WEAPON_RPG, + WEAPON_GAUSS, + WEAPON_EGON, + WEAPON_HORNETGUN, + WEAPON_HANDGRENADE, + WEAPON_SATCHEL, + WEAPON_TRIPMINE, + WEAPON_SNARK +}; + +#define MAX_A_9MM 250 +#define MAX_A_357 36 +#define MAX_A_BUCKSHOT 125 +#define MAX_A_M203_GRENADE 10 +#define MAX_A_BOLT 50 +#define MAX_A_ROCKET 5 +#define MAX_A_URANIUM 100 +#define MAX_A_HANDGRENADE 10 +#define MAX_A_SATCHEL 5 +#define MAX_A_TRIPMINE 10 +#define MAX_A_SNARK 10 +#define MAX_A_HORNET 8 diff --git a/src/shared/weapons.qc b/src/shared/weapons.qc new file mode 100644 index 0000000..6d388ad --- /dev/null +++ b/src/shared/weapons.qc @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * 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. + */ + +weapon_t w_null = {}; +weapon_t g_weapons[] = { + w_null, + w_crowbar, + w_glock, + w_python, + w_mp5, + w_shotgun, + w_crossbow, + w_rpg, + w_gauss, + w_egon, + w_hornetgun, + w_handgrenade, + w_satchel, + w_tripmine, + w_snark +}; diff --git a/zpak001.pk3dir/default.cfg b/zpak001.pk3dir/default.cfg new file mode 100755 index 0000000..1aaebc3 --- /dev/null +++ b/zpak001.pk3dir/default.cfg @@ -0,0 +1,45 @@ +// Generic Binds +bind "ESC" "togglemenu" +bind "w" "+forward" +bind "s" "+back" +bind "a" "+moveleft" +bind "d" "+moveright" +bind "SPACE" "+jump" +bind "CTRL" "+duck" +bind "SHIFT" "+speed" +bind "0" "slot10" +bind "1" "slot1" +bind "2" "slot2" +bind "3" "slot3" +bind "4" "slot4" +bind "5" "slot5" +bind "6" "slot6" +bind "7" "slot7" +bind "8" "slot8" +bind "9" "slot9" +bind "UPARROW" "+forward" +bind "DOWNARROW" "+back" +bind "LEFTARROW" "+left" +bind "RIGHTARROW" "+right" +bind "MOUSE1" "+attack" +bind "MOUSE2" "+attack2" +bind "MWHEELDOWN" "invnext" +bind "MWHEELUP" "invprev" +bind "r" "+reload" +bind "e" "+use" +bind "TAB" "+showscores" +bind "y" "messagemode" +bind "u" "messagemode2" +bind "t" "impulse 201" +bind "f" "impulse 100" +bind "f1" "vote yes" +bind "f2" "vote no" + +// Game Variables +seta "hostname" "FreeHL Server" +seta "maxplayers" "8" + +// 2D/HUD Variables +seta "con_color" "255 150 0" +seta "vgui_color" "255 170 0" +seta "cross_color" "0 255 0" diff --git a/zpak001.pk3dir/particles/fx_explosion.cfg b/zpak001.pk3dir/particles/fx_explosion.cfg new file mode 100644 index 0000000..33a6712 --- /dev/null +++ b/zpak001.pk3dir/particles/fx_explosion.cfg @@ -0,0 +1,69 @@ +r_part ember +{ + count 1 + texture "particles/fteparticlefont.tga" + tcoords 97 97 191 191 256 + rgb 255 128 76 + alpha 0 + scale 15 + scalefactor 1 + friction 8 + gravity 50 + die 1.5 + blend add + randomvel 5 + veladd 1 + rampmode delta + ramp 0 0 0 -0.5 0 + ramp 0 0 0 0.1 0 + ramp 0 0 0 0.1 0 + ramp 0 0 0 0.1 0 + ramp 0 0 0 0.1 0 + ramp 0 0 0 0.1 0 +} + +r_part expgib +{ + cliptype expgib + texture "particles/fteparticlefont.tga" + tcoords 97 97 191 191 256 + alpha 0 + count 16 + die 1 + randomvel 128 + gravity 50 + friction 2 + emit ember + emitinterval 0.01 + spawnmode circle +} + +r_part main +{ + texture "particles/fteparticlefont.tga" + tcoords 97 97 191 191 256 + count 1 + scale 200 + scalefactor 1 + die 1 + rgb 255 128 76 + rgbdelta 0 -32 -32 + friction 1 + blend add + assoc expgib +} + +r_part blacksmoke +{ + texture "particles/fteparticlefont.tga" + tcoords 97 97 191 191 256 + count 1 + scale 324 + scalefactor 1 + die 3 + alpha 1 + rgb 0 0 0 + spawnmode ball + gravity -25 + veladd -20 +} diff --git a/zpak001.pk3dir/particles/fx_impact.cfg b/zpak001.pk3dir/particles/fx_impact.cfg new file mode 100644 index 0000000..132c8b5 --- /dev/null +++ b/zpak001.pk3dir/particles/fx_impact.cfg @@ -0,0 +1,72 @@ +r_part spark +{ + type texturedspark + texture ball + tcoords 1 65 31 95 256 8 32 + scale 1 + count 8 + scalefactor 1 + alpha 0.5 + die 0.8 + rgb 255 115 0 + blend add + spawnmode ball + spawnorg 1 + spawnvel 100 + veladd 100 + friction 0.5 + gravity 800 +} + +r_part blackbits +{ + type ball + texture ball + tcoords 1 65 31 95 256 8 32 + scale 4 + count 18 + scalefactor 1 + alpha 1 + die 0.8 + rgb 25 25 25 + spawnmode ball + spawnorg 1 + spawnvel 100 + veladd 100 + friction 0.3 + gravity 800 +} + +r_part smoke_brown +{ + texture ball + tcoords 1 65 31 95 256 8 32 + count 3 + scale 25 + scalefactor 1 + die 1 + alpha 0.5 + rgb 155 90 0 + blend add + spawnmode ball + spawnorg 2 + spawnvel 20 + veladd 20 +} + +r_part smoke_grey +{ + texture ball + tcoords 1 65 31 95 256 8 32 + count 3 + scale 25 + scalefactor 1 + die 1 + alpha 0.5 + rgb 25 25 25 + blend add + spawnmode ball + spawnorg 2 + spawnvel 20 + veladd 20 +} diff --git a/zpak001.pk3dir/particles/fx_spark.cfg b/zpak001.pk3dir/particles/fx_spark.cfg new file mode 100644 index 0000000..62ee1a6 --- /dev/null +++ b/zpak001.pk3dir/particles/fx_spark.cfg @@ -0,0 +1,19 @@ +r_part effect +{ + type texturedspark + texture ball + tcoords 1 65 31 95 256 8 32 + scale 1 + count 8 + scalefactor 1 + alpha 0.5 + die 0.8 + rgb 255 115 0 + blend add + spawnmode ball + spawnorg 1 + spawnvel 100 + veladd 100 + friction 0.5 + gravity 800 +} diff --git a/zpak001.pk3dir/particles/weapon_egon.cfg b/zpak001.pk3dir/particles/weapon_egon.cfg new file mode 100644 index 0000000..9230c3e --- /dev/null +++ b/zpak001.pk3dir/particles/weapon_egon.cfg @@ -0,0 +1,43 @@ +r_part beam +{ + texture "classicparticle" + tcoords 0 0 16 16 32 + scale 1 + scalefactor 0.8 + step 1 + alpha 0.25 + spawnmode spiral 32 + spawnorg 3 + spawnvel 18 + blend adda + rgbf 0.5 0.5 1.0 +} +r_part +beam +{ + texture "particles/fteparticlefont.tga" + tcoords 97 97 191 191 256 + scale 2 + scaledelta 0.25 + rgbf 1 1 1 + alpha 0.25 + blend add + step 4 + randomvel 0 + type beam +} + +r_part trail +{ + texture "classicparticle" + tcoords 0 0 16 16 32 + scale 1 + scalefactor 0.8 + step 0.25 + alpha 0.1 + spawnmode spiral 32 + spawnorg 3 + spawnvel 6 + blend adda + rgbf 0.1 0.1 0.5 + die 0.1 +} diff --git a/zpak001.pk3dir/particles/weapon_hornet.cfg b/zpak001.pk3dir/particles/weapon_hornet.cfg new file mode 100644 index 0000000..dd8922d --- /dev/null +++ b/zpak001.pk3dir/particles/weapon_hornet.cfg @@ -0,0 +1,14 @@ +r_part trail +{ + texture "particles/fteparticlefont.tga" + tcoords 97 97 191 191 256 + scale 2 + scaledelta 0.25 + rgbf 1 0.5 0 + alpha 0.5 + blend add + step 4 + die 1 + randomvel 0 + type beam +} diff --git a/zpak001.pk3dir/particles/weapon_rpg.cfg b/zpak001.pk3dir/particles/weapon_rpg.cfg new file mode 100644 index 0000000..d655dbc --- /dev/null +++ b/zpak001.pk3dir/particles/weapon_rpg.cfg @@ -0,0 +1,14 @@ +r_part trail +{ + texture "particles/fteparticlefont.tga" + tcoords 97 97 191 191 256 + scale 2 + scaledelta 0.25 + rgbf 1 1 1 + alpha 0.5 + blend add + step 4 + die 2 + randomvel 0 + type beam +} diff --git a/zpak001.pk3dir/particles/weapon_tripmine.cfg b/zpak001.pk3dir/particles/weapon_tripmine.cfg new file mode 100644 index 0000000..d2dfc0d --- /dev/null +++ b/zpak001.pk3dir/particles/weapon_tripmine.cfg @@ -0,0 +1,14 @@ +r_part beam +{ + texture "particles/fteparticlefont.tga" + tcoords 97 97 191 191 256 + scale 1 + scaledelta 0.5 + alpha 0.4 + step 4 + randomvel 0 + rgb 0 255 200 + rgbdelta 0 -150 -150 + type beam + blend add +} diff --git a/zpak001.pk3dir/scripts/map_blacklist b/zpak001.pk3dir/scripts/map_blacklist new file mode 100644 index 0000000..50f09d7 --- /dev/null +++ b/zpak001.pk3dir/scripts/map_blacklist @@ -0,0 +1,106 @@ +c0a0.bsp +c0a0a.bsp +c0a0b.bsp +c0a0c.bsp +c0a0d.bsp +c0a0e.bsp +c1a0.bsp +c1a0a.bsp +c1a0b.bsp +c1a0c.bsp +c1a0d.bsp +c1a0e.bsp +c1a1.bsp +c1a1a.bsp +c1a1b.bsp +c1a1c.bsp +c1a1d.bsp +c1a1f.bsp +c1a2.bsp +c1a2a.bsp +c1a2b.bsp +c1a2c.bsp +c1a2d.bsp +c1a3.bsp +c1a3a.bsp +c1a3b.bsp +c1a3c.bsp +c1a3d.bsp +c1a4.bsp +c1a4b.bsp +c1a4d.bsp +c1a4e.bsp +c1a4f.bsp +c1a4g.bsp +c1a4i.bsp +c1a4j.bsp +c1a4k.bsp +c2a1.bsp +c2a1a.bsp +c2a1b.bsp +c2a2.bsp +c2a2a.bsp +c2a2b1.bsp +c2a2b2.bsp +c2a2c.bsp +c2a2d.bsp +c2a2e.bsp +c2a2f.bsp +c2a2g.bsp +c2a2h.bsp +c2a3.bsp +c2a3a.bsp +c2a3b.bsp +c2a3c.bsp +c2a3d.bsp +c2a3e.bsp +c2a4.bsp +c2a4a.bsp +c2a4b.bsp +c2a4c.bsp +c2a4d.bsp +c2a4e.bsp +c2a4f.bsp +c2a4g.bsp +c2a5.bsp +c2a5a.bsp +c2a5b.bsp +c2a5c.bsp +c2a5d.bsp +c2a5e.bsp +c2a5f.bsp +c2a5g.bsp +c2a5w.bsp +c2a5x.bsp +c3a1.bsp +c3a1a.bsp +c3a1b.bsp +c3a2.bsp +c3a2a.bsp +c3a2b.bsp +c3a2c.bsp +c3a2d.bsp +c3a2e.bsp +c3a2f.bsp +c4a1.bsp +c4a1a.bsp +c4a1b.bsp +c4a1c.bsp +c4a1d.bsp +c4a1e.bsp +c4a1f.bsp +c4a2.bsp +c4a2a.bsp +c4a2b.bsp +c4a3.bsp +c5a1.bsp +t0a0.bsp +t0a0a.bsp +t0a0b.bsp +t0a0b1.bsp +t0a0b2.bsp +t0a0c.bsp +t0a0d.bsp +hldemo1.bsp +hldemo2.bsp +hldemo3.bsp diff --git a/zpak001.pk3dir/skill_valve.cfg b/zpak001.pk3dir/skill_valve.cfg new file mode 100644 index 0000000..041d4ac --- /dev/null +++ b/zpak001.pk3dir/skill_valve.cfg @@ -0,0 +1,255 @@ +set sk_12mm_bullet1 "8" +set sk_12mm_bullet2 "10" +set sk_12mm_bullet3 "10" +set sk_9mmAR_bullet1 "3" +set sk_9mmAR_bullet2 "4" +set sk_9mmAR_bullet3 "5" +set sk_9mm_bullet1 "5" +set sk_9mm_bullet2 "5" +set sk_9mm_bullet3 "8" +set sk_agrunt_dmg_punch1 "10" +set sk_agrunt_dmg_punch2 "20" +set sk_agrunt_dmg_punch3 "20" +set sk_agrunt_health1 "60" +set sk_agrunt_health2 "90" +set sk_agrunt_health3 "120" +set sk_apache_health1 "150" +set sk_apache_health2 "250" +set sk_apache_health3 "400" +set sk_barnacle_health1 "25" +set sk_barnacle_health2 "25" +set sk_barnacle_health3 "25" +set sk_barney_health1 "35" +set sk_barney_health2 "35" +set sk_barney_health3 "35" +set sk_battery1 "15" +set sk_battery2 "15" +set sk_battery3 "10" +set sk_bigmomma_dmg_blast1 "100" +set sk_bigmomma_dmg_blast2 "120" +set sk_bigmomma_dmg_blast3 "160" +set sk_bigmomma_dmg_slash1 "50" +set sk_bigmomma_dmg_slash2 "60" +set sk_bigmomma_dmg_slash3 "70" +set sk_bigmomma_health_factor1 "1.0" +set sk_bigmomma_health_factor2 "1.5" +set sk_bigmomma_health_factor3 "2" +set sk_bigmomma_radius_blast1 "250" +set sk_bigmomma_radius_blast2 "250" +set sk_bigmomma_radius_blast3 "275" +set sk_bullsquid_dmg_bite1 "15" +set sk_bullsquid_dmg_bite2 "25" +set sk_bullsquid_dmg_bite3 "25" +set sk_bullsquid_dmg_spit1 "10" +set sk_bullsquid_dmg_spit2 "10" +set sk_bullsquid_dmg_spit3 "15" +set sk_bullsquid_dmg_whip1 "25" +set sk_bullsquid_dmg_whip2 "35" +set sk_bullsquid_dmg_whip3 "35" +set sk_bullsquid_health1 "40" +set sk_bullsquid_health2 "40" +set sk_bullsquid_health3 "120" +set sk_controller_dmgball1 "3" +set sk_controller_dmgball2 "4" +set sk_controller_dmgball3 "5" +set sk_controller_dmgzap1 "15" +set sk_controller_dmgzap2 "25" +set sk_controller_dmgzap3 "35" +set sk_controller_health1 "60" +set sk_controller_health2 "60" +set sk_controller_health3 "100" +set sk_controller_speedball1 "650" +set sk_controller_speedball2 "800" +set sk_controller_speedball3 "1000" +set sk_gargantua_dmg_fire1 "3" +set sk_gargantua_dmg_fire2 "5" +set sk_gargantua_dmg_fire3 "5" +set sk_gargantua_dmg_slash1 "10" +set sk_gargantua_dmg_slash2 "30" +set sk_gargantua_dmg_slash3 "30" +set sk_gargantua_dmg_stomp1 "50" +set sk_gargantua_dmg_stomp2 "100" +set sk_gargantua_dmg_stomp3 "100" +set sk_gargantua_health1 "800" +set sk_gargantua_health2 "800" +set sk_gargantua_health3 "1000" +set sk_hassassin_health1 "30" +set sk_hassassin_health2 "50" +set sk_hassassin_health3 "50" +set sk_headcrab_dmg_bite1 "5" +set sk_headcrab_dmg_bite2 "10" +set sk_headcrab_dmg_bite3 "10" +set sk_headcrab_health1 "10" +set sk_headcrab_health2 "10" +set sk_headcrab_health3 "20" +set sk_healthcharger1 "50" +set sk_healthcharger2 "40" +set sk_healthcharger3 "25" +set sk_healthkit1 "15" +set sk_healthkit2 "15" +set sk_healthkit3 "10" +set sk_hgrunt_gspeed1 "400" +set sk_hgrunt_gspeed2 "600" +set sk_hgrunt_gspeed3 "800" +set sk_hgrunt_health1 "50" +set sk_hgrunt_health2 "50" +set sk_hgrunt_health3 "80" +set sk_hgrunt_kick1 "5" +set sk_hgrunt_kick2 "10" +set sk_hgrunt_kick3 "10" +set sk_hgrunt_pellets1 "3" +set sk_hgrunt_pellets2 "5" +set sk_hgrunt_pellets3 "6" +set sk_hornet_dmg1 "4" +set sk_hornet_dmg2 "5" +set sk_hornet_dmg3 "8" +set sk_houndeye_dmg_blast1 "10" +set sk_houndeye_dmg_blast2 "15" +set sk_houndeye_dmg_blast3 "15" +set sk_houndeye_health1 "20" +set sk_houndeye_health2 "20" +set sk_houndeye_health3 "30" +set sk_ichthyosaur_health1 "200" +set sk_ichthyosaur_health2 "200" +set sk_ichthyosaur_health3 "400" +set sk_ichthyosaur_shake1 "20" +set sk_ichthyosaur_shake2 "35" +set sk_ichthyosaur_shake3 "50" +set sk_islave_dmg_claw1 "8" +set sk_islave_dmg_claw2 "10" +set sk_islave_dmg_claw3 "10" +set sk_islave_dmg_clawrake1 "25" +set sk_islave_dmg_clawrake2 "25" +set sk_islave_dmg_clawrake3 "25" +set sk_islave_dmg_zap1 "10" +set sk_islave_dmg_zap2 "10" +set sk_islave_dmg_zap3 "15" +set sk_islave_health1 "30" +set sk_islave_health2 "30" +set sk_islave_health3 "60" +set sk_leech_dmg_bite1 "2" +set sk_leech_dmg_bite2 "2" +set sk_leech_dmg_bite3 "2" +set sk_leech_health1 "2" +set sk_leech_health2 "2" +set sk_leech_health3 "2" +set sk_miniturret_health1 "40" +set sk_miniturret_health2 "40" +set sk_miniturret_health3 "50" +set sk_monster_arm1 "1" +set sk_monster_arm2 "1" +set sk_monster_arm3 "1" +set sk_monster_chest1 "1" +set sk_monster_chest2 "1" +set sk_monster_chest3 "1" +set sk_monster_head1 "3" +set sk_monster_head2 "3" +set sk_monster_head3 "3" +set sk_monster_leg1 "1" +set sk_monster_leg2 "1" +set sk_monster_leg3 "1" +set sk_monster_stomach1 "1" +set sk_monster_stomach2 "1" +set sk_monster_stomach3 "1" +set sk_nihilanth_health1 "800" +set sk_nihilanth_health2 "800" +set sk_nihilanth_health3 "1000" +set sk_nihilanth_zap1 "30" +set sk_nihilanth_zap2 "30" +set sk_nihilanth_zap3 "50" +set sk_player_arm1 "1" +set sk_player_arm2 "1" +set sk_player_arm3 "1" +set sk_player_chest1 "1" +set sk_player_chest2 "1" +set sk_player_chest3 "1" +set sk_player_head1 "3" +set sk_player_head2 "3" +set sk_player_head3 "3" +set sk_player_leg1 "1" +set sk_player_leg2 "1" +set sk_player_leg3 "1" +set sk_player_stomach1 "1" +set sk_player_stomach2 "1" +set sk_player_stomach3 "1" +set sk_plr_357_bullet1 "40" +set sk_plr_357_bullet2 "40" +set sk_plr_357_bullet3 "40" +set sk_plr_9mmAR_bullet1 "5" +set sk_plr_9mmAR_bullet2 "5" +set sk_plr_9mmAR_bullet3 "5" +set sk_plr_9mmAR_grenade1 "100" +set sk_plr_9mmAR_grenade2 "100" +set sk_plr_9mmAR_grenade3 "100" +set sk_plr_9mm_bullet1 "8" +set sk_plr_9mm_bullet2 "8" +set sk_plr_9mm_bullet3 "8" +set sk_plr_buckshot1 "5" +set sk_plr_buckshot2 "5" +set sk_plr_buckshot3 "5" +set sk_plr_crowbar1 "10" +set sk_plr_crowbar2 "10" +set sk_plr_crowbar3 "10" +set sk_plr_egon_narrow1 "6" +set sk_plr_egon_narrow2 "6" +set sk_plr_egon_narrow3 "6" +set sk_plr_egon_wide1 "14" +set sk_plr_egon_wide2 "14" +set sk_plr_egon_wide3 "14" +set sk_plr_gauss1 "20" +set sk_plr_gauss2 "20" +set sk_plr_gauss3 "20" +set sk_plr_hand_grenade1 "100" +set sk_plr_hand_grenade2 "100" +set sk_plr_hand_grenade3 "100" +set sk_plr_hornet1 "10" +set sk_plr_hornet2 "10" +set sk_plr_hornet3 "10" +set sk_plr_rpg1 "100" +set sk_plr_rpg2 "100" +set sk_plr_rpg3 "100" +set sk_plr_satchel1 "150" +set sk_plr_satchel2 "150" +set sk_plr_satchel3 "150" +set sk_plr_tripmine1 "150" +set sk_plr_tripmine2 "150" +set sk_plr_tripmine3 "150" +set sk_plr_xbow_bolt_client1 "10" +set sk_plr_xbow_bolt_client2 "10" +set sk_plr_xbow_bolt_client3 "10" +set sk_plr_xbow_bolt_monster1 "50" +set sk_plr_xbow_bolt_monster2 "50" +set sk_plr_xbow_bolt_monster3 "50" +set sk_scientist_heal1 "25" +set sk_scientist_heal2 "25" +set sk_scientist_heal3 "25" +set sk_scientist_health1 "20" +set sk_scientist_health2 "20" +set sk_scientist_health3 "20" +set sk_sentry_health1 "40" +set sk_sentry_health2 "40" +set sk_sentry_health3 "50" +set sk_snark_dmg_bite1 "10" +set sk_snark_dmg_bite2 "10" +set sk_snark_dmg_bite3 "10" +set sk_snark_dmg_pop1 "5" +set sk_snark_dmg_pop2 "5" +set sk_snark_dmg_pop3 "5" +set sk_snark_health1 "2" +set sk_snark_health2 "2" +set sk_snark_health3 "2" +set sk_suitcharger1 "75" +set sk_suitcharger2 "50" +set sk_suitcharger3 "35" +set sk_turret_health1 "50" +set sk_turret_health2 "50" +set sk_turret_health3 "60" +set sk_zombie_dmg_both_slash1 "25" +set sk_zombie_dmg_both_slash2 "40" +set sk_zombie_dmg_both_slash3 "40" +set sk_zombie_dmg_one_slash1 "10" +set sk_zombie_dmg_one_slash2 "20" +set sk_zombie_dmg_one_slash3 "20" +set sk_zombie_health1 "50" +set sk_zombie_health2 "50" +set sk_zombie_health3 "100" diff --git a/zpak001.pk3dir/sound/fx_valve.sndshd b/zpak001.pk3dir/sound/fx_valve.sndshd new file mode 100644 index 0000000..3e85fe7 --- /dev/null +++ b/zpak001.pk3dir/sound/fx_valve.sndshd @@ -0,0 +1,7 @@ +fx.explosion +{ + sample weapons/explode3.wav + sample weapons/explode4.wav + sample weapons/explode5.wav + shakes 1.5 +} diff --git a/zpak001.pk3dir/sound/items_valve.sndshd b/zpak001.pk3dir/sound/items_valve.sndshd new file mode 100644 index 0000000..b01e9df --- /dev/null +++ b/zpak001.pk3dir/sound/items_valve.sndshd @@ -0,0 +1,29 @@ +ammo.pickup +{ + sample items/9mmclip1.wav +} + +ammo.respawn +{ + sample items/suitchargeok1.wav +} + +item.battery +{ + sample items/gunpickup2.wav +} + +item.healthkit +{ + sample items/smallmedkit1.wav +} + +item.respawn +{ + sample items/suitchargeok1.wav +} + +weapon.pickup +{ + sample items/gunpickup2.wav +} diff --git a/zpak001.pk3dir/sound/monsters_valve.sndshd b/zpak001.pk3dir/sound/monsters_valve.sndshd new file mode 100644 index 0000000..ac95038 --- /dev/null +++ b/zpak001.pk3dir/sound/monsters_valve.sndshd @@ -0,0 +1,630 @@ +monster_alien_grunt.alert +{ + sample agrunt/ag_alert1.wav + sample agrunt/ag_alert2.wav + sample agrunt/ag_alert3.wav + sample agrunt/ag_alert4.wav + sample agrunt/ag_alert5.wav +} + +monster_alien_grunt.attack +{ + sample agrunt/ag_attack1.wav + sample agrunt/ag_attack2.wav + sample agrunt/ag_attack3.wav +} + +monster_alien_grunt.die +{ + sample agrunt/ag_die1.wav + sample agrunt/ag_die2.wav + sample agrunt/ag_die3.wav + sample agrunt/ag_die4.wav + sample agrunt/ag_die5.wav +} + +monster_alien_grunt.idle +{ + sample agrunt/ag_idle1.wav + sample agrunt/ag_idle2.wav + sample agrunt/ag_idle3.wav + sample agrunt/ag_idle4.wav + sample agrunt/ag_idle5.wav +} + +monster_alien_grunt.pain +{ + sample agrunt/ag_pain1.wav + sample agrunt/ag_pain2.wav + sample agrunt/ag_pain3.wav + sample agrunt/ag_pain4.wav + sample agrunt/ag_pain5.wav +} + +monster_alien_controller.alert +{ + sample controller/con_alert1.wav + sample controller/con_alert2.wav + sample controller/con_alert3.wav +} + +monster_alien_controller.attack +{ + sample controller/con_attack1.wav + sample controller/con_attack2.wav + sample controller/con_attack3.wav +} + +monster_alien_controller.die +{ + sample controller/con_die1.wav + sample controller/con_die2.wav +} + +monster_alien_controller.idle +{ + sample controller/con_idle1.wav + sample controller/con_idle2.wav + sample controller/con_idle3.wav + sample controller/con_idle4.wav + sample controller/con_idle5.wav +} + +monster_alien_controller.pain +{ + sample controller/con_pain1.wav + sample controller/con_pain2.wav + sample controller/con_pain3.wav +} + +monster_alien_slave.die +{ + sample aslave/slv_die1.wav + sample aslave/slv_die2.wav +} + +monster_alien_slave.pain +{ + sample aslave/slv_pain1.wav + sample aslave/slv_pain2.wav +} + +monster_alien_slave.attack_charge +{ + sample debris/zap4.wav +} + +monster_alien_slave.attack_shoot +{ + pitch_min 1.3 + pitch_max 1.6 + sample hassault/hw_shoot1.wav +} + +// bcl_bite3 is played when an entity is in it's mouth bcl_tongue1 unused? +monster_barnacle.attackchew +{ + sample barnacle/bcl_chew1.wav + sample barnacle/bcl_chew2.wav + sample barnacle/bcl_chew3.wav +} +monster_barnacle.attackpull +{ + sample barnacle/bcl_alert2.wav +} + +monster_barnacle.die +{ + sample barnacle/bcl_die1.wav + sample barnacle/bcl_die3.wav +} + +monster_barney.die +{ + sample barney/ba_die1.wav + sample barney/ba_die1.wav + sample barney/ba_die2.wav +} + +monster_barney.pain +{ + sample barney/ba_pain1.wav + sample barney/ba_pain1.wav + sample barney/ba_pain1.wav +} + +monster_bigmomma.alert +{ + sample gonarch/gon_alert1.wav + sample gonarch/gon_alert2.wav + sample gonarch/gon_alert3.wav +} + +// the groans for when she spits? +monster_bigmomma.attack +{ + sample gonarch/gon_attack1.wav + sample gonarch/gon_attack2.wav + sample gonarch/gon_attack3.wav +} + +/ mourns the death of her children +monster_bigmomma.child +{ + sample gonarch/gon_childdie1.wav + sample gonarch/gon_childdie2.wav + sample gonarch/gon_childdie3.wav +} + +monster_bigmomma.die +{ + sample gonarch/gon_die1.wav + sample gonarch/gon_die2.wav + sample gonarch/gon_die3.wav +} + +monster_bigmomma.idle +{ + sample gonarch/gon_sack1.wav + sample gonarch/gon_sack2.wav + sample gonarch/gon_sack3.wav +} + +monster_bigmomma.pain +{ + sample gonarch/gon_pain2.wav + sample gonarch/gon_pain3.wav + sample gonarch/gon_pain4.wav + sample gonarch/gon_pain5.wav +} + +// has unique foot step sounds +monster_bigmomma.step +{ + sample gonarch/gon_step1.wav + sample gonarch/gon_step2.wav + sample gonarch/gon_step3.wav +} + +monster_bullchicken.attack +{ + sample bullchicken/bc_attackgrowl1.wav + sample bullchicken/bc_attackgrowl2.wav + sample bullchicken/bc_attackgrowl3.wav +} + +monster_bullchicken.attackbite +{ + sample bullchicken/bc_bite1.wav + sample bullchicken/bc_bite2.wav + sample bullchicken/bc_bite3.wav +} + +monster_bullchicken.attackshoot +{ + sample bullchicken/bc_attack1.wav + sample bullchicken/bc_attack2.wav + sample bullchicken/bc_attack3.wav +} + +monster_bullchicken.die +{ + sample bullchicken/bc_die1.wav + sample bullchicken/bc_die2.wav + sample bullchicken/bc_die3.wav +} + +monster_bullchicken.idle +{ + sample bullchicken/bc_idle1.wav + sample bullchicken/bc_idle2.wav + sample bullchicken/bc_idle3.wav + sample bullchicken/bc_idle4.wav + sample bullchicken/bc_idle5.wav +} + +monster_bullchicken.pain +{ + sample bullchicken/bc_pain1.wav + sample bullchicken/bc_pain2.wav + sample bullchicken/bc_pain3.wav + sample bullchicken/bc_pain4.wav +} + +monster_cockroach.die +{ + sample roach/rch_die.wav + sample roach/rch_smash.wav +} + +monster_gargantua.alert +{ + sample garg/gar_alert1.wav + sample garg/gar_alert2.wav + sample garg/gar_alert3.wav +} + +// similar to bullsquid groans during and after attacks +monster_gargantua.attack +{ + sample garg/gar_attack1.wav + sample garg/gar_attack2.wav + sample garg/gar_attack3.wav +} + +monster_gargantua.attackflame +{ + sample garg/gar_flamerun1.wav +} + +monster_gargantua.attackflameoff +{ + sample garg/gar_flameoff1.wav +} + +monster_gargantua.attackflameon +{ + sample garg/gar_flameon1.wav +} + + +monster_gargantua.die +{ + sample garg/gar_die1.wav + sample garg/gar_die2.wav +} + +monster_gargantua.idle +{ + sample garg/gar_idle1.wav + sample garg/gar_idle2.wav + sample garg/gar_idle3.wav + sample garg/gar_idle4.wav + sample garg/gar_idle5.wav + sample garg/gar_breathe1.wav + sample garg/gar_breathe2.wav + sample garg/gar_breathe3.wav +} + +monster_gargantua.pain +{ + sample garg/gar_pain1.wav + sample garg/gar_pain2.wav + sample garg/gar_pain3.wav +} + +// has unique foot step sounds +monster_gargantua.step +{ + sample garg/gar_step1.wav + sample garg/gar_step2.wav +} + +monster_headcrab.alert +{ + sample headcrab/hc_alert1.wav + sample headcrab/hc_alert2.wav +} + +// these attack sounds are actually the cry when it jumps +monster_headcrab.attack +{ + sample headcrab/hc_attack1.wav + sample headcrab/hc_attack2.wav + sample headcrab/hc_attack3.wav +} + +monster_headcrab.attackhit +{ + sample headcrab/hc_headbite.wav +} + +monster_headcrab.die +{ + sample headcrab/hc_die1.wav + sample headcrab/hc_die2.wav +} + +monster_headcrab.idle +{ + sample headcrab/hc_idle1.wav + sample headcrab/hc_idle2.wav + sample headcrab/hc_idle3.wav + sample headcrab/hc_idle4.wav + sample headcrab/hc_idle5.wav +} + +monster_headcrab.pain +{ + sample headcrab/hc_pain1.wav + sample headcrab/hc_pain2.wav + sample headcrab/hc_pain3.wav +} + +monster_houndeye.alert +{ + sample houndeye/he_alert1.wav + sample houndeye/he_alert2.wav + sample houndeye/he_alert3.wav +} + +monster_houndeye.attack +{ + sample houndeye/he_attack1.wav + sample houndeye/he_attack2.wav + sample houndeye/he_attack3.wav +} + +monster_houndeye.blast +{ + sample houndeye/he_blast1.wav + sample houndeye/he_blast2.wav + sample houndeye/he_blast3.wav +} + + +monster_houndeye.die +{ + sample houndeye/he_die1.wav + sample houndeye/he_die2.wav + sample houndeye/he_die3.wav +} + +// Not sure where the hunt sounds are actually used +monster_houndeye.idle +{ + sample houndeye/he_hunt1.wav + sample houndeye/he_hunt2.wav + sample houndeye/he_hunt3.wav + sample houndeye/he_hunt4.wav + sample houndeye/he_idle1.wav + sample houndeye/he_idle2.wav + sample houndeye/he_idle3.wav + sample houndeye/he_idle4.wav +} + +monster_houndeye.pain +{ + sample houndeye/he_pain1.wav + sample houndeye/he_pain2.wav + sample houndeye/he_pain3.wav + sample houndeye/he_pain4.wav + sample houndeye/he_pain5.wav +} + +monster_human_grunt.die +{ + sample hgrunt/gr_die1.wav + sample hgrunt/gr_die2.wav + sample hgrunt/gr_die3.wav +} + +monster_human_grunt.pain +{ + sample hgrunt/gr_pain1.wav + sample hgrunt/gr_pain2.wav + sample hgrunt/gr_pain3.wav + sample hgrunt/gr_pain4.wav + sample hgrunt/gr_pain5.wav +} + +monster_ichthyosaur.alert +{ + sample ichy/ichy_alert1.wav + sample ichy/ichy_alert2.wav + sample ichy/ichy_alert3.wav +} + +monster_ichthyosaur.attack +{ + sample ichy/ichy_attack1.wav + sample ichy/ichy_attack2.wav +} + +monster_ichthyosaur.die +{ + sample ichy/ichy_die1.wav + sample ichy/ichy_die2.wav + sample ichy/ichy_die3.wav + sample ichy/ichy_die4.wav +} + +monster_ichthyosaur.idle +{ + sample ichy/ichy_idle1.wav + sample ichy/ichy_idle2.wav + sample ichy/ichy_idle3.wav + sample ichy/ichy_idle4.wav +} + +monster_ichthyosaur.pain +{ + sample ichy/ichy_pain1.wav + sample ichy/ichy_pain2.wav + sample ichy/ichy_pain3.wav + sample ichy/ichy_pain4.wav + sample ichy/ichy_pain5.wav +} + +monster_leech.alert +{ + sample leech/leech_alert1.wav + sample leech/leech_alert2.wav +} + +monster_leech.attack +{ + sample leech/leech_bite1.wav + sample leech/leech_bite2.wav + sample leech/leech_bite3.wav +} + +// other sounds +// * x_shoot1 - ? +// * nih_die2 - used in map not code? + +// these attack sounds are his growls +monster_nihilanth.attack +{ + sample x/x_attack1.wav + sample x/x_attack2.wav + sample x/x_attack3.wav +} + +monster_nihilanth.attackball +{ + sample x/x_ballattack1.wav +} + +monster_nihilanth.attackballmove +{ + sample x/x_teleattack1.wav +} + +monster_nihilanth.die +{ + sample x/x_die1.wav +} + +monster_nihilanth.idle +{ + sample x/x_laugh1.wav + sample x/x_laugh2.wav +} + +monster_nihilanth.pain +{ + sample x/x_pain1.wav + sample x/x_pain2.wav + sample x/x_pain3.wav +} + +monster_nihilanth.recharge +{ + sample x/x_recharge1.wav + sample x/x_recharge2.wav + sample x/x_recharge3.wav +} + +monster_scientist.die +{ + sample scientist/sci_die1.wav + sample scientist/sci_die2.wav + sample scientist/sci_die3.wav + sample scientist/sci_die4.wav +} + +monster_scientist.pain +{ + sample scientist/sci_pain1.wav + sample scientist/sci_pain2.wav + sample scientist/sci_pain3.wav + sample scientist/sci_pain4.wav + sample scientist/sci_pain5.wav + sample scientist/sci_pain6.wav + sample scientist/sci_pain7.wav + sample scientist/sci_pain8.wav + sample scientist/sci_pain9.wav + sample scientist/sci_pain10.wav +} + +monster_sentry.alert +{ + sample turret/tu_deploy.wav +} + +monster_sentry.die +{ + sample turret/tu_die.wav + sample turret/tu_die2.wav + sample turret/tu_die3.wav +} + +monster_sentry.idle +{ + sample turret/tu_ping.wav +} + +monster_sentry.retract +{ + sample turret/tu_retract.wav +} + +// cut sounds listing here because why not +monster_tentacle.alert +{ + sample tentacle/te_alert1.wav + sample tentacle/te_alert2.wav +} + +monster_tentacle.attack +{ + sample tentacle/te_strike1.wav + sample tentacle/te_strike2.wav +} + +monster_tentacle.die +{ + sample tentacle/te_death2.wav +} + +// includes some cut sounds might be interesting +monster_tentacle.idle +{ + sample tentacle/te_roar1.wav + sample tentacle/te_roar2.wav + sample tentacle/te_search1.wav + sample tentacle/te_search2.wav + sample tentacle/te_sing1.wav + sample tentacle/te_sing2.wav +} + +monster_turret.spindown +{ + sample turret/tu_spindown.wav +} + +monster_turret.spinup +{ + sample turret/tu_spinup.wav +} + +monster_zombie.alert +{ + sample zombie/zo_alert10.wav + sample zombie/zo_alert20.wav + sample zombie/zo_alert30.wav +} + +monster_zombie.attack +{ + sample zombie/zo_attack1.wav + sample zombie/zo_attack2.wav +} + +monster_zombie.attackhit +{ + sample zombie/claw_strike1.wav + sample zombie/claw_strike2.wav + sample zombie/claw_strike3.wav +} + +monster_zombie.attackmiss +{ + sample zombie/claw_miss1.wav + sample zombie/claw_miss2.wav +} + +monster_zombie.idle +{ + sample zombie/zo_idle1.wav + sample zombie/zo_idle2.wav + sample zombie/zo_idle3.wav + sample zombie/zo_idle4.wav +} + +monster_zombie.pain +{ + sample zombie/zo_pain1.wav + sample zombie/zo_pain2.wav +} diff --git a/zpak001.pk3dir/sound/player_valve.sndshd b/zpak001.pk3dir/sound/player_valve.sndshd new file mode 100644 index 0000000..3adbb07 --- /dev/null +++ b/zpak001.pk3dir/sound/player_valve.sndshd @@ -0,0 +1,12 @@ +player.fall +{ + sample player/pl_fallpain3.wav +} +player.lightfall +{ + sample player/pl_fallpain1.wav +} +player.die +{ + sample fvox/flatline.wav +} diff --git a/zpak001.pk3dir/sound/weapons_valve.sndshd b/zpak001.pk3dir/sound/weapons_valve.sndshd new file mode 100644 index 0000000..069f3ae --- /dev/null +++ b/zpak001.pk3dir/sound/weapons_valve.sndshd @@ -0,0 +1,166 @@ +weapon_357.shoot +{ + sample weapons/357_shot1.wav + sample weapons/357_shot2.wav +} + +weapon_357.reload +{ + sample weapons/357_reload1.wav +} + +weapon_crossbow.fire +{ + sample weapons/xbow_fire1.wav +} + +weapon_crossbow.hit +{ + sample weapons/xbow_hit1.wav +} + +weapon_crossbow.hitbody +{ + sample weapons/xbow_hitbod1.wav + sample weapons/xbow_hitbod2.wav +} + +weapon_crossbow.reload +{ + sample weapons/xbow_reload1.wav +} + +weapon_crowbar.hit +{ + sample weapons/cbar_hit1.wav + sample weapons/cbar_hit2.wav +} + +weapon_crowbar.hitbody +{ + sample weapons/cbar_hitbod1.wav + sample weapons/cbar_hitbod2.wav + sample weapons/cbar_hitbod3.wav +} + +weapon_crowbar.miss +{ + sample weapons/cbar_miss1.wav +} + +weapon_glock.fire +{ + sample weapons/pl_gun3.wav +} + +weapon_handgrenade.bounce +{ + sample weapons/grenade_hit1.wav + sample weapons/grenade_hit2.wav + sample weapons/grenade_hit3.wav +} + +weapon_hornetgun.fire +{ + sample agrunt/ag_fire1.wav + sample agrunt/ag_fire2.wav + sample agrunt/ag_fire3.wav +} + +weapon_hornetgun.buzz +{ + sample hornet/ag_buzz1.wav + sample hornet/ag_buzz2.wav + sample hornet/ag_buzz3.wav +} + +weapon_hornetgun.hit +{ + sample hornet/ag_hornethit1.wav + sample hornet/ag_hornethit2.wav + sample hornet/ag_hornethit3.wav +} + +weapon_mp5.shoot +{ + sample weapons/hks1.wav + sample weapons/hks2.wav +} + +weapon_mp5.gl +{ + sample weapons/glauncher.wav +} + +weapon_satchel.bounce +{ + sample weapons/g_bounce1.wav + sample weapons/g_bounce2.wav + sample weapons/g_bounce3.wav + sample weapons/g_bounce4.wav + sample weapons/g_bounce5.wav +} + +weapon_tripmine.deploy +{ + sample weapons/mine_deploy.wav +} + +weapon_tripmine.charge +{ + sample weapons/mine_charge.wav +} + +weapon_tripmine.activate +{ + sample weapons/mine_activate.wav +} + +weapon_rpg.shoot +{ + sample weapons/rocketfire1.wav +} + +weapon_shotgun.single +{ + sample weapons/sbarrel1.wav +} + +weapon_shotgun.double +{ + sample weapons/dbarrel1.wav +} + +weapon_shotgun.reload +{ + sample weapons/reload3.wav +} + +weapon_shotgun.cock +{ + sample weapons/scock1.wav +} + +weapon_snark.deploy +{ + pitch_min 1.0 + pitch_max 1.1 + sample squeek/sqk_deploy1.wav +} + +weapon_snark.die +{ + sample squeek/sqk_die1.wav +} + +weapon_snark.blast +{ + sample squeek/sqk_blast1.wav +} + +weapon_snark.hunt +{ + sample squeek/sqk_hunt1.wav + sample squeek/sqk_hunt2.wav + sample squeek/sqk_hunt3.wav +}