#W# Change: Mobs are hidden until player has line of site.

This commit is contained in:
WarrentyExpired 2026-09-21 09:48:08 -04:00
parent 128a40e2c0
commit 2cfa8c8a9c

View file

@ -261,12 +261,16 @@ namespace Server.Mobiles
typeof(AncientSmithyHammer), typeof(Scorp) typeof(AncientSmithyHammer), typeof(Scorp)
}; };
// --- Serialized state ---------------------------------------------------------
// Fields matching their defaults (including npc-speeds table values) are elided by [SaveFlag]. private bool _isLosHidden;
private bool m_bTamable;
private int m_ColdResistance;
[SerializableField(0, setter: "private")] [SerializableField(0, setter: "private")]
private AIType _defaultAI; private AIType _defaultAI;
[SerializableField(1, setter: "private")] [SerializableField(1, setter: "private")]
[SaveFlag(nameof(ShouldSerializeCurrentAI), nameof(CurrentAIDefaultValue))] [SaveFlag(nameof(ShouldSerializeCurrentAI), nameof(CurrentAIDefaultValue))]
private AIType _currentAI; private AIType _currentAI;
@ -1704,35 +1708,36 @@ namespace Server.Mobiles
return false; return false;
} }
var c = m as BaseCreature;
var cMaster = c?.GetMaster();
var ourEthic = EthicAllegiance; var ourEthic = EthicAllegiance;
var pl = Ethics.Player.Find(cMaster ?? m, false); var pl = Ethics.Player.Find(m, true);
if (pl?.IsShielded == true && (ourEthic == null || ourEthic == pl.Ethic)) if (pl?.IsShielded == true && (ourEthic == null || ourEthic == pl.Ethic))
{ {
return false; return false;
} }
// Player-only protections; they must veto before the non-creature early return below. if (VirtueSystem.GetVirtues(m as PlayerMobile)?.HonorActive == true)
if (VirtueSystem.GetVirtues(m as PlayerMobile)?.HonorActive == true ||
TransformationSpellHelper.UnderTransformation(m, typeof(EtherealVoyageSpell)))
{ {
return false; return false;
} }
if (c == null || m is MilitiaFighter) if (m is not BaseCreature c || m is MilitiaFighter)
{ {
return true; return true;
} }
if (TransformationSpellHelper.UnderTransformation(m, typeof(EtherealVoyageSpell)))
{
return false;
}
if (_team != c.Team || FightMode == FightMode.Evil && m.Karma < 0 || c.FightMode == FightMode.Evil && Karma < 0) if (_team != c.Team || FightMode == FightMode.Evil && m.Karma < 0 || c.FightMode == FightMode.Evil && Karma < 0)
{ {
return true; return true;
} }
var master = GetMaster(); var master = GetMaster();
var cMaster = c.GetMaster();
if (master == null) if (master == null)
{ {
@ -2523,12 +2528,9 @@ namespace Server.Mobiles
{ {
AIObject?.AITimer.Stop(); AIObject?.AITimer.Stop();
// Read once: each read constructs an AI whose ctor may start its timer. if (ForcedAI != null)
var forced = ForcedAI;
if (forced != null)
{ {
AIObject = forced; AIObject = ForcedAI;
return; return;
} }
@ -2735,18 +2737,22 @@ namespace Server.Mobiles
aggressor.Aggressors.Add(AggressorInfo.Create(this, aggressor, true)); aggressor.Aggressors.Add(AggressorInfo.Create(this, aggressor, true));
} }
var toldToStandDown = _controlled && _controlMaster != null && Commandable && var ct = _controlOrder;
BaseAI.IsStandDownOrder(_controlOrder);
if (StandsDownOnCommand && toldToStandDown) if (AIObject != null)
{ {
AIObject?.DebugSay("I'm being attacked but my master told me not to fight."); if (!StandsDownOnCommand || !BaseAI.IsStandDownOrder(ct))
Warmode = false; {
return; AIObject.OnAggressiveAction(aggressor);
}
else
{
AIObject.DebugSay("I'm being attacked but my master told me not to fight.");
Warmode = false;
return;
}
} }
AIObject?.OnAggressiveAction(aggressor);
StopFlee(); StopFlee();
ForceReacquire(); ForceReacquire();
@ -2762,7 +2768,7 @@ namespace Server.Mobiles
} }
// Only reachable when the pet does not stand down: the orders above returned early. // Only reachable when the pet does not stand down: the orders above returned early.
if (aggressor.ChangingCombatant && toldToStandDown) if (aggressor.ChangingCombatant && (_controlled || _summoned) && BaseAI.IsStandDownOrder(ct))
{ {
IssueOrder(OrderType.Attack, null, aggressor); IssueOrder(OrderType.Attack, null, aggressor);
} }
@ -3100,20 +3106,13 @@ namespace Server.Mobiles
pack?.DisplayTo(from); pack?.DisplayTo(from);
} }
if (DeathAdderCharmable && from.CanBeHarmful(this, false) && if (DeathAdderCharmable && from.CanBeHarmful(this, false))
SummonFamiliarSpell.Table.TryGetValue(from, out var bc) && bc is DeathAdder { Deleted: false } deathAddr &&
deathAddr.Map == from.Map)
{ {
if (from.NetState.HasProtocolChanges(ProtocolChanges.Version7000)) if (SummonFamiliarSpell.Table.TryGetValue(from, out var bc) && (bc as DeathAdder)?.Deleted == false)
{
from.SendLocalizedMessage(1114362); // You charm the snake. Select a target to attack.
}
else
{ {
from.SendAsciiMessage("You charm the snake. Select a target to attack."); from.SendAsciiMessage("You charm the snake. Select a target to attack.");
from.Target = new DeathAdderCharmTarget(this);
} }
from.Target = new DeathAdderCharmTarget(this);
} }
if (MLQuestSystem.Enabled && CanGiveMLQuest && from is PlayerMobile mobile) if (MLQuestSystem.Enabled && CanGiveMLQuest && from is PlayerMobile mobile)
@ -3820,6 +3819,45 @@ namespace Server.Mobiles
{ {
var tc = Core.TickCount; var tc = Core.TickCount;
if (!Controlled && !Summoned && Region is Server.Regions.DungeonRegion)
{
if (Combatant == null)
{
bool someoneSeesMe = false;
foreach (Mobile m in Map.GetMobilesInRange(Location, 18))
{
if (m is PlayerMobile { Alive: true, AccessLevel: AccessLevel.Player } player)
{
if (player.InLOS(this))
{
someoneSeesMe = true;
break;
}
}
}
if (!someoneSeesMe && !Hidden)
{
_isLosHidden = true;
Hidden = true;
CantWalk = true;
}
else if (someoneSeesMe && Hidden && _isLosHidden)
{
_isLosHidden = false;
Hidden = false;
CantWalk = false;
}
}
else if (Hidden && _isLosHidden)
{
_isLosHidden = false;
Hidden = false;
CantWalk = false;
}
}
if (EnableRummaging && CanRummageCorpses && !Summoned && !Controlled && tc - m_NextRummageTime >= 0) if (EnableRummaging && CanRummageCorpses && !Summoned && !Controlled && tc - m_NextRummageTime >= 0)
{ {
double min, max; double min, max;