Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Exiled 9] The honored one😭 #133

Open
wants to merge 14 commits into
base: dev
Choose a base branch
from
47 changes: 47 additions & 0 deletions EXILED/Exiled.API/Enums/Scp939VisibilityStates.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// -----------------------------------------------------------------------
// <copyright file="Scp939VisibilityStates.cs" company="Exiled Team">
// Copyright (c) Exiled Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------

namespace Exiled.API.Enums
{
using Features.Roles;

/// <summary>
/// Unique identifier for a <see cref="Scp939Role"/>.
/// </summary>
public enum Scp939VisibilityState
{
/// <summary>
/// SCP-939 doesnt see an other player, by default FPC role logic.
/// </summary>
None,

/// <summary>
/// SCP-939 doesnt see an player, by basic SCP-939 logic.
/// </summary>
NotSeen,

/// <summary>
/// SCP-939 sees an other player, who is teammate SCP.
/// </summary>
SeenAsScp,

/// <summary>
/// SCP-939 sees an other player due the Alpha Warhead detonation.
/// </summary>
SeenByDetonation,

/// <summary>
/// SCP-939 sees an other player, due the base-game vision range logic.
/// </summary>
SeenByRange,

/// <summary>
/// SCP-939 sees an other player for a while, after it's out of range.
/// </summary>
SeenByLastTime,
}
}
60 changes: 11 additions & 49 deletions EXILED/Exiled.API/Extensions/MirrorExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ namespace Exiled.API.Extensions
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;

using Exiled.API.Enums;
using Exiled.API.Features.Roles;

using Features;
using Features.Pools;

Expand Down Expand Up @@ -239,9 +242,7 @@ public static void SetRoomLightIntensityForTargetOnly(this Room room, Player tar
/// </summary>
/// <param name="player">Player to change.</param>
/// <param name="type">Model type.</param>
/// <param name="skipJump">Whether or not to skip the little jump that works around an invisibility issue.</param>
/// <param name="unitId">The UnitNameId to use for the player's new role, if the player's new role uses unit names. (is NTF).</param>
public static void ChangeAppearance(this Player player, RoleTypeId type, bool skipJump = false, byte unitId = 0) => ChangeAppearance(player, type, Player.List.Where(x => x != player), skipJump, unitId);
public static void ChangeAppearance(this Player player, RoleTypeId type) => ChangeAppearance(player, type, Player.List.Where(x => x != player));

/// <summary>
/// Change <see cref="Player"/> character model for appearance.
Expand All @@ -250,62 +251,23 @@ public static void SetRoomLightIntensityForTargetOnly(this Room room, Player tar
/// <param name="player">Player to change.</param>
/// <param name="type">Model type.</param>
/// <param name="playersToAffect">The players who should see the changed appearance.</param>
/// <param name="skipJump">Whether or not to skip the little jump that works around an invisibility issue.</param>
/// <param name="unitId">The UnitNameId to use for the player's new role, if the player's new role uses unit names. (is NTF).</param>
public static void ChangeAppearance(this Player player, RoleTypeId type, IEnumerable<Player> playersToAffect, bool skipJump = false, byte unitId = 0)
public static void ChangeAppearance(this Player player, RoleTypeId type, IEnumerable<Player> playersToAffect)
{
if (!player.IsConnected || !RoleExtensions.TryGetRoleBase(type, out PlayerRoleBase roleBase))
if (!player.IsConnected)
return;

bool isRisky = type.GetTeam() is Team.Dead || player.IsDead;

NetworkWriterPooled writer = NetworkWriterPool.Get();
writer.WriteUShort(38952);
writer.WriteUInt(player.NetId);
writer.WriteRoleType(type);

if (roleBase is HumanRole humanRole && humanRole.UsesUnitNames)
{
if (player.Role.Base is not HumanRole)
isRisky = true;
writer.WriteByte(unitId);
}

if (roleBase is ZombieRole)
if (!player.Role.CheckAppearanceCompatibility(type))
louis1706 marked this conversation as resolved.
Show resolved Hide resolved
{
if (player.Role.Base is not ZombieRole)
isRisky = true;

writer.WriteUShort((ushort)Mathf.Clamp(Mathf.CeilToInt(player.MaxHealth), ushort.MinValue, ushort.MaxValue));
writer.WriteBool(true);
}

if (roleBase is FpcStandardRoleBase fpc)
{
if (player.Role.Base is not FpcStandardRoleBase playerfpc)
isRisky = true;
else
fpc = playerfpc;

ushort value = 0;
fpc?.FpcModule.MouseLook.GetSyncValues(0, out value, out ushort _);
writer.WriteRelativePosition(player.RelativePosition);
writer.WriteUShort(value);
Log.Error($"Prevent Seld-Desync of {player.Nickname} ({player.Role.Type}) with {type}");
return;
}

foreach (Player target in playersToAffect)
{
if (target != player || !isRisky)
target.Connection.Send(writer.ToArraySegment());
else
Log.Error($"Prevent Seld-Desync of {player.Nickname} with {type}");
player.Role.TrySetIndividualAppearance(target, type, false);
}

NetworkWriterPool.Return(writer);

// To counter a bug that makes the player invisible until they move after changing their appearance, we will teleport them upwards slightly to force a new position update for all clients.
if (!skipJump)
player.Position += Vector3.up * 0.25f;
player.Role.UpdateAppearance();
}

/// <summary>
Expand Down
5 changes: 4 additions & 1 deletion EXILED/Exiled.API/Extensions/ReflectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,10 @@ public static void CopyProperties(this object target, object source)
throw new InvalidTypeException("Target and source type mismatch!");

foreach (PropertyInfo sourceProperty in type.GetProperties())
type.GetProperty(sourceProperty.Name)?.SetValue(target, sourceProperty.GetValue(source, null), null);
{
if (sourceProperty.SetMethod != null && sourceProperty.GetMethod != null)
sourceProperty.SetValue(target, sourceProperty.GetValue(source, null), null);
}
}
}
}
29 changes: 29 additions & 0 deletions EXILED/Exiled.API/Extensions/RoleExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ namespace Exiled.API.Extensions
using System.Linq;

using Enums;

using Exiled.API.Features;
using Exiled.API.Features.Roles;
using Exiled.API.Features.Spawn;
using InventorySystem;
using InventorySystem.Configs;
Expand Down Expand Up @@ -168,5 +171,31 @@ public static Dictionary<AmmoType, ushort> GetStartingAmmo(this RoleTypeId roleT

return info.Ammo.ToDictionary(kvp => kvp.Key.GetAmmoType(), kvp => kvp.Value);
}

/// <summary>
/// Gets a custom appearance for target <see cref="Player"/>, using <see cref="Role.GlobalAppearance"/>, <see cref="Role.TeamAppearances"/> and <see cref="Role.IndividualAppearances"/>.
/// </summary>
/// <param name="role">The player ><see cref="Role"/>, whose appearance we want to get.</param>
/// <param name="player">Target <see cref="Player"/>.</param>
/// <returns>A valid <see cref="RoleTypeId"/>, what target <see cref="Player"/> will see.</returns>
public static RoleTypeId GetAppearanceForPlayer(this Role role, Player player)
{
RoleTypeId appearance = role.GlobalAppearance;

if (player == null)
return appearance;

if (role.IndividualAppearances.TryGetValue(player, out appearance))
{
return appearance;
}

if (role.TeamAppearances.TryGetValue(player.Role.Team, out appearance))
{
return appearance;
}

return role.GlobalAppearance;
}
}
}
19 changes: 19 additions & 0 deletions EXILED/Exiled.API/Features/Items/Consumable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

namespace Exiled.API.Features.Items
{
using Exiled.API.Extensions;
using Exiled.API.Interfaces;
using InventorySystem.Items.Usables;

using BaseConsumable = InventorySystem.Items.Usables.Consumable;

Expand Down Expand Up @@ -40,6 +42,23 @@ internal Consumable(ItemType type)
/// </summary>
public new BaseConsumable Base { get; }

/// <inheritdoc/>
public override void Use(Player owner = null)
{
Player oldOwner = Owner;
owner ??= Owner;

if (owner is null)
throw new System.InvalidOperationException("The Owner of the item cannot be null.");

Base.Owner = owner.ReferenceHub;
Base.ActivateEffects();

typeof(UsableItemsController).InvokeStaticEvent(nameof(UsableItemsController.ServerOnUsingCompleted), new object[] { owner.ReferenceHub, Base });

Base.Owner = oldOwner.ReferenceHub;
}

/// <inheritdoc/>
internal override void ChangeOwner(Player oldOwner, Player newOwner)
{
Expand Down
86 changes: 24 additions & 62 deletions EXILED/Exiled.API/Features/Items/ExplosiveGrenade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,9 @@ namespace Exiled.API.Features.Items
using Exiled.API.Features.Pickups;
using Exiled.API.Features.Pickups.Projectiles;

using InventorySystem.Items;
using InventorySystem.Items.Pickups;
using InventorySystem.Items.ThrowableProjectiles;
using UnityEngine;

using Object = UnityEngine.Object;

/// <summary>
/// A wrapper class for <see cref="ExplosionGrenade"/>.
/// </summary>
Expand All @@ -31,7 +27,6 @@ public class ExplosiveGrenade : Throwable
public ExplosiveGrenade(ThrowableItem itemBase)
: base(itemBase)
{
Projectile = (ExplosionGrenadeProjectile)((Throwable)this).Projectile;
}

/// <summary>
Expand All @@ -45,64 +40,30 @@ internal ExplosiveGrenade(ItemType type, Player player = null)
{
}

/// <summary>
/// Gets a <see cref="ExplosionGrenadeProjectile"/> to change grenade properties.
/// </summary>
public new ExplosionGrenadeProjectile Projectile { get; }

/// <summary>
/// Gets or sets the maximum radius of the grenade.
/// </summary>
public float MaxRadius
{
get => Projectile.MaxRadius;
set => Projectile.MaxRadius = value;
}
public float MaxRadius { get; set; }

/// <summary>
/// Gets or sets the multiplier for damage against <see cref="Side.Scp"/> players.
/// </summary>
public float ScpDamageMultiplier
{
get => Projectile.ScpDamageMultiplier;
set => Projectile.ScpDamageMultiplier = value;
}
public float ScpDamageMultiplier { get; set; }

/// <summary>
/// Gets or sets how long the <see cref="EffectType.Burned"/> effect will last.
/// </summary>
public float BurnDuration
{
get => Projectile.BurnDuration;
set => Projectile.BurnDuration = value;
}
public float BurnDuration { get; set; }

/// <summary>
/// Gets or sets how long the <see cref="EffectType.Deafened"/> effect will last.
/// </summary>
public float DeafenDuration
{
get => Projectile.DeafenDuration;
set => Projectile.DeafenDuration = value;
}
public float DeafenDuration { get; set; }

/// <summary>
/// Gets or sets how long the <see cref="EffectType.Concussed"/> effect will last.
/// </summary>
public float ConcussDuration
{
get => Projectile.ConcussDuration;
set => Projectile.ConcussDuration = value;
}

/// <summary>
/// Gets or sets how long the fuse will last.
/// </summary>
public float FuseTime
{
get => Projectile.FuseTime;
set => Projectile.FuseTime = value;
}
public float ConcussDuration { get; set; }

Comment on lines 43 to 67

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why ? This change would be needed ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally didn't want to change the Projectile API structure, but still came to the conclusion that storing prefabs for throwables was a bad idea from the point of view of all cases😭
And removing it and writing all the properties and their relations correctly, is much more safer

/// <summary>
/// Spawns an active grenade on the map at the specified location.
Expand All @@ -115,28 +76,14 @@ public ExplosionGrenadeProjectile SpawnActive(Vector3 position, Player owner = n
#if DEBUG
Log.Debug($"Spawning active grenade: {FuseTime}");
#endif
ItemPickupBase ipb = Object.Instantiate(Projectile.Base, position, Quaternion.identity);

ipb.Info = new PickupSyncInfo(Type, Weight, ItemSerialGenerator.GenerateNext());

ExplosionGrenadeProjectile grenade = Pickup.Get<ExplosionGrenadeProjectile>(ipb);

grenade.Base.gameObject.SetActive(true);

grenade.MaxRadius = MaxRadius;
grenade.ScpDamageMultiplier = ScpDamageMultiplier;
grenade.BurnDuration = BurnDuration;
grenade.DeafenDuration = DeafenDuration;
grenade.ConcussDuration = ConcussDuration;
grenade.FuseTime = FuseTime;

grenade.PreviousOwner = owner ?? Server.Host;
Projectile projectile = CreateProjectile(position, Quaternion.identity);

grenade.Spawn();
projectile.PreviousOwner = owner;

grenade.Base.ServerActivate();
projectile.Activate();

return grenade;
return (ExplosionGrenadeProjectile)projectile;
}

/// <summary>
Expand Down Expand Up @@ -175,5 +122,20 @@ internal override void ReadPickupInfo(Pickup pickup)
FuseTime = explosiveGrenadePickup.FuseTime;
}
}

/// <inheritdoc/>
protected override void InitializeProperties(ThrowableItem throwable)
{
base.InitializeProperties(throwable);

if (throwable.Projectile is ExplosionGrenade grenade)
{
MaxRadius = grenade._maxRadius;
ScpDamageMultiplier = grenade._scpDamageMultiplier;
BurnDuration = grenade._burnedDuration;
DeafenDuration = grenade._deafenedDuration;
ConcussDuration = grenade._concussedDuration;
}
}
}
}
Loading