nuclide/src/server/weapons.qc

385 lines
8.4 KiB
Plaintext

/*
* Copyright (c) 2016-2022 Vera Visions LLC.
*
* 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.
*/
#ifndef NEW_INVENTORY
/* force the drawing of the first weapon that's picked up */
var int autocvar_sv_forceweapondraw = TRUE;
/*
=================
Weapon_GetCount
Returns the total number of weapons in the game.
=================
*/
int
Weapon_GetCount(void)
{
return g_weapons.length;
}
/*
=================
Weapon_GetBitID
Returns the item bitflag of a weapon index.
=================
*/
int
Weapon_GetBitID(int i)
{
return g_weapons[i].id;
}
/*
=================
Weapons_PickupNotify
Tells the client if we picked up a NEW weapon item.
=================
*/
void
Weapons_PickupNotify(NSClientPlayer pl, int w)
{
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EV_WEAPON_PICKUP);
WriteByte(MSG_MULTICAST, w);
msg_entity = (entity)pl;
multicast([0,0,0], MULTICAST_ONE);
}
/*
=================
Weapons_RefreshAmmo
Just calls updateammo() when available... maybe a bit redundant.
=================
*/
void
Weapons_RefreshAmmo(NSClientPlayer pl)
{
if (g_weapons[pl.activeweapon].updateammo != __NULL__) {
g_weapons[pl.activeweapon].updateammo((player)pl);
}
}
/*
=================
Weapons_SwitchBest
Switch to the 'best' weapon according to our weight system.
=================
*/
void
Weapons_SwitchBest(NSClientPlayer pl, optional float skip = 0)
{
entity oldself = self;
self = pl;
float old = pl.activeweapon;
/* loop through n weapon count */
for (float i = g_weapons.length - 1; i >= 1 ; i--) {
int x = g_weapon_weights[i]; /* map i to weapon table weight */
/* skip the weapon inside skip */
if (x == (int)skip)
continue;
/* do we have the weapon and is not not empty? */
if ((pl.g_items & g_weapons[x].id) && (Weapons_IsEmpty((player)pl, x) == 0)) {
pl.activeweapon = x;
break;
}
}
if (old == pl.activeweapon)
return;
Weapons_Draw((player)pl);
self = oldself;
pl.gflags |= GF_SEMI_TOGGLED;
}
/*
=================
Weapons_AddItem
returns TRUE if weapon pickup gets removed from this world
=================
*/
int
Weapons_AddItem(NSClientPlayer pl, int w, int startammo)
{
int value = false;
bool over_limit = false;
/* let's check if we've got a limit */
int maxit;
CGameRules rules = (CGameRules)g_grMode;
maxit = rules.MaxItemPerSlot(g_weapons[w].slot);
if (maxit > 0) {
int wantslot = g_weapons[w].slot;
int c = 0;
for (int i = 0; i < g_weapons.length; i++) {
if (pl.g_items & g_weapons[i].id && g_weapons[i].slot == wantslot) {
c++;
/* we're over the slot limit. */
if (c >= maxit) {
over_limit = true;
break;
}
}
}
}
/* in case programmer decided not to add a pickup callback */
if (g_weapons[w].pickup == __NULL__) {
if (pl.g_items & g_weapons[w].id) {
/* already present, skip */
value = FALSE;
} else if (over_limit == false) {
/* new to our arsenal */
pl.g_items |= g_weapons[w].id;
value = TRUE;
/* it's new, so autoswitch? */
if (pl.activeweapon == 0 && autocvar_sv_forceweapondraw == 1) {
pl.activeweapon = w;
Weapons_Draw((player)pl);
} else {
Weapons_PickupNotify(pl, w);
}
}
} else {
/* call pickup to handle the ammo */
if (pl.g_items & g_weapons[w].id) {
/* we have the item already, se let's see if we hit maxammo */
value = g_weapons[w].pickup((player)pl, FALSE, startammo);
/* FALSE means maxammo is hit */
if (!value)
return value;
} else if (over_limit == false) {
/* new to our arsenal */
if (g_weapons[w].pickup((player)pl, TRUE, startammo) == TRUE) {
pl.g_items |= g_weapons[w].id;
value = TRUE;
/* it's new, so autoswitch? */
if (pl.activeweapon == 0 && autocvar_sv_forceweapondraw == 1) {
pl.activeweapon = w;
Weapons_Draw((player)pl);
} else {
Weapons_PickupNotify(pl, w);
}
} else {
/* cannot pickup this weapon (weapon says no) */
return FALSE;
}
} else {
return FALSE;
}
}
Weapons_RefreshAmmo(pl);
return value;
}
int
Weapons_AddItemSilent(NSClientPlayer pl, int w, int startammo)
{
int value = false;
bool over_limit = false;
/* let's check if we've got a limit */
int maxit;
CGameRules rules = (CGameRules)g_grMode;
maxit = rules.MaxItemPerSlot(g_weapons[w].slot);
if (maxit > 0) {
int wantslot = g_weapons[w].slot;
int c = 0;
for (int i = 0; i < g_weapons.length; i++) {
if (pl.g_items & g_weapons[i].id && g_weapons[i].slot == wantslot) {
c++;
/* we're over the slot limit. */
if (c >= maxit) {
over_limit = true;
break;
}
}
}
}
/* in case programmer decided not to add a pickup callback */
if (g_weapons[w].pickup == __NULL__) {
if (pl.g_items & g_weapons[w].id) {
/* already present, skip */
value = FALSE;
} else if (over_limit == false) {
/* new to our arsenal */
pl.g_items |= g_weapons[w].id;
value = TRUE;
/* it's new, so autoswitch? */
if (pl.activeweapon == 0 && autocvar_sv_forceweapondraw == 1) {
pl.activeweapon = w;
Weapons_Draw((player)pl);
}
}
} else {
/* call pickup to handle the ammo */
if (pl.g_items & g_weapons[w].id) {
/* we have the item already, se let's see if we hit maxammo */
value = g_weapons[w].pickup((player)pl, FALSE, startammo);
/* FALSE means maxammo is hit */
if (!value)
return value;
} else if (over_limit == false) {
/* new to our arsenal */
if (g_weapons[w].pickup((player)pl, TRUE, startammo) == TRUE) {
pl.g_items |= g_weapons[w].id;
value = TRUE;
/* it's new, so autoswitch? */
if (pl.activeweapon == 0 && autocvar_sv_forceweapondraw == 1) {
pl.activeweapon = w;
Weapons_Draw((player)pl);
}
} else {
/* cannot pickup this weapon (weapon says no) */
return FALSE;
}
} else {
return false;
}
}
Weapons_RefreshAmmo(pl);
return value;
}
/*
=================
Weapons_RemoveItem
Makes sure the item bit of g_items is reliably unset without errors.
=================
*/
void
Weapons_RemoveItem(NSClientPlayer pl, int w)
{
if (pl.activeweapon == w)
pl.activeweapon = WEAPON_NONE;
pl.g_items &= ~g_weapons[w].id;
if (autocvar_sv_forceweapondraw == 1)
Weapons_SwitchBest(pl);
}
/*
=================
Weapons_InitItem
Called by the weapon_X function to initialize an in-world pickup.
=================
*/
void
Weapons_InitItem(int w)
{
item_pickup it = (item_pickup)self;
spawnfunc_item_pickup();
it.SetItem(w);
}
/*
=================
Weapons_ReloadWeapon
Manipulates the .mag and .ammo field pointer with some basic reload logic.
=================
*/
void
Weapons_ReloadWeapon(NSClientPlayer pl, .int mag, .int ammo, int max)
{
int iNeed = max - pl.(mag);
int iHave = pl.(ammo);
if (iNeed > iHave) {
pl.(mag) += iHave;
pl.(ammo) = 0;
} else {
pl.(mag) += iNeed;
pl.(ammo) -= iNeed;
}
}
/*
=================
Weapon_DropCurrentWeapon
=================
*/
void
Weapon_DropCurrentWeapon(NSClientPlayer pl)
{
static void DropWeapon_Enable(void)
{
self.solid = SOLID_TRIGGER;
}
if (!pl.activeweapon)
return;
if (g_weapons[pl.activeweapon].allow_drop != TRUE)
return;
item_pickup drop = spawn(item_pickup);
drop.m_iWasDropped = TRUE;
drop.m_iClip = pl.a_ammo1;
drop.real_owner = pl;
drop.owner = pl;
drop.SetItem(pl.activeweapon);
drop.SetSolid(SOLID_NOT);
drop.SetOrigin(pl.origin);
drop.ScheduleThink(DropWeapon_Enable, 1.5f);
drop.SetMovetype(MOVETYPE_TOSS);
drop.classname = "remove_me";
drop.SetGravity(1.0f);
makevectors([-fabs(pl.v_angle[0]), pl.v_angle[1], 0]);
drop.SetVelocity(v_forward * 256);
drop.SetAngularVelocity([0.0f, 500.0f, 0.0f]);
Weapons_RemoveItem(pl, pl.activeweapon);
}
/*
=================
CSEv_DropWeapon
The 'drop' command from the client-module calls this.
=================
*/
void
CSEv_DropWeapon(void)
{
player pl = (player)self;
Weapon_DropCurrentWeapon(pl);
}
#endif