commit 542bc73bef0bf9ddb28d5a10219960585d39ae41 Author: Marco Hladik Date: Thu Feb 11 19:06:14 2021 +0100 Initial commit of the fixed codebase 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/entities.qc b/src/client/entities.qc new file mode 100644 index 0000000..5c4f4a5 --- /dev/null +++ b/src/client/entities.qc @@ -0,0 +1,26 @@ +/* + * 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 +Game_Entity_Update(float id, float new) +{ + switch (id) { + 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..7edf3dc --- /dev/null +++ b/src/client/game_event.qc @@ -0,0 +1,154 @@ +/* + * 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_Parse_Event(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_LEGO: + vector vLegoPos; + + vLegoPos[0] = readcoord(); + vLegoPos[1] = readcoord(); + vLegoPos[2] = readcoord(); + + FX_Lego(vLegoPos); + break; + case EV_LEGOPIECE: + vector vLegoPiecePos; + + vLegoPiecePos[0] = readcoord(); + vLegoPiecePos[1] = readcoord(); + vLegoPiecePos[2] = readcoord(); + + FX_LegoPiece(vLegoPiecePos); + break; + case EV_SODA: + vector vSodaPos; + int vSodaColor; + + vSodaPos[0] = readcoord(); + vSodaPos[1] = readcoord(); + vSodaPos[2] = readcoord(); + + vSodaColor = readbyte(); + + FX_Soda(vSodaPos, vSodaColor); + break; + case EV_EXPLOSION: + vector vExploPos; + + vExploPos[0] = readcoord(); + vExploPos[1] = readcoord(); + vExploPos[2] = readcoord(); + + FX_Explosion(vExploPos); + break; + case EV_MODELGIB: + vector vecPos; + vecPos[0] = readcoord(); + vecPos[1] = readcoord(); + vecPos[2] = readcoord(); + + vector vSize; + vSize[0] = readcoord(); + vSize[1] = readcoord(); + vSize[2] = readcoord(); + + float fStyle = readbyte(); + int count = readbyte(); + FX_BreakModel(count, vecPos, vSize, [0,0,0], fStyle); + break; + case EV_IMPACT: + int iType; + vector vOrigin, vNormal; + + iType = (int)readbyte(); + vOrigin[0] = readcoord(); + vOrigin[1] = readcoord(); + vOrigin[2] = readcoord(); + + vNormal[0] = readcoord(); + vNormal[1] = readcoord(); + vNormal[2] = readcoord(); + + FX_Impact(iType, vOrigin, vNormal); + break; + case EV_CHAT: + float fSender = readbyte(); + float fTeam = readbyte(); + string sMessage = readstring(); + + CSQC_Parse_Print(sprintf("%s: %s", getplayerkeyvalue(fSender, "name"), sMessage), PRINT_CHAT); + break; + case EV_CHAT_TEAM: + float fSender2 = readbyte(); + float fTeam2 = readbyte(); + string sMessage2 = readstring(); + + CSQC_Parse_Print(sprintf("[TEAM] %s: %s", getplayerkeyvalue(fSender2, "name"), sMessage2), PRINT_CHAT); + break; + case EV_CHAT_VOX: + Vox_Play(readstring()); + break; + case EV_VIEWMODEL: + View_PlayAnimation(readbyte()); + break; + case EV_WEAPON_PICKUP: + int w = readbyte(); + + if (autocvar_cl_autoweaponswitch == 1) { + sendevent("PlayerSwitchWeapon", "i", w); + } + + HUD_WeaponPickupNotify(w); + break; + } +} diff --git a/src/client/hud.qc b/src/client/hud.qc new file mode 100644 index 0000000..5c47506 --- /dev/null +++ b/src/client/hud.qc @@ -0,0 +1,395 @@ +/* + * 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 HUD_NUMS "sprites/640_numbers.spr_0.tga" +#define NUMSIZE_X 32/256 +#define NUMSIZE_Y 32/64 +#define HUD_ALPHA 1.0 + +vector spr_hudnum[10] = { + [0 / 256, 0], + [32 / 256, 0], + [(32*2) / 256, 0], + [(32*3) / 256, 0], + [(32*4) / 256, 0], + [(32*5) / 256, 0], + [(32*6) / 256, 0], + [(32*7) / 256, 0], + [0 / 256, 32/64], + [32 / 256, 32/64] +}; + +/* pre-calculated sprite definitions */ +float spr_health[4] = { + 80 / 256, // pos x + 24 / 128, // pos y + 32 / 256, // size x + 32 / 128 // size y +}; + +#define HUD_SUIT "sprites/640_suit.spr_0.tga" +#define SUITSIZE_X 32/128 +#define SUITSIZE_Y 32/64 + +float spr_suit1[4] = { + 0 / 128, // pos x + 0 / 128, // pos y + 40 / 128, // size x + 40 / 64 // size y +}; + +float spr_suit2[4] = { + 40 / 128, // pos x + 0 / 128, // pos y + 40 / 128, // size x + 40 / 64 // size y +}; + +#define HUD_FLASH "sprites/640hud7.spr_0.tga" +#define FLASHSIZE_X 24/256 +#define FLASHSIZE_Y 24/128 + +float spr_flash1[4] = { + 160 / 256, // pos x + 24 / 128, // pos y + 32 / 256, // size x + 32 / 128 // size y +}; + +float spr_flash2[4] = { + 112 / 256, // pos x + 24 / 128, // pos u + 48 / 256, // size x + 32 / 128 // size y +}; + +/* precaches */ +void +HUD_Init(void) +{ + precache_model("sprites/640_cross.spr"); + precache_model("sprites/640_numbers.spr"); + precache_model("sprites/640_logo.spr"); + precache_model("sprites/640_suit.spr"); + precache_model("sprites/640hud7.spr"); +} + +/* handle single/multiple digits */ +void +HUD_DrawNumber(int iNumber, vector vecPos, float fAlpha, vector vColor) +{ + drawsubpic(vecPos, + [32,32], + "sprites/640_numbers.spr_0.tga", + spr_hudnum[iNumber], + [NUMSIZE_X, NUMSIZE_Y], + vColor, + fAlpha, + DRAWFLAG_ADDITIVE + ); +} + +void +HUD_DrawNums(float fNumber, vector vecPos, float fAlpha, vector vColor) +{ + int i = fNumber; + if (i > 0) { + while (i > 0) { + HUD_DrawNumber((float)i % 10, vecPos, fAlpha, vColor); + i = i / 10; + vecPos[0] -= 32; + } + } else { + HUD_DrawNumber(0, vecPos, fAlpha, vColor); + } +} + +/* health */ +/* We don't need the changing color if case + * it's always red in Household DEATH! */ +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 + [14, g_hudres[1] - 42]; + drawpic( + pos, + "sprites/640_cross.spr_0.tga", + [32,32], + [1,1,1], + pSeat->m_flHealthAlpha + ); + HUD_DrawNums(pl.health, pos + [96], pSeat->m_flHealthAlpha, g_hud_color); + + pSeat->m_iHealthOld = pl.health; +} + +/* armor/suit charge */ +void +HUD_DrawArmor(void) +{ + vector pos; + player pl = (player)pSeat->m_ePlayer; + + pos = g_hudmins + [198, g_hudres[1] - 42]; + + if (pl.armor != pSeat->m_iArmorOld) { + pSeat->m_flArmorAlpha = 1.0; + } + + if (pSeat->m_flArmorAlpha >= HUD_ALPHA) { + pSeat->m_flArmorAlpha -= clframetime * 0.5; + } else { + pSeat->m_flArmorAlpha = HUD_ALPHA; + } + + drawsubpic( + pos + [-48,-9], + [40,40], + HUD_SUIT, + [spr_suit2[0], spr_suit2[1]], + [spr_suit2[2], spr_suit2[3]], + [1,1,1], + 1.0f + ); + + if (pl.armor > 0) { + drawsubpic( + pos + [-48,-9], + [40, 40 * (pl.armor / 100)], + HUD_SUIT, + [spr_suit1[0], + spr_suit1[1]], + [spr_suit1[2], spr_suit1[3] * (pl.armor / 100)], + [1,1,1], + 1.0f + ); + } + + HUD_DrawNums(pl.armor, pos + [48] , 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); +} + +/* 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); +} + +/* 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], + HUD_FLASH, + [spr_flash1[0], spr_flash1[1]], + [spr_flash1[2], spr_flash1[3]], + g_hud_color, + 1.0f, + DRAWFLAG_ADDITIVE + ); + + drawsubpic( + pos, + [48,32], + HUD_FLASH, + [spr_flash2[0], spr_flash2[1]], + [spr_flash2[2], spr_flash2[3]], + g_hud_color, + 1.0f, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [32,32], + HUD_FLASH, + [spr_flash1[0], spr_flash1[1]], + [spr_flash1[2], spr_flash1[3]], + g_hud_color, + HUD_ALPHA, + DRAWFLAG_ADDITIVE + ); + } +} + +/* logo animation used during e3 1998 */ +void +HUD_DrawLogo(void) +{ + vector pos; + static int f; + static float frame_timer; + + frame_timer -= clframetime; + pos = [g_hudres[0] - 262, 48]; + + drawpic( + pos, + sprintf("sprites/640_logo.spr_%i.tga", f), + [256,48], + [1,1,1], + 1.0f, + DRAWFLAG_ADDITIVE + ); + + if (frame_timer > 0) { + return; + } + + frame_timer = 0.1f; + + f++; + if (f == 31) { + f = 0; + } +} + +/* weapon/ammo pickup notifications */ +void +HUD_DrawNotify(void) +{ + vector pos; + + if (pSeat->m_flPickupAlpha <= 0.0f) { + return; + } + + pos = g_hudmins + [g_hudres[0] - 192, g_hudres[1] - 128]; + Weapons_HUDPic(pSeat->m_iPickupWeapon, 1, pos, pSeat->m_flPickupAlpha); + pSeat->m_flPickupAlpha -= clframetime; +} + +void +HUD_WeaponPickupNotify(int w) +{ + pSeat->m_iPickupWeapon = w; + pSeat->m_flPickupAlpha = 1.0f; +} + +/* main entry */ +void +HUD_Draw(void) +{ + player pl = (player)pSeat->m_ePlayer; + + g_hud_color = autocvar_con_color * (1 / 255); + + /* little point in not drawing these, even if you don't have a suit */ + Weapons_DrawCrosshair(); + HUD_DrawWeaponSelect(); + Obituary_Draw(); + + if (!(pl.g_items & ITEM_SUIT)) { + return; + } + + HUD_DrawHealth(); + HUD_DrawArmor(); + HUD_DrawFlashlight(); + HUD_DrawNotify(); + Damage_Draw(); +} + +/* specatator main entry */ +void +HUD_DrawSpectator(void) +{ + // FIXME +} diff --git a/src/client/init.qc b/src/client/init.qc new file mode 100644 index 0000000..72dfb43 --- /dev/null +++ b/src/client/init.qc @@ -0,0 +1,55 @@ +/* + * 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(entity foo, float chanid) getchannellevel = #0; + + +/* +================= +Client_Init + +Comparable to worldspawn in SSQC in that it's mostly used for precaches +================= +*/ +void +Client_Init(float apilevel, string enginename, float engineversion) +{ +} + +void +Client_InitDone(void) +{ + Obituary_Init(); +} + +void +Game_RendererRestarted(string rstr) +{ + FX_Blood_Init(); + FX_BreakModel_Init(); + FX_Explosion_Init(); + FX_GibHuman_Init(); + FX_Spark_Init(); + FX_Impact_Init(); + FX_Lego_Init(); + FX_Soda_Init(); + + precache_model("sprites/640_death.spr"); + precache_model("sprites/640_cross.spr"); + precache_model("sprites/640_numbers.spr"); + precache_model("sprites/640_suit.spr"); + precache_model("sprites/640hud7.spr"); +} diff --git a/src/client/obituary.qc b/src/client/obituary.qc new file mode 100644 index 0000000..e9cff35 --- /dev/null +++ b/src/client/obituary.qc @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +void +Obituary_Init(void) +{ + int c; + int i; + filestream fh; + string line; + vector tmp; + + if (g_obtype_count > 0) { + return; + } + + print("Init Obituaries\n"); + g_obtype_count = 0; + i = 0; + + fh = fopen("sprites/hud.txt", FILE_READ); + if (fh < 0) { + return; + } + + /* count valid entries */ + while ((line = fgets(fh))) { + if (substring(line, 0, 2) == "d_") { + c = tokenize(line); + if (c == 7 && argv(1) == "640") { + g_obtype_count++; + } + } + } + + g_obtypes = memalloc(sizeof(obituaryimg_t) * g_obtype_count); + + fseek(fh, 0); + + /* read them in */ + while ((line = fgets(fh))) { + if (substring(line, 0, 2) == "d_") { + c = tokenize(line); + + /* we only care about the high-res (640) variants. the 320 + * HUD is useless to us. Just use the builtin scaler */ + if (c == 7 && argv(1) == "640") { + g_obtypes[i].name = substring(argv(0), 2, -1); + g_obtypes[i].src_sprite = sprintf("sprites/%s.spr", argv(2)); + precache_model(g_obtypes[i].src_sprite); + g_obtypes[i].sprite = sprintf("sprites/%s.spr_0.tga", argv(2)); + g_obtypes[i].size[0] = stof(argv(5)); + g_obtypes[i].size[1] = stof(argv(6)); + tmp = drawgetimagesize(g_obtypes[i].sprite); + g_obtypes[i].src_pos[0] = stof(argv(3)) / tmp[0]; + g_obtypes[i].src_pos[1] = stof(argv(4)) / tmp[1]; + g_obtypes[i].src_size[0] = g_obtypes[i].size[0] / tmp[0]; + g_obtypes[i].src_size[1] = g_obtypes[i].size[1] / tmp[1]; + i++; + } + } + } + + fclose(fh); +} + +void +Obituary_Precache(void) +{ + print("Precache Obituaries\n"); + + for (int i = 0; i < g_obtype_count; i++) + precache_model(g_obtypes[i].src_sprite); +} + +void +Obituary_KillIcon(int id, float w) +{ + for (int i = 0; i < g_obtype_count; i++) { + if (g_weapons[w].name == g_obtypes[i].name) { + g_obituary[id].icon = i; + return; + } + } + + /* look for skull instead */ + for (int i = 0; i < g_obtype_count; i++) { + if (g_obtypes[i].name == "skull") { + g_obituary[id].icon = i; + return; + } + } +} + +void +Obituary_Add(string attacker, string victim, float weapon, float flags) +{ + int i; + int x, y; + x = OBITUARY_LINES; + + /* we're not full yet, so fill up the buffer */ + if (g_obituary_count < x) { + y = g_obituary_count; + g_obituary[y].attacker = attacker; + g_obituary[y].victim = victim; + Obituary_KillIcon(y, weapon); + g_obituary_count++; + } else { + for (i = 0; i < (x-1); i++) { + g_obituary[i].attacker = g_obituary[i+1].attacker; + g_obituary[i].victim = g_obituary[i+1].victim; + g_obituary[i].icon = g_obituary[i+1].icon; + } + /* after rearranging, add the newest to the bottom. */ + g_obituary[x-1].attacker = attacker; + g_obituary[x-1].victim = victim; + Obituary_KillIcon(x-1, weapon); + } + + g_obituary_time = OBITUARY_TIME; +} + +void +Obituary_Draw(void) +{ + int i; + vector pos; + vector item; + drawfont = FONT_CON; + pos = g_hudmins + [g_hudres[0] - 18, 56]; + + if (g_obituary_time <= 0 && g_obituary_count > 0) { + for (i = 0; i < (OBITUARY_LINES-1); i++) { + g_obituary[i].attacker = g_obituary[i+1].attacker; + g_obituary[i].victim = g_obituary[i+1].victim; + g_obituary[i].icon = g_obituary[i+1].icon; + } + g_obituary[OBITUARY_LINES-1].attacker = ""; + + g_obituary_time = OBITUARY_TIME; + g_obituary_count--; + } + + if (g_obituary_count <= 0) { + return; + } + + item = pos; + for (i = 0; i < OBITUARY_LINES; i++) { + string a, v; + + if (!g_obituary[i].attacker) { + break; + } + + item[0] = pos[0]; + + + v = g_obituary[i].victim; + drawstring_r(item + [0,2], v, [12,12], [1,1,1], 1.0f, 0); + item[0] -= stringwidth(v, TRUE, [12,12]) + 4; + item[0] -= g_obtypes[g_obituary[i].icon].size[0]; + + drawsubpic( + item, + [g_obtypes[g_obituary[i].icon].size[0], g_obtypes[g_obituary[i].icon].size[1]], + g_obtypes[g_obituary[i].icon].sprite, + [g_obtypes[g_obituary[i].icon].src_pos[0],g_obtypes[g_obituary[i].icon].src_pos[1]], + [g_obtypes[g_obituary[i].icon].src_size[0],g_obtypes[g_obituary[i].icon].src_size[1]], + [1,1,1], + 1.0f, + 0 + ); + + a = g_obituary[i].attacker; + drawstring_r(item + [-4,2], a, [12,12], [1,1,1], 1.0f, 0); + item[1] += 18; + } + + g_obituary_time = max(0, g_obituary_time - clframetime); +} + +void +Obituary_Parse(void) +{ + string attacker; + string victim; + float weapon; + float flags; + + attacker = readstring(); + victim = readstring(); + weapon = readbyte(); + flags = readbyte(); + + if (!attacker) { + return; + } + + Obituary_Add(attacker, victim, weapon, flags); +} diff --git a/src/client/progs.src b/src/client/progs.src new file mode 100644 index 0000000..90bab0d --- /dev/null +++ b/src/client/progs.src @@ -0,0 +1,42 @@ +#pragma target fte +#pragma progs_dat "../../csprogs.dat" + +#define CSQC +#define CLIENT +#define VALVE +#define HHDEATH +#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 +init.qc +../../../valve/src/client/player.qc +entities.qc +../../../valve/src/client/cmds.qc +game_event.qc +../../../valve/src/client/view.qc +obituary.qc +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/ammo_hd.qc b/src/server/ammo_hd.qc new file mode 100644 index 0000000..aee840b --- /dev/null +++ b/src/server/ammo_hd.qc @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * Copyright (c) 2020 Gethyn ThomasQuail + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +class item_ammo:CBaseEntity +{ + void(void) item_ammo; + virtual void(void) Respawn; + virtual void(void) touch; +}; + +void item_ammo::touch(void) +{ + if not (other.flags & FL_CLIENT) { + return; + } + + player pl = (player)other; + Sound_Play(other, CHAN_ITEM, "ammo.pickup"); + Weapons_RefreshAmmo(pl); + Logging_Pickup(other, this, __NULL__); + + if (cvar("sv_playerslots") == 1) { + remove(self); + } else { + Hide(); + think = Respawn; + nextthink = time + 20.0f; + } +} + +void item_ammo::Respawn(void) +{ + SetSolid(SOLID_TRIGGER); + SetMovetype(MOVETYPE_TOSS); + SetSize([-16,-16,0],[16,16,16]); + SetOrigin(m_oldOrigin); + SetModel(m_oldModel); + + think = __NULL__; + nextthink = -1; + Sound_Play(this, CHAN_ITEM, "ammo.respawn"); + droptofloor(); +} + +void item_ammo::item_ammo(void) +{ + m_oldModel = model; + SetModel(m_oldModel); + CBaseEntity::CBaseEntity(); +} + +/*QUAKED ammo_forks (0 0 0.8) (-16 -16 0) (16 16 32) + +Household DEATH! (2003) ENTITY + +Ammo for the throwable Forks. +A single fork, despite name. It's just a fork man. + +*/ +class ammo_forks:item_ammo +{ + void(void) ammo_forks; + virtual void(void) touch; +}; + +void ammo_forks::ammo_forks(void) +{ + model = "models/w_fork2.mdl"; + item_ammo::item_ammo(); + SetRenderMode(RM_FULLBRIGHT); + SetRenderAmt(1.0f); +} +void ammo_forks::touch(void) +{ + if not (other.flags & FL_CLIENT) { + return; + } + if (other.classname == "player") { + player pl = (player)other; + if (pl.ammo_forks < MAX_A_FORKS) { + pl.ammo_forks = bound(0, pl.ammo_forks + 1, MAX_A_FORKS); + item_ammo::touch(); + } + } +} + +/*QUAKED ammo_legoblocks (0 0 0.8) (-16 -16 0) (16 16 32) + +Household DEATH! (2003) ENTITY + +Ammo for the Lego Launcher. +A single pack provides 12 legos. + +*/ + +class ammo_legoblocks:item_ammo +{ + void(void) ammo_legoblocks; + virtual void(void) touch; +}; + +void ammo_legoblocks::ammo_legoblocks(void) +{ + model = "models/legoblocks.mdl"; + item_ammo::item_ammo(); + SetRenderMode(RM_FULLBRIGHT); + SetRenderAmt(1.0f); +} +void ammo_legoblocks::touch(void) +{ + if not (other.flags & FL_CLIENT) { + return; + } + if (other.classname == "player") { + player pl = (player)other; + if (pl.ammo_legos < MAX_A_LEGOS) { + pl.ammo_legos = bound(0, pl.ammo_legos + 12, MAX_A_LEGOS); + item_ammo::touch(); + } + } +} + +/*QUAKED ammo_sodacans (0 0 0.8) (-16 -16 0) (16 16 32) + +Household DEATH! (2003) ENTITY + +Ammo for the Soda Launcher. +A single pack provides 6 soda cans. + +*/ + +class ammo_sodacans:item_ammo +{ + void(void) ammo_sodacans; + virtual void(void) touch; +}; + +void ammo_sodacans::ammo_sodacans(void) +{ + model = "models/nukacola.mdl"; + item_ammo::item_ammo(); + SetRenderMode(RM_FULLBRIGHT); + SetRenderAmt(1.0f); +} +void ammo_sodacans::touch(void) +{ + if not (other.flags & FL_CLIENT) { + return; + } + if (other.classname == "player") { + player pl = (player)other; + if (pl.ammo_soda < MAX_A_SODA) { + pl.ammo_soda = bound(0, pl.ammo_soda + 6, MAX_A_SODA); + item_ammo::touch(); + } + } +} 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..84df940 --- /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 HHDMultiplayerRules: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) PlayerPain; + virtual void(base_player) PlayerPostFrame; + + 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..b81684f --- /dev/null +++ b/src/server/gamerules.qc @@ -0,0 +1,347 @@ +/* + * 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_sv_playerkeepalive = TRUE; + +void +HHDMultiplayerRules::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 +HHDMultiplayerRules::PlayerPain(base_player pl) +{ + /* Vampire Rune + * steals health from enemies */ + + if (g_dmg_eAttacker.flags & RUNE_VAMPIRE) + g_dmg_eAttacker.health += g_dmg_iDamage; + + /* This probably doesn't go here? + * damage checks for vampire rune */ + + if (g_dmg_eAttacker == (entity)pl) + return; + + if (!(g_dmg_eAttacker.flags & FL_CLIENT)) + return; +} + +void +HHDMultiplayerRules::PlayerSpawn(base_player pp) +{ + player pl = (player)pp; + /* this is where the mods want to deviate */ + entity spot; + + pl.classname = "player"; + pl.health = pl.max_health = 100; + pl.takedamage = DAMAGE_YES; + pl.solid = SOLID_SLIDEBOX; + pl.movetype = MOVETYPE_WALK; + pl.flags = FL_CLIENT; + pl.viewzoom = 1.0; + pl.model = "models/player.mdl"; + string mymodel = infokey(pl, "model"); + + if (mymodel) { + mymodel = sprintf("models/player/%s/%s.mdl", mymodel, mymodel); + if (whichpack(mymodel)) { + pl.model = mymodel; + } + } + setmodel(pl, pl.model); + + setsize(pl, VEC_HULL_MIN, VEC_HULL_MAX); + pl.view_ofs = VEC_PLAYER_VIEWPOS; + pl.velocity = [0,0,0]; + pl.gravity = __NULL__; + pl.frame = 1; + pl.SendEntity = Player_SendEntity; + pl.SendFlags = UPDATE_ALL; + pl.customphysics = Empty; + pl.iBleeds = TRUE; + forceinfokey(pl, "*spec", "0"); + forceinfokey(pl, "*deaths", ftos(pl.deaths)); + + spot = Spawn_SelectRandom("info_player_deathmatch"); + setorigin(pl, spot.origin); + pl.angles = spot.angles; + + pl.g_items = ITEM_BROOM | ITEM_SUIT; + pl.activeweapon = WEAPON_BROOM; + Weapons_RefreshAmmo(pl); + + Client_FixAngle(pl, pl.angles); +} + +void +HHDMultiplayerRules::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 = parm18; + + pl.ammo_forks = parm12; + pl.ammo_knives = parm13; + pl.ammo_legogrenade = parm14; + pl.ammo_legos = parm15; + pl.ammo_soda = parm16; + pl.ammo_spray = parm17; + + if (pl.flags & FL_CROUCHING) { + setsize(pl, VEC_CHULL_MIN, VEC_CHULL_MAX); + } else { + setsize(pl, VEC_HULL_MIN, VEC_HULL_MAX); + } +} + +void +HHDMultiplayerRules::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_forks; + parm13 = pl.ammo_knives; + parm14 = pl.ammo_legogrenade; + parm15 = pl.ammo_legos; + parm16 = pl.ammo_soda; + parm17 = pl.ammo_spray; +} + +void +HHDMultiplayerRules::LevelNewParms(void) +{ + parm1 = parm2 = parm3 = parm4 = parm5 = parm6 = parm7 = + parm8 = parm9 = parm10 = parm11 = parm12 = parm13 = parm14 = + parm15 = parm16 = parm17 = 0; + parm18 = FL_CLIENT; +} + +/* we check what fields have changed over the course of the frame and network + * only the ones that have actually changed */ +void +HHDMultiplayerRules::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_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_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; + + /* Almost all powerups are on a timer */ + + pl.powerup_time = bound(0.0f, pl.powerup_time - frametime, pl.powerup_time); + + /* and remove when time runs out ;) */ + + if (pl.powerup_time <= 0.0f) { + pl.g_items &= ~ITEM_RUNE_HASTE; + } +} + +void +HHDMultiplayerRules::PlayerConnect(base_player pl) +{ + entity a; + bprint(PRINT_HIGH, sprintf("%s connected\n", pl.netname)); + + int playercount = 0; + for (a = world; (a = find(a, ::classname, "player"));) { + playercount++; + } +} + +void +HHDMultiplayerRules::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 +HHDMultiplayerRules::PlayerKill(base_player pp) +{ + player pl = (player)pp; + Damage_Apply(pl, pl, pl.health, WEAPON_NONE, DMG_SKIP_ARMOR); +} diff --git a/src/server/info_runespawn.qc b/src/server/info_runespawn.qc new file mode 100644 index 0000000..cb28bfd --- /dev/null +++ b/src/server/info_runespawn.qc @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*QUAKED info_runespawn (0 0 0.8) (-16 -16 0) (16 16 36) + +HALF-LIFE (1998) ENTITY + +HEV Suit energy battery. +It adds the following energy values to the HEV Suit by default: +Skill 1 (Easy): 15 +Skill 2 (Medium): 15 +Skill 3 (Hard): 10 + +The values can be tweaked in the skill.cfg file. + +*/ +class info_runespawn:CBaseEntity +{ + void(void) info_runespawn; + virtual void(void) Respawn; + virtual void(void) touch; +}; + +void info_runespawn::touch(void) +{ + if (other.classname != "player") { + return; + } + + /* TODO runestuff + *if (other.rune >= 1) { + * return; + * } + */ + + Logging_Pickup(other, this, __NULL__); + Sound_Play(other, CHAN_ITEM, "item.armor"); + + think = Respawn; + nextthink = time + 30.0f; + } + +void info_runespawn::Respawn(void) +{ + SetSolid(SOLID_TRIGGER); + SetMovetype(MOVETYPE_NONE); + /* One of the models is transparent, and we need to set additive + SetRenderMode(RM_ADDITIVE); + SetRenderAmt(255); + SetSize([-16,-16,0],[16,16,16]); + * Rotation + * avelocity[1] = 420; */ + /* Always spawns a little above origin */ + SetOrigin(m_oldOrigin + [0,0,16]); + SetModel(m_oldModel); + + + think = __NULL__; + nextthink = -1; + Sound_Play(this, CHAN_ITEM, "item.respawn"); +} + +void info_runespawn::info_runespawn(void) +{ + Sound_Precache("item.armor"); + Sound_Precache("item.respawn"); + /* TODO actually uses two models... */ + model = "models/rune_stand.mdl"; + CBaseEntity::CBaseEntity(); + item_healthkit::Respawn(); +} diff --git a/src/server/input.qc b/src/server/input.qc new file mode 100644 index 0000000..2f34ee3 --- /dev/null +++ b/src/server/input.qc @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* +================= +Input_Handle + +Handles impulse and whatnot +================= +*/ +void Game_Input(void) +{ + CGameRules rules = (CGameRules)g_grMode; + + if (rules.m_iIntermission) { + rules.IntermissionEnd(); + return; + } + + if (input_buttons & INPUT_BUTTON0) { + Weapons_Primary(); + } else if (input_buttons & INPUT_BUTTON4) { + Weapons_Reload(); + } else if (input_buttons & INPUT_BUTTON3) { + Weapons_Secondary(); + } else { + Weapons_Release(); + } + + if (input_buttons & INPUT_BUTTON5) { + Player_UseDown(); + } else { + Player_UseUp(); + } + + if (self.impulse == 100) { + Flashlight_Toggle(); + } + + if (self.impulse == 240) + Bot_AddQuick(); + + /* Uncomment rune weapons if you desire */ + if (cvar("sv_cheats") == 1) { + player pl = (player)self; + if (self.impulse == 101) { + pl.health = 100; + pl.armor = 100; + pl.g_items |= ITEM_SUIT; + Weapons_AddItem(pl, WEAPON_BROOM, -1); + Weapons_AddItem(pl, WEAPON_FORKS, -1); + Weapons_AddItem(pl, WEAPON_FRYINGPAN, -1); + /* Weapons_AddItem(pl, WEAPON_GLOVE, -1); */ + Weapons_AddItem(pl, WEAPON_HAIRSPRAY, -1); + Weapons_AddItem(pl, WEAPON_KNIFE, -1); + Weapons_AddItem(pl, WEAPON_LEGO, -1); + Weapons_AddItem(pl, WEAPON_LEGOLAUNCHER, -1); + Weapons_AddItem(pl, WEAPON_MACHETTE, -1); + Weapons_AddItem(pl, WEAPON_SODALAUNCHER, -1); + } + } + + self.impulse = 0; +} diff --git a/src/server/item_battery.qc b/src/server/item_battery.qc new file mode 100644 index 0000000..139fde4 --- /dev/null +++ b/src/server/item_battery.qc @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*QUAKED item_battery (0 0 0.8) (-16 -16 0) (16 16 36) + +HALF-LIFE (1998) ENTITY + +HEV Suit energy battery. +It adds the following energy values to the HEV Suit by default: +Skill 1 (Easy): 15 +Skill 2 (Medium): 15 +Skill 3 (Hard): 10 + +The values can be tweaked in the skill.cfg file. + +*/ +class item_battery:CBaseEntity +{ + void(void) item_battery; + virtual void(void) Respawn; + virtual void(void) touch; +}; + +void item_battery::touch(void) +{ + if (other.classname != "player") { + return; + } + + base_player pl = (base_player)other; + + if (pl.armor >= 100) { + return; + } + /* Move this somewhere else? */ + pl.armor += Skill_GetValue("battery", 10); + if (pl.armor > 100) { + pl.armor = 100; + } + + Logging_Pickup(other, this, __NULL__); + Sound_Play(other, CHAN_ITEM, "item.battery"); + + if (cvar("sv_playerslots") == 1) { + remove(self); + } else { + Hide(); + think = Respawn; + nextthink = time + 20.0f; + } +} + +void item_battery::Respawn(void) +{ + SetSolid(SOLID_TRIGGER); + SetMovetype(MOVETYPE_NONE); + /* One of the models is transparent, and we need to set additive */ + SetRenderMode(RM_ADDITIVE); + SetRenderAmt(255); + SetSize([-16,-16,0],[16,16,16]); + /* Always spawns a little above origin */ + SetOrigin(m_oldOrigin + [0,0,16]); + SetModel(m_oldModel); + + think = __NULL__; + nextthink = -1; + Sound_Play(this, CHAN_ITEM, "item.respawn"); +} + +void item_battery::item_battery(void) +{ + Sound_Precache("item.armor"); + Sound_Precache("item.respawn"); + /* TODO actually uses two models... */ + model = "models/armor.mdl"; + CBaseEntity::CBaseEntity(); + item_healthkit::Respawn(); +} diff --git a/src/server/item_healthkit.qc b/src/server/item_healthkit.qc new file mode 100644 index 0000000..416e260 --- /dev/null +++ b/src/server/item_healthkit.qc @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*QUAKED item_healthkit (0 0 0.8) (-16 -16 0) (16 16 36) + +HALF-LIFE (1998) ENTITY + +Healthkit item. +Adds 20 of health to the player. + +*/ +class item_healthkit:CBaseEntity +{ + void(void) item_healthkit; + virtual void(void) Respawn; + virtual void(void) touch; +}; + +void item_healthkit::touch(void) +{ + if (other.classname != "player") { + return; + } + + if (other.health >= other.max_health) { + return; + } + Damage_Apply(other, this, -20, 0, DMG_GENERIC); + Sound_Play(this, CHAN_ITEM, "item.healthkit"); + Logging_Pickup(other, this, __NULL__); + + if (cvar("sv_playerslots") == 1) { + remove(self); + } else { + Hide(); + think = Respawn; + nextthink = time + 20.0f; + } +} + +void item_healthkit::Respawn(void) +{ + SetSolid(SOLID_TRIGGER); + SetMovetype(MOVETYPE_NONE); + /* One of the models is transparent, and we need to set additive */ + SetRenderMode(RM_ADDITIVE); + SetRenderAmt(255); + SetSize([-16,-16,0],[16,16,16]); + /* Always spawns a little above origin */ + SetOrigin(m_oldOrigin + [0,0,16]); + SetModel(m_oldModel); + + think = __NULL__; + nextthink = -1; + Sound_Play(this, CHAN_ITEM, "item.respawn"); +} + +void item_healthkit::item_healthkit(void) +{ + Sound_Precache("item.health"); + Sound_Precache("item.respawn"); + + entity base = spawn(); + setorigin(base, this.origin); + setmodel(base, "models/health2.mdl"); + + model = "models/health.mdl"; + CBaseEntity::CBaseEntity(); + item_healthkit::Respawn(); +} diff --git a/src/server/item_rune_haste.qc b/src/server/item_rune_haste.qc new file mode 100644 index 0000000..e528cc1 --- /dev/null +++ b/src/server/item_rune_haste.qc @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * Copyright (c) 2020 Gethyn ThomasQuail + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*QUAKED item_rune_haste (0 0 0.8) (-16 -16 0) (16 16 36) + +Household DEATH! (2003) ENTITY + +Haste Rune +Doubles the player's speed for 30 seconds. + +*/ +class item_rune_haste:CBaseTrigger +{ + void(void) item_rune_haste; + + virtual void(void) touch; + virtual void(void) Respawn; +}; + +void item_rune_haste::touch(void) +{ + if (other.classname != "player") { + return; + } + + /* Make sure we don't have more than one powerup */ + + player pl = (player)other; + if (pl.g_items & ITEM_MACHETTE) { + return; + } + + if (pl.g_items & ITEM_RUNE_HASTE) { + return; + } + + Logging_Pickup(other, this, __NULL__); +/* Replace with proper sounds + * sound(other, CHAN_ITEM, "fvox/bell.wav", 1, ATTN_NORM); + sound(other, CHAN_VOICE, "fvox/hev_logon.wav", 1, ATTN_NORM); */ + pl.g_items |= ITEM_RUNE_HASTE; + pl.powerup_time = 30.0f; + + CBaseTrigger::UseTargets(this, TRIG_TOGGLE, 0.0f); + + if (cvar("sv_playerslots") == 1) { + remove(self); + } else { + Hide(); + think = Respawn; + nextthink = time + 30.0f; + } +} + +void item_rune_haste::Respawn(void) +{ + SetSolid(SOLID_TRIGGER); + SetMovetype(MOVETYPE_NONE); + SetSize(VEC_HULL_MIN, VEC_HULL_MAX); + SetOrigin(m_oldOrigin); + SetModel(m_oldModel); + + think = __NULL__; + nextthink = -1; + sound(this, CHAN_ITEM, "items/suitchargeok1.wav", 1, ATTN_NORM, 150); +} + +void item_rune_haste::item_rune_haste(void) +{ + /* Powerups have a base model always visable */ + entity base = spawn(); + setorigin(base, this.origin); + setmodel(base, "models/rune_stand.mdl"); + + model = "models/rune_haste.mdl"; + + precache_sound("items/suitchargeok1.wav"); + precache_sound("fvox/hev_logon.wav"); + precache_sound("fvox/bell.wav"); + CBaseTrigger::CBaseTrigger(); +} diff --git a/src/server/item_rune_stalker.qc b/src/server/item_rune_stalker.qc new file mode 100644 index 0000000..b3b0c0e --- /dev/null +++ b/src/server/item_rune_stalker.qc @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * Copyright (c) 2020 Gethyn ThomasQuail + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*QUAKED item_rune_stalker (0 0 0.8) (-16 -16 0) (16 16 36) + +Household DEATH! (2003) ENTITY + +Stalker Rune + +Turns the player into a health and speed boosted killer... +Includes a trademark throwable machette weapon! +*/ +class item_rune_stalker:CBaseTrigger +{ + void(void) item_rune_stalker; + + virtual void(void) touch; + virtual void(void) Respawn; +}; + +void item_rune_stalker::touch(void) +{ + if (other.classname != "player") { + return; + } + + /* Make sure we don't have more than one powerup */ + + player pl = (player)other; + if (pl.g_items & ITEM_MACHETTE) { + return; + } + + if (pl.g_items & ITEM_RUNE_HASTE) { + return; + } + + Logging_Pickup(other, this, __NULL__); + Sound_Play(other, CHAN_ITEM, "weapon_machette.pickup"); + env_message_single(other, "Activating Rune STALKER!"); + pl.g_items = ITEM_SUIT | ITEM_MACHETTE; + pl.health = 300; /* Player gets more health */ + pl.activeweapon = WEAPON_MACHETTE; + Weapons_RefreshAmmo(pl); + + CBaseTrigger::UseTargets(this, TRIG_TOGGLE, 0.0f); + + if (cvar("sv_playerslots") == 1) { + remove(self); + } else { + Hide(); + think = Respawn; + nextthink = time + 30.0f; + } +} + +void item_rune_stalker::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; + sound(this, CHAN_ITEM, "items/suitchargeok1.wav", 1, ATTN_NORM, 150); +} + +void item_rune_stalker::item_rune_stalker(void) +{ + /* Powerups have a base model always visable */ + + entity base = spawn(); + setorigin(base, this.origin); + setmodel(base, "models/rune_stand.mdl"); + + model = "models/rune_stalker.mdl"; + + Sound_Precache("weapon_machette.pickup"); + CBaseTrigger::CBaseTrigger(); +} diff --git a/src/server/item_weaponbox.qc b/src/server/item_weaponbox.qc new file mode 100644 index 0000000..09f0bd2 --- /dev/null +++ b/src/server/item_weaponbox.qc @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +class item_weaponbox:CBaseEntity +{ + int ammo_forks; + int ammo_knives; + int ammo_legogrenade; + int ammo_legos; + int ammo_soda; + int ammo_spray; + int weapon_items; + + void(void) item_weaponbox; + virtual void(void) touch; + virtual void(player) setup; +}; + +void item_weaponbox::touch(void) +{ + if (other.classname != "player") { + return; + } + + player pl = (player)other; + Logging_Pickup(other, this, __NULL__); + sound(pl, CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_NORM); + + pl.ammo_forks += ammo_forks; + pl.ammo_knives += ammo_knives; + pl.ammo_legogrenade += ammo_legogrenade; + pl.ammo_legos += ammo_legos; + pl.ammo_soda += ammo_soda; + pl.ammo_spray += ammo_spray; + + /* cull */ + pl.ammo_forks = min(pl.ammo_forks, MAX_A_FORKS); + pl.ammo_knives = min(pl.ammo_knives, MAX_A_KNIVES); + pl.ammo_legogrenade = min(pl.ammo_legogrenade, MAX_A_LEGOGRENADE); + pl.ammo_legos = min(pl.ammo_legos, MAX_A_LEGOS); + pl.ammo_soda = min(pl.ammo_soda, MAX_A_SODA); + pl.ammo_spray = min(pl.ammo_spray, MAX_A_SPRAY); + + pl.g_items |= weapon_items; + Weapons_RefreshAmmo(pl); + + remove(this); +} + +void item_weaponbox::setup(player pl) +{ + /* TODO: Should the magazine bits be transferred too? */ + ammo_forks = pl.ammo_forks; + ammo_knives = pl.ammo_knives; + ammo_legogrenade = pl.ammo_legogrenade; + ammo_legos = pl.ammo_legos; + ammo_soda = pl.ammo_soda; + ammo_spray = pl.ammo_spray; + weapon_items = pl.g_items; +} + +void item_weaponbox::item_weaponbox(void) +{ + SetModel("models/basket.mdl"); + SetSize([-16,-16,0], [16,16,16]); + SetSolid(SOLID_TRIGGER); + SetMovetype(MOVETYPE_TOSS); +} + +void weaponbox_spawn(player spawner) +{ + item_weaponbox weaponbox = spawn(item_weaponbox); + weaponbox.SetOrigin(spawner.origin); + weaponbox.setup(spawner); +} diff --git a/src/server/items.qc b/src/server/items.qc new file mode 100644 index 0000000..f70bfce --- /dev/null +++ b/src/server/items.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 item_pickup::touch(void) +{ + if (other.classname != "player") { + return; + } + + player pl = (player)other; + if (pl.g_items & ITEM_MACHETTE) { + return; + } + + /* don't remove if AddItem fails */ + if (Weapons_AddItem((player)other, id, m_iClip) == FALSE) { + return; + } + + Logging_Pickup(other, this, __NULL__); + Sound_Play(other, CHAN_ITEM, "weapon.pickup"); + + CBaseTrigger::UseTargets(this, TRIG_TOGGLE, 0.0f); + + if (real_owner || m_iWasDropped == 1 || cvar("sv_playerslots") == 1) { + remove(self); + } else { + Hide(); + think = Respawn; + nextthink = time + 30.0f; + } +} + +void item_pickup::SetItem(int i) +{ + id = i; + m_oldModel = Weapons_GetWorldmodel(id); + SetModel(m_oldModel); +} + +void item_pickup::SetFloating(int i) +{ + m_bFloating = rint(bound(0, m_bFloating, 1)); +} + +void item_pickup::Respawn(void) +{ + SetSolid(SOLID_TRIGGER); + SetOrigin(m_oldOrigin); + + /* At some points, the item id might not yet be set */ + if (m_oldModel) { + SetModel(m_oldModel); + } + + SetSize([-16,-16,0], [16,16,16]); + + think = __NULL__; + nextthink = -1; + + if (!m_iWasDropped && cvar("sv_playerslots") > 1) { + if (!real_owner) + Sound_Play(this, CHAN_ITEM, "item.respawn"); + + m_iClip = -1; + } + + if (!m_bFloating) { + droptofloor(); + SetMovetype(MOVETYPE_TOSS); + } +} + +void item_pickup::item_pickup(void) +{ + Sound_Precache("item.respawn"); + Sound_Precache("weapon.pickup"); + CBaseTrigger::CBaseTrigger(); + Respawn(); +} diff --git a/src/server/progs.src b/src/server/progs.src new file mode 100755 index 0000000..03a9427 --- /dev/null +++ b/src/server/progs.src @@ -0,0 +1,52 @@ +#pragma target fte +#pragma progs_dat "../../progs.dat" + +#define QWSSQC +#define SERVER +#define VALVE +#define HHDEATH +#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_scientist_dead.qc + +../../../valve/src/server/player.qc +../../../valve/src/server/spectator.qc +items.qc +../../../valve/src/server/item_suit.qc +item_healthkit.qc +item_battery.qc +item_rune_haste.qc +item_rune_stalker.qc +item_weaponbox.qc +../../../valve/src/server/world_items.qc +ammo_hd.qc + +gamerules.qc +../../../valve/src/server/client.qc +server.qc +../../../valve/src/server/damage.qc +../../../valve/src/server/rules.qc + +../../../src/botlib/include.src + +../../../valve/src/server/flashlight.qc +../../../base/src/server/modelevent.qc + +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..39e9cce --- /dev/null +++ b/src/server/server.qc @@ -0,0 +1,32 @@ +/* + * 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(HHDMultiplayerRules); +} + +void Game_Worldspawn(void) +{ + precache_model("models/player.mdl"); + precache_model("models/w_weaponbox.mdl"); + Sound_Precache("player.die"); + Sound_Precache("player.fall"); + + Player_Precache(); + Weapons_Init(); +} diff --git a/src/shared/events.h b/src/shared/events.h new file mode 100644 index 0000000..8d0a008 --- /dev/null +++ b/src/shared/events.h @@ -0,0 +1,22 @@ +/* + * 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_LEGO = EV_SEPARATOR, + EV_LEGOPIECE, + EV_SODA +}; diff --git a/src/shared/flags.h b/src/shared/flags.h new file mode 100644 index 0000000..879714d --- /dev/null +++ b/src/shared/flags.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "../../../valve/src/shared/flags.h" + +/* game flags */ +#define GF_UNUSED1 (1<<0) +#define GF_UNUSED2 (1<<1) +#define GF_UNUSED3 (1<<2) +#define GF_UNUSED4 (1<<3) +#define GF_UNUSED5 (1<<4) +#define GF_UNUSED6 (1<<5) +#define GF_UNUSED7 (1<<6) +#define GF_UNUSED8 (1<<7) +#define GF_UNUSED9 (1<<8) +#define GF_UNUSED10 (1<<9) +#define GF_UNUSED11 (1<<10) +#define GF_UNUSED12 (1<<11) +#define GF_UNUSED13 (1<<12) +#define GF_UNUSED14 (1<<14) +#define GF_UNUSED15 (1<<16) +#define GF_UNUSED16 (1<<13) +#define GF_UNUSED17 (1<<17) +#define GF_UNUSED18 (1<<18) +#define GF_UNUSED19 (1<<19) +#define GF_UNUSED20 (1<<20) +#define GF_UNUSED21 (1<<21) +#define GF_UNUSED22 (1<<22) +#define RUNE_VAMPIRE (1<<23) diff --git a/src/shared/fx_lego.qc b/src/shared/fx_lego.qc new file mode 100644 index 0000000..e2631d3 --- /dev/null +++ b/src/shared/fx_lego.qc @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef CLIENT +var float PARTICLE_LEGO; + +void +FX_Lego_Init(void) +{ + Sound_Precache("weapon_lego.explode"); + precache_model("models/lego.mdl"); + PARTICLE_LEGO = particleeffectnum("part_lego"); +} + +string g_fxlego_sets[12] = { +"geomset 0 1", +"geomset 0 2", +"geomset 0 3", +"geomset 0 4", +"geomset 0 5", +"geomset 0 6", +"geomset 0 7", +"geomset 0 8", +"geomset 0 9", +"geomset 0 10", +"geomset 0 11", +"geomset 0 12", +}; + +#endif + +void +FX_Lego(vector pos) +{ +#ifdef SERVER + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EV_LEGO); + WriteCoord(MSG_MULTICAST, pos[0]); + WriteCoord(MSG_MULTICAST, pos[1]); + WriteCoord(MSG_MULTICAST, pos[2]); + msg_entity = self; + multicast(pos, MULTICAST_PVS); +#else + static void Lego_Remove(void) { + remove(self); + } + + /* cast an explosive effect minus the fire */ + /* wrong decal atm, decal.wad loading is messy in HL */ + Decals_Place(pos, "{spraypaint"); + + /* has a different smoke sprite */ + env_sprite eSmoke = spawn(env_sprite); + setorigin(eSmoke, pos); + setmodel(eSmoke, "sprites/stmbal1.spr"); + Sound_Play(eSmoke, CHAN_WEAPON, "weapon_lego.explode"); + + eSmoke.effects = EF_ADDITIVE; + eSmoke.drawmask = MASK_ENGINE; + eSmoke.maxframe = modelframecount(eSmoke.modelindex); + eSmoke.loops = 0; + eSmoke.framerate = 15; + eSmoke.nextthink = time + 0.05f; + + /* there are many different lego submodels */ + for (int i = 0; i < 12; i++) { + entity eLego = spawn(); + setorigin(eLego, pos); + setmodel(eLego, "models/lego.mdl"); + eLego.movetype = MOVETYPE_BOUNCE; + eLego.gravity = 0.5f; + eLego.scale = 1.0f; + eLego.drawmask = MASK_ENGINE; + eLego.velocity = randomvec() * 192; + eLego.think = Lego_Remove; + eLego.nextthink = time + 5.0f; + eLego.solid = SOLID_BBOX; + setcustomskin(eLego, "", g_fxlego_sets[floor(random(0,12))]); + setsize(eLego, [0,0,0], [0,0,0]); + } +#endif +} diff --git a/src/shared/fx_legopiece.qc b/src/shared/fx_legopiece.qc new file mode 100644 index 0000000..9a39b03 --- /dev/null +++ b/src/shared/fx_legopiece.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. + */ + +#ifdef CLIENT +var float PARTICLE_LEGOPIECE; + +void +FX_LegoPiece_Init(void) +{ + precache_model("models/lego.mdl"); + PARTICLE_LEGOPIECE = particleeffectnum("part_legopiece"); +} +#endif + +void +FX_LegoPiece(vector pos) +{ +#ifdef SERVER + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EV_LEGOPIECE); + WriteCoord(MSG_MULTICAST, pos[0]); + WriteCoord(MSG_MULTICAST, pos[1]); + WriteCoord(MSG_MULTICAST, pos[2]); + msg_entity = self; + multicast(pos, MULTICAST_PVS); +#else + static void LegoPiece_Remove(void) { + remove(self); + } + entity eLegoPiece = spawn(); + setorigin(eLegoPiece, pos); + setmodel(eLegoPiece, "models/lego.mdl"); + eLegoPiece.movetype = MOVETYPE_BOUNCE; + eLegoPiece.gravity = 0.5f; + eLegoPiece.scale = 1.0f; + eLegoPiece.drawmask = MASK_ENGINE; + eLegoPiece.velocity = randomvec() * 128; + eLegoPiece.think = LegoPiece_Remove; + eLegoPiece.nextthink = time + 5.0f; + eLegoPiece.solid = SOLID_BBOX; + setcustomskin(eLegoPiece, "", g_fxlego_sets[floor(random(0,12))]); + setsize(eLegoPiece, [0,0,0], [0,0,0]); + +#endif +} diff --git a/src/shared/fx_soda.qc b/src/shared/fx_soda.qc new file mode 100644 index 0000000..661f69d --- /dev/null +++ b/src/shared/fx_soda.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 CLIENT +var float PARTICLE_SODA; + +void +FX_Soda_Init(void) +{ + precache_model("sprites/blooddrop.spr"); + precache_model("sprites/soda2.spr"); + PARTICLE_SODA = particleeffectnum("part_soda"); +} +#endif + +void +FX_Soda(vector pos, int col) +{ +#ifdef SERVER + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EV_SODA); + WriteCoord(MSG_MULTICAST, pos[0]); + WriteCoord(MSG_MULTICAST, pos[1]); + WriteCoord(MSG_MULTICAST, pos[2]); + WriteByte(MSG_MULTICAST, col); + msg_entity = self; + multicast(pos, MULTICAST_PVS); +#else + + vector color; + /* Decals use a palette to set color, we need to set sprite colors by hand. + * The order is important, we need to keep the clients and server in sync + * for the view model, soda projectile, and soda spill effect */ + switch (col) { + case 1: + Decals_Place(pos, "{sodared"); + color = [1,0,0]; + break; + case 2: + Decals_Place(pos, "{sodapurple"); + color = [1,0,1]; + break; + case 3: + Decals_Place(pos, "{sodayellow"); + color = [1,1,0]; + break; + case 4: + Decals_Place(pos, "{sodablue"); + color = [0,0,1]; + break; + case 5: + Decals_Place(pos, "{sodabrown"); + color = [1,0.5,0]; + break; + default: + Decals_Place(pos, "{sodagreen"); + color = [0,1,0]; + + break; + } + + /* enchancement from mod */ + env_sprite eSoda = spawn(env_sprite); + setorigin(eSoda, pos); + setmodel(eSoda, "sprites/soda1.spr"); + + eSoda.effects = EF_ADDITIVE; + eSoda.drawmask = MASK_ENGINE; + eSoda.maxframe = modelframecount(eSoda.modelindex); + eSoda.loops = 0; + eSoda.scale = 1.0f; + eSoda.m_vecRenderColor = color; + eSoda.framerate = 20; + eSoda.nextthink = time + 0.05f; + + static void Soda_Remove(void) { + remove(self); + } + + /* Has a dripping effect from spill */ + for (int i = 0; i < 10; i++) { + env_sprite eSodaPart = spawn(env_sprite); + setorigin(eSodaPart, pos); + setmodel(eSodaPart, "sprites/soda2.spr"); + eSodaPart.movetype = MOVETYPE_TOSS; + eSodaPart.gravity = 0.3f; + eSodaPart.scale = 0.5f; + eSodaPart.effects = EF_ADDITIVE; + eSodaPart.drawmask = MASK_ENGINE; + eSodaPart.maxframe = modelframecount(eSodaPart.modelindex); + eSodaPart.loops = 1; + eSodaPart.m_vecRenderColor = color; + eSodaPart.framerate = 10; + eSodaPart.touch = Soda_Remove; + eSodaPart.nextthink = time + 0.1f; + eSodaPart.velocity = randomvec() * 128; + eSodaPart.solid = SOLID_BBOX; + setsize(eSodaPart, [0,0,0], [0,0,0]); + } +#endif +} diff --git a/src/shared/include.src b/src/shared/include.src new file mode 100644 index 0000000..9a22b26 --- /dev/null +++ b/src/shared/include.src @@ -0,0 +1,36 @@ + #includelist +../../../valve/src/shared/entities.h +flags.h +events.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 +fx_lego.qc +fx_legopiece.qc +fx_soda.qc + +items.h +weapons.h +w_broom.qc +w_forks.qc +w_fryingpan.qc +w_glove.qc +w_hairspray.qc +w_knife.qc +w_lego.qc +w_legolauncher.qc +w_machette.qc +w_sodalauncher.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..f567a7c --- /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_BROOM 0x00000001i +#define ITEM_FORKS 0x00000002i +#define ITEM_FRYINGPAN 0x00000004i +#define ITEM_GLOVE 0x00000008i +#define ITEM_HAIRSPRAY 0x00000010i +#define ITEM_KNIFE 0x00000020i +#define ITEM_LEGO 0x00000040i +#define ITEM_LEGOLAUNCHER 0x00000080i + +#define ITEM_MACHETTE 0x00000100i +#define ITEM_SODALAUNCHER 0x00000200i +#define ITEM_SUIT 0x00000400i +#define ITEM_LONGJUMP 0x00000800i +#define ITEM_RUNE_HASTE 0x00001000i +#define ITEM_UNUSED14 0x00002000i +#define ITEM_UNUSED15 0x00004000i +#define ITEM_UNUSED16 0x00008000i + +#define ITEM_UNUSED17 0x00010000i +#define ITEM_UNUSED18 0x00020000i +#define ITEM_UNUSED19 0x00040000i +#define ITEM_UNUSED20 0x00080000i +#define ITEM_UNUSED21 0x00100000i +#define ITEM_UNUSED22 0x00200000i +#define ITEM_UNUSED23 0x00400000i +#define ITEM_UNUSED24 0x00800000i + +#define ITEM_UNUSED25 0x01000000i +#define ITEM_UNUSED26 0x02000000i +#define ITEM_UNUSED27 0x04000000i +#define ITEM_UNUSED28 0x08000000i +#define ITEM_UNUSED29 0x10000000i +#define ITEM_UNUSED30 0x20000000i +#define ITEM_UNUSED31 0x40000000i +#define ITEM_UNUSED32 0x80000000i diff --git a/src/shared/player.qc b/src/shared/player.qc new file mode 100644 index 0000000..f938efc --- /dev/null +++ b/src/shared/player.qc @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +int input_sequence; + +class player:base_player +{ +#ifdef CLIENT + /* External model */ + entity p_model; + int p_hand_bone; + int p_model_bone; + float pitch; + float lastweapon; + + virtual void(void) gun_offset; + virtual void(void) draw; + virtual float() predraw; + virtual void(void) postdraw; +#else + int ammo_forks; + int ammo_knives; + int ammo_legogrenade; + int ammo_legos; + int ammo_soda; + int ammo_spray; + float powerup_time; +#endif +}; diff --git a/src/shared/pmove.qc b/src/shared/pmove.qc new file mode 100644 index 0000000..522f1df --- /dev/null +++ b/src/shared/pmove.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. + */ + +#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) +{ + /* The Haste Rune doubles player speed */ + if (target.g_items & 0x00001000i) { + return (target.flags & FL_CROUCHING) ? 200 : 420; + } else { + return (target.flags & FL_CROUCHING) ? 100 : 220; + } +} + +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); + /* TODO add armor specific fall damage */ + 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 { + 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_broom.qc b/src/shared/w_broom.qc new file mode 100644 index 0000000..a3bee67 --- /dev/null +++ b/src/shared/w_broom.qc @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * Copyright (c) 2019-2020 Gethyn ThomasQuail + * + * 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 +{ + BROOM_IDLE, + BROOM_DRAW, + BROOM_HOLSTER, + BROOM_ATTACK1HIT, + BROOM_ATTACK1MISS, + BROOM_ATTACK2MISS, + BROOM_ATTACK2HIT, + BROOM_ATTACK3MISS, + BROOM_ATTACK3HIT +}; + +void +w_broom_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_broom.hit"); + Sound_Precache("weapon_broom.miss"); + Sound_Precache("weapon_broom.hitbody"); +#endif + precache_model("models/v_broom.mdl"); + precache_model("models/w_broom.mdl"); + precache_model("models/p_broom.mdl"); +} + +void +w_broom_updateammo(player pl) +{ +#ifdef SERVER + Weapons_UpdateAmmo(pl, -1, -1, -1); +#endif +} + +string +w_broom_wmodel(void) +{ + return "models/w_broom.mdl"; + /* model = "models/w_broom.mdl"; + SetRenderMode(RM_FULLBRIGHT); + SetRenderAmt(255); */ +} +string +w_broom_pmodel(void) +{ + return "models/p_broom.mdl"; +} + +string +w_broom_deathmsg(void) +{ + return ""; +} + +void +w_broom_draw(void) +{ + Weapons_SetModel("models/v_broom.mdl"); + Weapons_ViewAnimation(BROOM_DRAW); +} + +void +w_broom_holster(void) +{ + Weapons_ViewAnimation(BROOM_HOLSTER); +} + +void +w_broom_primary(void) +{ + int anim = 0; + vector src; + player pl = (player)self; + + if (pl.w_attack_next) { + return; + } + + Weapons_MakeVectors(); + src = pl.origin + pl.view_ofs; + + /* make sure we can gib corpses */ + int oldhitcontents = self.hitcontentsmaski; + self.hitcontentsmaski = CONTENTBITS_POINTSOLID | CONTENTBIT_CORPSE; + traceline(src, src + (v_forward * 32), FALSE, pl); + self.hitcontentsmaski = oldhitcontents; + + if (trace_fraction >= 1.0) { + pl.w_attack_next = 0.5f; + } else { + pl.w_attack_next = 0.25f; + } + pl.w_idle_next = 2.5f; + + int r = (float)input_sequence % 3; + switch (r) { + case 0: + Weapons_ViewAnimation(trace_fraction >= 1 ? BROOM_ATTACK1MISS:BROOM_ATTACK1HIT); + break; + case 1: + Weapons_ViewAnimation(trace_fraction >= 1 ? BROOM_ATTACK2MISS:BROOM_ATTACK2HIT); + break; + default: + Weapons_ViewAnimation(trace_fraction >= 1 ? BROOM_ATTACK3MISS:BROOM_ATTACK3HIT); + } + +#ifdef SERVER + if (pl.flags & FL_CROUCHING) { + Animation_PlayerTopTemp(ANIM_SHOOTCROWBAR, 0.5f); + } else { + Animation_PlayerTopTemp(ANIM_CR_SHOOTCROWBAR, 0.42f); + } + + Sound_Play(self, CHAN_WEAPON, "weapon_broom.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]); + } + + if (trace_ent.takedamage) { + Damage_Apply(trace_ent, pl, Skill_GetValue("plr_crowbar", 10), WEAPON_BROOM, DMG_BLUNT); + if (trace_ent.iBleeds) { + Sound_Play(self, CHAN_WEAPON, "weapon_broom.hitbody"); + } + } else { + Sound_Play(self, CHAN_WEAPON, "weapon_broom.hit"); + } +#endif +} + +void +w_broom_release(void) +{ + player pl = (player)self; + + if (pl.w_idle_next) { + return; + } + + Weapons_ViewAnimation(BROOM_IDLE); + pl.w_idle_next = 15.0f; +} + + +float +w_broom_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CR_AIMCROWBAR : ANIM_AIMCROWBAR; +} + +void +w_broom_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + if (selected) { + drawsubpic( + pos, + [170,45], + "sprites/640hud4.spr_0.tga", + [0,0], + [170/256,45/256], + g_hud_color, + a, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + "sprites/640hud1.spr_0.tga", + [0,0], + [170/256,45/256], + g_hud_color, + a, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_broom = +{ + .name = "broom", + .id = ITEM_BROOM, + .slot = 0, + .slot_pos = 0, + .draw = w_broom_draw, + .holster = w_broom_holster, + .primary = w_broom_primary, + .secondary = __NULL__, + .reload = __NULL__, + .release = w_broom_release, + .crosshair = __NULL__, + .precache = w_broom_precache, + .pickup = __NULL__, + .updateammo = w_broom_updateammo, + .wmodel = w_broom_wmodel, + .pmodel = w_broom_pmodel, + .deathmsg = w_broom_deathmsg, + .aimanim = w_broom_aimanim, + .hudpic = w_broom_hudpic +}; + +/* entity definitions for pickups */ +#ifdef SERVER +void +weapon_broom(void) +{ + Weapons_InitItem(WEAPON_BROOM); + /*item_pickup item = (item_pickup)self; + item.SetRenderMode(RM_FULLBRIGHT); + item.SetRenderAmt(1.0f);*/ +} +#endif diff --git a/src/shared/w_forks.qc b/src/shared/w_forks.qc new file mode 100644 index 0000000..4bc81ac --- /dev/null +++ b/src/shared/w_forks.qc @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * Copyright (c) 2019-2020 Gethyn ThomasQuail + * + * 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_forks (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_fork.mdl" + +Household DEATH! (2003) ENTITY + +Throwable Forks Weapon + +Pile of 6 forks. + +*/ + +enum +{ + FORKS_IDLE1, + FORKS_IDLE2, + FORKS_IDLE3, + FORKS_STAB, + FORKS_THROW, + FORKS_DRAW, + FORKS_HOLSTER +}; + +void +w_forks_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_forks.hit"); + Sound_Precache("weapon_forks.hitbody"); + Sound_Precache("weapon_forks.stick"); + Sound_Precache("weapon_forks.throw"); +#endif + precache_model("sprites/hud_fork.spr"); + precache_model("models/v_fork.mdl"); + precache_model("models/w_fork.mdl"); + precache_model("models/p_fork.mdl"); +} + +void +w_forks_updateammo(player pl) +{ +#ifdef SERVER + Weapons_UpdateAmmo(pl, __NULL__, pl.ammo_forks, __NULL__); +#endif +} + +string +w_forks_wmodel(void) +{ + return "models/w_forks.mdl"; +} +string +w_forks_pmodel(void) +{ + return "models/p_fork.mdl"; +} + +string +w_forks_deathmsg(void) +{ + return ""; +} + +int +w_forks_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + if (pl.ammo_forks < MAX_A_FORKS) { + pl.ammo_forks = bound(0, pl.ammo_forks + 6, MAX_A_FORKS); + } else { + return FALSE; + } +#endif + return TRUE; +} +void +w_forks_draw(void) +{ + Weapons_SetModel("models/v_fork.mdl"); + Weapons_ViewAnimation(FORKS_DRAW); +} + +void +w_forks_holster(void) +{ + Weapons_ViewAnimation(FORKS_HOLSTER); +} + +void +w_forks_primary(void) +{ + int anim = 0; + vector src; + player pl = (player)self; + + if (pl.w_attack_next) { + return; + } + + Weapons_MakeVectors(); + src = pl.origin + pl.view_ofs; + traceline(src, src + (v_forward * 32), FALSE, pl); + + + pl.w_attack_next = 0.4f; + pl.w_idle_next = 2.5f; + + Weapons_ViewAnimation(FORKS_STAB); + +#ifdef SERVER + if (pl.flags & FL_CROUCHING) { + Animation_PlayerTopTemp(ANIM_SHOOTCROWBAR, 0.5f); + } else { + Animation_PlayerTopTemp(ANIM_CR_SHOOTCROWBAR, 0.42f); + } + + if (trace_fraction >= 1.0) { + return; + } + + /* don't bother with decals, we got squibs */ + if (trace_ent.iBleeds) { + FX_Blood(trace_endpos, [1,0,0]); + } else { + FX_Impact(IMPACT_MELEE, trace_endpos, trace_plane_normal); + } + + if (trace_ent.takedamage) { + Damage_Apply(trace_ent, pl, 5, WEAPON_FORKS, DMG_BLUNT); + if (trace_ent.iBleeds) { + Sound_Play(self, CHAN_WEAPON, "weapon_forks.hitbody"); + } + } else { + Sound_Play(self, CHAN_WEAPON, "weapon_forks.hit"); + } +#endif +} + +void +w_forks_secondary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + + /* Ammo check */ +#ifdef CLIENT + if (pl.a_ammo2 <= 0) { + return; + } +#else + if (pl.ammo_forks <= 0) { + return; + } +#endif + + /* Secondary has an ammo based throw function */ +#ifdef SERVER + static void Fork_Touch(void) { + setmodel(self, "models/fork.mdl"); + + /* don't bother with decals, we got squibs */ + if (trace_ent.iBleeds) { + FX_Blood(trace_endpos, [1,0,0]); + remove(self); + } else { + FX_Impact(IMPACT_DEFAULT, trace_endpos, trace_plane_normal); + } + + if (other.takedamage == DAMAGE_YES) { + Damage_Apply(other, self.owner, 15, WEAPON_FORKS, DMG_GENERIC); + Sound_Play(self, CHAN_WEAPON, "weapon_forks.hitbody"); + } else { + Sound_Play(self, CHAN_WEAPON, "weapon_forks.stick"); + } + self.movetype = MOVETYPE_NONE; + } + + Weapons_MakeVectors(); + entity fork = spawn(); + setmodel(fork, "models/fork.mdl"); + setorigin(fork, Weapons_GetCameraPos() + (v_forward * 16)); + fork.owner = self; + fork.velocity = v_forward * 2000; + fork.movetype = MOVETYPE_FLY; + fork.solid = SOLID_BBOX; + fork.angles = vectoangles(fork.velocity); + fork.avelocity[2] = 10; + fork.touch = Fork_Touch; + setsize(fork, [0,0,0], [0,0,0]); + + pl.ammo_forks--; + Sound_Play(pl, CHAN_WEAPON, "weapon_forks.throw"); +#else + Weapons_ViewPunchAngle([-2,0,0]); + Weapons_ViewAnimation(FORKS_THROW); +#endif + + pl.w_attack_next = 0.5f; + pl.w_idle_next = 5.0f; +} + +void +w_forks_release(void) +{ + player pl = (player)self; + + if (pl.w_idle_next > 0.0) { + return; + } + + int r; + r = (float)input_sequence % 3; + switch (r) { + case 1: + Weapons_ViewAnimation(FORKS_IDLE1); + pl.w_idle_next = 1.3f; + break; + case 2: + Weapons_ViewAnimation(FORKS_IDLE2); + pl.w_idle_next = 3.0f; + break; + default: + Weapons_ViewAnimation(FORKS_IDLE3); + pl.w_idle_next = 1.285f; + } +} + +void +w_forks_crosshair(void) +{ +#ifdef CLIENT + static vector cross_pos; + cross_pos = g_hudmins + (g_hudres / 2) + [-12,-12]; + drawsubpic( + cross_pos, + [24,24], + "sprites/crosshairs.spr_0.tga", + [72/128,48/128], + [0.1875, 0.1875], + [1,1,1], + 1, + DRAWFLAG_NORMAL + ); + + HUD_DrawAmmo2(); + + vector aicon_pos = g_hudmins + [g_hudres[0] - 42, g_hudres[1] - 64]; + drawpic( + aicon_pos, + "sprites/hud_fork.spr_0.tga", + [32,64], + [1,1,1], + pSeat->m_flAmmo2Alpha, + 0 + ); +#endif +} + +float +w_forks_aimanim(void) +{ + return w_broom_aimanim(); +} + +void +w_forks_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + if (selected) { + drawsubpic( + pos, + [170,45], + "sprites/tfchud04.spr_0.tga", + [0,135/256], + [170/256,45/256], + g_hud_color, + a, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + "sprites/tfchud03.spr_0.tga", + [0,90/256], + [170/256,45/256], + g_hud_color, + a, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_forks = +{ + .name = "fork", + .id = ITEM_FORKS, + .slot = 0, + .slot_pos = 2, + .draw = w_forks_draw, + .holster = w_forks_holster, + .primary = w_forks_primary, + .secondary = w_forks_secondary, + .reload = __NULL__, + .release = w_forks_release, + .crosshair = w_forks_crosshair, + .precache = w_forks_precache, + .pickup = w_forks_pickup, + .updateammo = w_forks_updateammo, + .wmodel = w_forks_wmodel, + .pmodel = w_forks_pmodel, + .deathmsg = w_forks_deathmsg, + .aimanim = w_forks_aimanim, + .hudpic = w_forks_hudpic +}; + +/* entity definitions for pickups */ +#ifdef SERVER +void +weapon_forks(void) +{ + Weapons_InitItem(WEAPON_FORKS); + /*item_pickup item = (item_pickup)self; + item.SetRenderMode(RM_FULLBRIGHT); + item.SetRenderAmt(1.0f);*/ +} +#endif diff --git a/src/shared/w_fryingpan.qc b/src/shared/w_fryingpan.qc new file mode 100644 index 0000000..7378231 --- /dev/null +++ b/src/shared/w_fryingpan.qc @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * Copyright (c) 2019-2020 Gethyn ThomasQuail + * + * 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 +{ + FRYINGPAN_IDLE1, + FRYINGPAN_IDLE2, + FRYINGPAN_HIT1, + FRYINGPAN_HIT2, + FRYINGPAN_DRAW, + FRYINGPAN_HOLSTER +}; + +void +w_fryingpan_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_fryingpan.hit"); + Sound_Precache("weapon_fryingpan.miss"); + Sound_Precache("weapon_fryingpan.hitbody"); +#endif + precache_model("models/v_pan.mdl"); + precache_model("models/w_pan.mdl"); + precache_model("models/p_pan.mdl"); +} + +void +w_fryingpan_updateammo(player pl) +{ + w_broom_updateammo(pl); +} + +string +w_fryingpan_wmodel(void) +{ + return "models/w_pan.mdl"; +} +string +w_fryingpan_pmodel(void) +{ + return "models/p_pan.mdl"; +} + +string +w_fryingpan_deathmsg(void) +{ + return ""; +} + +void +w_fryingpan_draw(void) +{ + Weapons_SetModel("models/v_pan.mdl"); + Weapons_ViewAnimation(FRYINGPAN_DRAW); +} + +void +w_fryingpan_holster(void) +{ + w_broom_holster(); +} + +void +w_fryingpan_primary(void) +{ + int anim = 0; + vector src; + player pl = (player)self; + + if (pl.w_attack_next) { + return; + } + + Weapons_MakeVectors(); + src = pl.origin + pl.view_ofs; + traceline(src, src + (v_forward * 32), FALSE, pl); + + + pl.w_attack_next = 1.0f; + pl.w_idle_next = 2.5f; + + Weapons_ViewAnimation(FRYINGPAN_HIT1); + +#ifdef SERVER + if (pl.flags & FL_CROUCHING) { + Animation_PlayerTopTemp(ANIM_SHOOTCROWBAR, 0.5f); + } else { + Animation_PlayerTopTemp(ANIM_CR_SHOOTCROWBAR, 0.42f); + } + + Sound_Play(self, CHAN_WEAPON, "weapon_fryingpan.miss"); + + if (trace_fraction >= 1.0) { + return; + } + + /* don't bother with decals, we got squibs */ + if (trace_ent.iBleeds) { + FX_Blood(trace_endpos, [1,0,0]); + } else { + FX_Impact(IMPACT_MELEE, trace_endpos, trace_plane_normal); + } + + if (trace_ent.takedamage) { + Damage_Apply(trace_ent, pl, 5, WEAPON_FRYINGPAN, DMG_BLUNT); + if (trace_ent.iBleeds) { + Sound_Play(self, CHAN_WEAPON, "weapon_fryingpan.hitbody"); + } + } else { + Sound_Play(self, CHAN_WEAPON, "weapon_fryingpan.hit"); + } +#endif +} + +void +w_fryingpan_secondary(void) +{ + int anim = 0; + vector src; + player pl = (player)self; + + if (pl.w_attack_next) { + return; + } + + Weapons_MakeVectors(); + src = pl.origin + pl.view_ofs; + traceline(src, src + (v_forward * 32), FALSE, pl); + + + pl.w_attack_next = 1.0f; + pl.w_idle_next = 2.5f; + + Weapons_ViewAnimation(FRYINGPAN_HIT2); + +#ifdef SERVER + if (pl.flags & FL_CROUCHING) { + Animation_PlayerTopTemp(ANIM_SHOOTCROWBAR, 0.5f); + } else { + Animation_PlayerTopTemp(ANIM_CR_SHOOTCROWBAR, 0.42f); + } + + Sound_Play(self, CHAN_WEAPON, "weapon_fryingpan.miss"); + + if (trace_fraction >= 1.0) { + return; + } + + /* don't bother with decals, we got squibs */ + if (trace_ent.iBleeds) { + FX_Blood(trace_endpos, [1,0,0]); + } else { + FX_Impact(IMPACT_MELEE, trace_endpos, trace_plane_normal); + } + + if (trace_ent.takedamage) { + Damage_Apply(trace_ent, pl, 5, WEAPON_FRYINGPAN, DMG_BLUNT); + if (trace_ent.iBleeds) { + Sound_Play(self, CHAN_WEAPON, "weapon_fryingpan.hitbody"); + } + } else { + Sound_Play(self, CHAN_WEAPON, "weapon_fryingpan.hit"); + } +#endif +} + +void +w_fryingpan_release(void) +{ + w_broom_release(); +} + +float +w_fryingpan_aimanim(void) +{ + return w_broom_aimanim(); +} + +void +w_fryingpan_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + if (selected) { + drawsubpic( + pos, + [170,45], + "sprites/tfchud04.spr_0.tga", + [0,135/256], + [170/256,45/256], + g_hud_color, + a, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + "sprites/tfchud03.spr_0.tga", + [0,90/256], + [170/256,45/256], + g_hud_color, + a, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_fryingpan = +{ + .name = "fryingpan", + .id = ITEM_FRYINGPAN, + .slot = 0, + .slot_pos = 1, + .draw = w_fryingpan_draw, + .holster = w_fryingpan_holster, + .primary = w_fryingpan_primary, + .secondary = w_fryingpan_secondary, + .reload = __NULL__, + .release = w_fryingpan_release, + .crosshair = __NULL__, + .precache = w_fryingpan_precache, + .pickup = __NULL__, + .updateammo = w_fryingpan_updateammo, + .wmodel = w_fryingpan_wmodel, + .pmodel = w_fryingpan_pmodel, + .deathmsg = w_fryingpan_deathmsg, + .aimanim = w_fryingpan_aimanim, + .hudpic = w_fryingpan_hudpic +}; + +/* entity definitions for pickups */ +#ifdef SERVER +void +weapon_fryingpan(void) +{ + Weapons_InitItem(WEAPON_FRYINGPAN); + /*item_pickup item = (item_pickup)self; + item.SetRenderMode(RM_FULLBRIGHT); + item.SetRenderAmt(1.0f);*/ +} +#endif diff --git a/src/shared/w_glove.qc b/src/shared/w_glove.qc new file mode 100644 index 0000000..e0dc691 --- /dev/null +++ b/src/shared/w_glove.qc @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * Copyright (c) 2019-2020 Gethyn ThomasQuail + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*QUAKED item_rune_slasher (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/rune_slasher.mdl" + +Household DEATH! (2003) ENTITY + +Powerup/Weapon that turns players into a similar looking legally +copyrighted character. + +*/ + +enum +{ + GLOVE_IDLE, + GLOVE_DRAW, + GLOVE_HOLSTER, + GLOVE_SLICE, + GLOVE_DICE + +}; + +void +w_glove_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_glove.hit"); + Sound_Precache("weapon_glove.miss"); + Sound_Precache("weapon_glove.hitbody"); + Sound_Precache("weapon_glove.pickup"); +#endif + precache_model("models/v_glove.mdl"); + precache_model("models/rune_slasher.mdl"); + precache_model("models/p_glove.mdl"); +} + +void +w_glove_updateammo(player pl) +{ + w_broom_updateammo(pl); +} + +string +w_glove_wmodel(void) +{ + return "models/rune_slasher.mdl"; +} +string +w_glove_pmodel(void) +{ + return "models/p_glove.mdl"; +} + +string +w_glove_deathmsg(void) +{ + return ""; +} + +void +w_glove_pickup(void) +{ +#ifdef SERVER + /* Broadcast a message and sound upon picking up Rune */ + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EV_TEXT); + WriteByte(MSG_MULTICAST, 0); + WriteString(MSG_MULTICAST, sprintf("One... Two... %s's coming for you...", self.netname)); + WriteFloat(MSG_MULTICAST, -1); + WriteFloat(MSG_MULTICAST, -1); + WriteByte(MSG_MULTICAST, 0); + WriteByte(MSG_MULTICAST, 255); + WriteByte(MSG_MULTICAST, 0); + WriteByte(MSG_MULTICAST, 0); + WriteByte(MSG_MULTICAST, 255); + WriteByte(MSG_MULTICAST, 0); + WriteByte(MSG_MULTICAST, 0); + WriteFloat(MSG_MULTICAST, 0.2); + WriteFloat(MSG_MULTICAST, 0.5); + WriteFloat(MSG_MULTICAST, 5.0); + WriteFloat(MSG_MULTICAST, 0); + + msg_entity = world; + multicast([0,0,0], MULTICAST_ALL); + + Sound_Play(self, CHAN_ITEM, "weapon_glove.pickup"); +#endif + return TRUE; +} + +void +w_glove_draw(void) +{ + Weapons_SetModel("models/v_glove.mdl"); + Weapons_ViewAnimation(GLOVE_DRAW); +} + +void +w_glove_holster(void) +{ + Weapons_ViewAnimation(GLOVE_HOLSTER); +} + +void +w_glove_primary(void) +{ + int anim = 0; + vector src; + player pl = (player)self; + + if (pl.w_attack_next) { + return; + } + + Weapons_MakeVectors(); + src = pl.origin + pl.view_ofs; + traceline(src, src + (v_forward * 32), FALSE, pl); + + if (trace_fraction >= 1.0) { + pl.w_attack_next = 0.5f; + } else { + pl.w_attack_next = 0.25f; + } + pl.w_idle_next = 2.5f; + + int r = (float)input_sequence % 2; + switch (r) { + case 1: + Weapons_ViewAnimation(GLOVE_SLICE); + pl.w_idle_next = 3.6f; + break; + default: + Weapons_ViewAnimation(GLOVE_DICE); + pl.w_idle_next = 3.6f; + break; + } + +#ifdef SERVER + if (pl.flags & FL_CROUCHING) { + Animation_PlayerTopTemp(ANIM_SHOOTCROWBAR, 0.5f); + } else { + Animation_PlayerTopTemp(ANIM_CR_SHOOTCROWBAR, 0.42f); + } + + Sound_Play(self, CHAN_WEAPON, "weapon_glove.miss"); + + if (trace_fraction >= 1.0) { + return; + } + + /* Since it has custom decals we do both FX and decal placement */ + if (trace_ent.iBleeds) { + FX_Blood(trace_endpos, [1,0,0]); + } else { + FX_Spark(trace_endpos, trace_plane_normal); + int r = (float)input_sequence % 2; + Decals_Place(trace_endpos, r == 1 ? "{slash1" : "{slash2"); + + } + + if (trace_ent.takedamage) { + Damage_Apply(trace_ent, pl, Skill_GetValue("plr_crowbar", 10), WEAPON_GLOVE, DMG_BLUNT); + if (trace_ent.iBleeds) { + Sound_Play(self, CHAN_WEAPON, "weapon_glove.hitbody"); + } + } else { + Sound_Play(self, CHAN_WEAPON, "weapon_glove.hit"); + } +#endif +} + +void +w_glove_release(void) +{ + player pl = (player)self; + + if (pl.w_idle_next) { + return; + } + + Weapons_ViewAnimation(GLOVE_IDLE); + pl.w_idle_next = 2.0f; +} + +float +w_glove_aimanim(void) +{ + return w_broom_aimanim(); +} + +void +w_glove_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + if (selected) { + drawsubpic( + pos, + [170,45], + "sprites/tfchud04.spr_0.tga", + [0,135/256], + [170/256,45/256], + g_hud_color, + a, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + "sprites/tfchud03.spr_0.tga", + [0,90/256], + [170/256,45/256], + g_hud_color, + a, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_glove = +{ + .name = "glove", + .id = ITEM_GLOVE, + .slot = 1, + .slot_pos = 0, + .draw = w_glove_draw, + .holster = w_glove_holster, + .primary = w_glove_primary, + .secondary = __NULL__, + .reload = __NULL__, + .release = w_glove_release, + .crosshair = __NULL__, + .precache = w_glove_precache, + .pickup = w_glove_pickup, + .updateammo = w_glove_updateammo, + .wmodel = w_glove_wmodel, + .pmodel = w_glove_pmodel, + .deathmsg = w_glove_deathmsg, + .aimanim = w_glove_aimanim, + .hudpic = w_glove_hudpic +}; + +/* entity definitions for pickups */ +#ifdef SERVER +void +item_rune_slasher(void) +{ + Weapons_InitItem(WEAPON_GLOVE); +} +#endif diff --git a/src/shared/w_hairspray.qc b/src/shared/w_hairspray.qc new file mode 100644 index 0000000..8fbd1f3 --- /dev/null +++ b/src/shared/w_hairspray.qc @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * Copyright (c) 2019-2020 Gethyn ThomasQuail + * + * 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_hairspray (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_knife.mdl" + +Household DEATH! (2003) ENTITY + +Flamethrower Hairspray Weapon + +*/ + +enum +{ + HAIRSPRAY_IDLE, + HAIRSPRAY_FIRE, + HAIRSPRAY_DRAW, + HAIRSPRAY_HOLSTER +}; + +void +w_hairspray_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_hairspray.fire"); +#endif + precache_model("sprites/flame.spr"); + precache_model("sprites/hud_haircan.spr"); + precache_model("models/v_hairspray.mdl"); + precache_model("models/w_hairspray.mdl"); + precache_model("models/p_hairspray.mdl"); +} + +void +w_hairspray_updateammo(player pl) +{ +#ifdef SERVER + Weapons_UpdateAmmo(pl, __NULL__, pl.ammo_spray, __NULL__); +#endif +} + +string +w_hairspray_wmodel(void) +{ + return "models/w_hairspray.mdl"; +} + +string +w_hairspray_pmodel(void) +{ + return "models/p_hairspray.mdl"; +} + +string +w_hairspray_deathmsg(void) +{ + return ""; +} + +int +w_hairspray_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + if (pl.ammo_spray < MAX_A_SPRAY) { + pl.ammo_spray = bound(0, pl.ammo_spray + 20, MAX_A_SPRAY); + } else { + return FALSE; + } +#endif + return TRUE; +} + +void +w_hairspray_draw(void) +{ + Weapons_SetModel("models/v_hairspray.mdl"); + Weapons_ViewAnimation(HAIRSPRAY_DRAW); +} + +void +w_hairspray_holster(void) +{ + Weapons_ViewAnimation(HAIRSPRAY_HOLSTER); +} + + +#ifdef SERVER +void +Flame_Touch(void) +{ + if (other.takedamage != DAMAGE_YES) { + remove(self); + return; + } + + /* anything else that can take damage */ + Damage_Apply(other, self.owner, 40, WEAPON_HAIRSPRAY, DMG_BURN); + remove(self); +} +#endif + + +void +w_hairspray_primary(void) +{ + player pl = (player)self; + if (pl.w_attack_next > 0.0) { + return; + } + + /* Ammo check */ +#ifdef CLIENT + if (pl.a_ammo2 <= 0) { + return; + } +#else + if (pl.ammo_spray <= 0) { + return; + } +#endif + + /* Actual firing */ +#ifdef CLIENT + Weapons_ViewAnimation(HAIRSPRAY_FIRE); + pl.a_ammo2--; +#else + Weapons_MakeVectors(); + entity flame = spawn(); + setmodel(flame, "sprites/flame.spr"); + setorigin(flame, Weapons_GetCameraPos() + (v_forward * 16)); + flame.owner = self; + flame.velocity = v_forward * 300; + flame.movetype = MOVETYPE_FLYMISSILE; + flame.solid = SOLID_BBOX; + //flame.flags |= FL_LAGGEDMOVE; + flame.angles = vectoangles(flame.velocity); + flame.avelocity[2] = 10; + flame.touch = Flame_Touch; + flame.think = Flame_Touch; + flame.nextthink = time + 1.0f; + flame.effects |= EF_BRIGHTLIGHT; + setsize(flame, [0,0,0], [0,0,0]); + + pl.ammo_spray--; + Sound_Play(pl, CHAN_WEAPON, "weapon_hairspray.fire"); + + /* Remove weapon if we run out of ammo */ + if (pl.ammo_spray <= 0) { + Weapons_RemoveItem(pl, WEAPON_HAIRSPRAY); + return; + } +#endif + + pl.w_attack_next = 0.2f; + pl.w_idle_next = 2.5f; +} + +void +w_hairspray_release(void) +{ + player pl = (player)self; + + if (pl.w_idle_next) { + return; + } + + sound(pl, CHAN_WEAPON, "", 1.0, ATTN_NORM); + Weapons_ViewAnimation(HAIRSPRAY_IDLE); + pl.w_idle_next = 2.0f; +} +void +w_hairspray_crosshair(void) +{ +#ifdef CLIENT + static vector cross_pos; + cross_pos = g_hudmins + (g_hudres / 2) + [-12,-12]; + drawsubpic( + cross_pos, + [24,24], + "sprites/crosshairs.spr_0.tga", + [72/128,48/128], + [0.1875, 0.1875], + [1,1,1], + 1, + DRAWFLAG_NORMAL + ); + + HUD_DrawAmmo2(); + + vector aicon_pos = g_hudmins + [g_hudres[0] - 42, g_hudres[1] - 64]; + drawpic( + aicon_pos, + "sprites/hud_haircan.spr_0.tga", + [32,64], + [1,1,1], + pSeat->m_flAmmo2Alpha, + 0 + ); +#endif +} + +float +w_hairspray_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CR_AIMBOW : ANIM_AIMBOW; +} + +void +w_hairspray_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + if (selected) { + drawsubpic( + pos, + [170,45], + "sprites/tfchud04.spr_0.tga", + [0,45/256], + [170/256,45/256], + g_hud_color, + a, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + "sprites/tfchud03.spr_0.tga", + [0,0/256], + [170/256,45/256], + g_hud_color, + a, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_hairspray = +{ + .name = "flame", + .id = ITEM_HAIRSPRAY, + .slot = 0, + .slot_pos = 6, + .draw = w_hairspray_draw, + .holster = w_hairspray_holster, + .primary = w_hairspray_primary, + .secondary = __NULL__, + .reload = __NULL__, + .release = w_hairspray_release, + .crosshair = w_hairspray_crosshair, + .precache = w_hairspray_precache, + .pickup = w_hairspray_pickup, + .updateammo = w_hairspray_updateammo, + .wmodel = w_hairspray_wmodel, + .pmodel = w_hairspray_pmodel, + .deathmsg = w_hairspray_deathmsg, + .aimanim = w_hairspray_aimanim, + .hudpic = w_hairspray_hudpic +}; + +#ifdef SERVER +void +weapon_hairspray(void) +{ + Weapons_InitItem(WEAPON_HAIRSPRAY); + /*item_pickup item = (item_pickup)self; + item.SetRenderMode(RM_FULLBRIGHT); + item.SetRenderAmt(1.0f);*/ +} +#endif diff --git a/src/shared/w_knife.qc b/src/shared/w_knife.qc new file mode 100644 index 0000000..7d4f9f3 --- /dev/null +++ b/src/shared/w_knife.qc @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*QUAKED weapon_knife (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_knife.mdl" + +Household DEATH! (2003) ENTITY + +Throwable Butcher/Cleaver Knife Weapon + +*/ + +enum +{ + KNIFE_IDLE1, + KNIFE_IDLE2, + KNIFE_IDLE3, + KNIFE_THROW, + KNIFE_DRAW, + KNIFE_HOLSTER +}; + +void +w_knife_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_knife.hitbody"); + Sound_Precache("weapon_knife.move"); + Sound_Precache("weapon_knife.stick"); + Sound_Precache("weapon_knife.throw"); +#endif + precache_model("sprites/hud_knife.spr"); + precache_model("models/knife.mdl"); + precache_model("models/v_knife.mdl"); + precache_model("models/w_knife.mdl"); + precache_model("models/p_knife.mdl"); +} + +void +w_knife_updateammo(player pl) +{ +#ifdef SERVER + Weapons_UpdateAmmo(pl, __NULL__, pl.ammo_knives, __NULL__); +#endif +} + +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 ""; +} + +int +w_knife_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (pl.ammo_knives < MAX_A_KNIVES) { + pl.ammo_knives = bound(0, pl.ammo_knives + 1, MAX_A_KNIVES); + } else { + return FALSE; + } +#endif + return TRUE; +} + +void +w_knife_draw(void) +{ + Weapons_SetModel("models/v_knife.mdl"); + Weapons_ViewAnimation(KNIFE_DRAW); +} + +void +w_knife_holster(void) +{ + Weapons_ViewAnimation(KNIFE_HOLSTER); +} + +void +w_knife_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + + /* Ammo check */ +#ifdef CLIENT + if (pl.a_ammo2 <= 0) { + return; + } +#else + if (pl.ammo_knives <= 0) { + return; + } +#endif + + /* Actual firing */ +#ifdef SERVER + static void Knife_Touch(void) { + /* Has two submodels, changes random in view and world model */ + setmodel(self, "models/knife.mdl"); + + /* Bleed and remove model if player, stick and spark if wall */ + if (trace_ent.iBleeds) { + FX_Blood(trace_endpos, [1,0,0]); + remove(self); + } else { + FX_Spark(trace_endpos, trace_plane_normal); + } + + if (other.takedamage == DAMAGE_YES) { + Damage_Apply(other, self.owner, 15, WEAPON_KNIFE, DMG_GENERIC); + Sound_Play(self, CHAN_WEAPON, "weapon_knife.hitbody"); + } else { + Sound_Play(self, CHAN_WEAPON, "weapon_knife.stick"); + } + self.movetype = MOVETYPE_NONE; + } + + Weapons_MakeVectors(); + entity knife = spawn(); + setmodel(knife, "models/w_knife.mdl"); + setorigin(knife, Weapons_GetCameraPos()); + knife.owner = self; + knife.velocity = v_forward * 1200; + knife.movetype = MOVETYPE_FLY; + knife.solid = SOLID_BBOX; + knife.angles = vectoangles(knife.velocity); + knife.avelocity[2] = 10; + knife.touch = Knife_Touch; + /* TODO Knife randomly changes submodel upon firing, no API support yet + * int r = (float)input_sequence % 2; + * setcustomskin(knife, "", r == 1 ? "geomset 1 0\n" : "geomset 1 1\n"); */ + setsize(knife, [0,0,0], [0,0,0]); + + /* The thrown knife has it's own movement sound */ + Sound_Play(knife, CHAN_WEAPON, "weapon_knife.move"); + + pl.ammo_knives--; + Sound_Play(pl, CHAN_WEAPON, "weapon_knife.throw"); + + /* Remove weapon if we run out of knives */ + if (pl.ammo_knives <= 0) { + Weapons_RemoveItem(pl, WEAPON_KNIFE); + return; + } +#else + Weapons_ViewPunchAngle([-2,0,0]); + Weapons_ViewAnimation(KNIFE_THROW); + /* Knife randomly changes submodel upon firing */ + int r = (float)input_sequence % 2; + Weapons_SetGeomset(r == 1 ? "geomset 0 0\n" : "geomset 0 1\n"); +#endif + + pl.w_attack_next = 0.8f; + pl.w_idle_next = 5.0f; +} + +void +w_knife_release(void) +{ + player pl = (player)self; + + if (pl.w_idle_next > 0.0) { + return; + } + + int r; + r = (float)input_sequence % 3; + switch (r) { + case 1: + Weapons_ViewAnimation(KNIFE_IDLE1); + pl.w_idle_next = 2.0f; + break; + case 2: + Weapons_ViewAnimation(KNIFE_IDLE2); + pl.w_idle_next = 3.0f; + break; + default: + Weapons_ViewAnimation(KNIFE_IDLE3); + pl.w_idle_next = 1.285f; + } +} + +void +w_knife_crosshair(void) +{ +#ifdef CLIENT + static vector cross_pos; + cross_pos = g_hudmins + (g_hudres / 2) + [-12,-12]; + drawsubpic( + cross_pos, + [24,24], + "sprites/crosshairs.spr_0.tga", + [72/128,48/128], + [0.1875, 0.1875], + [1,1,1], + 1, + DRAWFLAG_NORMAL + ); + + HUD_DrawAmmo2(); + + vector aicon_pos = g_hudmins + [g_hudres[0] - 42, g_hudres[1] - 64]; + drawpic( + aicon_pos, + "sprites/hud_knife.spr_0.tga", + [32,64], + [1,1,1], + pSeat->m_flAmmo2Alpha, + 0 + ); +#endif +} + +float +w_knife_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CR_AIMBOW : ANIM_AIMBOW; +} + +void +w_knife_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + if (selected) { + drawsubpic( + pos, + [170,45], + "sprites/640hud5.spr_0.tga", + [0,0], + [170/256,45/256], + g_hud_color, + a, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + "sprites/640hud2.spr_0.tga", + [0,0], + [170/256,45/256], + g_hud_color, + a, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_knife = +{ + .name = "knife", + .id = ITEM_KNIFE, + .slot = 0, + .slot_pos = 3, + .draw = w_knife_draw, + .holster = w_knife_holster, + .primary = w_knife_primary, + .secondary = __NULL__, + .reload = __NULL__, + .release = w_knife_release, + .crosshair = w_knife_crosshair, + .precache = w_knife_precache, + .pickup = w_knife_pickup, + .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); + /*item_pickup item = (item_pickup)self; + item.SetRenderMode(RM_FULLBRIGHT); + item.SetRenderAmt(1.0f); + item.SetFloating(TRUE);*/ +} +#endif + diff --git a/src/shared/w_lego.qc b/src/shared/w_lego.qc new file mode 100644 index 0000000..cce89de --- /dev/null +++ b/src/shared/w_lego.qc @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*QUAKED weapon_handgrenade (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_lego.mdl" + +HALF-LIFE (1998) ENTITY + +Handgrenade Weapon + +*/ + +enum +{ + LEGO_IDLE, + LEGO_DRAW, + LEGO_PULLPIN, + LEGO_THROW, + LEGO_HOLSTER +}; + +void w_lego_precache(void) +{ + precache_model("sprites/hud_lego.spr"); + precache_model("models/v_lego.mdl"); + precache_model("models/w_lego.mdl"); + precache_model("models/p_lego.mdl"); +} +void w_lego_updateammo(player pl) +{ +#ifdef SERVER + Weapons_UpdateAmmo(pl, -1, pl.ammo_legogrenade, -1); +#endif +} +string w_lego_wmodel(void) +{ + return "models/w_lego.mdl"; +} +string w_lego_pmodel(void) +{ + return "models/p_lego.mdl"; +} +string w_lego_deathmsg(void) +{ + return ""; +} + +int w_lego_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (pl.ammo_legogrenade < MAX_A_LEGOGRENADE) { + pl.ammo_legogrenade = bound(0, pl.ammo_legogrenade + 1, MAX_A_LEGOGRENADE); + } else { + return FALSE; + } +#endif + return TRUE; +} + +#ifdef SERVER +void w_lego_throw(void) +{ + static void WeaponLego_Throw_Touch(void) { + float dmg = Skill_GetValue("plr_hand_grenade", 150); + FX_Lego(trace_endpos); + Damage_Radius(self.origin, self.owner, dmg, dmg * 1.5f, TRUE, WEAPON_LEGO); + remove(self); + } + + 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.touch = WeaponLego_Throw_Touch; + eGrenade.nextthink = time + 4.0f; + setmodel(eGrenade, "models/lego2.mdl"); + setsize(eGrenade, [0,0,0], [0,0,0]); + setorigin(eGrenade, vecSrc); +} +#endif + +void w_lego_draw(void) +{ +#ifdef CLIENT + Weapons_SetModel("models/v_lego.mdl"); + Weapons_ViewAnimation(LEGO_DRAW); +#endif +} + +void w_lego_holster(void) +{ + +} +void w_lego_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.a_ammo3 > 0) { + return; + } + + /* Ammo check */ +#ifdef CLIENT + if (pl.a_ammo2 <= 0) { + return; + } +#else + if (pl.ammo_legogrenade <= 0) { + return; + } +#endif + +#ifdef CLIENT + Weapons_ViewAnimation(LEGO_PULLPIN); +#endif + + pl.a_ammo3 = 1; + pl.w_attack_next = 0.5f; + pl.w_idle_next = 0.5f; +} + +void w_lego_hud(void) +{ +#ifdef CLIENT + static vector cross_pos; + cross_pos = g_hudmins + (g_hudres / 2) + [-12,-12]; + drawsubpic( + cross_pos, + [24,24], + "sprites/crosshairs.spr_0.tga", + [72/128,48/128], + [0.1875, 0.1875], + [1,1,1], + 1, + DRAWFLAG_NORMAL + ); + + HUD_DrawAmmo2(); + + vector aicon_pos = g_hudmins + [g_hudres[0] - 42, g_hudres[1] - 64]; + drawpic( + aicon_pos, + "sprites/hud_lego.spr_0.tga", + [32,64], + [1,1,1], + pSeat->m_flAmmo2Alpha, + 0 + ); +#endif +} + +void w_lego_release(void) +{ + player pl = (player)self; + + if (pl.w_idle_next > 0.0) { + return; + } + + if (pl.a_ammo3 == 1) { +#ifdef CLIENT + pl.a_ammo2--; + Weapons_ViewAnimation(LEGO_THROW); +#else + pl.ammo_legogrenade--; + w_lego_throw(); +#endif + pl.a_ammo3 = 2; + pl.w_attack_next = 1.0f; + pl.w_idle_next = 0.5f; + } else if (pl.a_ammo3 == 2) { +#ifdef CLIENT + Weapons_ViewAnimation(LEGO_DRAW); +#else + if (!pl.ammo_legogrenade) { + Weapons_RemoveItem(pl, WEAPON_LEGO); + } +#endif + pl.w_attack_next = 0.5f; + pl.w_idle_next = 0.5f; + pl.a_ammo3 = 0; + } else { + Weapons_ViewAnimation(LEGO_IDLE); + pl.w_idle_next = 3.0f; + } +} + +float +w_lego_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CR_AIMCROWBAR : ANIM_AIMCROWBAR; +} + +void +w_lego_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + if (selected) { + drawsubpic(pos, [170,45], "sprites/640hud6.spr_0.tga", [0,0], [170/256,45/256], g_hud_color, a, DRAWFLAG_ADDITIVE); + } else { + drawsubpic(pos, [170,45], "sprites/640hud3.spr_0.tga", [0,0], [170/256,45/256], g_hud_color, a, DRAWFLAG_ADDITIVE); + } +#endif +} + +weapon_t w_lego = +{ + .name = "lego", + .id = ITEM_LEGO, + .slot = 0, + .slot_pos = 5, + .draw = w_lego_draw, + .holster = w_lego_holster, + .primary = w_lego_primary, + .secondary = __NULL__, + .reload = __NULL__, + .release = w_lego_release, + .crosshair = w_lego_hud, + .precache = w_lego_precache, + .pickup = w_lego_pickup, + .updateammo = w_lego_updateammo, + .wmodel = w_lego_wmodel, + .pmodel = w_lego_pmodel, + .deathmsg = w_lego_deathmsg, + .aimanim = w_lego_aimanim, + .hudpic = w_lego_hudpic +}; + +#ifdef SERVER +void weapon_lego(void) +{ + Weapons_InitItem(WEAPON_LEGO); + /*item_pickup item = (item_pickup)self; + item.SetRenderMode(RM_FULLBRIGHT); + item.SetRenderAmt(1.0f);*/ +} +#endif diff --git a/src/shared/w_legolauncher.qc b/src/shared/w_legolauncher.qc new file mode 100644 index 0000000..3d5da6b --- /dev/null +++ b/src/shared/w_legolauncher.qc @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*QUAKED weapon_legolauncher (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_legolauncher.mdl" + +Household DEATH! (2003) ENTITY + +Lego Launcher Weapon + +*/ + +enum +{ + LEGOLAUNCHER_IDLE, + LEGOLAUNCHER_SHOOT, + LEGOLAUNCHER_DRAW, + LEGOLAUNCHER_HOLSTER +}; + +void +w_legolauncher_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_legolauncher.fire"); + Sound_Precache("weapon_legolauncher.hit"); + Sound_Precache("weapon_legolauncher.hitbody"); +#endif + precache_model("models/lego.mdl"); + precache_model("models/v_legolauncher.mdl"); + precache_model("models/w_legolauncher.mdl"); + precache_model("models/p_legolauncher.mdl"); +} + +void +w_legolauncher_updateammo(player pl) +{ +#ifdef SERVER + Weapons_UpdateAmmo(pl, __NULL__, pl.ammo_legos, __NULL__); +#endif +} + +string +w_legolauncher_wmodel(void) +{ + return "models/w_legolauncher.mdl"; +} + +string +w_legolauncher_pmodel(void) +{ + return "models/p_legolauncher.mdl"; +} + +string +w_legolauncher_deathmsg(void) +{ + return ""; +} + +int +w_legolauncher_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + + if (pl.ammo_legos < MAX_A_LEGOS) { + pl.ammo_legos = bound(0, pl.ammo_legos + 40, MAX_A_LEGOS); + } else { + return FALSE; + } +#endif + return TRUE; +} + +void +w_legolauncher_draw(void) +{ + Weapons_SetModel("models/v_legolauncher.mdl"); + Weapons_ViewAnimation(LEGOLAUNCHER_DRAW); +} + +void +w_legolauncher_holster(void) +{ + Weapons_ViewAnimation(LEGOLAUNCHER_HOLSTER); +} + +#ifdef SERVER +void +w_legolauncher_shootlego(void) +{ + player pl = (player)self; + static void Lego_Touch(void) { + FX_LegoPiece(trace_endpos); + + /* different effects/sounds for players and walls */ + if (trace_ent.iBleeds) { + FX_Blood(trace_endpos, [1,0,0]); + } else { + FX_Impact(IMPACT_DEFAULT, self.origin, trace_plane_normal); + } + + if (other.takedamage == DAMAGE_YES) { + Damage_Apply(other, self.owner, 15, WEAPON_LEGOLAUNCHER, DMG_GENERIC); + Sound_Play(self, CHAN_WEAPON, "weapon_legolauncher.hitbody"); + } else { + Sound_Play(self, CHAN_WEAPON, "weapon_legolauncher.hit"); + } + remove(self); + } + + Weapons_MakeVectors(); + entity lego = spawn(); + setmodel(lego, "models/lego.mdl"); + /* TODO needs to spawn more to the right */ + setorigin(lego, Weapons_GetCameraPos() + (v_forward * 16) + (v_right * 4) + (v_up * -8)); + lego.owner = self; + lego.velocity = v_forward * 2000; + lego.movetype = MOVETYPE_FLY; + lego.solid = SOLID_BBOX; + lego.angles = vectoangles(lego.velocity); + lego.avelocity[2] = 10; + lego.touch = Lego_Touch; + setsize(lego, [0,0,0], [0,0,0]); + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_SHOOT1HAND, 0.45f); + else + Animation_PlayerTopTemp(ANIM_CR_SHOOT1HAND, 0.45f); +} +#endif + +void +w_legolauncher_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + + /* Ammo check */ +#ifdef CLIENT + if (pl.a_ammo2 <= 0) { + return; + } +#else + if (pl.ammo_legos <= 0) { + return; + } +#endif + + /* Actual firing */ +#ifdef SERVER + w_legolauncher_shootlego(); + pl.ammo_legos--; + Sound_Play(pl, CHAN_WEAPON, "weapon_legolauncher.fire"); +#else + Weapons_ViewPunchAngle([-2,0,0]); + Weapons_ViewAnimation(LEGOLAUNCHER_SHOOT); +#endif + + pl.w_attack_next = 0.15f; + pl.w_idle_next = 5.0f; +} + +void +w_legolauncher_release(void) +{ + player pl = (player)self; + + if (pl.w_idle_next > 0.0) { + return; + } + + Weapons_ViewAnimation(LEGOLAUNCHER_IDLE); + + pl.w_idle_next = 3.0f; +} + +void +w_legolauncher_crosshair(void) +{ +#ifdef CLIENT + vector aicon_pos; + + static vector cross_pos; + cross_pos = g_hudmins + (g_hudres / 2) + [-12,-12]; + drawsubpic( + cross_pos, + [24,24], + "sprites/crosshairs.spr_0.tga", + [72/128,48/128], + [0.1875, 0.1875], + [1,1,1], + 1, + DRAWFLAG_NORMAL + ); + + HUD_DrawAmmo2(); + + aicon_pos = g_hudmins + [g_hudres[0] - 48, g_hudres[1] - 42]; + drawsubpic( + aicon_pos, + [52,20], + "sprites/640_death.spr_0.tga", + [0/128,100/128], + [60/128,20/128], + [1,1,1], + pSeat->m_flAmmo2Alpha, + 0 + ); +#endif +} + +float +w_legolauncher_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CR_AIMBOW : ANIM_AIMBOW; +} + +void +w_legolauncher_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + if (selected) { + drawsubpic( + pos, + [170,45], + "sprites/640hud5.spr_0.tga", + [0,0], + [170/256,45/256], + g_hud_color, + a, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + "sprites/640hud2.spr_0.tga", + [0,0], + [170/256,45/256], + g_hud_color, + a, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_legolauncher = +{ + .name = "legoblock", + .id = ITEM_LEGOLAUNCHER, + .slot = 0, + .slot_pos = 7, + .draw = w_legolauncher_draw, + .holster = w_legolauncher_holster, + .primary = w_legolauncher_primary, + .secondary = __NULL__, + .reload = __NULL__, + .release = w_legolauncher_release, + .crosshair = w_legolauncher_crosshair, + .precache = w_legolauncher_precache, + .pickup = w_legolauncher_pickup, + .updateammo = w_legolauncher_updateammo, + .wmodel = w_legolauncher_wmodel, + .pmodel = w_legolauncher_pmodel, + .deathmsg = w_legolauncher_deathmsg, + .aimanim = w_legolauncher_aimanim, + .hudpic = w_legolauncher_hudpic +}; + +#ifdef SERVER +void +weapon_legolauncher(void) +{ + Weapons_InitItem(WEAPON_LEGOLAUNCHER); + /*item_pickup item = (item_pickup)self; + item.SetRenderMode(RM_FULLBRIGHT); + item.SetRenderAmt(1.0f);*/ +} +#endif + diff --git a/src/shared/w_machette.qc b/src/shared/w_machette.qc new file mode 100644 index 0000000..9ead0d4 --- /dev/null +++ b/src/shared/w_machette.qc @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * Copyright (c) 2019-2020 Gethyn ThomasQuail + * + * 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 +{ + MACHETTE_IDLE1, + MACHETTE_IDLE2, + MACHETTE_IDLE3, + MACHETTE_THROW, + MACHETTE_DRAW, + MACHETTE_HOLSTER +}; + +void +w_machette_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_crowbar.hit"); + Sound_Precache("weapon_crowbar.miss"); + Sound_Precache("weapon_crowbar.hitbody"); +#endif + precache_model("models/v_machette.mdl"); + precache_model("models/rune_stalker.mdl"); + precache_model("models/p_machette.mdl"); +} + +void +w_machette_updateammo(player pl) +{ + w_broom_updateammo(pl); +} + +string +w_machette_wmodel(void) +{ + return "models/w_machette.mdl"; +} +string +w_machette_pmodel(void) +{ + return "models/p_machette.mdl"; +} + +string +w_machette_deathmsg(void) +{ + return ""; +} + + +void +w_machette_pickup(void) +{ +#ifdef SERVER + /* Broadcast a message and sound upon picking up Rune */ + Sound_Play(self, CHAN_ITEM, "weapon_machette.pickup"); + env_message_single(self, "Activating Rune STALKER!"); +#endif + return TRUE; +} + +void +w_machette_draw(void) +{ + Weapons_SetModel("models/v_machette.mdl"); + Weapons_ViewAnimation(MACHETTE_DRAW); +} + +void +w_machette_holster(void) +{ + Weapons_ViewAnimation(MACHETTE_HOLSTER); +} + +void +w_machette_primary(void) +{ + int anim = 0; + vector src; + player pl = (player)self; + + if (pl.w_attack_next) { + return; + } + + Weapons_MakeVectors(); + src = pl.origin + pl.view_ofs; + traceline(src, src + (v_forward * 32), FALSE, pl); + + + pl.w_attack_next = 0.4f; + pl.w_idle_next = 2.5f; + + Weapons_ViewAnimation(MACHETTE_THROW); + +#ifdef SERVER + if (pl.flags & FL_CROUCHING) { + Animation_PlayerTopTemp(ANIM_SHOOTCROWBAR, 0.5f); + } else { + Animation_PlayerTopTemp(ANIM_CR_SHOOTCROWBAR, 0.42f); + } + + if (trace_fraction >= 1.0) { + return; + } + + /* don't bother with decals, we got squibs */ + if (trace_ent.iBleeds) { + FX_Blood(trace_endpos, [1,0,0]); + } else { + FX_Impact(IMPACT_MELEE, trace_endpos, trace_plane_normal); + } + + if (trace_ent.takedamage) { + Damage_Apply(trace_ent, pl, 5, WEAPON_MACHETTE, DMG_BLUNT); + if (trace_ent.iBleeds) { + Sound_Play(self, CHAN_WEAPON, "weapon_machette.hitbody"); + } + } else { + Sound_Play(self, CHAN_WEAPON, "weapon_machette.hit"); + } +#endif +} + +#ifdef SERVER +void +w_machette_throw(void) +{ + player pl = (player)self; + static void Machette_Touch(void) { + setmodel(self, "models/machette.mdl"); + + /* squibs */ + if (trace_ent.iBleeds) { + FX_Blood(trace_endpos, [1,0,0]); + remove(self); + } else { + FX_Impact(IMPACT_DEFAULT, trace_endpos, trace_plane_normal); + } + + if (other.takedamage == DAMAGE_YES) { + Damage_Apply(other, self.owner, 15, WEAPON_MACHETTE, DMG_GENERIC); + Sound_Play(self, CHAN_WEAPON, "weapon_machette.hitbody"); + } else { + Sound_Play(self, CHAN_WEAPON, "weapon_machette.stick"); + } + self.movetype = MOVETYPE_NONE; + } + + Weapons_MakeVectors(); + entity machette = spawn(); + setmodel(machette, "models/machette.mdl"); + setorigin(machette, Weapons_GetCameraPos() + (v_forward * 16)); + machette.owner = self; + machette.velocity = v_forward * 2000; + machette.movetype = MOVETYPE_FLY; + machette.solid = SOLID_BBOX; + machette.angles = vectoangles(machette.velocity); + machette.avelocity[2] = 10; + machette.touch = Machette_Touch; + setsize(machette, [0,0,0], [0,0,0]); + + if (self.flags & FL_CROUCHING) + Animation_PlayerTopTemp(ANIM_SHOOT1HAND, 0.45f); + else + Animation_PlayerTopTemp(ANIM_CR_SHOOT1HAND, 0.45f); +} +#endif + +void +w_machette_secondary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + + /* Actual firing */ +#ifdef SERVER + w_machette_throw(); + Sound_Play(pl, CHAN_WEAPON, "weapon_machette.throw"); +#else + Weapons_ViewPunchAngle([-2,0,0]); + Weapons_ViewAnimation(MACHETTE_THROW); +#endif + + pl.w_attack_next = 0.5f; + pl.w_idle_next = 5.0f; +} + +void +w_machette_release(void) +{ + player pl = (player)self; + + if (pl.w_idle_next > 0.0) { + return; + } + + int r; + r = (float)input_sequence % 3; + switch (r) { + case 1: + Weapons_ViewAnimation(MACHETTE_IDLE1); + pl.w_idle_next = 1.3f; + break; + case 2: + Weapons_ViewAnimation(MACHETTE_IDLE2); + pl.w_idle_next = 3.0f; + break; + default: + Weapons_ViewAnimation(MACHETTE_IDLE3); + pl.w_idle_next = 1.285f; + } +} + +float +w_machette_aimanim(void) +{ + return w_broom_aimanim(); +} + +void +w_machette_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + if (selected) { + drawsubpic( + pos, + [170,45], + "sprites/tfchud04.spr_0.tga", + [0,135/256], + [170/256,45/256], + g_hud_color, + a, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + "sprites/tfchud03.spr_0.tga", + [0,90/256], + [170/256,45/256], + g_hud_color, + a, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_machette = +{ + .name = "machette", + .id = ITEM_MACHETTE, + .slot = 1, + .slot_pos = 1, + .draw = w_machette_draw, + .holster = w_machette_holster, + .primary = w_machette_primary, + .secondary = w_machette_secondary, + .reload = __NULL__, + .release = w_machette_release, + .crosshair = __NULL__, + .precache = w_machette_precache, + .pickup = w_machette_pickup, + .updateammo = w_machette_updateammo, + .wmodel = w_machette_wmodel, + .pmodel = w_machette_pmodel, + .deathmsg = w_machette_deathmsg, + .aimanim = w_machette_aimanim, + .hudpic = w_machette_hudpic +}; + +/* entity definitions for pickups */ +#ifdef SERVER +void +rune_stalker(void) +{ + Weapons_InitItem(WEAPON_MACHETTE); +} +#endif diff --git a/src/shared/w_sodalauncher.qc b/src/shared/w_sodalauncher.qc new file mode 100644 index 0000000..858d0ad --- /dev/null +++ b/src/shared/w_sodalauncher.qc @@ -0,0 +1,357 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*QUAKED weapon_sodalauncher (0 0 1) (-16 -16 0) (16 16 32) +"model" "models/w_soda.mdl" + +HALF-LIFE (1998) ENTITY + +MP5/9mmAR Weapon +Same as weapon_9mmAR + +*/ + +/* Animations */ +enum +{ + SODA_IDLE, + SODA_IDLE_NOSODA, + SODA_SHOOT_RELOAD, + SODA_SHOOT, + SODA_DRAW, + SODA_DRAW_NOSODA, + SODA_HOLSTER, + SODA_HOLSTER_NOSODA +}; + +void +w_sodalauncher_precache(void) +{ +#ifdef SERVER + Sound_Precache("weapon_sodalauncher.fire"); + Sound_Precache("weapon_sodalauncher.explode"); +#endif + precache_model("sprites/hud_sodacan.spr"); + precache_model("models/v_soda.mdl"); + precache_model("models/w_soda.mdl"); + precache_model("models/p_soda.mdl"); + precache_model("models/sodacan.mdl"); +} + +int +w_sodalauncher_pickup(int new, int startammo) +{ +#ifdef SERVER + player pl = (player)self; + if (pl.ammo_soda < MAX_A_SODA) { + pl.ammo_soda = bound(0, pl.ammo_soda + 3, MAX_A_SODA); + } else { + return FALSE; + } +#endif + return TRUE; +} + +void +w_sodalauncher_updateammo(player pl) +{ +#ifdef SERVER + Weapons_UpdateAmmo(pl, __NULL__, pl.ammo_soda, __NULL__); +#endif +} + +string +w_sodalauncher_wmodel(void) +{ + return "models/w_soda.mdl"; +} + +string +w_sodalauncher_pmodel(void) +{ + return "models/p_soda.mdl"; +} + +string +w_sodalauncher_deathmsg(void) +{ + return ""; +} + +void +w_sodalauncher_draw(void) +{ +#ifdef CLIENT + Weapons_SetModel("models/v_soda.mdl"); + Weapons_ViewAnimation(SODA_DRAW); + /* needs the skin value to be set, no API yet + * the soda cans in the viewmodel sync up with + * the projectile and spill decals */ +#endif +} + +void +w_sodalauncher_holster(void) +{ + Weapons_ViewAnimation(SODA_HOLSTER); +} + +/* TODO now just need parameters */ + +#ifdef SERVER +void +w_sodalauncher_shoot(void) +{ + player pl = (player)self; + + /* Apply force */ + if (pl.flags & FL_ONGROUND) { + pl.velocity += v_forward * -100; + } else { + pl.velocity += v_forward * -300; + } + + static void Soda_ExplodeTouch(void) { + float dmg = Skill_GetValue("plr_9mmAR_grenade", 150); + FX_Soda(trace_endpos, self.skin); + Damage_Radius(self.origin, self.owner, dmg, dmg * 2.5f, TRUE, WEAPON_SODALAUNCHER); + Sound_Play(self, 1, "weapon_sodalauncher.explode"); + remove(self); + } + + Weapons_MakeVectors(); + entity soda = spawn(); + setmodel(soda, "models/sodacan.mdl"); + setorigin(soda, Weapons_GetCameraPos() + (v_forward * 16)); + soda.skin = floor(random(0,6)); + soda.owner = self; + soda.velocity = v_forward * 800; + soda.angles = vectoangles(soda.velocity); + soda.avelocity[0] = random(-100, -500); + soda.gravity = 0.5f; + soda.movetype = MOVETYPE_BOUNCE; + //soda.flags |= FL_LAGGEDMOVE; + soda.solid = SOLID_BBOX; + setsize(soda, [0,0,0], [0,0,0]); + soda.touch = Soda_ExplodeTouch; +} + +#else + + /* TODO make server side + * Sprite code in Nuclide is still WIP + * so this is client-only for now */ +void +w_sodalauncher_exhaust(void) { + env_sprite eExhaust = spawn(env_sprite); + setorigin(eExhaust, Weapons_GetCameraPos() + (v_forward * 16)); + setmodel(eExhaust, "sprites/co2_puff.spr"); + + eExhaust.effects = EF_ADDITIVE; + eExhaust.drawmask = MASK_ENGINE; + eExhaust.maxframe = modelframecount(eExhaust.modelindex); + eExhaust.loops = 0; + eExhaust.scale = 0.5f; + eExhaust.framerate = 20; + eExhaust.nextthink = time + 0.05f; + } + +#endif + +void +w_sodalauncher_primary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + + /* Ammo check */ +#ifdef CLIENT + if (pl.a_ammo2 <= 0) { + return; + } +#else + if (pl.ammo_soda <= 0) { + return; + } +#endif + + /* Actual firing + * TODO make this one fast straight */ +#ifdef CLIENT + Weapons_ViewPunchAngle([-10,0,0]); + Weapons_ViewAnimation(SODA_SHOOT_RELOAD); + w_sodalauncher_exhaust(); +#else + w_sodalauncher_shoot(); + pl.ammo_soda--; + Sound_Play(pl, CHAN_WEAPON, "weapon_sodalauncher.fire"); +#endif + + pl.w_attack_next = 1.0f; + pl.w_idle_next = 10.0f; +} +/* TODO Actual secondary fire is the current primary fire code, just WIP */ +void +w_sodalauncher_secondary(void) +{ + player pl = (player)self; + + if (pl.w_attack_next > 0.0) { + return; + } + + /* Ammo check */ +#ifdef CLIENT + if (pl.a_ammo2 <= 0) { + return; + } +#else + if (pl.ammo_soda <= 0) { + return; + } +#endif + + /* TODO make this one sag and slower */ +#ifdef CLIENT + Weapons_ViewPunchAngle([-10,0,0]); + Weapons_ViewAnimation(SODA_SHOOT_RELOAD); + w_sodalauncher_exhaust(); +#else + w_sodalauncher_shoot(); + pl.ammo_soda--; + Sound_Play(pl, CHAN_WEAPON, "weapon_sodalauncher.fire"); +#endif + + pl.w_attack_next = 1.0f; + pl.w_idle_next = 10.0f; +} + +void +w_sodalauncher_release(void) +{ + player pl = (player)self; + + if (pl.w_idle_next) { + return; + } + + Weapons_ViewAnimation(SODA_IDLE); + pl.w_idle_next = 3.0f; +} + +void +w_sodalauncher_crosshair(void) +{ +#ifdef CLIENT + static vector cross_pos; + cross_pos = g_hudmins + (g_hudres / 2) + [-12,-12]; + drawsubpic( + cross_pos, + [24,24], + "sprites/crosshairs.spr_0.tga", + [72/128,48/128], + [0.1875, 0.1875], + [1,1,1], + 1, + DRAWFLAG_NORMAL + ); + + HUD_DrawAmmo2(); + + vector aicon_pos = g_hudmins + [g_hudres[0] - 42, g_hudres[1] - 64]; + drawpic( + aicon_pos, + "sprites/hud_sodacan.spr_0.tga", + [32,64], + [1,1,1], + pSeat->m_flAmmo2Alpha, + 0 + ); +#endif +} + +float +w_sodalauncher_aimanim(void) +{ + return self.flags & FL_CROUCHING ? ANIM_CR_AIMBOW : ANIM_AIMBOW; +} + +void +w_sodalauncher_hudpic(int selected, vector pos, float a) +{ +#ifdef CLIENT + if (selected) { + drawsubpic( + pos, + [170,45], + "sprites/640hud4.spr_0.tga", + [0,135/256], + [170/256,45/256], + g_hud_color, + a, + DRAWFLAG_ADDITIVE + ); + } else { + drawsubpic( + pos, + [170,45], + "sprites/640hud1.spr_0.tga", + [0,135/256], + [170/256,45/256], + g_hud_color, + a, + DRAWFLAG_ADDITIVE + ); + } +#endif +} + +weapon_t w_sodalauncher = { + .name = "sodacan", + .id = ITEM_SODALAUNCHER, + .slot = 0, + .slot_pos = 4, + .draw = w_sodalauncher_draw, + .holster = w_sodalauncher_holster, + .primary = w_sodalauncher_primary, + .secondary = w_sodalauncher_secondary, + .reload = __NULL__, + .release = w_sodalauncher_release, + .crosshair = w_sodalauncher_crosshair, + .precache = w_sodalauncher_precache, + .pickup = w_sodalauncher_pickup, + .updateammo = w_sodalauncher_updateammo, + .wmodel = w_sodalauncher_wmodel, + .pmodel = w_sodalauncher_pmodel, + .deathmsg = w_sodalauncher_deathmsg, + .aimanim = w_sodalauncher_aimanim, + .hudpic = w_sodalauncher_hudpic +}; + +#ifdef SERVER +void +weapon_sodalauncher(void) +{ + Weapons_InitItem(WEAPON_SODALAUNCHER); + /*item_pickup item = (item_pickup)self; + item.SetRenderMode(RM_FULLBRIGHT); + item.SetRenderAmt(1.0f); + item.SetFloating(TRUE);*/ +} +#endif diff --git a/src/shared/weapons.h b/src/shared/weapons.h new file mode 100644 index 0000000..272ed76 --- /dev/null +++ b/src/shared/weapons.h @@ -0,0 +1,39 @@ +/* + * 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_BROOM, + WEAPON_FRYINGPAN, + WEAPON_FORKS, + WEAPON_KNIFE, + WEAPON_SODALAUNCHER, + WEAPON_LEGO, + WEAPON_HAIRSPRAY, + WEAPON_LEGOLAUNCHER, + WEAPON_GLOVE, + WEAPON_MACHETTE +}; + + +#define MAX_A_FORKS 25 +#define MAX_A_KNIVES 15 +#define MAX_A_LEGOGRENADE 3 +#define MAX_A_LEGOS 200 +#define MAX_A_SODA 24 +#define MAX_A_SPRAY 100 diff --git a/src/shared/weapons.qc b/src/shared/weapons.qc new file mode 100644 index 0000000..92736d7 --- /dev/null +++ b/src/shared/weapons.qc @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2016-2020 Marco Hladik + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +weapon_t w_null = {}; +weapon_t g_weapons[] = { + w_null, + w_broom, + w_fryingpan, + w_forks, + w_knife, + w_sodalauncher, + w_lego, + w_hairspray, + w_legolauncher, + w_glove, + w_machette +}; diff --git a/zpak_sound.pk3dir/sound/items_hhdeath.sndshd b/zpak_sound.pk3dir/sound/items_hhdeath.sndshd new file mode 100644 index 0000000..a90fe4d --- /dev/null +++ b/zpak_sound.pk3dir/sound/items_hhdeath.sndshd @@ -0,0 +1,29 @@ +ammo.pickup +{ + sample items/pickup.wav +} + +ammo.respawn +{ + sample items/respawn.wav +} + +item.armor +{ + sample items/pickup_armor.wav +} + +item.health +{ + sample items/takehealth.wav +} + +item.respawn +{ + sample items/respawn.wav +} + +weapon.pickup +{ + sample items/pickup.wav +} diff --git a/zpak_sound.pk3dir/sound/players_hhdeath.sndshd b/zpak_sound.pk3dir/sound/players_hhdeath.sndshd new file mode 100644 index 0000000..ba20d77 --- /dev/null +++ b/zpak_sound.pk3dir/sound/players_hhdeath.sndshd @@ -0,0 +1,9 @@ +player.die +{ + sample player/killed.wav +} + +player.fallarmor +{ + sample player/fall_armor.wav +} diff --git a/zpak_sound.pk3dir/sound/weapons_hhdeath.sndshd b/zpak_sound.pk3dir/sound/weapons_hhdeath.sndshd new file mode 100644 index 0000000..e79c46f --- /dev/null +++ b/zpak_sound.pk3dir/sound/weapons_hhdeath.sndshd @@ -0,0 +1,145 @@ +weapon_broom.hit +{ + sample weapons/broom_hit1.wav + sample weapons/broom_hit2.wav +} + +weapon_broom.hitbody +{ + sample weapons/broom_hitbod1.wav + sample weapons/broom_hitbod2.wav + sample weapons/broom_hitbod3.wav +} + +weapon_broom.miss +{ + sample weapons/broom_swing1.wav + sample weapons/broom_swing2.wav +} + +weapon_forks.hitbody +{ + sample weapons/fork_stick_body.wav +} + +weapon_forks.hit +{ + sample weapons/fork_hit_wall.wav +} + +weapon_forks.stick +{ + sample weapons/fork_stick.wav +} + +weapon_forks.throw +{ + sample weapons/fork_throw.wav +} + +weapon_fryingpan.hit +{ + sample weapons/pan_hitwall1.wav +} + +weapon_fryingpan.hitbody +{ + sample weapons/pan_hitbody1.wav + sample weapons/pan_hitbody2.wav +} + +weapon_fryingpan.miss +{ + sample weapons/pan_swing1.wav + sample weapons/pan_swing2.wav +} + + +weapon_glove.hitbody +{ + sample weapon/glove_hitbod1.wav + sample weapon/glove_hitbod2.wav + sample weapon/glove_hitbod3.wav +} + +weapon_glove.hit +{ + sample weapon/glove_hit1.wav + sample weapon/glove_hit2.wav +} + +weapon_glove.miss +{ + sample weapons/glove_swing1.wav + sample weapons/glove_swing2.wav +} + +weapon_glove.pickup +{ + sample runes/on_slasher.wav +} + + +weapon_hairspray.fire +{ + sample weapons/hspray_loop.wav +} + +weapon_knife.hitbody +{ + sample weapons/knife_stick_wall.wav +} + +/* should have looping flag but can't due to engine bug */ +weapon_knife.move +{ + follow + sample weapons/knife_move.wav +} + +weapon_knife.stick +{ + sample weapons/knife_stick_wall.wav +} + +weapon_knife.throw +{ + sample weapons/knife_throw1.wav + sample weapons/knife_throw2.wav +} + +weapon_lego.explode +{ + sample gibs/lego_explode1.wav + sample gibs/lego_explode2.wav +} + +weapon_legolauncher.fire +{ + sample weapons/launch_lego.wav +} + +weapon_legolauncher.hitbody +{ + sample weapons/launch_lego.wav +} + +weapon_legolauncher.hit +{ + sample gibs/lego_bounce1.wav + sample gibs/lego_bounce2.wav + sample gibs/lego_bounce3.wav + sample gibs/lego_bounce4.wav + sample gibs/lego_bounce5.wav + sample gibs/lego_bounce6.wav +} + +weapon_sodalauncher.explode +{ + sample weapons/soda_spill.wav +} + +weapon_sodalauncher.fire +{ + sample weapons/launch_soda.wav +}