commit a3ef0328231a14e5d1e0cee560a1816af99630e8 Author: Marco Hladik Date: Mon Mar 8 11:11:06 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..c478c20 --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +# FreeSci +Clean-room reimplementation of Scientist Hunt in QuakeC. + +Scientist Hunt was a Half-Life mod in which you had to contain the spread +of lab-coat wearing beings that populate like crazy. + +The original mod was created by Rich Whitehouse. +I'm just nostalgic for it, so I recoded the important bits to use with +FreeHL and Nuclide. + +It's not complete (yet). + +![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 scihunt + +then either run Nuclide's ./build_game.sh shell script, or issue 'make' inside +./scihunt/src! + +Obviously make sure that Nuclide has fteqw and fteqcc set-up for building. + +## Community +Join us on #halflife or #scihunt via irc.frag-net.com and chat. + +## License +ISC License + +Copyright (c) 2016-2021 Marco Hladik + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING +OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/img/preview1.jpg b/img/preview1.jpg new file mode 100644 index 0000000..3088f80 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..e6f3737 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..896dab7 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..c88e07d Binary files /dev/null and b/img/preview4.jpg differ diff --git a/src/.kdev4/src.kdev4 b/src/.kdev4/src.kdev4 new file mode 100644 index 0000000..8825d48 --- /dev/null +++ b/src/.kdev4/src.kdev4 @@ -0,0 +1,5 @@ +[Buildset] +BuildItems=@Variant(\x00\x00\x00\t\x00\x00\x00\x00\x01\x00\x00\x00\x0b\x00\x00\x00\x00\x01\x00\x00\x00\x1c\x00S\x00c\x00i\x00e\x00n\x00t\x00i\x00s\x00t\x00 \x00H\x00u\x00n\x00t) + +[Project] +VersionControlSupport=kdevgit 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/progs.src b/src/client/progs.src new file mode 100644 index 0000000..1488785 --- /dev/null +++ b/src/client/progs.src @@ -0,0 +1,38 @@ +#pragma target fte +#pragma progs_dat "../../csprogs.dat" + +#define CSQC +#define CLIENT +#define VALVE +#define CLASSIC_VGUI +#define GS_RENDERFX + +#includelist +../../../src/shared/fteextensions.qc +../../../src/shared/defs.h +../../../valve/src/client/defs.h +../../../src/client/defs.h + +../../../src/vgui/include.src + +../../../src/gs-entbase/client.src +../../../src/gs-entbase/shared.src +../shared/include.src + +../../../valve/src/client/predict.qc +../../../valve/src/client/init.qc +../../../valve/src/client/player.qc +../../../valve/src/client/entities.qc +../../../valve/src/client/cmds.qc +../../../valve/src/client/game_event.qc +../../../valve/src/client/view.qc +../../../valve/src/client/obituary.qc +../../../valve/src/client/hud.qc +../../../valve/src/client/hud_weaponselect.qc +../../../valve/src/client/scoreboard.qc +../../../valve/src/client/input.qc +../../../base/src/client/modelevent.qc + +../../../src/client/include.src +../../../src/shared/include.src +#endlist 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/defs.h b/src/server/defs.h new file mode 100644 index 0000000..465ac3d --- /dev/null +++ b/src/server/defs.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "gamerules.h" +#include "../../../valve/src/server/items.h" diff --git a/src/server/gamerules.h b/src/server/gamerules.h new file mode 100644 index 0000000..f31edc4 --- /dev/null +++ b/src/server/gamerules.h @@ -0,0 +1,33 @@ +/* + * 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 "../../../src/server/gamerules.h" + +class SHMultiplayerRules:CGameRules +{ + /* client */ + virtual void(base_player) PlayerSpawn; + virtual void(base_player) PlayerConnect; + virtual void(base_player) PlayerDisconnect; + virtual void(base_player) PlayerKill; + virtual void(base_player) PlayerDeath; + virtual void(base_player) PlayerPostFrame; + virtual void(base_player, entity) ScientistKill; + + virtual void(base_player) LevelDecodeParms; + virtual void(base_player) LevelChangeParms; + virtual void(void) LevelNewParms; +}; diff --git a/src/server/gamerules.qc b/src/server/gamerules.qc new file mode 100644 index 0000000..b072fd3 --- /dev/null +++ b/src/server/gamerules.qc @@ -0,0 +1,399 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +var int autocvar_sh_insanity = 10; +var int autocvar_sv_playerkeepalive = TRUE; + +void +SHMultiplayerRules::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 (pl.flags & FL_CLIENT || pl.flags & FL_MONSTER) + if (g_dmg_eAttacker.flags & FL_CLIENT) { + if (pl == g_dmg_eAttacker) + g_dmg_eAttacker.frags--; + else + g_dmg_eAttacker.frags++; + } + + pl.movetype = MOVETYPE_NONE; + pl.solid = SOLID_NOT; + pl.takedamage = DAMAGE_NO; + pl.gflags &= ~GF_FLASHLIGHT; + pl.armor = pl.activeweapon = pl.g_items = 0; + + pl.think = PutClientInServer; + pl.nextthink = time + 4.0f; + Sound_Play(pl, CHAN_AUTO, "player.die"); + + if (pl.health < -50) { + pl.health = 0; + FX_GibHuman(pl.origin); + return; + } + + pl.health = 0; + + /* Let's handle corpses on the clientside */ + entity corpse = spawn(); + setorigin(corpse, pl.origin + [0,0,32]); + setmodel(corpse, pl.model); + setsize(corpse, VEC_HULL_MIN, VEC_HULL_MAX); + corpse.movetype = MOVETYPE_TOSS; + corpse.solid = SOLID_TRIGGER; + corpse.modelindex = pl.modelindex; + corpse.frame = ANIM_DIESIMPLE; + corpse.angles = pl.angles; + corpse.velocity = pl.velocity; +} + +void +SHMultiplayerRules::PlayerSpawn(base_player pp) +{ + player pl = (player)pp; + /* this is where the mods want to deviate */ + entity spot; + + pl.classname = "player"; + pl.health = pl.max_health = 100; + pl.takedamage = DAMAGE_YES; + pl.solid = SOLID_SLIDEBOX; + pl.movetype = MOVETYPE_WALK; + pl.flags = FL_CLIENT; + pl.viewzoom = 1.0; + pl.model = "models/player.mdl"; + string mymodel = infokey(pl, "model"); + + if (mymodel) { + mymodel = sprintf("models/player/%s/%s.mdl", mymodel, mymodel); + if (whichpack(mymodel)) { + pl.model = mymodel; + } + } + setmodel(pl, pl.model); + + setsize(pl, VEC_HULL_MIN, VEC_HULL_MAX); + pl.view_ofs = VEC_PLAYER_VIEWPOS; + pl.velocity = [0,0,0]; + pl.gravity = __NULL__; + pl.frame = 1; + pl.SendFlags = UPDATE_ALL; + pl.customphysics = Empty; + pl.iBleeds = TRUE; + forceinfokey(pl, "*spec", "0"); + forceinfokey(pl, "*deaths", ftos(pl.deaths)); + + spot = Spawn_SelectRandom("info_player_deathmatch"); + setorigin(pl, spot.origin); + pl.angles = spot.angles; + + pl.g_items = ITEM_CROWBAR | ITEM_GLOCK | ITEM_SUIT; + pl.activeweapon = WEAPON_GLOCK; + pl.glock_mag = 18; + pl.ammo_9mm = 44; + Weapons_RefreshAmmo(pl); + SHData_GetItems(pl); + + Client_FixAngle(pl, pl.angles); +} + +void +SHMultiplayerRules::LevelDecodeParms(base_player pp) +{ + player pl = (player)pp; + g_landmarkpos[0] = parm1; + g_landmarkpos[1] = parm2; + g_landmarkpos[2] = parm3; + pl.angles[0] = parm4; + pl.angles[1] = parm5; + pl.angles[2] = parm6; + pl.velocity[0] = parm7; + pl.velocity[1] = parm8; + pl.velocity[2] = parm9; + pl.g_items = parm10; + pl.activeweapon = parm11; + pl.flags = parm64; + + pl.ammo_9mm = parm12; + pl.ammo_357 = parm13; + pl.ammo_buckshot = parm14; + pl.ammo_m203_grenade = parm15; + pl.ammo_bolt = parm16; + pl.ammo_rocket = parm17; + pl.ammo_uranium = parm18; + pl.ammo_handgrenade = parm19; + pl.ammo_satchel = parm20; + pl.ammo_tripmine = parm21; + pl.ammo_snark = parm22; + pl.ammo_hornet = parm23; + + pl.glock_mag = parm24; + pl.mp5_mag = parm25; + pl.python_mag = parm26; + pl.shotgun_mag = parm27; + pl.crossbow_mag = parm28; + pl.rpg_mag = parm29; + pl.satchel_chg = parm30; + + if (pl.flags & FL_CROUCHING) { + setsize(pl, VEC_CHULL_MIN, VEC_CHULL_MAX); + } else { + setsize(pl, VEC_HULL_MIN, VEC_HULL_MAX); + } +} + +void +SHMultiplayerRules::LevelChangeParms(base_player pp) +{ + player pl = (player)pp; + parm1 = g_landmarkpos[0]; + parm2 = g_landmarkpos[1]; + parm3 = g_landmarkpos[2]; + parm4 = pl.angles[0]; + parm5 = pl.angles[1]; + parm6 = pl.angles[2]; + parm7 = pl.velocity[0]; + parm8 = pl.velocity[1]; + parm9 = pl.velocity[2]; + parm64 = pl.flags; + parm10 = pl.g_items; + parm11 = pl.activeweapon; + parm12 = pl.ammo_9mm; + parm13 = pl.ammo_357; + parm14 = pl.ammo_buckshot; + parm15 = pl.ammo_m203_grenade; + parm16 = pl.ammo_bolt; + parm17 = pl.ammo_rocket; + parm18 = pl.ammo_uranium; + parm19 = pl.ammo_handgrenade; + parm20 = pl.ammo_satchel; + parm21 = pl.ammo_tripmine; + parm22 = pl.ammo_snark; + parm23 = pl.ammo_hornet; + parm24 = pl.glock_mag; + parm25 = pl.mp5_mag; + parm26 = pl.python_mag; + parm27 = pl.shotgun_mag; + parm28 = pl.crossbow_mag; + parm29 = pl.rpg_mag; + parm30 = pl.satchel_chg; +} + +void +SHMultiplayerRules::LevelNewParms(void) +{ + parm1 = parm2 = parm3 = parm4 = parm5 = parm6 = parm7 = + parm8 = parm9 = parm10 = parm11 = parm12 = parm13 = parm14 = + parm15 = parm16 = parm17 = parm18 = parm19 = parm20 = parm21 = + parm22 = parm23 = parm24 = parm25 = parm26 = parm27 = parm28 = + parm29 = parm30 = 0; + parm64 = FL_CLIENT; +} + +/* we check what fields have changed over the course of the frame and network + * only the ones that have actually changed */ +void +SHMultiplayerRules::PlayerPostFrame(base_player pp) +{ + player pl = (player)pp; + Animation_PlayerUpdate(); + + if (autocvar_sv_playerkeepalive) + pl.SendFlags |= PLAYER_KEEPALIVE; + + if (pl.old_modelindex != pl.modelindex) + pl.SendFlags |= PLAYER_MODELINDEX; + + if (pl.old_origin[0] != pl.origin[0]) + pl.SendFlags |= PLAYER_ORIGIN; + + if (pl.old_origin[1] != pl.origin[1]) + pl.SendFlags |= PLAYER_ORIGIN; + + if (pl.old_origin[2] != pl.origin[2]) + pl.SendFlags |= PLAYER_ORIGIN_Z; + + if (pl.old_angles[0] != pl.v_angle[0]) + pl.SendFlags |= PLAYER_ANGLES_X; + + if (pl.old_angles[1] != pl.angles[1]) + pl.SendFlags |= PLAYER_ANGLES_Y; + + if (pl.old_angles[2] != pl.angles[2]) + pl.SendFlags |= PLAYER_ANGLES_Z; + + if (pl.old_velocity[0] != pl.velocity[0]) + pl.SendFlags |= PLAYER_VELOCITY; + + if (pl.old_velocity[1] != pl.velocity[1]) + pl.SendFlags |= PLAYER_VELOCITY; + + if (pl.old_velocity[2] != pl.velocity[2]) + pl.SendFlags |= PLAYER_VELOCITY_Z; + + if (pl.old_flags != pl.flags) + pl.SendFlags |= PLAYER_FLAGS; + + if (pl.old_gflags != pl.gflags) + pl.SendFlags |= PLAYER_FLAGS; + + if (pl.old_activeweapon != pl.activeweapon) + pl.SendFlags |= PLAYER_WEAPON; + + if (pl.old_items != pl.g_items) + pl.SendFlags |= PLAYER_ITEMS; + + if (pl.old_health != pl.health) + pl.SendFlags |= PLAYER_HEALTH; + + if (pl.old_armor != pl.armor) + pl.SendFlags |= PLAYER_ARMOR; + + if (pl.old_movetype != pl.movetype) + pl.SendFlags |= PLAYER_MOVETYPE; + + if (pl.old_viewofs != pl.view_ofs[2]) + pl.SendFlags |= PLAYER_VIEWOFS; + + if (pl.old_baseframe != pl.baseframe) + pl.SendFlags |= PLAYER_BASEFRAME; + + if (pl.old_frame != pl.frame) + pl.SendFlags |= PLAYER_FRAME; + + if (pl.old_a_ammo1 != pl.a_ammo1) + pl.SendFlags |= PLAYER_AMMO1; + + if (pl.old_a_ammo2 != pl.a_ammo2) + pl.SendFlags |= PLAYER_AMMO2; + + if (pl.old_a_ammo3 != pl.a_ammo3) + pl.SendFlags |= PLAYER_AMMO3; + + pl.old_modelindex = pl.modelindex; + pl.old_origin = pl.origin; + pl.old_angles = pl.angles; + pl.old_angles[0] = pl.v_angle[0]; + pl.old_velocity = pl.velocity; + pl.old_flags = pl.flags; + pl.old_gflags = pl.gflags; + pl.old_activeweapon = pl.activeweapon; + pl.old_items = pl.g_items; + pl.old_health = pl.health; + pl.old_armor = pl.armor; + pl.old_movetype = pl.movetype; + pl.old_viewofs = pl.view_ofs[2]; + pl.old_baseframe = pl.baseframe; + pl.old_frame = pl.frame; + pl.old_a_ammo1 = pl.a_ammo1; + pl.old_a_ammo2 = pl.a_ammo2; + pl.old_a_ammo3 = pl.a_ammo3; + + pl.sh_insaneactive = bound(0.0f, pl.sh_insaneactive - frametime, pl.sh_insaneactive); + + if (pl.sh_insaneactive > 0.0f) + pl.flags |= FL_RESERVED1; + else { + if (pl.flags & FL_RESERVED1) { + bprint(PRINT_CHAT, sprintf("%s is no longer insane!\n", pl.netname)); + } + pl.flags &= ~FL_RESERVED1; + } +} + +void +SHMultiplayerRules::PlayerConnect(base_player pl) +{ + if (Plugin_PlayerConnect(pl) == FALSE) + bprint(PRINT_HIGH, sprintf("%s connected\n", pl.netname)); +} + +void +SHMultiplayerRules::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 +SHMultiplayerRules::PlayerKill(base_player pp) +{ + player pl = (player)pp; + Damage_Apply(pl, pl, pl.health, WEAPON_NONE, DMG_SKIP_ARMOR); +} + +void +SHMultiplayerRules::ScientistKill(base_player pp, entity sci) +{ + player pl = (player)pp; + /* obituary networking */ + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EV_OBITUARY); + WriteString(MSG_MULTICAST, pl.netname); + WriteString(MSG_MULTICAST, sci.netname); + WriteByte(MSG_MULTICAST, g_dmg_iWeapon); + WriteByte(MSG_MULTICAST, 0); + msg_entity = world; + multicast([0,0,0], MULTICAST_ALL); + pl.frags++; + + /*if (g_weapons[g_dmg_iWeapon].slot != 0) + return;*/ + + /* if this is our first kill in a while, or in the timer... */ + if (pl.sh_insanecount == 0 || pl.sh_insanetime > time) { + pl.sh_insanecount++; + } else { + pl.sh_insanecount = 0; + } + + if (pl.sh_insanecount >= autocvar_sh_insanity) { + if (pl.sh_insaneactive <= 0.0f) + bprint(PRINT_CHAT, sprintf("%s is going insane!\n", pl.netname)); + + pl.sh_insaneactive += 3.0f; + + if (pl.sh_insaneactive > 60) + pl.sh_insaneactive = 60; + } + + /* timer gets touched every time */ + pl.sh_insanetime = time + 2.0f; +} diff --git a/src/server/input.qc b/src/server/input.qc new file mode 100644 index 0000000..da1b426 --- /dev/null +++ b/src/server/input.qc @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* +================= +Input_Handle + +Handles impulse and whatnot +================= +*/ +void Game_Input(void) +{ + 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 == 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"); + } + + if (self.impulse == 103) { + for (entity a = world; (a = find(a, classname, "func_breakable"));) { + func_breakable caw = (func_breakable)a; + caw.Death(); + } + bprint(PRINT_HIGH, "BREAK EVERYTHING!\n"); + } + } + + self.impulse = 0; +} diff --git a/src/server/monster_scientist.qc b/src/server/monster_scientist.qc new file mode 100644 index 0000000..f33b627 --- /dev/null +++ b/src/server/monster_scientist.qc @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +var int autocvar_sh_scialert = FALSE; +var int autocvar_sh_scispeed = 40; +var int autocvar_sh_sciattack = TRUE; + + +enum +{ + SCIA_WALK, + SCIA_WALKSCARED, + SCIA_RUN, + SCIA_RUNSCARED, + SCIA_RUNLOOK, + SCIA_180LEFT, + SCIA_180RIGHT, + SCIA_FLINCH, + SCIA_PAIN, + SCIA_PAINLEFT, + SCIA_PAINRIGHT, + SCIA_PAINLEGLEFT, + SCIA_PAINLEGRIGHT, + SCIA_IDLE1, + SCIA_IDLE2, + SCIA_IDLE3, + SCIA_IDLE4, + SCIA_IDLE5, + SCIA_IDLE6, + SCIA_SCARED_END, + SCIA_SCARED1, + SCIA_SCARED2, + SCIA_SCARED3, + SCIA_SCARED4, + SCIA_PANIC, + SCIA_FEAR1, + SCIA_FEAR2, + SCIA_CRY, + SCIA_SCI1, + SCIA_SCI2, + SCIA_SCI3, + SCIA_DIE_SIMPLE, + SCIA_DIE_FORWARD1, + SCIA_DIE_FORWARD2, + SCIA_DIE_BACKWARD, + SCIA_DIE_HEADSHOT, + SCIA_DIE_GUTSHOT, + SCIA_LYING1, + SCIA_LYING2, + SCIA_DEADSIT, + SCIA_DEADTABLE1, + SCIA_DEADTABLE2, + SCIA_DEADTABLE3 +}; + +class monster_scientist:CBaseNPC +{ + void(void) monster_scientist; + + virtual void(void) Respawn; + virtual void(void) Pain; + virtual void(void) Death; + virtual void(void) OnPlayerUse; + virtual void(void) TalkPanic; + virtual int(void) AnimIdle; + virtual int(void) AnimWalk; + virtual int(void) AnimRun; + + virtual int(void) AttackMelee; + virtual void(void) AttackNeedle; +}; + +int +monster_scientist::AttackMelee(void) +{ + /* visual */ + AnimPlay(28); + m_flAttackThink = m_flAnimTime; + + float r = random(); + + if (r < 0.33) + Sentence("!SC_CUREA"); + else if (r < 0.66) + Sentence("!SC_CUREB"); + else + Sentence("!SC_CUREC"); + + /* functional */ + think = AttackNeedle; + nextthink = 0.25f; + return TRUE; +} + +void +monster_scientist::AttackNeedle(void) +{ + traceline(origin, m_eEnemy.origin, FALSE, this); + + if (trace_fraction >= 1.0 || trace_ent.takedamage != DAMAGE_YES) { + return; + } + + Damage_Apply(trace_ent, this, 25, 0, 0); + AnimPlay(30); +} + +int +monster_scientist::AnimIdle(void) +{ + return SCIA_IDLE1; +} + +int +monster_scientist::AnimWalk(void) +{ + return SCIA_WALK; +} + +int +monster_scientist::AnimRun(void) +{ + return SCIA_RUN; +} + +void +monster_scientist::TalkPanic(void) +{ + int r = floor(random(0,30)); + + switch (r) { + case 1: + Sentence("!SC_SCARED1"); //scientist/stopattacking + break; + case 2: + Sentence("!SC_SCARED2"); //scientist/youinsane + break; + case 3: + Sentence("!SC_PLFEAR0"); //scientist/whatyoudoing + break; + case 4: + Sentence("!SC_PLFEAR2"); //scientist/madness + break; + case 5: + Sentence("!SC_PLFEAR3"); //scientist/noplease + break; + case 6: + Sentence("!SC_PLFEAR4"); //scientist/getoutofhere + break; + case 7: + Sentence("!SC_FEAR3"); //scientist/dontwantdie + break; + case 8: + Sentence("!SC_FEAR4"); //scientist/getoutalive + break; + case 9: + Sentence("!SC_FEAR5"); //scientist/startle3 + break; + case 10: + Sentence("!SC_FEAR6"); //scientist/startle4 + break; + case 11: + Sentence("!SC_FEAR7"); //scientist/startle5 + break; + case 12: + Sentence("!SC_FEAR8"); //scientist/startle6 + break; + case 13: + Sentence("!SC_FEAR9"); //scientist/startle7 + break; + case 14: + Sentence("!SC_FEAR10"); //scientist/startle8 + break; + case 15: + Sentence("!SC_FEAR11"); //scientist/startle9 + break; + case 16: + Sentence("!SC_FEAR12"); //scientist/startle1 + break; + default: + Sentence("!SC_SCREAM"); //scientist/sci_fear15 + } + + m_flNextSentence = time + 2.0 + random(0,3); +} + +void +monster_scientist::Pain(void) +{ + if (autocvar_sh_sciattack) + CBaseMonster::Pain(); + + StartleAllies(); + + if (m_flAnimTime > time) { + return; + } + + if (random() < 0.25f) { + return; + } + + Sound_Speak(this, "monster_scientist.pain"); + + frame = SCIA_FLINCH + floor(random(0, 6)); + m_flAnimTime = time + 0.25f; + m_iFlags |= MONSTER_FEAR; +} + +void +monster_scientist::Death(void) +{ + WarnAllies(); + + if (style != MONSTER_DEAD) { + SHMultiplayerRules rules = (SHMultiplayerRules)g_grMode; + + if (g_dmg_eAttacker.flags & FL_CLIENT) + rules.ScientistKill((player)g_dmg_eAttacker, (entity)this); + + Plugin_PlayerObituary(g_dmg_eAttacker, this, g_dmg_iWeapon, g_dmg_iHitBody, g_dmg_iDamage); + + SetFrame(SCIA_DIE_SIMPLE + floor(random(0, 6))); + Sound_Speak(this, "monster_scientist.die"); + } + + /* now mark our state as 'dead' */ + CBaseNPC::Death(); +} + +void +monster_scientist::OnPlayerUse(void) +{ + if (m_iFlags & MONSTER_FEAR) + return; + + CBaseNPC::OnPlayerUse(); +} + +void +monster_scientist::Respawn(void) +{ + CBaseNPC::Respawn(); + m_iFlags |= MONSTER_CANFOLLOW; + PlayerUse = OnPlayerUse; + + if (autocvar_sh_scialert) { + m_iFlags |= MONSTER_FEAR; + } +} + +void +monster_scientist::monster_scientist(void) +{ + spawnflags |= MSF_MULTIPLAYER; + + Sound_Precache("monster_scientist.die"); + Sound_Precache("monster_scientist.pain"); + + m_iBody = -1; + for (int i = 1; i < (tokenize(__fullspawndata)-1); i += 2) { + switch (argv(i)) { + case "body": + m_iBody = stoi(argv(i+1)) + 1; + break; + default: + break; + } + } + + m_talkAsk = "!SC_QUESTION"; + m_talkPlayerAsk = "!SC_PQUEST"; + m_talkPlayerGreet = "!SC_PHELLO"; + m_talkPlayerIdle = "!SC_PIDLE"; + m_talkAnswer = "!SC_ANSWER"; + m_talkAllyShot = "!SC_PLFEAR"; + m_talkGreet = ""; + m_talkIdle = "!SC_IDLE"; + m_talkHearing = "!SC_HEAR"; + m_talkSmelling = "!SC_SMELL"; + m_talkStare = "!SC_STARE"; + m_talkSurvived = "!SC_WOUND"; + m_talkWounded = "!SC_MORTAL"; + m_talkUnfollow = "!SC_WAIT"; + m_talkFollow = "!SC_OK"; + m_talkStopFollow = "!SC_STOP"; + + m_iBody = -1; + + if (autocvar_sh_sciattack) + m_iAlliance = MAL_ALIEN; + + model = "models/scientist.mdl"; + base_mins = [-16,-16,0]; + base_maxs = [16,16,72]; + CBaseNPC::CBaseNPC(); + precache_model(m_oldModel); + base_health = Skill_GetValue("scientist_health", 20); + + if (m_iBody == -1) { + /* This stuff needs to be persistent because we can't guarantee that + * the client-side geomset refresh happens. Don't shove this into Respawn */ + m_iBody = floor(random(1,5)); + } + + switch (m_iBody) { + case 1: + m_flPitch = 105; + netname = "Walter"; + break; + case 2: + m_flPitch = 100; + netname = "Einstein"; + break; + case 3: + m_flPitch = 95; + netname = "Luther"; + skin = 1; + break; + default: + m_flPitch = 100; + netname = "Slick"; + } +} diff --git a/src/server/progs.src b/src/server/progs.src new file mode 100755 index 0000000..f9f17ce --- /dev/null +++ b/src/server/progs.src @@ -0,0 +1,89 @@ +#pragma target fte +#pragma progs_dat "../../progs.dat" + +#define QWSSQC +#define SERVER +#define VALVE +#define SCIHUNT +#define GS_RENDERFX + +#includelist +../../../src/shared/fteextensions.qc +../../../src/gs-entbase/server/defs.h +../../../src/shared/defs.h +../../../src/server/defs.h + +../../../src/gs-entbase/server.src +../../../src/gs-entbase/shared.src +../shared/include.src + +defs.h + +../../../valve/src/server/monster_apache.qc +../../../valve/src/server/monster_alien_controller.qc +../../../valve/src/server/monster_alien_grunt.qc +../../../valve/src/server/monster_alien_slave.qc +../../../valve/src/server/monster_barnacle.qc +../../../valve/src/server/monster_barney.qc +../../../valve/src/server/monster_barney_dead.qc +../../../valve/src/server/monster_bigmomma.qc +../../../valve/src/server/monster_bloater.qc +../../../valve/src/server/monster_bullchicken.qc +../../../valve/src/server/monster_cockroach.qc +../../../valve/src/server/monster_flyer_flock.qc +../../../valve/src/server/monster_gargantua.qc +../../../valve/src/server/monster_gman.qc +../../../valve/src/server/monster_headcrab.qc +../../../valve/src/server/monster_babycrab.qc +../../../valve/src/server/monster_hevsuit_dead.qc +../../../valve/src/server/monster_houndeye.qc +../../../valve/src/server/monster_human_grunt.qc +../../../valve/src/server/monster_hgrunt_dead.qc +../../../valve/src/server/monster_human_assassin.qc +../../../valve/src/server/monster_ichthyosaur.qc +../../../valve/src/server/monster_leech.qc +../../../valve/src/server/monster_miniturret.qc +../../../valve/src/server/monster_nihilanth.qc +../../../valve/src/server/monster_osprey.qc +../../../valve/src/server/monster_rat.qc +../../../valve/src/server/monster_scientist_dead.qc +../../../valve/src/server/monster_sitting_scientist.qc +../../../valve/src/server/monster_sentry.qc +../../../valve/src/server/monster_tentacle.qc +../../../valve/src/server/monster_turret.qc +../../../valve/src/server/monster_zombie.qc +monster_scientist.qc + +../../../valve/src/server/player.qc +../../../valve/src/server/spectator.qc +../../../valve/src/server/items.qc +../../../valve/src/server/item_longjump.qc +../../../valve/src/server/item_suit.qc +../../../valve/src/server/item_healthkit.qc +../../../valve/src/server/item_battery.qc +../../../valve/src/server/item_weaponbox.qc +../../../valve/src/server/world_items.qc +../../../valve/src/server/xen_spore_small.qc +../../../valve/src/server/xen_spore_medium.qc +../../../valve/src/server/xen_spore_large.qc +../../../valve/src/server/xen_hair.qc +../../../valve/src/server/xen_plantlight.qc +../../../valve/src/server/ammo.qc + +shdata_parse.qc +gamerules.qc +../../../valve/src/server/client.qc +server.qc +../../../valve/src/server/damage.qc +../../../valve/src/server/rules.qc +../../../valve/src/server/flashlight.qc +../../../base/src/server/modelevent.qc + +../../../src/botlib/include.src + +input.qc +../../../valve/src/server/spawn.qc + +../../../src/server/include.src +../../../src/shared/include.src +#endlist diff --git a/src/server/server.qc b/src/server/server.qc new file mode 100644 index 0000000..d600c1f --- /dev/null +++ b/src/server/server.qc @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +void +Game_InitRules(void) +{ + g_grMode = spawn(SHMultiplayerRules); +} + +void Game_Worldspawn(void) +{ + precache_model("models/player.mdl"); + precache_model("models/w_weaponbox.mdl"); + Sound_Precache("player.die"); + Sound_Precache("player.fall"); + Sound_Precache("player.lightfall"); + + Player_Precache(); + Weapons_Init(); + SHData_Parse(mapname); +} diff --git a/src/server/shdata_parse.qc b/src/server/shdata_parse.qc new file mode 100644 index 0000000..90f07b3 --- /dev/null +++ b/src/server/shdata_parse.qc @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* If only origin parameters are applied, make something up */ +var int autocvar_sh_sciyaw = TRUE; + +/* Limit the amount of scientists spawned */ +var int autocvar_sh_scimax = 30; + +void SHData_SpawnScientist(void) +{ + static int slimit = 0; + if (autocvar_sh_scimax) { + if (slimit >= autocvar_sh_scimax) { + dprint("shdata: scientist limit hit. ignored\n"); + remove(self); + return; + } + slimit++; + } + spawnfunc_monster_scientist(); +} + +void SHData_New(void) +{ + entity new = spawn(); + new.origin[0] = stof(argv(1)); + new.origin[1] = stof(argv(2)); + new.origin[2] = stof(argv(3)); + + if (autocvar_sh_sciyaw) { + new.angles[1] = Math_FixDelta(random(0,360)); + } + + setorigin(new, new.origin); + + entity oldself = self; + self = new; + + if (argv(0) == "monster_scientist") { + SHData_SpawnScientist(); + } else if (argv(0) == "info_player_team1") { + self.classname = "info_player_deathmatch"; + } else if (argv(0) == "info_player_team2") { + self.classname = "info_player_deathmatch"; + } + self = oldself; +} + +void SHData_NewAngles(void) +{ + entity new = spawn(); + new.origin[0] = stof(argv(1)); + new.origin[1] = stof(argv(2)); + new.origin[2] = stof(argv(3)); + new.angles[0] = stof(argv(4)); + new.angles[1] = stof(argv(5)); + new.angles[2] = stof(argv(6)); + setorigin(new, new.origin); + + entity oldself = self; + self = new; + + if (argv(0) == "monster_scientist") { + SHData_SpawnScientist(); + } else if (argv(0) == "info_player_team1") { + self.classname = "info_player_deathmatch"; + } else if (argv(0) == "info_player_team2") { + self.classname = "info_player_deathmatch"; + } + self = oldself; +} + +void SHData_GetItems(player pl) +{ + /* TODO: Parse the config files */ + pl.ammo_9mm = 68; + pl.ammo_buckshot = 34; + pl.ammo_bolt = 50; + pl.ammo_357 = 36; + pl.ammo_m203_grenade = 3; + pl.ammo_rocket = 5; + pl.ammo_snark = 10; + Weapons_AddItem(pl, WEAPON_CROWBAR, -1); + Weapons_AddItem(pl, WEAPON_GLOCK, -1); + Weapons_AddItem(pl, WEAPON_PYTHON, -1); + Weapons_AddItem(pl, WEAPON_MP5, -1); + Weapons_AddItem(pl, WEAPON_SHOTGUN, -1); + Weapons_AddItem(pl, WEAPON_CROSSBOW, -1); + Weapons_AddItem(pl, WEAPON_RPG, -1); + Weapons_AddItem(pl, WEAPON_GAUSS, -1); + Weapons_AddItem(pl, WEAPON_EGON, -1); + Weapons_AddItem(pl, WEAPON_HORNETGUN, -1); + Weapons_AddItem(pl, WEAPON_HANDGRENADE, -1); + Weapons_AddItem(pl, WEAPON_SATCHEL, -1); + Weapons_AddItem(pl, WEAPON_TRIPMINE, -1); + Weapons_AddItem(pl, WEAPON_SNARK, -1); + Weapons_AddItem(pl, WEAPON_CANNON, -1); + Weapons_AddItem(pl, WEAPON_CHAINSAW, -1); + Weapons_AddItem(pl, WEAPON_HAMMER, -1); +} + +void SHData_Parse(string map) +{ + int c; + string temp; + filestream shdfile = fopen(sprintf("SH_Data/%s.shd", map), FILE_READ); + + if (shdfile < 0) { + dprint(sprintf("^1WARNING^7: Could not load SH_Data/%s.shd\n", map)); + return; + } + + if (shdfile >= 0) { + while ((temp = fgets(shdfile))) { + c = tokenize(temp); + + if (c == 4) { + SHData_New(); + } else if (c == 7) { + SHData_NewAngles(); + } + } + } +} diff --git a/src/shared/include.src b/src/shared/include.src new file mode 100644 index 0000000..66a9dcb --- /dev/null +++ b/src/shared/include.src @@ -0,0 +1,39 @@ + #includelist +../../../valve/src/shared/entities.h +../../../valve/src/shared/flags.h +player.qc +../../../valve/src/shared/weapon_common.h +../../../valve/src/shared/animations.h +../../../valve/src/shared/animations.qc +pmove.qc +../../../valve/src/shared/pmove_water.qc + +../../../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 +../../../valve/src/shared/fx_impact.qc + +items.h +weapons.h +../../../valve/src/shared/w_crossbow.qc +../../../valve/src/shared/w_crowbar.qc +../../../valve/src/shared/w_egon.qc +../../../valve/src/shared/w_gauss.qc +../../../valve/src/shared/w_glock.qc +../../../valve/src/shared/w_handgrenade.qc +../../../valve/src/shared/w_hornetgun.qc +../../../valve/src/shared/w_mp5.qc +../../../valve/src/shared/w_python.qc +../../../valve/src/shared/w_rpg.qc +../../../valve/src/shared/w_satchel.qc +../../../valve/src/shared/w_shotgun.qc +../../../valve/src/shared/w_snark.qc +../../../valve/src/shared/w_tripmine.qc +w_cannon.qc +w_chainsaw.qc +w_hammer.qc +weapons.qc +../../../valve/src/shared/weapon_common.qc +#endlist diff --git a/src/shared/items.h b/src/shared/items.h new file mode 100644 index 0000000..f93abcf --- /dev/null +++ b/src/shared/items.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define ITEM_CROWBAR 0x00000001 +#define ITEM_GLOCK 0x00000002 +#define ITEM_PYTHON 0x00000004 +#define ITEM_MP5 0x00000008 +#define ITEM_CROSSBOW 0x00000010 +#define ITEM_SHOTGUN 0x00000020 +#define ITEM_RPG 0x00000040 +#define ITEM_GAUSS 0x00000080 + +#define ITEM_EGON 0x00000100 +#define ITEM_HORNETGUN 0x00000200 +#define ITEM_HANDGRENADE 0x00000400 +#define ITEM_TRIPMINE 0x00000800 +#define ITEM_SATCHEL 0x00001000 +#define ITEM_SNARK 0x00002000 +#define ITEM_SUIT 0x00004000 +#define ITEM_LONGJUMP 0x00008000 + +#define ITEM_HEALTHKIT 0x00010000 +#define ITEM_BATTERY 0x00020000 +#define ITEM_CANNON 0x00040000 +#define ITEM_CHAINSAW 0x00080000 +#define ITEM_HAMMER 0x00100000 +#define ITEM_UNUSED22 0x00200000 +#define ITEM_UNUSED23 0x00400000 +#define ITEM_UNUSED24 0x00800000 + +#define ITEM_UNUSED25 0x01000000 +#define ITEM_UNUSED26 0x02000000 +#define ITEM_UNUSED27 0x04000000 +#define ITEM_UNUSED28 0x08000000 +#define ITEM_UNUSED29 0x10000000 +#define ITEM_UNUSED30 0x20000000 +#define ITEM_UNUSED31 0x40000000 +#define ITEM_UNUSED32 0x80000000 diff --git a/src/shared/player.qc b/src/shared/player.qc new file mode 100644 index 0000000..9e64dd9 --- /dev/null +++ b/src/shared/player.qc @@ -0,0 +1,710 @@ +/* + * Copyright (c) 2016-2021 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* all potential SendFlags bits we can possibly send */ +enumflags +{ + PLAYER_KEEPALIVE, + PLAYER_MODELINDEX, + PLAYER_ORIGIN, + PLAYER_ORIGIN_Z, + PLAYER_ANGLES_X, + PLAYER_ANGLES_Y, + PLAYER_ANGLES_Z, + PLAYER_VELOCITY, + PLAYER_VELOCITY_Z, + PLAYER_FLAGS, + PLAYER_WEAPON, + PLAYER_ITEMS, + PLAYER_HEALTH, + PLAYER_ARMOR, + PLAYER_MOVETYPE, + PLAYER_VIEWOFS, + PLAYER_BASEFRAME, + PLAYER_FRAME, + PLAYER_AMMO1, + PLAYER_AMMO2, + PLAYER_AMMO3, + PLAYER_UNUSED1, + PLAYER_UNUSED2 +}; + +/* ammo 1 type updates */ +enumflags +{ + AMMO1_GLOCK, + AMMO1_MP5, + AMMO1_PYTHON, + AMMO1_SHOTGUN, + AMMO1_CROSSBOW, + AMMO1_RPG, + AMMO1_SATCHEL, + AMMO1_CANNON +}; + +/* ammo 2 type updates */ +enumflags +{ + AMMO2_9MM, + AMMO2_357, + AMMO2_BUCKSHOT, + AMMO2_BOLT, + AMMO2_ROCKET, + AMMO2_URANIUM, + AMMO2_HANDGRENADE, + AMMO2_SATCHEL, + AMMO2_TRIPMINE, + AMMO2_SNARK, + AMMO2_HORNET +}; + +enumflags +{ + AMMO3_M203_GRENADE, + AMMO3_SHOTGUN_STATE, + AMMO3_GAUSS_STATE, + AMMO3_GAUSS_VOLUME, + AMMO3_EGON_STATE, + AMMO3_RPG_STATE, + AMMO3_HANDGRENADE_STATE, + AMMO3_CHAINSAW_STATE, + AMMO3_HAMMER_STATE +}; + +noref int input_sequence; +class player:base_player +{ + /* Weapon specific */ + int glock_mag; + int glock_mag_net; + int mp5_mag; + int mp5_mag_net; + int python_mag; + int python_mag_net; + int shotgun_mag; + int shotgun_mag_net; + int crossbow_mag; + int crossbow_mag_net; + int rpg_mag; + int rpg_mag_net; + int satchel_chg; + int satchel_chg_net; + int cannon_mag; + int cannon_mag_net; + + int ammo_9mm; + int ammo_9mm_net; + int ammo_357; + int ammo_357_net; + int ammo_buckshot; + int ammo_buckshot_net; + int ammo_bolt; + int ammo_bolt_net; + int ammo_rocket; + int ammo_rocket_net; + int ammo_uranium; + int ammo_uranium_net; + int ammo_handgrenade; + int ammo_handgrenade_net; + int ammo_satchel; + int ammo_satchel_net; + int ammo_tripmine; + int ammo_tripmine_net; + int ammo_snark; + int ammo_snark_net; + int ammo_hornet; + int ammo_hornet_net; + + int ammo_m203_grenade; + int ammo_m203_grenade_net; + int ammo_shotgun_state; + int ammo_shotgun_state_net; + int ammo_gauss_state; + int ammo_gauss_state_net; + int ammo_gauss_volume; + int ammo_gauss_volume_net; + int ammo_egon_state; + int ammo_egon_state_net; + int ammo_rpg_state; + int ammo_rpg_state_net; + int mode_tempstate; + int mode_tempstate_net; + int ammo_chainsaw_state; + int ammo_chainsaw_state_net; + int ammo_hammer_state; + int ammo_hammer_state_net; + +#ifdef CLIENT + /* External model */ + entity p_model; + int p_hand_bone; + int p_model_bone; + float lastweapon; + + virtual void(void) gun_offset; + virtual void(void) draw; + virtual float() predraw; + virtual void(void) postdraw; + virtual void(float) ReceiveEntity; + virtual void(void) PredictPreFrame; + virtual void(void) PredictPostFrame; +#else + virtual void(void) EvaluateEntity; + virtual float(entity, float) SendEntity; + + int sh_insanecount; + float sh_insanetime; + float sh_insaneactive; +#endif +}; + +#ifdef CLIENT +void Weapons_AmmoUpdate(entity); +/* +================= +player::ReceiveEntity +================= +*/ +void +player::ReceiveEntity(float new) +{ + float fl; + if (new == FALSE) { + /* Go through all the physics code between the last received frame + * and the newest frame and keep the changes this time around instead + * of rolling back, because we'll apply the new server-verified values + * right after anyway. */ + /* FIXME: splitscreen */ + if (entnum == player_localentnum) { + /* FIXME: splitscreen */ + pSeat = &g_seats[0]; + + for (int i = sequence+1; i <= servercommandframe; i++) { + /* ...maybe the input state is too old? */ + if (!getinputstate(i)) { + break; + } + input_sequence = i; + PMove_Run(); + } + + /* any differences in things that are read below are now + * officially from prediction misses. */ + } + } + + /* seed for our prediction table */ + sequence = servercommandframe; + + fl = readfloat(); + + /* HACK: we need to make this more reliable */ + if (fl == UPDATE_ALL) { + /* we respawned */ + gravity = __NULL__; + } + + if (fl & PLAYER_MODELINDEX) + modelindex = readshort(); + + if (fl & PLAYER_ORIGIN) { + origin[0] = readcoord(); + origin[1] = readcoord(); + } + + if (fl & PLAYER_ORIGIN_Z) + origin[2] = readcoord(); + if (fl & PLAYER_ANGLES_X) + pitch = readfloat(); + if (fl & PLAYER_ANGLES_Y) + angles[1] = readfloat(); + if (fl & PLAYER_ANGLES_Z) + angles[2] = readfloat(); + + if (fl & PLAYER_VELOCITY) { + velocity[0] = readcoord(); + velocity[1] = readcoord(); + } + + if (fl & PLAYER_VELOCITY_Z) + velocity[2] = readcoord(); + if (fl & PLAYER_FLAGS) { + flags = readfloat(); + gflags = readfloat(); + } + if (fl & PLAYER_WEAPON) + activeweapon = readbyte(); + if (fl & PLAYER_ITEMS) + g_items = (__variant)readfloat(); + if (fl & PLAYER_HEALTH) + health = readbyte(); + if (fl & PLAYER_ARMOR) + armor = readbyte(); + if (fl & PLAYER_MOVETYPE) + movetype = readbyte(); + if (fl & PLAYER_VIEWOFS) + view_ofs[2] = readfloat(); + if (fl & PLAYER_BASEFRAME) + baseframe = readbyte(); + if (fl & PLAYER_FRAME) { + frame = readbyte(); + frame1time = 0.0f; + frame2time = 0.0f; + } + + if (fl & PLAYER_AMMO1) { + glock_mag = readbyte(); + mp5_mag = readbyte(); + python_mag = readbyte(); + shotgun_mag = readbyte(); + crossbow_mag = readbyte(); + rpg_mag = readbyte(); + satchel_chg = readbyte(); + cannon_mag = readbyte(); + } + + if (fl & PLAYER_AMMO2) { + ammo_9mm = readbyte(); + ammo_357 = readbyte(); + ammo_buckshot = readbyte(); + ammo_bolt = readbyte(); + ammo_rocket = readbyte(); + ammo_uranium = readbyte(); + ammo_handgrenade = readbyte(); + ammo_satchel = readbyte(); + ammo_tripmine = readbyte(); + ammo_snark = readbyte(); + ammo_hornet = readbyte(); + } + + if (fl & PLAYER_AMMO3) { + ammo_m203_grenade = readbyte(); + ammo_shotgun_state = readbyte(); + ammo_gauss_state = readbyte(); + ammo_gauss_volume = readbyte(); + ammo_egon_state = readbyte(); + ammo_rpg_state = readbyte(); + mode_tempstate = readbyte(); + ammo_chainsaw_state = readbyte(); + ammo_hammer_state = readbyte(); + } + + if (fl & PLAYER_AMMO1 || fl & PLAYER_AMMO2 || fl & PLAYER_AMMO3) + Weapons_AmmoUpdate(this); + + setorigin(this, origin); +} + +/* +================= +player::PredictPostFrame + +Save the last valid server values away in the _net variants of each field +so we can roll them back later. +================= +*/ +void +player::PredictPreFrame(void) +{ + glock_mag_net = glock_mag; + mp5_mag_net = mp5_mag; + python_mag_net = python_mag; + shotgun_mag_net = shotgun_mag; + crossbow_mag_net = crossbow_mag; + rpg_mag_net = rpg_mag; + satchel_chg_net = satchel_chg; + cannon_mag_net = cannon_mag; + ammo_9mm_net = ammo_9mm; + ammo_357_net = ammo_357; + ammo_buckshot_net = ammo_buckshot; + ammo_bolt_net = ammo_bolt; + ammo_rocket_net = ammo_rocket; + ammo_uranium_net = ammo_uranium; + ammo_handgrenade_net = ammo_handgrenade; + ammo_satchel_net = ammo_satchel; + ammo_tripmine_net = ammo_tripmine; + ammo_snark_net = ammo_snark; + ammo_hornet_net = ammo_hornet; + + ammo_m203_grenade_net = ammo_m203_grenade; + ammo_shotgun_state_net = ammo_shotgun_state; + ammo_gauss_state_net = ammo_gauss_state; + ammo_gauss_volume_net = ammo_gauss_volume; + ammo_egon_state_net = ammo_egon_state; + ammo_rpg_state_net = ammo_rpg_state; + mode_tempstate_net = mode_tempstate; + ammo_chainsaw_state_net = ammo_chainsaw_state; + ammo_hammer_state_net = ammo_hammer_state; +} + +/* +================= +player::PredictPostFrame + +Where we roll back our values to the ones last sent/verified by the server. +================= +*/ +void +player::PredictPostFrame(void) +{ + glock_mag = glock_mag_net; + mp5_mag = mp5_mag_net; + python_mag = python_mag_net; + shotgun_mag = shotgun_mag_net; + crossbow_mag = crossbow_mag_net; + rpg_mag = rpg_mag_net; + satchel_chg = satchel_chg_net; + cannon_mag = cannon_mag_net; + + ammo_9mm = ammo_9mm_net; + ammo_357 = ammo_357_net; + ammo_buckshot = ammo_buckshot_net; + ammo_m203_grenade = ammo_m203_grenade_net; + ammo_bolt = ammo_bolt_net; + ammo_rocket = ammo_rocket_net; + ammo_uranium = ammo_uranium_net; + ammo_handgrenade = ammo_handgrenade_net; + ammo_satchel = ammo_satchel_net; + ammo_tripmine = ammo_tripmine_net; + ammo_snark = ammo_snark_net; + ammo_hornet = ammo_hornet_net; + + ammo_m203_grenade = ammo_m203_grenade_net; + ammo_shotgun_state = ammo_shotgun_state_net; + ammo_gauss_state = ammo_gauss_state_net; + ammo_gauss_volume = ammo_gauss_volume_net; + ammo_egon_state = ammo_egon_state_net; + ammo_rpg_state = ammo_rpg_state_net; + mode_tempstate = mode_tempstate_net; + ammo_chainsaw_state = ammo_chainsaw_state_net; + ammo_hammer_state = ammo_hammer_state_net; +} + +#else +void +player::EvaluateEntity(void) +{ + SendFlags |= PLAYER_KEEPALIVE; + + if (old_modelindex != modelindex) + SendFlags |= PLAYER_MODELINDEX; + + if (old_origin[0] != origin[0]) + SendFlags |= PLAYER_ORIGIN; + + if (old_origin[1] != origin[1]) + SendFlags |= PLAYER_ORIGIN; + + if (old_origin[2] != origin[2]) + SendFlags |= PLAYER_ORIGIN_Z; + + if (old_angles[0] != v_angle[0]) + SendFlags |= PLAYER_ANGLES_X; + + if (old_angles[1] != angles[1]) + SendFlags |= PLAYER_ANGLES_Y; + + if (old_angles[2] != angles[2]) + SendFlags |= PLAYER_ANGLES_Z; + + if (old_velocity[0] != velocity[0]) + SendFlags |= PLAYER_VELOCITY; + + if (old_velocity[1] != velocity[1]) + SendFlags |= PLAYER_VELOCITY; + + if (old_velocity[2] != velocity[2]) + SendFlags |= PLAYER_VELOCITY_Z; + + if (old_flags != flags) + SendFlags |= PLAYER_FLAGS; + + if (old_gflags != gflags) + SendFlags |= PLAYER_FLAGS; + + if (old_activeweapon != activeweapon) + SendFlags |= PLAYER_WEAPON; + + if (old_items != g_items) + SendFlags |= PLAYER_ITEMS; + + if (old_health != health) + SendFlags |= PLAYER_HEALTH; + + if (old_armor != armor) + SendFlags |= PLAYER_ARMOR; + + if (old_movetype != movetype) + SendFlags |= PLAYER_MOVETYPE; + + if (old_viewofs != view_ofs[2]) + SendFlags |= PLAYER_VIEWOFS; + + if (old_baseframe != baseframe) + SendFlags |= PLAYER_BASEFRAME; + + if (old_frame != frame) + SendFlags |= PLAYER_FRAME; + + /* ammo 1 type updates */ + if (glock_mag != glock_mag_net) { + SendFlags |= PLAYER_AMMO1; + } + if (mp5_mag != mp5_mag_net) { + SendFlags |= PLAYER_AMMO1; + } + if (python_mag != python_mag_net) { + SendFlags |= PLAYER_AMMO1; + } + if (shotgun_mag != shotgun_mag_net) { + SendFlags |= PLAYER_AMMO1; + } + if (crossbow_mag != crossbow_mag_net) { + SendFlags |= PLAYER_AMMO1; + } + if (rpg_mag != rpg_mag_net) { + SendFlags |= PLAYER_AMMO1; + } + if (satchel_chg != satchel_chg_net) { + SendFlags |= PLAYER_AMMO1; + } + if (cannon_mag != cannon_mag_net) { + SendFlags |= PLAYER_AMMO1; + } + + /* ammo 2 type updates */ + if (ammo_9mm != ammo_9mm_net) { + SendFlags |= PLAYER_AMMO2; + } + if (ammo_357 != ammo_357_net) { + SendFlags |= PLAYER_AMMO2; + } + if (ammo_buckshot != ammo_buckshot_net) { + SendFlags |= PLAYER_AMMO2; + } + if (ammo_bolt != ammo_bolt_net) { + SendFlags |= PLAYER_AMMO2; + } + if (ammo_rocket != ammo_rocket_net) { + SendFlags |= PLAYER_AMMO2; + } + if (ammo_uranium != ammo_uranium_net) { + SendFlags |= PLAYER_AMMO2; + } + if (ammo_handgrenade != ammo_handgrenade_net) { + SendFlags |= PLAYER_AMMO2; + } + if (ammo_satchel != ammo_satchel_net) { + SendFlags |= PLAYER_AMMO2; + } + if (ammo_tripmine != ammo_tripmine_net) { + SendFlags |= PLAYER_AMMO2; + } + if (ammo_snark != ammo_snark_net) { + SendFlags |= PLAYER_AMMO2; + } + if (ammo_hornet != ammo_hornet_net) { + SendFlags |= PLAYER_AMMO2; + } + + if (ammo_m203_grenade != ammo_m203_grenade_net) { + SendFlags |= PLAYER_AMMO3; + } + if (ammo_shotgun_state != ammo_shotgun_state_net) { + SendFlags |= PLAYER_AMMO3; + } + if (ammo_gauss_state != ammo_gauss_state_net) { + SendFlags |= PLAYER_AMMO3; + } + if (ammo_gauss_volume != ammo_gauss_volume_net) { + SendFlags |= PLAYER_AMMO3; + } + if (ammo_egon_state != ammo_egon_state_net) { + SendFlags |= PLAYER_AMMO3; + } + if (ammo_rpg_state != ammo_rpg_state_net) { + SendFlags |= PLAYER_AMMO3; + } + if (mode_tempstate != mode_tempstate_net) { + SendFlags |= PLAYER_AMMO3; + } + if (ammo_chainsaw_state != ammo_chainsaw_state_net) { + SendFlags |= PLAYER_AMMO3; + } + if (ammo_hammer_state != ammo_hammer_state_net) { + SendFlags |= PLAYER_AMMO3; + } + + old_modelindex = modelindex; + old_origin = origin; + old_angles = angles; + old_angles[0] = v_angle[0]; + old_velocity = velocity; + old_flags = flags; + old_gflags = gflags; + old_activeweapon = activeweapon; + old_items = g_items; + old_health = health; + old_armor = armor; + old_movetype = movetype; + old_viewofs = view_ofs[2]; + old_baseframe = baseframe; + old_frame = frame; + + glock_mag_net = glock_mag; + mp5_mag_net = mp5_mag; + python_mag_net = python_mag; + shotgun_mag_net = shotgun_mag; + crossbow_mag_net = crossbow_mag; + rpg_mag_net = rpg_mag; + satchel_chg_net = satchel_chg; + cannon_mag_net = cannon_mag; + + ammo_9mm_net = ammo_9mm; + ammo_357_net = ammo_357; + ammo_buckshot_net = ammo_buckshot; + ammo_m203_grenade_net = ammo_m203_grenade; + ammo_bolt_net = ammo_bolt; + ammo_rocket_net = ammo_rocket; + ammo_uranium_net = ammo_uranium; + ammo_handgrenade_net = ammo_handgrenade; + ammo_satchel_net = ammo_satchel; + ammo_tripmine_net = ammo_tripmine; + ammo_snark_net = ammo_snark; + ammo_hornet_net = ammo_hornet; + + ammo_m203_grenade_net = ammo_m203_grenade; + ammo_shotgun_state_net = ammo_shotgun_state; + ammo_gauss_state_net = ammo_gauss_state; + ammo_gauss_volume_net = ammo_gauss_volume; + ammo_egon_state_net = ammo_egon_state; + ammo_rpg_state_net = ammo_rpg_state; + mode_tempstate_net = mode_tempstate; + ammo_chainsaw_state_net = ammo_chainsaw_state; + ammo_hammer_state_net = ammo_hammer_state; +} + +/* +================= +player::SendEntity +================= +*/ +float +player::SendEntity(entity ePEnt, float fChanged) +{ + if (health <= 0 && ePEnt != this) { + return FALSE; + } + + if (clienttype(ePEnt) != CLIENTTYPE_REAL) { + return FALSE; + } + + if (ePEnt != self) { + fChanged &= ~PLAYER_ITEMS; + fChanged &= ~PLAYER_HEALTH; + fChanged &= ~PLAYER_ARMOR; + fChanged &= ~PLAYER_VIEWOFS; + fChanged &= ~PLAYER_AMMO1; + fChanged &= ~PLAYER_AMMO2; + fChanged &= ~PLAYER_AMMO3; + } + + WriteByte(MSG_ENTITY, ENT_PLAYER); + WriteFloat(MSG_ENTITY, fChanged); + + /* really trying to get our moneys worth with 23 bits of mantissa */ + if (fChanged & PLAYER_MODELINDEX) + WriteShort(MSG_ENTITY, modelindex); + if (fChanged & PLAYER_ORIGIN) { + WriteCoord(MSG_ENTITY, origin[0]); + WriteCoord(MSG_ENTITY, origin[1]); + } + if (fChanged & PLAYER_ORIGIN_Z) + WriteCoord(MSG_ENTITY, origin[2]); + if (fChanged & PLAYER_ANGLES_X) + WriteFloat(MSG_ENTITY, v_angle[0]); + if (fChanged & PLAYER_ANGLES_Y) + WriteFloat(MSG_ENTITY, angles[1]); + if (fChanged & PLAYER_ANGLES_Z) + WriteFloat(MSG_ENTITY, angles[2]); + if (fChanged & PLAYER_VELOCITY) { + WriteCoord(MSG_ENTITY, velocity[0]); + WriteCoord(MSG_ENTITY, velocity[1]); + } + if (fChanged & PLAYER_VELOCITY_Z) + WriteCoord(MSG_ENTITY, velocity[2]); + if (fChanged & PLAYER_FLAGS) { + WriteFloat(MSG_ENTITY, flags); + WriteFloat(MSG_ENTITY, gflags); + } + if (fChanged & PLAYER_WEAPON) + WriteByte(MSG_ENTITY, activeweapon); + if (fChanged & PLAYER_ITEMS) + WriteFloat(MSG_ENTITY, (__variant)g_items); + if (fChanged & PLAYER_HEALTH) + WriteByte(MSG_ENTITY, bound(0, health, 255)); + if (fChanged & PLAYER_ARMOR) + WriteByte(MSG_ENTITY, armor); + if (fChanged & PLAYER_MOVETYPE) + WriteByte(MSG_ENTITY, movetype); + if (fChanged & PLAYER_VIEWOFS) + WriteFloat(MSG_ENTITY, view_ofs[2]); + if (fChanged & PLAYER_BASEFRAME) + WriteByte(MSG_ENTITY, baseframe); + if (fChanged & PLAYER_FRAME) + WriteByte(MSG_ENTITY, frame); + + if (fChanged & PLAYER_AMMO1) { + WriteByte(MSG_ENTITY, glock_mag); + WriteByte(MSG_ENTITY, mp5_mag); + WriteByte(MSG_ENTITY, python_mag); + WriteByte(MSG_ENTITY, shotgun_mag); + WriteByte(MSG_ENTITY, crossbow_mag); + WriteByte(MSG_ENTITY, rpg_mag); + WriteByte(MSG_ENTITY, satchel_chg); + WriteByte(MSG_ENTITY, cannon_mag); + } + + if (fChanged & PLAYER_AMMO2) { + WriteByte(MSG_ENTITY, ammo_9mm); + WriteByte(MSG_ENTITY, ammo_357); + WriteByte(MSG_ENTITY, ammo_buckshot); + WriteByte(MSG_ENTITY, ammo_bolt); + WriteByte(MSG_ENTITY, ammo_rocket); + WriteByte(MSG_ENTITY, ammo_uranium); + WriteByte(MSG_ENTITY, ammo_handgrenade); + WriteByte(MSG_ENTITY, ammo_satchel); + WriteByte(MSG_ENTITY, ammo_tripmine); + WriteByte(MSG_ENTITY, ammo_snark); + WriteByte(MSG_ENTITY, ammo_hornet); + } + + if (fChanged & PLAYER_AMMO3) { + WriteByte(MSG_ENTITY, ammo_m203_grenade); + WriteByte(MSG_ENTITY, ammo_shotgun_state); + WriteByte(MSG_ENTITY, ammo_gauss_state); + WriteByte(MSG_ENTITY, ammo_gauss_volume); + WriteByte(MSG_ENTITY, ammo_egon_state); + WriteByte(MSG_ENTITY, ammo_rpg_state); + WriteByte(MSG_ENTITY, mode_tempstate); + WriteByte(MSG_ENTITY, ammo_chainsaw_state); + WriteByte(MSG_ENTITY, ammo_hammer_state); + } + + return TRUE; +} +#endif diff --git a/src/shared/pmove.qc b/src/shared/pmove.qc new file mode 100644 index 0000000..823aa5e --- /dev/null +++ b/src/shared/pmove.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. + */ + +#define PHY_JUMP_CHAINWINDOW 0.5 +#define PHY_JUMP_CHAIN 100 +#define PHY_JUMP_CHAINDECAY 50 + +.float waterlevel; +.float watertype; + +float GamePMove_Maxspeed(player target) +{ + if (target.flags & FL_RESERVED1) + return (target.flags & FL_CROUCHING) ? 135 : 500; + else + return (target.flags & FL_CROUCHING) ? 135 : 270; +} + +void GamePMove_Fall(player target, float impactspeed) +{ + if (impactspeed > 580) { +#ifdef SERVER + float fFallDamage = (impactspeed - 580) * (100 / (1024 - 580)); + Damage_Apply(self, world, fFallDamage, 0, DMG_FALL); + Sound_Play(self, CHAN_AUTO, "player.fall"); +#endif + } +} + +void GamePMove_Jump(player target) +{ + float flJumptimeDelta; + float flChainBonus; + + if (target.waterlevel >= 2) { + if (target.watertype == CONTENT_WATER) { + target.velocity[2] = 100; + } else if (target.watertype == CONTENT_SLIME) { + target.velocity[2] = 80; + } else { + target.velocity[2] = 50; + } + } else { + /* Half-Life: Longjump module */ +#ifdef VALVE + if (target.flags & FL_CROUCHING && target.g_items & 0x00008000i) { + target.velocity = v_forward * 512; + target.velocity[2] += 100; + } +#endif + target.velocity[2] += 240; + } + + if (target.jumptime > 0) { + flJumptimeDelta = 0 - (target.jumptime - PHY_JUMP_CHAINWINDOW); + flChainBonus = PHY_JUMP_CHAIN - (((PHY_JUMP_CHAINWINDOW - (PHY_JUMP_CHAINWINDOW - flJumptimeDelta)) * 2) * PHY_JUMP_CHAINDECAY); + target.velocity[2] += flChainBonus; + } + target.jumptime = PHY_JUMP_CHAINWINDOW; +} diff --git a/src/shared/w_cannon.qc b/src/shared/w_cannon.qc new file mode 100644 index 0000000..150b734 --- /dev/null +++ b/src/shared/w_cannon.qc @@ -0,0 +1,279 @@ +/* + * 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. + */ + +#ifdef CLIENT +var string g_cannon_spr; +#endif + +typedef enum +{ + CANNON_FIREBOTH, + CANNON_FIRELEFT, + CANNON_FIRERIGHT, + CANNON_RELOAD, + CANNON_DEPLOY, + CANNON_PUTAWAY, + CANNON_IDLE1, + CANNON_IDLE2 +} cannonAnims_t; + +void +w_cannon_precache(void) +{ +#ifdef SERVER + precache_sound("cannon/cin.wav"); + precache_sound("cannon/close.wav"); + precache_sound("cannon/cout.wav"); + precache_sound("cannon/fire.wav"); + precache_sound("cannon/open.wav"); +#else + precache_model("models/v_cannon.mdl"); + precache_model("models/p_cannon.mdl"); + g_cannon_spr = spriteframe("sprites/w_cannon.spr", 0, 0.0f); +#endif +} + +void +w_cannon_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, pl.cannon_mag, pl.ammo_buckshot, -1); +} + +string +w_cannon_pmodel(void) +{ + return "models/p_cannon.mdl"; +} + +string +w_cannon_deathmsg(void) +{ + return "%s killed %s with handcannon."; +} + +void +w_cannon_reload(void) +{ + player pl = (player)self; + if (pl.w_attack_next > 0) { + return; + } + if (pl.cannon_mag >= 2) { + return; + } + if (!pl.ammo_buckshot) { + return; + } + +#ifdef CLIENT + Weapons_ViewAnimation(CANNON_RELOAD); +#else + Weapons_ReloadWeapon(pl, player::cannon_mag, player::ammo_buckshot, 2); +#endif + + pl.w_attack_next = 3.0f; + pl.w_idle_next = 3.0f; +} + +int +w_cannon_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (new) { + pl.cannon_mag = 2; + } else { + if (pl.ammo_buckshot < 125) { + pl.ammo_buckshot = bound(0, pl.ammo_buckshot + 2, 125); + } else { + return FALSE; + } + } +#endif + return TRUE; +} + +void +w_cannon_draw(void) +{ + Weapons_SetModel("models/v_cannon.mdl"); + Weapons_ViewAnimation(CANNON_DEPLOY); +} + +void +w_cannon_holster(void) +{ + Weapons_ViewAnimation(CANNON_PUTAWAY); +} + +void +w_cannon_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + + if (pl.cannon_mag != 2) { + w_cannon_reload(); + return; + } + +#ifdef CLIENT + View_SetMuzzleflash(MUZZLE_SMALL); +#else + int dmg; + dmg = Skill_GetValue("plr_cannon", 5); + TraceAttack_FireBullets(20, pl.origin + pl.view_ofs, dmg, [0.08716,0.04362], WEAPON_CANNON); + pl.cannon_mag -= 2; + Weapons_PlaySound(pl, CHAN_WEAPON, "cannon/fire.wav", 1, ATTN_NORM); + Weapons_UpdateAmmo(pl, pl.cannon_mag, pl.ammo_buckshot, __NULL__); +#endif + Weapons_ViewPunchAngle([-5,0,0]); + Weapons_ViewAnimation(CANNON_FIREBOTH); + pl.w_attack_next = 1.5f; + pl.w_idle_next = 2.5f; +} + +void +w_cannon_secondary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + + if (!pl.cannon_mag) { + w_cannon_reload(); + return; + } + +#ifdef CLIENT + Weapons_ViewPunchAngle([-5,0,0]); +#else + int dmg; + dmg = Skill_GetValue("plr_cannon", 5); + TraceAttack_FireBullets(10, pl.origin + pl.view_ofs, dmg, [0.08716,0.04362], WEAPON_CANNON); + pl.cannon_mag--; + Weapons_PlaySound(pl, CHAN_WEAPON, "cannon/fire.wav", 1, ATTN_NORM); +#endif + + if (pl.cannon_mag == 2) { + Weapons_ViewAnimation(CANNON_FIRELEFT); + } else { + Weapons_ViewAnimation(CANNON_FIRERIGHT); + } + + pl.w_attack_next = 1.5f; + pl.w_idle_next = 2.5f; +} +void +w_cannon_release(void) +{ + player pl = (player)self; + + /* auto-reload if need be */ + if (pl.w_attack_next <= 0.0) + if (pl.cannon_mag == 0 && pl.ammo_buckshot > 0) { + Weapons_Reload(); + return; + } + + if (pl.w_idle_next > 0.0) { + return; + } + + int r = floor(random(0,2)); + switch (r) { + case 0: + Weapons_ViewAnimation(CANNON_IDLE1); + break; + case 1: + Weapons_ViewAnimation(CANNON_IDLE2); + break; + } + + pl.w_idle_next = 8.0f; +} + +void +w_cannon_crosshair(void) +{ +#ifdef CLIENT + static vector cross_pos; + cross_pos = g_hudmins + (g_hudres / 2) + [-12,-12]; + drawsubpic(cross_pos, [24,24], g_cross_spr, [48/128,24/128], [0.1875, 0.1875], [1,1,1], 1, DRAWFLAG_NORMAL); + HUD_DrawAmmo1(); + HUD_DrawAmmo2(); + vector aicon_pos = g_hudmins + [g_hudres[0] - 48, g_hudres[1] - 42]; + drawsubpic(aicon_pos, [24,24], g_hud7_spr, [72/256,72/128], [24/256, 24/128], g_hud_color, pSeat->m_flAmmo2Alpha, DRAWFLAG_ADDITIVE); +#endif +} + +float +w_cannon_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CR_AIMSHOTGUN : ANIM_AIMSHOTGUN; +} + +void +w_cannon_hudpic(int s, vector pos, float a) +{ +#ifdef CLIENT + player pl = (player)self; + vector hud_col; + + if (pl.cannon_mag == 0 && pl.ammo_buckshot == 0) + hud_col = [1,0,0]; + else + hud_col = g_hud_color; + + if (s) { + drawsubpic(pos, [170,45], g_cannon_spr, [0,48/256], [170/256,45/256], g_hud_color, a, DRAWFLAG_ADDITIVE); + } else { + drawsubpic(pos, [170,45], g_cannon_spr, [0,0], [170/256,45/256], g_hud_color, a, DRAWFLAG_ADDITIVE); + } + + HUD_DrawAmmoBar(pos, pl.ammo_buckshot, MAX_A_BUCKSHOT, a); +#endif +} + +weapon_t +w_cannon = +{ + .name = "handcannon", + .id = ITEM_CANNON, + .slot = 2, + .slot_pos = 3, + .draw = w_cannon_draw, + .holster = w_cannon_holster, + .primary = w_cannon_primary, + .secondary = w_cannon_secondary, + .reload = w_cannon_reload, + .release = w_cannon_release, + .crosshair = w_cannon_crosshair, + .precache = w_cannon_precache, + .pickup = w_cannon_pickup, + .updateammo = w_cannon_updateammo, + .wmodel = __NULL__, + .pmodel = w_cannon_pmodel, + .deathmsg = w_cannon_deathmsg, + .aimanim = w_cannon_aimanim, + .hudpic = w_cannon_hudpic +}; diff --git a/src/shared/w_chainsaw.qc b/src/shared/w_chainsaw.qc new file mode 100644 index 0000000..5579c3b --- /dev/null +++ b/src/shared/w_chainsaw.qc @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef CLIENT +var string g_chainsaw_spr; +#endif + +enum +{ + CHAINSAW_STARTFIRE, + CHAINSAW_CONTINUEFIRE, + CHAINSAW_STOPFIRE, + CHAINSAW_DEPLOY, + CHAINSAW_HOLSTER, + CHAINSAW_IDLE1, + CHAINSAW_IDLE2, +}; + +void w_chainsaw_precache(void) +{ +#ifdef SERVER + precache_sound("sh/chainsaw_cutinto.wav"); + precache_sound("sh/chainsaw_cutintoflesh.wav"); + precache_sound("sh/chainsaw_idle.wav"); + precache_sound("sh/chainsaw_idle2.wav"); + precache_sound("sh/chainsaw_pullout.wav"); + precache_sound("sh/chainsaw_startup.wav"); +#else + precache_model("models/v_chainsaw.mdl"); + precache_model("models/p_saw.mdl"); + g_chainsaw_spr = spriteframe("sprites/chainsaw.spr", 0, 0.0f); +#endif +} + +void w_chainsaw_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, -1, -1, -1); +} +string w_chainsaw_pmodel(void) +{ + return "models/p_saw.mdl"; +} +string w_chainsaw_deathmsg(void) +{ + return "%s killed %s with chainsaw."; +} + +void w_chainsaw_draw(void) +{ + Weapons_SetModel("models/v_chainsaw.mdl"); + Weapons_ViewAnimation(CHAINSAW_DEPLOY); +} + +void w_chainsaw_holster(void) +{ + Weapons_ViewAnimation(CHAINSAW_HOLSTER); +} +void w_chainsaw_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next) { + return; + } + + pl.ammo_chainsaw_state = 1; + Weapons_ViewAnimation(CHAINSAW_CONTINUEFIRE); + +#ifdef SERVER + Weapons_MakeVectors(); + vector src = pl.origin + pl.view_ofs; + traceline(src, src + (v_forward * 32), FALSE, pl); + + if (trace_fraction >= 1.0) { + Weapons_PlaySound(pl, CHAN_WEAPON, "sh/chainsaw_idle2.wav", 1, ATTN_NORM); + pl.w_attack_next = 0.2f; + } else { + int dmg; + FX_Impact(IMPACT_MELEE, trace_endpos, trace_plane_normal); + + if (trace_ent.takedamage) { + if (trace_ent.iBleeds) { + /* Push the player towards the victim */ + pl.velocity = normalize(trace_ent.origin - pl.origin) * 240; + } + dmg = Skill_GetValue("plr_chainsaw", 10); + Damage_Apply(trace_ent, self, dmg, WEAPON_CHAINSAW, DMG_BLUNT); + Weapons_PlaySound(pl, CHAN_WEAPON, "sh/chainsaw_cutintoflesh.wav", 1, ATTN_NORM); + } else { + FX_Spark(trace_endpos, trace_plane_normal); + Weapons_PlaySound(pl, CHAN_WEAPON, "sh/chainsaw_cutinto.wav", 1, ATTN_NORM); + } + pl.w_attack_next = 0.1f; + } +#endif + + pl.w_idle_next = 0.0f; +} + +void w_chainsaw_release(void) +{ + player pl = (player)self; + + if (pl.w_idle_next) { + return; + } + + if (pl.ammo_chainsaw_state == 1) { + pl.ammo_chainsaw_state = 0; + pl.w_idle_next = 1.0f; + Weapons_ViewAnimation(CHAINSAW_STOPFIRE); + return; + } else { + pl.w_idle_next = 10.0f; + } + + if (random() < 0.5) { + Weapons_ViewAnimation(CHAINSAW_IDLE1); + } else { + Weapons_ViewAnimation(CHAINSAW_IDLE2); + } +} + +float w_chainsaw_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CR_AIMSQUEAK : ANIM_AIMSQUEAK; +} + +void w_chainsaw_hudpic(int s, vector pos, float a) +{ +#ifdef CLIENT + if (s) { + drawsubpic(pos, [170,45], g_chainsaw_spr, + [0,48/256], [170/256,45/256], + g_hud_color, a, DRAWFLAG_ADDITIVE); + } else { + drawsubpic(pos, [170,45], g_chainsaw_spr, + [0,0], [170/256,45/256], + g_hud_color, a, DRAWFLAG_ADDITIVE); + } +#endif +} + +weapon_t w_chainsaw = +{ + .name = "chainsaw", + .id = ITEM_CHAINSAW, + .slot = 0, + .slot_pos = 2, + .draw = w_chainsaw_draw, + .holster = w_chainsaw_holster, + .primary = w_chainsaw_primary, + .secondary = w_chainsaw_release, + .reload = w_chainsaw_release, + .release = w_chainsaw_release, + .crosshair = __NULL__, + .precache = w_chainsaw_precache, + .pickup = __NULL__, + .updateammo = w_chainsaw_updateammo, + .wmodel = __NULL__, + .pmodel = w_chainsaw_pmodel, + .deathmsg = w_chainsaw_deathmsg, + .aimanim = w_chainsaw_aimanim, + .hudpic = w_chainsaw_hudpic +}; diff --git a/src/shared/w_hammer.qc b/src/shared/w_hammer.qc new file mode 100644 index 0000000..3e0c448 --- /dev/null +++ b/src/shared/w_hammer.qc @@ -0,0 +1,244 @@ +/* + * 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 string g_hammer_spr; +#endif + +enum +{ + HAMMER_IDLE1, + HAMMER_DRAW, + HAMMER_HOLSTER, + HAMMER_ATTACK1, + HAMMER_ATTACK2, + HAMMER_IDLE2, + HAMMER_IDLE3, + HAMMER_HOLSTER2, + HAMMER_HOLSTER3 +}; + +void w_hammer_precache(void) +{ +#ifdef SERVER + precache_sound("sh/ham_hitbod1.wav"); + precache_sound("sh/ham_hitbod2.wav"); + precache_sound("sh/ham_hitbod3.wav"); + precache_sound("sh/ham_hitw.wav"); + precache_sound("sh/ham_swing.wav"); +#else + precache_model("models/p_hammer.mdl"); + precache_model("models/v_hammer.mdl"); + g_hammer_spr = spriteframe("sprites/hammer.spr", 0, 0.0f); +#endif +} + +void w_hammer_updateammo(player pl) +{ + Weapons_UpdateAmmo(pl, -1, -1, -1); +} +string w_hammer_pmodel(void) +{ + return "models/p_hammer.mdl"; +} +string w_hammer_deathmsg(void) +{ + return "%s killed %s with hammer."; +} + +void w_hammer_draw(void) +{ + Weapons_SetModel("models/v_hammer.mdl"); + Weapons_ViewAnimation(HAMMER_DRAW); +} + +void w_hammer_holster(void) +{ + Weapons_ViewAnimation(HAMMER_HOLSTER); +} +void w_hammer_primary(void) +{ + player pl = (player)self; + + if (!pl.w_attack_next) { + /* Hack */ + if (pl.ammo_hammer_state != 1) { + Weapons_ViewAnimation(HAMMER_HOLSTER2); + pl.ammo_hammer_state = 1; + pl.w_attack_next = 0.5f; + } + } + pl.w_idle_next = 2.5f; +} +void w_hammer_secondary(void) +{ + player pl = (player)self; + + if (!pl.w_attack_next) { + /* Hack */ + if (pl.ammo_hammer_state != 2) { + Weapons_ViewAnimation(HAMMER_HOLSTER3); + pl.ammo_hammer_state = 2; + pl.w_attack_next = 0.5f; + } + } + pl.w_idle_next = 2.5f; +} +void w_hammer_reload(void) +{ + +} +void w_hammer_release(void) +{ + player pl = (player)self; + + if (pl.w_attack_next) { + return; + } + +#ifdef SERVER + int hdmg; + int hitsound = 0; + vector src = pl.origin + pl.view_ofs; + makevectors(pl.v_angle); + traceline(src, src + v_forward * 64, FALSE, self); +#endif + + if (pl.ammo_hammer_state == 1) { + #ifdef SERVER + if (trace_ent.takedamage) { + hitsound = floor(random(1, 4)); + + /* players only take half damage */ + if (trace_ent.classname == "player") + hdmg = Skill_GetValue("plr_hammer", 100) / 2; + else + hdmg = Skill_GetValue("plr_hammer", 100); + + Damage_Apply(trace_ent, self, hdmg, WEAPON_HAMMER, DMG_BLUNT); + + if (trace_ent.classname == "monster_scientist") { + trace_ent.movetype = MOVETYPE_TOSS; + trace_ent.velocity = v_forward * 768 + v_up * 256; + } else if (trace_ent.classname == "player") { + trace_ent.velocity = v_forward * 768 + v_up * 256; + } + } else { + if (trace_fraction < 1.0) { + hitsound = 4; + } + } + #endif + Weapons_ViewAnimation(HAMMER_ATTACK1); + pl.w_attack_next = 1.0f; + } else if (pl.ammo_hammer_state == 2) { +#ifdef SERVER + if (trace_ent.takedamage) { + hitsound = floor(random(1, 4)); + hdmg = Skill_GetValue("plr_hammeralt", 200); + Damage_Apply(trace_ent, self, hdmg, WEAPON_HAMMER, DMG_BLUNT); + } else { + if (trace_fraction < 1.0) { + hitsound = 4; + } + } +#endif + Weapons_ViewAnimation(HAMMER_ATTACK2); + pl.w_attack_next = 0.75f; + } + +#ifdef SERVER + if (pl.ammo_hammer_state > 0) { + string snd = "sh/ham_swing.wav"; + switch (hitsound) { + case 1: + snd = "sh/ham_hitbod1.wav"; + break; + case 2: + snd = "sh/ham_hitbod2.wav"; + break; + case 3: + snd = "sh/ham_hitbod3.wav"; + break; + case 4: + snd = "sh/ham_hitw.wav"; + break; + } + Weapons_PlaySound(pl, CHAN_WEAPON, snd, 1.0f, ATTN_NORM); + } +#endif + + /* Reset the hack */ + pl.ammo_hammer_state = 0; + + /* Pure cosmetics start here */ + if (pl.w_idle_next) { + return; + } + + int r = floor(random(0,3)); + switch (r) { + case 0: + Weapons_ViewAnimation(HAMMER_IDLE1); + break; + case 1: + Weapons_ViewAnimation(HAMMER_IDLE2); + break; + case 2: + Weapons_ViewAnimation(HAMMER_IDLE3); + break; + } + pl.w_idle_next = 10.0f; +} + +float w_hammer_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CR_AIMCROWBAR : ANIM_AIMCROWBAR; +} + +void w_hammer_hudpic(int s, vector pos, float a) +{ +#ifdef CLIENT + if (s) { + drawsubpic(pos, [170,45], g_hammer_spr, [0,48/256], [170/256,45/256], g_hud_color, a, DRAWFLAG_ADDITIVE); + } else { + drawsubpic(pos, [170,45], g_hammer_spr, [0,0], [170/256,45/256], g_hud_color, a, DRAWFLAG_ADDITIVE); + } +#endif +} + +weapon_t w_hammer = +{ + .name = "hammer", + .id = ITEM_HAMMER, + .slot = 0, + .slot_pos = 1, + .draw = w_hammer_draw, + .holster = w_hammer_holster, + .primary = w_hammer_primary, + .secondary = w_hammer_secondary, + .reload = w_hammer_reload, + .release = w_hammer_release, + .crosshair = __NULL__, + .precache = w_hammer_precache, + .pickup = __NULL__, + .updateammo = w_hammer_updateammo, + .wmodel = __NULL__, + .pmodel = w_hammer_pmodel, + .deathmsg = w_hammer_deathmsg, + .aimanim = w_hammer_aimanim, + .hudpic = w_hammer_hudpic +}; diff --git a/src/shared/weapons.h b/src/shared/weapons.h new file mode 100644 index 0000000..5813e67 --- /dev/null +++ b/src/shared/weapons.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. + */ + +/* weapon Indices for the weapon table */ +enum +{ + WEAPON_NONE, + WEAPON_CROWBAR, + WEAPON_HAMMER, + WEAPON_CHAINSAW, + WEAPON_GLOCK, + WEAPON_PYTHON, + WEAPON_MP5, + WEAPON_SHOTGUN, + WEAPON_CROSSBOW, + WEAPON_CANNON, + WEAPON_RPG, + WEAPON_GAUSS, + WEAPON_EGON, + WEAPON_HORNETGUN, + WEAPON_HANDGRENADE, + WEAPON_SATCHEL, + WEAPON_TRIPMINE, + WEAPON_SNARK +}; + +#define MAX_A_9MM 250 +#define MAX_A_357 36 +#define MAX_A_BUCKSHOT 125 +#define MAX_A_M203_GRENADE 10 +#define MAX_A_BOLT 50 +#define MAX_A_ROCKET 5 +#define MAX_A_URANIUM 100 +#define MAX_A_HANDGRENADE 10 +#define MAX_A_SATCHEL 5 +#define MAX_A_TRIPMINE 10 +#define MAX_A_SNARK 10 +#define MAX_A_HORNET 8 diff --git a/src/shared/weapons.qc b/src/shared/weapons.qc new file mode 100644 index 0000000..8de8364 --- /dev/null +++ b/src/shared/weapons.qc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +weapon_t w_null = {}; +weapon_t g_weapons[] = { + w_null, + w_crowbar, + w_hammer, + w_chainsaw, + w_glock, + w_python, + w_mp5, + w_shotgun, + w_crossbow, + w_cannon, + w_rpg, + w_gauss, + w_egon, + w_hornetgun, + w_handgrenade, + w_satchel, + w_tripmine, + w_snark +}; diff --git a/src/src.kdev4 b/src/src.kdev4 new file mode 100644 index 0000000..a6f503a --- /dev/null +++ b/src/src.kdev4 @@ -0,0 +1,4 @@ +[Project] +CreatedFrom=Makefile +Manager=KDevCustomMakeManager +Name=Scientist Hunt diff --git a/zpak001.pk3dir/csprogs.dat b/zpak001.pk3dir/csprogs.dat new file mode 100644 index 0000000..c565a3d Binary files /dev/null and b/zpak001.pk3dir/csprogs.dat differ diff --git a/zpak001.pk3dir/csprogs.lno b/zpak001.pk3dir/csprogs.lno new file mode 100644 index 0000000..e945ad0 Binary files /dev/null and b/zpak001.pk3dir/csprogs.lno differ diff --git a/zpak001.pk3dir/default.cfg b/zpak001.pk3dir/default.cfg new file mode 100644 index 0000000..3fa0da2 --- /dev/null +++ b/zpak001.pk3dir/default.cfg @@ -0,0 +1,45 @@ +// Generic Binds +bind "ESC" "togglemenu" +bind "w" "+forward" +bind "s" "+back" +bind "a" "+moveleft" +bind "d" "+moveright" +bind "SPACE" "+jump" +bind "CTRL" "+duck" +bind "SHIFT" "+speed" +bind "0" "slot10" +bind "1" "slot1" +bind "2" "slot2" +bind "3" "slot3" +bind "4" "slot4" +bind "5" "slot5" +bind "6" "slot6" +bind "7" "slot7" +bind "8" "slot8" +bind "9" "slot9" +bind "UPARROW" "+forward" +bind "DOWNARROW" "+back" +bind "LEFTARROW" "+left" +bind "RIGHTARROW" "+right" +bind "MOUSE1" "+attack" +bind "MOUSE2" "+attack2" +bind "MWHEELDOWN" "invnext" +bind "MWHEELUP" "invprev" +bind "r" "+reload" +bind "e" "+use" +bind "TAB" "+showscores" +bind "y" "messagemode" +bind "u" "messagemode2" +bind "t" "impulse 201" +bind "f" "impulse 100" +bind "f1" "vote yes" +bind "f2" "vote no" + +// Game Variables +seta "hostname" "FreeSH Server" +seta "maxplayers" "8" + +// 2D/HUD Variables +seta "con_color" "255 255 215" +seta "vgui_color" "255 255 215" +seta "cross_color" "0 255 0" diff --git a/zpak001.pk3dir/progs.dat b/zpak001.pk3dir/progs.dat new file mode 100644 index 0000000..6769f1c Binary files /dev/null and b/zpak001.pk3dir/progs.dat differ diff --git a/zpak001.pk3dir/progs.lno b/zpak001.pk3dir/progs.lno new file mode 100644 index 0000000..d998e9b Binary files /dev/null and b/zpak001.pk3dir/progs.lno differ diff --git a/zpak001.pk3dir/skill_scihunt.cfg b/zpak001.pk3dir/skill_scihunt.cfg new file mode 100644 index 0000000..1f9c50e --- /dev/null +++ b/zpak001.pk3dir/skill_scihunt.cfg @@ -0,0 +1,17 @@ +exec skill_valve.cfg + +set sk_plr_hammer1 "100" +set sk_plr_hammer2 "100" +set sk_plr_hammer3 "100" + +set sk_plr_hammeralt1 "200" +set sk_plr_hammeralt2 "200" +set sk_plr_hammeralt3 "200" + +set sk_plr_chainsaw1 "10" +set sk_plr_chainsaw2 "10" +set sk_plr_chainsaw3 "10" + +set sk_plr_cannon1 "5" +set sk_plr_cannon2 "5" +set sk_plr_cannon3 "5"