C#:
using Facepunch;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Plugins.DangerousTreasuresExtensionMethods;
using Rust;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.SceneManagement;
namespace Oxide.Plugins {
[Info("Dangerous Treasures", "nivex", "2.6.0")]
[Description("Event with treasure chests.")]
internal class DangerousTreasures : RustPlugin {
[PluginReference]
Plugin ZoneManager, Economics, ServerRewards, GUIAnnouncements, MarkerManager, Kits, Duelist, RaidableBases, AbandonedBases, Notify, AdvancedAlerts, Clans, Friends;
private new const string Name = "Dangerous Treasures";
private bool wipeChestsSeed;
private StoredData data = new();
private List<int> BlockedLayers = new() {
(int)Layer.Water, (int)Layer.Construction, (int)Layer.Trigger, (int)Layer.Prevent_Building, (int)Layer.Deployed, (int)Layer.Tree, (int)Layer.Clutter
}
;
private Dictionary<ulong, HumanoidBrain> HumanoidBrains = new();
private Dictionary<string, MonumentInfoEx> allowedMonuments = new();
private List<MonumentInfoEx> monuments = new();
private Dictionary<Vector3, ZoneInfo> managedZones = new();
private Dictionary<NetworkableId, TreasureChest> treasureChests = new();
private Dictionary<NetworkableId, string> looters = new();
private Dictionary<string, ItemDefinition> _definitions = new();
private Dictionary<string, SkinInfo> Skins = new();
private List<ulong> newmanProtections = new();
private List<ulong> indestructibleWarnings = new();
// indestructible messages limited to once every 10 seconds
private List<ulong> drawGrants = new();
// limit draw to once every 15 seconds by default
private List<int> obstructionLayers = new() {
Layers.Mask.Player_Server, Layers.Mask.Construction, Layers.Mask.Deployed
}
;
private List<string> _blockedColliders = new() {
"powerline_", "invisible_", "TopCol", "train", "swamp_", "floating_"
}
;
private List<string> underground = new() {
"Cave", "Sewer Branch", "Military Tunnel", "Underwater Lab", "Train Tunnel"
}
;
private HashSet<Vector3> _gridPositions = new();
private const int TARGET_MASK = 8454145;
private const int targetMask = Layers.Mask.World | Layers.Mask.Terrain | Layers.Mask.Default;
private const int visibleMask = Layers.Mask.Deployed | Layers.Mask.Construction | targetMask;
private const int obstructionLayer = Layers.Mask.Player_Server | Layers.Mask.Construction | Layers.Mask.Deployed;
private const int heightLayer = TARGET_MASK | Layers.Mask.Construction | Layers.Mask.Deployed | Layers.Mask.Clutter;
private StringBuilder _sb = new();
private Vector3 sd_customPos;
private static ulong BotIdCounter = 624922525;
public class ZoneInfo {
public Vector3 Position;
public Vector3 Size;
public float Distance;
public OBB OBB;
}
public class SkinInfo {
public List<ulong> skins = new();
public List<ulong> workshopSkins = new();
public List<ulong> importedSkins = new();
public List<ulong> allSkins = new();
}
private class PlayerInfo {
public int StolenChestsTotal;
public int StolenChestsSeed;
public PlayerInfo() {
}
}
private class StoredData {
public Dictionary<string, PlayerInfo> Players = new();
public double SecondsUntilEvent = double.MinValue;
public string CustomPosition;
public int TotalEvents = 0;
public StoredData() {
}
}
public class HumanoidNPC : ScientistNPC {
public new HumanoidBrain Brain;
public Configuration config => Brain.Instance.config;
public new Translate.Phrase LootPanelTitle => displayName;
public override string Categorize() => "Humanoid";
public override bool ShouldDropActiveItem() => false;
public override string displayName => Brain == null ? "HumanoidNPC" : Brain.displayName;
public override void AttackerInfo(ProtoBuf.PlayerLifeStory.DeathInfo info) {
info.attackerName = displayName;
info.attackerSteamID = userID;
info.inflictorName = inventory.containerBelt.GetSlot(0).info.shortname;
info.attackerDistance = Vector3.Distance(Brain.ServerPosition, Brain.AttackPosition);
}
public override void OnDied(HitInfo info) {
Brain.DisableShouldThink();
if (Brain.tc == null) {
return;
}
if (Brain.isMurderer && config.NPC.Murderers.DespawnInventory || !Brain.isMurderer && config.NPC.Scientists.DespawnInventory) {
inventory?.Strip();
}
svActiveItemID = default;
SendNetworkUpdate(BasePlayer.NetworkQueue.Update);
Brain.tc.npcs.Remove(this);
if (Brain.tc.whenNpcsDie && Brain.tc.npcs.Count == 0) {
Brain.tc.Unlock();
}
if (config.Unlock.LockToPlayerOnNpcDeath) {
Brain.tc.TrySetOwner(info);
}
if (config.Event.DestructTimeResetsWhenKilled && info != null && info.Initiator.Is(out BasePlayer attacker) && attacker.userID.IsSteamId()) {
Brain.tc.SetDestructTime();
}
if (config.BlockPaidContent) {
RemoveOwnershipPass();
}
base.OnDied(info);
}
private void RemoveOwnershipPass() {
using var itemList = Facepunch.Pool.Get<PooledList<Item>>();
inventory.GetAllItems(itemList);
for (int i = itemList.Count - 1; i >= 0; i--) {
Item item = itemList[i];
if (Brain.Instance.RequiresOwnership(item.info, item.skin)) {
item.GetHeldEntity().SafelyKill();
item.RemoveFromContainer();
item.Remove(0f);
}
}
}
}
public class HumanoidBrain : ScientistBrain {
public void DisableShouldThink() {
ThinkMode = AIThinkMode.FixedUpdate;
thinkRate = float.MaxValue;
lastWarpTime = float.MaxValue;
lastThinkTime = 0f;
sleeping = true;
isKilled = true;
SetEnabled(false);
}
internal DangerousTreasures Instance;
internal enum AttackType {
BaseProjectile, FlameThrower, Melee, Water, None
}
internal string displayName;
internal Transform NpcTransform;
internal HumanoidNPC npc;
internal AttackEntity _attackEntity;
internal FlameThrower flameThrower;
internal LiquidWeapon liquidWeapon;
internal BaseMelee baseMelee;
internal BaseProjectile baseProjectile;
internal BasePlayer AttackTarget;
internal Transform AttackTransform;
internal TreasureChest tc;
internal NpcSettings Settings;
internal List<Vector3> positions;
internal Vector3 DestinationOverride;
internal bool isKilled;
internal bool isMurderer;
internal ulong uid;
internal float lastWarpTime;
internal float softLimitSenseRange;
internal float nextAttackTime;
internal float attackRange;
internal float attackCooldown;
internal AttackType attackType = AttackType.None;
internal BaseNavigator.NavigationSpeed CurrentSpeed = BaseNavigator.NavigationSpeed.Normal;
internal Vector3 AttackPosition => AttackTransform == null ? default : AttackTransform.position;
internal Vector3 ServerPosition => NpcTransform == null ? default : NpcTransform.position;
private Configuration config => Instance.config;
internal AttackEntity AttackEntity {
get {
if (_attackEntity.IsNull()) {
IdentifyWeapon();
}
return _attackEntity;
}
}
public void UpdateWeapon(AttackEntity attackEntity, ItemId uid) {
npc.UpdateActiveItem(uid);
if (attackEntity is Chainsaw cs) {
cs.ServerNPCStart();
}
npc.damageScale = 1f;
attackEntity.TopUpAmmo();
attackEntity.SetHeld(true);
}
internal void IdentifyWeapon() {
_attackEntity = GetEntity().GetAttackEntity();
attackRange = 0f;
attackCooldown = 99999f;
attackType = AttackType.None;
baseMelee = null;
flameThrower = null;
liquidWeapon = null;
if (_attackEntity.IsNull()) {
return;
}
Action action = _attackEntity.ShortPrefabName switch {
"double_shotgun.entity" or "shotgun_pump.entity" or "shotgun_waterpipe.entity" or "spas12.entity" or "blowpipe.entity" or "boomerang.entity" => () => {
SetAttackRestrictions(AttackType.BaseProjectile, 30f, 0f, 30f);
}
, "ak47u.entity" or "ak47u_ice.entity" or "bolt_rifle.entity" or "glock.entity" or "hmlmg.entity" or "l96.entity" or "lr300.entity" or "m249.entity" or "m39.entity" or "m92.entity" or "mp5.entity" or "nailgun.entity" or "pistol_eoka.entity" or "pistol_revolver.entity" or "pistol_semiauto.entity" or "python.entity" or "semi_auto_rifle.entity" or "thompson.entity" or "smg.entity" => () => {
SetAttackRestrictions(AttackType.BaseProjectile, 300f, 0f, 150f);
}
, "snowballgun.entity" => () => {
SetAttackRestrictions(AttackType.BaseProjectile, 15f, 0.1f, 15f);
}
, "chainsaw.entity" or "jackhammer.entity" => () => {
baseMelee = _attackEntity as BaseMelee;
SetAttackRestrictions(AttackType.Melee, 2.5f, (_attackEntity.animationDelay + _attackEntity.deployDelay) * 2f);
}
, "axe_salvaged.entity" or "bone_club.entity" or "butcherknife.entity" or "candy_cane.entity" or "hammer_salvaged.entity" or "hatchet.entity" or "icepick_salvaged.entity" or "knife.combat.entity" or "knife_bone.entity" or "longsword.entity" or "mace.baseballbat" or "mace.entity" or "machete.weapon" or "pickaxe.entity" or "pitchfork.entity" or "salvaged_cleaver.entity" or "salvaged_sword.entity" or "sickle.entity" or "spear_stone.entity" or "spear_wooden.entity" or "stone_pickaxe.entity" or "stonehatchet.entity" => () => {
baseMelee = _attackEntity as BaseMelee;
SetAttackRestrictions(AttackType.Melee, 2.5f, _attackEntity.animationDelay + _attackEntity.deployDelay);
}
, "flamethrower.entity" => () => {
flameThrower = _attackEntity as FlameThrower;
SetAttackRestrictions(AttackType.FlameThrower, 10f, (_attackEntity.animationDelay + _attackEntity.deployDelay) * 2f);
}
, "compound_bow.entity" or "crossbow.entity" or "speargun.entity" or "bow_hunting.entity" => () => {
SetAttackRestrictions(AttackType.BaseProjectile, 200f, (_attackEntity.animationDelay + _attackEntity.deployDelay) * 1.25f, 150f);
}
, "watergun.entity" or "waterpistol.entity" => () => {
if ((liquidWeapon = _attackEntity as LiquidWeapon) != null) {
liquidWeapon.AutoPump = true;
SetAttackRestrictions(AttackType.Water, 10f, 2f);
}
}
, _ => () => _attackEntity = null
}
;
action();
}
private void SetAttackRestrictions(AttackType attackType, float attackRange, float attackCooldown, float effectiveRange = 0f) {
if (attackType == AttackType.BaseProjectile) {
baseProjectile = _attackEntity as BaseProjectile;
if (baseProjectile != null) {
baseProjectile.MuzzlePoint ??= baseProjectile.transform;
}
}
if (effectiveRange != 0f) {
_attackEntity.effectiveRange = effectiveRange;
}
this.attackType = attackType;
this.attackRange = attackRange;
this.attackCooldown = attackCooldown;
}
public bool ValidTarget => AttackTransform != null && !AttackTarget.IsKilled() && !ShouldForgetTarget(AttackTarget);
public override void OnDestroy() {
if (!Rust.Application.isQuitting) {
BaseEntity.Query.Server.RemoveBrain(GetEntity());
LeaveGroup();
}
Instance?.HumanoidBrains?.Remove(uid);
try {
CancelInvoke();
}
catch {
}
}
public override void InitializeAI() {
base.InitializeAI();
base.ForceSetAge(0f);
NpcTransform = GetEntity().transform;
Pet = false;
sleeping = false;
UseAIDesign = true;
AllowedToSleep = false;
HostileTargetsOnly = false;
AttackRangeMultiplier = 2f;
MaxGroupSize = 0;
Senses.Init(owner : GetEntity(), brain : this, memoryDuration : 5f, range : 50f, targetLostRange : 75f, visionCone : -1f, checkVision : false, checkLOS : true, ignoreNonVisionSneakers : true, listenRange : 15f, hostileTargetsOnly : false, senseFriendlies : false, ignoreSafeZonePlayers : false, senseTypes : EntityType.Player, refreshKnownLOS : true );
//senseTypes: config.Settings.Management.TargetNpcs? EntityType.Player | EntityType.BasePlayerNPC : EntityType.Player,
CanUseHealingItems = true;
}
public override void AddStates() {
base.AddStates();
states[AIState.Attack] = new AttackState(this);
}
public class AttackState : BaseAttackState {
private new HumanoidBrain brain;
private global : : HumanNPC npc;
private Transform NpcTransform;
private IAIAttack attack => brain.Senses.ownerAttack;
public AttackState(HumanoidBrain humanoidBrain) {
base.brain = brain = humanoidBrain;
base.AgrresiveState = true;
npc = brain.GetBrainBaseEntity() as global : : HumanNPC;
NpcTransform = npc.transform;
}
public override void StateEnter(BaseAIBrain _brain, BaseEntity _entity) {
if (_brain != null && NpcTransform != null && brain.ValidTarget) {
if (InAttackRange()) {
StartAttacking();
}
if (brain.CanUseNavMesh()) {
brain.Navigator.SetDestination(brain.DestinationOverride, BaseNavigator.NavigationSpeed.Fast, 0f, 0f);
}
}
}
public override void StateLeave(BaseAIBrain _brain, BaseEntity _entity) {
}
private void StopAttacking() {
if (attack != null) {
attack.StopAttacking();
brain.AttackTarget = null;
brain.AttackTransform = null;
brain.Navigator.ClearFacingDirectionOverride();
}
}
public override StateStatus StateThink(float delta, BaseAIBrain _brain, BaseEntity _entity) {
if (_brain == null || NpcTransform == null || attack == null) {
return StateStatus.Error;
}
if (!brain.ValidTarget || brain.isKilled) {
StopAttacking();
return StateStatus.Finished;
}
if (brain.Senses.ignoreSafeZonePlayers && brain.AttackTarget.InSafeZone()) {
return StateStatus.Error;
}
if (brain.CanUseNavMesh() && !brain.Navigator.SetDestination(brain.DestinationOverride, BaseNavigator.NavigationSpeed.Fast, 0f, 0f)) {
return StateStatus.Error;
}
if (!brain.CanShoot()) {
brain.Forget();
StopAttacking();
return StateStatus.Finished;
}
if (InAttackRange()) {
StartAttacking();
}
return StateStatus.Running;
}
private bool InAttackRange() {
return !npc.IsWounded() && brain.AttackTransform != null && attack.CanAttack(brain.AttackTarget) && brain.IsInAttackRange() && brain.CanSeeTarget(brain.AttackTarget);
}
private void StartAttacking() {
if (brain.AttackTransform == null) {
return;
}
brain.SetAimDirection();
if (!brain.CanShoot() || brain.IsAttackOnCooldown()) {
return;
}
if (brain.attackType == AttackType.BaseProjectile) {
RealisticShotTest();
}
else if (brain.attackType == AttackType.FlameThrower) {
brain.UseFlameThrower();
}
else if (brain.attackType == AttackType.Water) {
brain.UseWaterGun();
}
else brain.MeleeAttack();
}
private void RealisticShotTest() {
if (NpcTransform == null || brain.baseProjectile == null || brain.baseProjectile.primaryMagazine == null) {
return;
}
if (brain.AttackTarget.IsNpc) {
var faction = brain.AttackTarget.faction;
brain.AttackTarget.faction = BaseCombatEntity.Faction.Horror;
npc.ShotTest(brain.AttackPosition.Distance(brain.ServerPosition));
if (brain.AttackTarget != null) brain.AttackTarget.faction = faction;
}
else npc.ShotTest(brain.AttackPosition.Distance(brain.ServerPosition));
}
}
private bool init;
public void Init() {
if (init) return;
init = true;
lastWarpTime = Time.time;
npc.spawnPos = tc.containerPos;
npc.AdditionalLosBlockingLayer = visibleMask;
SetupNavigator(GetEntity(), GetComponent<BaseNavigator>(), tc.Radius);
}
private void Converge() {
foreach (var brain in Instance.HumanoidBrains.Values) {
if (brain != null && brain.NpcTransform != null && brain != this && brain.attackType == attackType && brain.CanConverge(npc)) {
brain.SetTarget(AttackTarget, false);
}
}
}
public void Forget() {
Senses.Players.Clear();
Senses.Memory.All.Clear();
Senses.Memory.Threats.Clear();
Senses.Memory.Targets.Clear();
Senses.Memory.Players.Clear();
Navigator.ClearFacingDirectionOverride();
DestinationOverride = GetRandomRoamPosition();
SenseRange = ListenRange = isMurderer ? Settings.Murderers.AggressionRange : Settings.Scientists.AggressionRange;
Senses.targetLostRange = TargetLostRange = SenseRange * 1.25f;
AttackTarget = null;
AttackTransform = null;
TryReturnHome();
}
private void RandomMove(float radius) {
var to = AttackPosition + UnityEngine.Random.onUnitSphere * radius;
to.y = TerrainMeta.HeightMap.GetHeight(to);
SetDestination(to);
}
public void SetupNavigator(BaseCombatEntity owner, BaseNavigator navigator, float distance) {
navigator.CanUseNavMesh = !Rust.Ai.AiManager.nav_disable;
navigator.MaxRoamDistanceFromHome = navigator.BestMovementPointMaxDistance = navigator.BestRoamPointMaxDistance = distance * 0.85f;
navigator.DefaultArea = "Walkable";
navigator.topologyPreference = ((TerrainTopology.Enum)TerrainTopology.EVERYTHING);
navigator.Agent.agentTypeID = NavMesh.GetSettingsByIndex(1).agentTypeID;
// 0:0, 1: -1372625422, 2: 1479372276, 3: -1923039037
navigator.MaxWaterDepth = 3f;
if (navigator.CanUseNavMesh) {
navigator.Init(owner, navigator.Agent);
}
}
public Vector3 GetAimDirection() {
if (Navigator.IsOverridingFacingDirection) {
return Navigator.FacingDirectionOverride;
}
if (InRange2D(AttackPosition, ServerPosition, 1f)) {
return npc.eyes.BodyForward();
}
return (AttackPosition - ServerPosition).normalized;
}
private void SetAimDirection() {
Navigator.SetFacingDirectionEntity(AttackTarget);
npc.SetAimDirection(GetAimDirection());
}
private void SetDestination() {
SetDestination(GetRandomRoamPosition());
}
private void SetDestination(Vector3 destination) {
if (!CanLeave(destination)) {
if (attackType != AttackType.BaseProjectile) {
destination = ((destination.XZ3D() - tc.containerPos.XZ3D()).normalized * (tc.Radius * 0.75f)) + tc.containerPos;
destination += UnityEngine.Random.onUnitSphere * (tc.Radius * 0.2f);
}
else {
destination = GetRandomRoamPosition();
}
CurrentSpeed = BaseNavigator.NavigationSpeed.Normal;
}
if (destination != DestinationOverride) {
destination.y = TerrainMeta.HeightMap.GetHeight(destination);
DestinationOverride = destination;
}
Navigator.SetCurrentSpeed(CurrentSpeed);
if (Navigator.CurrentNavigationType == BaseNavigator.NavigationType.None && !Rust.Ai.AiManager.ai_dormant && !Rust.Ai.AiManager.nav_disable) {
Navigator.SetCurrentNavigationType(BaseNavigator.NavigationType.NavMesh);
}
if (CanUseNavMesh() && !Navigator.SetDestination(destination, CurrentSpeed)) {
Navigator.Destination = destination;
npc.finalDestination = destination;
}
}
public bool CanUseNavMesh() => Navigator.CanUseNavMesh && !Navigator.StuckOffNavmesh;
public bool SetTarget(BasePlayer player, bool converge = true) {
if (NpcTransform == null) {
DisableShouldThink();
Destroy(this);
return false;
}
if (player.IsKilled() || player.limitNetworking) {
return false;
}
if (AttackTarget == player) {
return true;
}
if (npc.lastGunShotTime < Time.time + 2f) {
npc.nextTriggerTime = Time.time + 0.2f;
nextAttackTime = Time.realtimeSinceStartup + 0.2f;
}
Senses.Memory.SetKnown(player, npc, null);
npc.lastAttacker = player;
AttackTarget = player;
AttackTransform = player.transform;
if (!IsInSenseRange(AttackPosition)) {
SenseRange = ListenRange = (isMurderer ? Settings.Murderers.AggressionRange : Settings.Scientists.AggressionRange) + AttackPosition.Distance(ServerPosition);
TargetLostRange = SenseRange + (SenseRange * 0.25f);
}
else {
SenseRange = ListenRange = softLimitSenseRange;
TargetLostRange = softLimitSenseRange * 1.25f;
}
if (converge) {
Converge();
}
return true;
}
private void TryReturnHome() {
if (Settings.CanLeave && !IsInHomeRange()) {
CurrentSpeed = BaseNavigator.NavigationSpeed.Normal;
Warp();
}
}
private void TryToAttack() => TryToAttack(null);
private void TryToAttack(BasePlayer attacker) {
if ((attacker ??= GetBestTarget()).IsNull()) {
return;
}
if (ShouldForgetTarget(attacker)) {
Forget();
return;
}
if (!SetTarget(attacker) || AttackTransform == null || !CanSeeTarget(attacker)) {
return;
}
if (attackType == AttackType.BaseProjectile) {
TryScientistActions();
}
else {
TryMurdererActions();
}
SwitchToState(AIState.Attack, -1);
}
private void TryMurdererActions() {
CurrentSpeed = BaseNavigator.NavigationSpeed.Fast;
if (!IsInReachableRange()) {
RandomMove(15f);
}
else if (!IsInAttackRange()) {
if (attackType == AttackType.FlameThrower) {
RandomMove(attackRange);
}
else {
SetDestination(AttackPosition);
}
}
}
private void TryScientistActions() {
CurrentSpeed = BaseNavigator.NavigationSpeed.Fast;
SetDestination();
}
public void SetupMovement(List<Vector3> positions) {
this.positions = positions;
InvokeRepeating(TryToRoam, 0f, 7.5f);
InvokeRepeating(TryToAttack, 1f, 1.25f);
}
private void TryToRoam() {
if (Settings.KillUnderwater && npc.playerCollider != null && npc.IsSwimming()) {
DisableShouldThink();
Instance.SafelyKillNpc(npc);
Destroy(this);
return;
}
if (ValidTarget) {
return;
}
if (IsStuck()) {
Warp();
Navigator.stuckTimer = 0f;
}
CurrentSpeed = BaseNavigator.NavigationSpeed.Normal;
SetDestination();
}
private bool IsStuck() => false;
//InRange(npc.transform.position, Navigator.stuckCheckPosition, Navigator.StuckDistance);
public void Warp() {
if (Time.time < lastWarpTime) {
return;
}
lastWarpTime = Time.time + 1f;
DestinationOverride = GetRandomRoamPosition();
Navigator.Warp(DestinationOverride);
}
private void UseFlameThrower() {
if (flameThrower.ammo < flameThrower.maxAmmo * 0.25) {
flameThrower.SetFlameState(false);
flameThrower.ServerReload();
return;
}
npc.triggerEndTime = Time.time + attackCooldown;
flameThrower.SetFlameState(true);
flameThrower.Invoke(() => flameThrower.SetFlameState(false), 2f);
}
private void UseWaterGun() {
if (Physics.Raycast(npc.eyes.BodyRay(), out var hit, 10f, 1218652417)) {
WaterBall.DoSplash(hit.point, 2f, ItemManager.FindItemDefinition("water"), 10);
DamageUtil.RadiusDamage(npc, liquidWeapon.LookupPrefab(), hit.point, 0.15f, 0.15f, new(), 131072, true);
}
}
private void UseChainsaw() {
AttackEntity.TopUpAmmo();
AttackEntity.ServerUse();
AttackTarget.Hurt(10f * AttackEntity.npcDamageScale, DamageType.Bleeding, npc);
}
private void MeleeAttack() {
if (baseMelee.IsNull()) {
return;
}
if (AttackEntity is Chainsaw) {
UseChainsaw();
return;
}
Vector3 position = AttackPosition;
AttackEntity.StartAttackCooldown(AttackEntity.repeatDelay * 2f);
npc.SignalBroadcast(BaseEntity.Signal.Attack, string.Empty, null);
if (baseMelee.swingEffect.isValid) {
Effect.server.Run(baseMelee.swingEffect.resourcePath, position, Vector3.forward, npc.Connection, false);
}
HitInfo hitInfo = new() {
damageTypes = new(), DidHit = true, Initiator = npc, HitEntity = AttackTarget, HitPositionWorld = position, HitPositionLocal = AttackTransform.InverseTransformPoint(position), HitNormalWorld = npc.eyes.BodyForward(), HitMaterial = StringPool.Get("Flesh"), PointStart = ServerPosition, PointEnd = position, Weapon = AttackEntity, WeaponPrefab = AttackEntity
}
;
hitInfo.damageTypes.Set(DamageType.Slash, baseMelee.TotalDamage() * AttackEntity.npcDamageScale);
Effect.server.ImpactEffect(hitInfo);
AttackTarget.OnAttacked(hitInfo);
}
private bool CanConverge(global : : HumanNPC other) {
if (ValidTarget || other.IsKilled() || other.IsDead()) return false;
return IsInTargetRange(other.transform.position);
}
private bool CanLeave(Vector3 destination) {
return Settings.CanLeave || IsInLeaveRange(destination);
}
private bool CanSeeTarget(BasePlayer target) {
if (Navigator.CurrentNavigationType == BaseNavigator.NavigationType.None && (attackType == AttackType.FlameThrower || attackType == AttackType.Melee)) {
return true;
}
if (ServerPosition.Distance(target.ServerPosition) < 10f || Senses.Memory.IsLOS(target)) {
return true;
}
nextAttackTime = Time.realtimeSinceStartup + 1f;
return false;
}
public bool CanRoam(Vector3 destination) {
return destination == DestinationOverride && IsInSenseRange(destination);
}
private bool CanShoot() {
if (attackType == AttackType.None) {
return false;
}
return true;
}
public BasePlayer GetBestTarget() {
if (npc.IsWounded()) {
return null;
}
float delta = -1f;
BasePlayer target = null;
foreach (var player in Senses.Memory.Targets.OfType<BasePlayer>()) {
if (ShouldForgetTarget(player) || !player.IsHuman() && !config.NPC.TargetNpcs) continue;
float dist = player.transform.position.Distance(npc.transform.position);
float rangeDelta = 1f - Mathf.InverseLerp(1f, SenseRange, dist);
rangeDelta += (CanSeeTarget(player) ? 2f : 0f);
if (rangeDelta <= delta) continue;
target = player;
delta = rangeDelta;
}
return target;
}
private Vector3 GetRandomRoamPosition() {
return positions.GetRandom();
}
private bool IsAttackOnCooldown() {
if (attackType == AttackType.None || Time.realtimeSinceStartup < nextAttackTime) {
return true;
}
if (attackCooldown > 0f) {
nextAttackTime = Time.realtimeSinceStartup + attackCooldown;
}
return false;
}
private bool IsInAttackRange(float range = 0f) {
return InRange(ServerPosition, AttackPosition, range == 0f ? attackRange : range);
}
private bool IsInHomeRange() {
return InRange(ServerPosition, tc.containerPos, Mathf.Max(tc.Radius, TargetLostRange));
}
private bool IsInLeaveRange(Vector3 destination) {
return InRange(tc.containerPos, destination, tc.Radius);
}
private bool IsInReachableRange() {
if (AttackPosition.y - ServerPosition.y > attackRange) {
return false;
}
return attackType != AttackType.Melee || InRange(AttackPosition, ServerPosition, 15f);
}
private bool IsInSenseRange(Vector3 destination) {
return InRange2D(tc.containerPos, destination, SenseRange);
}
private bool IsInTargetRange(Vector3 destination) {
return InRange2D(tc.containerPos, destination, TargetLostRange);
}
private bool ShouldForgetTarget(BasePlayer target) {
return target.IsKilled() || target.health <= 0f || target.limitNetworking || target.IsDead() || target.skinID == 14922524 || !IsInTargetRange(target.transform.position);
}
}
private class GuidanceSystem : FacepunchBehaviour {
private TimedExplosive missile;
private ServerProjectile projectile;
private BaseEntity target;
private Vector3 launchPos;
private List<ulong> newmans = new();
internal DangerousTreasures Instance;
private Configuration config => Instance.config;
private void Awake() {
missile = GetComponent<TimedExplosive>();
projectile = missile.GetComponent<ServerProjectile>();
launchPos = missile.transform.position;
launchPos.y = TerrainMeta.HeightMap.GetHeight(launchPos);
projectile.gravityModifier = 0f;
projectile.speed = 0.1f;
projectile.InitializeVelocity(Vector3.up);
missile.explosionRadius = 0f;
missile.damageTypes = new();
// no damage
}
public void SetTarget(BaseEntity target) {
this.target = target;
}
public void Launch(float targettingTime) {
missile.timerAmountMin = config.MissileLauncher.Lifetime;
missile.timerAmountMax = config.MissileLauncher.Lifetime;
missile.Spawn();
Instance.timer.Once(targettingTime, () => {
if (missile.IsKilled()) return;
using var list = Pool.Get<PooledList<BasePlayer>>();
using var players = FindEntitiesOfType<BasePlayer>(launchPos, config.Event.Radius + config.MissileLauncher.Distance, Layers.Mask.Player_Server);
for (int i = 0; i < players.Count; i++) {
var player = players[i];
if (player.IsKilled() || !player.IsHuman() || !player.CanInteract()) continue;
if (config.MissileLauncher.IgnoreFlying && player.IsFlying) continue;
if (newmans.Contains(player.userID) || Instance.newmanProtections.Contains(player.userID)) continue;
list.Add(player);
// acquire a player target
}
if (list.Count > 0) {
target = list.GetRandom();
// pick a random player
}
else if (!config.MissileLauncher.TargetChest) {
missile.SafelyKill();
return;
}
projectile.speed = config.Rocket.Speed * 2f;
InvokeRepeating(GuideMissile, 0.2f, 0.2f);
}
);
}
public void Exclude(List<ulong> newmans) {
if (newmans != null && newmans.Count > 0) {
this.newmans = newmans.ToList();
}
}
private void GuideMissile() {
if (target == null) return;
if (target.IsDestroyed) {
missile.SafelyKill();
return;
}
if (missile.IsKilled() || projectile == null) {
Destroy(this);
return;
}
if (InRange(target.transform.position, missile.transform.position, 1f)) {
missile.Explode();
return;
}
var direction = (target.transform.position - missile.transform.position) + Vector3.down;
// direction to guide the missile
projectile.InitializeVelocity(direction);
// guide the missile to the target's position
}
private void OnDestroy() {
try {
CancelInvoke();
}
catch {
}
Destroy(this);
}
}
public class TreasureChest : FacepunchBehaviour {
internal DangerousTreasures Instance;
internal ulong userid;
internal GameObject go;
internal StorageContainer container;
internal Vector3 containerPos;
internal Vector3 lastFirePos;
internal int npcMaxAmountMurderers;
internal int npcMaxAmountScientists;
internal int npcSpawnedAmount;
internal int countdownTime;
internal bool started;
internal bool opened;
internal bool firstEntered;
internal bool markerCreated;
internal bool killed;
internal bool IsUnloading;
internal bool requireAllNpcsDie;
internal bool whenNpcsDie;
internal float claimTime;
internal float _radius;
internal long _unlockTime;
internal NetworkableId uid;
private Dictionary<string, List<string>> npcKits = Pool.Get<Dictionary<string, List<string>>>();
private Dictionary<ulong, float> fireticks = Pool.Get<Dictionary<ulong, float>>();
private List<FireBall> fireballs = Pool.Get<List<FireBall>>();
private List<ulong> newmans = Pool.Get<List<ulong>>();
private List<ulong> traitors = Pool.Get<List<ulong>>();
private List<ulong> protects = Pool.Get<List<ulong>>();
private List<ulong> players = Pool.Get<List<ulong>>();
private List<TimedExplosive> missiles = Pool.Get<List<TimedExplosive>>();
private List<int> times = Pool.Get<List<int>>();
private List<SphereEntity> spheres = Pool.Get<List<SphereEntity>>();
private List<Vector3> missilePositions = Pool.Get<List<Vector3>>();
private List<Vector3> firePositions = Pool.Get<List<Vector3>>();
public List<HumanoidNPC> npcs = Pool.Get<List<HumanoidNPC>>();
private Timer destruct, unlock, countdown, announcement;
private MapMarkerExplosion explosionMarker;
private MapMarkerGenericRadius genericMarker;
private VendingMachineMapMarker vendingMarker;
private string FormatGridReference(Vector3 position) => Instance.FormatGridReference(position, config.Settings.ShowGrid);
private void Message(BasePlayer player, string key, params object[] args) => Instance.Message(player, key, args);
private Configuration config => Instance.config;
public float Radius {
get {
return _radius;
}
set {
_radius = value;
Awaken();
}
}
public float SqrRadius => Radius * Radius;
private void Free() {
fireballs.ResetToPool();
newmans.ResetToPool();
traitors.ResetToPool();
protects.ResetToPool();
missiles.ResetToPool();
times.ResetToPool();
spheres.ResetToPool();
missilePositions.ResetToPool();
firePositions.ResetToPool();
npcKits.ResetToPool();
destruct?.Destroy();
unlock?.Destroy();
countdown?.Destroy();
announcement?.Destroy();
}
private class NewmanTracker : FacepunchBehaviour {
BasePlayer player;
TreasureChest chest;
DangerousTreasures Instance;
Configuration config => Instance.config;
private void Message(BasePlayer player, string key, params object[] args) => Instance.Message(player, key, args);
private void Awake() {
player = GetComponent<BasePlayer>();
}
public void Assign(DangerousTreasures instance, TreasureChest chest) {
Instance = instance;
this.chest = chest;
InvokeRepeating(Track, 1f, 0.25f);
}
private void Track() {
if (chest == null || chest.started || player.IsKilled() || !player.IsConnected || !chest.players.Contains(player.userID)) {
Destroy(this);
return;
}
if (!config.NewmanMode.Aura && !config.NewmanMode.Harm && !config.Fireballs.Enabled) {
Destroy(this);
return;
}
if (!InRange2D(player.transform.position, chest.containerPos, chest.Radius)) {
return;
}
if (config.NewmanMode.Aura || config.NewmanMode.Harm) {
using var itemList = player.GetAllItems();
int sum = itemList.Sum(item => player.IsHostileItem(item) ? 1 : 0);
if (sum == 0) {
if (config.NewmanMode.Aura && !chest.newmans.Contains(player.userID) && !chest.traitors.Contains(player.userID)) {
Message(player, "Newman Enter");
chest.newmans.Add(player.userID);
}
if (config.NewmanMode.Harm && !Instance.newmanProtections.Contains(player.userID) && !chest.protects.Contains(player.userID) && !chest.traitors.Contains(player.userID)) {
Message(player, "Newman Protect");
Instance.newmanProtections.Add(player.userID);
chest.protects.Add(player.userID);
}
if (!chest.traitors.Contains(player.userID)) {
return;
}
}
if (chest.newmans.Remove(player.userID)) {
Message(player, config.Fireballs.Enabled ? "Newman Traitor Burn" : "Newman Traitor");
if (!chest.traitors.Contains(player.userID)) chest.traitors.Add(player.userID);
Instance.newmanProtections.Remove(player.userID);
chest.protects.Remove(player.userID);
}
}
if (!config.Fireballs.Enabled || player.IsFlying) {
return;
}
var stamp = Time.realtimeSinceStartup;
if (!chest.fireticks.ContainsKey(player.userID)) {
chest.fireticks[player.userID] = stamp + config.Fireballs.SecondsBeforeTick;
}
if (chest.fireticks[player.userID] - stamp <= 0) {
chest.fireticks[player.userID] = stamp + config.Fireballs.SecondsBeforeTick;
chest.SpawnFire(player.transform.position);
}
}
private void OnDestroy() {
try {
CancelInvoke(Track);
}
catch {
}
Destroy(this);
}
}
public void Kill(bool isUnloading) {
Instance.treasureChests.Remove(ui
// ... [Code preview truncated due to length. Full source in attached file] ...