SpellObject.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  1. using System.Drawing;
  2. using Server.MirEnvir;
  3. using S = ServerPackets;
  4. namespace Server.MirObjects
  5. {
  6. public class SpellObject : MapObject
  7. {
  8. public override ObjectType Race
  9. {
  10. get { return ObjectType.Spell; }
  11. }
  12. public override string Name { get; set; }
  13. public override int CurrentMapIndex { get; set; }
  14. public override Point CurrentLocation { get; set; }
  15. public override MirDirection Direction { get; set; }
  16. public override ushort Level { get; set; }
  17. public override bool Blocking
  18. {
  19. get
  20. {
  21. return false;
  22. }
  23. }
  24. public long TickTime, StartTime;
  25. public MapObject Caster;
  26. public int Value, TickSpeed, BonusDmg;
  27. public Spell Spell;
  28. public Point CastLocation;
  29. public bool Show, Decoration;
  30. //ExplosiveTrap
  31. public int ExplosiveTrapID;
  32. public int ExplosiveTrapCount;
  33. public bool DetonatedTrap;
  34. public override int Health
  35. {
  36. get { throw new NotSupportedException(); }
  37. }
  38. public override int MaxHealth
  39. {
  40. get { throw new NotSupportedException(); }
  41. }
  42. public int CastInstanceId { get; set; }
  43. public override void Process()
  44. {
  45. if (Decoration) return;
  46. if (Caster != null && Caster.Node == null) Caster = null;
  47. if (Envir.Time > ExpireTime || ((Spell == Spell.FireWall || Spell == Spell.Portal || Spell == Spell.ExplosiveTrap || Spell == Spell.Reincarnation || Spell == Spell.HealingCircle) && Caster == null) || (Spell == Spell.TrapHexagon && Target != null) || (Spell == Spell.Trap && Target != null))
  48. {
  49. if (Spell == Spell.TrapHexagon && Target != null || Spell == Spell.Trap && Target != null)
  50. {
  51. MonsterObject ob = (MonsterObject)Target;
  52. if (Envir.Time < ExpireTime && ob.ShockTime != 0) return;
  53. }
  54. if (Spell == Spell.Reincarnation && Caster != null)
  55. {
  56. ((HumanObject)Caster).ReincarnationReady = true;
  57. ((HumanObject)Caster).ReincarnationExpireTime = Envir.Time + 6000;
  58. }
  59. if ((Spell == Spell.Blizzard || Spell == Spell.MeteorStrike) && Caster != null)
  60. {
  61. ((HumanObject)Caster).ActiveBlizzard = false;
  62. }
  63. CurrentMap.RemoveObject(this);
  64. Despawn();
  65. return;
  66. }
  67. if (Spell == Spell.FireWall)
  68. {
  69. if (CurrentMap != Caster?.CurrentMap)
  70. {
  71. CurrentMap.RemoveObject(this);
  72. Despawn();
  73. return;
  74. }
  75. }
  76. if (Spell == Spell.Reincarnation && !((HumanObject)Caster).ActiveReincarnation)
  77. {
  78. CurrentMap.RemoveObject(this);
  79. Despawn();
  80. return;
  81. }
  82. if (Spell == Spell.ExplosiveTrap && Caster != null && !Functions.InRange(CurrentLocation, Caster.CurrentLocation, 15))
  83. {
  84. CurrentMap.RemoveObject(this);
  85. Despawn();
  86. return;
  87. }
  88. if (Caster != null && Spell == Spell.HealingCircle && FindObject(Caster.ObjectID, 20) == null)
  89. {
  90. CurrentMap.RemoveObject(this);
  91. Despawn();
  92. return;
  93. }
  94. if (Envir.Time < TickTime) return;
  95. TickTime = Envir.Time + TickSpeed;
  96. Cell cell = CurrentMap.GetCell(CurrentLocation);
  97. for (int i = 0; i < cell.Objects.Count; i++)
  98. if (cell != null)
  99. {
  100. ProcessSpell(cell.Objects[i]);
  101. }
  102. if ((Spell == Spell.MapLava) || (Spell == Spell.MapLightning)) Value = 0;
  103. }
  104. public void ProcessSpell(MapObject ob)
  105. {
  106. if (Envir.Time < StartTime) return;
  107. switch (Spell)
  108. {
  109. case Spell.FireWall:
  110. {
  111. if (ob.Race != ObjectType.Player && ob.Race != ObjectType.Monster) return;
  112. if (ob.Dead) return;
  113. if (!ob.IsAttackTarget(Caster)) return;
  114. ob.Attacked(((HumanObject)Caster), Value, DefenceType.MAC, false);
  115. }
  116. break;
  117. case Spell.Healing: //SafeZone
  118. {
  119. if (ob.Master == null) return;
  120. if (ob.Dead || ob.HealAmount != 0 || ob.PercentHealth == 100) return;
  121. if (ob.Race == ObjectType.Player || ob.Race == ObjectType.Hero || (ob.Race == ObjectType.Monster && ob.Master.Race == ObjectType.Player))
  122. {
  123. ob.HealAmount += 25;
  124. Broadcast(new S.ObjectEffect { ObjectID = ob.ObjectID, Effect = SpellEffect.Healing });
  125. }
  126. }
  127. break;
  128. case Spell.PoisonCloud:
  129. {
  130. if (ob.Race != ObjectType.Player && ob.Race != ObjectType.Monster) return;
  131. if (ob.Dead) return;
  132. if (!ob.IsAttackTarget(Caster)) return;
  133. ob.Attacked(((HumanObject)Caster), Value, DefenceType.MAC, false);
  134. if (!ob.Dead)
  135. ob.ApplyPoison(new Poison
  136. {
  137. Duration = 12,
  138. Owner = Caster,
  139. PType = PoisonType.Green,
  140. TickSpeed = TickSpeed,
  141. Value = (Caster.Stats[Stat.MinSC] + Caster.Stats[Stat.MaxSC]) / 2 + BonusDmg
  142. }, Caster, false, false);
  143. }
  144. break;
  145. case Spell.Blizzard:
  146. {
  147. if (ob.Race != ObjectType.Player && ob.Race != ObjectType.Monster) return;
  148. if (ob.Dead) return;
  149. if (Caster != null && ((HumanObject)Caster).ActiveBlizzard == false) return;
  150. if (!ob.IsAttackTarget(Caster)) return;
  151. ob.Attacked(((HumanObject)Caster), Value, DefenceType.MAC, false);
  152. if (!ob.Dead && Envir.Random.Next(8) == 0)
  153. ob.ApplyPoison(new Poison
  154. {
  155. Duration = 5 + Envir.Random.Next(Caster.Stats[Stat.Freezing]),
  156. Owner = Caster,
  157. PType = PoisonType.Slow,
  158. TickSpeed = 2000,
  159. }, Caster);
  160. }
  161. break;
  162. case Spell.MeteorStrike:
  163. {
  164. if (ob.Race != ObjectType.Player && ob.Race != ObjectType.Monster) return;
  165. if (ob.Dead) return;
  166. if (Caster != null && ((HumanObject)Caster).ActiveBlizzard == false) return;
  167. if (!ob.IsAttackTarget(Caster)) return;
  168. ob.Attacked(((HumanObject)Caster), Value, DefenceType.MAC, false);
  169. }
  170. break;
  171. case Spell.ExplosiveTrap:
  172. {
  173. if (ob.Race != ObjectType.Player && ob.Race != ObjectType.Monster) return;
  174. if (ob.Dead) return;
  175. if (!ob.IsAttackTarget(Caster)) return;
  176. if (DetonatedTrap) return;
  177. DetonateTrapNow();
  178. ob.Attacked(((HumanObject)Caster), Value, DefenceType.MAC, false);
  179. }
  180. break;
  181. case Spell.MapLava:
  182. case Spell.MapLightning:
  183. {
  184. if (ob is PlayerObject player)
  185. {
  186. if (player.Account.AdminAccount && player.Observer) return;
  187. player.Struck(Value, DefenceType.MAC);
  188. }
  189. }
  190. break;
  191. case Spell.MapQuake1:
  192. case Spell.MapQuake2:
  193. {
  194. if (Value == 0) return;
  195. if (ob.Race != ObjectType.Player && ob.Race != ObjectType.Monster) return;
  196. if (ob.Dead) return;
  197. ob.Struck(Value, DefenceType.MAC);
  198. }
  199. break;
  200. case Spell.GeneralMeowMeowThunder:
  201. {
  202. if (ob.Race != ObjectType.Player && ob.Race != ObjectType.Monster) return;
  203. if (ob.Dead) return;
  204. if (ob == Caster) return;
  205. if (!ob.IsAttackTarget(Caster)) return;
  206. if (Value == 0) return;
  207. ob.Struck(Value, DefenceType.MAC);
  208. }
  209. break;
  210. case Spell.TreeQueenRoot:
  211. {
  212. if (ob.Race != ObjectType.Player && ob.Race != ObjectType.Monster) return;
  213. if (ob.Dead) return;
  214. if (ob == Caster) return;
  215. if (!ob.IsAttackTarget(Caster)) return;
  216. if (Value == 0) return;
  217. ob.Struck(Value, DefenceType.MAC);
  218. }
  219. break;
  220. case Spell.TreeQueenMassRoots:
  221. {
  222. if (ob.Race != ObjectType.Player && ob.Race != ObjectType.Monster) return;
  223. if (ob.Dead) return;
  224. if (ob == Caster) return;
  225. if (!ob.IsAttackTarget(Caster)) return;
  226. if (Value == 0) return;
  227. ob.Struck(Value, DefenceType.MAC);
  228. }
  229. break;
  230. case Spell.TreeQueenGroundRoots:
  231. {
  232. if (ob.Race != ObjectType.Player && ob.Race != ObjectType.Monster) return;
  233. if (ob.Dead) return;
  234. if (ob == Caster) return;
  235. if (!ob.IsAttackTarget(Caster)) return;
  236. ob.Struck(Value, DefenceType.MAC);
  237. if (Envir.Random.Next(3) > 0)
  238. {
  239. ob.ApplyPoison(new Poison { PType = PoisonType.Paralysis, Duration = 5, TickSpeed = 1000 }, this);
  240. }
  241. }
  242. break;
  243. case Spell.StoneGolemQuake:
  244. {
  245. if (ob.Race != ObjectType.Player && ob.Race != ObjectType.Monster) return;
  246. if (ob.Dead) return;
  247. if (ob == Caster) return;
  248. if (!ob.IsAttackTarget(Caster)) return;
  249. if (Value == 0) return;
  250. ob.Struck(Value, DefenceType.AC);
  251. }
  252. break;
  253. case Spell.EarthGolemPile:
  254. {
  255. if (ob.Race != ObjectType.Player && ob.Race != ObjectType.Monster) return;
  256. if (ob.Dead) return;
  257. if (ob == Caster) return;
  258. if (!ob.IsAttackTarget(Caster)) return;
  259. if (Value == 0) return;
  260. ob.Struck(Value, DefenceType.AC);
  261. }
  262. break;
  263. case Spell.TucsonGeneralRock:
  264. {
  265. if (ob.Race != ObjectType.Player && ob.Race != ObjectType.Monster) return;
  266. if (ob.Dead) return;
  267. if (ob == Caster) return;
  268. if (!ob.IsAttackTarget(Caster)) return;
  269. if (Value == 0) return;
  270. ob.Struck(Value, DefenceType.AC);
  271. }
  272. break;
  273. case Spell.Portal:
  274. {
  275. if (ob.Race != ObjectType.Player) return;
  276. if (Caster != ob && (Caster == null || (Caster.GroupMembers == null) || (!Caster.GroupMembers.Contains((HumanObject)ob)))) return;
  277. var portal = Envir.Spells.SingleOrDefault(ob => ob != this && ob.Node != null
  278. && ob.Spell == Spell.Portal
  279. && ob.Caster == Caster);
  280. if (portal != null)
  281. {
  282. Point newExit = Functions.PointMove(portal.CurrentLocation, ob.Direction, 1);
  283. if (!portal.CurrentMap.ValidPoint(newExit)) return;
  284. ob.Teleport(portal.CurrentMap, newExit, false);
  285. }
  286. Value -= 1;
  287. if (Value < 1)
  288. {
  289. ExpireTime = 0;
  290. }
  291. }
  292. break;
  293. case Spell.FlyingStatueIceTornado:
  294. {
  295. if (ob.Race != ObjectType.Player && ob.Race != ObjectType.Monster) return;
  296. if (ob.Dead) return;
  297. if (ob == Caster) return;
  298. if (!ob.IsAttackTarget(Caster)) return;
  299. ob.Struck(Value, DefenceType.MAC);
  300. if (Envir.Random.Next(8) == 0)
  301. {
  302. ob.ApplyPoison(new Poison { PType = PoisonType.Slow, Duration = 5, TickSpeed = 1000 }, this);
  303. }
  304. }
  305. break;
  306. case Spell.DarkOmaKingNuke:
  307. {
  308. if (ob.Race != ObjectType.Player && ob.Race != ObjectType.Monster) return;
  309. if (ob.Dead) return;
  310. if (ob == Caster) return;
  311. if (!ob.IsAttackTarget(Caster)) return;
  312. ob.Struck(Value, DefenceType.AC);
  313. ob.ApplyPoison(new Poison { PType = PoisonType.Dazed, Duration = 5, TickSpeed = 1000 }, this);
  314. }
  315. break;
  316. case Spell.HornedSorcererDustTornado:
  317. {
  318. if (ob.Race != ObjectType.Player && ob.Race != ObjectType.Monster) return;
  319. if (ob.Dead) return;
  320. if (ob == Caster) return;
  321. if (!ob.IsAttackTarget(Caster)) return;
  322. if (Value == 0) return;
  323. ob.Struck(Value, DefenceType.AC);
  324. }
  325. break;
  326. case Spell.HornedCommanderRockFall:
  327. case Spell.HornedCommanderRockSpike:
  328. {
  329. if (ob.Race != ObjectType.Player && ob.Race != ObjectType.Monster) return;
  330. if (ob.Dead) return;
  331. if (ob == Caster) return;
  332. if (!ob.IsAttackTarget(Caster)) return;
  333. if (Value == 0) return;
  334. ob.Struck(Value, DefenceType.AC);
  335. }
  336. break;
  337. case Spell.HealingCircle:
  338. if (ob.Race != ObjectType.Player && ob.Race != ObjectType.Monster) return;
  339. if (Caster == ob || (Caster.GroupMembers != null && Caster.GroupMembers.Contains(ob)) || (ob.Master != null && Caster.GroupMembers != null && Caster.GroupMembers.Contains(ob.Master)))
  340. {
  341. if (ob.Dead || ob.HealAmount != 0 || ob.PercentHealth == 100) return;
  342. ob.HealAmount += 25;
  343. Broadcast(new S.ObjectEffect { ObjectID = ob.ObjectID, Effect = SpellEffect.Healing });
  344. }
  345. else if (ob.IsAttackTarget(Caster))
  346. {
  347. if (ob.Dead) return;
  348. if (!ob.IsAttackTarget(Caster)) return;
  349. if (Value == 0) return;
  350. ob.Struck(Value, DefenceType.MAC);
  351. ob.ApplyPoison(new Poison
  352. {
  353. Owner = Caster,
  354. Duration = ob.Race == ObjectType.Player ? 4 : 5 + Envir.Random.Next(5),
  355. PType = PoisonType.Slow,
  356. TickSpeed = 1000,
  357. }, Caster);
  358. ob.OperateTime = 0;
  359. }
  360. break;
  361. }
  362. }
  363. public void DetonateTrapNow()
  364. {
  365. DetonatedTrap = true;
  366. Broadcast(GetInfo());
  367. ExpireTime = Envir.Time + 1000;
  368. }
  369. public override void SetOperateTime()
  370. {
  371. long time = Envir.Time + 2000;
  372. if (TickTime < time && TickTime > Envir.Time)
  373. time = TickTime;
  374. if (OwnerTime < time && OwnerTime > Envir.Time)
  375. time = OwnerTime;
  376. if (ExpireTime < time && ExpireTime > Envir.Time)
  377. time = ExpireTime;
  378. if (PKPointTime < time && PKPointTime > Envir.Time)
  379. time = PKPointTime;
  380. if (LastHitTime < time && LastHitTime > Envir.Time)
  381. time = LastHitTime;
  382. if (EXPOwnerTime < time && EXPOwnerTime > Envir.Time)
  383. time = EXPOwnerTime;
  384. if (BrownTime < time && BrownTime > Envir.Time)
  385. time = BrownTime;
  386. for (int i = 0; i < ActionList.Count; i++)
  387. {
  388. if (ActionList[i].Time >= time && ActionList[i].Time > Envir.Time) continue;
  389. time = ActionList[i].Time;
  390. }
  391. for (int i = 0; i < PoisonList.Count; i++)
  392. {
  393. if (PoisonList[i].TickTime >= time && PoisonList[i].TickTime > Envir.Time) continue;
  394. time = PoisonList[i].TickTime;
  395. }
  396. for (int i = 0; i < Buffs.Count; i++)
  397. {
  398. if (Buffs[i].NextTime >= time && Buffs[i].NextTime > Envir.Time) continue;
  399. time = Buffs[i].NextTime;
  400. }
  401. if (OperateTime <= Envir.Time || time < OperateTime)
  402. OperateTime = time;
  403. }
  404. public override void Process(DelayedAction action)
  405. {
  406. throw new NotSupportedException();
  407. }
  408. public override bool IsAttackTarget(HumanObject attacker)
  409. {
  410. throw new NotSupportedException();
  411. }
  412. public override bool IsAttackTarget(MonsterObject attacker)
  413. {
  414. throw new NotSupportedException();
  415. }
  416. public override int Attacked(HumanObject attacker, int damage, DefenceType type = DefenceType.ACAgility, bool damageWeapon = true)
  417. {
  418. throw new NotSupportedException();
  419. }
  420. public override int Attacked(MonsterObject attacker, int damage, DefenceType type = DefenceType.ACAgility)
  421. {
  422. throw new NotSupportedException();
  423. }
  424. public override int Struck(int damage, DefenceType type = DefenceType.ACAgility)
  425. {
  426. throw new NotSupportedException();
  427. }
  428. public override bool IsFriendlyTarget(HumanObject ally)
  429. {
  430. throw new NotSupportedException();
  431. }
  432. public override bool IsFriendlyTarget(MonsterObject ally)
  433. {
  434. throw new NotSupportedException();
  435. }
  436. public override void ReceiveChat(string text, ChatType type)
  437. {
  438. throw new NotSupportedException();
  439. }
  440. public override Packet GetInfo()
  441. {
  442. switch (Spell)
  443. {
  444. case Spell.Healing:
  445. return null;
  446. case Spell.PoisonCloud:
  447. case Spell.Blizzard:
  448. case Spell.MeteorStrike:
  449. case Spell.StoneGolemQuake:
  450. case Spell.EarthGolemPile:
  451. case Spell.TreeQueenMassRoots:
  452. case Spell.TreeQueenGroundRoots:
  453. case Spell.FlyingStatueIceTornado:
  454. case Spell.DarkOmaKingNuke:
  455. case Spell.HornedSorcererDustTornado:
  456. case Spell.HornedCommanderRockFall:
  457. case Spell.HornedCommanderRockSpike:
  458. case Spell.HealingCircle:
  459. if (!Show)
  460. return null;
  461. return new S.ObjectSpell
  462. {
  463. ObjectID = ObjectID,
  464. Location = CastLocation,
  465. Spell = Spell,
  466. Direction = Direction
  467. };
  468. case Spell.ExplosiveTrap:
  469. return new S.ObjectSpell
  470. {
  471. ObjectID = ObjectID,
  472. Location = CurrentLocation,
  473. Spell = Spell,
  474. Direction = Direction,
  475. Param = DetonatedTrap
  476. };
  477. default:
  478. return new S.ObjectSpell
  479. {
  480. ObjectID = ObjectID,
  481. Location = CurrentLocation,
  482. Spell = Spell,
  483. Direction = Direction
  484. };
  485. }
  486. }
  487. public override void ApplyPoison(Poison p, MapObject Caster = null, bool NoResist = false, bool ignoreDefence = true)
  488. {
  489. throw new NotSupportedException();
  490. }
  491. public override void Die()
  492. {
  493. throw new NotSupportedException();
  494. }
  495. public override int Pushed(MapObject pusher, MirDirection dir, int distance)
  496. {
  497. throw new NotSupportedException();
  498. }
  499. public override void SendHealth(HumanObject player)
  500. {
  501. throw new NotSupportedException();
  502. }
  503. public override void Spawned()
  504. {
  505. base.Spawned();
  506. Envir.Spells.Add(this);
  507. }
  508. public override void Despawn()
  509. {
  510. base.Despawn();
  511. Envir.Spells.Remove(this);
  512. if (Spell == Spell.Reincarnation && Caster != null && Caster.Node != null)
  513. {
  514. ((HumanObject)Caster).ActiveReincarnation = false;
  515. ((HumanObject)Caster).Enqueue(new S.CancelReincarnation { });
  516. }
  517. if (Spell == Spell.ExplosiveTrap && Caster != null)
  518. {
  519. var linkedTraps = CurrentMap.GetSpellObjects(Spell.ExplosiveTrap, Owner).Where(x => x.ExplosiveTrapID == ExplosiveTrapID);
  520. foreach (var trap in linkedTraps)
  521. {
  522. trap.DetonateTrapNow();
  523. }
  524. }
  525. if (Spell == Spell.Portal && Caster != null)
  526. {
  527. var portal = Envir.Spells.SingleOrDefault(ob => ob.Node != null && ob != this
  528. && ob.Spell == Spell.Portal
  529. && ob.Caster == Caster);
  530. if (portal != null)
  531. {
  532. portal.ExpireTime = 0;
  533. portal.Process();
  534. }
  535. }
  536. }
  537. public override void BroadcastInfo()
  538. {
  539. if ((Spell != Spell.ExplosiveTrap) || (Caster == null))
  540. base.BroadcastInfo();
  541. Packet p;
  542. if (CurrentMap == null) return;
  543. for (int i = CurrentMap.Players.Count - 1; i >= 0; i--)
  544. {
  545. PlayerObject player = CurrentMap.Players[i];
  546. if (Functions.InRange(CurrentLocation, player.CurrentLocation, Globals.DataRange))
  547. {
  548. if ((Caster == null) || (player == null)) continue;
  549. if ((player == Caster) || player.IsFriendlyTarget(Caster))
  550. {
  551. p = GetInfo();
  552. if (p != null)
  553. player.Enqueue(p);
  554. }
  555. }
  556. }
  557. }
  558. }
  559. }