MapObject.cs 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030
  1. using System.Drawing;
  2. using Server.MirDatabase;
  3. using Server.MirEnvir;
  4. using Server.MirObjects.Monsters;
  5. using S = ServerPackets;
  6. namespace Server.MirObjects
  7. {
  8. public abstract class MapObject
  9. {
  10. protected static MessageQueue MessageQueue
  11. {
  12. get { return MessageQueue.Instance; }
  13. }
  14. protected static Envir Envir
  15. {
  16. get { return Envir.Main; }
  17. }
  18. public readonly uint ObjectID = Envir.ObjectID;
  19. public abstract ObjectType Race { get; }
  20. public abstract string Name { get; set; }
  21. public long ExplosionInflictedTime;
  22. public int ExplosionInflictedStage;
  23. private int SpawnThread;
  24. private Map _currentMap;
  25. public Map CurrentMap
  26. {
  27. set
  28. {
  29. _currentMap = value;
  30. CurrentMapIndex = _currentMap != null ? _currentMap.Info.Index : 0;
  31. }
  32. get { return _currentMap; }
  33. }
  34. public abstract int CurrentMapIndex { get; set; }
  35. public abstract Point CurrentLocation { get; set; }
  36. public abstract MirDirection Direction { get; set; }
  37. public abstract ushort Level { get; set; }
  38. public abstract int Health { get; }
  39. public abstract int MaxHealth { get; }
  40. public byte PercentHealth
  41. {
  42. get { return (byte)(Health / (float)MaxHealth * 100); }
  43. }
  44. public byte Light;
  45. public int AttackSpeed;
  46. protected long brownTime;
  47. public virtual long BrownTime
  48. {
  49. get { return brownTime; }
  50. set { brownTime = value; }
  51. }
  52. public long CellTime, PKPointTime, LastHitTime, EXPOwnerTime;
  53. public Color NameColour = Color.White;
  54. public bool Dead, Undead, Harvested, AutoRev;
  55. public List<KeyValuePair<string, string>> NPCVar = new List<KeyValuePair<string, string>>();
  56. public virtual int PKPoints { get; set; }
  57. public ushort PotHealthAmount, PotManaAmount, HealAmount, VampAmount;
  58. public bool CoolEye;
  59. private bool _hidden;
  60. public bool Hidden
  61. {
  62. get
  63. {
  64. return _hidden;
  65. }
  66. set
  67. {
  68. if (_hidden == value) return;
  69. _hidden = value;
  70. CurrentMap.Broadcast(new S.ObjectHidden { ObjectID = ObjectID, Hidden = value }, CurrentLocation);
  71. }
  72. }
  73. private bool _observer;
  74. public bool Observer
  75. {
  76. get
  77. {
  78. return _observer;
  79. }
  80. set
  81. {
  82. if (_observer == value) return;
  83. _observer = value;
  84. if (!_observer)
  85. BroadcastInfo();
  86. else
  87. Broadcast(new S.ObjectRemove { ObjectID = ObjectID });
  88. }
  89. }
  90. #region Sneaking
  91. private bool _sneakingActive;
  92. public bool SneakingActive
  93. {
  94. get { return _sneakingActive; }
  95. set
  96. {
  97. if (_sneakingActive == value) return;
  98. _sneakingActive = value;
  99. Observer = _sneakingActive;
  100. }
  101. }
  102. private bool _sneaking;
  103. public bool Sneaking
  104. {
  105. get { return _sneaking; }
  106. set { _sneaking = value; SneakingActive = value; }
  107. }
  108. #endregion
  109. public MapObject _target;
  110. public virtual MapObject Target
  111. {
  112. get { return _target; }
  113. set
  114. {
  115. if (_target == value) return;
  116. _target = value;
  117. }
  118. }
  119. protected MapObject master;
  120. public virtual MapObject Master
  121. {
  122. get { return master; }
  123. set { master = value; }
  124. }
  125. public MapObject LastHitter, EXPOwner, Owner;
  126. public long ExpireTime, OwnerTime, OperateTime;
  127. public int OperateDelay = 100;
  128. public Stats Stats;
  129. public List<MonsterObject> Pets = new List<MonsterObject>();
  130. public virtual List<Buff> Buffs { get; set; } = new List<Buff>();
  131. public List<PlayerObject> GroupMembers;
  132. public virtual AttackMode AMode { get; set; }
  133. public virtual PetMode PMode { get; set; }
  134. private bool _inSafeZone;
  135. public bool InSafeZone {
  136. get { return _inSafeZone; }
  137. set
  138. {
  139. if (_inSafeZone == value) return;
  140. _inSafeZone = value;
  141. OnSafeZoneChanged();
  142. }
  143. }
  144. public float ArmourRate, DamageRate; //recieved not given
  145. public virtual List<Poison> PoisonList { get; set; } = new List<Poison>();
  146. public PoisonType CurrentPoison = PoisonType.None;
  147. public List<DelayedAction> ActionList = new List<DelayedAction>();
  148. public LinkedListNode<MapObject> Node;
  149. public LinkedListNode<MapObject> NodeThreaded;
  150. public long RevTime;
  151. public virtual bool Blocking
  152. {
  153. get { return true; }
  154. }
  155. public Point Front
  156. {
  157. get { return Functions.PointMove(CurrentLocation, Direction, 1); }
  158. }
  159. public Point Back
  160. {
  161. get { return Functions.PointMove(CurrentLocation, Direction, -1); }
  162. }
  163. public virtual void Process()
  164. {
  165. if (Master != null && Master.Node == null) Master = null;
  166. if (LastHitter != null && LastHitter.Node == null) LastHitter = null;
  167. if (EXPOwner != null && EXPOwner.Node == null) EXPOwner = null;
  168. if (Target != null && (Target.Node == null || Target.Dead)) Target = null;
  169. if (Owner != null && Owner.Node == null) Owner = null;
  170. if (PKPoints > 0 && Envir.Time > PKPointTime)
  171. {
  172. PKPointTime = Envir.Time + Settings.PKDelay * Settings.Second;
  173. PKPoints--;
  174. }
  175. if (LastHitter != null && Envir.Time > LastHitTime)
  176. {
  177. LastHitter = null;
  178. }
  179. if (EXPOwner != null && Envir.Time > EXPOwnerTime)
  180. {
  181. EXPOwner = null;
  182. }
  183. for (int i = 0; i < ActionList.Count; i++)
  184. {
  185. if (Envir.Time < ActionList[i].Time) continue;
  186. Process(ActionList[i]);
  187. ActionList.RemoveAt(i);
  188. }
  189. }
  190. public virtual void OnSafeZoneChanged()
  191. {
  192. }
  193. public abstract void SetOperateTime();
  194. public int GetAttackPower(int min, int max)
  195. {
  196. if (min < 0) min = 0;
  197. if (min > max) max = min;
  198. if (Stats[Stat.Luck] > 0)
  199. {
  200. if (Stats[Stat.Luck] > Envir.Random.Next(Settings.MaxLuck))
  201. return max;
  202. }
  203. else if (Stats[Stat.Luck] < 0)
  204. {
  205. if (Stats[Stat.Luck] < -Envir.Random.Next(Settings.MaxLuck))
  206. return min;
  207. }
  208. return Envir.Random.Next(min, max + 1);
  209. }
  210. public int GetRangeAttackPower(int min, int max, int range)
  211. {
  212. //maxRange = highest possible damage
  213. //minRange = lowest possible damage
  214. decimal x = ((decimal)min / (Globals.MaxAttackRange)) * (Globals.MaxAttackRange - range);
  215. min -= (int)Math.Floor(x);
  216. return GetAttackPower(min, max);
  217. }
  218. public int GetDefencePower(int min, int max)
  219. {
  220. if (min < 0) min = 0;
  221. if (min > max) max = min;
  222. return Envir.Random.Next(min, max + 1);
  223. }
  224. public virtual void Remove(HumanObject player)
  225. {
  226. player.Enqueue(new S.ObjectRemove { ObjectID = ObjectID });
  227. }
  228. public virtual void Add(HumanObject player)
  229. {
  230. if (player.Race != ObjectType.Player) return;
  231. if (Race == ObjectType.Merchant)
  232. {
  233. NPCObject npc = (NPCObject)this;
  234. npc.CheckVisible((PlayerObject)player, true);
  235. return;
  236. }
  237. player.Enqueue(GetInfo());
  238. //if (Race == ObjectType.Player)
  239. //{
  240. // PlayerObject me = (PlayerObject)this;
  241. // player.Enqueue(me.GetInfoEx(player));
  242. //}
  243. //else
  244. //{
  245. // player.Enqueue(GetInfo());
  246. //}
  247. }
  248. public virtual void Remove(MonsterObject monster)
  249. {
  250. }
  251. public virtual void Add(MonsterObject monster)
  252. {
  253. }
  254. public abstract void Process(DelayedAction action);
  255. public bool CanFly(Point target)
  256. {
  257. Point location = CurrentLocation;
  258. while (location != target)
  259. {
  260. MirDirection dir = Functions.DirectionFromPoint(location, target);
  261. location = Functions.PointMove(location, dir, 1);
  262. if (location.X < 0 || location.Y < 0 || location.X >= CurrentMap.Width || location.Y >= CurrentMap.Height) return false;
  263. if (!CurrentMap.GetCell(location).Valid) return false;
  264. }
  265. return true;
  266. }
  267. public virtual void Spawned()
  268. {
  269. if (Node != null)
  270. throw new InvalidOperationException("Node is not null, Object already spawned");
  271. Node = Envir.Objects.AddLast(this);
  272. if ((Race == ObjectType.Monster) && Settings.Multithreaded)
  273. {
  274. SpawnThread = CurrentMap.Thread;
  275. NodeThreaded = Envir.MobThreads[SpawnThread].ObjectsList.AddLast(this);
  276. }
  277. OperateTime = Envir.Time + Envir.Random.Next(OperateDelay);
  278. BroadcastInfo();
  279. BroadcastHealthChange();
  280. InSafeZone = CurrentMap != null && CurrentMap.GetSafeZone(CurrentLocation) != null;
  281. }
  282. public virtual void Despawn()
  283. {
  284. if (Node == null)
  285. throw new InvalidOperationException("Node is null, Object already Despawned");
  286. Broadcast(new S.ObjectRemove { ObjectID = ObjectID });
  287. Envir.Objects.Remove(Node);
  288. if (Settings.Multithreaded && (Race == ObjectType.Monster))
  289. {
  290. Envir.MobThreads[SpawnThread].ObjectsList.Remove(NodeThreaded);
  291. }
  292. ActionList.Clear();
  293. for (int i = Pets.Count - 1; i >= 0; i--)
  294. Pets[i].Die();
  295. Node = null;
  296. }
  297. public MapObject FindObject(uint targetID, int dist)
  298. {
  299. for (int d = 0; d <= dist; d++)
  300. {
  301. for (int y = CurrentLocation.Y - d; y <= CurrentLocation.Y + d; y++)
  302. {
  303. if (y < 0) continue;
  304. if (y >= CurrentMap.Height) break;
  305. for (int x = CurrentLocation.X - d; x <= CurrentLocation.X + d; x += Math.Abs(y - CurrentLocation.Y) == d ? 1 : d * 2)
  306. {
  307. if (x < 0) continue;
  308. if (x >= CurrentMap.Width) break;
  309. Cell cell = CurrentMap.GetCell(x, y);
  310. if (!cell.Valid || cell.Objects == null) continue;
  311. for (int i = 0; i < cell.Objects.Count; i++)
  312. {
  313. MapObject ob = cell.Objects[i];
  314. if (ob.ObjectID != targetID) continue;
  315. return ob;
  316. }
  317. }
  318. }
  319. }
  320. return null;
  321. }
  322. public virtual void Broadcast(Packet p)
  323. {
  324. if (p == null || CurrentMap == null) return;
  325. for (int i = CurrentMap.Players.Count - 1; i >= 0; i--)
  326. {
  327. PlayerObject player = CurrentMap.Players[i];
  328. if (player == this) continue;
  329. if (Functions.InRange(CurrentLocation, player.CurrentLocation, Globals.DataRange))
  330. player.Enqueue(p);
  331. }
  332. }
  333. public virtual void BroadcastInfo()
  334. {
  335. Broadcast(GetInfo());
  336. return;
  337. }
  338. public bool IsAttackTarget(MapObject attacker)
  339. {
  340. if (attacker == null || attacker.Node == null) return false;
  341. if (Dead || attacker == this) return false;
  342. var flag = true;
  343. if (Race == ObjectType.Monster)
  344. {
  345. // Check if we are a training AI - we can be attacked in safezones
  346. if (((MonsterObject)this).Info.AI == 56)
  347. flag = false;
  348. }
  349. if (flag && (InSafeZone || attacker.InSafeZone)) return false;
  350. switch (attacker.Race)
  351. {
  352. case ObjectType.Player:
  353. return IsAttackTarget((PlayerObject)attacker);
  354. case ObjectType.Hero:
  355. return IsAttackTarget((HeroObject)attacker);
  356. case ObjectType.Monster:
  357. return IsAttackTarget((MonsterObject)attacker);
  358. default:
  359. throw new NotSupportedException();
  360. }
  361. }
  362. public abstract bool IsAttackTarget(HumanObject attacker);
  363. public abstract bool IsAttackTarget(MonsterObject attacker);
  364. public abstract int Attacked(HumanObject attacker, int damage, DefenceType type = DefenceType.ACAgility, bool damageWeapon = true);
  365. public abstract int Attacked(MonsterObject attacker, int damage, DefenceType type = DefenceType.ACAgility);
  366. public virtual int GetArmour(DefenceType type, MapObject attacker, out bool hit)
  367. {
  368. var armour = 0;
  369. hit = true;
  370. switch (type)
  371. {
  372. case DefenceType.ACAgility:
  373. if (Envir.Random.Next(Stats[Stat.Agility] + 1) > attacker.Stats[Stat.Accuracy])
  374. {
  375. BroadcastDamageIndicator(DamageType.Miss);
  376. hit = false;
  377. }
  378. armour = GetDefencePower(Stats[Stat.MinAC], Stats[Stat.MaxAC]);
  379. break;
  380. case DefenceType.AC:
  381. armour = GetDefencePower(Stats[Stat.MinAC], Stats[Stat.MaxAC]);
  382. break;
  383. case DefenceType.MACAgility:
  384. if (Envir.Random.Next(Settings.MagicResistWeight) < Stats[Stat.MagicResist])
  385. {
  386. BroadcastDamageIndicator(DamageType.Miss);
  387. hit = false;
  388. }
  389. if (Envir.Random.Next(Stats[Stat.Agility] + 1) > attacker.Stats[Stat.Accuracy])
  390. {
  391. BroadcastDamageIndicator(DamageType.Miss);
  392. hit = false;
  393. }
  394. armour = GetDefencePower(Stats[Stat.MinMAC], Stats[Stat.MaxMAC]);
  395. break;
  396. case DefenceType.MAC:
  397. if (Envir.Random.Next(Settings.MagicResistWeight) < Stats[Stat.MagicResist])
  398. {
  399. BroadcastDamageIndicator(DamageType.Miss);
  400. hit = false;
  401. }
  402. armour = GetDefencePower(Stats[Stat.MinMAC], Stats[Stat.MaxMAC]);
  403. break;
  404. case DefenceType.Agility:
  405. if (Envir.Random.Next(Stats[Stat.Agility] + 1) > attacker.Stats[Stat.Accuracy])
  406. {
  407. BroadcastDamageIndicator(DamageType.Miss);
  408. hit = false;
  409. }
  410. break;
  411. }
  412. return armour;
  413. }
  414. public virtual void ApplyNegativeEffects(HumanObject attacker, DefenceType type, ushort levelOffset)
  415. {
  416. if (attacker.SpecialMode.HasFlag(SpecialItemMode.Paralize) && type != DefenceType.MAC && type != DefenceType.MACAgility && 1 == Envir.Random.Next(1, 15))
  417. {
  418. ApplyPoison(new Poison { PType = PoisonType.Paralysis, Duration = 5, TickSpeed = 1000 }, attacker);
  419. }
  420. if ((attacker.Stats[Stat.Freezing] > 0) && (Settings.PvpCanFreeze || Race != ObjectType.Player) && type != DefenceType.MAC && type != DefenceType.MACAgility)
  421. {
  422. if ((Envir.Random.Next(Settings.FreezingAttackWeight) < attacker.Stats[Stat.Freezing]) && (Envir.Random.Next(levelOffset) == 0))
  423. ApplyPoison(new Poison { PType = PoisonType.Slow, Duration = Math.Min(10, (3 + Envir.Random.Next(attacker.Stats[Stat.Freezing]))), TickSpeed = 1000 }, attacker);
  424. }
  425. if (attacker.Stats[Stat.PoisonAttack] > 0 && type != DefenceType.MAC && type != DefenceType.MACAgility)
  426. {
  427. if ((Envir.Random.Next(Settings.PoisonAttackWeight) < attacker.Stats[Stat.PoisonAttack]) && (Envir.Random.Next(levelOffset) == 0))
  428. ApplyPoison(new Poison { PType = PoisonType.Green, Duration = 5, TickSpeed = 1000, Value = Math.Min(10, 3 + Envir.Random.Next(attacker.Stats[Stat.PoisonAttack])) }, attacker);
  429. }
  430. }
  431. public abstract int Struck(int damage, DefenceType type = DefenceType.ACAgility);
  432. public bool IsFriendlyTarget(MapObject ally)
  433. {
  434. switch (ally.Race)
  435. {
  436. case ObjectType.Player:
  437. return IsFriendlyTarget((PlayerObject)ally);
  438. case ObjectType.Hero:
  439. return IsFriendlyTarget((HeroObject)ally);
  440. case ObjectType.Monster:
  441. return IsFriendlyTarget((MonsterObject)ally);
  442. default:
  443. throw new NotSupportedException();
  444. }
  445. }
  446. public abstract bool IsFriendlyTarget(HumanObject ally);
  447. public abstract bool IsFriendlyTarget(MonsterObject ally);
  448. public abstract void ReceiveChat(string text, ChatType type);
  449. public abstract Packet GetInfo();
  450. public virtual void WinExp(uint amount, uint targetLevel = 0)
  451. {
  452. }
  453. public virtual bool CanGainGold(uint gold)
  454. {
  455. return false;
  456. }
  457. public virtual void WinGold(uint gold)
  458. {
  459. }
  460. public virtual bool Harvest(PlayerObject player) { return false; }
  461. public abstract void ApplyPoison(Poison p, MapObject Caster = null, bool NoResist = false, bool ignoreDefence = true);
  462. public virtual Buff AddBuff(BuffType type, MapObject owner, int duration, Stats stats, bool refreshStats = true, bool updateOnly = false, params int[] values)
  463. {
  464. if (!HasBuff(type, out Buff buff))
  465. {
  466. buff = new Buff(type)
  467. {
  468. Caster = owner,
  469. ObjectID = ObjectID,
  470. ExpireTime = duration,
  471. LastTime = Envir.Time,
  472. Stats = stats
  473. };
  474. Buffs.Add(buff);
  475. }
  476. else
  477. {
  478. if (!updateOnly)
  479. {
  480. switch (buff.StackType)
  481. {
  482. case BuffStackType.ResetDuration:
  483. {
  484. buff.ExpireTime = duration;
  485. }
  486. break;
  487. case BuffStackType.StackDuration:
  488. {
  489. buff.ExpireTime += duration;
  490. }
  491. break;
  492. case BuffStackType.StackStat:
  493. {
  494. if (stats != null)
  495. {
  496. buff.Stats.Add(stats);
  497. }
  498. }
  499. break;
  500. case BuffStackType.StackStatAndDuration:
  501. {
  502. if (stats != null)
  503. {
  504. buff.Stats.Add(stats);
  505. }
  506. buff.ExpireTime += duration;
  507. }
  508. break;
  509. case BuffStackType.ResetStat:
  510. {
  511. if (stats != null)
  512. {
  513. buff.Stats = stats;
  514. }
  515. }
  516. break;
  517. case BuffStackType.ResetStatAndDuration:
  518. {
  519. buff.ExpireTime = duration;
  520. if (stats != null)
  521. {
  522. buff.Stats = stats;
  523. }
  524. }
  525. break;
  526. case BuffStackType.Infinite:
  527. case BuffStackType.None:
  528. break;
  529. }
  530. }
  531. }
  532. if (buff.Properties.HasFlag(BuffProperty.PauseInSafeZone) && InSafeZone)
  533. {
  534. buff.Paused = true;
  535. }
  536. buff.Stats ??= new Stats();
  537. buff.Values = values ?? new int[0];
  538. if (buff.Caster?.Node == null)
  539. buff.Caster = owner;
  540. switch (buff.Type)
  541. {
  542. case BuffType.MoonLight:
  543. case BuffType.DarkBody:
  544. Hidden = true;
  545. Sneaking = true;
  546. HideFromTargets();
  547. break;
  548. case BuffType.Hiding:
  549. case BuffType.ClearRing:
  550. Hidden = true;
  551. HideFromTargets();
  552. break;
  553. }
  554. return buff;
  555. }
  556. public virtual void RemoveBuff(BuffType b)
  557. {
  558. for (int i = 0; i < Buffs.Count; i++)
  559. {
  560. if (Buffs[i].Type != b) continue;
  561. Buffs[i].FlagForRemoval = true;
  562. Buffs[i].Paused = false;
  563. Buffs[i].ExpireTime = 0;
  564. switch(b)
  565. {
  566. case BuffType.Hiding:
  567. case BuffType.MoonLight:
  568. case BuffType.DarkBody:
  569. if (!HasAnyBuffs(b, BuffType.ClearRing, BuffType.Hiding, BuffType.MoonLight, BuffType.DarkBody))
  570. {
  571. Hidden = false;
  572. }
  573. break;
  574. }
  575. }
  576. }
  577. public bool HasBuff(BuffType type)
  578. {
  579. for (int i = 0; i < Buffs.Count; i++)
  580. {
  581. if (Buffs[i].Type != type) continue;
  582. return true;
  583. }
  584. return false;
  585. }
  586. public bool HasBuff(BuffType type, out Buff buff)
  587. {
  588. for (int i = 0; i < Buffs.Count; i++)
  589. {
  590. if (Buffs[i].Type != type) continue;
  591. buff = Buffs[i];
  592. return true;
  593. }
  594. buff = null;
  595. return false;
  596. }
  597. public bool HasAnyBuffs(BuffType exceptBuff, params BuffType[] types)
  598. {
  599. return Buffs.Select(x => x.Type).Except(new List<BuffType> { exceptBuff }).Intersect(types).Any();
  600. }
  601. public virtual void PauseBuff(Buff b)
  602. {
  603. if (b.Paused) return;
  604. b.Paused = true;
  605. }
  606. public virtual void UnpauseBuff(Buff b)
  607. {
  608. if (!b.Paused) return;
  609. b.Paused = false;
  610. }
  611. protected void HideFromTargets()
  612. {
  613. for (int y = CurrentLocation.Y - Globals.DataRange; y <= CurrentLocation.Y + Globals.DataRange; y++)
  614. {
  615. if (y < 0) continue;
  616. if (y >= CurrentMap.Height) break;
  617. for (int x = CurrentLocation.X - Globals.DataRange; x <= CurrentLocation.X + Globals.DataRange; x++)
  618. {
  619. if (x < 0) continue;
  620. if (x >= CurrentMap.Width) break;
  621. if (x < 0 || x >= CurrentMap.Width) continue;
  622. Cell cell = CurrentMap.GetCell(x, y);
  623. if (!cell.Valid || cell.Objects == null) continue;
  624. for (int i = 0; i < cell.Objects.Count; i++)
  625. {
  626. MapObject ob = cell.Objects[i];
  627. if (ob.Race != ObjectType.Monster) continue;
  628. if (ob.Target == this && (!ob.CoolEye || ob.Level < Level)) ob.Target = null;
  629. }
  630. }
  631. }
  632. }
  633. public bool CheckStacked()
  634. {
  635. Cell cell = CurrentMap.GetCell(CurrentLocation);
  636. if (cell.Objects != null)
  637. for (int i = 0; i < cell.Objects.Count; i++)
  638. {
  639. MapObject ob = cell.Objects[i];
  640. if (ob == this || !ob.Blocking) continue;
  641. return true;
  642. }
  643. return false;
  644. }
  645. public virtual bool Teleport(Map temp, Point location, bool effects = true, byte effectnumber = 0)
  646. {
  647. if (temp == null || !temp.ValidPoint(location)) return false;
  648. CurrentMap.RemoveObject(this);
  649. if (effects) Broadcast(new S.ObjectTeleportOut {ObjectID = ObjectID, Type = effectnumber});
  650. Broadcast(new S.ObjectRemove {ObjectID = ObjectID});
  651. CurrentMap = temp;
  652. CurrentLocation = location;
  653. InTrapRock = false;
  654. CurrentMap.AddObject(this);
  655. BroadcastInfo();
  656. if (effects) Broadcast(new S.ObjectTeleportIn { ObjectID = ObjectID, Type = effectnumber });
  657. BroadcastHealthChange();
  658. return true;
  659. }
  660. public virtual bool TeleportRandom(int attempts, int distance, Map map = null)
  661. {
  662. if (map == null) map = CurrentMap;
  663. if (map.Cells == null) return false;
  664. if (map.WalkableCells.Count == 0) return false;
  665. int cellIndex = Envir.Random.Next(map.WalkableCells.Count);
  666. return Teleport(map, map.WalkableCells[cellIndex]);
  667. }
  668. public Point GetRandomPoint(int attempts, int distance, Map map)
  669. {
  670. byte edgeoffset = 0;
  671. if (map.Width < 150)
  672. {
  673. if (map.Height < 30) edgeoffset = 2;
  674. else edgeoffset = 20;
  675. }
  676. for (int i = 0; i < attempts; i++)
  677. {
  678. Point location;
  679. if (distance <= 0)
  680. location = new Point(edgeoffset + Envir.Random.Next(map.Width - edgeoffset), edgeoffset + Envir.Random.Next(map.Height - edgeoffset)); //Can adjust Random Range...
  681. else
  682. location = new Point(CurrentLocation.X + Envir.Random.Next(-distance, distance + 1),
  683. CurrentLocation.Y + Envir.Random.Next(-distance, distance + 1));
  684. if (map.ValidPoint(location)) return location;
  685. }
  686. return new Point(0, 0);
  687. }
  688. public virtual void BroadcastHealthChange()
  689. {
  690. if (Race != ObjectType.Player && Race != ObjectType.Monster) return;
  691. byte time = Math.Min(byte.MaxValue, (byte)Math.Max(5, (RevTime - Envir.Time) / 1000));
  692. Packet p = new S.ObjectHealth { ObjectID = ObjectID, Percent = PercentHealth, Expire = time };
  693. if (Envir.Time < RevTime)
  694. {
  695. CurrentMap.Broadcast(p, CurrentLocation);
  696. return;
  697. }
  698. if (Race == ObjectType.Monster && !AutoRev && Master == null) return;
  699. if (Race == ObjectType.Player)
  700. {
  701. if (GroupMembers != null) //Send HP to group
  702. {
  703. for (int i = 0; i < GroupMembers.Count; i++)
  704. {
  705. PlayerObject member = GroupMembers[i];
  706. if (this == member) continue;
  707. if (member.CurrentMap != CurrentMap || !Functions.InRange(member.CurrentLocation, CurrentLocation, Globals.DataRange)) continue;
  708. member.Enqueue(p);
  709. }
  710. }
  711. return;
  712. }
  713. if (Master != null && Master.Race == ObjectType.Player)
  714. {
  715. PlayerObject player = (PlayerObject)Master;
  716. player.Enqueue(p);
  717. if (player.GroupMembers != null) //Send pet HP to group
  718. {
  719. for (int i = 0; i < player.GroupMembers.Count; i++)
  720. {
  721. PlayerObject member = player.GroupMembers[i];
  722. if (player == member) continue;
  723. if (member.CurrentMap != CurrentMap || !Functions.InRange(member.CurrentLocation, CurrentLocation, Globals.DataRange)) continue;
  724. member.Enqueue(p);
  725. }
  726. }
  727. }
  728. if (EXPOwner != null && EXPOwner.Race == ObjectType.Player)
  729. {
  730. PlayerObject player = (PlayerObject)EXPOwner;
  731. if (player.IsMember(Master)) return;
  732. player.Enqueue(p);
  733. if (player.GroupMembers != null)
  734. {
  735. for (int i = 0; i < player.GroupMembers.Count; i++)
  736. {
  737. PlayerObject member = player.GroupMembers[i];
  738. if (player == member) continue;
  739. if (member.CurrentMap != CurrentMap || !Functions.InRange(member.CurrentLocation, CurrentLocation, Globals.DataRange)) continue;
  740. member.Enqueue(p);
  741. }
  742. }
  743. }
  744. }
  745. public void BroadcastDamageIndicator(DamageType type, int damage = 0)
  746. {
  747. Packet p = new S.DamageIndicator { ObjectID = ObjectID, Damage = damage, Type = type };
  748. if (Race == ObjectType.Player)
  749. {
  750. PlayerObject player = (PlayerObject)this;
  751. player.Enqueue(p);
  752. }
  753. Broadcast(p);
  754. }
  755. public abstract void Die();
  756. public abstract int Pushed(MapObject pusher, MirDirection dir, int distance);
  757. public bool IsMember(MapObject member)
  758. {
  759. if (member == this) return true;
  760. if (GroupMembers == null || member == null) return false;
  761. for (int i = 0; i < GroupMembers.Count; i++)
  762. if (GroupMembers[i] == member) return true;
  763. return false;
  764. }
  765. public abstract void SendHealth(HumanObject player);
  766. public bool InTrapRock
  767. {
  768. set
  769. {
  770. if (this is PlayerObject)
  771. {
  772. var player = (PlayerObject)this;
  773. player.Enqueue(new S.InTrapRock { Trapped = value });
  774. }
  775. }
  776. get
  777. {
  778. Point checklocation;
  779. for (int i = 0; i <= 6; i += 2)
  780. {
  781. checklocation = Functions.PointMove(CurrentLocation, (MirDirection)i, 1);
  782. if (checklocation.X < 0) continue;
  783. if (checklocation.X >= CurrentMap.Width) continue;
  784. if (checklocation.Y < 0) continue;
  785. if (checklocation.Y >= CurrentMap.Height) continue;
  786. Cell cell = CurrentMap.GetCell(checklocation.X, checklocation.Y);
  787. if (!cell.Valid || cell.Objects == null) continue;
  788. for (int j = 0; j < cell.Objects.Count; j++)
  789. {
  790. MapObject ob = cell.Objects[j];
  791. switch (ob.Race)
  792. {
  793. case ObjectType.Monster:
  794. if (ob is TrapRock)
  795. {
  796. TrapRock rock = (TrapRock)ob;
  797. if (rock.Dead) continue;
  798. if (rock.Target != this) continue;
  799. if (!rock.Visible) continue;
  800. }
  801. else continue;
  802. return true;
  803. default:
  804. continue;
  805. }
  806. }
  807. }
  808. return false;
  809. }
  810. }
  811. }
  812. public class Poison
  813. {
  814. private MapObject owner;
  815. public MapObject Owner
  816. {
  817. get
  818. {
  819. return owner switch
  820. {
  821. HeroObject hero => hero.Owner,
  822. _ => owner
  823. };
  824. }
  825. set { owner = value; }
  826. }
  827. public PoisonType PType;
  828. public int Value;
  829. public long Duration, Time, TickTime, TickSpeed;
  830. public Poison() { }
  831. public Poison(BinaryReader reader)
  832. {
  833. Owner = null;
  834. PType = (PoisonType)reader.ReadByte();
  835. Value = reader.ReadInt32();
  836. Duration = reader.ReadInt64();
  837. Time = reader.ReadInt64();
  838. TickTime = reader.ReadInt64();
  839. TickSpeed = reader.ReadInt64();
  840. }
  841. }
  842. }