Skip to content
16 changes: 10 additions & 6 deletions Game.Server/BaseAnimating.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public partial class InfoLightingRelative : BaseEntity
}

[NetworkName("CBaseAnimating")]
public class BaseAnimating : BaseEntity
public partial class BaseAnimating : BaseEntity
{
public const int ANIMATION_SKIN_BITS = 10;
public const int ANIMATION_BODY_BITS = 32;
Expand Down Expand Up @@ -107,17 +107,17 @@ public class BaseAnimating : BaseEntity
public InlineArrayMaxStudioBoneCtrls<float> EncodedController;
public InlineArrayMaxStudioBoneCtrls<float> OldEncodedController;
[NetworkName("m_nSequence")]
public int Sequence;
[NetworkVar] public partial int Sequence { get; set; }
[NetworkName("m_flPlaybackRate")]
public TimeUnit_t PlaybackRate;
[NetworkVar] public partial TimeUnit_t PlaybackRate { get; set; }
[NetworkName("m_bClientSideAnimation")]
public bool ClientSideAnimation;
[NetworkName("m_bClientSideFrameReset")]
public bool ClientSideFrameReset;
[NetworkName("m_nNewSequenceParity")]
public int NewSequenceParity;
[NetworkVar] public partial int NewSequenceParity { get; set; }
[NetworkName("m_nResetEventsParity")]
public int ResetEventsParity;
[NetworkVar] public partial int ResetEventsParity { get; set; }
[NetworkName("m_nMuzzleFlashParity")]
public int MuzzleFlashParity;
[NetworkName("m_hLightingOrigin")]
Expand All @@ -135,7 +135,7 @@ public class BaseAnimating : BaseEntity
[NetworkName("m_flFadeScale")]
public float FadeScale;
[NetworkName("m_flCycle")]
public TimeUnit_t Cycle;
[NetworkVar] public partial TimeUnit_t Cycle { get; set; }
[NetworkName("m_OverrideViewTarget")]
public Vector3 OverrideViewTarget;

Expand Down Expand Up @@ -329,6 +329,10 @@ public Activity GetSequenceActivity(int sequence) {
public void SetPlaybackRate(TimeUnit_t rate) => PlaybackRate = rate;

public bool IsSequenceFinished() => SequenceFinished;
public bool IsDissolving() => (GetFlags() & EntityFlags.Dissolving) != 0;

public virtual void SetLightingOriginRelative(BaseEntity? lightingOriginRelative) => LightingOriginRelative.Set(lightingOriginRelative);
public BaseEntity? GetLightingOriginRelative() => LightingOriginRelative.Get();

public bool IsModelScaleFractional() => ModelScale < 1.0f;
public bool IsModelScaled() => ModelScale > 1.0f + FLT_EPSILON || ModelScale < 1.0f - FLT_EPSILON;
Expand Down
156 changes: 155 additions & 1 deletion Game.Server/BaseCombatCharacter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ public virtual bool FInViewCone(in Vector3 spot) {
return false;
}

public override bool IsBaseCombatCharacter() => true;

public virtual BaseEntity? GetVehicleEntity() => null;

public virtual bool IsInAVehicle() => false;
Expand Down Expand Up @@ -283,7 +285,159 @@ public virtual void Weapon_FrameUpdate() {
}

public BaseCombatWeapon? Weapon_Create(ReadOnlySpan<char> weaponName) => throw new NotImplementedException();
public virtual void Weapon_Equip(BaseCombatWeapon weapon) => throw new NotImplementedException();
public virtual void Weapon_Equip(BaseCombatWeapon weapon) {
for (int i = 0; i < MAX_WEAPONS; i++) {
if (MyWeapons[i].Get() == null) {
MyWeapons[i].Set(weapon);
break;
}
}

weapon.ChangeTeam(GetTeamNumber());

if (weapon.GetMaxClip1() == -1)
GiveAmmo(weapon.GetDefaultClip1(), weapon.PrimaryAmmoType);
else if (weapon.GetDefaultClip1() > weapon.GetMaxClip1()) {
weapon.iClip1 = weapon.GetMaxClip1();
GiveAmmo(weapon.GetDefaultClip1() - weapon.GetMaxClip1(), weapon.PrimaryAmmoType);
}

if (weapon.GetMaxClip2() == -1)
GiveAmmo(weapon.GetDefaultClip2(), weapon.SecondaryAmmoType);
else if (weapon.GetDefaultClip2() > weapon.GetMaxClip2()) {
weapon.iClip2 = weapon.GetMaxClip2();
GiveAmmo(weapon.GetDefaultClip2() - weapon.GetMaxClip2(), weapon.SecondaryAmmoType);
}

weapon.Equip(this);

if (IsPlayer() == false) {
if (ActiveWeapon.Get() != null) {
ActiveWeapon.Get()!.Holster();
ActiveWeapon.Get()!.AddEffects(EntityEffects.NoDraw);
}
SetActiveWeapon(weapon);
ActiveWeapon.Get()!.RemoveEffects(EntityEffects.NoDraw);
}

if (IsPlayer() == false) {
if (HasSpawnFlags(AI_BaseNPCGlobals.SF_NPC_LONG_RANGE)) {
ActiveWeapon.Get()!.MaxRange1 = 999999999;
ActiveWeapon.Get()!.MaxRange2 = 999999999;
}
}

WeaponProficiency proficiency;
proficiency = CalcWeaponProficiency(weapon);

if (BaseCombatWeapon.weapon_showproficiency.GetBool())
Msg($"{GetClassname()} equipped with {weapon.GetClassname()}, proficiency is {GetWeaponProficiencyName(proficiency)}\n");

SetCurrentWeaponProficiency(proficiency);

weapon.SetLightingOriginRelative(GetLightingOriginRelative());
}

public bool Weapon_EquipAmmoOnly(BaseCombatWeapon weapon) {
for (int i = 0; i < MAX_WEAPONS; i++) {
if (MyWeapons[i].Get() != null && FClassnameIs(MyWeapons[i].Get(), weapon.GetClassname())) {
int primaryGiven = (weapon.UsesClipsForAmmo1()) ? weapon.iClip1 : weapon.GetPrimaryAmmoCount();
int secondaryGiven = (weapon.UsesClipsForAmmo2()) ? weapon.iClip2 : weapon.GetSecondaryAmmoCount();

int takenPrimary = GiveAmmo(primaryGiven, weapon.PrimaryAmmoType);
int takenSecondary = GiveAmmo(secondaryGiven, weapon.SecondaryAmmoType);

if (weapon.UsesClipsForAmmo1())
weapon.iClip1 -= takenPrimary;
else
weapon.SetPrimaryAmmoCount(weapon.GetPrimaryAmmoCount() - takenPrimary);

if (weapon.UsesClipsForAmmo2())
weapon.iClip2 -= takenSecondary;
else
weapon.SetSecondaryAmmoCount(weapon.GetSecondaryAmmoCount() - takenSecondary);

if (takenPrimary > 0 || takenSecondary > 0)
return true;

return false;
}
}

return false;
}

public virtual bool Weapon_CanUse(BaseCombatWeapon weapon) {
ReadOnlySpan<BaseCombatWeapon.ActTable> table = weapon.ActivityList();

if (table.Length < 1)
return false;

for (int i = 0; i < table.Length; i++) {
if (table[i].Required) {
Activity translatedActivity = NPC_TranslateActivity(table[i].WeaponAct);

if (SelectWeightedSequence(translatedActivity) == StudioHdr.ACTIVITY_NOT_AVAILABLE)
return false;
}
}

return true;
}

public virtual int GiveAmmo(int count, int ammoIndex, bool suppressSound = false) {
if (count <= 0)
return 0;

if (!g_pGameRules.CanHaveAmmo(this, ammoIndex))
return 0;

if (ammoIndex < 0 || ammoIndex >= MAX_AMMO_SLOTS)
return 0;

int max = GetAmmoDef().MaxCarry(ammoIndex);
int add = Math.Min(count, max - Ammo[ammoIndex]);
if (add < 1)
return 0;

if (!suppressSound)
EmitSound("BaseCombatCharacter.AmmoPickup");

Ammo.Set(ammoIndex, Ammo[ammoIndex] + add);

return add;
}

public int GiveAmmo(int count, ReadOnlySpan<char> name, bool suppressSound = false) {
int ammoType = GetAmmoDef().Index(name);
if (ammoType == -1) {
Msg($"ERROR: Attempting to give unknown ammo type ({name})\n");
return 0;
}
return GiveAmmo(count, ammoType, suppressSound);
}

public void SetActiveWeapon(BaseCombatWeapon? newWeapon) {
BaseCombatWeapon? oldWeapon = ActiveWeapon.Get();
if (newWeapon != oldWeapon) {
ActiveWeapon.Set(newWeapon);
OnChangeActiveWeapon(oldWeapon, newWeapon);
}
}

public virtual void OnChangeActiveWeapon(BaseCombatWeapon? oldWeapon, BaseCombatWeapon? newWeapon) { }

public bool PreventWeaponPickup;
public bool IsAllowedToPickupWeapons() => !PreventWeaponPickup;

public void SetCurrentWeaponProficiency(WeaponProficiency proficiency) => CurrentWeaponProficiency = proficiency;
public virtual WeaponProficiency CalcWeaponProficiency(BaseCombatWeapon? weapon) => WeaponProficiency.Average;

public override void SetLightingOriginRelative(BaseEntity? lightingOrigin) {
base.SetLightingOriginRelative(lightingOrigin);
if (GetActiveWeapon() != null)
GetActiveWeapon()!.SetLightingOriginRelative(lightingOrigin);
}

public int WeaponCount() => MAX_WEAPONS;
public BaseCombatWeapon? GetWeapon(int i) => MyWeapons[i].Get();
Expand Down
118 changes: 117 additions & 1 deletion Game.Server/BaseCombatWeapon.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,30 @@
namespace Game.Server;
using Game.Shared;

using Source;
using Source.Common;
using Source.Common.Commands;
using Source.Common.Formats.BSP;
using Source.Common.Physics;

namespace Game.Server;

using DEFINE = Source.DEFINE<BaseCombatWeapon>;

public partial class BaseCombatWeapon : BaseAnimating
{
public static readonly ConVar weapon_showproficiency = new("weapon_showproficiency", "0");

public OutputEvent OnPlayerPickup = new();
public OutputEvent OnNPCPickup = new();
public OutputEvent OnCacheInteraction = new();

public static readonly new DataMap DataDesc = new(typeof(BaseCombatWeapon), BaseEntity.DataDesc, [
DEFINE.OUTPUT(nameof(OnPlayerPickup), "OnPlayerPickup", eventFuncs),
DEFINE.OUTPUT(nameof(OnNPCPickup), "OnNPCPickup", eventFuncs),
DEFINE.OUTPUT(nameof(OnCacheInteraction), "OnCacheInteraction", eventFuncs),
]);
public override DataMap? GetDataDescMap() => DataDesc;

public override bool IsWeapon() => true;
public override GarrysMod.LuaClass Lua_GetLuaClass() => GarrysMod.LuaEntity.LC_Weapon;

Expand All @@ -11,4 +35,96 @@ public static void W_Precache(){
public virtual Capability CapabilitiesGet() => 0;

public virtual void Operator_FrameUpdate(BaseCombatCharacter op) => throw new NotImplementedException();

public BaseEntity? Respawn() {
BaseEntity? newWeapon = Create(GetClassname(), g_pGameRules.VecWeaponRespawnSpot(this), GetLocalAngles(), GetOwnerEntity());

if (newWeapon != null) {
newWeapon.AddEffects(EntityEffects.NoDraw);
newWeapon.SetTouch(null);
newWeapon.SetThink(((BaseCombatWeapon)newWeapon).AttemptToMaterialize);

Util.DropToFloor(this, Mask.Solid);

newWeapon.SetNextThink(gpGlobals.CurTime + g_pGameRules.FlWeaponRespawnTime(this));
}
else
Warning($"Respawn failed to create {GetClassname()}!\n");

return newWeapon;
}

public void FallInit() {
SetModel(GetWorldModel());
VPhysicsDestroyObject();

if (VPhysicsInitNormal(SolidType.BBox, GetSolidFlags() | SolidFlags.Trigger, false) == null) {
SetMoveType(Source.MoveType.FlyGravity);
SetSolid(SolidType.BBox);
AddSolidFlags(SolidFlags.Trigger);
}

SetPickupTouch();

SetThink(FallThink);

SetNextThink(gpGlobals.CurTime + 0.1f);
}

public void FallThink() {
SetNextThink(gpGlobals.CurTime + 0.1f);

bool shouldMaterialize = false;
IPhysicsObject? physics = VPhysicsGetObject();
if (physics != null)
shouldMaterialize = physics.IsAsleep();
else
shouldMaterialize = (GetFlags() & EntityFlags.OnGround) != 0;

if (shouldMaterialize) {
if (GetOwnerEntity() != null)
EmitSound("BaseCombatWeapon.WeaponDrop");

Materialize();
}
}

public void Materialize() {
if (IsEffectActive(EntityEffects.NoDraw)) {
EmitSound("AlyxEmp.Charge");

RemoveEffects(EntityEffects.NoDraw);
DoMuzzleFlash();
}

if (HasSpawnFlags(BasePlayer.SF_NORESPAWN) == false) {
VPhysicsInitNormal(SolidType.BBox, GetSolidFlags() | SolidFlags.Trigger, false);
SetMoveType(Source.MoveType.VPhysics);
}

SetPickupTouch();

SetThink(null);
}

public void AttemptToMaterialize() {
TimeUnit_t time = g_pGameRules.FlWeaponTryRespawn(this);

if (time == 0) {
Materialize();
return;
}

SetNextThink(gpGlobals.CurTime + time);
}

public void CheckRespawn() {
switch (g_pGameRules.WeaponShouldRespawn(this)) {
case GameRulesRespawnReturnCode.WeaponRespawnYes:
Respawn();
break;
case GameRulesRespawnReturnCode.WeaponRespawnNo:
return;
}
}
}
37 changes: 36 additions & 1 deletion Game.Server/BaseEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3183,7 +3183,42 @@ public virtual void Teleport(Vector3? newPosition, QAngle? newAngles, Vector3? n
}
public bool IsWorld() => EntIndex() == 0;

public virtual bool FVisible(BaseEntity entity) => throw new NotImplementedException();
public virtual bool FVisible(BaseEntity entity, Mask traceMask = Mask.BlockLOS) {
if ((entity.GetFlags() & EntityFlags.NoTarget) != 0)
return false;

Vector3 vecLookerOrigin = EyePosition();
Vector3 vecTargetOrigin = entity.EyePosition();

Trace tr;
if (AI_BaseNPCGlobals.ai_LOS_mode.GetBool())
Util.TraceLine(vecLookerOrigin, vecTargetOrigin, traceMask, this, Source.CollisionGroup.None, out tr);
else {
if (traceMask == Mask.BlockLOS)
traceMask = Mask.BlockLOSAndNPCs;

if (IsPlayer())
traceMask &= ~(Mask)Contents.BlockLOS;

TraceFilterLOS traceFilter = new(this, Source.CollisionGroup.None, entity);
Util.TraceLine(vecLookerOrigin, vecTargetOrigin, traceMask, ref traceFilter, out tr);
}

if (tr.Fraction != 1.0 || tr.StartSolid) {
if (tr.Ent == entity)
return true;

if (entity.IsPlayer()) {
BasePlayer player = (BasePlayer)entity;
if (tr.Ent == player.GetVehicleEntity())
return true;
}

return false;
}

return true;
}

public virtual void GetVectors(out Vector3 forward, out Vector3 right, out Vector3 up) {
ref readonly Matrix3x4 entityToWorld = ref EntityToWorldTransform();
Expand Down
Loading
Loading