commit 6ff68fcc349a3b9747ef7bf8ebf726a0daabfbbb Author: Marco Hladik Date: Mon Mar 8 10:38:54 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..3439c2a --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +# FreeCS +Clean-room reimplementation of Counter-Strike 1.5 (mod-version). + +Aiming for a stable reimagining of the original mod in QuakeC. +Not aiming for accuracy, but for a smooth, exploit and bug-free +experience over the Internet. + +This is all 100% new, original code written by good old trial and error. +Differences exist and features are slowly being implemented one by one. +This allows the code to be fully free and unencumbered, unlike similar projects. + +Featuring proper weapon prediction to enable stress-free netplay. + +![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 cstrike + +then either run Nuclide's ./build_game.sh shell script, or issue 'make' inside +./cstrike/src! + +Obviously make sure that Nuclide has fteqw and fteqcc set-up for building. + +## Community +Join us on #freecs via irc.freenode.net 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..f0393d9 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..591c003 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..19e070a 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..676d86a 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..375a002 --- /dev/null +++ b/src/client/cmds.qc @@ -0,0 +1,204 @@ +/* + * 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)) { + case "chooseteam": + Textmenu_Toggle("TEAM_SELECT"); + break; + case "buy": + Textmenu_Toggle("BUY"); + break; + case "radio1": + Textmenu_Toggle("RADIOA"); + break; + case "radio2": + Textmenu_Toggle("RADIOB"); + break; + case "radio3": + Textmenu_Toggle("RADIOC"); + break; + case "nightvision": + /*Nightvision_Toggle();*/ + break; + case "drop": + sendevent("DropWeapon", ""); + break; + case "glock": + sendevent("BuyWeapon", "f", WEAPON_GLOCK18); + break; + case "usp": + sendevent("BuyWeapon", "f", WEAPON_USP45); + break; + case "p228": + sendevent("BuyWeapon", "f", WEAPON_P228); + break; + case "deagle": + sendevent("BuyWeapon", "f", WEAPON_DEAGLE); + break; + case "fn57": + sendevent("BuyWeapon", "f", WEAPON_FIVESEVEN); + break; + case "elites": + sendevent("BuyWeapon", "f", WEAPON_ELITES); + break; + case "m3": + sendevent("BuyWeapon", "f", WEAPON_M3); + break; + case "xm1014": + sendevent("BuyWeapon", "f", WEAPON_XM1014); + break; + case "tmp": + sendevent("BuyWeapon", "f", WEAPON_TMP); + break; + case "mac10": + sendevent("BuyWeapon", "f", WEAPON_MAC10); + break; + case "mp5": + sendevent("BuyWeapon", "f", WEAPON_MP5); + break; + case "ump45": + sendevent("BuyWeapon", "f", WEAPON_UMP45); + break; + case "p90": + sendevent("BuyWeapon", "f", WEAPON_P90); + break; + case "ak47": + sendevent("BuyWeapon", "f", WEAPON_AK47); + break; + case "m4a1": + sendevent("BuyWeapon", "f", WEAPON_M4A1); + break; + case "sg552": + sendevent("BuyWeapon", "f", WEAPON_SG552); + break; + case "aug": + sendevent("BuyWeapon", "f", WEAPON_AUG); + break; + case "scout": + sendevent("BuyWeapon", "f", WEAPON_SCOUT); + break; + case "sg550": + sendevent("BuyWeapon", "f", WEAPON_SG550); + break; + case "awp": + sendevent("BuyWeapon", "f", WEAPON_AWP); + break; + case "g3sg1": + sendevent("BuyWeapon", "f", WEAPON_G3SG1); + break; + case "m249": + sendevent("BuyWeapon", "f", WEAPON_PARA); + break; + case "buyammo1": + case "primammo": + sendevent("AmmoBuyPrimary", ""); + break; + case "buyammo2": + case "secammo": + sendevent("AmmoBuySecondary", ""); + break; + case "vest": + sendevent("BuyEquipment", "f", 0); + break; + case "vesthelm": + sendevent("BuyEquipment", "f", 1); + break; + case "flash": + sendevent("BuyEquipment", "f", 2); + break; + case "hegren": + sendevent("BuyEquipment", "f", 3); + break; + case "vsgren": + sendevent("BuyEquipment", "f", 4); + break; + case "defuser": + sendevent("BuyEquipment", "f", 5); + break; + case "nvg": + sendevent("BuyEquipment", "f", 6); + break; + case "coverme": + sendevent("Radio", "f", RADIO_CT_COVERME); + break; + case "takepoint": + sendevent("Radio", "f", RADIO_CT_POINT); + break; + case "takepoint": + sendevent("Radio", "f", RADIO_POSITION); + break; + case "regroup": + sendevent("Radio", "f", RADIO_REGROUP); + break; + case "followme": + sendevent("Radio", "f", RADIO_FOLLOWME); + break; + case "takingfire": + sendevent("Radio", "f", RADIO_FIREASSIS); + break; + case "go": + sendevent("Radio", "f", RADIO_GO); + break; + case "fallback": + sendevent("Radio", "f", RADIO_FALLBACK); + break; + case "sticktog": + sendevent("Radio", "f", RADIO_STICKTOG); + break; + case "getinpos": + sendevent("Radio", "f", RADIO_COM_GETINPOS); + break; + case "stormfront": + sendevent("Radio", "f", RADIO_STORMFRONT); + break; + case "report": + sendevent("Radio", "f", RADIO_COM_REPORTIN); + break; + case "roger": + sendevent("Radio", "f", RADIO_ROGER); + break; + case "enemyspot": + sendevent("Radio", "f", RADIO_CT_ENEMYS); + break; + case "needbackup": + sendevent("Radio", "f", RADIO_CT_BACKUP); + break; + case "sectorclear": + sendevent("Radio", "f", RADIO_CLEAR); + break; + case "inposition": + sendevent("Radio", "f", RADIO_CT_INPOS); + break; + case "reportingin": + sendevent("Radio", "f", RADIO_CT_REPORTINGIN); + break; + case "getout": + sendevent("Radio", "f", RADIO_GETOUT); + break; + case "negative": + sendevent("Radio", "f", RADIO_NEGATIVE); + break; + case "enemydown": + sendevent("Radio", "f", RADIO_ENEMYDOWN); + break; + default: + return FALSE; + } + return TRUE; +} diff --git a/src/client/crosshair.qc b/src/client/crosshair.qc new file mode 100644 index 0000000..b2e7fd0 --- /dev/null +++ b/src/client/crosshair.qc @@ -0,0 +1,123 @@ +/* + * 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. + */ + +#define CS_CROSS_COLOR [0,1,0] +#define CS_CROSS_ALPHA 1.0f + +void +Cstrike_DrawCrosshair(void) +{ + player pl = (player)self; + int cross_dist; + int line_length; + + /* these are defined in the weapon-code files */ + float distance = pl.cs_cross_mindist; + float delta = pl.cs_cross_deltadist; + + if (!(pl.flags & FL_ONGROUND)) { + distance = distance * 2.0f; + } else if (pl.flags & FL_CROUCHING) { /* crouching... */ + distance = distance * 0.5f; + } else if (vlen(pl.velocity) > 120) { /* running, not walking */ + distance = distance * 1.5f; + } + + /* amount of shots that we've shot does affect our accuracy */ + if (pl.cs_shotmultiplier > pl.cs_shotmultiplier_net) { + pl.cs_crosshairdistance = min(15, pl.cs_crosshairdistance + delta); + } else if (pl.cs_crosshairdistance > distance) { + pl.cs_crosshairdistance -= (pl.cs_crosshairdistance * clframetime); + } + + pl.cs_shotmultiplier_net = pl.cs_shotmultiplier; + + if (pl.cs_crosshairdistance < distance) { + pl.cs_crosshairdistance = distance; + } + + cross_dist = ceil(pl.cs_crosshairdistance); + line_length = max(1, ((cross_dist - distance) / 2) + 5); + + /* line setup */ + vector vert1, vert2, hori1, hori2; + vert1 = vert2 = hori1 = hori2 = g_hudmins + (g_hudres / 2); + + /* vertical Lines */ + vert1[1] -= (cross_dist + line_length); + vert2[1] += cross_dist + 1; + + /* horizontal Lines */ + hori1[0] -= (cross_dist + line_length); + hori2[0] += cross_dist + 1; + + drawfill(vert1, [1, line_length], CS_CROSS_COLOR, CS_CROSS_ALPHA, DRAWFLAG_ADDITIVE); + drawfill(vert2, [1, line_length], CS_CROSS_COLOR, CS_CROSS_ALPHA, DRAWFLAG_ADDITIVE); + drawfill(hori1, [line_length, 1], CS_CROSS_COLOR, CS_CROSS_ALPHA, DRAWFLAG_ADDITIVE); + drawfill(hori2, [line_length, 1], CS_CROSS_COLOR, CS_CROSS_ALPHA, DRAWFLAG_ADDITIVE); +} + +/* AUG zoom uses this. so does the spectator cam */ +void +Cstrike_DrawSimpleCrosshair(void) +{ + static vector cross_pos; + cross_pos = g_hudmins + (g_hudres / 2) + [-12,-12]; + drawsubpic(cross_pos, [24,24], g_cs_cross, [0.1875,0], [0.1875, 0.1875], [1,1,1], 1, DRAWFLAG_NORMAL); +} + +/* +================= +HUD_DrawScope + +Tries to draw a scope whenever viewzoom < 1.0f +================= +*/ +void +Cstrike_DrawScope(void) +{ + vector scope_pos; + static float scope_offset; + static float scope_scale; + + static void Cstrike_ScopePic(vector pos, vector sz, string img) { + drawpic((pos * scope_scale) + [scope_offset, 0], img, sz * scope_scale, [1,1,1], 1.0f); + } + + // Draw the scope in the middle, seperately from the border + scope_pos = g_hudmins + (g_hudres / 2) + [-128,-128]; + drawpic(scope_pos, g_cs_scope0, [256,256], [1,1,1], 1.0f, DRAWFLAG_NORMAL); + + // Border scale to fit the screen + scope_scale = g_hudres[1] / 480; + scope_offset = g_hudmins[0] + (g_hudres[0] / 2) - ((640 * scope_scale) / 2); + + // Type 1 Border... more coming soon? + Cstrike_ScopePic([0,0], [192,112], g_cs_scope1); + Cstrike_ScopePic([192,0], [256,112], g_cs_scope2); + Cstrike_ScopePic([448,0], [192,112], g_cs_scope3); + Cstrike_ScopePic([0,112], [192,256], g_cs_scope4); + Cstrike_ScopePic([448,112], [192,256], g_cs_scope5); + Cstrike_ScopePic([0,368], [192,112], g_cs_scope6); + Cstrike_ScopePic([192,368], [256,112], g_cs_scope7); + Cstrike_ScopePic([448,368], [192,112], g_cs_scope8); + + // Rect borders left and right + if (scope_offset > 0) { + drawfill([0,0], [scope_offset, g_hudres[1]], [0,0,0], 1.0f); + drawfill([(640 * scope_scale) + scope_offset, 0], [scope_offset, g_hudres[1]], [0,0,0], 1.0f); + } +} diff --git a/src/client/defs.h b/src/client/defs.h new file mode 100644 index 0000000..c29e4d5 --- /dev/null +++ b/src/client/defs.h @@ -0,0 +1,160 @@ +/* + * 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 "hud.h" +#include "radio.h" +#include "../../../valve/src/client/obituary.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_hud10_spr; +var string g_hud11_spr; +var string g_hud12_spr; +var string g_hud13_spr; +var string g_hud14_spr; +var string g_hud15_spr; +var string g_hud16_spr; + +var string g_cs_cross; +var string g_cs_scope0; +var string g_cs_scope1; +var string g_cs_scope2; +var string g_cs_scope3; +var string g_cs_scope4; +var string g_cs_scope5; +var string g_cs_scope6; +var string g_cs_scope7; +var string g_cs_scope8; + +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; + entity m_pWeaponFX; + + /* shake */ + float m_flShakeFreq; + float m_flShakeDuration; + float m_flShakeTime; + float m_flShakeAmp; + + /* cstrike additions */ + float m_iMoneyOld; + float m_flMoneyAlpha; + float m_iMoneyDelta; + int m_iTimeUnitsOld; + float m_flTimeAlpha; + vector m_vecMoneyColor; +} g_seats[4], *pSeat; + +void HUD_DrawAmmo1(void); +void HUD_DrawAmmo2(void); +void HUD_DrawAmmo3(void); +void HUD_WeaponPickupNotify(int); +void HUD_DrawAmmoBar(vector pos, float val, float max, float a); + +void Cstrike_DrawCrosshair(void); +void Cstrike_DrawSimpleCrosshair(void); +void Cstrike_DrawScope(void); +void Textmenu_Call(string); diff --git a/src/client/entities.qc b/src/client/entities.qc new file mode 100644 index 0000000..a56f667 --- /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_C4BOMB: + w_c4bomb_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..1e01ed8 --- /dev/null +++ b/src/client/game_event.qc @@ -0,0 +1,144 @@ +/* + * 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_SMOKE: + vector vSmokePos; + vSmokePos[0] = readcoord(); + vSmokePos[1] = readcoord(); + vSmokePos[2] = readcoord(); + FX_Smokenade(vSmokePos); + 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); + } + + player pl = (player)pSeat->m_ePlayer; + if (getplayerkeyfloat(pl.entnum-1, "*team") == TEAM_CT) { + setcustomskin(pSeat->m_eViewModel, "", "geomset 0 2\n"); + } else { + setcustomskin(pSeat->m_eViewModel, "", "geomset 0 1\n"); + } + + HUD_WeaponPickupNotify(w); + break; + case EV_RADIOMSG: + Radio_PlayMessage(readbyte()); + break; + case EV_RADIOMSG2: + Radio_PlayPlayerMessage(readbyte(), readbyte()); + break; + } +} diff --git a/src/client/hud.h b/src/client/hud.h new file mode 100644 index 0000000..f9b26f2 --- /dev/null +++ b/src/client/hud.h @@ -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. + */ + +/* pre-calculated sprite definitions */ +float spr_health[4] = { + 48 / 256, // pos x + 25 / 256, // pos u + 24 / 256, // size x + 24 / 256 // size y +}; + +float spr_suit1[4] = { + 0 / 256, // pos x + 25 / 256, // pos u + 24 / 256, // size x + 24 / 256 // size y +}; + +float spr_suit2[4] = { + 24 / 256, // pos x + 25 / 256, // pos u + 24 / 256, // size x + 24 / 256 // size y +}; + +float spr_suit3[4] = { + 0 / 256, // pos x + 124 / 256, // pos u + 24 / 256, // size x + 24 / 256 // size y +}; + +float spr_suit4[4] = { + 24 / 256, // pos x + 124 / 256, // pos u + 24 / 256, // size x + 24 / 256 // size y +}; + +float spr_flash1[4] = { + 160 / 256, // pos x + 24 / 256, // pos u + 32 / 256, // size x + 32 / 256 // size y +}; + +float spr_flash2[4] = { + 112 / 256, // pos x + 24 / 256, // pos u + 48 / 256, // size x + 32 / 256 // size y +}; + +string +HUD_GetChatColorHEX(float fTeam) +{ + if (fTeam == TEAM_CT) { + return "^x7AC"; + } else if (fTeam == TEAM_T) { + return "^xC33"; + } else { + return "^xCCC"; + } +} diff --git a/src/client/hud.qc b/src/client/hud.qc new file mode 100644 index 0000000..6da96ac --- /dev/null +++ b/src/client/hud.qc @@ -0,0 +1,691 @@ +/* + * 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 HUD_DrawWeaponSelect(void); + +/* Use first frame for drawing (needs precache) */ +#define NUMSIZE_X 24/256 +#define NUMSIZE_Y 24/256 +#define HUD_ALPHA 0.5 + +float spr_hudnum[10] = { + 0 / 256, + 23 / 256, + 47 / 256, + 70 / 256, + 95 / 256, + 119 / 256, + 144 / 256, + 169 / 256, + 192 / 256, + 216 / 256 +}; + +/* precaches */ +void +HUD_Init(void) +{ + precache_model("sprites/640_logo.spr"); + + 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); + g_hud10_spr = spriteframe("sprites/640hud10.spr", 0, 0.0f); + g_hud11_spr = spriteframe("sprites/640hud11.spr", 0, 0.0f); + g_hud12_spr = spriteframe("sprites/640hud12.spr", 0, 0.0f); + g_hud13_spr = spriteframe("sprites/640hud13.spr", 0, 0.0f); + g_hud14_spr = spriteframe("sprites/640hud14.spr", 0, 0.0f); + g_hud15_spr = spriteframe("sprites/640hud15.spr", 0, 0.0f); + g_hud16_spr = spriteframe("sprites/640hud16.spr", 0, 0.0f); + + precache_model("sprites/bottom.spr"); + precache_model("sprites/bottom_left.spr"); + precache_model("sprites/bottom_right.spr"); + precache_model("sprites/left.spr"); + precache_model("sprites/radar640.spr"); + precache_model("sprites/right.spr"); + precache_model("sprites/sniper_scope.spr"); + precache_model("sprites/top.spr"); + precache_model("sprites/top_left.spr"); + precache_model("sprites/top_right.spr"); +} + +/* seperator for mainly ammo */ +void +HUD_DrawSeperator(vector pos) +{ + drawsubpic(pos, + [2,24], + g_hud7_spr, + [240/256, 0], + [2/256, 24/256], + g_hud_color, + HUD_ALPHA, + DRAWFLAG_ADDITIVE + ); +} + +/* handle single/multiple digits */ +void +HUD_DrawNumber(int iNumber, vector vecPos, float fAlpha, vector vColor) +{ + drawsubpic(vecPos, + [20,25], + g_hud7_spr, + [spr_hudnum[iNumber], 0], + [20/256, 25/256], + 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); + } +} + +/* timer */ +void +HUD_DrawTimer(int spectator) +{ + int iMinutes, iSeconds, iTens, iUnits; + vector time_pos; + + if (spectator) { + time_pos = g_hudmins + [(g_hudres[0] / 2) - 62, 16]; + } else { + time_pos = g_hudmins + [(g_hudres[0] / 2) - 62, g_hudres[1] - 42]; + } + + if (getstatf(STAT_GAMETIME) == -1) { + return; + } + + iMinutes = getstatf(STAT_GAMETIME) / 60; + iSeconds = getstatf(STAT_GAMETIME) - 60 * iMinutes; + iTens = iSeconds / 10; + iUnits = iSeconds - 10 * iTens; + + /* Flashing red numbers */ + if ((iMinutes == 0) && (iTens <= 1)) { + float fAlpha; + + /* 0:00 is fully red */ + if ((iTens == 0) && (iUnits == 0)) { + fAlpha = 1; + } else { + fAlpha = fabs(sin(time * 20)); + } + + HUD_DrawNumber(iMinutes, time_pos + [48,0], fAlpha, [1,0,0]); + HUD_DrawNumber(iTens, time_pos + [75,0], fAlpha, [1,0,0]); + HUD_DrawNumber(iUnits, time_pos + [99,0],fAlpha, [1,0,0]); + HUD_DrawNumber(iMinutes, time_pos + [48,0], 1 - fAlpha, g_hud_color); + HUD_DrawNumber(iTens, time_pos + [75,0], 1 - fAlpha, g_hud_color); + HUD_DrawNumber(iUnits, time_pos + [99,0],1 - fAlpha, g_hud_color); + + /* : symbol */ + drawsubpic(time_pos + [70,6], [3,3], g_hud7_spr, [0.9375, 0], [0.01171875, 0.01171875], [1,0,0], fAlpha, DRAWFLAG_ADDITIVE); + drawsubpic(time_pos + [70,16], [3,3], g_hud7_spr, [0.9375, 0], [0.01171875, 0.01171875], [1,0,0], fAlpha, DRAWFLAG_ADDITIVE); + drawsubpic(time_pos + [70,6], [3,3], g_hud7_spr, [0.9375, 0], [0.01171875, 0.01171875], g_hud_color, 1 - fAlpha, DRAWFLAG_ADDITIVE); + drawsubpic(time_pos + [70,16], [3,3], g_hud7_spr, [0.9375, 0], [0.01171875, 0.01171875], g_hud_color, 1 - fAlpha, DRAWFLAG_ADDITIVE); + + /* clock */ + drawsubpic(time_pos, [24,25], g_hud7_spr, [NUMSIZE_X * 6, NUMSIZE_Y * 3], [NUMSIZE_X, NUMSIZE_Y], [1,0,0], fAlpha, DRAWFLAG_ADDITIVE); + drawsubpic(time_pos, [24,25], g_hud7_spr, [NUMSIZE_X * 6, NUMSIZE_Y * 3], [NUMSIZE_X, NUMSIZE_Y], g_hud_color, 1 - fAlpha, DRAWFLAG_ADDITIVE); + } else { + if (iUnits != pSeat->m_iTimeUnitsOld) { + pSeat->m_flTimeAlpha = 1.0; + } + + if (pSeat->m_flTimeAlpha >= HUD_ALPHA) { + pSeat->m_flTimeAlpha -= clframetime * 0.5; + } else { + pSeat->m_flTimeAlpha = HUD_ALPHA; + } + HUD_DrawNumber(iMinutes, time_pos + [48,0], pSeat->m_flTimeAlpha, g_hud_color); + HUD_DrawNumber(iTens, time_pos + [75,0], pSeat->m_flTimeAlpha, g_hud_color); + HUD_DrawNumber(iUnits, time_pos + [95,0], pSeat->m_flTimeAlpha, g_hud_color); + + drawsubpic(time_pos + [70,6], [3,3], g_hud7_spr, [0.9375, 0], [0.01171875, 0.01171875], g_hud_color, pSeat->m_flTimeAlpha, DRAWFLAG_ADDITIVE); + drawsubpic(time_pos + [70,16], [3,3], g_hud7_spr, [0.9375, 0], [0.01171875, 0.01171875], g_hud_color, pSeat->m_flTimeAlpha, DRAWFLAG_ADDITIVE); + + drawsubpic(time_pos, [24,25], g_hud7_spr, [NUMSIZE_X * 6, NUMSIZE_Y * 3], [NUMSIZE_X, NUMSIZE_Y], g_hud_color, pSeat->m_flTimeAlpha, DRAWFLAG_ADDITIVE); + pSeat->m_iTimeUnitsOld = iUnits; + } +} + +void +HUD_DrawMoney(void) +{ + vector money_pos; + float endalpha; + + money_pos = g_hudmins + [g_hudres[0] - 160, g_hudres[1] - 72]; + + /* if the money differs from last frame, paint it appropriately */ + if (getstati(STAT_MONEY) > pSeat->m_iMoneyOld) { + /* effect already in progress from something else, go add on top of it! */ + if (pSeat->m_flMoneyAlpha > 0) { + pSeat->m_iMoneyDelta += (pSeat->m_iMoneyOld - getstati(STAT_MONEY)); + } else { + pSeat->m_iMoneyDelta = pSeat->m_iMoneyOld - getstati(STAT_MONEY); + } + /* make it green for a short time */ + pSeat->m_vecMoneyColor = [0,1,0]; + pSeat->m_flMoneyAlpha = 1.0; + } else if (getstati(STAT_MONEY) < pSeat->m_iMoneyOld) { + /* same one as above */ + if (pSeat->m_flMoneyAlpha > 0) { + pSeat->m_iMoneyDelta += (pSeat->m_iMoneyOld - getstati(STAT_MONEY)); + } else { + pSeat->m_iMoneyDelta = pSeat->m_iMoneyOld - getstati(STAT_MONEY); + } + /* make it red */ + pSeat->m_vecMoneyColor = [1,0,0]; + pSeat->m_flMoneyAlpha = 1.0; + pSeat->m_iMoneyDelta = pSeat->m_iMoneyOld - getstati(STAT_MONEY); + } + + /* maximum alpha is variable. */ + endalpha = pSeat->m_flMoneyAlpha * HUD_ALPHA; + + /* dollar sign */ + drawsubpic( + money_pos, + [18,26], + g_hud7_spr, + [192/256, 24/256], + [18/256, 26/256], + g_hud_color, + HUD_ALPHA - endalpha, + DRAWFLAG_ADDITIVE + ); + + /* if the alpha/color effect is active, draw the money twice in their + * varying alphas/colors */ + if (pSeat->m_flMoneyAlpha > 0) { + /* red/green dollar sign */ + drawsubpic( + money_pos, + [18,26], + g_hud7_spr, + [192/256, 24/256], + [18/256, 26/256], + pSeat->m_vecMoneyColor, + endalpha, + DRAWFLAG_ADDITIVE + ); + + /* draw the +/- symbols depending on whether + * or not we made or lost money */ + if (pSeat->m_iMoneyDelta < 0) { + drawsubpic(money_pos + [0,-32], [18,23], g_hud7_spr, [0.8671875, 0.09765625], [0.0703125, 0.08984375], pSeat->m_vecMoneyColor, endalpha, DRAWFLAG_ADDITIVE); + } else { + drawsubpic(money_pos + [0,-32], [13,23], g_hud7_spr, [0.8203125, 0.09765625], [0.05078125, 0.08984375], pSeat->m_vecMoneyColor, endalpha, DRAWFLAG_ADDITIVE); + } + + /* shift the numbers for reverse drawing */ + money_pos[0] += (24 * 5); + + /* draw the red/green overlay numbers on top of ours */ + HUD_DrawNums(getstati(STAT_MONEY), money_pos, endalpha, pSeat->m_vecMoneyColor); + + /* draw above how much money we've lost/gotten from all this */ + HUD_DrawNums(fabs(pSeat->m_iMoneyDelta), money_pos + [0,-32], endalpha, pSeat->m_vecMoneyColor); + } else { + money_pos[0] += (24 * 5); + } + + /* regular number */ + HUD_DrawNums( + getstati(STAT_MONEY), + money_pos, + HUD_ALPHA - endalpha, + g_hud_color + ); + + pSeat->m_iMoneyOld = getstati(STAT_MONEY); + pSeat->m_flMoneyAlpha = max(0, pSeat->m_flMoneyAlpha - (clframetime * 0.5)); +} + +/* 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,1], + [24,24], + 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,1], + [24,24], + 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; + } + + if (pl.g_items & ITEM_HELMET) { + drawsubpic( + pos + [-80,1], + [24,24], + g_hud7_spr, + [spr_suit4[0], spr_suit4[1]], + [spr_suit4[2], spr_suit4[3]], + g_hud_color, + pSeat->m_flArmorAlpha, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos + [-80,1], + [24,24], + 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) { + if (pl.g_items & ITEM_HELMET) { + drawsubpic( + pos + [-80,1], + [24, 24 * (pl.armor / 100)], + g_hud7_spr, + [spr_suit3[0], + spr_suit3[1]], + [spr_suit3[2], spr_suit3[3] * (pl.armor / 100)], + g_hud_color, + pSeat->m_flArmorAlpha, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos + [-80,1], + [24, 24 * (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,5], [20,4], g_hud_color, a, DRAWFLAG_NORMAL); + drawfill(pos + [10,5], [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 + ); + } +} + +void +HUD_DrawZones(void) +{ + int zc = 0; + vector pos = [0,0,0]; + player pl = (player)pSeat->m_ePlayer; + + if (pl.gflags & GF_BUYZONE) { + zc++; + } + if (pl.gflags & GF_BOMBZONE) { + zc++; + } + if (pl.gflags & GF_RESCUEZONE) { + zc++; + } + if (pl.g_items & ITEM_DEFUSAL) { + zc++; + } + + pos = g_hudmins + [16, (g_hudres[1] / 2) - (zc * 16)]; + + if (pl.gflags & GF_BUYZONE) { + drawsubpic( + pos, + [32,32], + g_hud7_spr, + [96/256,148/256], + [32/256,32/256], + [0,1,0], + 1.0f, + DRAWFLAG_ADDITIVE + ); + pos[1] += 32; + } + if (pl.gflags & GF_BOMBZONE) { + drawsubpic( + pos, + [32,32], + g_hud7_spr, + [0/256,148/256], + [32/256,32/256], + [0,1,0], + 1.0f, + DRAWFLAG_ADDITIVE + ); + pos[1] += 32; + } + if (pl.gflags & GF_RESCUEZONE) { + drawsubpic( + pos, + [32,32], + g_hud7_spr, + [64/256,148/256], + [32/256,32/256], + [0,1,0], + 1.0f, + DRAWFLAG_ADDITIVE + ); + pos[1] += 32; + } + if (pl.g_items & ITEM_DEFUSAL) { + drawsubpic( + pos, + [32,32], + g_hud7_spr, + [32/256,148/256], + [32/256,32/256], + [0,1,0], + 1.0f, + DRAWFLAG_ADDITIVE + ); + pos[1] += 32; + } +} + +/* defusal etc. progress bar */ +void +HUD_DrawProgress(void) +{ + vector vSize = [540,16]; + vector vMainPos; + float progress; + + progress = getstatf(STAT_PROGRESS) / 10.0f; + + if (progress > 0) { + vMainPos = g_hudmins; + vMainPos[0] += (g_hudres[0] / 2) - (vSize[0] / 2); + vMainPos[1] += (g_hudres[1] / 2) - (vSize[1] / 2); + + vector vBar = vSize; + vBar[0] = 538 * progress; + vBar[1] = 14; + drawfill(vMainPos + [1,1], vBar, g_hud_color, 1.0, DRAWFLAG_ADDITIVE); + drawfill(vMainPos, [vSize[0], 1], g_hud_color, 1.0f); // Top + drawfill([vMainPos[0], vMainPos[1] + vSize[1]], [vSize[0], 1], g_hud_color, 1.0f); // Bottom + drawfill(vMainPos, [1, vSize[1]], g_hud_color, 1.0f); // Left + drawfill([vMainPos[0] + vSize[0], vMainPos[1]], [1, vSize[1] + 1], g_hud_color, 1.0f); // Right + } +} + +/* 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(); + + HUD_DrawMoney(); + HUD_DrawTimer(0); + + if (!(pl.g_items & ITEM_SUIT)) { + return; + } + + HUD_DrawNotify(); + HUD_DrawHealth(); + HUD_DrawArmor(); + HUD_DrawZones(); + HUD_DrawProgress(); + HUD_DrawFlashlight(); + Damage_Draw(); +} + +/* specatator main entry */ +void +HUD_DrawSpectator(void) +{ + // FIXME + Textmenu_Draw(); + Obituary_Draw(); + + HUD_DrawTimer(1); +} diff --git a/src/client/hud_weaponselect.qc b/src/client/hud_weaponselect.qc new file mode 100644 index 0000000..01bd0d4 --- /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 / 256], + [188 / 256, 72 / 256], + [208 / 256, 72 / 256], + [168 / 256, 92 / 256], + [188 / 256, 92 / 256], + [208 / 256, 92 / 256] +}; + +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, int val) +{ + drawsubpic(vecPos, [20,20], g_hud7_spr, g_vecHUDNums[val], [20/256, 20/256], 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..9281776 --- /dev/null +++ b/src/client/init.qc @@ -0,0 +1,141 @@ +/* + * 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) +{ + registercommand("chooseteam"); + registercommand("buy"); + + /* radio */ + registercommand("radio1"); + registercommand("radio2"); + registercommand("radio3"); + + registercommand("motd"); + registercommand("drop"); + registercommand("nightvision"); + + /* pistols */ + registercommand("glock"); + registercommand("usp"); + registercommand("p228"); + registercommand("deagle"); + registercommand("fn57"); + registercommand("elites"); + + /* shotties */ + registercommand("m3"); + registercommand("xm1014"); + + /* smg */ + registercommand("tmp"); + registercommand("mac10"); + registercommand("mp5"); + registercommand("ump45"); + + /* rifles */ + registercommand("p90"); + registercommand("ak47"); + registercommand("m4a1"); + registercommand("sg552"); + registercommand("aug"); + registercommand("scout"); + registercommand("sg550"); + registercommand("awp"); + registercommand("g3sg1"); + + /* lonely */ + registercommand("m249"); + + /* ammo */ + registercommand("primammo"); + registercommand("buyammo1"); + registercommand("secammo"); + registercommand("buyammo2"); + + /* equipment */ + registercommand("vest"); + registercommand("vesthelm"); + registercommand("flash"); + registercommand("hegren"); + registercommand("vsgren"); + registercommand("defuser"); + registercommand("nvg"); + + /* radio */ + registercommand("coverme"); + registercommand("takepoint"); + registercommand("holdpos"); + registercommand("regroup"); + registercommand("followme"); + registercommand("takingfire"); + registercommand("go"); + registercommand("fallback"); + registercommand("sticktog"); + registercommand("getinpos"); + registercommand("stormfront"); + registercommand("report"); + registercommand("roger"); + registercommand("enemyspot"); + registercommand("needbackup"); + registercommand("sectorclear"); + registercommand("inposition"); + registercommand("reportingin"); + registercommand("getout"); + registercommand("negative"); + registercommand("enemydown"); + + Obituary_Init(); +} + +void +ClientGame_InitDone(void) +{ + Textmenu_Call("TEAM_SELECT"); +} + +void +ClientGame_RendererRestart(string rstr) +{ + g_cs_cross = spriteframe("sprites/crosshairs.spr", 0, 0.0f); + g_cs_scope0 = spriteframe("sprites/sniper_scope.spr", 0, 0.0f); + g_cs_scope1 = spriteframe("sprites/top_left.spr", 0, 0.0f); + g_cs_scope2 = spriteframe("sprites/top.spr", 0, 0.0f); + g_cs_scope3 = spriteframe("sprites/top_right.spr", 0, 0.0f); + g_cs_scope4 = spriteframe("sprites/left.spr", 0, 0.0f); + g_cs_scope5 = spriteframe("sprites/right.spr", 0, 0.0f); + g_cs_scope6 = spriteframe("sprites/bottom_left.spr", 0, 0.0f); + g_cs_scope7 = spriteframe("sprites/bottom.spr", 0, 0.0f); + g_cs_scope8 = spriteframe("sprites/bottom_right.spr", 0, 0.0f); + + Obituary_Precache(); + + FX_Blood_Init(); + FX_BreakModel_Init(); + FX_Explosion_Init(); + FX_GibHuman_Init(); + FX_Spark_Init(); + FX_Impact_Init(); + FX_Smokenade_Init(); +} diff --git a/src/client/player.qc b/src/client/player.qc new file mode 100644 index 0000000..42bacf9 --- /dev/null +++ b/src/client/player.qc @@ -0,0 +1,149 @@ +/* + * 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); + } + } +} + +void +Player_ReceiveEntity(float new) +{ + float fl; + player pl = (player)self; + + if (new == TRUE) { + spawnfunc_player(); + pl.classname = "player"; + pl.solid = SOLID_SLIDEBOX; + pl.drawmask = MASK_ENGINE; + pl.customphysics = Empty; + setsize(pl, VEC_HULL_MIN, VEC_HULL_MAX); + } else { + int i; + //FIXME: splitscreen + if (pl.entnum == player_localentnum) { + //FIXME: splitscreen + pSeat = &g_seats[0]; + for (i = pl.sequence+1; i <= servercommandframe; i++) { + if (!getinputstate(i)) { + break; //erk?... too old? + } + input_sequence = i; + PMove_Run(); + } + + /* any differences in things that are read below are now + * officially from prediction misses. */ + } + } + + pl.sequence = servercommandframe; + + fl = readfloat(); + + /* HACK: we need to make this more reliable */ + if (fl == UPDATE_ALL) { + /* we respawned */ + pl.gravity = __NULL__; + } + + if (fl & PLAYER_MODELINDEX) + pl.modelindex = readshort(); + + if (fl & PLAYER_ORIGIN) { + pl.origin[0] = readcoord(); + pl.origin[1] = readcoord(); + } + + if (fl & PLAYER_ORIGIN_Z) + pl.origin[2] = readcoord(); + if (fl & PLAYER_ANGLES_X) + pl.pitch = readfloat(); + if (fl & PLAYER_ANGLES_Y) + pl.angles[1] = readfloat(); + if (fl & PLAYER_ANGLES_Z) + pl.angles[2] = readfloat(); + + if (fl & PLAYER_VELOCITY) { + pl.velocity[0] = readcoord(); + pl.velocity[1] = readcoord(); + } + + if (fl & PLAYER_VELOCITY_Z) + pl.velocity[2] = readcoord(); + if (fl & PLAYER_FLAGS) { + pl.flags = readfloat(); + pl.gflags = readfloat(); + } + if (fl & PLAYER_WEAPON) + pl.activeweapon = readbyte(); + if (fl & PLAYER_ITEMS) + pl.g_items = (__variant)readfloat(); + if (fl & PLAYER_HEALTH) + pl.health = readbyte(); + if (fl & PLAYER_ARMOR) + pl.armor = readbyte(); + if (fl & PLAYER_MOVETYPE) + pl.movetype = readbyte(); + if (fl & PLAYER_VIEWOFS) + pl.view_ofs[2] = readfloat(); + if (fl & PLAYER_BASEFRAME) { + pl.baseframe = readbyte(); + pl.baseframe1time = 0.0f; + pl.baseframe2time = 0.0f; + } + if (fl & PLAYER_FRAME) + pl.frame = readbyte(); + + if (fl & PLAYER_AMMO1) + pl.a_ammo1 = readbyte(); + if (fl & PLAYER_AMMO2) + pl.a_ammo2 = readbyte(); + if (fl & PLAYER_AMMO3) + pl.a_ammo3 = readbyte(); + + if (fl & PLAYER_CSSHOT) + pl.cs_shotmultiplier = readbyte(); + if (fl & PLAYER_CSSHOTTIME) + pl.cs_shottime = readfloat(); + + setorigin(pl, pl.origin); +} diff --git a/src/client/predict.qc b/src/client/predict.qc new file mode 100644 index 0000000..7dabe8f --- /dev/null +++ b/src/client/predict.qc @@ -0,0 +1,43 @@ +/* + * 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. + */ + +/* +================= +Predict_PreFrame + +We're part way through parsing new player data. +Propagate our pmove state to whatever the current frame before its stomped on +(so any non-networked state updates locally). +================= +*/ +void +GamePredict_PreFrame(player pl) +{ +} + +/* +================= +Predict_PostFrame + +We're part way through parsing new player data. +Rewind our pmove state back to before we started predicting. +(to give consistent state instead of accumulating errors) +================= +*/ +void +GamePredict_PostFrame(player pl) +{ +} diff --git a/src/client/progs.src b/src/client/progs.src new file mode 100644 index 0000000..1b33b5d --- /dev/null +++ b/src/client/progs.src @@ -0,0 +1,42 @@ +#pragma target fte +#pragma progs_dat "../../csprogs.dat" +#pragma includedir "../../../valve/src" + +#define CSQC +#define CLIENT +#define VALVE +#define CSTRIKE +#define CLASSIC_VGUI +#define GS_RENDERFX + +#includelist +../../../src/shared/fteextensions.qc +../../../src/shared/defs.h +../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 +textmenu.qc +init.qc +player.qc +entities.qc +cmds.qc +game_event.qc +../../../valve/src/client/view.qc +crosshair.qc +../../../valve/src/client/obituary.qc +hud.qc +hud_weaponselect.qc +../../../valve/src/client/scoreboard.qc +../../../valve/src/client/input.qc +radio.qc +../../../base/src/client/modelevent.qc + +../../../src/client/include.src +../../../src/shared/include.src +#endlist diff --git a/src/client/radio.h b/src/client/radio.h new file mode 100644 index 0000000..64cbe23 --- /dev/null +++ b/src/client/radio.h @@ -0,0 +1,3 @@ +void Radio_Init(void); +void Radio_PlayMessage(float); +void Radio_PlayPlayerMessage(float, float); diff --git a/src/client/radio.qc b/src/client/radio.qc new file mode 100755 index 0000000..5a44087 --- /dev/null +++ b/src/client/radio.qc @@ -0,0 +1,147 @@ +/* + * 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. + */ + +string g_cstrikeRadioWAVs[43] = { + "radio/blow.wav", + "radio/bombdef.wav", + "radio/bombpl.wav", + "radio/circleback.wav", + "radio/clear.wav", + "radio/com_followcom.wav", + "radio/com_getinpos.wav", + "radio/com_go.wav", + "radio/com_reportin.wav", + "radio/ct_affirm.wav", + "radio/ct_backup.wav", + "radio/ct_coverme.wav", + "radio/ct_enemys.wav", + "radio/ct_fireinhole.wav", + "radio/ct_imhit.wav", + "radio/ct_inpos.wav", + "radio/ct_point.wav", + "radio/ct_reportingin.wav", + "radio/ctwin.wav", + "radio/enemydown.wav", + "radio/fallback.wav", + "radio/fireassis.wav", + "radio/followme.wav", + "radio/getout.wav", + "radio/go.wav", + "radio/hitassist.wav", + "radio/hosdown.wav", + "radio/letsgo.wav", + "radio/locknload.wav", + "radio/matedown.wav", + "radio/meetme.wav", + "radio/moveout.wav", + "radio/negative.wav", + "radio/position.wav", + "radio/regroup.wav", + "radio/rescued.wav", + "radio/roger.wav", + "radio/rounddraw.wav", + "radio/sticktog.wav", + "radio/stormfront.wav", + "radio/takepoint.wav", + "radio/terwin.wav", + "radio/vip.wav" +}; + +string g_cstrikeRadioChat[43] = { + _("RADIO_BLOW"), + _("RADIO_BOMBDEF"), + _("RADIO_BOMBPL"), + _("RADIO_CIRCLEBACK"), + _("RADIO_CLEAR"), + _("RADIO_COM_FOLLOWCOM"), + _("RADIO_COM_GETINPOS"), + _("RADIO_COM_GO"), + _("RADIO_COM_REPORTIN"), + _("RADIO_CT_AFFIRM"), + _("RADIO_CT_BACKUP"), + _("RADIO_CT_COVERME"), + _("RADIO_CT_ENEMYS"), + _("RADIO_CT_FIREINHOLE"), + _("RADIO_CT_IMHIT"), + _("RADIO_CT_INPOS"), + _("RADIO_CT_POINT"), + _("RADIO_CT_REPORTINGIN"), + _("RADIO_CTWIN"), + _("RADIO_ENEMYDOWN"), + _("RADIO_FALLBACK"), + _("RADIO_FIREASSIS"), + _("RADIO_FOLLOWME"), + _("RADIO_GETOUT"), + _("RADIO_GO"), + _("RADIO_HITASSIST"), + _("RADIO_HOSDOWN"), + _("RADIO_LETSGO"), + _("RADIO_LOCKNLOAD"), + _("RADIO_MATEDOWN"), + _("RADIO_MEETME"), + _("RADIO_MOVEOUT"), + _("RADIO_NEGATIVE"), + _("RADIO_POSITION"), + _("RADIO_REGROUP"), + _("RADIO_RESCUED"), + _("RADIO_ROGER"), + _("RADIO_ROUNDDRAW"), + _("RADIO_STICKTOG"), + _("RADIO_STORMFRONT"), + _("RADIO_TAKEPOINT"), + _("RADIO_TERWIN"), + _("RADIO_VIP"), +}; + +void +Radio_Init(void) +{ + for (int i = 0; i < g_cstrikeRadioWAVs.length; i++) + precache_sound(g_cstrikeRadioWAVs[i]); +} + +/* +================= +Radio_PlayMessage + +Play a radio message that doesn't come from a player +================= +*/ +void +Radio_PlayMessage(float fMessage) +{ + sound(world, CHAN_VOICE, g_cstrikeRadioWAVs[fMessage], 1, ATTN_NONE, 0, SOUNDFLAG_NOSPACIALISE); + + if (fMessage == RADIO_CTWIN || fMessage == RADIO_TERWIN || fMessage == RADIO_ROUNDDRAW) { + CSQC_Parse_CenterPrint(sprintf("%s", g_cstrikeRadioChat[fMessage])); + } else { + CSQC_Parse_Print(sprintf("^2[RADIO]^xF80: %s", g_cstrikeRadioChat[fMessage]), PRINT_CHAT); + } +} + +/* +================= +Radio_PlayPlayerMessage + +This radio message does come from a player +================= +*/ +void +Radio_PlayPlayerMessage(float fPlayerNum, float fMessage) +{ + sound(world, CHAN_VOICE, g_cstrikeRadioWAVs[fMessage], 1, ATTN_NONE, 0, SOUNDFLAG_NOSPACIALISE); + CSQC_Parse_Print(sprintf("^2[RADIO] %s%s^xF80: %s", HUD_GetChatColorHEX(stof(getplayerkeyvalue(fPlayerNum, "*team"))), getplayerkeyvalue(fPlayerNum, "name"), g_cstrikeRadioChat[fMessage]), PRINT_CHAT); +} diff --git a/src/client/textmenu.qc b/src/client/textmenu.qc new file mode 100644 index 0000000..d368b34 --- /dev/null +++ b/src/client/textmenu.qc @@ -0,0 +1,506 @@ +/* + * 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 +TEAM_SELECT(int n) +{ + switch (n) { + case 1: + Textmenu_Call("TERRORIST_SELECT"); + break; + case 2: + Textmenu_Call("CT_SELECT"); + break; + case 5: + sendevent("JoinAuto", ""); + Textmenu_Call(""); + break; + } +} +void +TERRORIST_SELECT(int n) +{ + switch (n) { + case 1: + sendevent("JoinTeam", "f", 1); + Textmenu_Call(""); + break; + case 2: + sendevent("JoinTeam", "f", 2); + Textmenu_Call(""); + break; + case 3: + sendevent("JoinTeam", "f", 3); + Textmenu_Call(""); + break; + case 4: + sendevent("JoinTeam", "f", 4); + Textmenu_Call(""); + break; + case 5: + sendevent("JoinTeam", "f", floor(random(1,5))); + Textmenu_Call(""); + break; + } +} +void +CT_SELECT(int n) +{ + switch (n) { + case 1: + sendevent("JoinTeam", "f", 5); + Textmenu_Call(""); + break; + case 2: + sendevent("JoinTeam", "f", 6); + Textmenu_Call(""); + break; + case 3: + sendevent("JoinTeam", "f", 7); + Textmenu_Call(""); + break; + case 4: + sendevent("JoinTeam", "f", 8); + Textmenu_Call(""); + break; + case 5: + sendevent("JoinTeam", "f", floor(random(1,5))); + Textmenu_Call(""); + break; + } +} +void +BUY(int n) +{ + player pl; + pl = (player)pSeat->m_ePlayer; + int inteam = getplayerkeyfloat(pl.entnum-1, "*team"); + + switch (n) { + case 1: + Textmenu_Call(inteam == TEAM_T ? "T_BUYPISTOL" : "CT_BUYPISTOL"); + break; + case 2: + Textmenu_Call("BUYSHOTGUN"); + break; + case 3: + Textmenu_Call(inteam == TEAM_T ? "T_BUYSUBMACHINEGUN" : "CT_BUYSUBMACHINEGUN"); + break; + case 4: + Textmenu_Call(inteam == TEAM_T ? "T_BUYRIFLE" : "CT_BUYRIFLE"); + break; + case 5: + Textmenu_Call(inteam == TEAM_T ? "BUYMACHINEGUN" : "BUYMACHINEGUN"); + break; + case 6: + sendevent("AmmoBuyPrimary", ""); + Textmenu_Call(""); + break; + case 7: + sendevent("AmmoBuySecondary", ""); + Textmenu_Call(""); + break; + case 8: + Textmenu_Call(inteam == TEAM_T ? "DT_BUYITEM" : "DCT_BUYITEM"); + break; + case 10: + Textmenu_Call(""); + break; + } +} + +/* Equipment */ +void +DT_BUYITEM(int n) +{ + switch (n) { + case 1: + sendevent("BuyEquipment", "f", 0); + break; + case 2: + sendevent("BuyEquipment", "f", 1); + break; + case 3: + sendevent("BuyEquipment", "f", 2); + break; + case 4: + sendevent("BuyEquipment", "f", 3); + break; + case 5: + sendevent("BuyEquipment", "f", 4); + break; + case 7: + sendevent("BuyEquipment", "f", 6); + break; + case 10: + Textmenu_Call(""); + break; + default: + return; + } + + Textmenu_Call(""); +} +void +DCT_BUYITEM(int n) +{ + switch (n) { + case 1: + sendevent("BuyEquipment", "f", 0); + break; + case 2: + sendevent("BuyEquipment", "f", 1); + break; + case 3: + sendevent("BuyEquipment", "f", 2); + break; + case 4: + sendevent("BuyEquipment", "f", 3); + break; + case 5: + sendevent("BuyEquipment", "f", 4); + break; + case 6: + sendevent("BuyEquipment", "f", 5); + break; + case 7: + sendevent("BuyEquipment", "f", 6); + break; + case 10: + Textmenu_Call(""); + break; + default: + return; + } + + Textmenu_Call(""); +} + +/* Handguns */ +void +T_BUYPISTOL(int n) +{ + switch (n) { + case 1: + sendevent("BuyWeapon", "f", WEAPON_USP45); + break; + case 2: + sendevent("BuyWeapon", "f", WEAPON_GLOCK18); + break; + case 3: + sendevent("BuyWeapon", "f", WEAPON_DEAGLE); + break; + case 4: + sendevent("BuyWeapon", "f", WEAPON_P228); + break; + case 5: + sendevent("BuyWeapon", "f", WEAPON_ELITES); + break; + case 10: + Textmenu_Call(""); + break; + default: + return; + } + + Textmenu_Call(""); +} +void +CT_BUYPISTOL(int n) +{ + switch (n) { + case 1: + sendevent("BuyWeapon", "f", WEAPON_USP45); + break; + case 2: + sendevent("BuyWeapon", "f", WEAPON_GLOCK18); + break; + case 3: + sendevent("BuyWeapon", "f", WEAPON_DEAGLE); + break; + case 4: + sendevent("BuyWeapon", "f", WEAPON_P228); + break; + case 6: + sendevent("BuyWeapon", "f", WEAPON_FIVESEVEN); + break; + case 10: + Textmenu_Call(""); + break; + default: + return; + } + + Textmenu_Call(""); +} + +/* Shotguns */ +void +BUYSHOTGUN(int n) +{ + switch (n) { + case 1: + sendevent("BuyWeapon", "f", WEAPON_M3); + break; + case 2: + sendevent("BuyWeapon", "f", WEAPON_XM1014); + break; + case 10: + Textmenu_Call(""); + break; + default: + return; + } + + Textmenu_Call(""); +} + +/* Rifles, Sniper */ +void +T_BUYRIFLE(int n) +{ + switch (n) { + case 1: + sendevent("BuyWeapon", "f", WEAPON_AK47); + break; + case 2: + sendevent("BuyWeapon", "f", WEAPON_SG552); + break; + case 5: + sendevent("BuyWeapon", "f", WEAPON_SCOUT); + break; + case 6: + sendevent("BuyWeapon", "f", WEAPON_AWP); + break; + case 7: + sendevent("BuyWeapon", "f", WEAPON_G3SG1); + break; + case 10: + Textmenu_Call(""); + break; + default: + return; + } + + Textmenu_Call(""); +} +void +CT_BUYRIFLE(int n) +{ + switch (n) { + case 3: + sendevent("BuyWeapon", "f", WEAPON_M4A1); + break; + case 4: + sendevent("BuyWeapon", "f", WEAPON_AUG); + break; + case 5: + sendevent("BuyWeapon", "f", WEAPON_SCOUT); + break; + case 6: + sendevent("BuyWeapon", "f", WEAPON_AWP); + break; + case 7: + sendevent("BuyWeapon", "f", WEAPON_G3SG1); + break; + case 8: + sendevent("BuyWeapon", "f", WEAPON_SG550); + break; + case 10: + Textmenu_Call(""); + break; + default: + return; + } + + Textmenu_Call(""); +} + +/* SMGs */ +void +T_BUYSUBMACHINEGUN(int n) +{ + switch (n) { + case 1: + sendevent("BuyWeapon", "f", WEAPON_MP5); + break; + case 3: + sendevent("BuyWeapon", "f", WEAPON_P90); + break; + case 4: + sendevent("BuyWeapon", "f", WEAPON_MAC10); + break; + case 5: + sendevent("BuyWeapon", "f", WEAPON_UMP45); + break; + case 10: + Textmenu_Call(""); + break; + default: + return; + } + + Textmenu_Call(""); +} +void +CT_BUYSUBMACHINEGUN(int n) +{ + switch (n) { + case 1: + sendevent("BuyWeapon", "f", WEAPON_MP5); + break; + case 2: + sendevent("BuyWeapon", "f", WEAPON_TMP); + break; + case 3: + sendevent("BuyWeapon", "f", WEAPON_P90); + break; + case 5: + sendevent("BuyWeapon", "f", WEAPON_UMP45); + break; + case 10: + Textmenu_Call(""); + break; + default: + return; + } + + Textmenu_Call(""); +} + +/* Big and heavy */ +void +BUYMACHINEGUN(int n) +{ + switch (n) { + case 1: + sendevent("BuyWeapon", "f", WEAPON_PARA); + break; + case 10: + Textmenu_Call(""); + break; + default: + return; + } + + Textmenu_Call(""); +} + +/* Radio */ +void +RADIOA(int n) +{ + switch (n) { + case 1: + sendevent("Radio", "f", RADIO_CT_COVERME); + break; + case 2: + sendevent("Radio", "f", RADIO_TAKEPOINT); + break; + case 3: + sendevent("Radio", "f", RADIO_POSITION); + break; + case 4: + sendevent("Radio", "f", RADIO_REGROUP); + break; + case 5: + sendevent("Radio", "f", RADIO_FOLLOWME); + break; + case 6: + sendevent("Radio", "f", RADIO_FIREASSIS); + break; + case 10: + Textmenu_Call(""); + break; + default: + return; + } + + Textmenu_Call(""); +} + +void +RADIOB(int n) +{ + switch (n) { + case 1: + sendevent("Radio", "f", RADIO_COM_GO); + break; + case 2: + sendevent("Radio", "f", RADIO_FALLBACK); + break; + case 3: + sendevent("Radio", "f", RADIO_STICKTOG); + break; + case 4: + sendevent("Radio", "f", RADIO_COM_GETINPOS); + break; + case 5: + sendevent("Radio", "f", RADIO_STORMFRONT); + break; + case 6: + sendevent("Radio", "f", RADIO_COM_REPORTIN); + break; + case 10: + Textmenu_Call(""); + break; + default: + return; + } + + Textmenu_Call(""); +} + +void +RADIOC(int n) +{ + switch (n) { + case 1: + sendevent("Radio", "f", RADIO_CT_AFFIRM); + break; + case 2: + sendevent("Radio", "f", RADIO_CT_ENEMYS); + break; + case 3: + sendevent("Radio", "f", RADIO_CT_BACKUP); + break; + case 4: + sendevent("Radio", "f", RADIO_CLEAR); + break; + case 5: + sendevent("Radio", "f", RADIO_CT_INPOS); + break; + case 6: + sendevent("Radio", "f", RADIO_COM_REPORTIN); + break; + case 7: + sendevent("Radio", "f", RADIO_BLOW); + break; + case 8: + sendevent("Radio", "f", RADIO_NEGATIVE); + break; + case 9: + sendevent("Radio", "f", RADIO_ENEMYDOWN); + break; + case 10: + Textmenu_Call(""); + break; + default: + return; + } + + Textmenu_Call(""); +} diff --git a/src/client/vgui_chooseteam.qc b/src/client/vgui_chooseteam.qc new file mode 100644 index 0000000..683d4f9 --- /dev/null +++ b/src/client/vgui_chooseteam.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. + */ + +static CUIWindow winChooseTeam; +static CUIWindow winCTTeam; +static CUIWindow winTTeam; + +void +T_Skin1(void) +{ + sendevent("GamePlayerSpawn", "f", 1); + winTTeam.Hide(); +} +void +T_Skin2(void) +{ + sendevent("GamePlayerSpawn", "f", 2); + winTTeam.Hide(); +} +void +T_Skin3(void) +{ + sendevent("GamePlayerSpawn", "f", 3); + winTTeam.Hide(); +} +void +T_Skin4(void) +{ + sendevent("GamePlayerSpawn", "f", 4); + winTTeam.Hide(); +} + +void +CT_Skin1(void) +{ + sendevent("GamePlayerSpawn", "f", 5); + winCTTeam.Hide(); +} +void +CT_Skin2(void) +{ + sendevent("GamePlayerSpawn", "f", 6); + winCTTeam.Hide(); +} +void +CT_Skin3(void) +{ + sendevent("GamePlayerSpawn", "f", 7); + winCTTeam.Hide(); +} +void +CT_Skin4(void) +{ + sendevent("GamePlayerSpawn", "f", 8); + winCTTeam.Hide(); +} +void +VGUI_GoSpectator(void) +{ + sendevent("GamePlayerSpawn", "f", 0); + winChooseTeam.Hide(); +} + +void +VGUI_ChooseTeam_CT(void) +{ + static int initialized; + static CUIButton btnSkin1; + static CUIButton btnSkin2; + static CUIButton btnSkin3; + static CUIButton btnSkin4; + + if (!initialized) { + initialized = TRUE; + winCTTeam = spawn(CUIWindow); + winCTTeam.SetTitle("Choose Skin"); + winCTTeam.SetSize([420,320]); + + btnSkin1 = spawn(CUIButton); + btnSkin1.SetTitle("Skin 1"); + btnSkin1.SetPos([8,132]); + btnSkin1.SetFunc(CT_Skin1); + + btnSkin2 = spawn(CUIButton); + btnSkin2.SetTitle("Skin 2"); + btnSkin2.SetPos([8,132+30]); + btnSkin2.SetFunc(CT_Skin2); + + btnSkin3 = spawn(CUIButton); + btnSkin3.SetTitle("Skin 3"); + btnSkin3.SetPos([8,132+30+30]); + btnSkin3.SetFunc(CT_Skin3); + + btnSkin4 = spawn(CUIButton); + btnSkin4.SetTitle("Skin 4"); + btnSkin4.SetPos([8,132+30+30+30]); + btnSkin4.SetFunc(CT_Skin4); + + g_uiDesktop.Add(winCTTeam); + winCTTeam.Add(btnSkin1); + winCTTeam.Add(btnSkin2); + winCTTeam.Add(btnSkin3); + winCTTeam.Add(btnSkin4); + } + + winChooseTeam.Hide(); + winCTTeam.Show(); + winCTTeam.SetPos((video_res / 2) - (winCTTeam.GetSize() / 2)); +} + +void +VGUI_ChooseTeam_T(void) +{ + static int initialized; + static CUIButton btnSkin1; + static CUIButton btnSkin2; + static CUIButton btnSkin3; + static CUIButton btnSkin4; + + if (!initialized) { + initialized = TRUE; + winTTeam = spawn(CUIWindow); + winTTeam.SetTitle("Choose Skin"); + winTTeam.SetSize([420,320]); + + btnSkin1 = spawn(CUIButton); + btnSkin1.SetTitle("Skin 1"); + btnSkin1.SetPos([8,132]); + btnSkin1.SetFunc(T_Skin1); + + btnSkin2 = spawn(CUIButton); + btnSkin2.SetTitle("Skin 2"); + btnSkin2.SetPos([8,132+30]); + btnSkin2.SetFunc(T_Skin2); + + btnSkin3 = spawn(CUIButton); + btnSkin3.SetTitle("Skin 3"); + btnSkin3.SetPos([8,132+30+30]); + btnSkin3.SetFunc(T_Skin3); + + btnSkin4 = spawn(CUIButton); + btnSkin4.SetTitle("Skin 4"); + btnSkin4.SetPos([8,132+30+30+30]); + btnSkin4.SetFunc(T_Skin4); + + g_uiDesktop.Add(winTTeam); + winTTeam.Add(btnSkin1); + winTTeam.Add(btnSkin2); + winTTeam.Add(btnSkin3); + winTTeam.Add(btnSkin4); + } + + winChooseTeam.Hide(); + winTTeam.Show(); + winTTeam.SetPos((video_res / 2) - (winTTeam.GetSize() / 2)); +} + +void +VGUI_ChooseTeam(void) +{ + static int initialized; + static CUIButton btnGoCT; + static CUIButton btnGoT; + static CUIButton btnGoSpectator; + + if (!initialized) { + initialized = TRUE; + winChooseTeam = spawn(CUIWindow); + winChooseTeam.SetTitle("Choose Team"); + winChooseTeam.SetSize('420 320'); + + btnGoCT = spawn(CUIButton); + btnGoCT.SetTitle("Counter-Terrorists"); + btnGoCT.SetPos('8 132'); + btnGoCT.SetFunc(VGUI_ChooseTeam_CT); + + btnGoT = spawn(CUIButton); + btnGoT.SetTitle("Terrorists"); + btnGoT.SetPos('8 162'); + btnGoT.SetFunc(VGUI_ChooseTeam_T); + + btnGoSpectator = spawn(CUIButton); + btnGoSpectator.SetTitle("Spectator"); + btnGoSpectator.SetPos('8 192'); + btnGoSpectator.SetFunc(VGUI_GoSpectator); + + g_uiDesktop.Add(winChooseTeam); + winChooseTeam.Add(btnGoCT); + winChooseTeam.Add(btnGoT); + winChooseTeam.Add(btnGoSpectator); + } + + winChooseTeam.Show(); + winChooseTeam.SetPos((video_res / 2) - (winChooseTeam.GetSize() / 2)); +} 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..0e74cfc --- /dev/null +++ b/src/server/ammo.qc @@ -0,0 +1,312 @@ +/* + * 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. + */ + +/* Ammo information courtesy of https://wiki.alliedmods.net/CS_weapons_information */ + +enum +{ + CALIBER_NONE, + CALIBER_50AE, + CALIBER_762MM, + CALIBER_556MM, + CALIBER_556MMBOX, + CALIBER_338MAG, + CALIBER_9MM, + CALIBER_BUCKSHOT, + CALIBER_45ACP, + CALIBER_357SIG, + CALIBER_57MM +}; + +typedef struct +{ + int a_size; + int a_max; + int price; +} ammoinfo_t; + +ammoinfo_t cs_ammoinfo[11] = { + /* CALIBER_NONE */ + { + .a_size = 0, + .a_max = 0, + .price = 0 + }, + /* CALIBER_50AE */ + { + .a_size = 7, + .a_max = 35, + .price = 40 + }, + /* CALIBER_762MM */ + { + .a_size = 30, + .a_max = 90, + .price = 80 + }, + /* CALIBER_556MM */ + { + .a_size = 30, + .a_max = 90, + .price = 60 + }, + /* CALIBER_556MMBOX */ + { + .a_size = 30, + .a_max = 200, + .price = 60 + }, + /* CALIBER_338MAG */ + { + .a_size = 10, + .a_max = 30, + .price = 125 + }, + /* CALIBER_9MM */ + { + .a_size = 30, + .a_max = 150, + .price = 20 + }, + /* CALIBER_BUCKSHOT */ + { + .a_size = 8, + .a_max = 32, + .price = 65 + }, + /* CALIBER_45ACP */ + { + .a_size = 12, + .a_max = 100, + .price = 25 + }, + /* CALIBER_357SIG */ + { + .a_size = 13, + .a_max = 52, + .price = 50 + }, + /* CALIBER_57MM */ + { + .a_size = 50, + .a_max = 100, + .price = 50 + } +}; + +int +Ammo_BuyCaliber(player pl, int cal, int free) +{ + int *ptr_ammo = __NULL__; + int rv = 0; + + while (pl.money - cs_ammoinfo[cal].price > 0) { + switch (cal) { + case CALIBER_50AE: + ptr_ammo = &pl.ammo_50ae; + break; + case CALIBER_762MM: + ptr_ammo = &pl.ammo_762mm; + break; + case CALIBER_556MM: + ptr_ammo = &pl.ammo_556mm; + break; + case CALIBER_556MMBOX: + ptr_ammo = &pl.ammo_556mmbox; + break; + case CALIBER_338MAG: + ptr_ammo = &pl.ammo_338mag; + break; + case CALIBER_9MM: + ptr_ammo = &pl.ammo_9mm; + break; + case CALIBER_BUCKSHOT: + ptr_ammo = &pl.ammo_buckshot; + break; + case CALIBER_45ACP: + ptr_ammo = &pl.ammo_45acp; + break; + case CALIBER_357SIG: + ptr_ammo = &pl.ammo_357sig; + break; + case CALIBER_57MM: + ptr_ammo = &pl.ammo_57mm; + break; + default: + error("Ammo_BuyCaliber: Impossible caliber definition."); + } + + if (*ptr_ammo >= cs_ammoinfo[cal].a_max) { + break; + } + + *ptr_ammo += cs_ammoinfo[cal].a_size; + + /* clamp */ + if (*ptr_ammo >= cs_ammoinfo[cal].a_max) { + *ptr_ammo = cs_ammoinfo[cal].a_max; + } + + if (!free) + Money_AddMoney(pl, -cs_ammoinfo[cal].price); + + rv = 1; + } + + return rv; +} + +/* We want to loop through all the possible weapons in case the server + * enabled the ability to pick up more than one primary/secondary weapon */ +void +CSEv_AmmoBuySecondary(void) +{ + int cal = 0; + int ps = 0; + player pl = (player)self; + CSGameRules rules = (CSGameRules)g_grMode; + + if (rules.BuyingPossible(pl) == FALSE) { + return; + } + + for (int i = 1; i < g_weapons.length; i++) { + if ((pl.g_items & g_weapons[i].id) && (g_weapons[i].slot == 1)) { + switch (i) { + case WEAPON_USP45: + cal = CALIBER_45ACP; + break; + case WEAPON_GLOCK18: + cal = CALIBER_9MM; + break; + case WEAPON_DEAGLE: + cal = CALIBER_50AE; + break; + case WEAPON_P228: + cal = CALIBER_357SIG; + break; + case WEAPON_ELITES: + cal = CALIBER_9MM; + break; + case WEAPON_FIVESEVEN: + cal = CALIBER_57MM; + break; + } + + if (Ammo_BuyCaliber(pl, cal, FALSE) == 1) { + ps = 1; + } + } + } + + if (ps) { + Sound_Play(pl, CHAN_ITEM, "buy.ammo"); + } + Weapons_RefreshAmmo(pl); +} + +void +CSEv_AmmoBuyPrimary(void) +{ + int ps = 0; + int cal = 0; + player pl = (player)self; + CSGameRules rules = (CSGameRules)g_grMode; + + if (rules.BuyingPossible(pl) == FALSE) { + return; + } + + for (int i = 1; i < g_weapons.length; i++) { + if ((pl.g_items & g_weapons[i].id) && (g_weapons[i].slot == 0)) { + switch (i) { + case WEAPON_M3: + cal = CALIBER_BUCKSHOT; + break; + case WEAPON_XM1014: + cal = CALIBER_BUCKSHOT; + break; + case WEAPON_MP5: + cal = CALIBER_9MM; + break; + case WEAPON_P90: + cal = CALIBER_57MM; + break; + case WEAPON_UMP45: + cal = CALIBER_45ACP; + break; + case WEAPON_MAC10: + cal = CALIBER_45ACP; + break; + case WEAPON_TMP: + cal = CALIBER_9MM; + break; + case WEAPON_AK47: + cal = CALIBER_762MM; + break; + case WEAPON_SG552: + cal = CALIBER_556MM; + break; + case WEAPON_M4A1: + cal = CALIBER_556MM; + break; + case WEAPON_AUG: + cal = CALIBER_762MM; + break; + case WEAPON_SCOUT: + cal = CALIBER_762MM; + break; + case WEAPON_AWP: + cal = CALIBER_338MAG; + break; + case WEAPON_G3SG1: + cal = CALIBER_762MM; + break; + case WEAPON_SG550: + cal = CALIBER_556MM; + break; + case WEAPON_PARA: + cal = CALIBER_556MMBOX; + break; + } + + if (Ammo_BuyCaliber(pl, cal, FALSE) == 1) { + ps = 1; + } + } + } + + if (ps) { + Sound_Play(pl, CHAN_ITEM, "buy.ammo"); + } + Weapons_RefreshAmmo(pl); +} + +void +Ammo_AutoFill(player pl) +{ + if (autocvar_fcs_fillweapons == FALSE) { + return; + } + + /* TODO: */ +} + +void +Ammo_Clear(void) +{ + +} diff --git a/src/server/armoury_entity.qc b/src/server/armoury_entity.qc new file mode 100644 index 0000000..bf39393 --- /dev/null +++ b/src/server/armoury_entity.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 armoury_entity (0 0 0.8) (-16 -16 0) (16 16 16) +"targetname" Name +"target" Target when triggered. +"killtarget" Target to kill when triggered. +"item" Which weapon/equipment this item will contain +"count" How many of said item will players be able to pick up from this + +COUNTER-STRIKE (1999) ENTITY + +Items in the armoury. + +An item oriented entity that contains one or more weapon/equipment item +for players to pick up. It's limited to early Counter-Strike weapons, as +it was never updated in newer versions to add support for newly added ones. + +List of available items: + 0 = H&K MP5-Navy + 1 = Steyr Tactical (TMP) + 2 = FN P90 + 3 = Ingram MAC-10 + 4 = AK-47 + 5 = Sig SG-552 Commando + 6 = Colt M4A1 Carbine + 7 = Steyr AUG + 8 = Steyr Scout + 9 = H&K G3/SG-1 + 10 = AI Arctic Warfare/Magnum + 11 = Benneli M3 Super90 + 12 = Benneli XM1014 + 13 = FN M249 Para + 14 = Flashbang Grenade + 15 = HE Grenade + 16 = Kevlar + 17 = Kevlar + Helmet + 18 = Smoke Grenade +*/ + +var int autocvar_fcs_nopickups = FALSE; + +int g_cstrike_armouryitems[19] = { + WEAPON_MP5, + WEAPON_TMP, + WEAPON_P90, + WEAPON_MAC10, + WEAPON_AK47, + WEAPON_SG552, + WEAPON_M4A1, + WEAPON_AUG, + WEAPON_SCOUT, + WEAPON_G3SG1, + WEAPON_AWP, + WEAPON_M3, + WEAPON_XM1014, + WEAPON_PARA, + WEAPON_FLASHBANG, + WEAPON_HEGRENADE, + 0,/*EQUIPMENT_KEVLAR,*/ + 0,/*EQUIPMENT_HELMET,*/ + WEAPON_SMOKEGRENADE +}; + +string sArmouryModels[19] = { + "models/w_mp5.mdl", + "models/w_tmp.mdl", + "models/w_p90.mdl", + "models/w_mac10.mdl", + "models/w_ak47.mdl", + "models/w_sg552.mdl", + "models/w_m4a1.mdl", + "models/w_aug.mdl", + "models/w_scout.mdl", + "models/w_g3sg1.mdl", + "models/w_awp.mdl", + "models/w_m3.mdl", + "models/w_xm1014.mdl", + "models/w_m249.mdl", + "models/w_flashbang.mdl", + "models/w_hegrenade.mdl", + "models/w_kevlar.mdl", + "models/w_assault.mdl", + "models/w_smokegrenade.mdl" +}; + +class armoury_entity:CBaseEntity +{ + int m_iCount; + int m_iLeft; + int m_iItem; + + void(void) armoury_entity; + virtual void(void) touch; + virtual void(void) Respawn; + virtual void(string, string) SpawnKey; +}; + +void +armoury_entity::touch(void) +{ + if (other.classname != "player") { + return; + } + + /* temp */ + if (m_iItem == 17 || m_iItem == 16) + return; + + if (Weapons_AddItem((player)other, m_iItem, -1) == FALSE) { + return; + } + + Logging_Pickup(other, this, __NULL__); + sound(other, CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_NORM); + + m_iLeft--; + if (m_iLeft <= 0) { + Hide(); + } +} + +void +armoury_entity::Respawn(void) +{ + SetModel(m_oldModel); + setsize(this, [-16,-16,0], [16,16,16]); + solid = SOLID_TRIGGER; + m_iLeft = m_iCount; + droptofloor(); +} + +void +armoury_entity::SpawnKey(string strKey, string strValue) +{ + switch (strKey) { + case "count": + m_iCount = stoi(strValue); + break; + case "item": + int id = stoi(strValue); + + if (id < 0 || id >= 19) { + print(sprintf("^1armoury_entity with invalid item %i. ignoring\n", m_iItem)); + remove(this); + return; + } + + m_iItem = g_cstrike_armouryitems[id]; + model = sArmouryModels[id]; + break; + default: + CBaseEntity::SpawnKey(strKey, strValue); + break; + } +} + +void +armoury_entity::armoury_entity(void) +{ + if (autocvar_fcs_nopickups == TRUE) { + remove(this); + return; + } + + m_iCount = 1; + CBaseEntity::CBaseEntity(); +} diff --git a/src/server/buy.qc b/src/server/buy.qc new file mode 100644 index 0000000..c374770 --- /dev/null +++ b/src/server/buy.qc @@ -0,0 +1,205 @@ +/* + * 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. + */ + +/* values courtesy of https://wiki.alliedmods.net/Cs_weapons_information */ +int g_cstrikeWeaponPrice[] = +{ + 0, /* WEAPON_NONE */ + 1700, /* WEAPON_M3 */ + 3000, /* WEAPON_XM1014 */ + 1500, /* WEAPON_MP5 */ + 2350, /* WEAPON_P90 */ + 1700, /* WEAPON_UMP45 */ + 1400, /* WEAPON_MAC10 */ + 1250, /* WEAPON_TMP */ + 2500, /* WEAPON_AK47 */ + 3500, /* WEAPON_SG552 */ + 3100, /* WEAPON_M4A1 */ + 3500, /* WEAPON_AUG */ + 2750, /* WEAPON_SCOUT */ + 4750, /* WEAPON_AWP */ + 5000, /* WEAPON_G3SG1 */ + 4200, /* WEAPON_SG550 */ + 5750, /* WEAPON_PARA */ + 500, /* WEAPON_USP45 */ + 400, /* WEAPON_GLOCK18 */ + 650, /* WEAPON_DEAGLE */ + 600, /* WEAPON_P228 */ + 800, /* WEAPON_ELITES */ + 750, /* WEAPON_FIVESEVEN */ + 0, /* WEAPON_KNIFE */ + 300, /* WEAPON_HEGRENADE */ + 200, /* WEAPON_FLASHBANG */ + 300, /* WEAPON_SMOKEGRENADE */ + 0 /* WEAPON_C4BOMB */ +}; + +void +CSEv_BuyWeapon_f(float fWeapon) +{ + CSGameRules rules = (CSGameRules)g_grMode; + + int iWeapon; + player pl = (player)self; + iWeapon = (int)fWeapon; + + if (rules.BuyingPossible(pl) == FALSE) { + return; + } + + if (pl.team == TEAM_T) { + if (iWeapon == WEAPON_M4A1) { return; } + if (iWeapon == WEAPON_AUG) { return; } + if (iWeapon == WEAPON_SG550) { return; } + if (iWeapon == WEAPON_FIVESEVEN) { return; } + if (iWeapon == WEAPON_TMP) { return; } + } else if (pl.team == TEAM_CT) { + if (iWeapon == WEAPON_AK47) { return; } + if (iWeapon == WEAPON_SG552) { return; } + if (iWeapon == WEAPON_G3SG1) { return; } + if (iWeapon == WEAPON_ELITES) { return; } + if (iWeapon == WEAPON_MAC10) { return; } + } + + if (Weapons_IsPresent(pl, iWeapon)) + return; + + if ((pl.money - g_cstrikeWeaponPrice[iWeapon]) >= 0) { + /* let's check if we've got a limit */ + int maxit; + maxit = rules.MaxItemPerSlot(g_weapons[iWeapon].slot); + if (maxit > 0) { + int wantslot = g_weapons[iWeapon].slot; + int c = 0; + for (int i = 0; i < g_weapons.length; i++) { + if (pl.g_items & g_weapons[i].id && g_weapons[i].slot == wantslot) { + c++; + + /* we're over the slot limit. */ + if (c >= maxit) { + pl.activeweapon = i; + Weapon_DropCurrentWeapon(pl); + } + } + } + } + + Weapons_AddItem(pl, iWeapon, -1); + Money_AddMoney(pl, -g_cstrikeWeaponPrice[iWeapon]); + Sound_Play(pl, CHAN_ITEM, "buy.weapon"); + } else { + //centerprint(pl, "You have insufficient funds!"); + } +} + +int g_cstrikeUtilPrice[] = +{ + 650, /* Kevlar Vest */ + 1000, /* Kevlar Vest & Helmet */ + 200, /* Flashbang */ + 300, /* HE Grenade */ + 300, /* Smoke Grenade */ + 200, /* Defuse Kit */ + 1250 /* NightVision Goggles */ +}; + +void +CSEv_BuyEquipment_f(float fUtil) +{ + CSGameRules rules = (CSGameRules)g_grMode; + + int iUtil; + player pl = (player)self; + iUtil = (int)fUtil; + + if (rules.BuyingPossible(pl) == FALSE) { + return; + } + + if (pl.team == TEAM_T) { + if (iUtil == 5) { return; } + } + + if ((pl.money - g_cstrikeUtilPrice[iUtil]) >= 0) { + switch (iUtil) { + case 0: + if (pl.armor >= 100) + return; + + pl.armor = 100; + Sound_Play(pl, CHAN_ITEM, "buy.kevlar"); + break; + case 1: + if (pl.g_items & ITEM_HELMET && pl.armor >= 0) + return; + + pl.armor = 100; + pl.g_items |= ITEM_HELMET; + Sound_Play(pl, CHAN_ITEM, "buy.kevlar"); + break; + case 2: + if (Weapons_IsPresent(pl, WEAPON_FLASHBANG)) { + if (pl.ammo_fbgrenade >= AMMO_MAX_FLASHBANG) + return; + else + pl.ammo_fbgrenade++; + } else + Weapons_AddItem(pl, WEAPON_FLASHBANG, -1); + + Sound_Play(pl, CHAN_ITEM, "buy.weapon"); + break; + case 3: + if (Weapons_IsPresent(pl, WEAPON_HEGRENADE)) { + if (pl.ammo_hegrenade >= AMMO_MAX_HENADE) + return; + else + pl.ammo_hegrenade++; + } else + Weapons_AddItem(pl, WEAPON_HEGRENADE, -1); + + Sound_Play(pl, CHAN_ITEM, "buy.weapon"); + break; + case 4: + if (Weapons_IsPresent(pl, WEAPON_SMOKEGRENADE)) { + if (pl.ammo_smokegrenade >= AMMO_MAX_SMOKE) + return; + else + pl.ammo_smokegrenade++; + } else + Weapons_AddItem(pl, WEAPON_SMOKEGRENADE, -1); + + Sound_Play(pl, CHAN_ITEM, "buy.weapon"); + break; + case 5: + if (pl.g_items & ITEM_DEFUSAL) + return; + + pl.g_items |= ITEM_DEFUSAL; + Sound_Play(pl, CHAN_ITEM, "buy.weapon"); + break; + case 6: + if (pl.g_items & ITEM_NIGHTVISION) + return; + + pl.g_items |= ITEM_NIGHTVISION; + Sound_Play(pl, CHAN_ITEM, "buy.weapon"); + break; + } + Money_AddMoney(pl, -g_cstrikeUtilPrice[iUtil]); + } else { + //centerprint(pl, "You have insufficient funds!"); + } +} diff --git a/src/server/client.qc b/src/server/client.qc new file mode 100644 index 0000000..8343ae6 --- /dev/null +++ b/src/server/client.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. + */ + +void +Game_RunClientCommand(void) +{ + player pl = (player)self; + + pl.gflags &= ~GF_BUYZONE; + pl.gflags &= ~GF_RESCUEZONE; + pl.gflags &= ~GF_BOMBZONE; + + Footsteps_Update(); + PMove_Run(); +} + +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)); +} + +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); +} + +void +Game_SetNewParms(void) +{ + +} diff --git a/src/server/defs.h b/src/server/defs.h new file mode 100644 index 0000000..442a4dc --- /dev/null +++ b/src/server/defs.h @@ -0,0 +1,79 @@ +/* + * 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 "money.h" +#include "radio.h" +#include "../../../valve/src/server/items.h" + +var int g_cstrike_buying = 0; +var float g_cstrike_bombradius = 500; + +var int g_cs_vipzones; +var int g_cs_escapezones; +var int g_cs_bombzones; + +var int g_cs_bombplanted; +var int g_cs_roundswon_ct; +var int g_cs_roundswon_t; +var int g_cs_roundsplayed; +var int g_cs_alive_t; +var int g_cs_alive_ct; +var int g_cs_hostagesrescued; +var int g_cs_hostagestotal; +var int g_cs_roundslost_ct; +var int g_cs_roundslost_t; +var int g_cs_winstreak_ct; +var int g_cs_winstreak_t; +var int g_cs_bonus_ct; +var int g_cs_bonus_t; +var int g_cs_gamestate; +var float g_cs_gametime; + +/* Counter-Strike's own cvars */ +var int autocvar_mp_winlimit = 0; +var int autocvar_mp_halftime = 0; +var int autocvar_mp_startmoney = 800; +var float autocvar_mp_buytime = 90; +var float autocvar_mp_freezetime = 6; +var float autocvar_mp_c4timer = 45; +var float autocvar_mp_roundtime = 5; +var float autocvar_mp_timelimit = 60; +var string autocvar_motdfile = "motd.txt"; +var int autocvar_mp_friendlyfire = FALSE; + +/* new, FreeCS exclusive variables */ +var int autocvar_fcs_voxannounce = FALSE; +var int autocvar_fcs_knifeonly = FALSE; /* Disallows buying and spawning with weps */ +var int autocvar_fcs_swapteams = FALSE; /* Swaps spawnpoints */ +var int autocvar_fcs_nopickups = FALSE; /* Disable weapon items */ +var int autocvar_fcs_reward_kill = 300; +var int autocvar_fcs_penalty_pain = -150; +var int autocvar_fcs_penalty_kill = -1500; +var int autocvar_fcs_maxmoney = 16000; +var int autocvar_fcs_fillweapons = FALSE; /* This will automatically get ammo for the weapon you buy */ +var int autocvar_fcs_fix_bombtimer = TRUE; /* If true, the bomb-timer will dictate the round-end */ +var int autocvar_fcs_bombaltthrow = TRUE; /* Randomize the bomb-throw every time ever so slightly */ + +enum +{ + GAME_INACTIVE, + GAME_COMMENCING, + GAME_FREEZE, + GAME_ACTIVE, + GAME_END, + GAME_OVER +}; diff --git a/src/server/func_bomb_target.qc b/src/server/func_bomb_target.qc new file mode 100644 index 0000000..278ddf7 --- /dev/null +++ b/src/server/func_bomb_target.qc @@ -0,0 +1,63 @@ +/* + * 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 func_bomb_target (0 .5 .8) ? +"targetname" Name +"target" Target when bomb blows up. +"killtarget" Target to kill when triggered. + +COUNTER-STRIKE (1999) ENTITY + +Bomb target zone. + +Used in the bomb defusal mode (de_* maps). +Once the bomb explodes inside this volume, it'll trigger its targets. +*/ + +class func_bomb_target:CBaseTrigger +{ + void(void) func_bomb_target; + + virtual void(void) Respawn; + virtual void(void) touch; +}; + +void +func_bomb_target::touch(void) +{ + player pl = (player)other; + if (!(other.flags & FL_CLIENT)) { + return; + } + + if (pl.team != TEAM_T) { + return; + } + + pl.gflags |= GF_BOMBZONE; +} + +void +func_bomb_target::Respawn(void) +{ + InitBrushTrigger(); +} + +void +func_bomb_target::func_bomb_target(void) +{ + g_cs_bombzones++; +} diff --git a/src/server/func_buyzone.qc b/src/server/func_buyzone.qc new file mode 100644 index 0000000..17b6196 --- /dev/null +++ b/src/server/func_buyzone.qc @@ -0,0 +1,73 @@ +/* + * 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 func_buyzone (0 .5 .8) ? +"targetname" Name +"target" Target when triggered. +"killtarget" Target to kill when triggered. +"team" Limits the buyzone to be used by set team. + +COUNTER-STRIKE (1999) ENTITY + +Buy zone. + +The buyzone is a brush volume that upon creation forces players of set team +to buy weapons in its designated area. +If this entity is not present, the game will automatically spawn buyzones +around each player spawn, unless prevented using a setting inside the +info_map_parameters entity. + +Info about automatically generated buyzones: +If no func_buyzone exists, the game will create one buyzone volume with a total +width of 256 units (radius thus being 128 units) on every player spawn point. + +Choices for 'team' include: + 0 = All teams + 1 = Terrorist + 2 = Counter-Terrorist +*/ + +class func_buyzone:CBaseTrigger +{ + void(void) func_buyzone; + + virtual void(void) touch; + virtual void(void) Respawn; +}; + +void +func_buyzone::touch(void) +{ + player pl = (player)other; + if (!(other.flags & FL_CLIENT)) + return; + + if (team == 0 || team == pl.team) + pl.gflags |= GF_BUYZONE; +} + +void +func_buyzone::Respawn(void) +{ + InitBrushTrigger(); +} + +void +func_buyzone::func_buyzone(void) +{ + CBaseTrigger::CBaseTrigger(); + InitBrushTrigger(); +} diff --git a/src/server/func_escapezone.qc b/src/server/func_escapezone.qc new file mode 100644 index 0000000..53a7d92 --- /dev/null +++ b/src/server/func_escapezone.qc @@ -0,0 +1,38 @@ +/* + * 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 func_escapezone (0 .5 .8) ? +"targetname" Name +"target" Target when triggered. +"killtarget" Target to kill when triggered. + +COUNTER-STRIKE (1999) ENTITY + +Terrorist escape zone. + +Used in the Escape mode (es_* maps). +*/ + +class func_escapezone:CBaseTrigger +{ + virtual void(void) Respawn; +}; + +void +func_escapezone::Respawn(void) +{ + InitBrushTrigger(); +} diff --git a/src/server/func_hostage_rescue.qc b/src/server/func_hostage_rescue.qc new file mode 100644 index 0000000..fce9237 --- /dev/null +++ b/src/server/func_hostage_rescue.qc @@ -0,0 +1,85 @@ +/* + * 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 func_hostage_rescue (0 .5 .8) ? +"targetname" Name +"target" Target when triggered. +"killtarget" Target to kill when triggered. + +COUNTER-STRIKE (1999) ENTITY + +Hostage rescue zone. + +Used in the Hostage Rescue mode (cs_* maps). +If neither a func_hostage_rescue or a info_hostage_rescue is placed, +zones will be placed in Counter-Terrorist player spawn nodes automatically. +*/ + +class func_hostage_rescue:CBaseTrigger +{ + void(void) func_hostage_rescue; + + virtual void(void) touch; + virtual void(void) Respawn; +}; + +void +func_hostage_rescue::touch(void) +{ + if (other.flags & FL_CLIENT) { + player pl = (player)other; + pl.gflags |= GF_RESCUEZONE; + return; + } + + if (other.classname != "hostage_entity") { + return; + } + + CBaseNPC hosty = (CBaseNPC)other; + + if (hosty.solid == SOLID_NOT) { + return; + } + + if (!((player)hosty.m_eFollowing)) + return; + + Radio_BroadcastMessage(RADIO_RESCUED); + g_cs_hostagesrescued++; + + Money_AddMoney((player)hosty.m_eFollowing, 1000); + + /* In Hostage Rescue, all Counter-Terrorists receive an $850 + * bonus for every hostage that was rescued, even if they lose the round. */ + Money_QueTeamReward(TEAM_CT, 850); + + CBaseEntity targa = (CBaseEntity)other; + targa.Hide(); +} + +void +func_hostage_rescue::Respawn(void) +{ + InitBrushTrigger(); +} + +void +func_hostage_rescue::func_hostage_rescue(void) +{ + CBaseTrigger::CBaseTrigger(); + InitBrushTrigger(); +} diff --git a/src/server/func_vip_safetyzone.qc b/src/server/func_vip_safetyzone.qc new file mode 100644 index 0000000..a50b644 --- /dev/null +++ b/src/server/func_vip_safetyzone.qc @@ -0,0 +1,38 @@ +/* + * 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 func_vip_safetyzone (0 .5 .8) ? +"targetname" Name +"target" Target when triggered. +"killtarget" Target to kill when triggered. + +COUNTER-STRIKE (1999) ENTITY + +VIP safety zone. + +Used in the assassination mode (as_* maps). +*/ + +class func_vip_safetyzone:CBaseTrigger +{ + virtual void(void) Respawn; +}; + +void +func_vip_safetyzone::Respawn(void) +{ + InitBrushTrigger(); +} diff --git a/src/server/game_money.qc b/src/server/game_money.qc new file mode 100644 index 0000000..4a0a695 --- /dev/null +++ b/src/server/game_money.qc @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2016-2019 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 +Money_AddMoney(base_player pp, int iMoneyValue) +{ + player pl = (player)pp; + dprint(sprintf("^2Money_AddMoney^7: giving %s $%i\n", pl.netname, iMoneyValue)); + pl.money += (float)iMoneyValue; + + if (pl.money > autocvar_fcs_maxmoney) { + pl.money = autocvar_fcs_maxmoney; + } + + /* Because people do tend to kill hostages... */ + if (pl.money < 0) { + pl.money = 0; + } +} + +void +Money_QueTeamReward(int t, int iMoneyValue) +{ + if (t == TEAM_T) { + g_cs_moneyreward_t += iMoneyValue; + } else { + g_cs_moneyreward_ct += iMoneyValue; + } +} + +void +Money_GiveTeamReward(base_player pl) +{ + if (pl.team == TEAM_T) { + Money_AddMoney(pl, g_cs_moneyreward_t); + } else { + Money_AddMoney(pl, g_cs_moneyreward_ct); + } +} + +void +Money_ResetTeamReward(void) +{ + g_cs_moneyreward_t = 0; + g_cs_moneyreward_ct = 0; +} + +int +Money_GetLosses(int team) +{ + if (team == TEAM_T) { + return g_cs_roundslost_t; + } else { + return g_cs_roundslost_ct; + } +} + +int +Money_HasBonus(int team) +{ + if (team == TEAM_T) { + return g_cs_bonus_t; + } else { + return g_cs_bonus_ct; + } +} + +void +Money_HandleRoundReward(int winner) +{ + int loser = -1; + + if (winner == TEAM_CT) { + g_cs_winstreak_ct++; + g_cs_winstreak_t = 0; + g_cs_roundslost_t++; + g_cs_roundslost_ct = 0; + loser = TEAM_T; + + if (g_cs_winstreak_ct >= 2) { + g_cs_bonus_ct = TRUE; + } + } else { + g_cs_winstreak_t++; + g_cs_winstreak_ct = 0; + g_cs_roundslost_ct++; + g_cs_roundslost_t = 0; + loser = TEAM_CT; + + if (g_cs_winstreak_t >= 2) { + g_cs_bonus_t = TRUE; + } + } + + /* After the condition of a team winning two consecutive rounds is + * satisfied then the loss bonus money changes to above where their + * first loss means they receive $1500 and not $1400. */ + if (Money_HasBonus(loser)) { + switch (Money_GetLosses(loser)) { + case 1: + Money_QueTeamReward(loser, 1500); + break; + case 2: + Money_QueTeamReward(loser, 2000); + break; + case 3: + Money_QueTeamReward(loser, 2500); + break; + default: + Money_QueTeamReward(loser, 3000); + break; + } + } else { + switch (Money_GetLosses(loser)) { + case 1: + Money_QueTeamReward(loser, 1400); + break; + case 2: + Money_QueTeamReward(loser, 1900); + break; + case 3: + Money_QueTeamReward(loser, 2400); + break; + case 4: + Money_QueTeamReward(loser, 2900); + break; + default: + Money_QueTeamReward(loser, 3400); + break; + } + } +} + +void +Money_ResetRoundReward(void) +{ + g_cs_roundslost_ct = + g_cs_roundslost_t = + g_cs_winstreak_ct = + g_cs_winstreak_t = + g_cs_bonus_ct = + g_cs_bonus_t = 0; +} diff --git a/src/server/game_rules.qc b/src/server/game_rules.qc new file mode 100755 index 0000000..d5ce10e --- /dev/null +++ b/src/server/game_rules.qc @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016-2019 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. + */ + +/* Edit this for a custom gun-game order */ +int gg_order[] = { + WEAPON_KNIFE, + WEAPON_GLOCK18, + WEAPON_USP45, + WEAPON_P228, + WEAPON_FIVESEVEN, + WEAPON_ELITES, + WEAPON_DEAGLE, + WEAPON_M3, + WEAPON_XM1014, + WEAPON_TMP, + WEAPON_MAC10, + WEAPON_MP5, + WEAPON_UMP45, + WEAPON_P90, + WEAPON_AK47, + WEAPON_SCOUT, + WEAPON_M4A1, + WEAPON_SG552, + WEAPON_AUG, + WEAPON_G3SG1, + WEAPON_SG550, + WEAPON_AWP, + WEAPON_PARA +}; + + diff --git a/src/server/gamerules.h b/src/server/gamerules.h new file mode 100644 index 0000000..deebb81 --- /dev/null +++ b/src/server/gamerules.h @@ -0,0 +1,81 @@ +/* + * 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 CSGameRules: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) PlayerDeath; + virtual void(base_player) PlayerPain; + + /* level transitions */ + virtual void(base_player) LevelChangeParms; + virtual void(base_player) LevelDecodeParms; + virtual void(void) LevelNewParms; + + virtual int(base_player) BuyingPossible; +}; + +class CSSingleplayerRules:CSGameRules +{ + /* client */ + virtual void(base_player) PlayerSpawn; + virtual void(base_player) PlayerDeath; +}; + +class CSMultiplayerRules:CSGameRules +{ + entity m_eLastTSpawn; + entity m_eLastCTSpawn; + + void() CSMultiplayerRules; + + virtual void(void) InitPostEnts; + virtual void(void) FrameStart; + virtual void(base_player) PlayerDisconnect; + virtual void(base_player) PlayerSpawn; + virtual void(base_player) PlayerPreFrame; + virtual void(base_player) PlayerDeath; + virtual int(int) MaxItemPerSlot; + virtual float(base_player, string) ConsoleCommand; + + /* CS specific */ + virtual void(void) CreateRescueZones; + virtual void(void) CreateCTBuyzones; + virtual void(void) CreateTBuyzones; + virtual void(float, int) TimerBegin; + virtual void(void) TimerUpdate; + + virtual int(base_player) BuyingPossible; + virtual void(int, int, int) RoundOver; + virtual void(int) RestartRound; + virtual void(base_player) DeathCheck; + virtual void(base_player) MakeBomber; + virtual void(base_player) MakeVIP; + virtual void(void) CountPlayers; + virtual void(void) SwitchTeams; + virtual void(void) TimeOut; + + virtual void(base_player) PlayerClearWeaponry; + virtual void(base_player, int) PlayerMakePlayable; + virtual void(base_player) PlayerMakeSpectator; + virtual void(base_player, int) PlayerRespawn; + virtual entity(float) PlayerFindSpawn; +}; + +void CSEv_JoinAuto(void); diff --git a/src/server/gamerules.qc b/src/server/gamerules.qc new file mode 100644 index 0000000..cc0f4c3 --- /dev/null +++ b/src/server/gamerules.qc @@ -0,0 +1,197 @@ +/* + * 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. + */ + +var int autocvar_sv_playerkeepalive = TRUE; + +void +CSGameRules::PlayerDeath(base_player pl) +{ +} + +void +CSGameRules::PlayerPain(base_player pl) +{ +} + +int +CSGameRules::BuyingPossible(base_player pl) +{ + return FALSE; +} + +/* we check what fields have changed over the course of the frame and network + * only the ones that have actually changed */ +void +CSGameRules::PlayerPostFrame(base_player pp) +{ + Animation_PlayerUpdate(); +} + +void +CSGameRules::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_50ae = parm12; + pl.ammo_762mm = parm13; + pl.ammo_556mm = parm14; + pl.ammo_556mmbox = parm15; + pl.ammo_338mag = parm16; + pl.ammo_9mm = parm17; + pl.ammo_buckshot = parm18; + pl.ammo_45acp = parm19; + pl.ammo_357sig = parm20; + pl.ammo_57mm = parm21; + pl.ammo_hegrenade = parm22; + pl.ammo_fbgrenade = parm23; + pl.ammo_smokegrenade = parm24; + pl.usp45_mag = parm25; + pl.glock18_mag = parm26; + pl.deagle_mag = parm27; + pl.p228_mag = parm28; + pl.elites_mag = parm29; + pl.fiveseven_mag = parm30; + pl.m3_mag = parm31; + pl.xm1014_mag = parm32; + pl.mp5_mag = parm33; + pl.p90_mag = parm34; + pl.ump45_mag = parm35; + pl.mac10_mag = parm36; + pl.tmp_mag = parm37; + pl.ak47_mag = parm38; + pl.sg552_mag = parm39; + pl.m4a1_mag = parm40; + pl.aug_mag = parm41; + pl.scout_mag = parm42; + pl.awp_mag = parm43; + pl.g3sg1_mag = parm44; + pl.sg550_mag = parm45; + pl.para_mag = parm46; + pl.gflags = parm63; + + if (pl.flags & FL_CROUCHING) { + setsize(pl, VEC_CHULL_MIN, VEC_CHULL_MAX); + } else { + setsize(pl, VEC_HULL_MIN, VEC_HULL_MAX); + } +} + +void +CSGameRules::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]; + parm63 = pl.gflags; + parm64 = pl.flags; + parm10 = pl.g_items; + parm11 = pl.activeweapon; + + parm12 = pl.ammo_50ae; + parm13 = pl.ammo_762mm; + parm14 = pl.ammo_556mm; + parm15 = pl.ammo_556mmbox; + parm16 = pl.ammo_338mag; + parm17 = pl.ammo_9mm; + parm18 = pl.ammo_buckshot; + parm19 = pl.ammo_45acp; + parm20 = pl.ammo_357sig; + parm21 = pl.ammo_57mm; + parm22 = pl.ammo_hegrenade; + parm23 = pl.ammo_fbgrenade; + parm24 = pl.ammo_smokegrenade; + parm25 = pl.usp45_mag; + parm26 = pl.glock18_mag; + parm27 = pl.deagle_mag; + parm28 = pl.p228_mag; + parm29 = pl.elites_mag; + parm30 = pl.fiveseven_mag; + parm31 = pl.m3_mag; + parm32 = pl.xm1014_mag; + parm33 = pl.mp5_mag; + parm34 = pl.p90_mag; + parm35 = pl.ump45_mag; + parm36 = pl.mac10_mag; + parm37 = pl.tmp_mag; + parm38 = pl.ak47_mag; + parm39 = pl.sg552_mag; + parm40 = pl.m4a1_mag; + parm41 = pl.aug_mag; + parm42 = pl.scout_mag; + parm43 = pl.awp_mag; + parm44 = pl.g3sg1_mag; + parm45 = pl.sg550_mag; + parm46 = pl.para_mag; +} + +void +CSGameRules::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 = parm31 = parm32 = parm33 = parm34 = parm35 = + parm36 = parm37 = parm38 = parm39 = parm40 = parm41 = parm42 = + parm43 = parm44 = parm45 = parm46 = parm63= 0; + parm64 = FL_CLIENT; +} + +void +CSGameRules::PlayerConnect(base_player pl) +{ + if (Plugin_PlayerConnect(pl) == FALSE) + bprint(PRINT_HIGH, sprintf("%s connected\n", pl.netname)); +} + +void +CSGameRules::PlayerDisconnect(base_player pl) +{ + 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 = PLAYER_MODELINDEX; +} + +void +CSGameRules::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..a4a1549 --- /dev/null +++ b/src/server/gamerules_multiplayer.qc @@ -0,0 +1,1022 @@ +/* + * 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. + */ + +int +CSMultiplayerRules::MaxItemPerSlot(int slot) +{ + /* grenades */ + if (slot == 3) { + return 3; + } + return 1; +} + +void +CSMultiplayerRules::PlayerDisconnect(base_player pl) +{ + if (health > 0) + PlayerDeath(pl); +} + +void +CSMultiplayerRules::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); + + /* death-counter */ + pl.deaths++; + forceinfokey(pl, "*deaths", ftos(pl.deaths)); + + /* update score-counter */ + if (g_dmg_eTarget.flags & FL_CLIENT || g_dmg_eTarget.flags & FL_MONSTER) + if (g_dmg_eAttacker.flags & FL_CLIENT) { + if (g_dmg_eTarget == g_dmg_eAttacker || g_dmg_eTarget.team == g_dmg_eAttacker.team) + g_dmg_eAttacker.frags--; + else + g_dmg_eAttacker.frags++; + } + + Weapon_DropCurrentWeapon(pl); + + /* if we're the bomb carrier, make sure we drop the bomb. */ + if (pl.g_items & ITEM_C4BOMB) { + pl.activeweapon = WEAPON_C4BOMB; + Weapon_DropCurrentWeapon(pl); + } + + /* clear all ammo and inventory... */ + PlayerClearWeaponry(pl); + + pl.movetype = MOVETYPE_NONE; + pl.solid = SOLID_NOT; + pl.takedamage = DAMAGE_NO; + pl.gflags &= ~GF_FLASHLIGHT; + pl.armor = 0; + pl.health = 0; + + 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_DEATH1; + corpse.angles = pl.angles; + corpse.velocity = pl.velocity; + corpse.classname = "remove_me"; + + /* gamerule stuff */ + PlayerMakeSpectator(pl); + pl.classname = "player"; + forceinfokey(pl, "*dead", "1"); + forceinfokey(pl, "*team", ftos(pl.team)); + CountPlayers(); + + /* In Assassination, all Terrorists receive a $2500 + * reward if they won by killing the VIP. */ + if (self.team == TEAM_VIP) { + RoundOver(TEAM_T, 2500, FALSE); + return; + } + DeathCheck(pl); +} + +void +CSMultiplayerRules::PlayerPreFrame(base_player pl) +{ + player pp = (player)pl; + + if (pl.health <= 0) + return; + + if (g_cs_gamestate == GAME_FREEZE || pp.progress > 0.0f) { + pl.flags |= FL_FROZEN; + } +} + +void +CSMultiplayerRules::FrameStart(void) +{ + if ((g_cs_alive_t + g_cs_alive_ct) == 0) { + int iInGamePlayers = 0; + for (entity eFind = world; (eFind = find(eFind, ::classname, "player"));) { + iInGamePlayers++; + } + + if ((iInGamePlayers > 0) && (g_cs_gamestate != GAME_COMMENCING && g_cs_gamestate != GAME_END)) { + TimerBegin(2, GAME_COMMENCING); + } else if (iInGamePlayers == 0) { + g_cs_gamestate = GAME_INACTIVE; + g_cs_gametime = 0; + g_cs_roundswon_t = 0; + g_cs_roundswon_ct = 0; + g_cs_roundsplayed = 0; + } else { + TimerUpdate(); // Timer that happens once players have started joining + } + } else { + TimerUpdate(); // Normal gameplay timer + } +} + +void +CSMultiplayerRules::CreateRescueZones(void) +{ + int zones = 0; + + /* not in hostage rescue mode */ + if (g_cs_hostagestotal <= 0) { + return; + } + + /* count the already existing rescue zones. */ + for (entity e = world; (e = find(e, ::classname, "func_hostage_rescue"));) { + zones++; + } + + /* we don't need to create any additional rescue zones. */ + if (zones > 0) + return; + + /* since no buyzones are available, let's create one around every CT spawn */ + for (entity e = world; (e = find(e, ::classname, "info_player_start"));) { + info_hostage_rescue newzone = spawn(info_hostage_rescue); + setorigin(newzone, e.origin); + } +} + +void +CSMultiplayerRules::CreateCTBuyzones(void) +{ + int zones = 0; + + /* count the already existing CT zones. */ + for (entity e = world; (e = find(e, ::classname, "func_buyzone"));) { + if (e.team == TEAM_CT) { + zones++; + } + } + + /* we don't need to create any additional CT zones. */ + if (zones > 0) + return; + + /* since no buyzones are available, let's create one around every CT spawn */ + for (entity e = world; (e = find(e, ::classname, "info_player_start"));) { + info_buyzone newzone = spawn(info_buyzone); + setorigin(newzone, e.origin); + newzone.team = TEAM_CT; + } +} + +void +CSMultiplayerRules::CreateTBuyzones(void) +{ + int zones = 0; + + /* count the already existing T zones. */ + for (entity e = world; (e = find(e, ::classname, "func_buyzone"));) { + if (e.team == TEAM_T) { + zones++; + } + } + + /* we don't need to create any additional T zones. */ + if (zones > 0) + return; + + /* since no buyzones are available, let's create one around every T spawn */ + for (entity e = world; (e = find(e, ::classname, "info_player_deathmatch"));) { + info_buyzone newzone = spawn(info_buyzone); + setorigin(newzone, e.origin); + newzone.team = TEAM_T; + } +} + +void +CSMultiplayerRules::InitPostEnts(void) +{ + /* let's check if we need to create buyzones */ + switch (g_cstrike_buying) { + case BUY_CT: + CreateCTBuyzones(); + break; + case BUY_T: + CreateTBuyzones(); + break; + case BUY_NEITHER: + break; + default: + CreateCTBuyzones(); + CreateTBuyzones(); + } + + CreateRescueZones(); +} + +void +CSMultiplayerRules::TimerBegin(float tleft, int mode) +{ + if (mode == GAME_FREEZE) { + g_cs_gamestate = GAME_FREEZE; + } else if (mode == GAME_ACTIVE) { + g_cs_gamestate = GAME_ACTIVE; + } else if (mode == GAME_END) { + g_cs_gamestate = GAME_END; + } else if (mode == GAME_COMMENCING) { + g_cs_gamestate = GAME_COMMENCING; + } else if (mode == GAME_OVER) { + g_cs_gamestate = GAME_OVER; + } + + g_cs_gametime = tleft; +} + +void +CSMultiplayerRules::TimerUpdate(void) +{ + if (cvar("sv_playerslots") == 1) { + g_cs_gametime = -1; + return; + } + + if (g_cs_hostagestotal > 0) { + if (g_cs_hostagesrescued >= g_cs_hostagestotal) { + RoundOver(TEAM_CT, 0, FALSE); + return; + } + } + + // This map has been played enough we think + if (g_cs_gamestate != GAME_OVER) { + if (cvar("mp_timelimit") > 0) { + if (time >= (cvar("mp_timelimit") * 60)) { + IntermissionStart(); + g_cs_gamestate = GAME_OVER; + } + } + } + + // Okay, this means that timelimit is not the only deciding factor + if (autocvar_mp_winlimit > 0 && g_cs_gamestate != GAME_OVER) { + // It really doesn't matter who won. Do some logging perhaps? + if (g_cs_roundswon_ct == autocvar_mp_winlimit) { + IntermissionStart(); + } else if (g_cs_roundswon_t == autocvar_mp_winlimit) { + IntermissionStart(); + } + } + + if (g_cs_gamestate == GAME_INACTIVE) { + return; + } + + if (g_cs_gametime > 0) { + g_cs_gametime -= frametime; + } else { + g_cs_gametime = 0; + } + + if (g_cs_gamestate == GAME_COMMENCING || g_cs_gamestate == GAME_END) { + if (g_cs_gametime <= 0) { + if (g_cs_roundswon_t == 0 && g_cs_roundswon_ct == 0) { + Money_ResetTeamReward(); + Money_ResetRoundReward(); + RestartRound(TRUE); + } else { + if (autocvar_mp_halftime == TRUE && (autocvar_mp_winlimit / 2 == g_cs_roundsplayed)) { + Money_ResetTeamReward(); + SwitchTeams(); + RestartRound(TRUE); + } else { + RestartRound(FALSE); + } + } + } + return; + } + + if ((g_cs_gamestate == GAME_ACTIVE) || (g_cs_gamestate == GAME_FREEZE)) { + if (g_cs_gametime <= 0) { + if (g_cs_gamestate == GAME_ACTIVE) { + /* 1.5 will make the T's lose if time runs out no matter what */ + if (autocvar_fcs_fix_bombtimer == TRUE) { + if (g_cs_bombzones > 0 && g_cs_bombplanted == TRUE) { + return; + } + } + TimeOut(); + TimerBegin(5, GAME_END); // Round is over, 5 seconds til a new round starts + } else { + TimerBegin(autocvar_mp_roundtime * 60, GAME_ACTIVE); // Unfreeze + Radio_StartMessage(); + } + } + } +} + +/* +================= +BuyingPossible + +Checks if it is possible for players to buy anything +================= +*/ +int +CSMultiplayerRules::BuyingPossible(base_player pl) +{ + if (pl.health <= 0) { + return FALSE; + } + + if (g_cs_gamestate == GAME_ACTIVE) { + if (((autocvar_mp_roundtime * 60) - g_cs_gametime) > autocvar_mp_buytime) { + centerprint(pl, sprintf("%d seconds have passed...\nYou can't buy anything now!", autocvar_mp_buytime)); + return FALSE; + } + } + + if (pl.team == TEAM_VIP) { + centerprint(pl, "You are the VIP...\nYou can't buy anything!\n"); + return FALSE; + } + + if (g_cstrike_buying == BUY_NEITHER) { + centerprint(pl, "Sorry, you aren't meant\nto be buying anything.\n"); + return FALSE; + } + + if (g_cstrike_buying != BUY_BOTH) { + if (g_cstrike_buying == BUY_CT && pl.team == TEAM_T) { + centerprint(pl, "Terrorists aren't allowed to\nbuy anything on this map!\n"); + return FALSE; + } else if (g_cstrike_buying == BUY_T && pl.team == TEAM_CT) { + centerprint(pl, "CTs aren't allowed to buy\nanything on this map!\n"); + return FALSE; + } + } + + if (!(pl.gflags & GF_BUYZONE)) { + centerprint(pl, "Sorry, you aren't in a buyzone.\n"); + return FALSE; + } + + return TRUE; +} + +void +CSMultiplayerRules::MakeBomber(base_player pl) +{ + Weapons_AddItem(pl, WEAPON_C4BOMB, -1); + centerprint(pl, "You have the bomb!\nFind the target zone or DROP\nthe bomb for another Terrorist."); +} + +void +CSMultiplayerRules::MakeVIP(base_player pl) +{ + pl.team = TEAM_VIP; + PlayerRespawn(pl, pl.team); + centerprint(pl, "You are the VIP\nMake your way to the safety zones!"); + forceinfokey(pl, "*dead", "2"); +} + +/* +================= +RestartRound + +Loop through all ents and handle them +================= +*/ +void +CSMultiplayerRules::RestartRound(int iWipe) +{ + // Spawn/Respawn everyone at their team position and give them $$$ + for (entity eFind = world; (eFind = find(eFind, ::classname, "player"));) { + player pl = (player)eFind; + + if (pl.health > 0 && iWipe == FALSE) { + PlayerRespawn(pl, pl.team); + } else { + PlayerMakeSpectator(pl); + PlayerMakePlayable(pl, pl.charmodel); + } + + if (iWipe == FALSE) { + Money_GiveTeamReward(pl); + } else { + pl.money = 0; + Money_AddMoney(pl, autocvar_mp_startmoney); + } + } + + /* clear the corpses/items/bombs */ + for (entity eFind = world; (eFind = find(eFind, ::classname, "remove_me"));) { + remove(eFind); + } + + // Select a random Terrorist for the bomb, if needed + if (g_cs_bombzones > 0) { + int iRandomT = floor(random(1, (float)g_cs_alive_t + 1)); + int iPickT = 0; + + for (entity eFind = world; (eFind = find(eFind, ::classname, "player"));) { + if (eFind.team == TEAM_T) { + iPickT++; + + if (iPickT == iRandomT) { + MakeBomber((player)eFind); + } + } + } + } + + // If there is a VIP, select a random CT to be it + if (g_cs_vipzones > 0) { + int iRandomCT = floor(random(1, (float)g_cs_alive_ct + 1)); + int iPickCT = 0; + + for (entity eFind = world; (eFind = find(eFind, ::classname, "player"));) { + if (eFind.team == TEAM_CT) { + iPickCT++; + if (iPickCT == iRandomCT) { + MakeVIP((player)eFind); + } + } + } + } + + // Respawn all the entities + for (entity a = world; (a = findfloat(a, ::identity, 1));) { + CBaseEntity caw = (CBaseEntity)a; + if (caw.classname != "player") + caw.Respawn(); + } + + TimerBegin(autocvar_mp_freezetime, GAME_FREEZE); + Money_ResetTeamReward(); +} + +/* +================= +RoundOver + +This happens whenever an objective is complete or time is up +================= +*/ +void +CSMultiplayerRules::RoundOver(int iTeamWon, int iMoneyReward, int fSilent) +{ + if (g_cs_gamestate != GAME_ACTIVE) { + return; + } + + if (iTeamWon == TEAM_T) { + if (fSilent == FALSE) { + Radio_BroadcastMessage(RADIO_TERWIN); + } + g_cs_roundswon_t++; + } else if (iTeamWon == TEAM_CT) { + if (fSilent == FALSE) { + Radio_BroadcastMessage(RADIO_CTWIN); + } + g_cs_roundswon_ct++; + + /* In Bomb Defusal, if Terrorists were able to plant the bomb + * but lose the round, all Terrorists receive an $800 bonus. */ + if (g_cs_bombplanted) { + Money_QueTeamReward(TEAM_T, 800); + } + } else { + if (fSilent == FALSE) { + Radio_BroadcastMessage(RADIO_ROUNDDRAW); + } + } + + Money_HandleRoundReward(iTeamWon); + Money_QueTeamReward(iTeamWon, iMoneyReward); + TimerBegin(5, GAME_END); // Round is over, 5 seconds til a new round starts + + g_cs_hostagesrescued = 0; + g_cs_bombplanted = 0; + g_cs_roundsplayed++; + + forceinfokey(world, "teamscore_1", sprintf("%i", g_cs_roundswon_t)); + forceinfokey(world, "teamscore_2", sprintf("%i", g_cs_roundswon_ct)); +} + +/* +================= +TimeOut + +Whenever mp_roundtime was being counted down to 0 +================= +*/ +void +CSMultiplayerRules::TimeOut(void) +{ + if (g_cs_vipzones > 0) { + RoundOver(TEAM_T, 3250, FALSE); + } else if (g_cs_bombzones > 0) { + /* In Bomb Defusal, all Counter-Terrorists receive $3250 + * if they won running down the time. */ + RoundOver(TEAM_CT, 3250, FALSE); + } else if (g_cs_hostagestotal > 0) { + // TODO: Broadcast_Print: Hostages have not been rescued! + RoundOver(TEAM_T, 3250, FALSE); + } else { + RoundOver(0, 0, FALSE); + } +} + +/* +================= +SwitchTeams + +Happens rarely +================= +*/ +void +CSMultiplayerRules::SwitchTeams(void) +{ + int iCTW, iTW; + + for (entity eFind = world; (eFind = find(eFind, ::classname, "player"));) { + player pl = (player)eFind; + if (pl.team == TEAM_CT) { + pl.team = TEAM_T; + pl.charmodel -= 4; + } else if (pl.team == TEAM_T) { + pl.team = TEAM_CT; + pl.charmodel += 4; + } + forceinfokey(pl, "*team", ftos(pl.team)); + } + + iCTW = g_cs_roundswon_ct; + iTW = g_cs_roundswon_t; + + g_cs_roundswon_t = iCTW; + g_cs_roundswon_ct = iTW; + + iCTW = g_cs_alive_ct; + iTW = g_cs_alive_t; + + g_cs_alive_ct = iTW; + g_cs_alive_t = iCTW; + + forceinfokey(world, "teamscore_1", sprintf("%i", g_cs_roundswon_t)); + forceinfokey(world, "teamscore_2", sprintf("%i", g_cs_roundswon_ct)); +} + +void +CSMultiplayerRules::CountPlayers(void) +{ + g_cs_alive_t = 0; + g_cs_alive_ct = 0; + + for (entity eFind = world; (eFind = find(eFind, ::classname, "player"));) { + if (eFind.health > 0) { + if (eFind.team == TEAM_T) { + g_cs_alive_t++; + } else if (eFind.team == TEAM_CT) { + g_cs_alive_ct++; + } else if (eFind.team == TEAM_VIP) { + g_cs_alive_ct++; + } + } + } +} + +void +CSMultiplayerRules::DeathCheck(base_player pl) +{ + if ((g_cs_alive_t == 0) && (g_cs_alive_ct == 0)) { + if (g_cs_bombplanted == TRUE) { + RoundOver(TEAM_T, 3600, FALSE); + } else { + RoundOver(FALSE, 0, FALSE); + } + } else { + int winner; + if ((pl.team == TEAM_T) && (g_cs_alive_t == 0)) { + winner = TEAM_CT; + } else if ((pl.team == TEAM_CT) && (g_cs_alive_ct == 0)) { + winner = TEAM_T; + } else { + return; + } + if (g_cs_bombzones > 0) { + /* In Bomb Defusal, the winning team receives $3250 + * if they won by eliminating the enemy team. */ + if (!g_cs_bombplanted || g_cs_alive_ct == 0) { + RoundOver(winner, 3250, FALSE); + } + } else { + /* In Hostage Rescue, the winning team receives $3600 + * if they won by eliminating the enemy team. */ + RoundOver(winner, 3600, FALSE); + } + } +} + +/* +================= +PlayerFindSpawn + +Recursive function that gets the next spawnpoint +================= +*/ +entity +CSMultiplayerRules::PlayerFindSpawn(float t) +{ + entity point = world; + + if (t == TEAM_T) { + m_eLastTSpawn = find(m_eLastTSpawn, ::classname, "info_player_deathmatch"); + + if (m_eLastTSpawn == world) { + m_eLastTSpawn = find(m_eLastTSpawn, ::classname, "info_player_deathmatch"); + } + point = m_eLastTSpawn; + } else if (t == TEAM_CT) { + m_eLastCTSpawn = find(m_eLastCTSpawn, ::classname, "info_player_start"); + + if (m_eLastCTSpawn == world) { + m_eLastCTSpawn = find(m_eLastCTSpawn, ::classname, "info_player_start"); + } + point = m_eLastCTSpawn; + } else if (t == TEAM_VIP) { + point = find(world, ::classname, "info_vip_start"); + } + + if (point == world) { + error("Error: No valid spawnpoints available."); + } + + return point; +} + +/* +================= +PlayerRespawn + +Called whenever a player survived a round and needs a basic respawn. +================= +*/ +void +CSMultiplayerRules::PlayerRespawn(base_player pp, int fTeam) +{ + player pl = (player)pp; + + entity eSpawn; + forceinfokey(pl, "*spec", "0"); + eSpawn = PlayerFindSpawn(pl.team); + + pl.classname = "player"; + pl.health = pl.max_health = 100; + forceinfokey(pl, "*dead", "0"); + CountPlayers(); + + pl.takedamage = DAMAGE_YES; + pl.solid = SOLID_SLIDEBOX; + pl.movetype = MOVETYPE_WALK; + pl.flags = FL_CLIENT; + pl.iBleeds = TRUE; + pl.viewzoom = 1.0; + pl.g_items &= ~ITEM_C4BOMB; + + pl.SetOrigin(eSpawn.origin); + pl.angles = eSpawn.angles; + pl.SendFlags = UPDATE_ALL; + Client_FixAngle(pl, pl.angles); + + switch (pl.charmodel) { + case 1: + pl.model = "models/player/terror/terror.mdl"; + break; + case 2: + pl.model = "models/player/leet/leet.mdl"; + break; + case 3: + pl.model = "models/player/arctic/arctic.mdl"; + break; + case 4: + pl.model = "models/player/guerilla/guerilla.mdl"; + break; + case 5: + pl.model = "models/player/urban/urban.mdl"; + break; + case 6: + pl.model = "models/player/gsg9/gsg9.mdl"; + break; + case 7: + pl.model = "models/player/sas/sas.mdl"; + break; + case 8: + pl.model = "models/player/gign/gign.mdl"; + break; + default: + pl.model = "models/player/vip/vip.mdl"; + } + + pl.SetModel(pl.model); + pl.SetSize(VEC_HULL_MIN, VEC_HULL_MAX); + + pl.view_ofs = VEC_PLAYER_VIEWPOS; + pl.velocity = [0,0,0]; + pl.progress = 0.0f; + Weapons_SwitchBest(pl); + + /*Ammo_AutoFill(pl.fSlotPrimary); + Ammo_AutoFill(pl.fSlotSecondary);*/ +} + +void +CSMultiplayerRules::PlayerClearWeaponry(base_player pp) +{ + player pl = (player)pp; + + pl.g_items = 0x0; + pl.activeweapon = 0; + pl.ammo_50ae = 0; + pl.ammo_762mm = 0; + pl.ammo_556mm = 0; + pl.ammo_556mmbox = 0; + pl.ammo_338mag = 0; + pl.ammo_9mm = 0; + pl.ammo_buckshot = 0; + pl.ammo_45acp = 0; + pl.ammo_357sig = 0; + pl.ammo_57mm = 0; + pl.ammo_hegrenade = 0; + pl.ammo_fbgrenade = 0; + pl.ammo_smokegrenade = 0; + pl.usp45_mag = 0; + pl.glock18_mag = 0; + pl.deagle_mag = 0; + pl.p228_mag = 0; + pl.elites_mag = 0; + pl.fiveseven_mag = 0; + pl.m3_mag = 0; + pl.xm1014_mag = 0; + pl.mp5_mag = 0; + pl.p90_mag = 0; + pl.ump45_mag = 0; + pl.mac10_mag = 0; + pl.tmp_mag = 0; + pl.ak47_mag = 0; + pl.sg552_mag = 0; + pl.m4a1_mag = 0; + pl.aug_mag = 0; + pl.scout_mag = 0; + pl.awp_mag = 0; + pl.g3sg1_mag = 0; + pl.sg550_mag = 0; + pl.para_mag = 0; + pl.viewzoom = 1.0f; +} + +/* +================= +PlayerMakePlayable + +Called whenever need a full-reinit of a player. +This may be after a player had died or when the game starts for the first time. +================= +*/ +void +CSMultiplayerRules::PlayerMakePlayable(base_player pp, int chara) +{ + player pl = (player)pp; + /* spectator */ + if (chara == 0) { + PlayerSpawn(pl); + return; + } + + pl.g_items |= ITEM_SUIT; + Weapons_AddItem(pl, WEAPON_KNIFE, -1); + + /* terrorists */ + if (chara < 5) { + pl.team = TEAM_T; + if (autocvar_fcs_knifeonly == FALSE) { + Weapons_AddItem(pl, WEAPON_GLOCK18, -1); + pl.ammo_9mm = 40; + } + } else { + pl.team = TEAM_CT; + + if (autocvar_fcs_knifeonly == FALSE) { + Weapons_AddItem(pl, WEAPON_USP45, -1); + pl.ammo_45acp = 24; + } + } + + pl.ingame = TRUE; + forceinfokey(pl, "*team", ftos(pl.team)); + PlayerRespawn(pl, pl.team); +} + +/* +================= +PlayerMakeSpectator + +Force the player to become an observer. +================= +*/ +void +CSMultiplayerRules::PlayerMakeSpectator(base_player pl) +{ + pl.classname = "spectator"; + pl.health = 0; + pl.armor = 0; + pl.takedamage = DAMAGE_NO; + pl.solid = SOLID_NOT; + pl.movetype = MOVETYPE_NOCLIP; + pl.flags = FL_CLIENT; + pl.weapon = 0; + pl.viewzoom = 1.0f; + pl.model = 0; + setsize (pl, [-16,-16,-16], [16,16,16]); + pl.view_ofs = pl.velocity = [0,0,0]; + forceinfokey(pl, "*spec", "2"); + + /* clear the inventory */ + pl.items = 0x0; + pl.activeweapon = 0; +} + +/* +================= +PlayerSpawn + +Called on the client first joining the server. +================= +*/ +void +CSMultiplayerRules::PlayerSpawn(base_player pl) +{ + /* immediately put us into spectating mode */ + PlayerMakeSpectator(pl); + Spawn_ObserverCam(pl); + + /* give the initial server-joining money */ + Money_AddMoney(pl, autocvar_mp_startmoney); + + /* we don't belong to any team */ + pl.team = 0; + forceinfokey(pl, "*team", "0"); +} + +float +CSMultiplayerRules::ConsoleCommand(base_player pp, string cmd) +{ + tokenize(cmd); + + switch (argv(0)) { + case "bot_add": + entity bot_ent = Bot_AddQuick(); + if (bot_ent) { + bot_ent.think = CSEv_JoinAuto; + bot_ent.nextthink = time; + } + break; + default: + return FALSE; + } + + return TRUE; +} + +void +CSMultiplayerRules::CSMultiplayerRules(void) +{ + forceinfokey(world, "teams", "2"); + forceinfokey(world, "team_1", "Terrorist"); + forceinfokey(world, "teamscore_1", "0"); + forceinfokey(world, "team_2", "Counter-Terrorist"); + forceinfokey(world, "teamscore_2", "0"); +} + +/* +================= +CSEv_JoinTeam_f + +Event Handling, called by the Client codebase via 'sendevent' +================= +*/ +void CSEv_JoinTeam_f(float flChar) +{ + CSMultiplayerRules rules = (CSMultiplayerRules)g_grMode; + player pl = (player)self; + + if (pl.team == TEAM_VIP) { + centerprint(pl, "You are the VIP!\nYou cannot switch roles now.\n"); + return; + } + + // alive and are trying to switch teams, so subtract us from the Alive_Team counter. + if (pl.health > 0) { + rules.PlayerKill(pl); + } + + switch (g_cs_gamestate) { + /* spawn the players immediately when its in the freeze state */ + case GAME_FREEZE: + pl.charmodel = (int)flChar; + rules.PlayerMakePlayable(pl, (int)flChar); + + if ((pl.team == TEAM_T) && (g_cs_alive_t == 1)) { + if (g_cs_bombzones > 0) { + rules.MakeBomber(pl); + } + } else if ((pl.team == TEAM_CT) && (g_cs_alive_ct == 1)) { + if (g_cs_vipzones > 0) { + rules.MakeVIP(pl); + } + } + + break; + /* otherwise, just prepare their fields for the next round */ + default: + if (flChar == 0) { + rules.PlayerSpawn(pl); + return; + } + + if (flChar < 5) { + pl.team = TEAM_T; + } else { + pl.team = TEAM_CT; + } + + rules.PlayerMakeSpectator(pl); + pl.classname = "player"; + pl.charmodel = (int)flChar; + pl.health = 0; + forceinfokey(pl, "*dead", "1"); + forceinfokey(pl, "*team", ftos(pl.team)); + break; + } + + pl.frags = 0; + pl.deaths = 0; + forceinfokey(pl, "*deaths", ftos(pl.deaths)); + + rules.CountPlayers(); + + /* if no players are present in the chosen team, force restart round */ + if ((pl.team == TEAM_T) && (g_cs_alive_t == 0)) { + rules.RoundOver(FALSE, 0, FALSE); + } else if ((pl.team == TEAM_CT) && (g_cs_alive_ct == 0)) { + rules.RoundOver(FALSE, 0, FALSE); + } +} + +void CSEv_JoinAuto(void) +{ + int ct_count = 0; + int t_count = 1; + + for (entity eFind = world; (eFind = find(eFind, ::classname, "player"));) { + player pl = (player)eFind; + if (pl.team == TEAM_T) { + t_count++; + } + if (pl.team == TEAM_CT) { + ct_count++; + } + } + + if (ct_count >= t_count) { + CSEv_JoinTeam_f(floor(random(1,5))); + } else { + CSEv_JoinTeam_f(floor(random(5,9))); + } +} diff --git a/src/server/gamerules_singleplayer.qc b/src/server/gamerules_singleplayer.qc new file mode 100644 index 0000000..b4ecae6 --- /dev/null +++ b/src/server/gamerules_singleplayer.qc @@ -0,0 +1,74 @@ +/* + * 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 +CSSingleplayerRules::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 = 0; + + if (pl.health < -50) { + pl.health = 0; + FX_GibHuman(pl.origin); + return; + } + + pl.health = 0; +} + +void +CSSingleplayerRules::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"; + 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.SendFlags = UPDATE_ALL; + pl.customphysics = Empty; + pl.iBleeds = TRUE; + forceinfokey(pl, "*spec", "0"); + forceinfokey(pl, "*deaths", ftos(pl.deaths)); + + 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/hostage_entity.qc b/src/server/hostage_entity.qc new file mode 100644 index 0000000..0184df6 --- /dev/null +++ b/src/server/hostage_entity.qc @@ -0,0 +1,229 @@ +/* + * 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 hostage_entity (0 1 0) (-16 -16 0) (16 16 72) +"targetname" Name +"target" Target when triggered. +"killtarget" Target to kill when triggered. +"model" "models/hostage.mdl" +"skin" "0" + +COUNTER-STRIKE (1999) ENTITY + +Hostage NPC. + +Used in the Hostage Rescue mode (cs_* maps). + +*/ + +enum +{ + HOSA_WALK, + HOSA_WALKSCARED, + HOSA_RUN, + HOSA_RUNSCARED, + HOSA_RUNLOOK, + HOSA_180LEFT, + HOSA_180RIGHT, + HOSA_FLINCH, + HOSA_PAIN, + HOSA_PAINLEFT, + HOSA_PAINRIGHT, + HOSA_PAINLEGLEFT, + HOSA_PAINLEGRIGHT, + HOSA_IDLE1, + HOSA_IDLE2, + HOSA_IDLE3, + HOSA_IDLE4, + HOSA_IDLE5, + HOSA_IDLE6, + HOSA_SCARED_END, + HOSA_SCARED1, + HOSA_SCARED2, + HOSA_SCARED3, + HOSA_SCARED4, + HOSA_PANIC, + HOSA_FEAR1, + HOSA_FEAR2, + HOSA_CRY, + HOSA_SCI1, + HOSA_SCI2, + HOSA_SCI3, + HOSA_DIE_SIMPLE, + HOSA_DIE_FORWARD1, + HOSA_DIE_FORWARD2, + HOSA_DIE_BACKWARD, + HOSA_DIE_HEADSHOT, + HOSA_DIE_GUTSHOT, + HOSA_LYING1, + HOSA_LYING2, + HOSA_DEADSIT, + HOSA_DEADTABLE1, + HOSA_DEADTABLE2, + HOSA_DEADTABLE3 +}; + +class hostage_entity:CBaseNPC +{ + int m_iUsedOnce; + + void(void) hostage_entity; + + 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; +}; + +int +hostage_entity::AnimIdle(void) +{ + return HOSA_IDLE1; +} + +int +hostage_entity::AnimWalk(void) +{ + return HOSA_WALK; +} + +int +hostage_entity::AnimRun(void) +{ + return HOSA_RUN; +} + +void +hostage_entity::OnPlayerUse(void) +{ + if (eActivator.team == TEAM_T) { + return; + } + + if (m_eFollowing == world) + Sound_Speak(this, "hostage_entity.follow"); + + /* CT reward, first time only */ + if (m_iUsedOnce == FALSE) { + Money_AddMoney((player)eActivator, 150); + m_iUsedOnce = TRUE; + } + + CBaseNPC::OnPlayerUse(); +} + +void +hostage_entity::Pain(void) +{ + switch (g_dmg_iHitBody) { + case BODY_HEAD: + case BODY_DEFAULT: + case BODY_CHEST: + case BODY_STOMACH: + AnimPlay(HOSA_PAIN); + break; + case BODY_ARMLEFT: + AnimPlay(HOSA_PAINLEFT); + break; + case BODY_ARMRIGHT: + AnimPlay(HOSA_PAINRIGHT); + break; + case BODY_LEGLEFT: + AnimPlay(HOSA_PAINLEGLEFT); + break; + case BODY_LEGRIGHT: + AnimPlay(HOSA_PAINLEGRIGHT); + break; + } + + /* penalties */ + if (g_dmg_eAttacker.classname != "player") + return; + + Money_AddMoney((base_player)g_dmg_eAttacker, -(g_dmg_iDamage * 25)); +} + +void +hostage_entity::Death(void) +{ + WarnAllies(); + + if (style != MONSTER_DEAD) { + SetFrame(HOSA_DIE_SIMPLE + floor(random(0, 6))); + } + + /* now mark our state as 'dead' */ + CBaseNPC::Death(); + + /* penalties */ + if (g_dmg_eAttacker.classname != "player") + return; + + if (g_dmg_iDamage >= 100) + Money_AddMoney((base_player)g_dmg_eAttacker, -2500); + else + Money_AddMoney((base_player)g_dmg_eAttacker, -500); + + Radio_BroadcastMessage(RADIO_HOSDOWN); +} + +void +hostage_entity::Respawn(void) +{ + CBaseNPC::Respawn(); + m_iFlags |= MONSTER_CANFOLLOW; + m_iUsedOnce = FALSE; + PlayerUse = OnPlayerUse; +} + +void +hostage_entity::hostage_entity(void) +{ + Sound_Precache("hostage_entity.follow"); + + m_talkAnswer = ""; + m_talkAsk = ""; + m_talkAllyShot = ""; + m_talkGreet = ""; + m_talkIdle = ""; + m_talkHearing = ""; + m_talkSmelling = ""; + m_talkStare = ""; + m_talkSurvived = ""; + m_talkWounded = ""; + + m_talkPlayerAsk = ""; + m_talkPlayerGreet = ""; + m_talkPlayerIdle = ""; + m_talkPlayerWounded1 = ""; + m_talkPlayerWounded2 = ""; + m_talkPlayerWounded3 = ""; + m_talkUnfollow = ""; + m_talkFollow = ""; + m_talkStopFollow = ""; + + spawnflags |= MSF_MULTIPLAYER; + model = "models/hostage.mdl"; + netname = "Hostage"; + base_health = 100; + base_mins = [-16,-16,0]; + base_maxs = [16,16,72]; + CBaseNPC::CBaseNPC(); + g_cs_hostagestotal++; +} diff --git a/src/server/info_buyzone.qc b/src/server/info_buyzone.qc new file mode 100644 index 0000000..99203ac --- /dev/null +++ b/src/server/info_buyzone.qc @@ -0,0 +1,50 @@ +/* + * 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 info_buyzone (0 0 0.8) (-16 -16 0) (16 16 16) +"targetname" Name +"target" Target when triggered. +"killtarget" Target to kill when triggered. + +COUNTER-STRIKE (1999) ENTITY + +Buy zone. +See func_buyzone for more information. +*/ + +class info_buyzone +{ + void(void) info_buyzone; + virtual void(void) touch; +}; + +void +info_buyzone::touch(void) +{ + player pl = (player)other; + if (!(other.flags & FL_CLIENT)) + return; + + if (team == 0 || team == pl.team) + pl.gflags |= GF_BUYZONE; +} + +void +info_buyzone::info_buyzone(void) +{ + solid = SOLID_TRIGGER; + setsize(this, [-128,-128,-128], [128,128,128]); +} diff --git a/src/server/info_hostage_rescue.qc b/src/server/info_hostage_rescue.qc new file mode 100644 index 0000000..865cb58 --- /dev/null +++ b/src/server/info_hostage_rescue.qc @@ -0,0 +1,79 @@ +/* + * 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 info_hostage_rescue (0 0 0.8) (-16 -16 0) (16 16 16) +"targetname" Name +"target" Target when triggered. +"killtarget" Target to kill when triggered. + +COUNTER-STRIKE (1999) ENTITY + +Hostage rescue point. + +Used in the Hostage Rescue mode (cs_* maps). +Creates a volume around itself with a radius of 128 units. +If neither a info_hostage_rescue or a func_hostage_rescue is placed, +zones will be placed in Counter-Terrorist player spawn nodes automatically. +*/ + +class info_hostage_rescue +{ + void(void) info_hostage_rescue; + virtual void(void) touch; +}; + +void +info_hostage_rescue::touch(void) +{ + if (other.flags & FL_CLIENT) { + player pl = (player)other; + pl.gflags |= GF_RESCUEZONE; + return; + } + + if (other.classname != "hostage_entity") { + return; + } + + CBaseNPC hosty = (CBaseNPC)other; + + if (hosty.solid == SOLID_NOT) { + return; + } + + /* some custom maps are very smart... */ + if (!((player)hosty.m_eFollowing)) + return; + + Radio_BroadcastMessage(RADIO_RESCUED); + g_cs_hostagesrescued++; + + Money_AddMoney((player)hosty.m_eFollowing, 1000); + + /* In Hostage Rescue, all Counter-Terrorists receive an $850 + * bonus for every hostage they rescue, even if they lose the round. */ + Money_QueTeamReward(TEAM_CT, 850); + + CBaseEntity targa = (CBaseEntity)other; + targa.Hide(); +} + +void +info_hostage_rescue::info_hostage_rescue(void) +{ + solid = SOLID_TRIGGER; + setsize(this, [-128,-128,-128], [128,128,128]); +} diff --git a/src/server/info_map_parameters.qc b/src/server/info_map_parameters.qc new file mode 100644 index 0000000..bda90c6 --- /dev/null +++ b/src/server/info_map_parameters.qc @@ -0,0 +1,63 @@ +/* + * 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 info_map_parameters (0 0 0.8) (-16 -16 0) (16 16 16) +"targetname" Name +"target" Target when triggered. +"killtarget" Target to kill when triggered. +"buying" Override for buy-behaviour. +"bombradius" Overrides the default bomb radius. + +COUNTER-STRIKE (1999) ENTITY + +Miscellaneous mapping parameters. + +Choices for 'buying': + 0 = Both teams can buy items + 1 = Only Counter-Terrorists can buy items + 2 = Only Terrorists can buy items + 3 = Neither Counter-Terrorists nor Terrorists can buy items +*/ + +enum +{ + BUY_BOTH, + BUY_CT, + BUY_T, + BUY_NEITHER +}; + +class info_map_parameters +{ + void(void) info_map_parameters; +}; + +void +info_map_parameters::info_map_parameters(void) +{ + for (int i = 1; i < (tokenize(__fullspawndata) - 1); i += 2) { + switch (argv(i)) { + case "buying": + g_cstrike_buying = stoi(argv(i+1)); + break; + case "bombradius": + g_cstrike_bombradius = stof(argv(i+1)); + break; + default: + break; + } + } +} diff --git a/src/server/input.qc b/src/server/input.qc new file mode 100644 index 0000000..39f9987 --- /dev/null +++ b/src/server/input.qc @@ -0,0 +1,93 @@ +/* + * 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) +{ + 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; + + Weapons_AddItem(pl, WEAPON_M3, -1); + Weapons_AddItem(pl, WEAPON_XM1014, -1); + Weapons_AddItem(pl, WEAPON_MP5, -1); + Weapons_AddItem(pl, WEAPON_P90, -1); + Weapons_AddItem(pl, WEAPON_UMP45, -1); + Weapons_AddItem(pl, WEAPON_MAC10, -1); + Weapons_AddItem(pl, WEAPON_TMP, -1); + Weapons_AddItem(pl, WEAPON_AK47, -1); + Weapons_AddItem(pl, WEAPON_SG552, -1); + Weapons_AddItem(pl, WEAPON_M4A1, -1); + Weapons_AddItem(pl, WEAPON_AUG, -1); + Weapons_AddItem(pl, WEAPON_SCOUT, -1); + Weapons_AddItem(pl, WEAPON_AWP, -1); + Weapons_AddItem(pl, WEAPON_G3SG1, -1); + Weapons_AddItem(pl, WEAPON_SG550, -1); + Weapons_AddItem(pl, WEAPON_PARA, -1); + Weapons_AddItem(pl, WEAPON_C4BOMB, -1); + Weapons_AddItem(pl, WEAPON_FLASHBANG, -1); + Weapons_AddItem(pl, WEAPON_HEGRENADE, -1); + Weapons_AddItem(pl, WEAPON_SMOKEGRENADE, -1); + Weapons_AddItem(pl, WEAPON_USP45, -1); + Weapons_AddItem(pl, WEAPON_GLOCK18, -1); + Weapons_AddItem(pl, WEAPON_DEAGLE, -1); + Weapons_AddItem(pl, WEAPON_P228, -1); + Weapons_AddItem(pl, WEAPON_ELITES, -1); + Weapons_AddItem(pl, WEAPON_FIVESEVEN, -1); + Weapons_AddItem(pl, WEAPON_KNIFE, -1); + } + + if (self.impulse == 102) { + // Respawn all the entities + for (entity a = world; (a = findfloat(a, ::identity, 1));) { + CBaseEntity caw = (CBaseEntity)a; + caw.Respawn(); + } + bprint(PRINT_HIGH, "Respawning all map entities...\n"); + } + } + + self.impulse = 0; +} diff --git a/src/server/item_suit.qc b/src/server/item_suit.qc new file mode 100644 index 0000000..6af786b --- /dev/null +++ b/src/server/item_suit.qc @@ -0,0 +1,69 @@ +/* + * 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_suit:CBaseTrigger +{ + void(void) item_suit; + + virtual void(void) touch; + virtual void(void) Respawn; +}; + +void item_suit::touch(void) +{ + player pl = (player)other; + + if (pl.classname != "player") { + return; + } + + if (pl.g_items & ITEM_SUIT) { + return; + } + + sound(other, CHAN_ITEM, "items/tr_kevlar.wav", 1, ATTN_NORM); + pl.g_items |= ITEM_SUIT; + + UseTargets(other, TRIG_TOGGLE, m_flDelay); + + if (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); + + think = __NULL__; + nextthink = -1; +} + +void item_suit::item_suit(void) +{ + model = "models/w_kevlar.mdl"; + precache_sound("items/tr_kevlar.wav"); + CBaseTrigger::CBaseTrigger(); + Respawn(); +} diff --git a/src/server/money.h b/src/server/money.h new file mode 100644 index 0000000..c81bdb9 --- /dev/null +++ b/src/server/money.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2016-2019 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 g_cs_moneyreward_ct; +int g_cs_moneyreward_t; + +void Money_AddMoney(base_player, int); +void Money_QueTeamReward(int, int); +void Money_GiveTeamReward(base_player); +void Money_ResetTeamReward(void); +void Money_HandleRoundReward(int) +void Money_ResetRoundReward(void); +int Money_GetLosses(int); +int Money_HasBonus(int); diff --git a/src/server/player.qc b/src/server/player.qc new file mode 100644 index 0000000..6bd06f1 --- /dev/null +++ b/src/server/player.qc @@ -0,0 +1,93 @@ +/* + * 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. + */ + +/* +==================== +UseWorkaround +==================== +*/ +void UseWorkaround(entity eTarget) +{ + eActivator = self; + entity eOldSelf = self; + self = eTarget; + self.PlayerUse(); + self = eOldSelf; +} + +/* +==================== +Player_UseDown +==================== +*/ +void Player_UseDown(void) +{ + if (self.health <= 0) { + return; + } else if (!(self.flags & FL_USE_RELEASED)) { + return; + } + + vector vSource; + + makevectors(self.v_angle); + vSource = self.origin + self.view_ofs; + traceline (vSource, vSource + (v_forward * 64), MOVE_EVERYTHING, 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..518b412 --- /dev/null +++ b/src/server/progs.src @@ -0,0 +1,61 @@ +#pragma target fte +#pragma progs_dat "../../progs.dat" + +#define QWSSQC +#define SERVER +#define VALVE +#define CSTRIKE +#define BULLETPENETRATION +#define BULLETPATTERNS +#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 + +defs.h +../shared/include.src + +player.qc +../../../valve/src/server/spectator.qc + +hostage_entity.qc +armoury_entity.qc +func_bomb_target.qc +func_buyzone.qc +info_buyzone.qc +func_escapezone.qc +func_hostage_rescue.qc +info_hostage_rescue.qc +func_vip_safetyzone.qc +info_map_parameters.qc +item_suit.qc +../../../valve/src/server/items.qc + +../../../src/botlib/include.src + +game_money.qc +gamerules.qc +gamerules_singleplayer.qc +gamerules_multiplayer.qc + +radio.qc +client.qc +ammo.qc +buy.qc +server.qc +../../../valve/src/server/damage.qc +../../../valve/src/server/rules.qc +../../../valve/src/server/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/radio.h b/src/server/radio.h new file mode 100644 index 0000000..1a900c2 --- /dev/null +++ b/src/server/radio.h @@ -0,0 +1,4 @@ +void Radio_BroadcastMessage(float fMessage) +void Radio_TeamMessage(float fMessage, float fTeam) +float Radio_DefaultStart(void) +void Radio_StartMessage(void) diff --git a/src/server/radio.qc b/src/server/radio.qc new file mode 100644 index 0000000..a55cc8e --- /dev/null +++ b/src/server/radio.qc @@ -0,0 +1,131 @@ +/* +================= +Radio_BroadcastMessage + +A global radio message for all players +================= +*/ +void +Radio_BroadcastMessage(float fMessage) +{ + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EV_RADIOMSG); + WriteByte(MSG_MULTICAST, fMessage); + msg_entity = self; + multicast([0,0,0], MULTICAST_ALL); +} + +/* +================= +Radio_TeamMessage + +A radio message targetted at members of a specific team +================= +*/ +void +Radio_TeamMessage(float fMessage, float fTeam) +{ + static void Radio_TeamMessage_Send(float fMessage, entity eEnt) { + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EV_RADIOMSG); + WriteByte(MSG_MULTICAST, fMessage); + msg_entity = eEnt; + multicast([0,0,0], MULTICAST_ONE); + } + + for (entity eFind = world; (eFind = find(eFind, classname, "player"));) { + if (eFind.team == fTeam) { + Radio_TeamMessage_Send(fMessage, eFind); + } else if (eFind.team == TEAM_VIP && fTeam == TEAM_CT) { + Radio_TeamMessage_Send(fMessage, eFind); + } + } +} + +/* +================= +Radio_DefaultStart + +Pick a generic, random radio string for global start messages +================= +*/ +float +Radio_DefaultStart(void) +{ + float fRand = floor(random(1, 4)); + + if (fRand == 1) { + return RADIO_MOVEOUT; + } else if (fRand == 2) { + return RADIO_LOCKNLOAD; + } else { + return RADIO_LETSGO; + } +} + +/* +================= +Radio_StartMessage + +Decide which startmessage to play at the beginning of each round +================= +*/ +void +Radio_StartMessage(void) +{ + if (g_cs_vipzones > 0) { + Radio_TeamMessage(RADIO_VIP, TEAM_CT); + Radio_TeamMessage(Radio_DefaultStart(), TEAM_T); + } else if (g_cs_escapezones > 0) { + Radio_TeamMessage(RADIO_GETOUT, TEAM_T); + Radio_TeamMessage(Radio_DefaultStart(), TEAM_CT); + } else { + Radio_BroadcastMessage(Radio_DefaultStart()); + } +} + +/* +================= +CSEv_Radio_f + +Triggered by clients, plays a message to members of the same team +================= +*/ +void +CSEv_Radio_f(float fMessage) +{ + static void CSEv_Radio_Send(float fMessage, entity eEnt) { + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EV_RADIOMSG2); + WriteByte(MSG_MULTICAST, num_for_edict(eEnt) - 1); + WriteByte(MSG_MULTICAST, fMessage); + msg_entity = eEnt; + multicast([0,0,0], MULTICAST_ONE); + } + + // Don't allow spamming + /*if (self.fRadioFinished > time) { + return; + }*/ + + // When dead, don't talk + if (self.health <= 0) { + return; + } + + // Make sure that VIPs and CTs get eachother + float fTargetTeam = self.team; + if (fTargetTeam == TEAM_VIP) { + fTargetTeam = TEAM_CT; + } + + for (entity eFind = world; (eFind = find(eFind, classname, "player"));) { + if (eFind.team == fTargetTeam) { + CSEv_Radio_Send(fMessage, eFind); + } else if (eFind.team == TEAM_VIP && fTargetTeam == TEAM_CT) { + CSEv_Radio_Send(fMessage, eFind); + } + } + + /*self.fRadioFinished = time + 3.0f;*/ +} diff --git a/src/server/server.qc b/src/server/server.qc new file mode 100644 index 0000000..833f29a --- /dev/null +++ b/src/server/server.qc @@ -0,0 +1,58 @@ +/* + * 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(CSSingleplayerRules); + } else { + g_grMode = spawn(CSMultiplayerRules); + } +} + +void Game_Worldspawn(void) +{ + precache_model("models/player/arctic/arctic.mdl"); + precache_model("models/player/gign/gign.mdl"); + precache_model("models/player/gsg9/gsg9.mdl"); + precache_model("models/player/guerilla/guerilla.mdl"); + precache_model("models/player/leet/leet.mdl"); + precache_model("models/player/sas/sas.mdl"); + precache_model("models/player/terror/terror.mdl"); + precache_model("models/player/urban/urban.mdl"); + precache_model("models/player/vip/vip.mdl"); + precache_sound("weapons/ric_metal-2.wav"); + precache_sound("player/pl_pain2.wav"); + precache_sound("player/pl_pain4.wav"); + Sound_Precache("buy.kevlar"); + Sound_Precache("buy.weapon"); + Sound_Precache("buy.ammo"); + + /* some Counter-Strike maps do not have weapon pickups, so we want to + * precache these regardless in case of someone dropping a weapon, + * which happens quite often (buying weapons, etc.) */ + Sound_Precache("item.respawn"); + Sound_Precache("weapon.pickup"); + + Weapons_Init(); + + clientstat(STAT_MONEY, EV_INTEGER, player::money); + clientstat(STAT_PROGRESS, EV_FLOAT, player::progress); + + pointerstat(STAT_GAMETIME, EV_FLOAT, &g_cs_gametime); + pointerstat(STAT_GAMESTATE, EV_INTEGER, &g_cs_gamestate); +} diff --git a/src/server/spawn.qc b/src/server/spawn.qc new file mode 100644 index 0000000..97bce39 --- /dev/null +++ b/src/server/spawn.qc @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2016-2019 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. + */ + +/* +================= +info_player_start + +Counter-Terrorist Spawnpoints +================= +*/ +void info_player_start(void) +{ + if (autocvar_fcs_swapteams == TRUE) { + self.classname = "info_player_deathmatch"; + } + self.botinfo = BOTINFO_SPAWNPOINT; +} + +/* +================= +info_player_deathmatch + +Terrorist Spawnpoints +================= +*/ +void info_player_deathmatch(void) +{ + if (autocvar_fcs_swapteams == TRUE) { + self.classname = "info_player_start"; + } + self.botinfo = BOTINFO_SPAWNPOINT; +} + +/* Counter-Strike: Source compat */ +void info_player_counterterrorist(void) +{ + setorigin(self, self.origin + [0,0,32]); + self.classname = "info_player_start"; + info_player_start(); +} + +void info_player_terrorist(void) +{ + setorigin(self, self.origin + [0,0,32]); + self.classname = "info_player_deathmatch"; + info_player_deathmatch(); +} + +/* +================= +info_vip_start +================= +*/ +void info_vip_start(void) +{ +} diff --git a/src/shared/animations.h b/src/shared/animations.h new file mode 100644 index 0000000..920874d --- /dev/null +++ b/src/shared/animations.h @@ -0,0 +1,117 @@ +/* + * 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_DUMMY1, + ANIM_IDLE, + ANIM_IDLE_CROUCH, + ANIM_WALK, + ANIM_RUN, + ANIM_RUN_CROUCH, + ANIM_JUMP, + ANIM_LONGJUMP, + ANIM_SWIM, + ANIM_TREADWATER, + ANIM_CROUCH_AIM_CARBINE, + ANIM_CROUCH_SHOOT_CARBINE, + ANIM_CROUCH_RELOAD_CARBINE, + ANIM_AIM_CARBINE, + ANIM_SHOOT_CARBINE, + ANIM_RELOAD_CARBINE, + ANIM_CROUCH_AIM_ONEHAND, + ANIM_CROUCH_SHOOT_ONEHAND, + ANIM_CROUCH_RELOAD_ONEHAND, + ANIM_AIM_ONEHAND, + ANIM_SHOOT_ONEHAND, + ANIM_RELOAD_ONEHAND, + ANIM_CROUCH_AIM_DUALPISTOLS, + ANIM_CROUCH_SHOOT_DUALPISTOLS, + ANIM_CROUCH_SHOOT2_DUALPISTOLS, + ANIM_CROUCH_RELOAD_DUALPISTOLS, + ANIM_AIM_DUALPISTOLS, + ANIM_SHOOT_DUALPISTOLS, + ANIM_SHOOT2_DUALPISTOLS, + ANIM_RELOAD_DUALPISTOLS, + ANIM_CROUCH_AIM_RIFLE, + ANIM_CROUCH_SHOOT_RIFLE, + ANIM_CROUCH_RELOAD_RIFLE, + ANIM_AIM_RIFLE, + ANIM_SHOOT_RIFLE, + ANIM_RELOAD_RIFLE, + ANIM_CROUCH_AIM_MP5, + ANIM_CROUCH_SHOOT_MP5, + ANIM_CROUCH_RELOAD_MP5, + ANIM_AIM_MP5, + ANIM_SHOOT_MP5, + ANIM_RELOAD_MP5, + ANIM_CROUCH_AIM_SHOTGUN, + ANIM_CROUCH_SHOOT_SHOTGUN, + ANIM_CROUCH_RELOAD_SHOTGUN, + ANIM_AIM_SHOTGUN, + ANIM_SHOOT_SHOTGUN, + ANIM_RELOAD_SHOTGUN, + ANIM_CROUCH_AIM_PARA, + ANIM_CROUCH_SHOOT_PARA, + ANIM_CROUCH_RELOAD_PARA, + ANIM_AIM_PARA, + ANIM_SHOOT_PARA, + ANIM_RELOAD_PARA, + ANIM_DUMMY2, + ANIM_DUMMY3, + ANIM_AIM_GRENADE, + ANIM_SHOOT_GRENADE, + ANIM_CROUCH_AIM_GRENADE, + ANIM_CROUCH_SHOOT_GRENADE, + ANIM_CROUCH_AIM_C4, + ANIM_CROUCH_SHOOT_C4, + ANIM_AIM_C4, + ANIM_SHOOT_C4, + ANIM_RELOAD_C4, + ANIM_DUPLICATE1, + ANIM_DUPLICATE2, + ANIM_DUPLICATE3, + ANIM_DUPLICATE4, + ANIM_DUPLICATE5, + ANIM_DUPLICATE6, + ANIM_DUPLICATE7, + ANIM_DUPLICATE8, + ANIM_CROUCH_AIM_KNIFE, + ANIM_CROUCH_SHOOT_KNIFE, + ANIM_AIM_KNIFE, + ANIM_SHOOT_KNIFE, + ANIM_CROUCH_AIM_AK47, + ANIM_CROUCH_SHOOT_AK47, + ANIM_CROUCH_RELOAD_AK47, + ANIM_AIM_AK47, + ANIM_SHOOT_AK47, + ANIM_RELOAD_AK47, + ANIM_GUT_FLINCH, + ANIM_HEAD_FLINCH, + ANIM_DEATH1, + ANIM_DEATH2, + ANIM_DEATH3, + ANIM_DIE_HEAD, + ANIM_DIE_GUT, + ANIM_DIE_LEFT, + ANIM_DIE_BACK, + ANIM_DIE_RIGHT, + ANIM_DIE_FORWARD, + ANIM_CROUCH_DIE +}; + +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..83e7422 --- /dev/null +++ b/src/shared/animations.qc @@ -0,0 +1,158 @@ +/* + * 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 baseframe1time; +.float baseframe2time; +.float baseframe2; +.float baseframe_last; +.float baseframe_old; +.float baseframe_time; +.float baselerpfrac; +.float frame2; +.float frame2time; +.float frame_last; +.float fWasCrouching; +.float lerpfrac; +.float subblend2frac; +.float subblendfrac; + +void Animation_Print(string sWow) { +#ifdef CLIENT + print(sprintf("[DEBUG] %s", sWow)); +#else + bprint(PRINT_HIGH, sprintf("SSQC: %s", sWow) ); +#endif +} + +/* +================= +Animation_PlayerUpdate + +Called every frame to update the animation sequences +depending on what the player is doing +================= +*/ +void Animation_PlayerUpdate(void) { + self.basebone = cvar("spinebone"); // gettagindex(self, "Bip01 Spine"); + + if (self.baseframe_time < time) { + base_player pl = (base_player)self; + self.baseframe = Weapons_GetAim(pl.activeweapon); + self.baseframe_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.frame == ANIM_JUMP)) { + self.frame = ANIM_JUMP; + } else if (vlen(self.velocity) == 0) { + if (self.flags & FL_CROUCHING) { + self.frame = ANIM_IDLE_CROUCH; + } else { + self.frame = ANIM_IDLE; + } + } else if (vlen(self.velocity) < 150) { + if (self.flags & FL_CROUCHING) { + self.frame = ANIM_RUN_CROUCH; + } else { + self.frame = ANIM_WALK; + } + } else if (vlen(self.velocity) > 150) { + if (self.flags & FL_CROUCHING) { + self.frame = ANIM_RUN_CROUCH; + } else { + self.frame = 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; + } + + // Force the code above to update if we switched positions + if (self.fWasCrouching != (self.flags & FL_CROUCHING)) { + self.baseframe_old = 0; + self.baseframe_time = 0; + self.fWasCrouching = (self.flags & FL_CROUCHING); + } + +#ifdef SERVER + // On the CSQC it's done in Player.c + self.subblendfrac = + self.subblend2frac = self.v_angle[0] / 90; +#endif + + self.angles[0] = self.angles[2] = 0; +} + +/* +================= +Animation_PlayerTop + +Changes the animation sequence for the upper body part +================= +*/ +void Animation_PlayerTop(float fFrame) { + self.baseframe = fFrame; + self.baseframe_old = fFrame; +} + +void Animation_PlayerTopTemp(float fFrame, float fTime) { + self.baseframe = fFrame; + self.baseframe_time = time + fTime; +#ifdef SERVER + self.SendFlags |= PLAYER_FRAME; +#endif +} diff --git a/src/shared/defs.h b/src/shared/defs.h new file mode 100644 index 0000000..120ce2c --- /dev/null +++ b/src/shared/defs.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016-2019 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 "animations.h" +#include "radio.h" +#include "weapons.h" +#include "items.h" +#include "entities.h" +#include "events.h" + +#define TEAM_T 1 +#define TEAM_CT 2 +#define TEAM_VIP 3 + +enum +{ + STAT_MONEY = 34, + STAT_PROGRESS, + STAT_GAMETIME, + STAT_GAMESTATE +}; diff --git a/src/shared/entities.h b/src/shared/entities.h new file mode 100644 index 0000000..49d7ee1 --- /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_C4BOMB = ENT_SEPARATOR, +}; diff --git a/src/shared/equipment.qc b/src/shared/equipment.qc new file mode 100755 index 0000000..f87c377 --- /dev/null +++ b/src/shared/equipment.qc @@ -0,0 +1,114 @@ +/* + * 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 CSEv_PlayerBuyEquipment_f(float fID) { +/* if (Rules_BuyingPossible() == FALSE) { + return; + } + + if ((self.fMoney - eqptTable[fID].iPrice) >= 0) { + if (eqptTable[fID].iID == EQUIPMENT_DEFUSALKIT) { + if (self.team == TEAM_T) { return; } + if (!(self.iEquipment & EQUIPMENT_DEFUSALKIT)) { + self.iEquipment |= EQUIPMENT_DEFUSALKIT; + Money_AddMoney(self, -200); + sound(self, CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_IDLE); + } else { + centerprint(self, "You already have a defusal kit!"); + } + } else if (eqptTable[fID].iID == EQUIPMENT_NIGHTVISION) { + if (!(self.iEquipment & EQUIPMENT_NIGHTVISION)) { + self.iEquipment |= EQUIPMENT_NIGHTVISION; + Money_AddMoney(self, -1250); + sound(self, CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_IDLE); + } else { + centerprint(self, "You already have nightvision goggles!"); + } + } else if (eqptTable[fID].iID == WEAPON_HEGRENADE) { + if (self.iAmmo_HEGRENADE < 2) { + self.iAmmo_HEGRENADE++; + Money_AddMoney(self, -300); + sound(self, CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_IDLE); + } else { + centerprint(self, "You can't carry any more!"); + } + } else if (eqptTable[fID].iID == WEAPON_FLASHBANG) { + if (self.iAmmo_FLASHBANG < 2) { + self.iAmmo_FLASHBANG++; + Money_AddMoney(self, -300); + sound(self, CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_IDLE); + } else { + centerprint(self, "You can't carry any more!"); + } + } else if (eqptTable[fID].iID == WEAPON_SMOKEGRENADE) { + if (self.iAmmo_SMOKEGRENADE < 2) { + self.iAmmo_SMOKEGRENADE++; + Money_AddMoney(self, -300); + sound(self, CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_IDLE); + } else { + centerprint(self, "You can't carry any more!"); + } + } else if (eqptTable[fID].iID == EQUIPMENT_KEVLAR) { + if (self.armor == 100) { + // You already own kevlar etc. + centerprint(self, "You already have kevlar!"); + } else { + self.armor = 100; + Money_AddMoney(self, -650); + } + + sound(self, CHAN_ITEM, "items/tr_kevlar.wav", 1, ATTN_IDLE); + self.fAttackFinished = time + 1.0; + return; + } else if (eqptTable[fID].iID == EQUIPMENT_HELMET) { + if (self.armor == 100) { + if (self.iEquipment & EQUIPMENT_HELMET) { + // You already have full kevlar and a helmet + centerprint(self, "You already have kevlar and a helmet!"); + } else { + // You have full kevlar, but no helmet + Money_AddMoney(self, -350); + sound(self, CHAN_ITEM, "items/tr_kevlar.wav", 1, ATTN_IDLE); + centerprint(self, "You already have some kevlar,\nand now you've bought a helmet!"); + self.iEquipment = self.iEquipment | EQUIPMENT_HELMET; + } + } else { + if (self.iEquipment & EQUIPMENT_HELMET) { + // Only get kevlar + self.armor = 100; + Money_AddMoney(self, -650); + sound(self, CHAN_ITEM, "items/tr_kevlar.wav", 1, ATTN_IDLE); + centerprint(self, "You already have a helmet,\nand now you've bought some kevlar!"); + } else { + // Get both + self.armor = 100; + self.iEquipment = self.iEquipment | EQUIPMENT_HELMET; + Money_AddMoney(self, -1000); + sound(self, CHAN_ITEM, "items/tr_kevlar.wav", 1, ATTN_IDLE); + } + } + + self.fAttackFinished = time + 1.0; + return; + } + } else { + centerprint(self, "You have insufficient funds!"); + } + + self.fAttackFinished = time + 1.0;*/ +} +#endif diff --git a/src/shared/events.h b/src/shared/events.h new file mode 100644 index 0000000..1204c4d --- /dev/null +++ b/src/shared/events.h @@ -0,0 +1,23 @@ +/* + * 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 +{ + EV_RADIOMSG = EV_SEPARATOR, + EV_RADIOMSG2, + EV_SMOKE, + EV_FLASH +}; diff --git a/src/shared/flags.h b/src/shared/flags.h new file mode 100644 index 0000000..53b1313 --- /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_BUYZONE (1<<2) +#define GF_RESCUEZONE (1<<3) +#define GF_BOMBZONE (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_flashbang.qc b/src/shared/fx_flashbang.qc new file mode 100644 index 0000000..c5445bc --- /dev/null +++ b/src/shared/fx_flashbang.qc @@ -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. + */ + +#ifdef SERVER +void +FX_Flashbang(vector org) +{ + for (entity e = world; (e = find(e, ::classname, "player"));) { + float fov_dot; + vector val; + float blindness; + float fade; + + /* wall check */ + traceline(e.origin + e.view_ofs, org, FALSE, e); + if (trace_fraction < 1.0f) + continue; + + /* calculate the fov in dotproduct form */ + makevectors(e.v_angle); + val = normalize(org - (e.origin + e.view_ofs)); + fov_dot = val * v_forward; + + /* it's behind us */ + if (fov_dot < 0) { + blindness = 0.1; + fade = 1.0f; + } else { + blindness = 2 * fov_dot; + fade = 4 * fov_dot; + } + + /* send the blinding env_fade event */ + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EV_FADE); + WriteFloat(MSG_MULTICAST, 1.0f); + WriteFloat(MSG_MULTICAST, 1.0f); + WriteFloat(MSG_MULTICAST, 1.0f); + WriteFloat(MSG_MULTICAST, 1.0f); + WriteFloat(MSG_MULTICAST, blindness); + WriteFloat(MSG_MULTICAST, fade); + WriteByte(MSG_MULTICAST, EVF_FADEDROM); + msg_entity = e; + multicast([0,0,0], MULTICAST_ONE_R); + } +} +#endif diff --git a/src/shared/fx_impact.qc b/src/shared/fx_impact.qc new file mode 100644 index 0000000..cdefcbd --- /dev/null +++ b/src/shared/fx_impact.qc @@ -0,0 +1,116 @@ +/* + * 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; + +void +FX_Impact_Init(void) +{ + precache_sound("weapons/ric_metal-1.wav"); + precache_sound("weapons/ric_metal-2.wav"); + precache_sound("weapons/ric_conc-1.wav"); + precache_sound("weapons/ric_conc-2.wav"); + precache_sound("weapons/knife_hitwall1.wav"); + precache_sound("weapons/ric1.wav"); + precache_sound("weapons/ric2.wav"); + precache_sound("weapons/ric3.wav"); + precache_sound("weapons/ric4.wav"); + precache_sound("weapons/ric5.wav"); + + 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"); +} +#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 */ + 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; + } + + switch (iType) { + case IMPACT_MELEE: + pointsound(vecPos, "weapons/knife_hitwall1.wav", 1, ATTN_STATIC); + break; + 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; + case IMPACT_DEFAULT: + 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_METAL: + pointsound(vecPos, sprintf("weapons/ric_metal-%d.wav", floor((random() * 2) + 1)), 1, ATTN_STATIC); + break; + case IMPACT_CONCRETE: + pointsound(vecPos, sprintf("weapons/ric_conc-%d.wav", floor((random() * 2) + 1)), 1, ATTN_STATIC); + break; + case IMPACT_FLESH: + break; + default: + pointsound(vecPos, sprintf("weapons/ric%d.wav", floor((random() * 5) + 1)), 1, ATTN_STATIC); + break; + } +#endif +} diff --git a/src/shared/fx_smokenade.qc b/src/shared/fx_smokenade.qc new file mode 100644 index 0000000..4636026 --- /dev/null +++ b/src/shared/fx_smokenade.qc @@ -0,0 +1,63 @@ +/* + * 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 PARTICLE_SMOKEGRENADE; + +void +FX_Smokenade_Init(void) +{ + PARTICLE_SMOKEGRENADE = particleeffectnum("fx_smokenade.effect"); +} +#endif + +void +FX_Smokenade(vector vecPos) +{ +#ifdef SERVER + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EV_SMOKE); + WriteCoord(MSG_MULTICAST, vecPos[0]); + WriteCoord(MSG_MULTICAST, vecPos[1]); + WriteCoord(MSG_MULTICAST, vecPos[2]); + msg_entity = self; + multicast([0,0,0], MULTICAST_ALL); +#else + static void Effect_CreateSmoke_Think(void) { + // HACK: This should only ever happen when rounds restart! + // Any way this can go wrong? + if (self.skin < getstatf(STAT_GAMETIME)) { + remove(self); + } + if (self.frame <= 0) { + remove(self); + return; + } + + pointparticles(PARTICLE_SMOKEGRENADE, self.origin, [0,0,0], 1); + self.frame--; + self.nextthink = time + 0.2f; + self.skin = getstatf(STAT_GAMETIME); + } + + entity eSmoke = spawn(); + setorigin(eSmoke, vecPos); + eSmoke.think = Effect_CreateSmoke_Think; + eSmoke.nextthink = time; + eSmoke.frame = 200; + eSmoke.skin = getstatf(STAT_GAMETIME); +#endif +} diff --git a/src/shared/include.src b/src/shared/include.src new file mode 100644 index 0000000..cb65439 --- /dev/null +++ b/src/shared/include.src @@ -0,0 +1,53 @@ + #includelist +defs.h +flags.h +player.h +../../../valve/src/shared/weapon_common.h +animations.h +animations.qc + +item_c4bomb.h + +../../../valve/src/shared/fx_blood.qc +../../../valve/src/shared/fx_breakmodel.qc +../../../valve/src/shared/fx_explosion.qc +../../../valve/src/shared/fx_gibhuman.qc +../../../valve/src/shared/fx_spark.qc +fx_impact.qc +fx_flashbang.qc +fx_smokenade.qc + +weapons_cstrike.qc +w_ak47.qc +w_deagle.qc +w_knife.qc +w_usp45.qc +w_glock18.qc +w_p228.qc +w_elites.qc +w_fiveseven.qc +w_m3.qc +w_xm1014.qc +w_mp5.qc +w_p90.qc +w_ump45.qc +w_mac10.qc +w_tmp.qc +w_sg552.qc +w_m4a1.qc +w_aug.qc +w_scout.qc +w_awp.qc +w_g3sg1.qc +w_sg550.qc +w_para.qc +w_c4bomb.qc +w_flashbang.qc +w_hegrenade.qc +w_smokegrenade.qc +weapons.qc +../../../valve/src/shared/weapon_common.qc +pmove.qc +../../../valve/src/shared/pmove_water.qc +item_c4bomb.qc +#endlist diff --git a/src/shared/item_c4bomb.h b/src/shared/item_c4bomb.h new file mode 100644 index 0000000..4373dc8 --- /dev/null +++ b/src/shared/item_c4bomb.h @@ -0,0 +1,8 @@ +#ifdef SERVER +void C4Bomb_Plant(base_player); +#endif + +#ifdef CLIENT +string g_c4bombled_spr; +void w_c4bomb_parse(void); +#endif diff --git a/src/shared/item_c4bomb.qc b/src/shared/item_c4bomb.qc new file mode 100644 index 0000000..fd70f21 --- /dev/null +++ b/src/shared/item_c4bomb.qc @@ -0,0 +1,286 @@ +/* + * 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_c4:CBaseEntity +{ +#ifdef SERVER + entity m_eUser; + float m_flBeepTime; + float m_flExplodeTime; + float m_flDefusalState; +#endif + +#ifdef CLIENT + float m_flAlpha; +#endif + +#ifdef SERVER + void(void) item_c4; + virtual float(entity, float) SendEntity; + virtual void(void) ClearProgress; + virtual void(void) OnPlayerUse; + virtual void(void) Logic; +#endif + +#ifdef CLIENT + void(void) item_c4; + virtual void(void) DrawLED; + virtual float(void) predraw; +#endif +}; + +#ifdef SERVER +float +item_c4::SendEntity(entity pvsent, float flags) +{ + WriteByte(MSG_ENTITY, ENT_C4BOMB); + 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]); + WriteShort(MSG_ENTITY, modelindex); + return TRUE; +} + +void +item_c4::ClearProgress(void) +{ + if (m_eUser != world) { + player pl = (player)m_eUser; + pl.progress = 0.0f; + pl.flags &= ~FL_FROZEN; + } +} + +void +item_c4::OnPlayerUse(void) +{ + player pl = (player)eActivator; + + /* obvious check */ + if (pl.team != TEAM_CT) { + return; + } + + /* don't allow anyone else to hijack. */ + if (m_eUser == world) { + m_eUser = eActivator; + sound(this, CHAN_ITEM, "weapons/c4_disarm.wav", 1.0, ATTN_NONE); + } +} + +void +item_c4::Logic(void) +{ + CSMultiplayerRules rules = (CSMultiplayerRules)g_grMode; + + /* check if we're being used */ + if (m_eUser != world) { + player pl = (player)m_eUser; + + /* we need to check if the user has changed every frame. */ + if (!m_eUser.button5) { + ClearProgress(); + + /* clear user */ + m_eUser = world; + m_flDefusalState = 0.0f; + } else { + + /* defusal kit always cuts the time in half */ + if (pl.g_items & ITEM_DEFUSAL) + m_flDefusalState += (frametime * 2); + else + m_flDefusalState += frametime; + + /* tracked stat */ + pl.progress = m_flDefusalState; + pl.flags |= FL_FROZEN; + } + } + + if (m_flDefusalState > 10.0f) { + ClearProgress(); + sound(this, CHAN_VOICE, "weapons/c4_disarmed.wav", 1.0, ATTN_NORM); + rules.RoundOver(TEAM_CT, 3600, TRUE); + Radio_BroadcastMessage(RADIO_BOMBDEF); + m_flBeepTime = 0.0f; + m_flDefusalState = 0; + remove(this); + return; + } + + /* if our time has passed, explode */ + if (m_flExplodeTime < time) { + ClearProgress(); + + /* In Bomb Defusal, all Terrorists receive $3500 + * if they won by detonating the bomb. */ + rules.RoundOver(TEAM_T, 3500, FALSE); + Damage_Radius(origin, this.owner, 500, g_cstrike_bombradius, FALSE, WEAPON_C4BOMB); + Sound_Play(this, CHAN_VOICE, "weapon_c4bomb.explode"); + + for (entity e = world; (e = find(e, ::classname, "func_bomb_target"));) { + CBaseTrigger trigger = (CBaseTrigger)e; + if (trigger.Trigger != __NULL__) { + trigger.Trigger(this, TRIG_TOGGLE); + } + } + + m_flBeepTime = 0.0f; + m_flDefusalState = 0; + remove(this); + return; + } + + if (m_flBeepTime > time) { + return; + } + m_flBeepTime = time + 1.5; + + if (m_flExplodeTime - time < 2) { + sound(this, CHAN_VOICE, "weapons/c4_beep5.wav", 1.0, ATTN_NONE); + } else if (m_flExplodeTime - time < 5) { + sound(this, CHAN_VOICE, "weapons/c4_beep5.wav", 1.0, ATTN_NORM); + } else if (m_flExplodeTime - time < 10) { + sound(this, CHAN_VOICE, "weapons/c4_beep4.wav", 1.0, ATTN_NORM); + } else if (m_flExplodeTime - time < 20) { + sound(this, CHAN_VOICE, "weapons/c4_beep3.wav", 1.0, ATTN_NORM); + } else if (m_flExplodeTime - time < 30) { + sound(this, CHAN_VOICE, "weapons/c4_beep2.wav", 1.0, ATTN_NORM); + } else { + sound(this, CHAN_VOICE, "weapons/c4_beep1.wav", 1.0, ATTN_NORM); + } +} + +void +item_c4::item_c4(void) +{ + /* throw this in with the other temporary round entities */ + classname = "remove_me"; + + SetMovetype(MOVETYPE_NONE); + SetSolid(SOLID_BBOX); + SetModel("models/w_c4.mdl"); + SetSize([-6,-6,0], [6,6,6]); + + customphysics = Logic; + PlayerUse = OnPlayerUse; + m_flExplodeTime = time + 45.0f; + + Sound_Play(this, CHAN_WEAPON, "weapon_c4bomb.plant"); +} + +void +C4Bomb_Plant(base_player planter) +{ + item_c4 bomb = spawn(item_c4); + bomb.owner = planter; + + /* place directly below */ + traceline(planter.origin, planter.origin + [0,0,-64], FALSE, planter); + setorigin(bomb, trace_endpos); + bomb.SendFlags = -1; + + Radio_BroadcastMessage(RADIO_BOMBPL); + g_cs_bombplanted = TRUE; +} +#endif + +#ifdef CLIENT +void +item_c4::DrawLED(void) +{ + vector vecPlayer; + + int s = (float)getproperty(VF_ACTIVESEAT); + pSeat = &g_seats[s]; + vecPlayer = pSeat->m_vecPredictedOrigin; + + m_flAlpha -= frametime; + + if (m_flAlpha <= 0.0f) + m_flAlpha = 1.0f; + + if (m_flAlpha > 0) { + vector forg; + vector fsize; + float falpha; + + /* Scale the glow somewhat with the players distance */ + fsize = [16,16]; + fsize *= bound(1, vlen(vecPlayer - origin) / 256, 4); + + /* Fade out when the player is starting to move away */ + falpha = 1 - bound(0, vlen(vecPlayer - origin) / 1024, 1); + falpha *= m_flAlpha; + + /* Nudge this slightly towards the camera */ + makevectors(vectoangles(origin - vecPlayer)); + forg = (origin + [0,0,8]) + (v_forward * -16); + + /* Project it, always facing the player */ + makevectors(view_angles); + R_BeginPolygon(g_c4bombled_spr, 1, 0); + R_PolygonVertex(forg + v_right * fsize[0] - v_up * fsize[1], + [1,1], [1,1,1], falpha); + R_PolygonVertex(forg - v_right * fsize[0] - v_up * fsize[1], + [0,1], [1,1,1], falpha); + R_PolygonVertex(forg - v_right * fsize[0] + v_up * fsize[1], + [0,0], [1,1,1], falpha); + R_PolygonVertex(forg + v_right * fsize[0] + v_up * fsize[1], + [1,0], [1,1,1], falpha); + R_EndPolygon(); + } +} + +float +item_c4::predraw(void) +{ + DrawLED(); + addentity(this); + return PREDRAW_NEXT; +} + +void +item_c4::item_c4(void) +{ + solid = SOLID_BBOX; + movetype = MOVETYPE_NONE; + drawmask = MASK_ENGINE; +} + +void +w_c4bomb_parse(void) +{ + item_c4 tm = (item_c4)self; + spawnfunc_item_c4(); + + g_c4bombled_spr = spriteframe("sprites/ledglow.spr", 0, 0.0f); + + 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.modelindex = readshort(); + setorigin(tm, tm.origin); + setsize(tm, [-6,-6,0], [6,6,6]); +} +#endif diff --git a/src/shared/items.h b/src/shared/items.h new file mode 100644 index 0000000..4ed3d50 --- /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_KNIFE 0x00000001i +#define ITEM_USP45 0x00000002i +#define ITEM_GLOCK18 0x00000004i +#define ITEM_DEAGLE 0x00000008i +#define ITEM_P228 0x00000010i +#define ITEM_ELITES 0x00000020i +#define ITEM_FIVESEVEN 0x00000040i +#define ITEM_M3 0x00000080i + +#define ITEM_XM1014 0x00000100i +#define ITEM_MP5 0x00000200i +#define ITEM_P90 0x00000400i +#define ITEM_UMP45 0x00000800i +#define ITEM_MAC10 0x00001000i +#define ITEM_TMP 0x00002000i +#define ITEM_SUIT 0x00004000i +#define ITEM_LONGJUMP 0x00008000i + +#define ITEM_AK47 0x00010000i +#define ITEM_SG552 0x00020000i +#define ITEM_M4A1 0x00040000i +#define ITEM_AUG 0x00080000i +#define ITEM_SCOUT 0x00100000i +#define ITEM_AWP 0x00200000i +#define ITEM_G3SG1 0x00400000i +#define ITEM_SG550 0x00800000i + +#define ITEM_PARA 0x01000000i +#define ITEM_C4BOMB 0x02000000i +#define ITEM_FLASHBANG 0x04000000i +#define ITEM_HEGRENADE 0x08000000i +#define ITEM_SMOKEGRENADE 0x10000000i +#define ITEM_DEFUSAL 0x20000000i +#define ITEM_NIGHTVISION 0x40000000i +#define ITEM_HELMET 0x80000000i diff --git a/src/shared/player.h b/src/shared/player.h new file mode 100644 index 0000000..16c6d60 --- /dev/null +++ b/src/shared/player.h @@ -0,0 +1,837 @@ +/* + * 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. + */ + +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_CSSHOT, + PLAYER_CSSHOTTIME +}; + +enumflags +{ + AMMO1_USP45, + AMMO1_GLOCK18, + AMMO1_DEAGLE, + AMMO1_P228, + AMMO1_ELITES, + AMMO1_FIVESEVEN, + AMMO1_M3, + AMMO1_XM1014, + AMMO1_MP5, + AMMO1_P90, + AMMO1_UMP45, + AMMO1_MAC10, + AMMO1_TMP, + AMMO1_AK47, + AMMO1_SG552, + AMMO1_M4A1, + AMMO1_AUG, + AMMO1_SCOUT, + AMMO1_AWP, + AMMO1_G3SG1, + AMMO1_SG550, + AMMO1_PARA, +}; + +enumflags +{ + AMMO2_50AE, + AMMO2_762MM, + AMMO2_556MM, + AMMO2_556MMBOX, + AMMO2_338MAG, + AMMO2_9MM, + AMMO2_BUCKSHOT, + AMMO2_45ACP, + AMMO2_357SIG, + AMMO2_57MM, + AMMO2_HEGRENADE, + AMMO2_FBGRENADE, + AMMO2_SMOKEGRENADE, +}; + +enumflags +{ + AMMO3_MODE_USP45, + AMMO3_MODE_M4A1, + AMMO3_MODE_GLOCK18, +}; + +noref int input_sequence; +class player:base_player +{ + int ingame; + + int ammo_50ae; + int ammo_50ae_net; + int ammo_762mm; + int ammo_762mm_net; + int ammo_556mm; + int ammo_556mm_net; + int ammo_556mmbox; + int ammo_556mmbox_net; + int ammo_338mag; + int ammo_338mag_net; + int ammo_9mm; + int ammo_9mm_net; + int ammo_buckshot; + int ammo_buckshot_net; + int ammo_45acp; + int ammo_45acp_net; + int ammo_357sig; + int ammo_357sig_net; + int ammo_57mm; + int ammo_57mm_net; + int ammo_hegrenade; + int ammo_hegrenade_net; + int ammo_fbgrenade; + int ammo_fbgrenade_net; + int ammo_smokegrenade; + int ammo_smokegrenade_net; + + /* Weapon specific */ + int usp45_mag; + int usp45_mag_net; + int glock18_mag; + int glock18_mag_net; + int deagle_mag; + int deagle_mag_net; + int p228_mag; + int p228_mag_net; + int elites_mag; + int elites_mag_net; + int fiveseven_mag; + int fiveseven_mag_net; + int m3_mag; + int m3_mag_net; + int xm1014_mag; + int xm1014_mag_net; + int mp5_mag; + int mp5_mag_net; + int p90_mag; + int p90_mag_net; + int ump45_mag; + int ump45_mag_net; + int mac10_mag; + int mac10_mag_net; + int tmp_mag; + int tmp_mag_net; + int ak47_mag; + int ak47_mag_net; + int sg552_mag; + int sg552_mag_net; + int m4a1_mag; + int m4a1_mag_net; + int aug_mag; + int aug_mag_net; + int scout_mag; + int scout_mag_net; + int awp_mag; + int awp_mag_net; + int g3sg1_mag; + int g3sg1_mag_net; + int sg550_mag; + int sg550_mag_net; + int para_mag; + int para_mag_net; + + int mode_usp45; + int mode_usp45_net; + int mode_m4a1; + int mode_m4a1_net; + int mode_glock18; + int mode_glock18_net; + + int mode_temp; + int mode_temp_net; + + int cs_shotmultiplier; + int cs_shotmultiplier_net; + float cs_shottime; + float cs_shottime_net; + +#ifdef CLIENT + /* External model */ + entity p_model; + int playertype; + int p_hand_bone; + int p_model_bone; + float lastweapon; + + int cs_cross_mindist; + int cs_cross_deltadist; + float cs_crosshairdistance; + + 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; + + int charmodel; + int money; + float progress; +#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) { + usp45_mag = readbyte(); + glock18_mag = readbyte(); + deagle_mag = readbyte(); + p228_mag = readbyte(); + elites_mag = readbyte(); + fiveseven_mag = readbyte(); + m3_mag = readbyte(); + xm1014_mag = readbyte(); + mp5_mag = readbyte(); + p90_mag = readbyte(); + ump45_mag = readbyte(); + mac10_mag = readbyte(); + tmp_mag = readbyte(); + ak47_mag = readbyte(); + sg552_mag = readbyte(); + m4a1_mag = readbyte(); + aug_mag = readbyte(); + scout_mag = readbyte(); + awp_mag = readbyte(); + g3sg1_mag = readbyte(); + sg550_mag = readbyte(); + para_mag = readbyte(); + } + + if (fl & PLAYER_AMMO2) { + ammo_50ae = readbyte(); + ammo_762mm = readbyte(); + ammo_556mm = readbyte(); + ammo_556mmbox = readbyte(); + ammo_338mag = readbyte(); + ammo_9mm = readbyte(); + ammo_buckshot = readbyte(); + ammo_45acp = readbyte(); + ammo_357sig = readbyte(); + ammo_57mm = readbyte(); + ammo_hegrenade = readbyte(); + ammo_fbgrenade = readbyte(); + ammo_smokegrenade = readbyte(); + } + + if (fl & PLAYER_AMMO3) { + mode_usp45 = readbyte(); + mode_m4a1 = readbyte(); + mode_glock18 = readbyte(); + mode_temp = readbyte(); + } + + if (fl & PLAYER_CSSHOT) { + cs_shotmultiplier = readbyte(); + } + if (fl & PLAYER_CSSHOTTIME) { + cs_shottime = readfloat(); + } + + 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) +{ + usp45_mag_net = usp45_mag; + glock18_mag_net = glock18_mag; + deagle_mag_net = deagle_mag; + p228_mag_net = p228_mag; + elites_mag_net = elites_mag; + fiveseven_mag_net = fiveseven_mag; + m3_mag_net = m3_mag; + xm1014_mag_net = xm1014_mag; + mp5_mag_net = mp5_mag; + p90_mag_net = p90_mag; + ump45_mag_net = ump45_mag; + mac10_mag_net = mac10_mag; + tmp_mag_net = tmp_mag; + ak47_mag_net = ak47_mag; + sg552_mag_net = sg552_mag; + m4a1_mag_net = m4a1_mag; + aug_mag_net = aug_mag; + scout_mag_net = scout_mag; + awp_mag_net = awp_mag; + g3sg1_mag_net = g3sg1_mag; + sg550_mag_net = sg550_mag; + para_mag_net = para_mag; + + ammo_50ae_net = ammo_50ae; + ammo_762mm_net = ammo_762mm; + ammo_556mm_net = ammo_556mm; + ammo_556mmbox_net = ammo_556mmbox; + ammo_338mag_net = ammo_338mag; + ammo_9mm_net = ammo_9mm; + ammo_buckshot_net = ammo_buckshot; + ammo_45acp_net = ammo_45acp; + ammo_357sig_net = ammo_357sig; + ammo_57mm_net = ammo_57mm; + ammo_hegrenade_net = ammo_hegrenade; + ammo_fbgrenade_net = ammo_fbgrenade; + ammo_smokegrenade_net = ammo_smokegrenade; + + mode_usp45_net = mode_usp45; + mode_m4a1_net = mode_m4a1; + mode_glock18_net = mode_glock18; + mode_temp_net = mode_temp; + + cs_shotmultiplier_net = cs_shotmultiplier; + cs_shottime_net = cs_shottime; +} + +/* +================= +player::PredictPostFrame + +Where we roll back our values to the ones last sent/verified by the server. +================= +*/ +void +player::PredictPostFrame(void) +{ + usp45_mag = usp45_mag_net; + glock18_mag = glock18_mag_net; + deagle_mag = deagle_mag_net; + p228_mag = p228_mag_net; + elites_mag = elites_mag_net; + fiveseven_mag = fiveseven_mag_net; + m3_mag = m3_mag_net; + xm1014_mag = xm1014_mag_net; + mp5_mag = mp5_mag_net; + p90_mag = p90_mag_net; + ump45_mag = ump45_mag_net; + mac10_mag = mac10_mag_net; + tmp_mag = tmp_mag_net; + ak47_mag = ak47_mag_net; + sg552_mag = sg552_mag_net; + m4a1_mag = m4a1_mag_net; + aug_mag = aug_mag_net; + scout_mag = scout_mag_net; + awp_mag = awp_mag_net; + g3sg1_mag = g3sg1_mag_net; + sg550_mag = sg550_mag_net; + para_mag = para_mag_net; + + ammo_50ae = ammo_50ae_net; + ammo_762mm = ammo_762mm_net; + ammo_556mm = ammo_556mm_net; + ammo_556mmbox = ammo_556mmbox_net; + ammo_338mag = ammo_338mag_net; + ammo_9mm = ammo_9mm_net; + ammo_buckshot = ammo_buckshot_net; + ammo_45acp = ammo_45acp_net; + ammo_357sig = ammo_357sig_net; + ammo_57mm = ammo_57mm_net; + ammo_hegrenade = ammo_hegrenade_net; + ammo_fbgrenade = ammo_fbgrenade_net; + ammo_smokegrenade = ammo_smokegrenade_net; + + mode_usp45 = mode_usp45_net; + mode_m4a1 = mode_m4a1_net; + mode_glock18 = mode_glock18_net; + mode_temp = mode_temp_net; + + cs_shotmultiplier = cs_shotmultiplier_net; + cs_shottime = cs_shottime_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 (glock18_mag_net != glock18_mag) + SendFlags |= PLAYER_AMMO1; + if (usp45_mag_net != usp45_mag) + SendFlags |= PLAYER_AMMO1; + if (glock18_mag_net != glock18_mag) + SendFlags |= PLAYER_AMMO1; + if (deagle_mag_net != deagle_mag) + SendFlags |= PLAYER_AMMO1; + if (p228_mag_net != p228_mag) + SendFlags |= PLAYER_AMMO1; + if (elites_mag_net != elites_mag) + SendFlags |= PLAYER_AMMO1; + if (fiveseven_mag_net != fiveseven_mag) + SendFlags |= PLAYER_AMMO1; + if (m3_mag_net != m3_mag) + SendFlags |= PLAYER_AMMO1; + if (xm1014_mag_net != xm1014_mag) + SendFlags |= PLAYER_AMMO1; + if (mp5_mag_net != mp5_mag) + SendFlags |= PLAYER_AMMO1; + if (p90_mag_net != p90_mag) + SendFlags |= PLAYER_AMMO1; + if (ump45_mag_net != ump45_mag) + SendFlags |= PLAYER_AMMO1; + if (mac10_mag_net != mac10_mag) + SendFlags |= PLAYER_AMMO1; + if (tmp_mag_net != tmp_mag) + SendFlags |= PLAYER_AMMO1; + if (ak47_mag_net != ak47_mag) + SendFlags |= PLAYER_AMMO1; + if (sg552_mag_net != sg552_mag) + SendFlags |= PLAYER_AMMO1; + if (m4a1_mag_net != m4a1_mag) + SendFlags |= PLAYER_AMMO1; + if (aug_mag_net != aug_mag) + SendFlags |= PLAYER_AMMO1; + if (scout_mag_net != scout_mag) + SendFlags |= PLAYER_AMMO1; + if (awp_mag_net != awp_mag) + SendFlags |= PLAYER_AMMO1; + if (g3sg1_mag_net != g3sg1_mag) + SendFlags |= PLAYER_AMMO1; + if (sg550_mag_net != sg550_mag) + SendFlags |= PLAYER_AMMO1; + if (para_mag_net != para_mag) + SendFlags |= PLAYER_AMMO1; + + if (ammo_50ae_net != ammo_50ae) + SendFlags |= PLAYER_AMMO2; + if (ammo_762mm_net != ammo_762mm) + SendFlags |= PLAYER_AMMO2; + if (ammo_556mm_net != ammo_556mm) + SendFlags |= PLAYER_AMMO2; + if (ammo_556mmbox_net != ammo_556mmbox) + SendFlags |= PLAYER_AMMO2; + if (ammo_338mag_net != ammo_338mag) + SendFlags |= PLAYER_AMMO2; + if (ammo_9mm_net != ammo_9mm) + SendFlags |= PLAYER_AMMO2; + if (ammo_buckshot_net != ammo_buckshot) + SendFlags |= PLAYER_AMMO2; + if (ammo_45acp_net != ammo_45acp) + SendFlags |= PLAYER_AMMO2; + if (ammo_357sig_net != ammo_357sig) + SendFlags |= PLAYER_AMMO2; + if (ammo_57mm_net != ammo_57mm) + SendFlags |= PLAYER_AMMO2; + if (ammo_hegrenade_net != ammo_hegrenade) + SendFlags |= PLAYER_AMMO2; + if (ammo_fbgrenade_net != ammo_fbgrenade) + SendFlags |= PLAYER_AMMO2; + if (ammo_smokegrenade_net != ammo_smokegrenade) + SendFlags |= PLAYER_AMMO2; + + if (mode_usp45_net != mode_usp45) + SendFlags |= PLAYER_AMMO3; + if (mode_m4a1_net != mode_m4a1) + SendFlags |= PLAYER_AMMO3; + if (mode_glock18_net != mode_glock18) + SendFlags |= PLAYER_AMMO3; + if (mode_temp_net != mode_temp) + SendFlags |= PLAYER_AMMO3; + if (cs_shotmultiplier != cs_shotmultiplier_net) + SendFlags |= PLAYER_CSSHOT; + if (cs_shottime != cs_shottime_net) + SendFlags |= PLAYER_CSSHOTTIME; + + 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; + + glock18_mag_net = glock18_mag; + usp45_mag_net = usp45_mag; + glock18_mag_net = glock18_mag; + deagle_mag_net = deagle_mag; + p228_mag_net = p228_mag; + elites_mag_net = elites_mag; + fiveseven_mag_net = fiveseven_mag; + m3_mag_net = m3_mag; + xm1014_mag_net = xm1014_mag; + mp5_mag_net = mp5_mag; + p90_mag_net = p90_mag; + ump45_mag_net = ump45_mag; + mac10_mag_net = mac10_mag; + tmp_mag_net = tmp_mag; + ak47_mag_net = ak47_mag; + sg552_mag_net = sg552_mag; + m4a1_mag_net = m4a1_mag; + aug_mag_net = aug_mag; + scout_mag_net = scout_mag; + awp_mag_net = awp_mag; + g3sg1_mag_net = g3sg1_mag; + sg550_mag_net = sg550_mag; + para_mag_net = para_mag; + + ammo_50ae_net = ammo_50ae; + ammo_762mm_net = ammo_762mm; + ammo_556mm_net = ammo_556mm; + ammo_556mmbox_net = ammo_556mmbox; + ammo_338mag_net = ammo_338mag; + ammo_9mm_net = ammo_9mm; + ammo_buckshot_net = ammo_buckshot; + ammo_45acp_net = ammo_45acp; + ammo_357sig_net = ammo_357sig; + ammo_57mm_net = ammo_57mm; + ammo_hegrenade_net = ammo_hegrenade; + ammo_fbgrenade_net = ammo_fbgrenade; + ammo_smokegrenade_net = ammo_smokegrenade; + + mode_usp45_net = mode_usp45; + mode_m4a1_net = mode_m4a1; + mode_glock18_net = mode_glock18; + mode_temp_net = mode_temp; + + cs_shotmultiplier_net = cs_shotmultiplier; + cs_shottime_net = cs_shottime; + + if (g_cs_gamestate != GAME_FREEZE) { + if (progress <= 0.0f) { + flags &= ~FL_FROZEN; + SendFlags |= PLAYER_FLAGS; + } + } +} + +/* +================= +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, usp45_mag); + WriteByte(MSG_ENTITY, glock18_mag); + WriteByte(MSG_ENTITY, deagle_mag); + WriteByte(MSG_ENTITY, p228_mag); + WriteByte(MSG_ENTITY, elites_mag); + WriteByte(MSG_ENTITY, fiveseven_mag); + WriteByte(MSG_ENTITY, m3_mag); + WriteByte(MSG_ENTITY, xm1014_mag); + WriteByte(MSG_ENTITY, mp5_mag); + WriteByte(MSG_ENTITY, p90_mag); + WriteByte(MSG_ENTITY, ump45_mag); + WriteByte(MSG_ENTITY, mac10_mag); + WriteByte(MSG_ENTITY, tmp_mag); + WriteByte(MSG_ENTITY, ak47_mag); + WriteByte(MSG_ENTITY, sg552_mag); + WriteByte(MSG_ENTITY, m4a1_mag); + WriteByte(MSG_ENTITY, aug_mag); + WriteByte(MSG_ENTITY, scout_mag); + WriteByte(MSG_ENTITY, awp_mag); + WriteByte(MSG_ENTITY, g3sg1_mag); + WriteByte(MSG_ENTITY, sg550_mag); + WriteByte(MSG_ENTITY, para_mag); + } + + if (fChanged & PLAYER_AMMO2) { + WriteByte(MSG_ENTITY, ammo_50ae); + WriteByte(MSG_ENTITY, ammo_762mm); + WriteByte(MSG_ENTITY, ammo_556mm); + WriteByte(MSG_ENTITY, ammo_556mmbox); + WriteByte(MSG_ENTITY, ammo_338mag); + WriteByte(MSG_ENTITY, ammo_9mm); + WriteByte(MSG_ENTITY, ammo_buckshot); + WriteByte(MSG_ENTITY, ammo_45acp); + WriteByte(MSG_ENTITY, ammo_357sig); + WriteByte(MSG_ENTITY, ammo_57mm); + WriteByte(MSG_ENTITY, ammo_hegrenade); + WriteByte(MSG_ENTITY, ammo_fbgrenade); + WriteByte(MSG_ENTITY, ammo_smokegrenade); + } + + if (fChanged & PLAYER_AMMO3) { + WriteByte(MSG_ENTITY, mode_usp45); + WriteByte(MSG_ENTITY, mode_m4a1); + WriteByte(MSG_ENTITY, mode_glock18); + WriteByte(MSG_ENTITY, mode_temp); + } + + if (fChanged & PLAYER_CSSHOT) { + WriteByte(MSG_ENTITY, cs_shotmultiplier); + } + if (fChanged & PLAYER_CSSHOTTIME) { + WriteFloat(MSG_ENTITY, cs_shottime); + } + + return TRUE; +} +#endif diff --git a/src/shared/pmove.qc b/src/shared/pmove.qc new file mode 100644 index 0000000..8940541 --- /dev/null +++ b/src/shared/pmove.qc @@ -0,0 +1,162 @@ +/* + * 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 PMOVE_STEPHEIGHT 18 +#define PMOVE_AIRSTEPHEIGHT 18 +#define PMOVE_FRICTION 4 +#define PMOVE_EDGEFRICTION 1 +#define PMOVE_STOPSPEED 75 +#define PMOVE_GRAVITY 800 +#define PMOVE_AIRACCELERATE 10 +#define PMOVE_WATERACCELERATE 8 +#define PMOVE_ACCELERATE 4 +#define PMOVE_MAXSPEED 250 + +.float waterlevel; +.float watertype; + +/* values courtesy of https://wiki.alliedmods.net/Cs_weapons_information */ +float +GamePMove_Maxspeed(player target) +{ + float spd = serverkeyfloat("phy_maxspeed"); + + switch (target.activeweapon) + { + case WEAPON_M3: + spd *= 230/250; + break; + case WEAPON_XM1014: + spd *= 240/250; + break; + case WEAPON_MP5: + spd *= 250/250; + break; + case WEAPON_P90: + spd *= 245/250; + break; + case WEAPON_UMP45: + spd *= 250/250; + break; + case WEAPON_MAC10: + spd *= 250/250; + break; + case WEAPON_TMP: + spd *= 250/250; + break; + case WEAPON_AK47: + spd *= 221/250; + break; + case WEAPON_SG552: + spd *= 235/250; + break; + case WEAPON_M4A1: + spd *= 230/250; + break; + case WEAPON_AUG: + spd *= 240/250; + break; + case WEAPON_SCOUT: + spd *= 260/250; + break; + case WEAPON_AWP: + spd *= 210/250; + break; + case WEAPON_G3SG1: + spd *= 210/250; + break; + case WEAPON_SG550: + spd *= 210/250; + break; + case WEAPON_PARA: + spd *= 220/250; + break; + case WEAPON_USP45: + spd *= 250/250; + break; + case WEAPON_GLOCK18: + spd *= 250/250; + break; + case WEAPON_DEAGLE: + spd *= 250/250; + break; + case WEAPON_P228: + spd *= 250/250; + break; + case WEAPON_ELITES: + spd *= 250/250; + break; + case WEAPON_FIVESEVEN: + spd *= 250/250; + break; + case WEAPON_KNIFE: + spd *= 250/250; + break; + case WEAPON_HEGRENADE: + spd *= 250/250; + break; + case WEAPON_FLASHBANG: + spd *= 250/250; + break; + case WEAPON_SMOKEGRENADE: + spd *= 250/250; + break; + case WEAPON_C4BOMB: + spd *= 250/250; + break; + default: + } + + if (target.flags & FL_CROUCHING) { + spd *= 0.5f; + } + + return spd; +} + +void +GamePMove_Fall(player target, float impactspeed) +{ + if (impactspeed > 580) { +#ifdef SERVER + float fFallDamage = (impactspeed - 580) * (100 / (1024 - 580)) * 1.75f; + Damage_Apply(target, world, fFallDamage, 0, DMG_FALL); + + if (random() < 0.5) + sound(target, CHAN_AUTO, "player/pl_pain2.wav", 1.0, ATTN_NORM); + else + sound(target, CHAN_AUTO, "player/pl_pain4.wav", 1.0, ATTN_NORM); +#endif + } +} + +void +GamePMove_Jump(player target) +{ + 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 { + /* slow the player down a bit to prevent bhopping like crazy */ + target.velocity *= 0.80f; + target.velocity[2] += 220; + } +} diff --git a/src/shared/radio.h b/src/shared/radio.h new file mode 100644 index 0000000..94eb79d --- /dev/null +++ b/src/shared/radio.h @@ -0,0 +1,47 @@ + +enum +{ + RADIO_BLOW, + RADIO_BOMBDEF, + RADIO_BOMBPL, + RADIO_CIRCLEBACK, + RADIO_CLEAR, + RADIO_COM_FOLLOWCOM, + RADIO_COM_GETINPOS, + RADIO_COM_GO, + RADIO_COM_REPORTIN, + RADIO_CT_AFFIRM, + RADIO_CT_BACKUP, + RADIO_CT_COVERME, + RADIO_CT_ENEMYS, + RADIO_CT_FIREINHOLE, + RADIO_CT_IMHIT, + RADIO_CT_INPOS, + RADIO_CT_POINT, + RADIO_CT_REPORTINGIN, + RADIO_CTWIN, + RADIO_ENEMYDOWN, + RADIO_FALLBACK, + RADIO_FIREASSIS, + RADIO_FOLLOWME, + RADIO_GETOUT, + RADIO_GO, + RADIO_HITASSIST, + RADIO_HOSDOWN, + RADIO_LETSGO, + RADIO_LOCKNLOAD, + RADIO_MATEDOWN, + RADIO_MEETME, + RADIO_MOVEOUT, + RADIO_NEGATIVE, + RADIO_POSITION, + RADIO_REGROUP, + RADIO_RESCUED, + RADIO_ROGER, + RADIO_ROUNDDRAW, + RADIO_STICKTOG, + RADIO_STORMFRONT, + RADIO_TAKEPOINT, + RADIO_TERWIN, + RADIO_VIP, +}; diff --git a/src/shared/w_ak47.qc b/src/shared/w_ak47.qc new file mode 100644 index 0000000..69a8582 --- /dev/null +++ b/src/shared/w_ak47.qc @@ -0,0 +1,271 @@ +/* + * 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_ak47 (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_ak47.mdl" + +COUNTER-STRIKE (1999) ENTITY + +AK-47 Weapon + +- Buy Menu - +Price: $2500 +Terrorists only weapon + +*/ + +enum +{ + AK47_IDLE, + AK47_RELOAD, + AK47_DRAW, + AK47_SHOOT1, + AK47_SHOOT2, + AK47_SHOOT3 +}; + +void +w_ak47_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_ak47.fire"); + precache_model("models/w_ak47.mdl"); +#else + precache_model("models/v_ak47.mdl"); + precache_model("models/p_ak47.mdl"); +#endif +} + +void +w_ak47_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.ak47_mag, pl.ammo_762mm, -1); +} + +string +w_ak47_wmodel(void) +{ + return "models/w_ak47.mdl"; +} + +string +w_ak47_pmodel(void) +{ + return "models/p_ak47.mdl"; +} + +string +w_ak47_deathmsg(void) +{ + return ""; +} + +int +w_ak47_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + if (startammo == -1) + pl.ak47_mag = 30; + else + pl.ak47_mag = startammo; + } else { + if (pl.ammo_762mm < AMMO_MAX_762MM) { + pl.ammo_762mm = bound(0, pl.ammo_762mm + 30, AMMO_MAX_762MM); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_ak47_draw(void) +{ + player pl = (player)self; + Weapons_SetModel("models/v_ak47.mdl"); + Weapons_ViewAnimation(AK47_DRAW); + +#ifdef CLIENT + pl.cs_cross_mindist = 4; + pl.cs_cross_deltadist = 4; +#endif +} + +void +w_ak47_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0) { + return; + } + if (!pl.ak47_mag) { + return; + } + + Cstrike_ShotMultiplierAdd(pl, 1); + float accuracy = Cstrike_CalculateAccuracy(pl, 200); + pl.ak47_mag--; + +#ifdef CLIENT + View_SetMuzzleflash(MUZZLE_RIFLE); + + int r = (float)input_sequence % 3; + switch (r) { + case 0: + Weapons_ViewAnimation(AK47_SHOOT1); + break; + case 1: + Weapons_ViewAnimation(AK47_SHOOT2); + break; + default: + Weapons_ViewAnimation(AK47_SHOOT3); + break; + } +#else + TraceAttack_SetPenetrationPower(1); + TraceAttack_FireBullets(1, pl.origin + pl.view_ofs, 36, [accuracy,accuracy], WEAPON_AK47); + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_SHOOT_AK47, 0.45f); + else + Animation_PlayerTopTemp(ANIM_CROUCH_SHOOT_AK47, 0.45f); + + Sound_Play(pl, CHAN_WEAPON, "weapon_ak47.fire"); +#endif + + pl.w_attack_next = 0.0955f; + pl.w_idle_next = pl.w_attack_next; +} + +void +w_ak47_reload(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (pl.ak47_mag >= 30) { + return; + } + if (!pl.ammo_762mm) { + return; + } + +#ifdef CLIENT + Weapons_ViewAnimation(AK47_RELOAD); +#else + Weapons_ReloadWeapon(pl, player::ak47_mag, player::ammo_762mm, 30); +#endif + + pl.w_attack_next = 2.4f; + pl.w_idle_next = pl.w_attack_next; +} + +float +w_ak47_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CROUCH_AIM_AK47 : ANIM_AIM_AK47; +} + +void +w_ak47_hud(void) +{ +#ifdef CLIENT + Cstrike_DrawCrosshair(); + 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/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_ak47_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.ak47_mag == 0 && pl.ammo_762mm == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + HUD_DrawAmmoBar(pos, pl.ammo_762mm, AMMO_MAX_762MM, a); + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud11_spr, + [0,0], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud11_spr, + [0,0], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_ak47 = +{ + .name = "ak47", + .id = ITEM_AK47, + .slot = 0, + .slot_pos = 7, + .allow_drop = TRUE, + .draw = w_ak47_draw, + .holster = __NULL__, + .primary = w_ak47_primary, + .secondary = __NULL__, + .reload = w_ak47_reload, + .release = w_cstrike_weaponrelease, + .crosshair = w_ak47_hud, + .precache = w_ak47_precache, + .pickup = w_ak47_pickup, + .updateammo = w_ak47_updateammo, + .wmodel = w_ak47_wmodel, + .pmodel = w_ak47_pmodel, + .deathmsg = w_ak47_deathmsg, + .aimanim = w_ak47_aimanim, + .hudpic = w_ak47_hudpic +}; + +#ifdef SERVER +void +weapon_ak47(void) +{ + Weapons_InitItem(WEAPON_AK47); +} +#endif diff --git a/src/shared/w_aug.qc b/src/shared/w_aug.qc new file mode 100644 index 0000000..cc5ce23 --- /dev/null +++ b/src/shared/w_aug.qc @@ -0,0 +1,296 @@ +/* + * 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_aug (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_aug.mdl" + +COUNTER-STRIKE (1999) ENTITY + +Steyr AUG Weapon + +- Buy Menu - +Price: $3500 +Counter-Terrorists only weapon + +*/ + +enum +{ + AUG_IDLE, + AUG_RELOAD, + AUG_DRAW, + AUG_SHOOT1, + AUG_SHOOT2, + AUG_SHOOT3 +}; + +void +w_aug_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_aug.fire"); + precache_model("models/w_aug.mdl"); +#else + precache_model("models/v_aug.mdl"); + precache_model("models/p_aug.mdl"); +#endif +} + +void +w_aug_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.aug_mag, pl.ammo_762mm, -1); +} + +string +w_aug_wmodel(void) +{ + return "models/w_aug.mdl"; +} + +string +w_aug_pmodel(void) +{ + return "models/p_aug.mdl"; +} + +string +w_aug_deathmsg(void) +{ + return ""; +} + +int +w_aug_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + if (startammo == -1) + pl.aug_mag = 30; + else + pl.aug_mag = startammo; + } else { + if (pl.ammo_762mm < AMMO_MAX_762MM) { + pl.ammo_762mm = bound(0, pl.ammo_762mm + 30, AMMO_MAX_762MM); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_aug_draw(void) +{ + player pl = (player)self; + Weapons_SetModel("models/v_aug.mdl"); + Weapons_ViewAnimation(AUG_DRAW); + +#ifdef CLIENT + pl.cs_cross_mindist = 3; + pl.cs_cross_deltadist = 3; +#endif +} + +void +w_aug_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (!pl.aug_mag) { + return; + } + + Cstrike_ShotMultiplierAdd(pl, 1); + float accuracy = Cstrike_CalculateAccuracy(pl, 215); + pl.aug_mag--; + +#ifdef CLIENT + View_SetMuzzleflash(MUZZLE_RIFLE); + + int r = (float)input_sequence % 3; + switch (r) { + case 0: + Weapons_ViewAnimation(AUG_SHOOT1); + break; + case 1: + Weapons_ViewAnimation(AUG_SHOOT2); + break; + default: + Weapons_ViewAnimation(AUG_SHOOT3); + break; + } +#else + TraceAttack_SetPenetrationPower(1); + TraceAttack_FireBullets(1, pl.origin + pl.view_ofs, 32, [accuracy,accuracy], WEAPON_AUG); + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_SHOOT_RIFLE, 0.45f); + else + Animation_PlayerTopTemp(ANIM_CROUCH_SHOOT_RIFLE, 0.45f); + + Sound_Play(pl, CHAN_WEAPON, "weapon_aug.fire"); +#endif + + if (pl.viewzoom == 1.0f) { + pl.w_attack_next = 0.0825f; + } else { + pl.w_attack_next = 0.15f; + } + pl.w_idle_next = pl.w_attack_next; +} + +void +w_aug_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_aug_reload(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (pl.aug_mag >= 30) { + return; + } + if (!pl.ammo_762mm) { + return; + } + +#ifdef CLIENT + Weapons_ViewAnimation(AUG_RELOAD); +#else + Weapons_ReloadWeapon(pl, player::aug_mag, player::ammo_762mm, 30); +#endif + + pl.w_attack_next = 3.3f; + pl.w_idle_next = pl.w_attack_next; +} + +float +w_aug_aimanim(void) +{ + return w_ak47_aimanim(); +} + +void +w_aug_hud(void) +{ +#ifdef CLIENT + player pl = (player)self; + if (pl.viewzoom == 1.0f) { + Cstrike_DrawCrosshair(); + } else { + Cstrike_DrawSimpleCrosshair(); + } + 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/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_aug_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.aug_mag == 0 && pl.ammo_762mm == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + HUD_DrawAmmoBar(pos, pl.ammo_762mm, AMMO_MAX_762MM, a); + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud15_spr, + [0,45/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud14_spr, + [0,45/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_aug = +{ + .name = "aug", + .id = ITEM_AUG, + .slot = 0, + .slot_pos = 10, + .allow_drop = TRUE, + .draw = w_aug_draw, + .holster = __NULL__, + .primary = w_aug_primary, + .secondary = w_aug_secondary, + .reload = w_aug_reload, + .release = w_cstrike_weaponrelease, + .crosshair = w_aug_hud, + .precache = w_aug_precache, + .pickup = w_aug_pickup, + .updateammo = w_aug_updateammo, + .wmodel = w_aug_wmodel, + .pmodel = w_aug_pmodel, + .deathmsg = w_aug_deathmsg, + .aimanim = w_aug_aimanim, + .hudpic = w_aug_hudpic +}; + +#ifdef SERVER +void +weapon_aug(void) +{ + Weapons_InitItem(WEAPON_AUG); +} +#endif diff --git a/src/shared/w_awp.qc b/src/shared/w_awp.qc new file mode 100644 index 0000000..475b47a --- /dev/null +++ b/src/shared/w_awp.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_awp (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_awp.mdl" + +COUNTER-STRIKE (1999) ENTITY + +AWP (AI Arctic Warfare/Magnum) Weapon + +- Buy Menu - +Price: $4750 + +*/ + +enum +{ + AWP_IDLE, + AWP_SHOOT1, + AWP_SHOOT2, + AWP_SHOOT3, + AWP_RELOAD, + AWP_DRAW +}; + +void +w_awp_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_awp.fire"); + Sound_Precache("weapon_awp.zoom"); + precache_model("models/w_awp.mdl"); +#else + precache_model("models/v_awp.mdl"); + precache_model("models/p_awp.mdl"); +#endif +} + +void +w_awp_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.awp_mag, pl.ammo_338mag, pl.mode_temp); +} + +string +w_awp_wmodel(void) +{ + return "models/w_awp.mdl"; +} + +string +w_awp_pmodel(void) +{ + return "models/p_awp.mdl"; +} + +string +w_awp_deathmsg(void) +{ + return ""; +} + +int +w_awp_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + if (startammo == -1) + pl.awp_mag = 10; + else + pl.awp_mag = startammo; + } else { + if (pl.ammo_338mag < 20) { + pl.ammo_338mag = bound(0, pl.ammo_338mag + 10, 20); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_awp_draw(void) +{ + player pl = (player)self; + Weapons_SetModel("models/v_awp.mdl"); + Weapons_ViewAnimation(AWP_DRAW); + pl.mode_temp = 0; + +#ifdef CLIENT + pl.cs_cross_mindist = 8; + pl.cs_cross_deltadist = 3; +#endif +} + +void +w_awp_release(void) +{ + player pl = (player)self; + + w_cstrike_weaponrelease(); + + if (pl.w_idle_next > 0.0f) { + pl.viewzoom = 1.0f; + return; + } + + if (pl.mode_temp == 1) { + pl.viewzoom = 0.45f; + } else if (pl.mode_temp == 2) { + pl.viewzoom = 0.1f; + } else { + pl.viewzoom = 1.0f; + } +} + +void +w_awp_secondary(void) +{ + player pl = (player)self; + if (pl.w_attack_next) { + return; + } + +#ifdef SSQC + Sound_Play(pl, CHAN_WEAPON, "weapon_awp.zoom"); +#endif + + /* Simple toggle of fovs */ + if (pl.mode_temp == 1) { + pl.mode_temp = 2; + } else if (pl.mode_temp == 2) { + pl.mode_temp = 0; + } else { + pl.mode_temp = 1; + } + + pl.w_attack_next = 0.3f; + pl.w_idle_next = 0.0f; + w_awp_release(); +} + +void +w_awp_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + w_awp_release(); + return; + } + if (!pl.awp_mag) { + return; + } + + Cstrike_ShotMultiplierAdd(pl, 1); + float accuracy = Cstrike_CalculateAccuracy(pl, -1); + pl.awp_mag--; + +#ifdef CLIENT + View_SetMuzzleflash(MUZZLE_RIFLE); + + int r = (float)input_sequence % 3; + switch (r) { + case 0: + Weapons_ViewAnimation(AWP_SHOOT1); + break; + case 1: + Weapons_ViewAnimation(AWP_SHOOT2); + break; + default: + Weapons_ViewAnimation(AWP_SHOOT3); + break; + } +#else + TraceAttack_SetPenetrationPower(2); + TraceAttack_FireBullets(1, pl.origin + pl.view_ofs, 115, [accuracy,accuracy], WEAPON_AWP); + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_SHOOT_RIFLE, 0.45f); + else + Animation_PlayerTopTemp(ANIM_CROUCH_SHOOT_RIFLE, 0.45f); + + Sound_Play(pl, CHAN_WEAPON, "weapon_awp.fire"); +#endif + + pl.w_attack_next = 1.2f; + pl.w_idle_next = pl.w_attack_next; +} + +void +w_awp_reload(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (pl.awp_mag >= 10) { + return; + } + if (!pl.ammo_338mag) { + return; + } + +#ifdef CLIENT + Weapons_ViewAnimation(AWP_RELOAD); +#else + Weapons_ReloadWeapon(pl, player::awp_mag, player::ammo_338mag, 10); +#endif + + pl.w_attack_next = 2.9f; + pl.w_idle_next = pl.w_attack_next; +} + +float +w_awp_aimanim(void) +{ + return w_ak47_aimanim(); +} + +void +w_awp_hud(void) +{ +#ifdef CLIENT + player pl = (player)self; + if (pl.viewzoom < 1.0f) { + Cstrike_DrawScope(); + } + 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, [24/256,96/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_awp_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.awp_mag == 0 && pl.ammo_338mag == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + HUD_DrawAmmoBar(pos, pl.ammo_338mag, AMMO_MAX_338MAG, a); + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud5_spr, + [0,135/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud2_spr, + [0,135/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_awp = +{ + .name = "awp", + .id = ITEM_AWP, + .slot = 0, + .slot_pos = 12, + .allow_drop = TRUE, + .draw = w_awp_draw, + .holster = __NULL__, + .primary = w_awp_primary, + .secondary = w_awp_secondary, + .reload = w_awp_reload, + .release = w_awp_release, + .crosshair = w_awp_hud, + .precache = w_awp_precache, + .pickup = w_awp_pickup, + .updateammo = w_awp_updateammo, + .wmodel = w_awp_wmodel, + .pmodel = w_awp_pmodel, + .deathmsg = w_awp_deathmsg, + .aimanim = w_awp_aimanim, + .hudpic = w_awp_hudpic +}; + +#ifdef SERVER +void +weapon_awp(void) +{ + Weapons_InitItem(WEAPON_AWP); +} +#endif diff --git a/src/shared/w_c4bomb.qc b/src/shared/w_c4bomb.qc new file mode 100644 index 0000000..b1d9cda --- /dev/null +++ b/src/shared/w_c4bomb.qc @@ -0,0 +1,241 @@ +/* + * 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_c4bomb (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_c4.mdl" + +COUNTER-STRIKE (1999) ENTITY + +C4 Bomb Weapon, Bomb Defusal Gamemode Entity + +Default arsenal for Terrorists + +Can only be picked up by Terrorists and planted in +func_bombtarget brush entities. + +*/ + +/* C4 weapon logic */ + +enum +{ + C4_IDLE, + C4_DRAW, + C4_DROP, + C4_ENTERCODE +}; + +enum +{ + C4S_NONE, + C4S_ENTERINGCODE, + C4S_DROPPING, + C4S_DONE +}; + +void +w_c4bomb_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_c4bomb.disarm"); + Sound_Precache("weapon_c4bomb.disarmed"); + Sound_Precache("weapon_c4bomb.explode"); + Sound_Precache("weapon_c4bomb.plant"); + precache_sound("weapons/c4_beep1.wav"); + precache_sound("weapons/c4_beep2.wav"); + precache_sound("weapons/c4_beep3.wav"); + precache_sound("weapons/c4_beep4.wav"); + precache_sound("weapons/c4_beep5.wav"); + precache_sound("weapons/c4_disarmed.wav"); + precache_sound("weapons/c4_disarm.wav"); + precache_model("models/w_c4.mdl"); + precache_model("models/w_backpack.mdl"); + precache_model("sprites/ledglow.spr"); +#else + precache_model("models/v_c4.mdl"); + precache_model("models/p_c4.mdl"); +#endif +} + +void +w_c4bomb_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, -1, 1, -1); +} + +string +w_c4bomb_wmodel(void) +{ + return "models/w_backpack.mdl"; +} + +string +w_c4bomb_pmodel(void) +{ + return "models/p_c4.mdl"; +} + +string +w_c4bomb_deathmsg(void) +{ + return ""; +} + +void +w_c4bomb_draw(void) +{ + player pl = (player)self; + Weapons_SetModel("models/v_c4.mdl"); + Weapons_ViewAnimation(C4_DRAW); + pl.mode_temp = 0; +} + +void +w_c4bomb_release(void) +{ + player pl = (player)self; + + if (pl.mode_temp == C4S_DROPPING) { + if (pl.w_idle_next <= 0.0f) { + pl.mode_temp = C4S_DONE; +#ifdef SERVER + C4Bomb_Plant(pl); + Weapons_RemoveItem(pl, WEAPON_C4BOMB); +#endif + } + return; + } + + /* reset animation */ + if (pl.mode_temp != C4S_NONE) { + Weapons_ViewAnimation(C4_IDLE); + } + pl.mode_temp = C4S_NONE; + pl.w_idle_next = 0.0f; +} + +void +w_c4bomb_primary(void) +{ + player pl = (player)self; + + if (!(pl.gflags & GF_BOMBZONE)) { + return; + } + + pl.flags |= FL_FROZEN; + + switch (pl.mode_temp) { + case C4S_NONE: + pl.mode_temp = C4S_ENTERINGCODE; + Weapons_ViewAnimation(C4_ENTERCODE); + pl.w_idle_next = 3.0f; + break; + case C4S_ENTERINGCODE: + if (pl.w_idle_next <= 0.0f) { + pl.mode_temp = C4S_DROPPING; + Weapons_ViewAnimation(C4_DROP); + pl.w_idle_next = 1.0f; + } + break; + case C4S_DROPPING: + w_c4bomb_release(); + return; + break; + default: + break; + } + + pl.w_attack_next = 0.0f; +} + +float +w_c4bomb_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CROUCH_AIM_C4 : ANIM_AIM_C4; +} + +void +w_c4bomb_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/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_c4bomb_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, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud1_spr, + [0,0], + [170/256,45/256], + g_hud_color, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_c4bomb = +{ + .name = "c4", + .id = ITEM_C4BOMB, + .slot = 4, + .slot_pos = 0, + .allow_drop = TRUE, + .draw = w_c4bomb_draw, + .holster = __NULL__, + .primary = w_c4bomb_primary, + .secondary = __NULL__, + .reload = __NULL__, + .release = w_c4bomb_release, + .crosshair = w_c4bomb_hud, + .precache = w_c4bomb_precache, + .pickup = __NULL__, + .updateammo = w_c4bomb_updateammo, + .wmodel = w_c4bomb_wmodel, + .pmodel = w_c4bomb_pmodel, + .deathmsg = w_c4bomb_deathmsg, + .aimanim = w_c4bomb_aimanim, + .hudpic = w_c4bomb_hudpic +}; + +#ifdef SERVER +void +weapon_c4bomb(void) +{ + Weapons_InitItem(WEAPON_C4BOMB); +} +#endif diff --git a/src/shared/w_deagle.qc b/src/shared/w_deagle.qc new file mode 100644 index 0000000..d0b720f --- /dev/null +++ b/src/shared/w_deagle.qc @@ -0,0 +1,276 @@ +/* + * 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_deagle (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_deagle.mdl" + +COUNTER-STRIKE (1999) ENTITY + +Desert Eagle .50 AE Weapon + +- Buy Menu - +Price: $650 + +*/ + +enum +{ + DEAGLE_IDLE, + DEAGLE_SHOOT1, + DEAGLE_SHOOT2, + DEAGLE_SHOOT_EMPTY, + DEAGLE_RELOAD, + DEAGLE_DRAW +}; + +void +w_deagle_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_deagle.fire"); + precache_model("models/w_deagle.mdl"); +#else + precache_model("models/v_deagle.mdl"); + precache_model("models/p_deagle.mdl"); +#endif +} + +void +w_deagle_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.deagle_mag, pl.ammo_50ae, -1); +} + +string +w_deagle_wmodel(void) +{ + return "models/w_deagle.mdl"; +} + +string +w_deagle_pmodel(void) +{ + return "models/p_deagle.mdl"; +} + +string +w_deagle_deathmsg(void) +{ + return ""; +} + +int +w_deagle_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + if (startammo == -1) + pl.deagle_mag = 7; + else + pl.deagle_mag = startammo; + } else { + if (pl.ammo_50ae < AMMO_MAX_50AE) { + pl.ammo_50ae = bound(0, pl.ammo_50ae + 7, AMMO_MAX_50AE); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_deagle_draw(void) +{ + player pl = (player)self; + Weapons_SetModel("models/v_deagle.mdl"); + Weapons_ViewAnimation(DEAGLE_DRAW); + +#ifdef CLIENT + pl.cs_cross_mindist = 8; + pl.cs_cross_deltadist = 3; +#endif +} + +void +w_deagle_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + + if (pl.gflags & GF_SEMI_TOGGLED) { + return; + } + if (!pl.deagle_mag) { + return; + } + + Cstrike_ShotMultiplierAdd(pl, 1); + float accuracy = Cstrike_CalculateAccuracy(pl, 200); + pl.deagle_mag--; + +#ifdef CLIENT + View_SetMuzzleflash(MUZZLE_RIFLE); + + if (pl.deagle_mag <= 0) { + Weapons_ViewAnimation(DEAGLE_SHOOT_EMPTY); + } else { + int r = (float)input_sequence % 2; + switch (r) { + case 0: + Weapons_ViewAnimation(DEAGLE_SHOOT1); + break; + default: + Weapons_ViewAnimation(DEAGLE_SHOOT2); + break; + } + } +#else + TraceAttack_SetPenetrationPower(1); + TraceAttack_FireBullets(1, pl.origin + pl.view_ofs, 54, [accuracy,accuracy], WEAPON_DEAGLE); + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_CROUCH_SHOOT_ONEHAND, 0.45f); + else + Animation_PlayerTopTemp(ANIM_SHOOT_ONEHAND, 0.45f); + + Sound_Play(pl, CHAN_WEAPON, "weapon_deagle.fire"); +#endif + + pl.gflags |= GF_SEMI_TOGGLED; + pl.w_attack_next = 0.15f; + pl.w_idle_next = pl.w_attack_next; +} + +void +w_deagle_reload(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (pl.deagle_mag >= 7) { + return; + } + if (!pl.ammo_50ae) { + return; + } + +#ifdef CLIENT + Weapons_ViewAnimation(DEAGLE_RELOAD); +#else + Weapons_ReloadWeapon(pl, player::deagle_mag, player::ammo_50ae, 7); +#endif + + pl.w_attack_next = 2.1f; + pl.w_idle_next = pl.w_attack_next; +} + +float +w_deagle_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CROUCH_AIM_ONEHAND : ANIM_AIM_ONEHAND; +} + +void +w_deagle_hud(void) +{ +#ifdef CLIENT + Cstrike_DrawCrosshair(); + 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, [24/256,72/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_deagle_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.deagle_mag == 0 && pl.ammo_50ae == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + HUD_DrawAmmoBar(pos, pl.ammo_50ae, AMMO_MAX_50AE, a); + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud11_spr, + [0,90/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud10_spr, + [0,90/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_deagle = +{ + .name = "deagle", + .id = ITEM_DEAGLE, + .slot = 1, + .slot_pos = 2, + .allow_drop = TRUE, + .draw = w_deagle_draw, + .holster = __NULL__, + .primary = w_deagle_primary, + .secondary = __NULL__, + .reload = w_deagle_reload, + .release = w_cstrike_weaponrelease, + .crosshair = w_deagle_hud, + .precache = w_deagle_precache, + .pickup = w_deagle_pickup, + .updateammo = w_deagle_updateammo, + .wmodel = w_deagle_wmodel, + .pmodel = w_deagle_pmodel, + .deathmsg = w_deagle_deathmsg, + .aimanim = w_deagle_aimanim, + .hudpic = w_deagle_hudpic +}; + +#ifdef SERVER +void +weapon_deagle(void) +{ + Weapons_InitItem(WEAPON_DEAGLE); +} +#endif diff --git a/src/shared/w_elites.qc b/src/shared/w_elites.qc new file mode 100644 index 0000000..f4cadda --- /dev/null +++ b/src/shared/w_elites.qc @@ -0,0 +1,328 @@ +/* + * 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_elites (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_elite.mdl" + +COUNTER-STRIKE (1999) ENTITY + +Dual Beretta 96G (Elites) Weapon + +- Buy Menu - +Price: $1000 + +*/ + +enum +{ + ELITES_IDLE, + ELITES_IDLE_LEFTEMPTY, + ELITES_SHOOT_LEFT1, + ELITES_SHOOT_LEFT2, + ELITES_SHOOT_LEFT3, + ELITES_SHOOT_LEFT4, + ELITES_SHOOT_LEFT5, + ELITES_SHOOT_LEFTLAST, + ELITES_SHOOT_RIGHT1, + ELITES_SHOOT_RIGHT2, + ELITES_SHOOT_RIGHT3, + ELITES_SHOOT_RIGHT4, + ELITES_SHOOT_RIGHT5, + ELITES_SHOOT_RIGHTLAST, + ELITES_RELOAD, + ELITES_DRAW +}; + +void +w_elites_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_elites.fire"); + precache_model("models/w_elite.mdl"); +#else + precache_model("models/v_elite.mdl"); + precache_model("models/p_elite.mdl"); +#endif +} + +void +w_elites_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.elites_mag, pl.ammo_9mm, -1); +} + +string +w_elites_wmodel(void) +{ + return "models/w_elites.mdl"; +} + +string +w_elites_pmodel(void) +{ + return "models/p_elites.mdl"; +} + +string +w_elites_deathmsg(void) +{ + return ""; +} + +int +w_elites_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + if (startammo == -1) + pl.elites_mag = 30; + else + pl.elites_mag = startammo; + } else { + if (pl.ammo_9mm < AMMO_MAX_9MM) { + pl.ammo_9mm = bound(0, pl.ammo_9mm + 30, AMMO_MAX_9MM); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_elites_draw(void) +{ + player pl = (player)self; + Weapons_SetModel("models/v_elite.mdl"); + Weapons_ViewAnimation(ELITES_DRAW); + pl.mode_temp = 0; + +#ifdef CLIENT + pl.cs_cross_mindist = 4; + pl.cs_cross_deltadist = 3; +#endif +} + +void +w_elites_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (pl.gflags & GF_SEMI_TOGGLED) { + return; + } + if (!pl.elites_mag) { + return; + } + + pl.mode_temp = 1 - pl.mode_temp; + + Cstrike_ShotMultiplierAdd(pl, 1); + float accuracy = Cstrike_CalculateAccuracy(pl, 200); + pl.elites_mag--; + +#ifdef CLIENT + View_SetMuzzleflash(MUZZLE_RIFLE); + + int r = (float)input_sequence % 5; + if (pl.mode_temp) { + if (pl.elites_mag <= 0) { + Weapons_ViewAnimation(ELITES_SHOOT_LEFTLAST); + } else { + switch (r) { + case 0: + Weapons_ViewAnimation(ELITES_SHOOT_LEFT1); + break; + case 1: + Weapons_ViewAnimation(ELITES_SHOOT_LEFT2); + break; + case 2: + Weapons_ViewAnimation(ELITES_SHOOT_LEFT3); + break; + case 3: + Weapons_ViewAnimation(ELITES_SHOOT_LEFT4); + break; + default: + Weapons_ViewAnimation(ELITES_SHOOT_LEFT1); + break; + } + } + } else { + if (pl.elites_mag <= 0) { + Weapons_ViewAnimation(ELITES_SHOOT_RIGHTLAST); + } else { + switch (r) { + case 0: + Weapons_ViewAnimation(ELITES_SHOOT_RIGHT1); + break; + case 1: + Weapons_ViewAnimation(ELITES_SHOOT_RIGHT2); + break; + case 2: + Weapons_ViewAnimation(ELITES_SHOOT_RIGHT3); + break; + case 3: + Weapons_ViewAnimation(ELITES_SHOOT_RIGHT4); + break; + default: + Weapons_ViewAnimation(ELITES_SHOOT_RIGHT1); + break; + } + } + } +#else + TraceAttack_SetPenetrationPower(0); + TraceAttack_FireBullets(1, pl.origin + pl.view_ofs, 45, [accuracy,accuracy], WEAPON_ELITES); + + if (self.flags & FL_CROUCHING) { + if (pl.mode_temp) + Animation_PlayerTopTemp(ANIM_CROUCH_SHOOT2_DUALPISTOLS, 0.45f); + else + Animation_PlayerTopTemp(ANIM_CROUCH_SHOOT_DUALPISTOLS, 0.45f); + } else { + if (pl.mode_temp) + Animation_PlayerTopTemp(ANIM_SHOOT2_DUALPISTOLS, 0.45f); + else + Animation_PlayerTopTemp(ANIM_SHOOT_DUALPISTOLS, 0.45f); + } + + Sound_Play(pl, CHAN_WEAPON, "weapon_elites.fire"); +#endif + + pl.gflags |= GF_SEMI_TOGGLED; + pl.w_attack_next = 0.15f; + pl.w_idle_next = pl.w_attack_next; +} + +void +w_elites_reload(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (pl.elites_mag >= 30) { + return; + } + if (!pl.ammo_9mm) { + return; + } + +#ifdef CLIENT + Weapons_ViewAnimation(ELITES_RELOAD); +#else + Weapons_ReloadWeapon(pl, player::elites_mag, player::ammo_9mm, 30); +#endif + + pl.w_attack_next = 4.6f; + pl.w_idle_next = pl.w_attack_next; +} + +float +w_elites_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CROUCH_AIM_DUALPISTOLS : ANIM_AIM_DUALPISTOLS; +} + +void +w_elites_hud(void) +{ +#ifdef CLIENT + Cstrike_DrawCrosshair(); + 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, [48/256,72/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_elites_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.elites_mag == 0 && pl.ammo_9mm == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + HUD_DrawAmmoBar(pos, pl.ammo_9mm, AMMO_MAX_9MM, a); + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud15_spr, + [0,90/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud14_spr, + [0,90/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_elites = +{ + .name = "elites", + .id = ITEM_ELITES, + .slot = 1, + .slot_pos = 4, + .allow_drop = TRUE, + .draw = w_elites_draw, + .holster = __NULL__, + .primary = w_elites_primary, + .secondary = __NULL__, + .reload = w_elites_reload, + .release = w_cstrike_weaponrelease, + .crosshair = w_elites_hud, + .precache = w_elites_precache, + .pickup = w_elites_pickup, + .updateammo = w_elites_updateammo, + .wmodel = w_elites_wmodel, + .pmodel = w_elites_pmodel, + .deathmsg = w_elites_deathmsg, + .aimanim = w_elites_aimanim, + .hudpic = w_elites_hudpic +}; + +#ifdef SERVER +void +weapon_elites(void) +{ + Weapons_InitItem(WEAPON_ELITES); +} +#endif diff --git a/src/shared/w_fiveseven.qc b/src/shared/w_fiveseven.qc new file mode 100644 index 0000000..a0cc0a1 --- /dev/null +++ b/src/shared/w_fiveseven.qc @@ -0,0 +1,276 @@ +/* + * 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_fiveseven (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_fiveseven.mdl" + +COUNTER-STRIKE (1999) ENTITY + +Five-SeveN Weapon + +- Buy Menu - +Price: $750 + +*/ + +enum +{ + FIVESEVEN_IDLE, + FIVESEVEN_SHOOT1, + FIVESEVEN_SHOOT2, + FIVESEVEN_SHOOT_EMPTY, + FIVESEVEN_RELOAD, + FIVESEVEN_DRAW +}; + +void +w_fiveseven_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_fiveseven.fire"); + precache_model("models/w_fiveseven.mdl"); +#else + precache_model("models/v_fiveseven.mdl"); + precache_model("models/p_fiveseven.mdl"); +#endif +} + +void +w_fiveseven_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.fiveseven_mag, pl.ammo_57mm, -1); +} + +string +w_fiveseven_wmodel(void) +{ + return "models/w_fiveseven.mdl"; +} + +string +w_fiveseven_pmodel(void) +{ + return "models/p_fiveseven.mdl"; +} + +string +w_fiveseven_deathmsg(void) +{ + return ""; +} + +int +w_fiveseven_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + if (startammo == -1) + pl.fiveseven_mag = 20; + else + pl.fiveseven_mag = startammo; + } else { + if (pl.ammo_57mm < AMMO_MAX_57MM) { + pl.ammo_57mm = bound(0, pl.ammo_57mm + 20, AMMO_MAX_57MM); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_fiveseven_draw(void) +{ + player pl = (player)self; + Weapons_SetModel("models/v_fiveseven.mdl"); + Weapons_ViewAnimation(FIVESEVEN_DRAW); + +#ifdef CLIENT + pl.cs_cross_mindist = 8; + pl.cs_cross_deltadist = 3; +#endif +} + +void +w_fiveseven_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + + if (pl.gflags & GF_SEMI_TOGGLED) { + return; + } + if (!pl.fiveseven_mag) { + return; + } + + Cstrike_ShotMultiplierAdd(pl, 1); + float accuracy = Cstrike_CalculateAccuracy(pl, 200); + pl.fiveseven_mag--; + +#ifdef CLIENT + View_SetMuzzleflash(MUZZLE_RIFLE); + + if (pl.fiveseven_mag <= 0) { + Weapons_ViewAnimation(FIVESEVEN_SHOOT_EMPTY); + } else { + int r = (float)input_sequence % 2; + switch (r) { + case 0: + Weapons_ViewAnimation(FIVESEVEN_SHOOT1); + break; + default: + Weapons_ViewAnimation(FIVESEVEN_SHOOT2); + break; + } + } +#else + TraceAttack_SetPenetrationPower(0); + TraceAttack_FireBullets(1, pl.origin + pl.view_ofs, 25, [accuracy,accuracy], WEAPON_FIVESEVEN); + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_SHOOT_ONEHAND, 0.45f); + else + Animation_PlayerTopTemp(ANIM_CROUCH_SHOOT_ONEHAND, 0.45f); + + Sound_Play(pl, CHAN_WEAPON, "weapon_fiveseven.fire"); +#endif + + pl.gflags |= GF_SEMI_TOGGLED; + pl.w_attack_next = 0.15f; + pl.w_idle_next = pl.w_attack_next; +} + +void +w_fiveseven_reload(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (pl.fiveseven_mag >= 20) { + return; + } + if (!pl.ammo_57mm) { + return; + } + +#ifdef CLIENT + Weapons_ViewAnimation(FIVESEVEN_RELOAD); +#else + Weapons_ReloadWeapon(pl, player::fiveseven_mag, player::ammo_57mm, 20); +#endif + + pl.w_attack_next = 3.1f; + pl.w_idle_next = pl.w_attack_next; +} + +float +w_fiveseven_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CROUCH_AIM_ONEHAND : ANIM_AIM_ONEHAND; +} + +void +w_fiveseven_hud(void) +{ +#ifdef CLIENT + Cstrike_DrawCrosshair(); + 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, [120/256,96/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_fiveseven_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.fiveseven_mag == 0 && pl.ammo_57mm == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + HUD_DrawAmmoBar(pos, pl.ammo_57mm, AMMO_MAX_57MM, a); + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud15_spr, + [0,135/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud14_spr, + [0,135/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_fiveseven = +{ + .name = "fiveseven", + .id = ITEM_FIVESEVEN, + .slot = 1, + .slot_pos = 5, + .allow_drop = TRUE, + .draw = w_fiveseven_draw, + .holster = __NULL__, + .primary = w_fiveseven_primary, + .secondary = __NULL__, + .reload = w_fiveseven_reload, + .release = w_cstrike_weaponrelease, + .crosshair = w_fiveseven_hud, + .precache = w_fiveseven_precache, + .pickup = w_fiveseven_pickup, + .updateammo = w_fiveseven_updateammo, + .wmodel = w_fiveseven_wmodel, + .pmodel = w_fiveseven_pmodel, + .deathmsg = w_fiveseven_deathmsg, + .aimanim = w_fiveseven_aimanim, + .hudpic = w_fiveseven_hudpic +}; + +#ifdef SERVER +void +weapon_fiveseven(void) +{ + Weapons_InitItem(WEAPON_FIVESEVEN); +} +#endif diff --git a/src/shared/w_flashbang.qc b/src/shared/w_flashbang.qc new file mode 100644 index 0000000..6734d1e --- /dev/null +++ b/src/shared/w_flashbang.qc @@ -0,0 +1,292 @@ +/* + * 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_flashbang (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_flashbang.mdl" + +COUNTER-STRIKE (1999) ENTITY + +Concussion (Flashbang) Grenade Weapon + +When thrown, nearby players become blinded temporarily from the blast. + +- Buy Menu - +Price: $200 + +*/ + +enum +{ + FLASHBANG_IDLE, + FLASHBANG_PULLPIN, + FLASHBANG_THROW, + FLASHBANG_DRAW, +}; + +void +w_flashbang_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_flashbang.bounce"); + Sound_Precache("weapon_flashbang.explode"); + precache_model("models/w_flashbang.mdl"); +#else + precache_model("models/v_flashbang.mdl"); + precache_model("models/p_flashbang.mdl"); +#endif +} + +void +w_flashbang_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, -1, pl.ammo_fbgrenade, pl.mode_temp); +} + +int +w_flashbang_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (pl.ammo_fbgrenade < AMMO_MAX_FLASHBANG) { + pl.ammo_fbgrenade = bound(0, pl.ammo_fbgrenade + 1, AMMO_MAX_FLASHBANG); + } else { + return FALSE; + } +#endif + return TRUE; +} + +string +w_flashbang_wmodel(void) +{ + return "models/w_flashbang.mdl"; +} + +string +w_flashbang_pmodel(void) +{ + return "models/p_flashbang.mdl"; +} + +string +w_flashbang_deathmsg(void) +{ + return ""; +} + +void +w_flashbang_draw(void) +{ + player pl = (player)self; + Weapons_SetModel("models/v_flashbang.mdl"); + Weapons_ViewAnimation(FLASHBANG_DRAW); + pl.mode_temp = 0; +} + +#ifdef SERVER +void w_flashbang_throw(void) +{ + static void flashbang_explode(void) + { + FX_Flashbang(self.origin); + Sound_Play(self, CHAN_BODY, "weapon_flashbang.explode"); + remove(self); + } + + static void flashbang_touch(void) + { + if (other.takedamage == DAMAGE_YES) { + Damage_Apply(other, self.owner, 15, WEAPON_FLASHBANG, DMG_BLUNT); + } else { + Sound_Play(self, CHAN_BODY, "weapon_flashbang.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 = flashbang_explode; + eGrenade.touch = flashbang_touch; + eGrenade.nextthink = time + 4.0f; + setmodel(eGrenade, "models/w_flashbang.mdl"); + setsize(eGrenade, [0,0,0], [0,0,0]); + setorigin(eGrenade, vecSrc); +} +#endif + +void +w_flashbang_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_temp > 0) { + return; + } + + /* Ammo check */ + if (pl.ammo_fbgrenade <= 0) { + return; + } + + Weapons_ViewAnimation(FLASHBANG_PULLPIN); + + pl.mode_temp = 1; + pl.w_attack_next = 0.975f; + pl.w_idle_next = pl.w_attack_next; +} + +void +w_flashbang_release(void) +{ + player pl = (player)self; + + if (pl.w_idle_next > 0.0) { + return; + } + + if (pl.mode_temp == 1) { + pl.ammo_fbgrenade--; +#ifdef CLIENT + Weapons_ViewAnimation(FLASHBANG_THROW); +#else + w_flashbang_throw(); +#endif + pl.mode_temp = 2; + pl.w_attack_next = 1.0f; + pl.w_idle_next = 0.5f; + } else if (pl.mode_temp == 2) { +#ifdef CLIENT + Weapons_ViewAnimation(FLASHBANG_DRAW); +#else + if (!pl.ammo_fbgrenade) { + Weapons_RemoveItem(pl, WEAPON_FLASHBANG); + } +#endif + pl.w_attack_next = 0.5f; + pl.w_idle_next = 0.5f; + pl.mode_temp = 0; + } +} + +float +w_flashbang_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CROUCH_AIM_GRENADE : ANIM_AIM_GRENADE; +} + +void +w_flashbang_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/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_flashbang_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + + HUD_DrawAmmoBar(pos, pl.ammo_fbgrenade, AMMO_MAX_FLASHBANG, a); + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud6_spr, + [0,90/256], + [170/256,45/256], + g_hud_color, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud3_spr, + [0,90/256], + [170/256,45/256], + g_hud_color, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_flashbang = +{ + .name = "flashbang", + .id = ITEM_FLASHBANG, + .slot = 3, + .slot_pos = 1, + .allow_drop = FALSE, + .draw = w_flashbang_draw, + .holster = __NULL__, + .primary = w_flashbang_primary, + .secondary = __NULL__, + .reload = __NULL__, + .release = w_flashbang_release, + .crosshair = w_flashbang_hud, + .precache = w_flashbang_precache, + .pickup = w_flashbang_pickup, + .updateammo = w_flashbang_updateammo, + .wmodel = w_flashbang_wmodel, + .pmodel = w_flashbang_pmodel, + .deathmsg = w_flashbang_deathmsg, + .aimanim = w_flashbang_aimanim, + .hudpic = w_flashbang_hudpic +}; + +#ifdef SERVER +void +weapon_flashbang(void) +{ + Weapons_InitItem(WEAPON_FLASHBANG); +} +#endif diff --git a/src/shared/w_g3sg1.qc b/src/shared/w_g3sg1.qc new file mode 100644 index 0000000..a0688bc --- /dev/null +++ b/src/shared/w_g3sg1.qc @@ -0,0 +1,288 @@ +/* + * 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_g3sg1 (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_g3sg1.mdl" + +COUNTER-STRIKE (1999) ENTITY + +Heckler & Koch G3/SG-1 Weapon + +- Buy Menu - +Price: $5000 + +*/ + +enum +{ + G3SG1_IDLE, + G3SG1_SHOOT1, + G3SG1_SHOOT2, + G3SG1_RELOAD, + G3SG1_DRAW +}; + +void +w_g3sg1_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_g3sg1.fire"); + precache_model("models/w_g3sg1.mdl"); +#else + precache_model("models/v_g3sg1.mdl"); + precache_model("models/p_g3sg1.mdl"); +#endif +} + +void +w_g3sg1_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.g3sg1_mag, pl.ammo_762mm, -1); +} + +string +w_g3sg1_wmodel(void) +{ + return "models/w_g3sg1.mdl"; +} + +string +w_g3sg1_pmodel(void) +{ + return "models/p_g3sg1.mdl"; +} + +string +w_g3sg1_deathmsg(void) +{ + return ""; +} + +int +w_g3sg1_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + if (startammo == -1) + pl.g3sg1_mag = 20; + else + pl.g3sg1_mag = startammo; + } else { + if (pl.ammo_762mm < AMMO_MAX_762MM) { + pl.ammo_762mm = bound(0, pl.ammo_762mm + 20, AMMO_MAX_762MM); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_g3sg1_draw(void) +{ + player pl = (player)self; + Weapons_SetModel("models/v_g3sg1.mdl"); + Weapons_ViewAnimation(G3SG1_DRAW); + +#ifdef CLIENT + pl.cs_cross_mindist = 6; + pl.cs_cross_deltadist = 4; +#endif +} + +void +w_g3sg1_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (!pl.g3sg1_mag) { + return; + } + + Cstrike_ShotMultiplierAdd(pl, 1); + float accuracy = Cstrike_CalculateAccuracy(pl, 200); + pl.g3sg1_mag--; + +#ifdef CLIENT + View_SetMuzzleflash(MUZZLE_RIFLE); + + int r = (float)input_sequence % 2; + switch (r) { + case 0: + Weapons_ViewAnimation(SCOUT_SHOOT1); + break; + default: + Weapons_ViewAnimation(SCOUT_SHOOT2); + break; + } +#else + TraceAttack_SetPenetrationPower(2); + TraceAttack_FireBullets(1, pl.origin + pl.view_ofs, 80, [accuracy,accuracy], WEAPON_G3SG1); + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_SHOOT_RIFLE, 0.45f); + else + Animation_PlayerTopTemp(ANIM_CROUCH_SHOOT_RIFLE, 0.45f); + + Sound_Play(pl, CHAN_WEAPON, "weapon_g3sg1.fire"); +#endif + + pl.w_attack_next = 0.25f; + pl.w_idle_next = pl.w_attack_next; +} + +void +w_g3sg1_secondary(void) +{ + player pl = (player)self; + if (pl.w_attack_next) { + return; + } + /* Simple toggle of fovs */ + if (pl.viewzoom == 1.0f) { + pl.viewzoom = 0.45f; + } else if (pl.viewzoom == 0.45f) { + pl.viewzoom = 0.1f; + } else { + pl.viewzoom = 1.0f; + } + pl.w_attack_next = 0.5f; +} + +void +w_g3sg1_reload(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (pl.g3sg1_mag >= 20) { + return; + } + if (!pl.ammo_762mm) { + return; + } + +#ifdef CLIENT + Weapons_ViewAnimation(G3SG1_RELOAD); +#else + Weapons_ReloadWeapon(pl, player::g3sg1_mag, player::ammo_762mm, 20); + Weapons_UpdateAmmo(pl, pl.g3sg1_mag, pl.ammo_762mm, -1); +#endif + + pl.w_attack_next = 4.6f; + pl.w_idle_next = pl.w_attack_next; +} + +float +w_g3sg1_aimanim(void) +{ + return w_ak47_aimanim(); +} + +void +w_g3sg1_hud(void) +{ +#ifdef CLIENT + player pl = (player)self; + if (pl.viewzoom < 1.0f) { + Cstrike_DrawScope(); + } + 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/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_g3sg1_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.g3sg1_mag == 0 && pl.ammo_762mm == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + HUD_DrawAmmoBar(pos, pl.ammo_762mm, AMMO_MAX_762MM, a); + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud5_spr, + [0,180/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud2_spr, + [0,180/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_g3sg1 = +{ + .name = "g3sg1", + .id = ITEM_G3SG1, + .slot = 0, + .slot_pos = 13, + .allow_drop = TRUE, + .draw = w_g3sg1_draw, + .holster = __NULL__, + .primary = w_g3sg1_primary, + .secondary = w_g3sg1_secondary, + .reload = w_g3sg1_reload, + .release = w_cstrike_weaponrelease, + .crosshair = w_g3sg1_hud, + .precache = w_g3sg1_precache, + .pickup = w_g3sg1_pickup, + .updateammo = w_g3sg1_updateammo, + .wmodel = w_g3sg1_wmodel, + .pmodel = w_g3sg1_pmodel, + .deathmsg = w_g3sg1_deathmsg, + .aimanim = w_g3sg1_aimanim, + .hudpic = w_g3sg1_hudpic +}; + +#ifdef SERVER +void +weapon_g3sg1(void) +{ + Weapons_InitItem(WEAPON_G3SG1); +} +#endif diff --git a/src/shared/w_glock18.qc b/src/shared/w_glock18.qc new file mode 100644 index 0000000..07de738 --- /dev/null +++ b/src/shared/w_glock18.qc @@ -0,0 +1,336 @@ +/* + * 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_glock18 (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_glock18.mdl" + +COUNTER-STRIKE (1999) ENTITY + +Glock 18 Select Fire Weapon + +Default arsenal for Terrorists + +- Buy Menu - +Price: $400 + +*/ + +enum +{ + GLOCK_IDLE1, + GLOCK_IDLE2, + GLOCK_IDLE3, + GLOCK_SHOOT_BURST1, + GLOCK_SHOOT_BURST2, + GLOCK_SHOOT, + GLOCK_SHOOT_EMPTY, + GLOCK_RELOAD1, + GLOCK_DRAW1, + GLOCK_UNUSED1, + GLOCK_UNUSED2, + GLOCK_DRAW2, + GLOCK_RELOAD2 +}; + +void +w_glock18_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_glock18.fire"); + Sound_Precache("weapon_glock18.burstfire"); + precache_model("models/w_glock18.mdl"); +#else + precache_model("models/v_glock18.mdl"); + precache_model("models/p_glock18.mdl"); +#endif +} + +void +w_glock18_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.glock18_mag, pl.ammo_9mm, -1); +} + +string +w_glock18_wmodel(void) +{ + return "models/w_glock18.mdl"; +} + +string +w_glock18_pmodel(void) +{ + return "models/p_glock18.mdl"; +} + +string +w_glock18_deathmsg(void) +{ + return ""; +} + +int +w_glock18_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + if (startammo == -1) + pl.glock18_mag = 20; + else + pl.glock18_mag = startammo; + } else { + if (pl.ammo_9mm < AMMO_MAX_9MM) { + pl.ammo_9mm = bound(0, pl.ammo_9mm + 20, AMMO_MAX_9MM); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_glock18_draw(void) +{ + player pl = (player)self; + Weapons_SetModel("models/v_glock18.mdl"); + int r = (float)input_sequence % 2; + switch (r) { + case 0: + Weapons_ViewAnimation(GLOCK_DRAW1); + break; + default: + Weapons_ViewAnimation(GLOCK_DRAW2); + break; + } + +#ifdef CLIENT + pl.cs_cross_mindist = 8; + pl.cs_cross_deltadist = 3; +#endif +} + +void +w_glock18_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + + if (pl.gflags & GF_SEMI_TOGGLED) { + return; + } + if (!pl.glock18_mag) { + return; + } + + Cstrike_ShotMultiplierAdd(pl, 1); + float accuracy = Cstrike_CalculateAccuracy(pl, 200); + pl.glock18_mag--; + +#ifdef CLIENT + View_SetMuzzleflash(MUZZLE_RIFLE); +#else + TraceAttack_SetPenetrationPower(0); + TraceAttack_FireBullets(1, pl.origin + pl.view_ofs, 25, [accuracy,accuracy], WEAPON_GLOCK18); + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_SHOOT_ONEHAND, 0.45f); + else + Animation_PlayerTopTemp(ANIM_CROUCH_SHOOT_ONEHAND, 0.45f); + + if (pl.mode_glock18) { + Sound_Play(pl, CHAN_WEAPON, "weapon_glock18.burstfire"); + } else { + Sound_Play(pl, CHAN_WEAPON, "weapon_glock18.fire"); + } +#endif + + if (pl.mode_glock18) { + int r = (float)input_sequence % 2; + switch (r) { + case 0: + Weapons_ViewAnimation(GLOCK_SHOOT_BURST1); + break; + default: + Weapons_ViewAnimation(GLOCK_SHOOT_BURST2); + break; + } + pl.w_attack_next = 0.5f; + } else { + if (pl.glock18_mag <= 0) { + Weapons_ViewAnimation(GLOCK_SHOOT_EMPTY); + } else { + Weapons_ViewAnimation(GLOCK_SHOOT); + } + pl.w_attack_next = 0.15f; + } + pl.gflags |= GF_SEMI_TOGGLED; + pl.w_idle_next = pl.w_attack_next; +} + +void +w_glock18_secondary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0) { + return; + } + + /* toggle burst-fire */ + pl.mode_glock18 = 1 - pl.mode_glock18; + +#ifdef CLIENT + if (pl.mode_glock18) { + CSQC_Parse_CenterPrint("Switched to Burst-Fire mode"); + } else { + CSQC_Parse_CenterPrint("Switched to Semi-Automatic mode"); + } +#endif + + pl.w_attack_next = 1.0f; + pl.w_idle_next = pl.w_attack_next; +} + +void +w_glock18_reload(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (pl.glock18_mag >= 20) { + return; + } + if (!pl.ammo_9mm) { + return; + } + +#ifdef CLIENT + int r = (float)input_sequence % 2; + switch (r) { + case 0: + Weapons_ViewAnimation(GLOCK_RELOAD1); + break; + default: + Weapons_ViewAnimation(GLOCK_RELOAD2); + break; + } +#else + + Weapons_ReloadWeapon(pl, player::glock18_mag, player::ammo_9mm, 20); + Weapons_UpdateAmmo(pl, pl.glock18_mag, pl.ammo_9mm, -1); +#endif + + pl.w_attack_next = 2.1f; + pl.w_idle_next = pl.w_attack_next; +} + +float +w_glock18_aimanim(void) +{ + return w_deagle_aimanim(); +} + +void +w_glock18_hud(void) +{ +#ifdef CLIENT + Cstrike_DrawCrosshair(); + 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, [48/256,72/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_glock18_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.glock18_mag == 0 && pl.ammo_9mm == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + HUD_DrawAmmoBar(pos, pl.ammo_9mm, AMMO_MAX_9MM, a); + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud4_spr, + [0,45/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud1_spr, + [0,45/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_glock18 = +{ + .name = "glock18", + .id = ITEM_GLOCK18, + .slot = 1, + .slot_pos = 1, + .allow_drop = TRUE, + .draw = w_glock18_draw, + .holster = __NULL__, + .primary = w_glock18_primary, + .secondary = w_glock18_secondary, + .reload = w_glock18_reload, + .release = w_cstrike_weaponrelease, + .crosshair = w_glock18_hud, + .precache = w_glock18_precache, + .pickup = w_glock18_pickup, + .updateammo = w_glock18_updateammo, + .wmodel = w_glock18_wmodel, + .pmodel = w_glock18_pmodel, + .deathmsg = w_glock18_deathmsg, + .aimanim = w_glock18_aimanim, + .hudpic = w_glock18_hudpic +}; + +#ifdef SERVER +void +weapon_glock18(void) +{ + Weapons_InitItem(WEAPON_GLOCK18); +} +#endif diff --git a/src/shared/w_hegrenade.qc b/src/shared/w_hegrenade.qc new file mode 100644 index 0000000..3340a30 --- /dev/null +++ b/src/shared/w_hegrenade.qc @@ -0,0 +1,301 @@ +/* + * 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_hegrenade (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_hegrenade.mdl" + +COUNTER-STRIKE (1999) ENTITY + +HE (High Explosive) Grenade Weapon + +When thrown, explodes with a fairly deadly blast radius to players. + +- Buy Menu - +Price: $300 + +*/ + +enum +{ + HEGRENADE_IDLE, + HEGRENADE_PULLPIN, + HEGRENADE_THROW, + HEGRENADE_DRAW, +}; + +void +w_hegrenade_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_hegrenade.bounce"); + Sound_Precache("weapon_hegrenade.explode"); + precache_model("models/w_hegrenade.mdl"); +#else + precache_model("models/v_hegrenade.mdl"); + precache_model("models/p_hegrenade.mdl"); +#endif +} + +void +w_hegrenade_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, -1, pl.ammo_hegrenade, pl.mode_temp); +} + +int +w_hegrenade_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (pl.ammo_hegrenade < AMMO_MAX_HENADE) { + pl.ammo_hegrenade = bound(0, pl.ammo_hegrenade + 1, AMMO_MAX_HENADE); + } else { + return FALSE; + } +#endif + return TRUE; +} + +string +w_hegrenade_wmodel(void) +{ + return "models/w_hegrenade.mdl"; +} + +string +w_hegrenade_pmodel(void) +{ + return "models/p_hegrenade.mdl"; +} + +string +w_hegrenade_deathmsg(void) +{ + return ""; +} + +void +w_hegrenade_draw(void) +{ + Weapons_SetModel("models/v_hegrenade.mdl"); + Weapons_ViewAnimation(HEGRENADE_DRAW); + player pl = (player)self; + pl.mode_temp = 0; +} + +#ifdef SERVER +void w_hegrenade_throw(void) +{ + static void hegrenade_explode(void) + { + float dmg = 100; + FX_Explosion(self.origin); + Damage_Radius(self.origin, self.owner, dmg, dmg * 2.5f, TRUE, WEAPON_HEGRENADE); + Sound_Play(self, CHAN_BODY, "weapon_hegrenade.explode"); + remove(self); + } + + static void hegrenade_touch(void) + { + if (other.takedamage == DAMAGE_YES) { + Damage_Apply(other, self.owner, 15, WEAPON_HEGRENADE, DMG_BLUNT); + } else { + Sound_Play(self, CHAN_BODY, "weapon_hegrenade.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 = hegrenade_explode; + eGrenade.touch = hegrenade_touch; + eGrenade.nextthink = time + 4.0f; + setmodel(eGrenade, "models/w_hegrenade.mdl"); + setsize(eGrenade, [0,0,0], [0,0,0]); + setorigin(eGrenade, vecSrc); +} +#endif + +void +w_hegrenade_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_temp > 0) { + return; + } + + /* Ammo check */ +#ifdef CLIENT + if (pl.ammo_hegrenade <= 0) { + return; + } +#else + if (pl.ammo_hegrenade <= 0) { + return; + } +#endif + + Weapons_ViewAnimation(HEGRENADE_PULLPIN); + + pl.mode_temp = 1; + pl.w_attack_next = 0.975f; + pl.w_idle_next = pl.w_attack_next; +} + +void +w_hegrenade_release(void) +{ + player pl = (player)self; + + if (pl.w_idle_next > 0.0) { + return; + } + + if (pl.mode_temp == 1) { +#ifdef CLIENT + pl.ammo_hegrenade--; + Weapons_ViewAnimation(HEGRENADE_THROW); +#else + pl.ammo_hegrenade--; + w_hegrenade_throw(); +#endif + pl.mode_temp = 2; + pl.w_attack_next = 1.0f; + pl.w_idle_next = 0.5f; + } else if (pl.mode_temp == 2) { +#ifdef CLIENT + Weapons_ViewAnimation(HEGRENADE_DRAW); +#else + if (!pl.ammo_hegrenade) { + Weapons_RemoveItem(pl, WEAPON_HEGRENADE); + } +#endif + pl.w_attack_next = 0.5f; + pl.w_idle_next = 0.5f; + pl.mode_temp = 0; + } +} + +float +w_hegrenade_aimanim(void) +{ + return w_flashbang_aimanim(); +} + +void +w_hegrenade_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/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_hegrenade_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + + HUD_DrawAmmoBar(pos, pl.ammo_hegrenade, AMMO_MAX_HENADE, a); + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud6_spr, + [0,45/256], + [170/256,45/256], + g_hud_color, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud3_spr, + [0,45/256], + [170/256,45/256], + g_hud_color, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_hegrenade = +{ + .name = "hegrenade", + .id = ITEM_HEGRENADE, + .slot = 3, + .slot_pos = 0, + .allow_drop = FALSE, + .draw = w_hegrenade_draw, + .holster = __NULL__, + .primary = w_hegrenade_primary, + .secondary = __NULL__, + .reload = __NULL__, + .release = w_hegrenade_release, + .crosshair = w_hegrenade_hud, + .precache = w_hegrenade_precache, + .pickup = w_hegrenade_pickup, + .updateammo = w_hegrenade_updateammo, + .wmodel = w_hegrenade_wmodel, + .pmodel = w_hegrenade_pmodel, + .deathmsg = w_hegrenade_deathmsg, + .aimanim = w_hegrenade_aimanim, + .hudpic = w_hegrenade_hudpic +}; + +#ifdef SERVER +void +weapon_hegrenade(void) +{ + Weapons_InitItem(WEAPON_HEGRENADE); +} +#endif diff --git a/src/shared/w_knife.qc b/src/shared/w_knife.qc new file mode 100644 index 0000000..ad8547c --- /dev/null +++ b/src/shared/w_knife.qc @@ -0,0 +1,243 @@ +/* + * 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_knife (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_knife.mdl" + +COUNTER-STRIKE (1999) ENTITY + +Knife Weapon + +Default arsenal on both teams + +*/ + +enum +{ + KNIFE_IDLE1, + KNIFE_SLASH1, + KNIFE_SLASH2, + KNIFE_DRAW, + KNIFE_STAB, + KNIFE_STAB_MISS, + KNIFE_MIDSLASH1, + KNIFE_MIDSLASH2 +}; + +void +w_knife_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_knife.hit"); + Sound_Precache("weapon_knife.hitbody"); + Sound_Precache("weapon_knife.hithard"); + Sound_Precache("weapon_knife.miss"); + precache_model("models/w_knife.mdl"); +#else + precache_model("models/v_knife.mdl"); + precache_model("models/p_knife.mdl"); +#endif +} + +void +w_knife_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, -1, -1, -1); +} + +string +w_knife_wmodel(void) +{ + return "models/w_knife.mdl"; +} + +string +w_knife_pmodel(void) +{ + return "models/p_knife.mdl"; +} + +string +w_knife_deathmsg(void) +{ + return ""; +} + +void +w_knife_draw(void) +{ +#ifdef CLIENT + Weapons_SetModel("models/v_knife.mdl"); + Weapons_ViewAnimation(KNIFE_DRAW); +#endif +} + +void +w_knife_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + pl.w_attack_next = 0.7f; + +#ifdef CLIENT + int r = (float)input_sequence % 2; + switch (r) { + case 0: + Weapons_ViewAnimation(KNIFE_SLASH1); + break; + default: + Weapons_ViewAnimation(KNIFE_SLASH2); + break; + } +#else + vector src; + Weapons_MakeVectors(); + src = pl.origin + pl.view_ofs; + traceline(src, src + (v_forward * 32), FALSE, pl); + + Sound_Play(pl, CHAN_WEAPON, "weapon_knife.miss"); + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_CROUCH_SHOOT_KNIFE, 0.45f); + else + Animation_PlayerTopTemp(ANIM_SHOOT_KNIFE, 0.45f); + + if (trace_fraction >= 1.0) { + return; + } + + if (trace_ent.iBleeds) { + FX_Blood(trace_endpos, [1,0,0]); + Sound_Play(pl, CHAN_WEAPON, "weapon_knife.hitbody"); + } else { + Sound_Play(pl, CHAN_WEAPON, "weapon_knife.hit"); + } + + if (trace_ent.takedamage) { + Damage_Apply(trace_ent, pl, 15, WEAPON_KNIFE, DMG_SLASH); + } +#endif +} + +void +w_knife_secondary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + pl.w_attack_next = 1.2f; + +#ifdef CLIENT + Weapons_ViewAnimation(KNIFE_STAB); +#else + + vector src; + Weapons_MakeVectors(); + src = pl.origin + pl.view_ofs; + traceline(src, src + (v_forward * 32), FALSE, pl); + + Sound_Play(pl, CHAN_WEAPON, "weapon_knife.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]); + Sound_Play(pl, CHAN_WEAPON, "weapon_knife.hitbody"); + } else { + Sound_Play(pl, CHAN_WEAPON, "weapon_knife.hit"); + } + + if (trace_ent.takedamage) { + Damage_Apply(trace_ent, pl, 65, WEAPON_KNIFE, DMG_SLASH); + } +#endif +} + +float +w_knife_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CROUCH_AIM_KNIFE : ANIM_AIM_KNIFE; +} + +void +w_knife_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud11_spr, + [0,135/256], + [170/256,45/256], + g_hud_color, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud10_spr, + [0,135/256], + [170/256,45/256], + g_hud_color, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_knife = +{ + .name = "knife", + .id = ITEM_KNIFE, + .slot = 2, + .slot_pos = 0, + .allow_drop = FALSE, + .draw = w_knife_draw, + .holster = __NULL__, + .primary = w_knife_primary, + .secondary = w_knife_secondary, + .reload = __NULL__, + .release = __NULL__, + .crosshair = __NULL__, + .precache = w_knife_precache, + .pickup = __NULL__, + .updateammo = w_knife_updateammo, + .wmodel = w_knife_wmodel, + .pmodel = w_knife_pmodel, + .deathmsg = w_knife_deathmsg, + .aimanim = w_knife_aimanim, + .hudpic = w_knife_hudpic +}; + +#ifdef SERVER +void +weapon_knife(void) +{ + Weapons_InitItem(WEAPON_KNIFE); +} +#endif diff --git a/src/shared/w_m3.qc b/src/shared/w_m3.qc new file mode 100644 index 0000000..f728555 --- /dev/null +++ b/src/shared/w_m3.qc @@ -0,0 +1,328 @@ +/* + * 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_m3 (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_m3.mdl" + +COUNTER-STRIKE (1999) ENTITY + +Benneli M3 Super90 Weapon + +- Buy Menu - +Price: $1700 + +*/ + +enum +{ + M3_IDLE, + M3_SHOOT1, + M3_SHOOT2, + M3_INSERT, + M3_RELOAD_END, + M3_RELOAD_START, + M3_DRAW +}; + +enum +{ + M3S_IDLE, + M3S_RELOAD_START, + M3S_RELOAD, + M3S_RELOAD_END +}; + +void +w_m3_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_m3.fire"); + precache_model("models/w_m3.mdl"); +#else + precache_model("models/v_m3.mdl"); + precache_model("models/p_m3.mdl"); +#endif +} + +void +w_m3_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.m3_mag, pl.ammo_buckshot, -1); +} + +string +w_m3_wmodel(void) +{ + return "models/w_m3.mdl"; +} + +string +w_m3_pmodel(void) +{ + return "models/p_m3.mdl"; +} + +string +w_m3_deathmsg(void) +{ + return ""; +} + +int +w_m3_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + if (startammo == -1) + pl.m3_mag = 8; + else + pl.m3_mag = startammo; + } else { + if (pl.ammo_buckshot < AMMO_MAX_BUCKSHOT) { + pl.ammo_buckshot = bound(0, pl.ammo_buckshot + 8, AMMO_MAX_BUCKSHOT); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_m3_draw(void) +{ + player pl = (player)self; + Weapons_SetModel("models/v_m3.mdl"); + Weapons_ViewAnimation(M3_DRAW); + pl.mode_temp = 0; + +#ifdef CLIENT + pl.cs_cross_mindist = 8; + pl.cs_cross_deltadist = 6; +#endif +} + +void +w_m3_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + +#ifdef CLIENT + if (!pl.m3_mag) { + return; + } +#else + if (!pl.m3_mag) { + return; + } +#endif + + Cstrike_ShotMultiplierAdd(pl, 9); + float accuracy = Cstrike_CalculateAccuracy(pl, 200); + +#ifdef CLIENT + pl.m3_mag--; + View_SetMuzzleflash(MUZZLE_RIFLE); +#else + TraceAttack_SetPenetrationPower(0); + TraceAttack_FireBullets(9, pl.origin + pl.view_ofs, 26, [accuracy,accuracy], WEAPON_M3); + pl.m3_mag--; + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_SHOOT_SHOTGUN, 0.45f); + else + Animation_PlayerTopTemp(ANIM_CROUCH_SHOOT_SHOTGUN, 0.45f); + + Sound_Play(pl, CHAN_WEAPON, "weapon_m3.fire"); +#endif + + int r = (float)input_sequence % 2; + switch (r) { + case 0: + Weapons_ViewAnimation(M3_SHOOT1); + break; + default: + Weapons_ViewAnimation(M3_SHOOT2); + break; + } + + pl.w_attack_next = 1.0f; + pl.w_idle_next = pl.w_attack_next; +} + +void +w_m3_reload(void) +{ + player pl = (player)self; +#ifdef CLIENT + if (pl.m3_mag >= 8) { + return; + } + if (pl.ammo_buckshot <= 0) { + return; + } +#else + if (pl.m3_mag >= 8) { + return; + } + if (pl.ammo_buckshot <= 0) { + return; + } +#endif + + if (pl.mode_temp > M3S_IDLE) { + return; + } + pl.mode_temp = M3S_RELOAD_START; + pl.w_idle_next = 0.0f; +} + +void +w_m3_release(void) +{ + player pl = (player)self; + + w_cstrike_weaponrelease(); + + if (pl.w_idle_next > 0.0) { + return; + } + + if (pl.mode_temp == M3S_RELOAD_START) { + Weapons_ViewAnimation(M3_RELOAD_START); + pl.mode_temp = M3S_RELOAD; + pl.w_idle_next = 0.65f; + } else if (pl.mode_temp == M3S_RELOAD) { + Weapons_ViewAnimation(M3_INSERT); +#ifdef CLIENT + pl.m3_mag++; + pl.ammo_buckshot--; + + if (pl.ammo_buckshot <= 0 || pl.m3_mag >= 8) { + pl.mode_temp = M3S_RELOAD_END; + } +#else + pl.m3_mag++; + pl.ammo_buckshot--; + w_m3_updateammo(pl); + if (pl.ammo_buckshot <= 0 || pl.m3_mag >= 8) { + pl.mode_temp = M3S_RELOAD_END; + } +#endif + pl.w_idle_next = 0.5f; + } else if (pl.mode_temp == M3S_RELOAD_END) { + Weapons_ViewAnimation(M3_RELOAD_END); + pl.mode_temp = M3S_IDLE; + pl.w_idle_next = 10.0f; + pl.w_attack_next = 0.5f; + } +} + +float +w_m3_aimanim(void) +{ + return w_ak47_aimanim(); +} + +void +w_m3_hud(void) +{ +#ifdef CLIENT + Cstrike_DrawCrosshair(); + 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, [0,72/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_m3_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.m3_mag == 0 && pl.ammo_buckshot == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + HUD_DrawAmmoBar(pos, pl.ammo_buckshot, AMMO_MAX_BUCKSHOT, a); + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud4_spr, + [0,135/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud1_spr, + [0,135/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_m3 = +{ + .name = "m3", + .id = ITEM_M3, + .slot = 0, + .slot_pos = 0, + .allow_drop = TRUE, + .draw = w_m3_draw, + .holster = __NULL__, + .primary = w_m3_primary, + .secondary = __NULL__, + .reload = w_m3_reload, + .release = w_m3_release, + .crosshair = w_m3_hud, + .precache = w_m3_precache, + .pickup = w_m3_pickup, + .updateammo = w_m3_updateammo, + .wmodel = w_m3_wmodel, + .pmodel = w_m3_pmodel, + .deathmsg = w_m3_deathmsg, + .aimanim = w_m3_aimanim, + .hudpic = w_m3_hudpic +}; + +#ifdef SERVER +void +weapon_m3(void) +{ + Weapons_InitItem(WEAPON_M3); +} +#endif diff --git a/src/shared/w_m4a1.qc b/src/shared/w_m4a1.qc new file mode 100644 index 0000000..eba8354 --- /dev/null +++ b/src/shared/w_m4a1.qc @@ -0,0 +1,357 @@ +/* + * 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_m4a1 (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_m4a1.mdl" + +COUNTER-STRIKE (1999) ENTITY + +Colt M4A1 Carbine Weapon + +- Buy Menu - +Price: $3100 +Counter-Terrorists only weapon + +*/ + +enum +{ + M4A1_IDLE, + M4A1_SHOOT1, + M4A1_SHOOT2, + M4A1_SHOOT3, + M4A1_RELOAD, + M4A1_DRAW, + M4A1_ADDSIL, + M4A1_IDLEUNSIL, + M4A1_SHOOT1UNSIL, + M4A1_SHOOT2UNSIL, + M4A1_SHOOT3UNSIL, + M4A1_RELOADUNSIL, + M4A1_DRAWUNSIL, + M4A1_DETACHSIL +}; + +void +w_m4a1_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_m4a1.fire"); + Sound_Precache("weapon_m4a1.silenced"); + precache_model("models/w_m4a1.mdl"); +#else + precache_model("models/v_m4a1.mdl"); + precache_model("models/p_m4a1.mdl"); +#endif +} + +void +w_m4a1_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.m4a1_mag, pl.ammo_556mm, -1); +} + +string +w_m4a1_wmodel(void) +{ + return "models/w_m4a1.mdl"; +} + +string +w_m4a1_pmodel(void) +{ + return "models/p_m4a1.mdl"; +} + +string +w_m4a1_deathmsg(void) +{ + return ""; +} + +int +w_m4a1_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + if (startammo == -1) + pl.m4a1_mag = 30; + else + pl.m4a1_mag = startammo; + } else { + if (pl.ammo_556mm < AMMO_MAX_556MM) { + pl.ammo_556mm = bound(0, pl.ammo_556mm + 30, AMMO_MAX_556MM); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_m4a1_draw(void) +{ + player pl = (player)self; + + Weapons_SetModel("models/v_m4a1.mdl"); + + if (pl.mode_m4a1 == 1) { + Weapons_ViewAnimation(M4A1_DRAW); + } else { + Weapons_ViewAnimation(M4A1_DRAWUNSIL); + } + +#ifdef CLIENT + pl.cs_cross_mindist = 4; + pl.cs_cross_deltadist = 3; +#endif +} + +void +w_m4a1_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (!pl.m4a1_mag) { + return; + } + + Cstrike_ShotMultiplierAdd(pl, 1); + float accuracy = Cstrike_CalculateAccuracy(pl, 220); + pl.m4a1_mag--; + + /* actual firing */ +#ifdef CLIENT + if (pl.mode_m4a1 == 1) { + View_SetMuzzleflash(0); + } else { + View_SetMuzzleflash(MUZZLE_RIFLE); + } + + /* this stuff is predicted */ + int r = (float)input_sequence % 3; + if (pl.mode_m4a1 == 1) { + switch (r) { + case 0: + Weapons_ViewAnimation(M4A1_SHOOT1); + break; + case 1: + Weapons_ViewAnimation(M4A1_SHOOT2); + break; + default: + Weapons_ViewAnimation(M4A1_SHOOT3); + break; + } + } else { + switch (r) { + case 0: + Weapons_ViewAnimation(M4A1_SHOOT1UNSIL); + break; + case 1: + Weapons_ViewAnimation(M4A1_SHOOT2UNSIL); + break; + default: + Weapons_ViewAnimation(M4A1_SHOOT3UNSIL); + break; + } + } +#else + /* Different sounds without silencer */ + if (pl.mode_m4a1 == 1) { + Sound_Play(pl, CHAN_WEAPON, "weapon_m4a1.silenced"); + } else { + Sound_Play(pl, CHAN_WEAPON, "weapon_m4a1.fire"); + } + + TraceAttack_SetPenetrationPower(1); + TraceAttack_FireBullets(1, pl.origin + pl.view_ofs, 33, [accuracy,accuracy], WEAPON_M4A1); + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_SHOOT_RIFLE, 0.45f); + else + Animation_PlayerTopTemp(ANIM_CROUCH_SHOOT_RIFLE, 0.45f); +#endif + + pl.w_attack_next = 0.0875f; + pl.w_idle_next = 2.0f; +} + +void +w_m4a1_secondary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0) { + return; + } + + /* toggle silencer */ + pl.mode_m4a1 = 1 - pl.mode_m4a1; + + /* play the animation */ + if (pl.mode_m4a1) { + Weapons_ViewAnimation(M4A1_ADDSIL); + } else { + Weapons_ViewAnimation(M4A1_DETACHSIL); + } + + pl.w_attack_next = 2.0f; + pl.w_idle_next = pl.w_attack_next; +} + +void +w_m4a1_reload(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (pl.m4a1_mag >= 30) { + return; + } + if (!pl.ammo_556mm) { + return; + } + +#ifdef CLIENT + if (pl.mode_m4a1 == 1) { + Weapons_ViewAnimation(M4A1_RELOAD); + } else { + Weapons_ViewAnimation(M4A1_RELOADUNSIL); + } +#else + Weapons_ReloadWeapon(pl, player::m4a1_mag, player::ammo_556mm, 30); +#endif + + pl.w_attack_next = 3.1f; + pl.w_idle_next = pl.w_attack_next; +} + +float +w_m4a1_aimanim(void) +{ + return w_ak47_aimanim(); +} + +void +w_m4a1_hud(void) +{ +#ifdef CLIENT + Cstrike_DrawCrosshair(); + 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, [0,96/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_m4a1_release(void) +{ + player pl = (player)self; + + w_cstrike_weaponrelease(); + + if (pl.w_idle_next > 0.0) { + return; + } + + if (pl.mode_m4a1) { + Weapons_ViewAnimation(M4A1_IDLE); + } else { + Weapons_ViewAnimation(M4A1_IDLEUNSIL); + } + pl.w_idle_next = 5.0f; +} + +void +w_m4a1_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.m4a1_mag == 0 && pl.ammo_556mm == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + HUD_DrawAmmoBar(pos, pl.ammo_556mm, AMMO_MAX_556MM, a); + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud5_spr, + [0,45/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud2_spr, + [0,45/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_m4a1 = +{ + .name = "m4a1", + .id = ITEM_M4A1, + .slot = 0, + .slot_pos = 9, + .allow_drop = TRUE, + .draw = w_m4a1_draw, + .holster = __NULL__, + .primary = w_m4a1_primary, + .secondary = w_m4a1_secondary, + .reload = w_m4a1_reload, + .release = w_m4a1_release, + .crosshair = w_m4a1_hud, + .precache = w_m4a1_precache, + .pickup = w_m4a1_pickup, + .updateammo = w_m4a1_updateammo, + .wmodel = w_m4a1_wmodel, + .pmodel = w_m4a1_pmodel, + .deathmsg = w_m4a1_deathmsg, + .aimanim = w_m4a1_aimanim, + .hudpic = w_m4a1_hudpic +}; + +#ifdef SERVER +void +weapon_m4a1(void) +{ + Weapons_InitItem(WEAPON_M4A1); +} +#endif diff --git a/src/shared/w_mac10.qc b/src/shared/w_mac10.qc new file mode 100644 index 0000000..6c75472 --- /dev/null +++ b/src/shared/w_mac10.qc @@ -0,0 +1,271 @@ +/* + * 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_mac10 (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_mac10.mdl" + +COUNTER-STRIKE (1999) ENTITY + +Ingram MAC-10 Weapon + +- Buy Menu - +Price: $1400 + +*/ + +enum +{ + MAC10_IDLE, + MAC10_RELOAD, + MAC10_DRAW, + MAC10_SHOOT1, + MAC10_SHOOT2, + MAC10_SHOOT3 +}; + +void +w_mac10_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_mac10.fire"); + precache_model("models/w_mac10.mdl"); +#else + precache_model("models/v_mac10.mdl"); + precache_model("models/p_mac10.mdl"); +#endif +} + +void +w_mac10_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.mac10_mag, pl.ammo_45acp, -1); +} + +string +w_mac10_wmodel(void) +{ + return "models/w_mac10.mdl"; +} + +string +w_mac10_pmodel(void) +{ + return "models/p_mac10.mdl"; +} + +string +w_mac10_deathmsg(void) +{ + return ""; +} + +int +w_mac10_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + if (startammo == -1) + pl.mac10_mag = 30; + else + pl.mac10_mag = startammo; + } else { + if (pl.ammo_45acp < AMMO_MAX_45ACP) { + pl.ammo_45acp = bound(0, pl.ammo_45acp + 30, AMMO_MAX_45ACP); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_mac10_draw(void) +{ + player pl = (player)self; + Weapons_SetModel("models/v_mac10.mdl"); + Weapons_ViewAnimation(MAC10_DRAW); + +#ifdef CLIENT + pl.cs_cross_mindist = 9; + pl.cs_cross_deltadist = 3; +#endif +} + +void +w_mac10_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (!pl.mac10_mag) { + return; + } + + Cstrike_ShotMultiplierAdd(pl, 1); + float accuracy = Cstrike_CalculateAccuracy(pl, 200); + pl.mac10_mag--; + +#ifdef CLIENT + View_SetMuzzleflash(MUZZLE_RIFLE); + + int r = (float)input_sequence % 3; + switch (r) { + case 0: + Weapons_ViewAnimation(MAC10_SHOOT1); + break; + case 1: + Weapons_ViewAnimation(MAC10_SHOOT2); + break; + default: + Weapons_ViewAnimation(MAC10_SHOOT3); + break; + } +#else + TraceAttack_SetPenetrationPower(0); + TraceAttack_FireBullets(1, pl.origin + pl.view_ofs, 29, [accuracy,accuracy], WEAPON_MAC10); + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_SHOOT_MP5, 0.45f); + else + Animation_PlayerTopTemp(ANIM_CROUCH_SHOOT_MP5, 0.45f); + + Sound_Play(pl, CHAN_WEAPON, "weapon_mac10.fire"); +#endif + + pl.w_attack_next = 0.07f; + pl.w_idle_next = pl.w_attack_next; +} + +void +w_mac10_reload(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (pl.mac10_mag >= 30) { + return; + } + if (!pl.ammo_45acp) { + return; + } + +#ifdef CLIENT + Weapons_ViewAnimation(MAC10_RELOAD); +#else + Weapons_ReloadWeapon(pl, player::mac10_mag, player::ammo_45acp, 30); + Weapons_UpdateAmmo(pl, pl.mac10_mag, pl.ammo_45acp, -1); +#endif + + pl.w_attack_next = 3.2f; + pl.w_idle_next = pl.w_attack_next; +} + +float +w_mac10_aimanim(void) +{ + return w_deagle_aimanim(); +} + +void +w_mac10_hud(void) +{ +#ifdef CLIENT + Cstrike_DrawCrosshair(); + 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, [96/256,72/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_mac10_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.mac10_mag == 0 && pl.ammo_45acp == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + HUD_DrawAmmoBar(pos, pl.ammo_45acp, AMMO_MAX_45ACP, a); + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud15_spr, + [0,0], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud14_spr, + [0,0], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_mac10 = +{ + .name = "mac10", + .id = ITEM_MAC10, + .slot = 0, + .slot_pos = 5, + .allow_drop = TRUE, + .draw = w_mac10_draw, + .holster = __NULL__, + .primary = w_mac10_primary, + .secondary = __NULL__, + .reload = w_mac10_reload, + .release = w_cstrike_weaponrelease, + .crosshair = w_mac10_hud, + .precache = w_mac10_precache, + .pickup = w_mac10_pickup, + .updateammo = w_mac10_updateammo, + .wmodel = w_mac10_wmodel, + .pmodel = w_mac10_pmodel, + .deathmsg = w_mac10_deathmsg, + .aimanim = w_mac10_aimanim, + .hudpic = w_mac10_hudpic +}; + +#ifdef SERVER +void +weapon_mac10(void) +{ + Weapons_InitItem(WEAPON_MAC10); +} +#endif diff --git a/src/shared/w_mp5.qc b/src/shared/w_mp5.qc new file mode 100644 index 0000000..c585234 --- /dev/null +++ b/src/shared/w_mp5.qc @@ -0,0 +1,271 @@ +/* + * 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_mp5navy (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_mp5.mdl" + +COUNTER-STRIKE (1999) ENTITY + +Heckler & Koch MP5-Navy Weapon + +- Buy Menu - +Price: $1500 + +*/ + +enum +{ + MP5_IDLE, + MP5_RELOAD, + MP5_DRAW, + MP5_SHOOT1, + MP5_SHOOT2, + MP5_SHOOT3 +}; + +void +w_mp5_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_mp5.fire"); + precache_model("models/w_mp5.mdl"); +#else + precache_model("models/v_mp5.mdl"); + precache_model("models/p_mp5.mdl"); +#endif +} + +void +w_mp5_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.mp5_mag, pl.ammo_9mm, -1); +} + +string +w_mp5_wmodel(void) +{ + return "models/w_mp5.mdl"; +} + +string +w_mp5_pmodel(void) +{ + return "models/p_mp5.mdl"; +} + +string +w_mp5_deathmsg(void) +{ + return ""; +} + +int +w_mp5_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + if (startammo == -1) + pl.mp5_mag = 30; + else + pl.mp5_mag = startammo; + } else { + if (pl.ammo_9mm < AMMO_MAX_9MM) { + pl.ammo_9mm = bound(0, pl.ammo_9mm + 30, AMMO_MAX_9MM); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_mp5_draw(void) +{ + player pl = (player)self; + Weapons_SetModel("models/v_mp5.mdl"); + Weapons_ViewAnimation(MP5_DRAW); + +#ifdef CLIENT + pl.cs_cross_mindist = 5; + pl.cs_cross_deltadist = 2; +#endif +} + +void +w_mp5_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (!pl.mp5_mag) { + return; + } + + Cstrike_ShotMultiplierAdd(pl, 1); + float accuracy = Cstrike_CalculateAccuracy(pl, 220); + pl.mp5_mag--; + +#ifdef CLIENT + View_SetMuzzleflash(MUZZLE_RIFLE); + + int r = (float)input_sequence % 3; + switch (r) { + case 0: + Weapons_ViewAnimation(MP5_SHOOT1); + break; + case 1: + Weapons_ViewAnimation(MP5_SHOOT2); + break; + default: + Weapons_ViewAnimation(MP5_SHOOT3); + break; + } +#else + TraceAttack_SetPenetrationPower(0); + TraceAttack_FireBullets(1, pl.origin + pl.view_ofs, 26, [accuracy,accuracy], WEAPON_MP5); + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_SHOOT_MP5, 0.45f); + else + Animation_PlayerTopTemp(ANIM_CROUCH_SHOOT_MP5, 0.45f); + + Sound_Play(pl, CHAN_WEAPON, "weapon_mp5.fire"); +#endif + + pl.w_attack_next = 0.08f; + pl.w_idle_next = pl.w_attack_next; +} + +void +w_mp5_reload(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (pl.mp5_mag >= 30) { + return; + } + if (!pl.ammo_9mm) { + return; + } + +#ifdef CLIENT + Weapons_ViewAnimation(MP5_RELOAD); +#else + Weapons_ReloadWeapon(pl, player::mp5_mag, player::ammo_9mm, 30); + Weapons_UpdateAmmo(pl, pl.mp5_mag, pl.ammo_9mm, -1); +#endif + + pl.w_attack_next = 2.6f; + pl.w_idle_next = pl.w_attack_next; +} + +float +w_mp5_aimanim(void) +{ + return w_ak47_aimanim(); +} + +void +w_mp5_hud(void) +{ +#ifdef CLIENT + Cstrike_DrawCrosshair(); + 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, [48/256,72/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +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) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + HUD_DrawAmmoBar(pos, pl.ammo_9mm, AMMO_MAX_9MM, a); + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud4_spr, + [0,180/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud1_spr, + [0,180/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_mp5 = +{ + .name = "mp5navy", + .id = ITEM_MP5, + .slot = 0, + .slot_pos = 2, + .allow_drop = TRUE, + .draw = w_mp5_draw, + .holster = __NULL__, + .primary = w_mp5_primary, + .secondary = __NULL__, + .reload = w_mp5_reload, + .release = w_cstrike_weaponrelease, + .crosshair = w_mp5_hud, + .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_mp5navy(void) +{ + Weapons_InitItem(WEAPON_MP5); +} +#endif diff --git a/src/shared/w_p228.qc b/src/shared/w_p228.qc new file mode 100644 index 0000000..50ca274 --- /dev/null +++ b/src/shared/w_p228.qc @@ -0,0 +1,280 @@ +/* + * 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_ (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_p228.mdl" + +COUNTER-STRIKE (1999) ENTITY + +SIG P228 Weapon + +- Buy Menu - +Price: $600 + +*/ + +enum +{ + P228_IDLE, + P228_SHOOT1, + P228_SHOOT2, + P228_SHOOT3, + P228_SHOOT_EMPTY, + P228_RELOAD, + P228_DRAW +}; + +void +w_p228_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_p228.fire"); + precache_model("models/w_p228.mdl"); +#else + precache_model("models/v_p228.mdl"); + precache_model("models/p_p228.mdl"); +#endif +} + +void +w_p228_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.p228_mag, pl.ammo_357sig, -1); +} + +string +w_p228_wmodel(void) +{ + return "models/w_p228.mdl"; +} + +string +w_p228_pmodel(void) +{ + return "models/p_p228.mdl"; +} + +string +w_p228_deathmsg(void) +{ + return ""; +} + +int +w_p228_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + if (startammo == -1) + pl.p228_mag = 13; + else + pl.p228_mag = startammo; + } else { + if (pl.ammo_357sig < AMMO_MAX_357SIG) { + pl.ammo_357sig = bound(0, pl.ammo_357sig + 13, AMMO_MAX_357SIG); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_p228_draw(void) +{ + player pl = (player)self; + Weapons_SetModel("models/v_p228.mdl"); + Weapons_ViewAnimation(P228_DRAW); + +#ifdef CLIENT + pl.cs_cross_mindist = 8; + pl.cs_cross_deltadist = 3; +#endif +} + +void +w_p228_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (pl.gflags & GF_SEMI_TOGGLED) { + return; + } + if (!pl.p228_mag) { + return; + } + + Cstrike_ShotMultiplierAdd(pl, 1); + float accuracy = Cstrike_CalculateAccuracy(pl, 200); + pl.p228_mag--; + +#ifdef CLIENT + View_SetMuzzleflash(MUZZLE_RIFLE); + + if (pl.p228_mag <= 0) { + Weapons_ViewAnimation(P228_SHOOT_EMPTY); + } else { + int r = (float)input_sequence % 3; + switch (r) { + case 0: + Weapons_ViewAnimation(P228_SHOOT1); + break; + case 1: + Weapons_ViewAnimation(P228_SHOOT2); + break; + default: + Weapons_ViewAnimation(P228_SHOOT3); + break; + } + } +#else + TraceAttack_SetPenetrationPower(0); + TraceAttack_FireBullets(1, pl.origin + pl.view_ofs, 40, [accuracy,accuracy], WEAPON_P228); + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_SHOOT_ONEHAND, 0.45f); + else + Animation_PlayerTopTemp(ANIM_CROUCH_SHOOT_ONEHAND, 0.45f); + + Sound_Play(pl, CHAN_WEAPON, "weapon_p228.fire"); +#endif + + pl.gflags |= GF_SEMI_TOGGLED; + pl.w_attack_next = 0.15f; + pl.w_idle_next = pl.w_attack_next; +} + +void +w_p228_reload(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (pl.p228_mag >= 30) { + return; + } + if (!pl.ammo_357sig) { + return; + } + +#ifdef CLIENT + Weapons_ViewAnimation(P228_RELOAD); +#else + Weapons_ReloadWeapon(pl, player::p228_mag, player::ammo_357sig, 13); + Weapons_UpdateAmmo(pl, pl.p228_mag, pl.ammo_357sig, -1); +#endif + + pl.w_attack_next = 2.7f; + pl.w_idle_next = pl.w_attack_next; +} + +float +w_p228_aimanim(void) +{ + return w_deagle_aimanim(); +} + +void +w_p228_hud(void) +{ +#ifdef CLIENT + Cstrike_DrawCrosshair(); + 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, [120/256,72/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_p228_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.p228_mag == 0 && pl.ammo_357sig == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + HUD_DrawAmmoBar(pos, pl.ammo_357sig, AMMO_MAX_357SIG, a); + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud13_spr, + [0,90/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud12_spr, + [0,90/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_p228 = +{ + .name = "p228", + .id = ITEM_P228, + .slot = 1, + .slot_pos = 3, + .allow_drop = TRUE, + .draw = w_p228_draw, + .holster = __NULL__, + .primary = w_p228_primary, + .secondary = __NULL__, + .reload = w_p228_reload, + .release = w_cstrike_weaponrelease, + .crosshair = w_p228_hud, + .precache = w_p228_precache, + .pickup = w_p228_pickup, + .updateammo = w_p228_updateammo, + .wmodel = w_p228_wmodel, + .pmodel = w_p228_pmodel, + .deathmsg = w_p228_deathmsg, + .aimanim = w_p228_aimanim, + .hudpic = w_p228_hudpic +}; + +#ifdef SERVER +void +weapon_p228(void) +{ + Weapons_InitItem(WEAPON_P228); +} +#endif diff --git a/src/shared/w_p90.qc b/src/shared/w_p90.qc new file mode 100644 index 0000000..99bd687 --- /dev/null +++ b/src/shared/w_p90.qc @@ -0,0 +1,271 @@ +/* + * 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_p90 (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_p90.mdl" + +COUNTER-STRIKE (1999) ENTITY + +FN P90 Weapon + +- Buy Menu - +Price: $2350 + +*/ + +enum +{ + P90_IDLE, + P90_RELOAD, + P90_DRAW, + P90_SHOOT1, + P90_SHOOT2, + P90_SHOOT3 +}; + +void +w_p90_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_p90.fire"); + precache_model("models/w_p90.mdl"); +#else + precache_model("models/v_p90.mdl"); + precache_model("models/p_p90.mdl"); +#endif +} + +void +w_p90_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.p90_mag, pl.ammo_57mm, -1); +} + +string +w_p90_wmodel(void) +{ + return "models/w_p90.mdl"; +} + +string +w_p90_pmodel(void) +{ + return "models/p_p90.mdl"; +} + +string +w_p90_deathmsg(void) +{ + return ""; +} + +int +w_p90_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + if (startammo == -1) + pl.p90_mag = 50; + else + pl.p90_mag = startammo; + } else { + if (pl.ammo_57mm < AMMO_MAX_57MM) { + pl.ammo_57mm = bound(0, pl.ammo_57mm + 50, AMMO_MAX_57MM); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_p90_draw(void) +{ + player pl = (player)self; + Weapons_SetModel("models/v_p90.mdl"); + Weapons_ViewAnimation(P90_DRAW); + +#ifdef CLIENT + pl.cs_cross_mindist = 7; + pl.cs_cross_deltadist = 3; +#endif +} + +void +w_p90_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (!pl.p90_mag) { + return; + } + + Cstrike_ShotMultiplierAdd(pl, 1); + float accuracy = Cstrike_CalculateAccuracy(pl, 175); + pl.p90_mag--; + +#ifdef CLIENT + View_SetMuzzleflash(MUZZLE_RIFLE); + + int r = (float)input_sequence % 3; + switch (r) { + case 0: + Weapons_ViewAnimation(P90_SHOOT1); + break; + case 1: + Weapons_ViewAnimation(P90_SHOOT2); + break; + default: + Weapons_ViewAnimation(P90_SHOOT3); + break; + } +#else + TraceAttack_SetPenetrationPower(0); + TraceAttack_FireBullets(1, pl.origin + pl.view_ofs, 26, [accuracy,accuracy], WEAPON_P90); + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_SHOOT_MP5, 0.45f); + else + Animation_PlayerTopTemp(ANIM_CROUCH_SHOOT_MP5, 0.45f); + + Sound_Play(pl, CHAN_WEAPON, "weapon_p90.fire"); +#endif + + pl.w_attack_next = 0.07f; + pl.w_idle_next = pl.w_attack_next; +} + +void +w_p90_reload(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (pl.p90_mag >= 50) { + return; + } + if (!pl.ammo_57mm) { + return; + } + +#ifdef CLIENT + Weapons_ViewAnimation(P90_RELOAD); +#else + Weapons_ReloadWeapon(pl, player::p90_mag, player::ammo_57mm, 50); + Weapons_UpdateAmmo(pl, pl.p90_mag, pl.ammo_57mm, -1); +#endif + + pl.w_attack_next = 3.3f; + pl.w_idle_next = pl.w_attack_next; +} + +float +w_p90_aimanim(void) +{ + return w_ak47_aimanim(); +} + +void +w_p90_hud(void) +{ +#ifdef CLIENT + Cstrike_DrawCrosshair(); + 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, [120/256,96/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_p90_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.p90_mag == 0 && pl.ammo_57mm == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + HUD_DrawAmmoBar(pos, pl.ammo_57mm, AMMO_MAX_57MM, a); + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud13_spr, + [0,0], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud12_spr, + [0,0], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_p90 = +{ + .name = "p90", + .id = ITEM_P90, + .slot = 0, + .slot_pos = 3, + .allow_drop = TRUE, + .draw = w_p90_draw, + .holster = __NULL__, + .primary = w_p90_primary, + .secondary = __NULL__, + .reload = w_p90_reload, + .release = w_cstrike_weaponrelease, + .crosshair = w_p90_hud, + .precache = w_p90_precache, + .pickup = w_p90_pickup, + .updateammo = w_p90_updateammo, + .wmodel = w_p90_wmodel, + .pmodel = w_p90_pmodel, + .deathmsg = w_p90_deathmsg, + .aimanim = w_p90_aimanim, + .hudpic = w_p90_hudpic +}; + +#ifdef SERVER +void +weapon_p90(void) +{ + Weapons_InitItem(WEAPON_P90); +} +#endif diff --git a/src/shared/w_para.qc b/src/shared/w_para.qc new file mode 100644 index 0000000..b943594 --- /dev/null +++ b/src/shared/w_para.qc @@ -0,0 +1,267 @@ +/* + * 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_m249 (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_m249.mdl" + +COUNTER-STRIKE (1999) ENTITY + +FN M249 Para Weapon + +- Buy Menu - +Price: $5750 + +*/ + +enum +{ + PARA_IDLE, + PARA_SHOOT1, + PARA_SHOOT2, + PARA_RELOAD, + PARA_DRAW +}; + +void +w_para_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_para.fire"); + precache_model("models/w_m249.mdl"); +#else + precache_model("models/v_m249.mdl"); + precache_model("models/p_m249.mdl"); +#endif +} + +void +w_para_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.para_mag, pl.ammo_556mmbox, -1); +} + +string +w_para_wmodel(void) +{ + return "models/w_m249.mdl"; +} + +string +w_para_pmodel(void) +{ + return "models/p_m249.mdl"; +} + +string +w_para_deathmsg(void) +{ + return ""; +} + +int +w_para_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + if (startammo == -1) + pl.para_mag = 100; + else + pl.para_mag = startammo; + } else { + if (pl.ammo_556mmbox < AMMO_MAX_556MMBOX) { + pl.ammo_556mmbox = bound(0, pl.ammo_556mmbox + 100, AMMO_MAX_556MMBOX); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_para_draw(void) +{ + player pl = (player)self; + Weapons_SetModel("models/v_m249.mdl"); + Weapons_ViewAnimation(PARA_DRAW); + +#ifdef CLIENT + pl.cs_cross_mindist = 6; + pl.cs_cross_deltadist = 3; +#endif +} + +void +w_para_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (!pl.para_mag) { + return; + } + + Cstrike_ShotMultiplierAdd(pl, 1); + float accuracy = Cstrike_CalculateAccuracy(pl, 175); + pl.para_mag--; + +#ifdef CLIENT + View_SetMuzzleflash(MUZZLE_RIFLE); + + int r = (float)input_sequence % 2; + switch (r) { + case 0: + Weapons_ViewAnimation(SCOUT_SHOOT1); + break; + default: + Weapons_ViewAnimation(SCOUT_SHOOT2); + break; + } +#else + TraceAttack_SetPenetrationPower(1); + TraceAttack_FireBullets(1, pl.origin + pl.view_ofs, 35, [accuracy,accuracy], WEAPON_PARA); + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_CROUCH_SHOOT_PARA, 0.45f); + else + Animation_PlayerTopTemp(ANIM_SHOOT_PARA, 0.45f); + + Sound_Play(pl, CHAN_WEAPON, "weapon_para.fire"); +#endif + + pl.w_attack_next = 0.1f; + pl.w_idle_next = pl.w_attack_next; +} + +void +w_para_reload(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (pl.para_mag >= 100) { + return; + } + if (!pl.ammo_556mmbox) { + return; + } + +#ifdef CLIENT + Weapons_ViewAnimation(PARA_RELOAD); +#else + Weapons_ReloadWeapon(pl, player::para_mag, player::ammo_556mmbox, 100); + Weapons_UpdateAmmo(pl, pl.para_mag, pl.ammo_556mmbox, -1); +#endif + + pl.w_attack_next = 3.0f; + pl.w_idle_next = pl.w_attack_next; +} + +float +w_para_aimanim(void) +{ + return w_ak47_aimanim(); +} + +void +w_para_hud(void) +{ +#ifdef CLIENT + Cstrike_DrawCrosshair(); + 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, [0,96/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_para_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.para_mag == 0 && pl.ammo_556mmbox == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + HUD_DrawAmmoBar(pos, pl.ammo_556mmbox, AMMO_MAX_556MMBOX, a); + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud6_spr, + [0,0], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud3_spr, + [0,0], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_para = +{ + .name = "m249", + .id = ITEM_PARA, + .slot = 0, + .slot_pos = 15, + .allow_drop = TRUE, + .draw = w_para_draw, + .holster = __NULL__, + .primary = w_para_primary, + .secondary = __NULL__, + .reload = w_para_reload, + .release = w_cstrike_weaponrelease, + .crosshair = w_para_hud, + .precache = w_para_precache, + .pickup = w_para_pickup, + .updateammo = w_para_updateammo, + .wmodel = w_para_wmodel, + .pmodel = w_para_pmodel, + .deathmsg = w_para_deathmsg, + .aimanim = w_para_aimanim, + .hudpic = w_para_hudpic +}; + +#ifdef SERVER +void +weapon_m249(void) +{ + Weapons_InitItem(WEAPON_PARA); +} +#endif diff --git a/src/shared/w_scout.qc b/src/shared/w_scout.qc new file mode 100644 index 0000000..e98e69d --- /dev/null +++ b/src/shared/w_scout.qc @@ -0,0 +1,320 @@ +/* + * 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_scout (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_scout.mdl" + +COUNTER-STRIKE (1999) ENTITY + +Steyr Scout Weapon + +- Buy Menu - +Price: $1250 + +*/ + +enum +{ + SCOUT_IDLE, + SCOUT_SHOOT1, + SCOUT_SHOOT2, + SCOUT_RELOAD, + SCOUT_DRAW +}; + +void +w_scout_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_scout.fire"); + precache_model("models/w_scout.mdl"); +#else + precache_model("models/v_scout.mdl"); + precache_model("models/p_scout.mdl"); +#endif +} + +void +w_scout_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.scout_mag, pl.ammo_762mm, -1); +} + +string +w_scout_wmodel(void) +{ + return "models/w_scout.mdl"; +} + +string +w_scout_pmodel(void) +{ + return "models/p_scout.mdl"; +} + +string +w_scout_deathmsg(void) +{ + return ""; +} + +int +w_scout_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + if (startammo == -1) + pl.scout_mag = 10; + else + pl.scout_mag = startammo; + } else { + if (pl.ammo_762mm < AMMO_MAX_762MM) { + pl.ammo_762mm = bound(0, pl.ammo_762mm + 10, AMMO_MAX_762MM); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_scout_draw(void) +{ + player pl = (player)self; + Weapons_SetModel("models/v_scout.mdl"); + Weapons_ViewAnimation(SCOUT_DRAW); + pl.mode_temp = 0; + +#ifdef CLIENT + pl.cs_cross_mindist = 5; + pl.cs_cross_deltadist = 3; +#endif +} + + +void +w_scout_release(void) +{ + player pl = (player)self; + + w_cstrike_weaponrelease(); + + if (pl.w_idle_next > 0.0f) { + pl.viewzoom = 1.0f; + return; + } + + if (pl.mode_temp == 1) { + pl.viewzoom = 0.45f; + } else if (pl.mode_temp == 2) { + pl.viewzoom = 0.1f; + } else { + pl.viewzoom = 1.0f; + } +} + +void +w_scout_secondary(void) +{ + player pl = (player)self; + if (pl.w_attack_next) { + return; + } + +#ifdef SSQC + Sound_Play(pl, CHAN_WEAPON, "weapon_awp.zoom"); +#endif + + /* Simple toggle of fovs */ + if (pl.mode_temp == 1) { + pl.mode_temp = 2; + } else if (pl.mode_temp == 2) { + pl.mode_temp = 0; + } else { + pl.mode_temp = 1; + } + + pl.w_attack_next = 0.3f; + pl.w_idle_next = 0.0f; + w_scout_release(); +} + +void +w_scout_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + w_scout_release(); + return; + } + if (!pl.scout_mag) { + return; + } + + Cstrike_ShotMultiplierAdd(pl, 1); + float accuracy = Cstrike_CalculateAccuracy(pl, 200); + pl.scout_mag--; + +#ifdef CLIENT + View_SetMuzzleflash(MUZZLE_RIFLE); + + int r = (float)input_sequence % 2; + switch (r) { + case 0: + Weapons_ViewAnimation(SCOUT_SHOOT1); + break; + default: + Weapons_ViewAnimation(SCOUT_SHOOT2); + break; + } +#else + TraceAttack_SetPenetrationPower(2); + TraceAttack_FireBullets(1, pl.origin + pl.view_ofs, 75, [accuracy,accuracy], WEAPON_SCOUT); + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_SHOOT_RIFLE, 0.45f); + else + Animation_PlayerTopTemp(ANIM_CROUCH_SHOOT_RIFLE, 0.45f); + + Sound_Play(pl, CHAN_WEAPON, "weapon_scout.fire"); +#endif + + pl.w_attack_next = 1.25f; + pl.w_idle_next = pl.w_attack_next; +} + +void +w_scout_reload(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (pl.scout_mag >= 10) { + return; + } + if (!pl.ammo_762mm) { + return; + } + +#ifdef CLIENT + Weapons_ViewAnimation(SCOUT_RELOAD); +#else + Weapons_ReloadWeapon(pl, player::scout_mag, player::ammo_762mm, 10); + Weapons_UpdateAmmo(pl, pl.scout_mag, pl.ammo_762mm, -1); +#endif + + pl.w_attack_next = 2.0f; + pl.w_idle_next = pl.w_attack_next; +} + +float +w_scout_aimanim(void) +{ + return w_ak47_aimanim(); +} + +void +w_scout_hud(void) +{ +#ifdef CLIENT + player pl = (player)self; + if (pl.viewzoom < 1.0f) { + Cstrike_DrawScope(); + } + 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/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_scout_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.scout_mag == 0 && pl.ammo_762mm == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + HUD_DrawAmmoBar(pos, pl.ammo_762mm, AMMO_MAX_762MM, a); + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud13_spr, + [0,45/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud12_spr, + [0,45/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_scout = +{ + .name = "scout", + .id = ITEM_SCOUT, + .slot = 0, + .slot_pos = 11, + .allow_drop = TRUE, + .draw = w_scout_draw, + .holster = __NULL__, + .primary = w_scout_primary, + .secondary = w_scout_secondary, + .reload = w_scout_reload, + .release = w_scout_release, + .crosshair = w_scout_hud, + .precache = w_scout_precache, + .pickup = w_scout_pickup, + .updateammo = w_scout_updateammo, + .wmodel = w_scout_wmodel, + .pmodel = w_scout_pmodel, + .deathmsg = w_scout_deathmsg, + .aimanim = w_scout_aimanim, + .hudpic = w_scout_hudpic +}; + +#ifdef SERVER +void +weapon_scout(void) +{ + Weapons_InitItem(WEAPON_SCOUT); +} +#endif diff --git a/src/shared/w_sg550.qc b/src/shared/w_sg550.qc new file mode 100644 index 0000000..ffd912b --- /dev/null +++ b/src/shared/w_sg550.qc @@ -0,0 +1,288 @@ +/* + * 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_sg550 (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_sg550.mdl" + +COUNTER-STRIKE (1999) ENTITY + +SIG SG 550 Weapon + +- Buy Menu - +Price: $4200 + +*/ + +enum +{ + SG550_IDLE, + SG550_SHOOT1, + SG550_SHOOT2, + SG550_RELOAD, + SG550_DRAW, +}; + +void +w_sg550_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_sg550.fire"); + precache_model("models/w_sg550.mdl"); +#else + precache_model("models/v_sg550.mdl"); + precache_model("models/p_sg550.mdl"); +#endif +} + +void +w_sg550_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.sg550_mag, pl.ammo_556mm, -1); +} + +string +w_sg550_wmodel(void) +{ + return "models/w_sg550.mdl"; +} + +string +w_sg550_pmodel(void) +{ + return "models/p_sg550.mdl"; +} + +string +w_sg550_deathmsg(void) +{ + return ""; +} + +int +w_sg550_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + if (startammo == -1) + pl.sg550_mag = 30; + else + pl.sg550_mag = startammo; + } else { + if (pl.ammo_556mm < AMMO_MAX_556MM) { + pl.ammo_556mm = bound(0, pl.ammo_556mm + 30, AMMO_MAX_556MM); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_sg550_draw(void) +{ + player pl = (player)self; + Weapons_SetModel("models/v_sg550.mdl"); + Weapons_ViewAnimation(SG550_DRAW); + +#ifdef CLIENT + pl.cs_cross_mindist = 5; + pl.cs_cross_deltadist = 3; +#endif +} + +void +w_sg550_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (!pl.sg550_mag) { + return; + } + + Cstrike_ShotMultiplierAdd(pl, 1); + float accuracy = Cstrike_CalculateAccuracy(pl, 200); + pl.sg550_mag--; + +#ifdef CLIENT + View_SetMuzzleflash(MUZZLE_RIFLE); + + int r = (float)input_sequence % 2; + switch (r) { + case 0: + Weapons_ViewAnimation(SCOUT_SHOOT1); + break; + default: + Weapons_ViewAnimation(SCOUT_SHOOT2); + break; + } +#else + TraceAttack_SetPenetrationPower(1); + TraceAttack_FireBullets(1, pl.origin + pl.view_ofs, 70, [accuracy,accuracy], WEAPON_SG550); + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_SHOOT_RIFLE, 0.45f); + else + Animation_PlayerTopTemp(ANIM_CROUCH_SHOOT_RIFLE, 0.45f); + + Sound_Play(pl, CHAN_WEAPON, "weapon_sg550.fire"); +#endif + + pl.w_attack_next = 0.25f; + pl.w_idle_next = pl.w_attack_next; +} + +void +w_sg550_secondary(void) +{ + player pl = (player)self; + if (pl.w_attack_next) { + return; + } + /* Simple toggle of fovs */ + if (pl.viewzoom == 1.0f) { + pl.viewzoom = 0.45f; + } else if (pl.viewzoom == 0.45f) { + pl.viewzoom = 0.1f; + } else { + pl.viewzoom = 1.0f; + } + pl.w_attack_next = 0.5f; +} + +void +w_sg550_reload(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (pl.sg550_mag >= 30) { + return; + } + if (!pl.ammo_556mm) { + return; + } + +#ifdef CLIENT + Weapons_ViewAnimation(SG550_RELOAD); +#else + Weapons_ReloadWeapon(pl, player::sg550_mag, player::ammo_556mm, 30); + Weapons_UpdateAmmo(pl, pl.sg550_mag, pl.ammo_556mm, -1); +#endif + + pl.w_attack_next = 3.8f; + pl.w_idle_next = pl.w_attack_next; +} + +float +w_sg550_aimanim(void) +{ + return w_ak47_aimanim(); +} + +void +w_sg550_hud(void) +{ +#ifdef CLIENT + player pl = (player)self; + if (pl.viewzoom < 1.0f) { + Cstrike_DrawScope(); + } + 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, [0,96/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_sg550_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.sg550_mag == 0 && pl.ammo_556mm == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + HUD_DrawAmmoBar(pos, pl.ammo_556mm, AMMO_MAX_556MM, a); + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud15_spr, + [0,180/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud14_spr, + [0,180/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_sg550 = +{ + .name = "sg550", + .id = ITEM_SG550, + .slot = 0, + .slot_pos = 14, + .allow_drop = TRUE, + .draw = w_sg550_draw, + .holster = __NULL__, + .primary = w_sg550_primary, + .secondary = w_sg550_secondary, + .reload = w_sg550_reload, + .release = w_cstrike_weaponrelease, + .crosshair = w_sg550_hud, + .precache = w_sg550_precache, + .pickup = w_sg550_pickup, + .updateammo = w_sg550_updateammo, + .wmodel = w_sg550_wmodel, + .pmodel = w_sg550_pmodel, + .deathmsg = w_sg550_deathmsg, + .aimanim = w_sg550_aimanim, + .hudpic = w_sg550_hudpic +}; + +#ifdef SERVER +void +weapon_sg550(void) +{ + Weapons_InitItem(WEAPON_SG550); +} +#endif diff --git a/src/shared/w_sg552.qc b/src/shared/w_sg552.qc new file mode 100644 index 0000000..34d29eb --- /dev/null +++ b/src/shared/w_sg552.qc @@ -0,0 +1,296 @@ +/* + * 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_sg552 (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_sg552.mdl" + +COUNTER-STRIKE (1999) ENTITY + +SIG SG 552 Commando Weapon + +- Buy Menu - +Price: $3500 + +*/ + +enum +{ + SG552_IDLE, + SG552_RELOAD, + SG552_DRAW, + SG552_SHOOT1, + SG552_SHOOT2, + SG552_SHOOT3 +}; + +void +w_sg552_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_sg552.fire"); + precache_model("models/w_sg552.mdl"); +#else + precache_model("models/v_sg552.mdl"); + precache_model("models/p_sg552.mdl"); +#endif +} + +void +w_sg552_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.sg552_mag, pl.ammo_556mm, -1); +} + +string +w_sg552_wmodel(void) +{ + return "models/w_sg552.mdl"; +} + +string +w_sg552_pmodel(void) +{ + return "models/p_sg552.mdl"; +} + +string +w_sg552_deathmsg(void) +{ + return ""; +} + +int +w_sg552_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + if (startammo == -1) + pl.sg552_mag = 30; + else + pl.sg552_mag = startammo; + } else { + if (pl.ammo_556mm < AMMO_MAX_556MM) { + pl.ammo_556mm = bound(0, pl.ammo_556mm + 30, AMMO_MAX_556MM); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_sg552_draw(void) +{ + player pl = (player)self; + Weapons_SetModel("models/v_sg552.mdl"); + Weapons_ViewAnimation(SG552_DRAW); + +#ifdef CLIENT + pl.cs_cross_mindist = 5; + pl.cs_cross_deltadist = 3; +#endif +} + +void +w_sg552_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (!pl.sg552_mag) { + return; + } + + Cstrike_ShotMultiplierAdd(pl, 1); + float accuracy = Cstrike_CalculateAccuracy(pl, 220); + pl.sg552_mag--; + +#ifdef CLIENT + View_SetMuzzleflash(MUZZLE_RIFLE); + + int r = (float)input_sequence % 3; + switch (r) { + case 0: + Weapons_ViewAnimation(SG552_SHOOT1); + break; + case 1: + Weapons_ViewAnimation(SG552_SHOOT2); + break; + default: + Weapons_ViewAnimation(SG552_SHOOT3); + break; + } +#else + TraceAttack_SetPenetrationPower(1); + TraceAttack_FireBullets(1, pl.origin + pl.view_ofs, 33, [accuracy,accuracy], WEAPON_SG552); + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_SHOOT_RIFLE, 0.45f); + else + Animation_PlayerTopTemp(ANIM_CROUCH_SHOOT_RIFLE, 0.45f); + + Sound_Play(pl, CHAN_WEAPON, "weapon_sg552.fire"); +#endif + + if (pl.viewzoom == 1.0f) { + pl.w_attack_next = 0.0825f; + } else { + pl.w_attack_next = 0.15f; + } + pl.w_idle_next = pl.w_attack_next; +} + +void +w_sg552_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_sg552_reload(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (pl.sg552_mag >= 30) { + return; + } + if (!pl.ammo_556mm) { + return; + } + +#ifdef CLIENT + Weapons_ViewAnimation(SG552_RELOAD); +#else + Weapons_ReloadWeapon(pl, player::sg552_mag, player::ammo_556mm, 30); + Weapons_UpdateAmmo(pl, pl.sg552_mag, pl.ammo_556mm, -1); +#endif + + pl.w_attack_next = 3.2f; + pl.w_idle_next = pl.w_attack_next; +} + +float +w_sg552_aimanim(void) +{ + return w_ak47_aimanim(); +} + +void +w_sg552_hud(void) +{ +#ifdef CLIENT + player pl = (player)self; + if (pl.viewzoom == 1.0f) { + Cstrike_DrawCrosshair(); + } else { + Cstrike_DrawSimpleCrosshair(); + } + 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, [0,96/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_sg552_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.sg552_mag == 0 && pl.ammo_556mm == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + HUD_DrawAmmoBar(pos, pl.ammo_556mm, AMMO_MAX_556MM, a); + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud11_spr, + [0,45/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud10_spr, + [0,45/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_sg552 = +{ + .name = "sg552", + .id = ITEM_SG552, + .slot = 0, + .slot_pos = 8, + .allow_drop = TRUE, + .draw = w_sg552_draw, + .holster = __NULL__, + .primary = w_sg552_primary, + .secondary = w_sg552_secondary, + .reload = w_sg552_reload, + .release = w_cstrike_weaponrelease, + .crosshair = w_sg552_hud, + .precache = w_sg552_precache, + .pickup = w_sg552_pickup, + .updateammo = w_sg552_updateammo, + .wmodel = w_sg552_wmodel, + .pmodel = w_sg552_pmodel, + .deathmsg = w_sg552_deathmsg, + .aimanim = w_sg552_aimanim, + .hudpic = w_sg552_hudpic +}; + +#ifdef SERVER +void +weapon_sg552(void) +{ + Weapons_InitItem(WEAPON_SG552); +} +#endif diff --git a/src/shared/w_smokegrenade.qc b/src/shared/w_smokegrenade.qc new file mode 100644 index 0000000..1adfef8 --- /dev/null +++ b/src/shared/w_smokegrenade.qc @@ -0,0 +1,297 @@ +/* + * 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_smokegrenade (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_smokegrenade.mdl" + +COUNTER-STRIKE (1999) ENTITY + +Smoke Grenade Weapon + +When thrown, the explosion casts view-blocking smoke in that radius. + +- Buy Menu - +Price: $300 + +*/ + +enum +{ + SMOKEGRENADE_IDLE, + SMOKEGRENADE_PULLPIN, + SMOKEGRENADE_THROW, + SMOKEGRENADE_DRAW, +}; + +void +w_smokegrenade_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_smokegrenade.bounce"); + Sound_Precache("weapon_smokegrenade.explode"); + precache_model("models/w_smokegrenade.mdl"); +#else + precache_model("models/v_smokegrenade.mdl"); + precache_model("models/p_smokegrenade.mdl"); +#endif +} + +void +w_smokegrenade_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, -1, pl.ammo_smokegrenade, pl.mode_temp); +} + +int +w_smokegrenade_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (pl.ammo_smokegrenade < AMMO_MAX_SMOKE) { + pl.ammo_smokegrenade = bound(0, pl.ammo_smokegrenade + 1, AMMO_MAX_SMOKE); + } else { + return FALSE; + } +#endif + return TRUE; +} + +string +w_smokegrenade_wmodel(void) +{ + return "models/w_smokegrenade.mdl"; +} + +string +w_smokegrenade_pmodel(void) +{ + return "models/p_smokegrenade.mdl"; +} + +string +w_smokegrenade_deathmsg(void) +{ + return ""; +} + +void +w_smokegrenade_draw(void) +{ + Weapons_SetModel("models/v_smokegrenade.mdl"); + Weapons_ViewAnimation(SMOKEGRENADE_DRAW); + player pl = (player)self; + pl.mode_temp = 0; +} + +#ifdef SERVER +void w_smokegrenade_throw(void) +{ + static void smokegrenade_explode(void) + { + FX_Smokenade(self.origin); + Sound_Play(self, CHAN_BODY, "weapon_smokegrenade.explode"); + remove(self); + } + + static void smokegrenade_touch(void) + { + if (other.takedamage == DAMAGE_YES) { + Damage_Apply(other, self.owner, 15, WEAPON_SMOKEGRENADE, DMG_BLUNT); + } else { + Sound_Play(self, CHAN_BODY, "weapon_smokegrenade.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 = smokegrenade_explode; + eGrenade.touch = smokegrenade_touch; + eGrenade.nextthink = time + 4.0f; + setmodel(eGrenade, "models/w_smokegrenade.mdl"); + setsize(eGrenade, [0,0,0], [0,0,0]); + setorigin(eGrenade, vecSrc); +} +#endif + +void +w_smokegrenade_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_temp > 0) { + return; + } + + /* Ammo check */ +#ifdef CLIENT + if (pl.ammo_smokegrenade <= 0) { + return; + } +#else + if (pl.ammo_smokegrenade <= 0) { + return; + } +#endif + + Weapons_ViewAnimation(SMOKEGRENADE_PULLPIN); + pl.mode_temp = 1; + pl.w_attack_next = 0.975f; + pl.w_idle_next = pl.w_attack_next; +} + +void +w_smokegrenade_release(void) +{ + player pl = (player)self; + + if (pl.w_idle_next > 0.0) { + return; + } + + if (pl.mode_temp == 1) { +#ifdef CLIENT + pl.ammo_smokegrenade--; + Weapons_ViewAnimation(SMOKEGRENADE_THROW); +#else + pl.ammo_smokegrenade--; + w_smokegrenade_throw(); +#endif + pl.mode_temp = 2; + pl.w_attack_next = 1.0f; + pl.w_idle_next = 0.5f; + } else if (pl.mode_temp == 2) { +#ifdef CLIENT + Weapons_ViewAnimation(SMOKEGRENADE_DRAW); +#else + if (!pl.ammo_smokegrenade) { + Weapons_RemoveItem(pl, WEAPON_SMOKEGRENADE); + } +#endif + pl.w_attack_next = 0.5f; + pl.w_idle_next = 0.5f; + pl.mode_temp = 0; + } +} + +float +w_smokegrenade_aimanim(void) +{ + return w_flashbang_aimanim(); +} + +void +w_smokegrenade_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, [144/256,96/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_smokegrenade_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + + HUD_DrawAmmoBar(pos, pl.ammo_smokegrenade, AMMO_MAX_SMOKE, a); + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud6_spr, + [0,135/256], + [170/256,45/256], + g_hud_color, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud3_spr, + [0,135/256], + [170/256,45/256], + g_hud_color, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_smokegrenade = +{ + .name = "smokegrenade", + .id = ITEM_SMOKEGRENADE, + .slot = 3, + .slot_pos = 2, + .allow_drop = FALSE, + .draw = w_smokegrenade_draw, + .holster = __NULL__, + .primary = w_smokegrenade_primary, + .secondary = __NULL__, + .reload = __NULL__, + .release = w_smokegrenade_release, + .crosshair = w_smokegrenade_hud, + .precache = w_smokegrenade_precache, + .pickup = w_smokegrenade_pickup, + .updateammo = w_smokegrenade_updateammo, + .wmodel = w_smokegrenade_wmodel, + .pmodel = w_smokegrenade_pmodel, + .deathmsg = w_smokegrenade_deathmsg, + .aimanim = w_smokegrenade_aimanim, + .hudpic = w_smokegrenade_hudpic +}; + +#ifdef SERVER +void +weapon_smokegrenade(void) +{ + Weapons_InitItem(WEAPON_SMOKEGRENADE); +} +#endif diff --git a/src/shared/w_tmp.qc b/src/shared/w_tmp.qc new file mode 100644 index 0000000..dafd11b --- /dev/null +++ b/src/shared/w_tmp.qc @@ -0,0 +1,273 @@ +/* + * 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_tmp (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_tmp.mdl" + +COUNTER-STRIKE (1999) ENTITY + +Steyr Tactical Weapon + +- Buy Menu - +Price: $1250 + +*/ + +enum +{ + TMP_IDLE, + TMP_RELOAD, + TMP_DRAW, + TMP_SHOOT1, + TMP_SHOOT2, + TMP_SHOOT3 +}; + +void +w_tmp_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_tmp.fire"); + precache_model("models/w_tmp.mdl"); +#else + precache_model("models/v_tmp.mdl"); + precache_model("models/p_tmp.mdl"); +#endif +} + +void +w_tmp_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.tmp_mag, pl.ammo_9mm, -1); +} + +string +w_tmp_wmodel(void) +{ + return "models/w_tmp.mdl"; +} + +string +w_tmp_pmodel(void) +{ + return "models/p_tmp.mdl"; +} + +string +w_tmp_deathmsg(void) +{ + return ""; +} + +int +w_tmp_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + if (startammo == -1) + pl.tmp_mag = 30; + else + pl.tmp_mag = startammo; + } else { + if (pl.ammo_9mm < AMMO_MAX_9MM) { + pl.ammo_9mm = bound(0, pl.ammo_9mm + 30, AMMO_MAX_9MM); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_tmp_draw(void) +{ + player pl = (player)self; + Weapons_SetModel("models/v_tmp.mdl"); + Weapons_ViewAnimation(TMP_DRAW); + +#ifdef CLIENT + pl.cs_cross_mindist = 7; + pl.cs_cross_deltadist = 3; +#endif +} + +void +w_tmp_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + + /* ammo check */ + if (!pl.tmp_mag) { + return; + } + + Cstrike_ShotMultiplierAdd(pl, 1); + float accuracy = Cstrike_CalculateAccuracy(pl, 200); + pl.tmp_mag--; + +#ifdef CLIENT + View_SetMuzzleflash(MUZZLE_RIFLE); + + int r = (float)input_sequence % 3; + switch (r) { + case 0: + Weapons_ViewAnimation(TMP_SHOOT1); + break; + case 1: + Weapons_ViewAnimation(TMP_SHOOT2); + break; + default: + Weapons_ViewAnimation(TMP_SHOOT3); + break; + } +#else + TraceAttack_SetPenetrationPower(0); + TraceAttack_FireBullets(1, pl.origin + pl.view_ofs, 26, [accuracy,accuracy], WEAPON_TMP); + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_SHOOT_MP5, 0.45f); + else + Animation_PlayerTopTemp(ANIM_CROUCH_SHOOT_MP5, 0.45f); + + Sound_Play(pl, CHAN_WEAPON, "weapon_tmp.fire"); +#endif + + pl.w_attack_next = 0.07f; + pl.w_idle_next = pl.w_attack_next; +} + +void +w_tmp_reload(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (pl.tmp_mag >= 30) { + return; + } + if (!pl.ammo_9mm) { + return; + } + +#ifdef CLIENT + Weapons_ViewAnimation(TMP_RELOAD); +#else + Weapons_ReloadWeapon(pl, player::tmp_mag, player::ammo_9mm, 30); + Weapons_UpdateAmmo(pl, pl.tmp_mag, pl.ammo_9mm, -1); +#endif + + pl.w_attack_next = 2.1f; + pl.w_idle_next = pl.w_attack_next; +} + +float +w_tmp_aimanim(void) +{ + return w_ak47_aimanim(); +} + +void +w_tmp_hud(void) +{ +#ifdef CLIENT + Cstrike_DrawCrosshair(); + 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, [48/256,72/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_tmp_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.tmp_mag == 0 && pl.ammo_9mm == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + HUD_DrawAmmoBar(pos, pl.ammo_9mm, AMMO_MAX_9MM, a); + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud5_spr, + [0,0], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud2_spr, + [0,0], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_tmp = +{ + .name = "tmp", + .id = ITEM_TMP, + .slot = 0, + .slot_pos = 6, + .allow_drop = TRUE, + .draw = w_tmp_draw, + .holster = __NULL__, + .primary = w_tmp_primary, + .secondary = __NULL__, + .reload = w_tmp_reload, + .release = w_cstrike_weaponrelease, + .crosshair = w_tmp_hud, + .precache = w_tmp_precache, + .pickup = w_tmp_pickup, + .updateammo = w_tmp_updateammo, + .wmodel = w_tmp_wmodel, + .pmodel = w_tmp_pmodel, + .deathmsg = w_tmp_deathmsg, + .aimanim = w_tmp_aimanim, + .hudpic = w_tmp_hudpic +}; + +#ifdef SERVER +void +weapon_tmp(void) +{ + Weapons_InitItem(WEAPON_TMP); +} +#endif diff --git a/src/shared/w_ump45.qc b/src/shared/w_ump45.qc new file mode 100644 index 0000000..faf3060 --- /dev/null +++ b/src/shared/w_ump45.qc @@ -0,0 +1,287 @@ +/* + * 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_ump45 (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_ump45.mdl" + +COUNTER-STRIKE (1999) ENTITY + +Heckler & Koch UMP .45 ACP Weapon + +- Buy Menu - +Price: $1700 + +*/ + +enum +{ + UMP45_IDLE, + UMP45_RELOAD, + UMP45_DRAW, + UMP45_SHOOT1, + UMP45_SHOOT2, + UMP45_SHOOT3 +}; + +void +w_ump45_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_ump45.fire"); + precache_model("models/w_ump45.mdl"); +#else + precache_model("models/v_ump45.mdl"); + precache_model("models/p_ump45.mdl"); +#endif +} + +void +w_ump45_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.ump45_mag, pl.ammo_45acp, -1); +} + +string +w_ump45_wmodel(void) +{ + return "models/w_ump45.mdl"; +} + +string +w_ump45_pmodel(void) +{ + return "models/p_ump45.mdl"; +} + +string +w_ump45_deathmsg(void) +{ + return ""; +} + +int +w_ump45_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + if (startammo == -1) + pl.ump45_mag = 25; + else + pl.ump45_mag = startammo; + } else { + if (pl.ammo_45acp < AMMO_MAX_45ACP) { + pl.ammo_45acp = bound(0, pl.ammo_45acp + 25, AMMO_MAX_45ACP); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_ump45_draw(void) +{ + player pl = (player)self; + Weapons_SetModel("models/v_ump45.mdl"); + Weapons_ViewAnimation(UMP45_DRAW); + +#ifdef CLIENT + pl.cs_cross_mindist = 6; + pl.cs_cross_deltadist = 3; +#endif +} + +void +w_ump45_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + + /* ammo check */ +#ifdef CLIENT + if (!pl.ump45_mag) { + return; + } +#else + if (!pl.ump45_mag) { + return; + } +#endif + + Cstrike_ShotMultiplierAdd(pl, 1); + float accuracy = Cstrike_CalculateAccuracy(pl, 210); + +#ifdef CLIENT + pl.ump45_mag--; + View_SetMuzzleflash(MUZZLE_RIFLE); +#else + TraceAttack_SetPenetrationPower(0); + TraceAttack_FireBullets(1, pl.origin + pl.view_ofs, 30, [accuracy,accuracy], WEAPON_UMP45); + pl.ump45_mag--; + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_SHOOT_MP5, 0.45f); + else + Animation_PlayerTopTemp(ANIM_CROUCH_SHOOT_MP5, 0.45f); + + Sound_Play(pl, CHAN_WEAPON, "weapon_ump45.fire"); +#endif + + int r = (float)input_sequence % 3; + switch (r) { + case 0: + Weapons_ViewAnimation(UMP45_SHOOT1); + break; + case 1: + Weapons_ViewAnimation(UMP45_SHOOT2); + break; + default: + Weapons_ViewAnimation(UMP45_SHOOT3); + break; + } + + pl.w_attack_next = 0.105f; + pl.w_idle_next = pl.w_attack_next; +} + +void +w_ump45_reload(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + +#ifdef CLIENT + if (pl.ump45_mag >= 25) { + return; + } + if (!pl.ammo_45acp) { + return; + } +#else + if (pl.ump45_mag >= 25) { + return; + } + if (!pl.ammo_45acp) { + return; + } + + Weapons_ReloadWeapon(pl, player::ump45_mag, player::ammo_45acp, 25); + Weapons_UpdateAmmo(pl, pl.ump45_mag, pl.ammo_45acp, -1); +#endif + + Weapons_ViewAnimation(UMP45_RELOAD); + pl.w_attack_next = 3.5f; + pl.w_idle_next = pl.w_attack_next; +} + +float +w_ump45_aimanim(void) +{ + return w_ak47_aimanim(); +} + +void +w_ump45_hud(void) +{ +#ifdef CLIENT + Cstrike_DrawCrosshair(); + 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, [96/256,72/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_ump45_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.ump45_mag == 0 && pl.ammo_45acp == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + HUD_DrawAmmoBar(pos, pl.ammo_45acp, AMMO_MAX_45ACP, a); + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud16_spr, + [0,0], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud16_spr, + [0,0], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_ump45 = +{ + .name = "ump45", + .id = ITEM_UMP45, + .slot = 0, + .slot_pos = 4, + .allow_drop = TRUE, + .draw = w_ump45_draw, + .holster = __NULL__, + .primary = w_ump45_primary, + .secondary = __NULL__, + .reload = w_ump45_reload, + .release = w_cstrike_weaponrelease, + .crosshair = w_ump45_hud, + .precache = w_ump45_precache, + .pickup = w_ump45_pickup, + .updateammo = w_ump45_updateammo, + .wmodel = w_ump45_wmodel, + .pmodel = w_ump45_pmodel, + .deathmsg = w_ump45_deathmsg, + .aimanim = w_ump45_aimanim, + .hudpic = w_ump45_hudpic +}; + +#ifdef SERVER +void +weapon_ump45(void) +{ + Weapons_InitItem(WEAPON_UMP45); +} +#endif diff --git a/src/shared/w_usp45.qc b/src/shared/w_usp45.qc new file mode 100644 index 0000000..3f98c30 --- /dev/null +++ b/src/shared/w_usp45.qc @@ -0,0 +1,354 @@ +/* + * 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_usp45 (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_usp45.mdl" + +COUNTER-STRIKE (1999) ENTITY + +Heckler & Koch USP .45 Tactical Weapon + +Default arsenal for Counter-Terrorists + +- Buy Menu - +Price: $500 + +*/ + +enum +{ + USP45_IDLE, + USP45_SHOOT1, + USP45_SHOOT2, + USP45_SHOOT3, + USP45_SHOOTLAST, + USP45_RELOAD, + USP45_DRAW, + USP45_ADDSIL, + USP45_IDLEUNSIL, + USP45_SHOOT1UNSIL, + USP45_SHOOT2UNSIL, + USP45_SHOOT3UNSIL, + USP45_SHOOTLASTUNSIL, + USP45_RELOADUNSIL, + USP45_DRAWUNSIL, + USP45_DETACHSIL +}; + +void +w_usp45_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_usp45.fire"); + Sound_Precache("weapon_usp45.silenced"); + precache_model("models/w_usp.mdl"); +#else + precache_model("models/v_usp.mdl"); + precache_model("models/p_usp.mdl"); +#endif +} + +void +w_usp45_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.usp45_mag, pl.ammo_45acp, -1); +} + +string +w_usp45_wmodel(void) +{ + return "models/w_usp.mdl"; +} + +string +w_usp45_pmodel(void) +{ + return "models/p_usp.mdl"; +} + +string +w_usp45_deathmsg(void) +{ + return ""; +} + +int +w_usp45_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + if (startammo == -1) + pl.usp45_mag = 12; + else + pl.usp45_mag = startammo; + } else { + if (pl.ammo_45acp < AMMO_MAX_45ACP) { + pl.ammo_45acp = bound(0, pl.ammo_45acp + 12, AMMO_MAX_45ACP); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_usp45_draw(void) +{ + player pl = (player)self; + + Weapons_SetModel("models/v_usp.mdl"); + if (pl.mode_usp45 == 1) { + Weapons_ViewAnimation(USP45_DRAW); + } else { + Weapons_ViewAnimation(USP45_DRAWUNSIL); + } + +#ifdef CLIENT + pl.cs_cross_mindist = 8; + pl.cs_cross_deltadist = 3; +#endif +} + +void +w_usp45_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + + if (pl.gflags & GF_SEMI_TOGGLED) { + return; + } + + /* ammo check */ + if (!pl.usp45_mag) { + return; + } + + Cstrike_ShotMultiplierAdd(pl, 1); + float accuracy = Cstrike_CalculateAccuracy(pl, 200); + pl.usp45_mag--; + + /* actual firing */ +#ifdef CLIENT + if (pl.mode_usp45 == 1) { + View_SetMuzzleflash(0); + } else { + View_SetMuzzleflash(MUZZLE_SMALL); + } +#else + /* Different sounds without silencer */ + if (pl.mode_usp45 == 1) { + Sound_Play(pl, CHAN_WEAPON, "weapon_usp45.silenced"); + } else { + Sound_Play(pl, CHAN_WEAPON, "weapon_usp45.fire"); + } + + TraceAttack_SetPenetrationPower(0); + TraceAttack_FireBullets(1, pl.origin + pl.view_ofs, 33, [accuracy,accuracy], WEAPON_USP45); + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_SHOOT_ONEHAND, 0.45f); + else + Animation_PlayerTopTemp(ANIM_CROUCH_SHOOT_ONEHAND, 0.45f); +#endif + + /* this stuff is predicted */ + int r = (float)input_sequence % 3; + if (pl.mode_usp45 == 1) { + if (pl.usp45_mag <= 0) { + Weapons_ViewAnimation(USP45_SHOOTLAST); + } else { + switch (r) { + case 0: + Weapons_ViewAnimation(USP45_SHOOT1); + break; + case 1: + Weapons_ViewAnimation(USP45_SHOOT2); + break; + default: + Weapons_ViewAnimation(USP45_SHOOT3); + break; + } + } + } else { + if (pl.usp45_mag <= 0) { + Weapons_ViewAnimation(USP45_SHOOTLASTUNSIL); + } else { + switch (r) { + case 0: + Weapons_ViewAnimation(USP45_SHOOT1UNSIL); + break; + case 1: + Weapons_ViewAnimation(USP45_SHOOT2UNSIL); + break; + default: + Weapons_ViewAnimation(USP45_SHOOT3UNSIL); + break; + } + } + } + + pl.gflags |= GF_SEMI_TOGGLED; + pl.w_attack_next = 0.15f; + pl.w_idle_next = pl.w_attack_next; +} + +void +w_usp45_secondary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0) { + return; + } + + /* toggle silencer */ + pl.mode_usp45 = 1 - pl.mode_usp45; + + /* play the animation */ + if (pl.mode_usp45) { + Weapons_ViewAnimation(USP45_ADDSIL); + } else { + Weapons_ViewAnimation(USP45_DETACHSIL); + } + + pl.w_attack_next = 3.1f; + pl.w_idle_next = pl.w_attack_next; +} + +void +w_usp45_reload(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + if (pl.usp45_mag >= 12) { + return; + } + if (!pl.ammo_45acp) { + return; + } + +#ifdef CLIENT + if (pl.mode_usp45 == 1) { + Weapons_ViewAnimation(USP45_RELOAD); + } else { + Weapons_ViewAnimation(USP45_RELOADUNSIL); + } +#else + Weapons_ReloadWeapon(pl, player::usp45_mag, player::ammo_45acp, 12); +#endif + + pl.w_attack_next = 2.5f; +} + +float +w_usp45_aimanim(void) +{ + return w_deagle_aimanim(); +} + +void +w_usp45_hud(void) +{ +#ifdef CLIENT + Cstrike_DrawCrosshair(); + 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, [96/256,72/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_usp45_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.usp45_mag == 0 && pl.ammo_45acp == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + HUD_DrawAmmoBar(pos, pl.ammo_45acp, AMMO_MAX_45ACP, a); + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud4_spr, + [0,90/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud1_spr, + [0,90/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_usp45 = +{ + .name = "usp", + .id = ITEM_USP45, + .slot = 1, + .slot_pos = 0, + .allow_drop = TRUE, + .draw = w_usp45_draw, + .holster = __NULL__, + .primary = w_usp45_primary, + .secondary = w_usp45_secondary, + .reload = w_usp45_reload, + .release = w_cstrike_weaponrelease, + .crosshair = w_usp45_hud, + .precache = w_usp45_precache, + .pickup = w_usp45_pickup, + .updateammo = w_usp45_updateammo, + .wmodel = w_usp45_wmodel, + .pmodel = w_usp45_pmodel, + .deathmsg = w_usp45_deathmsg, + .aimanim = w_usp45_aimanim, + .hudpic = w_usp45_hudpic +}; + +#ifdef SERVER +void +weapon_usp45(void) +{ + Weapons_InitItem(WEAPON_USP45); +} +#endif diff --git a/src/shared/w_xm1014.qc b/src/shared/w_xm1014.qc new file mode 100644 index 0000000..7f19f18 --- /dev/null +++ b/src/shared/w_xm1014.qc @@ -0,0 +1,331 @@ +/* + * 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_xm1014 (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_xm1014.mdl" + +COUNTER-STRIKE (1999) ENTITY + +Benneli XM1014 Weapon + +- Buy Menu - +Price: $3000 + +*/ + +enum +{ + XM1014_IDLE, + XM1014_SHOOT1, + XM1014_SHOOT2, + XM1014_INSERT, + XM1014_RELOAD_END, + XM1014_RELOAD_START, + XM1014_DRAW +}; + +enum +{ + XM1014S_IDLE, + XM1014S_RELOAD_START, + XM1014S_RELOAD, + XM1014S_RELOAD_END +}; + +void +w_xm1014_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_xm1014.fire"); + Sound_Precache("weapon_xm1014.insertshell"); + precache_model("models/w_xm1014.mdl"); +#else + precache_model("models/v_xm1014.mdl"); + precache_model("models/p_xm1014.mdl"); +#endif +} + +void +w_xm1014_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.xm1014_mag, pl.ammo_buckshot, -1); +} + +string +w_xm1014_wmodel(void) +{ + return "models/w_xm1014.mdl"; +} + +string +w_xm1014_pmodel(void) +{ + return "models/p_xm1014.mdl"; +} + +string +w_xm1014_deathmsg(void) +{ + return ""; +} + +int +w_xm1014_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + if (startammo == -1) + pl.xm1014_mag = 7; + else + pl.xm1014_mag = startammo; + } else { + if (pl.ammo_buckshot < AMMO_MAX_BUCKSHOT) { + pl.ammo_buckshot = bound(0, pl.ammo_buckshot + 7, AMMO_MAX_BUCKSHOT); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_xm1014_draw(void) +{ + player pl = (player)self; + Weapons_SetModel("models/v_xm1014.mdl"); + Weapons_ViewAnimation(XM1014_DRAW); + pl.mode_temp = 0; + +#ifdef CLIENT + pl.cs_cross_mindist = 9; + pl.cs_cross_deltadist = 4; +#endif +} + +void +w_xm1014_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + + /* ammo check */ +#ifdef CLIENT + if (!pl.xm1014_mag) { + return; + } +#else + if (!pl.xm1014_mag) { + return; + } +#endif + + Cstrike_ShotMultiplierAdd(pl, 6); + float accuracy = Cstrike_CalculateAccuracy(pl, 200); + +#ifdef CLIENT + pl.xm1014_mag--; + View_SetMuzzleflash(MUZZLE_RIFLE); +#else + TraceAttack_SetPenetrationPower(0); + TraceAttack_FireBullets(6, pl.origin + pl.view_ofs, 22, [accuracy,accuracy], WEAPON_XM1014); + pl.xm1014_mag--; + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_SHOOT_SHOTGUN, 0.45f); + else + Animation_PlayerTopTemp(ANIM_CROUCH_SHOOT_SHOTGUN, 0.45f); + + Sound_Play(pl, CHAN_WEAPON, "weapon_xm1014.fire"); +#endif + + int r = (float)input_sequence % 3; + switch (r) { + case 0: + Weapons_ViewAnimation(XM1014_SHOOT1); + break; + default: + Weapons_ViewAnimation(XM1014_SHOOT2); + break; + } + + pl.w_attack_next = 0.25f; + pl.w_idle_next = pl.w_attack_next; +} + +void +w_xm1014_reload(void) +{ + player pl = (player)self; +#ifdef CLIENT + if (pl.xm1014_mag >= 7) { + return; + } + if (pl.ammo_buckshot <= 0) { + return; + } +#else + if (pl.xm1014_mag >= 7) { + return; + } + if (pl.ammo_buckshot <= 0) { + return; + } +#endif + + if (pl.mode_temp > XM1014S_IDLE) { + return; + } + pl.mode_temp = XM1014S_RELOAD_START; + pl.w_idle_next = 0.0f; +} + +void +w_xm1014_release(void) +{ + player pl = (player)self; + + w_cstrike_weaponrelease(); + + if (pl.w_idle_next > 0.0) { + return; + } + + if (pl.mode_temp == XM1014S_RELOAD_START) { + Weapons_ViewAnimation(XM1014_RELOAD_START); + pl.mode_temp = XM1014S_RELOAD; + pl.w_idle_next = 0.65f; + } else if (pl.mode_temp == XM1014S_RELOAD) { + Weapons_ViewAnimation(XM1014_INSERT); +#ifdef CLIENT + pl.xm1014_mag++; + pl.ammo_buckshot--; + + if (pl.ammo_buckshot <= 0 || pl.xm1014_mag >= 7) { + pl.mode_temp = XM1014S_RELOAD_END; + } +#else + pl.xm1014_mag++; + pl.ammo_buckshot--; + w_xm1014_updateammo(pl); + Sound_Play(pl, CHAN_WEAPON, "weapon_xm1014.insertshell"); + if (pl.ammo_buckshot <= 0 || pl.xm1014_mag >= 7) { + pl.mode_temp = XM1014S_RELOAD_END; + } +#endif + pl.w_idle_next = 0.5f; + } else if (pl.mode_temp == XM1014S_RELOAD_END) { + Weapons_ViewAnimation(XM1014_RELOAD_END); + pl.mode_temp = XM1014S_IDLE; + pl.w_idle_next = 10.0f; + pl.w_attack_next = 0.5f; + } +} + +float +w_xm1014_aimanim(void) +{ + return w_ak47_aimanim(); +} + +void +w_xm1014_hud(void) +{ +#ifdef CLIENT + Cstrike_DrawCrosshair(); + 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, [0,72/256], [24/256, 24/256], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +void +w_xm1014_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.xm1014_mag == 0 && pl.ammo_buckshot == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + HUD_DrawAmmoBar(pos, pl.ammo_buckshot, AMMO_MAX_BUCKSHOT, a); + + if (selected) { + drawsubpic( + pos, + [170,45], + g_hud13_spr, + [0,135/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + g_hud12_spr, + [0,135/256], + [170/256,45/256], + hud_col, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_xm1014 = +{ + .name = "xm1014", + .id = ITEM_XM1014, + .slot = 0, + .slot_pos = 1, + .allow_drop = TRUE, + .draw = w_xm1014_draw, + .holster = __NULL__, + .primary = w_xm1014_primary, + .secondary = __NULL__, + .reload = w_xm1014_reload, + .release = w_xm1014_release, + .crosshair = w_xm1014_hud, + .precache = w_xm1014_precache, + .pickup = w_xm1014_pickup, + .updateammo = w_xm1014_updateammo, + .wmodel = w_xm1014_wmodel, + .pmodel = w_xm1014_pmodel, + .deathmsg = w_xm1014_deathmsg, + .aimanim = w_xm1014_aimanim, + .hudpic = w_xm1014_hudpic +}; + +#ifdef SERVER +void +weapon_xm1014(void) +{ + Weapons_InitItem(WEAPON_XM1014); +} +#endif diff --git a/src/shared/weapons.h b/src/shared/weapons.h new file mode 100644 index 0000000..7084503 --- /dev/null +++ b/src/shared/weapons.h @@ -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. + */ + +/* Weapon Indices for the weapon table */ +enum +{ + WEAPON_NONE, + WEAPON_M3, + WEAPON_XM1014, + WEAPON_MP5, + WEAPON_P90, + WEAPON_UMP45, + WEAPON_MAC10, + WEAPON_TMP, + WEAPON_AK47, + WEAPON_SG552, + WEAPON_M4A1, + WEAPON_AUG, + WEAPON_SCOUT, + WEAPON_AWP, + WEAPON_G3SG1, + WEAPON_SG550, + WEAPON_PARA, + WEAPON_USP45, + WEAPON_GLOCK18, + WEAPON_DEAGLE, + WEAPON_P228, + WEAPON_ELITES, + WEAPON_FIVESEVEN, + WEAPON_KNIFE, + WEAPON_HEGRENADE, + WEAPON_FLASHBANG, + WEAPON_SMOKEGRENADE, + WEAPON_C4BOMB +}; + +#define AMMO_MAX_50AE 35 +#define AMMO_MAX_762MM 90 +#define AMMO_MAX_556MM 90 +#define AMMO_MAX_556MMBOX 200 +#define AMMO_MAX_338MAG 30 +#define AMMO_MAX_9MM 120 +#define AMMO_MAX_BUCKSHOT 32 +#define AMMO_MAX_45ACP 100 +#define AMMO_MAX_357SIG 52 +#define AMMO_MAX_57MM 100 +#define AMMO_MAX_FLASHBANG 2 +#define AMMO_MAX_SMOKE 1 +#define AMMO_MAX_HENADE 1 diff --git a/src/shared/weapons.qc b/src/shared/weapons.qc new file mode 100644 index 0000000..9600136 --- /dev/null +++ b/src/shared/weapons.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. + */ + +weapon_t w_null = {}; +weapon_t g_weapons[] = { + w_null, + w_m3, + w_xm1014, + w_mp5, + w_p90, + w_ump45, + w_mac10, + w_tmp, + w_ak47, + w_sg552, + w_m4a1, + w_aug, + w_scout, + w_awp, + w_g3sg1, + w_sg550, + w_para, + w_usp45, + w_glock18, + w_deagle, + w_p228, + w_elites, + w_fiveseven, + w_knife, + w_hegrenade, + w_flashbang, + w_smokegrenade, + w_c4bomb +}; diff --git a/src/shared/weapons_cstrike.qc b/src/shared/weapons_cstrike.qc new file mode 100644 index 0000000..dd7ec34 --- /dev/null +++ b/src/shared/weapons_cstrike.qc @@ -0,0 +1,81 @@ +/* + * 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. + */ + +/* + * How Counter-Strike's accuracy works (from my understanding) + * this was deducted from the decrypted CS:S script files: + * https://gamebanana.com/gamefiles/2293 + * + * Each and every bullet fired contributes to the shotmultiplier value, + * which decreases back to 0 slowly over time. + * + * Meanwhile, accuracy gets calculated by taking said value and dividing it + * by the weapon-specific divider. Each gun has a different one. + * The higher the divider value, the more accurate the weapon is in contrast + * to other weapons. +*/ + +/* called whenever a cstrike gun fires a successful shot */ +void +Cstrike_ShotMultiplierAdd(player pl, int shots) +{ + /* more than 12 is enough, you can barely hit the barn */ + pl.cs_shotmultiplier = bound(0, pl.cs_shotmultiplier + shots, 12); + + pl.cs_shottime = 0.2f; + pl.punchangle[0] = -4 * (pl.cs_shotmultiplier / 6); + pl.punchangle[1] = random(-1, 1); +} + +/* generate an accuracy value that we'll pass onto TraceAttack */ +float +Cstrike_CalculateAccuracy(player pl, float divisor) +{ + float m = 1.0f; + + if (!(pl.flags & FL_ONGROUND)) { + m = 2.0f; + } else if (pl.flags & FL_CROUCHING) { + m = 0.5f; + } else if (vlen(pl.velocity) > 120) { + m = 1.5f; + } + + if (divisor == -1) { + /* snipers shoot way less accurate overall. */ + return (pl.viewzoom < 1.0f) ? (0.0f) : (0.05 * m); + } else { + return (pl.cs_shotmultiplier / divisor) * m; + } +} + +/* called whenever cstrike guns aren't firing */ +void +Cstrike_ShotMultiplierUpdate(player pl) +{ + if ((pl.cs_shotmultiplier > 0) && (pl.cs_shottime <= 0.0f)) { + pl.cs_shottime = pl.w_attack_next + 0.01; + pl.cs_shotmultiplier--; + } + + pl.cs_shottime = max(0, pl.cs_shottime - input_timelength); +} +void +w_cstrike_weaponrelease(void) +{ + player pl = (player)self; + Cstrike_ShotMultiplierUpdate(pl); +} diff --git a/zpak001.pk3dir/default.cfg b/zpak001.pk3dir/default.cfg new file mode 100755 index 0000000..23dd12e --- /dev/null +++ b/zpak001.pk3dir/default.cfg @@ -0,0 +1,65 @@ +// 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" + +// Counter-Strike Binds +bind "b" "buy" +bind "m" "chooseteam" +bind "g" "drop" +bind "q" "lastinv" + +// Game Variables +seta "hostname" "FreeCS Server" +seta "maxplayers" "8" +seta "mp_startmoney" "800" +seta "mp_buytime" "90" +seta "mp_freezetime" "6" +seta "mp_c4timer" "45" +seta "mp_roundtime" "5" +seta "fcs_knifeonly" "0" +seta "fcs_swapteams" "0" +seta "fcs_nopickups" "0" +seta "fcs_reward_kill" "300" +seta "fcs_penalty_pain" "-150" +seta "fcs_penalty_kill" "-1500" +seta "fcs_maxmoney" "16000" +seta "fcs_fillweapons" "0" +seta "fcs_autoreload" "0" + +// 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/gfx/vgui/buymenu.tga b/zpak001.pk3dir/gfx/vgui/buymenu.tga new file mode 100644 index 0000000..9afcaab Binary files /dev/null and b/zpak001.pk3dir/gfx/vgui/buymenu.tga differ diff --git a/zpak001.pk3dir/particles/fx_smokenade.cfg b/zpak001.pk3dir/particles/fx_smokenade.cfg new file mode 100644 index 0000000..1df4014 --- /dev/null +++ b/zpak001.pk3dir/particles/fx_smokenade.cfg @@ -0,0 +1,15 @@ +r_part effect +{ + texture "particles/fteparticlefont.tga" + tcoords 97 97 191 191 256 + count 1 + scale 512 + scalefactor 1 + die 3 + alpha 0.7 + rgb 128 128 128 + spawnmode ball + gravity -25 + veladd -20 + randomvel 64 -64 +} diff --git a/zpak001.pk3dir/sound/items_cstrike.sndshd b/zpak001.pk3dir/sound/items_cstrike.sndshd new file mode 100644 index 0000000..cf7d022 --- /dev/null +++ b/zpak001.pk3dir/sound/items_cstrike.sndshd @@ -0,0 +1,14 @@ +buy.kevlar +{ + sample items/tr_kevlar.wav +} + +buy.weapon +{ + sample items/gunpickup2.wav +} + +buy.ammo +{ + sample items/9mmclip1.wav +} diff --git a/zpak001.pk3dir/sound/npcs_cstrike.sndshd b/zpak001.pk3dir/sound/npcs_cstrike.sndshd new file mode 100644 index 0000000..048524d --- /dev/null +++ b/zpak001.pk3dir/sound/npcs_cstrike.sndshd @@ -0,0 +1,8 @@ +hostage_entity.follow +{ + sample hostage/hos1.wav + sample hostage/hos2.wav + sample hostage/hos3.wav + sample hostage/hos4.wav + sample hostage/hos5.wav +} diff --git a/zpak001.pk3dir/sound/weapons_cstrike.sndshd b/zpak001.pk3dir/sound/weapons_cstrike.sndshd new file mode 100644 index 0000000..901a514 --- /dev/null +++ b/zpak001.pk3dir/sound/weapons_cstrike.sndshd @@ -0,0 +1,231 @@ +weapon_ak47.fire +{ + sample weapons/ak47-1.wav + sample weapons/ak47-2.wav +} + +weapon_aug.fire +{ + sample weapons/aug-1.wav +} + +weapon_awp.fire +{ + sample weapons/awp1.wav +} + +weapon_awp.zoom +{ + sample weapons/zoom.wav +} + +weapon_c4bomb.disarm +{ + sample weapons/c4_disarm.wav +} + +weapon_c4bomb.disarmed +{ + sample weapons/c4_disarmed.wav +} + +weapon_c4bomb.explode +{ + sample weapons/c4_explode1.wav +} + +weapon_c4bomb.plant +{ + sample weapons/c4_plant.wav +} + +weapon_deagle.fire +{ + sample weapons/deagle-1.wav + sample weapons/deagle-2.wav +} + +weapon_elites.fire +{ + sample weapons/elite_fire.wav + sample weapons/elite_fire.wav +} + +weapon_fiveseven.fire +{ + sample weapons/fiveseven-1.wav +} + +weapon_flashbang.explode +{ + sample weapons/flashbang-1.wav +} + +weapon_g3sg1.fire +{ + sample weapons/g3sg1-1.wav +} + +weapon_glock18.fire +{ + sample weapons/glock18-2.wav +} + +weapon_glock18.burstfire +{ + sample weapons/glock18-1.wav +} + +weapon_hegrenade.bounce +{ + sample weapons/he_bounce-1.wav +} + +weapon_hegrenade.explode +{ + sample weapons/explode3.wav + sample weapons/explode4.wav +} + +weapon_smokegrenade.bounce +{ + sample weapons/grenade_hit1.wav + sample weapons/grenade_hit2.wav + sample weapons/grenade_hit3.wav +} + +weapon_smokegrenade.explode +{ + sample weapons/sg_explode.wav +} + +weapon_flashbang.bounce +{ + sample weapons/grenade_hit1.wav + sample weapons/grenade_hit2.wav + sample weapons/grenade_hit3.wav +} + +weapon_flashbang.explode +{ + sample weapons/flashbang-1.wav + sample weapons/flashbang-2.wav +} + +weapon_knife.hit +{ + sample weapons/knife_hitwall1.wav +} + +weapon_knife.hitbody +{ + sample weapons/knife_hit1.wav + sample weapons/knife_hit2.wav + sample weapons/knife_hit3.wav + sample weapons/knife_hit4.wav +} + +weapon_knife.hithard +{ + sample weapons/knife_stab.wav +} + +weapon_knife.miss +{ + sample weapons/knife_slash1.wav +} + +weapon_m3.fire +{ + sample weapons/m3-1.wav +} + +weapon_m4a1.fire +{ + sample weapons/m4a1_unsil-1.wav + sample weapons/m4a1_unsil-2.wav +} + +weapon_m4a1.silenced +{ + sample weapons/m4a1-1.wav +} + +weapon_mac10.fire +{ + sample weapons/mac10-1.wav +} + +weapon_mp5.fire +{ + sample weapons/mp5-1.wav +} + +weapon_p228.fire +{ + sample weapons/p228-1.wav +} + +weapon_p90.fire +{ + sample weapons/p90-1.wav +} + + +weapon_para.fire +{ + sample weapons/m249-1.wav + sample weapons/m249-2.wav +} + +weapon_scout.fire +{ + sample weapons/scout_fire-1.wav +} + +weapon_sg550.fire +{ + sample weapons/sg550-1.wav +} + +weapon_sg552.fire +{ + sample weapons/sg552-1.wav +} + +weapon_smokegrenade.explode +{ + sample weapons/sg_explode.wav +} + +weapon_tmp.fire +{ + sample weapons/tmp-1.wav + sample weapons/tmp-2.wav +} + +weapon_ump45.fire +{ + sample weapons/ump45-1.wav +} + +weapon_usp45.fire +{ + sample weapons/usp_unsil-1.wav +} + +weapon_usp45.silenced +{ + sample weapons/usp1.wav + sample weapons/usp2.wav +} + +weapon_xm1014.fire +{ + sample weapons/xm1014-1.wav +} + +weapon_xm1014.insertshell +{ + sample weapons/m3_insertshell.wav +}