HeroObject.cs 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319
  1. using System.Drawing;
  2. using Server.MirDatabase;
  3. using Server.MirEnvir;
  4. using Server.MirNetwork;
  5. using S = ServerPackets;
  6. using Server.MirObjects.Monsters;
  7. namespace Server.MirObjects
  8. {
  9. public class HeroObject : HumanObject
  10. {
  11. public override ObjectType Race
  12. {
  13. get { return ObjectType.Hero; }
  14. }
  15. public override MirConnection Connection
  16. {
  17. get { return connection; }
  18. set { throw new NotSupportedException(); }
  19. }
  20. public HeroInfo HInfo;
  21. public new PlayerObject Owner;
  22. public override int PotionBeltMinimum => 0;
  23. public override int PotionBeltMaximum => 2;
  24. public override int AmuletBeltMinimum => 1;
  25. public override int AmuletBeltMaximum => 2;
  26. public override int BeltSize => 2;
  27. public override bool CanMove
  28. {
  29. get
  30. {
  31. return base.CanMove && !ActiveBlizzard && !ActiveReincarnation && (Owner.PMode == PetMode.MoveOnly || Owner.PMode == PetMode.Both || Owner.PMode == PetMode.FocusMasterTarget);
  32. }
  33. }
  34. public override bool CanWalk
  35. {
  36. get
  37. {
  38. return base.CanWalk && !ActiveBlizzard && !ActiveReincarnation && (Owner.PMode == PetMode.MoveOnly || Owner.PMode == PetMode.Both || Owner.PMode == PetMode.FocusMasterTarget);
  39. }
  40. }
  41. public override bool CanRun
  42. {
  43. get
  44. {
  45. return base.CanRun && !ActiveBlizzard && !ActiveReincarnation && (Owner.PMode == PetMode.MoveOnly || Owner.PMode == PetMode.Both || Owner.PMode == PetMode.FocusMasterTarget);
  46. }
  47. }
  48. public override bool CanAttack
  49. {
  50. get
  51. {
  52. return base.CanAttack && !ActiveBlizzard && !ActiveReincarnation && (Owner.PMode == PetMode.AttackOnly || Owner.PMode == PetMode.Both || Owner.PMode == PetMode.FocusMasterTarget);
  53. }
  54. }
  55. protected override bool CanCast
  56. {
  57. get
  58. {
  59. return base.CanCast && !ActiveBlizzard && !ActiveReincarnation && (Owner.PMode == PetMode.AttackOnly || Owner.PMode == PetMode.Both || Owner.PMode == PetMode.FocusMasterTarget);
  60. }
  61. }
  62. public const int SearchDelay = 3000, ViewRange = 8, RoamDelay = 1000, RevivalDelay = 2000, AutoPotDelay = 1000;
  63. public long RoamTime, AutoPotTime;
  64. public override long BrownTime
  65. {
  66. get { return Owner.BrownTime; }
  67. set { brownTime = value; }
  68. }
  69. public byte Grade
  70. {
  71. get { return HInfo.Grade; }
  72. set { HInfo.Grade = value; }
  73. }
  74. public bool AutoPot
  75. {
  76. get { return HInfo.AutoPot; }
  77. set { HInfo.AutoPot = value; }
  78. }
  79. public byte AutoHPPercent
  80. {
  81. get { return HInfo.AutoHPPercent; }
  82. set { HInfo.AutoHPPercent = value; }
  83. }
  84. public byte AutoMPPercent
  85. {
  86. get { return HInfo.AutoMPPercent; }
  87. set { HInfo.AutoMPPercent = value; }
  88. }
  89. public int HPItemIndex
  90. {
  91. get { return HInfo.HPItemIndex; }
  92. set { HInfo.HPItemIndex = value; }
  93. }
  94. public int MPItemIndex
  95. {
  96. get { return HInfo.MPItemIndex; }
  97. set { HInfo.MPItemIndex = value; }
  98. }
  99. protected Spell NextMagicSpell;
  100. protected MirDirection NextMagicDirection;
  101. protected uint NextMagicTargetID;
  102. protected Point NextMagicLocation;
  103. protected int TargetDistance;
  104. public override GuildObject MyGuild
  105. {
  106. get { return Owner.MyGuild; }
  107. set { throw new NotSupportedException(); }
  108. }
  109. public override MapObject Master
  110. {
  111. get { return Owner; }
  112. set { Owner = (PlayerObject)value; }
  113. }
  114. public HeroObject(CharacterInfo info, PlayerObject owner)
  115. {
  116. CheckCellTime = false;
  117. Owner = owner;
  118. base.Report = owner.Report;
  119. Load(info, null);
  120. }
  121. protected override void Load(CharacterInfo info, MirConnection connection)
  122. {
  123. info.Mount = new MountInfo(this);
  124. Info = info;
  125. HInfo = (HeroInfo)info;
  126. Stats = new Stats();
  127. if (Level == 0) NewCharacter();
  128. RefreshStats();
  129. SendInfo();
  130. switch (HP)
  131. {
  132. case 0:
  133. Dead = true;
  134. break;
  135. case -1:
  136. SetHP(Stats[Stat.HP]);
  137. SetMP(Stats[Stat.MP]);
  138. break;
  139. }
  140. }
  141. protected override void NewCharacter()
  142. {
  143. base.NewCharacter();
  144. Grade = (byte)Envir.Random.Next(4);
  145. }
  146. public override void Enqueue(Packet p)
  147. {
  148. if (p == null) return;
  149. switch ((ServerPacketIds)p.Index)
  150. {
  151. case ServerPacketIds.AddBuff:
  152. case ServerPacketIds.RemoveBuff:
  153. case ServerPacketIds.PauseBuff:
  154. case ServerPacketIds.MagicDelay:
  155. case ServerPacketIds.MagicLeveled:
  156. case ServerPacketIds.DeleteItem:
  157. case ServerPacketIds.UseItem:
  158. Owner.Enqueue(p);
  159. break;
  160. }
  161. }
  162. public override void RefreshNameColour()
  163. {
  164. Color colour = Color.MediumOrchid;
  165. if (colour == NameColour) return;
  166. NameColour = colour;
  167. if ((Owner.MyGuild == null) || (!Owner.MyGuild.IsAtWar()))
  168. Enqueue(new S.ColourChanged { NameColour = NameColour });
  169. BroadcastColourChange();
  170. }
  171. public void Spawn(Map map, Point p)
  172. {
  173. CurrentLocation = p;
  174. map.AddObject(this);
  175. CurrentMap = map;
  176. Envir.Heroes.Add(this);
  177. Spawned();
  178. }
  179. public void Despawn(bool cleanup)
  180. {
  181. Despawn();
  182. if (cleanup)
  183. CleanUp();
  184. }
  185. public override void Despawn()
  186. {
  187. Envir.Heroes.Remove(this);
  188. if (Node != null)
  189. {
  190. CurrentMap.RemoveObject(this);
  191. for (int i = Buffs.Count - 1; i >= 0; i--)
  192. {
  193. var buff = Buffs[i];
  194. buff.Caster = null;
  195. buff.ObjectID = 0;
  196. if (buff.Properties.HasFlag(BuffProperty.RemoveOnExit))
  197. {
  198. Buffs.RemoveAt(i);
  199. }
  200. }
  201. base.Despawn();
  202. }
  203. }
  204. protected override void CleanUp()
  205. {
  206. Owner = null;
  207. Info = null;
  208. }
  209. public override void Spawned()
  210. {
  211. base.Spawned();
  212. BroadcastHealthChange();
  213. BroadcastManaChange();
  214. BroadcastColourChange();
  215. }
  216. public override void Add(HumanObject player)
  217. {
  218. base.Add(player);
  219. if (player is PlayerObject viewer)
  220. {
  221. viewer.Enqueue(new S.ObjectColourChanged
  222. {
  223. ObjectID = ObjectID,
  224. NameColour = viewer.GetNameColour(this)
  225. });
  226. }
  227. }
  228. protected virtual void GetItemInfo()
  229. {
  230. UserItem item;
  231. for (int i = 0; i < Info.Inventory.Length; i++)
  232. {
  233. item = Info.Inventory[i];
  234. if (item == null) continue;
  235. Owner.CheckItem(item);
  236. }
  237. for (int i = 0; i < Info.Equipment.Length; i++)
  238. {
  239. item = Info.Equipment[i];
  240. if (item == null) continue;
  241. Owner.CheckItem(item);
  242. }
  243. if (HPItemIndex > 0)
  244. Owner.CheckItemInfo(Envir.GetItemInfo(HPItemIndex));
  245. if (MPItemIndex > 0)
  246. Owner.CheckItemInfo(Envir.GetItemInfo(MPItemIndex));
  247. }
  248. public override void SendMagicInfo(UserMagic magic)
  249. {
  250. Owner.Enqueue(magic.GetInfo(true));
  251. }
  252. protected bool CanUseMagic(UserMagic magic)
  253. {
  254. if (magic == null) return false;
  255. if (MagicCost(magic) > MP) return false;
  256. if (magic.Key <= 0) return false;
  257. return true;
  258. }
  259. protected bool HasMagic(Spell spell) => Info.Magics.Any(x => x.Spell == spell);
  260. public override bool TryMagic()
  261. {
  262. return true;
  263. }
  264. public override void BeginMagic(Spell spell, MirDirection dir, uint targetID, Point location, bool spellTargetLock = false)
  265. {
  266. NextMagicSpell = spell;
  267. NextMagicDirection = dir;
  268. NextMagicTargetID = targetID;
  269. NextMagicLocation = location;
  270. }
  271. public override MapObject DefaultMagicTarget => Owner;
  272. public override void UseItem(ulong id)
  273. {
  274. S.UseItem p = new S.UseItem { UniqueID = id, Grid = MirGridType.HeroInventory, Success = false };
  275. UserItem item = null;
  276. int index = -1;
  277. if (Owner.Hero != null && Owner.Hero.Dead) return;
  278. for (int i = 0; i < Info.Inventory.Length; i++)
  279. {
  280. item = Info.Inventory[i];
  281. if (item == null || item.UniqueID != id) continue;
  282. index = i;
  283. break;
  284. }
  285. if (item == null || index == -1 || !CanUseItem(item))
  286. {
  287. Owner.Enqueue(p);
  288. return;
  289. }
  290. if (Dead && !(item.Info.Type == ItemType.Scroll && item.Info.Shape == 6))
  291. {
  292. Owner.Enqueue(p);
  293. return;
  294. }
  295. switch (item.Info.Type)
  296. {
  297. case ItemType.Potion:
  298. switch (item.Info.Shape)
  299. {
  300. case 0: //NormalPotion
  301. PotHealthAmount = (ushort)Math.Min(ushort.MaxValue, PotHealthAmount + item.Info.Stats[Stat.HP]);
  302. PotManaAmount = (ushort)Math.Min(ushort.MaxValue, PotManaAmount + item.Info.Stats[Stat.MP]);
  303. break;
  304. case 1: //SunPotion
  305. ChangeHP(item.Info.Stats[Stat.HP]);
  306. ChangeMP(item.Info.Stats[Stat.MP]);
  307. break;
  308. case 2: //MysteryWater
  309. if (UnlockCurse)
  310. {
  311. ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.ReincarnationFailed), ChatType.Hint);
  312. Owner.Enqueue(p);
  313. return;
  314. }
  315. ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.YouCanUnequipCursedItem), ChatType.Hint);
  316. UnlockCurse = true;
  317. break;
  318. case 3: //Buff
  319. {
  320. int time = item.Info.Durability;
  321. if (item.GetTotal(Stat.MaxDC) > 0)
  322. AddBuff(BuffType.Impact, this, time * Settings.Minute, new Stats { [Stat.MaxDC] = item.GetTotal(Stat.MaxDC) });
  323. if (item.GetTotal(Stat.MaxMC) > 0)
  324. AddBuff(BuffType.Magic, this, time * Settings.Minute, new Stats { [Stat.MaxMC] = item.GetTotal(Stat.MaxMC) });
  325. if (item.GetTotal(Stat.MaxSC) > 0)
  326. AddBuff(BuffType.Taoist, this, time * Settings.Minute, new Stats { [Stat.MaxSC] = item.GetTotal(Stat.MaxSC) });
  327. if (item.GetTotal(Stat.AttackSpeed) > 0)
  328. AddBuff(BuffType.Storm, this, time * Settings.Minute, new Stats { [Stat.AttackSpeed] = item.GetTotal(Stat.AttackSpeed) });
  329. if (item.GetTotal(Stat.HP) > 0)
  330. AddBuff(BuffType.HealthAid, this, time * Settings.Minute, new Stats { [Stat.HP] = item.GetTotal(Stat.HP) });
  331. if (item.GetTotal(Stat.MP) > 0)
  332. AddBuff(BuffType.ManaAid, this, time * Settings.Minute, new Stats { [Stat.MP] = item.GetTotal(Stat.MP) });
  333. if (item.GetTotal(Stat.MaxAC) > 0)
  334. AddBuff(BuffType.Defence, this, time * Settings.Minute, new Stats { [Stat.MaxAC] = item.GetTotal(Stat.MaxAC) });
  335. if (item.GetTotal(Stat.MaxMAC) > 0)
  336. AddBuff(BuffType.MagicDefence, this, time * Settings.Minute, new Stats { [Stat.MaxMAC] = item.GetTotal(Stat.MaxMAC) });
  337. if (item.GetTotal(Stat.BagWeight) > 0)
  338. AddBuff(BuffType.BagWeight, this, time * Settings.Minute, new Stats { [Stat.BagWeight] = item.GetTotal(Stat.BagWeight) });
  339. }
  340. break;
  341. case 4: //Exp
  342. {
  343. int time = item.Info.Durability;
  344. AddBuff(BuffType.Exp, this, Settings.Minute * time, new Stats { [Stat.ExpRatePercent] = item.GetTotal(Stat.Luck) });
  345. }
  346. break;
  347. case 5: //Drop
  348. {
  349. int time = item.Info.Durability;
  350. AddBuff(BuffType.Drop, this, Settings.Minute * time, new Stats { [Stat.ItemDropRatePercent] = item.GetTotal(Stat.Luck) });
  351. }
  352. break;
  353. }
  354. break;
  355. case ItemType.Scroll:
  356. UserItem temp;
  357. switch (item.Info.Shape)
  358. {
  359. case 3: //BenedictionOil
  360. if (!TryLuckWeapon())
  361. {
  362. Owner.Enqueue(p);
  363. return;
  364. }
  365. break;
  366. case 4: //RepairOil
  367. temp = Info.Equipment[(int)EquipmentSlot.Weapon];
  368. if (temp == null || temp.MaxDura == temp.CurrentDura)
  369. {
  370. Owner.Enqueue(p);
  371. return;
  372. }
  373. if (temp.Info.Bind.HasFlag(BindMode.DontRepair))
  374. {
  375. Owner.Enqueue(p);
  376. return;
  377. }
  378. temp.MaxDura = (ushort)Math.Max(0, temp.MaxDura - Math.Min(5000, temp.MaxDura - temp.CurrentDura) / 30);
  379. temp.CurrentDura = (ushort)Math.Min(temp.MaxDura, temp.CurrentDura + 5000);
  380. temp.DuraChanged = false;
  381. ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.HeroWeaponPartiallyRepaired), ChatType.Hint);
  382. Owner.Enqueue(new S.ItemRepaired { UniqueID = temp.UniqueID, MaxDura = temp.MaxDura, CurrentDura = temp.CurrentDura });
  383. break;
  384. case 5: //WarGodOil
  385. temp = Info.Equipment[(int)EquipmentSlot.Weapon];
  386. if (temp == null || temp.MaxDura == temp.CurrentDura)
  387. {
  388. Owner.Enqueue(p);
  389. return;
  390. }
  391. if (temp.Info.Bind.HasFlag(BindMode.DontRepair) || (temp.Info.Bind.HasFlag(BindMode.NoSRepair)))
  392. {
  393. Owner.Enqueue(p);
  394. return;
  395. }
  396. temp.CurrentDura = temp.MaxDura;
  397. temp.DuraChanged = false;
  398. ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.HeroWeaponRepaired), ChatType.Hint);
  399. Owner.Enqueue(new S.ItemRepaired { UniqueID = temp.UniqueID, MaxDura = temp.MaxDura, CurrentDura = temp.CurrentDura });
  400. break;
  401. case 6: //ResurrectionScroll
  402. if (CurrentMap.Info.NoReincarnation)
  403. {
  404. ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.CannotUseOnThisMap), ChatType.System);
  405. Owner.Enqueue(p);
  406. return;
  407. }
  408. if (Dead)
  409. {
  410. MP = Stats[Stat.MP];
  411. Revive(MaxHealth, true);
  412. }
  413. break;
  414. case 15: //Increase Hero inventory
  415. if (Info.Inventory.Length >= 42)
  416. {
  417. ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.HeroInventoryMax)
  418. , ChatType.System);
  419. Owner.Enqueue(p);
  420. return;
  421. }
  422. Enqueue(new S.ResizeInventory { Size = Info.ResizeInventory() });
  423. ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.HeroInventoryIncreased), ChatType.System);
  424. Owner.Enqueue(p);
  425. break;
  426. }
  427. break;
  428. case ItemType.Book:
  429. UserMagic magic = new UserMagic((Spell)item.Info.Shape);
  430. if (magic.Info == null)
  431. {
  432. Owner.Enqueue(p);
  433. return;
  434. }
  435. Info.Magics.Add(magic);
  436. SendMagicInfo(magic);
  437. RefreshStats();
  438. break;
  439. case ItemType.Food:
  440. temp = Info.Equipment[(int)EquipmentSlot.Mount];
  441. if (temp == null || temp.MaxDura == temp.CurrentDura)
  442. {
  443. Owner.Enqueue(p);
  444. return;
  445. }
  446. switch (item.Info.Shape)
  447. {
  448. case 0:
  449. temp.MaxDura = (ushort)Math.Max(0, temp.MaxDura - Math.Min(1000, temp.MaxDura - (temp.CurrentDura / 30)));
  450. break;
  451. case 1:
  452. break;
  453. }
  454. temp.CurrentDura = (ushort)Math.Min(temp.MaxDura, temp.CurrentDura + item.CurrentDura);
  455. temp.DuraChanged = false;
  456. ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.HeroMountFed), ChatType.Hint);
  457. Owner.Enqueue(new S.ItemRepaired { UniqueID = temp.UniqueID, MaxDura = temp.MaxDura, CurrentDura = temp.CurrentDura });
  458. RefreshStats();
  459. break;
  460. case ItemType.Transform: //Transforms
  461. {
  462. AddBuff(BuffType.Transform, this, (Settings.Second * item.Info.Durability), new Stats(), values: item.Info.Shape);
  463. }
  464. break;
  465. case ItemType.Deco:
  466. DecoObject decoOb = new DecoObject
  467. {
  468. Image = item.Info.Shape,
  469. CurrentMap = CurrentMap,
  470. CurrentLocation = CurrentLocation,
  471. };
  472. CurrentMap.AddObject(decoOb);
  473. decoOb.Spawned();
  474. Owner.Enqueue(decoOb.GetInfo());
  475. break;
  476. case ItemType.MonsterSpawn:
  477. var monsterID = item.Info.Stats[Stat.HP];
  478. var spawnAsPet = item.Info.Shape == 1;
  479. var conquestOnly = item.Info.Shape == 2;
  480. var monsterInfo = Envir.GetMonsterInfo(monsterID);
  481. if (monsterInfo == null) break;
  482. MonsterObject monster = MonsterObject.GetMonster(monsterInfo);
  483. if (monster == null) break;
  484. if (spawnAsPet)
  485. {
  486. if (Pets.Count(t => !t.Dead && t.Race != ObjectType.Creature) >= Globals.MaxPets)
  487. {
  488. ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.MaximumPetsReached), ChatType.Hint);
  489. Owner.Enqueue(p);
  490. return;
  491. }
  492. monster.Master = this;
  493. monster.PetLevel = 0;
  494. monster.MaxPetLevel = 7;
  495. Pets.Add(monster);
  496. }
  497. if (conquestOnly)
  498. {
  499. var con = CurrentMap.GetConquest(CurrentLocation);
  500. if (con == null)
  501. {
  502. ReceiveChat(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.MonsterOnlySpawnDuringConquest), monsterInfo.GameName), ChatType.Hint);
  503. Owner.Enqueue(p);
  504. return;
  505. }
  506. }
  507. monster.Direction = Direction;
  508. monster.ActionTime = Envir.Time + 5000;
  509. if (!monster.Spawn(CurrentMap, Front))
  510. monster.Spawn(CurrentMap, CurrentLocation);
  511. break;
  512. case ItemType.SiegeAmmo:
  513. //TODO;
  514. break;
  515. default:
  516. return;
  517. }
  518. if (item.Count > 1) item.Count--;
  519. else Info.Inventory[index] = null;
  520. RefreshBagWeight();
  521. Report?.ItemChanged(item, 1, 1);
  522. p.Success = true;
  523. Owner.Enqueue(p);
  524. }
  525. public override void Die()
  526. {
  527. if (SpecialMode.HasFlag(SpecialItemMode.Revival) && Envir.Time > LastRevivalTime)
  528. {
  529. LastRevivalTime = Envir.Time + 300000;
  530. for (var i = (int)EquipmentSlot.RingL; i <= (int)EquipmentSlot.RingR; i++)
  531. {
  532. var item = Info.Equipment[i];
  533. if (item == null) continue;
  534. if (!(item.Info.Unique.HasFlag(SpecialItemMode.Revival)) || item.CurrentDura < 1000) continue;
  535. SetHP(Stats[Stat.HP]);
  536. item.CurrentDura = (ushort)(item.CurrentDura - 1000);
  537. Enqueue(new S.DuraChanged { UniqueID = item.UniqueID, CurrentDura = item.CurrentDura });
  538. RefreshStats();
  539. ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.HeroSecondChanceLife), ChatType.System);
  540. return;
  541. }
  542. }
  543. for (int i = Pets.Count - 1; i >= 0; i--)
  544. {
  545. if (Pets[i].Dead) continue;
  546. Pets[i].Die();
  547. }
  548. RemoveBuff(BuffType.MagicShield);
  549. RemoveBuff(BuffType.ElementalBarrier);
  550. if (!InSafeZone)
  551. DeathDrop(LastHitter);
  552. HP = 0;
  553. Dead = true;
  554. LogTime = Envir.Time;
  555. BrownTime = Envir.Time;
  556. Broadcast(new S.ObjectDied { ObjectID = ObjectID, Direction = Direction, Location = CurrentLocation });
  557. Owner.Enqueue(new S.UpdateHeroSpawnState { State = HeroSpawnState.Dead });
  558. for (int i = 0; i < Buffs.Count; i++)
  559. {
  560. Buff buff = Buffs[i];
  561. if (!buff.Properties.HasFlag(BuffProperty.RemoveOnDeath)) continue;
  562. RemoveBuff(buff.Type);
  563. }
  564. PoisonList.Clear();
  565. InTrapRock = false;
  566. }
  567. public override void Revive(int hp, bool effect)
  568. {
  569. if (!Dead) return;
  570. SetHP(hp);
  571. SetMP(Stats[Stat.MP]);
  572. CurrentMap.RemoveObject(this);
  573. Broadcast(new S.ObjectRemove { ObjectID = ObjectID });
  574. Dead = false;
  575. ActionTime = Envir.Time + RevivalDelay;
  576. CurrentMap.AddObject(this);
  577. BroadcastInfo();
  578. Broadcast(new S.ObjectRevived { ObjectID = ObjectID, Effect = effect });
  579. }
  580. public override void LevelUp()
  581. {
  582. base.LevelUp();
  583. Owner.Enqueue(new S.HeroLevelChanged { Level = Level, Experience = Experience, MaxExperience = MaxExperience });
  584. }
  585. protected override void SendHealthChanged()
  586. {
  587. Owner.Enqueue(new S.HeroHealthChanged { HP = HP, MP = MP });
  588. base.SendHealthChanged();
  589. BroadcastManaChange();
  590. }
  591. public override void BroadcastHealthChange()
  592. {
  593. byte time = Math.Min(byte.MaxValue, (byte)Math.Max(5, (RevTime - Envir.Time) / 1000));
  594. Packet p = new S.ObjectHealth { ObjectID = ObjectID, Percent = PercentHealth, Expire = time };
  595. if (Envir.Time < RevTime)
  596. {
  597. CurrentMap.Broadcast(p, CurrentLocation);
  598. return;
  599. }
  600. Owner.Enqueue(p);
  601. if (Owner.GroupMembers != null)
  602. {
  603. for (int i = 0; i < Owner.GroupMembers.Count; i++)
  604. {
  605. PlayerObject member = Owner.GroupMembers[i];
  606. if (Master == member) continue;
  607. if (member.CurrentMap != CurrentMap || !Functions.InRange(member.CurrentLocation, CurrentLocation, Globals.DataRange)) continue;
  608. member.Enqueue(p);
  609. }
  610. }
  611. }
  612. public byte PercentMana
  613. {
  614. get { return (byte)(MP / (float)Stats[Stat.MP] * 100); }
  615. }
  616. public void BroadcastManaChange()
  617. {
  618. Packet p = new S.ObjectMana { ObjectID = ObjectID, Percent = PercentMana };
  619. Owner.Enqueue(p);
  620. }
  621. public override void Process(DelayedAction action)
  622. {
  623. if (action.FlaggedToRemove)
  624. return;
  625. switch (action.Type)
  626. {
  627. case DelayedType.Magic:
  628. CompleteMagic(action.Params);
  629. break;
  630. case DelayedType.Damage:
  631. CompleteAttack(action.Params);
  632. break;
  633. case DelayedType.Mine:
  634. CompleteMine(action.Params);
  635. break;
  636. case DelayedType.Poison:
  637. CompletePoison(action.Params);
  638. break;
  639. case DelayedType.DamageIndicator:
  640. CompleteDamageIndicator(action.Params);
  641. break;
  642. case DelayedType.SpellEffect:
  643. CompleteSpellEffect(action.Params);
  644. break;
  645. }
  646. }
  647. public override void Process()
  648. {
  649. if (Node == null || Info == null) return;
  650. if (Owner != null && Owner.CurrentMap != null && Owner.CurrentMap.Info.NoHero)
  651. {
  652. Owner.DespawnHero();
  653. return;
  654. }
  655. base.Process();
  656. if (Target != null && (Target.CurrentMap != CurrentMap || !Target.IsAttackTarget(this) || !Functions.InRange(CurrentLocation, Target.CurrentLocation, Globals.DataRange)))
  657. Target = null;
  658. if (Owner != null && !Owner.CurrentMap.Info.NoHero)
  659. {
  660. if ((!Functions.InRange(CurrentLocation, Owner.CurrentLocation, Globals.DataRange) || CurrentMap != Owner.CurrentMap) && CanMove)
  661. OwnerRecall();
  662. }
  663. if (Dead) return;
  664. if (Owner.PMode == PetMode.MoveOnly || Owner.PMode == PetMode.None)
  665. Target = null;
  666. ProcessAutoPot();
  667. ProcessStacking();
  668. ProcessSearch();
  669. ProcessAI();
  670. ProcessTarget();
  671. ProcessRoam();
  672. }
  673. protected void ProcessAutoPot()
  674. {
  675. if (Envir.Time < AutoPotTime) return;
  676. AutoPotTime = Envir.Time + AutoPotDelay;
  677. if (PercentHealth < AutoHPPercent && HPItemIndex > 0 && PotHealthAmount <= 0)
  678. TryAutoPot(HPItemIndex);
  679. if (PercentMana < AutoMPPercent && MPItemIndex > 0 && PotManaAmount <= 0)
  680. TryAutoPot(MPItemIndex);
  681. }
  682. protected void TryAutoPot(int ItemIndex)
  683. {
  684. for (int i = 0; i < Info.Inventory.Length; i++)
  685. {
  686. UserItem item = Info.Inventory[i];
  687. if (item == null) continue;
  688. if (item.Info.Index != ItemIndex) continue;
  689. UseItem(item.UniqueID);
  690. return;
  691. }
  692. }
  693. protected void ProcessAI()
  694. {
  695. if (NextMagicSpell != Spell.None) return;
  696. ProcessFriend();
  697. if (NextMagicSpell != Spell.None) return;
  698. ProcessAttack();
  699. }
  700. protected virtual void ProcessStacking()
  701. {
  702. Stacking = CheckStacked();
  703. if (CanMove && ((Owner != null && Owner.Front == CurrentLocation) || Stacking))
  704. {
  705. if (!Walk(Direction))
  706. {
  707. MirDirection dir = Direction;
  708. switch (Envir.Random.Next(3))
  709. {
  710. case 0:
  711. for (int i = 0; i < 7; i++)
  712. {
  713. dir = Functions.NextDir(dir);
  714. if (Walk(dir))
  715. break;
  716. }
  717. break;
  718. default:
  719. for (int i = 0; i < 7; i++)
  720. {
  721. dir = Functions.PreviousDir(dir);
  722. if (Walk(dir))
  723. break;
  724. }
  725. break;
  726. }
  727. }
  728. return;
  729. }
  730. }
  731. protected virtual void ProcessSearch()
  732. {
  733. if (Envir.Time < SearchTime) return;
  734. if (Owner.Info.HeroBehaviour == HeroBehaviour.Follow || !Mount.CanAttack) return;
  735. SearchTime = Envir.Time + SearchDelay;
  736. if (Target == null || Envir.Random.Next(3) == 0)
  737. FindTarget();
  738. }
  739. protected virtual void ProcessRoam()
  740. {
  741. if (Target != null || Envir.Time < RoamTime) return;
  742. if (Owner != null)
  743. {
  744. MoveTo(Owner.Back);
  745. return;
  746. }
  747. RoamTime = Envir.Time + RoamDelay;
  748. }
  749. protected virtual void ProcessFriend() { }
  750. protected virtual void ProcessAttack() { }
  751. protected virtual void ProcessTarget()
  752. {
  753. if (CanCast && NextMagicSpell != Spell.None)
  754. {
  755. Magic(NextMagicSpell, NextMagicDirection, NextMagicTargetID, NextMagicLocation);
  756. NextMagicSpell = Spell.None;
  757. }
  758. if (Target == null || !CanAttack) return;
  759. TargetDistance = Functions.MaxDistance(CurrentLocation, Target.CurrentLocation);
  760. if (InAttackRange())
  761. {
  762. Attack();
  763. if (Target != null && Target.Dead)
  764. {
  765. FindTarget();
  766. }
  767. return;
  768. }
  769. MoveTo(Target.CurrentLocation);
  770. }
  771. protected virtual void Attack()
  772. {
  773. if (!Target.IsAttackTarget(Owner))
  774. {
  775. Target = null;
  776. return;
  777. }
  778. Direction = Functions.DirectionFromPoint(CurrentLocation, Target.CurrentLocation);
  779. Attack(Direction, Spell.None);
  780. }
  781. protected virtual bool InAttackRange()
  782. {
  783. if (Target.CurrentMap != CurrentMap) return false;
  784. return Target.CurrentLocation != CurrentLocation && Functions.InRange(CurrentLocation, Target.CurrentLocation, 1);
  785. }
  786. protected List<MapObject> FindAllTargets(int dist, Point location, bool needSight = true)
  787. {
  788. List<MapObject> targets = new List<MapObject>();
  789. for (int d = 0; d <= dist; d++)
  790. {
  791. for (int y = location.Y - d; y <= location.Y + d; y++)
  792. {
  793. if (y < 0) continue;
  794. if (y >= CurrentMap.Height) break;
  795. for (int x = location.X - d; x <= location.X + d; x += Math.Abs(y - location.Y) == d ? 1 : d * 2)
  796. {
  797. if (x < 0) continue;
  798. if (x >= CurrentMap.Width) break;
  799. Cell cell = CurrentMap.GetCell(x, y);
  800. if (!cell.Valid || cell.Objects == null) continue;
  801. for (int i = 0; i < cell.Objects.Count; i++)
  802. {
  803. MapObject ob = cell.Objects[i];
  804. switch (ob.Race)
  805. {
  806. case ObjectType.Monster:
  807. case ObjectType.Player:
  808. case ObjectType.Hero:
  809. if (!ob.IsAttackTarget(this)) continue;
  810. if (ob.Hidden && (!CoolEye || Level < ob.Level) && needSight) continue;
  811. if (ob.Race == ObjectType.Player)
  812. {
  813. PlayerObject player = ((PlayerObject)ob);
  814. if (player.GMGameMaster) continue;
  815. }
  816. targets.Add(ob);
  817. continue;
  818. default:
  819. continue;
  820. }
  821. }
  822. }
  823. }
  824. }
  825. return targets;
  826. }
  827. protected virtual void MoveTo(Point location)
  828. {
  829. if (CurrentLocation == location) return;
  830. bool inRange = Functions.InRange(location, CurrentLocation, 1);
  831. bool inRunRange = RidingMount ? Functions.InRange(location, CurrentLocation, 2) : inRange;
  832. if (inRange)
  833. {
  834. if (!CurrentMap.ValidPoint(location)) return;
  835. Cell cell = CurrentMap.GetCell(location);
  836. if (cell.Objects != null)
  837. for (int i = 0; i < cell.Objects.Count; i++)
  838. {
  839. MapObject ob = cell.Objects[i];
  840. if (!ob.Blocking) continue;
  841. return;
  842. }
  843. }
  844. MirDirection dir = Functions.DirectionFromPoint(CurrentLocation, location);
  845. if (!inRunRange && _stepCounter > 0 && Run(dir))
  846. return;
  847. if (Walk(dir)) return;
  848. switch (Envir.Random.Next(2))
  849. {
  850. case 0:
  851. for (int i = 0; i < 7; i++)
  852. {
  853. dir = Functions.NextDir(dir);
  854. if (Walk(dir))
  855. return;
  856. }
  857. break;
  858. default:
  859. for (int i = 0; i < 7; i++)
  860. {
  861. dir = Functions.PreviousDir(dir);
  862. if (Walk(dir))
  863. return;
  864. }
  865. break;
  866. }
  867. }
  868. public void OwnerRecall()
  869. {
  870. if (Owner == null) return;
  871. if (Owner.CurrentMap != null && Owner.CurrentMap.Info.NoHero)
  872. return;
  873. if (Dead)
  874. {
  875. Despawn(false);
  876. return;
  877. }
  878. if (!Teleport(Owner.CurrentMap, Owner.Back))
  879. Teleport(Owner.CurrentMap, Owner.CurrentLocation);
  880. if (!Dead)
  881. {
  882. BroadcastManaChange();
  883. }
  884. }
  885. protected virtual void FindTarget()
  886. {
  887. Map Current = CurrentMap;
  888. for (int d = 0; d <= ViewRange; d++)
  889. {
  890. for (int y = CurrentLocation.Y - d; y <= CurrentLocation.Y + d; y++)
  891. {
  892. if (y < 0) continue;
  893. if (y >= Current.Height) break;
  894. for (int x = CurrentLocation.X - d; x <= CurrentLocation.X + d; x += Math.Abs(y - CurrentLocation.Y) == d ? 1 : d * 2)
  895. {
  896. if (x < 0) continue;
  897. if (x >= Current.Width) break;
  898. Cell cell = Current.Cells[x, y];
  899. if (cell.Objects == null || !cell.Valid) continue;
  900. for (int i = 0; i < cell.Objects.Count; i++)
  901. {
  902. MapObject ob = cell.Objects[i];
  903. switch (ob.Race)
  904. {
  905. case ObjectType.Monster:
  906. case ObjectType.Hero:
  907. if (ob is TownArcher) continue;
  908. if (!ob.IsAttackTarget(Owner)) continue;
  909. if (ob.Hidden && (!CoolEye || Level < ob.Level)) continue;
  910. if (ob.Master != null && Target != ob) continue;
  911. if (Owner.Info.HeroBehaviour == HeroBehaviour.CounterAttack && ob.Target != this && ob.Target != Owner) continue;
  912. Target = ob;
  913. return;
  914. case ObjectType.Player:
  915. PlayerObject playerob = (PlayerObject)ob;
  916. if (!ob.IsAttackTarget(Owner)) continue;
  917. if (playerob.GMGameMaster || ob.Hidden && (!CoolEye || Level < ob.Level)) continue;
  918. if (Target != ob && Owner.LastHitter != ob && ob.LastHitter != Owner) continue;
  919. Target = ob;
  920. if (Owner != null)
  921. {
  922. for (int j = 0; j < playerob.Pets.Count; j++)
  923. {
  924. MonsterObject pet = playerob.Pets[j];
  925. if (!pet.IsAttackTarget(this)) continue;
  926. Target = pet;
  927. break;
  928. }
  929. }
  930. return;
  931. default:
  932. continue;
  933. }
  934. }
  935. }
  936. }
  937. }
  938. }
  939. public override bool IsAttackTarget(HumanObject attacker)
  940. {
  941. if (Owner == null) return false;
  942. if (Dead) return false;
  943. return Owner.IsAttackTarget(attacker);
  944. }
  945. public override bool IsAttackTarget(MonsterObject attacker)
  946. {
  947. if (Owner == null) return false;
  948. if (Dead) return false;
  949. return Owner.IsAttackTarget(attacker);
  950. }
  951. public override bool IsFriendlyTarget(HumanObject ally)
  952. {
  953. if (Owner == null) return false;
  954. return Owner.IsFriendlyTarget(ally);
  955. }
  956. public override bool IsFriendlyTarget(MonsterObject ally)
  957. {
  958. if (Owner == null) return false;
  959. return Owner.IsFriendlyTarget(ally);
  960. }
  961. public override void WinExp(uint amount, uint targetLevel = 0)
  962. {
  963. Owner.WinExp(amount, targetLevel);
  964. }
  965. public override void GainExp(uint amount)
  966. {
  967. if (amount == 0) return;
  968. if (CurrentMap?.Info?.NoExperience == true) return;
  969. for (int i = 0; i < Pets.Count; i++)
  970. {
  971. MonsterObject monster = Pets[i];
  972. if (monster.CurrentMap == CurrentMap && Functions.InRange(monster.CurrentLocation, CurrentLocation, Globals.DataRange) && !monster.Dead)
  973. monster.PetExp(amount);
  974. }
  975. if (!CanGainExp) return;
  976. if (Stats[Stat.ExpRatePercent] > 0)
  977. {
  978. amount += (uint)Math.Max(0, (amount * Stats[Stat.ExpRatePercent]) / 100);
  979. }
  980. Experience += amount;
  981. Owner.Enqueue(new S.GainHeroExperience { Amount = amount });
  982. if (Experience < MaxExperience) return;
  983. if (Level >= ushort.MaxValue) return;
  984. //Calculate increased levels
  985. var experience = Experience;
  986. while (experience >= MaxExperience)
  987. {
  988. Level++;
  989. experience -= MaxExperience;
  990. RefreshLevelStats();
  991. if (Level >= ushort.MaxValue) break;
  992. }
  993. Experience = experience;
  994. LevelUp();
  995. }
  996. private void SendInfo()
  997. {
  998. GetItemInfo();
  999. S.HeroInformation packet = new S.HeroInformation
  1000. {
  1001. ObjectID = ObjectID,
  1002. Name = Name,
  1003. Class = Class,
  1004. Gender = Gender,
  1005. Level = Level,
  1006. Hair = Hair,
  1007. HP = HP,
  1008. MP = MP,
  1009. Experience = Experience,
  1010. MaxExperience = MaxExperience,
  1011. Inventory = new UserItem[Info.Inventory.Length],
  1012. Equipment = new UserItem[Info.Equipment.Length],
  1013. AutoPot = AutoPot,
  1014. AutoHPPercent = AutoHPPercent,
  1015. AutoMPPercent = AutoMPPercent,
  1016. HPItemIndex = HPItemIndex,
  1017. MPItemIndex = MPItemIndex
  1018. };
  1019. for (int i = 0; i < Info.Magics.Count; i++)
  1020. packet.Magics.Add(Info.Magics[i].CreateClientMagic());
  1021. Info.Inventory.CopyTo(packet.Inventory, 0);
  1022. Info.Equipment.CopyTo(packet.Equipment, 0);
  1023. Owner.Enqueue(packet);
  1024. SendBaseStats();
  1025. }
  1026. public override void SendHealth(HumanObject player)
  1027. {
  1028. byte time = Math.Min(byte.MaxValue, (byte)Math.Max(5, (RevTime - Envir.Time) / 1000));
  1029. Packet p = new S.ObjectHealth { ObjectID = ObjectID, Percent = PercentHealth, Expire = time };
  1030. if (Envir.Time < RevTime)
  1031. {
  1032. CurrentMap.Broadcast(p, CurrentLocation);
  1033. return;
  1034. }
  1035. Owner.Enqueue(p);
  1036. if (Owner.GroupMembers != null)
  1037. {
  1038. for (int i = 0; i < Owner.GroupMembers.Count; i++)
  1039. {
  1040. PlayerObject member = Owner.GroupMembers[i];
  1041. if (Master == member) continue;
  1042. if (member.CurrentMap != CurrentMap || !Functions.InRange(member.CurrentLocation, CurrentLocation, Globals.DataRange)) continue;
  1043. member.Enqueue(p);
  1044. }
  1045. }
  1046. }
  1047. protected override void SendBaseStats()
  1048. {
  1049. Owner.Enqueue(new S.HeroBaseStatsInfo { Stats = Settings.HeroBaseStats[(byte)Class] });
  1050. }
  1051. protected override void RefreshLevelStats()
  1052. {
  1053. RefreshMaxExperience();
  1054. foreach (var stat in Settings.HeroBaseStats[(byte)Class].Stats)
  1055. {
  1056. Stats[stat.Type] = stat.Calculate(Class, Level);
  1057. }
  1058. }
  1059. public override void RefreshMaxExperience()
  1060. {
  1061. MaxExperience = Level < Settings.HeroExperienceList.Count ? Settings.HeroExperienceList[Level - 1] : 0;
  1062. }
  1063. public override Packet GetInfo()
  1064. {
  1065. return new S.ObjectHero
  1066. {
  1067. ObjectID = ObjectID,
  1068. Name = CurrentMap.Info.NoNames ? "?????" : Name,
  1069. NameColour = NameColour,
  1070. Class = Class,
  1071. Gender = Gender,
  1072. Level = Level,
  1073. Location = CurrentLocation,
  1074. Direction = Direction,
  1075. Hair = Hair,
  1076. Weapon = Looks_Weapon,
  1077. WeaponEffect = Looks_WeaponEffect,
  1078. Armour = Looks_Armour,
  1079. Light = Light,
  1080. Poison = CurrentPoison,
  1081. Dead = Dead,
  1082. Hidden = Hidden,
  1083. Effect = HasBuff(BuffType.MagicShield, out _) ? SpellEffect.MagicShieldUp : HasBuff(BuffType.ElementalBarrier, out _) ? SpellEffect.ElementalBarrierUp : SpellEffect.None,
  1084. WingEffect = Looks_Wings,
  1085. MountType = Mount.MountType,
  1086. RidingMount = RidingMount,
  1087. TransformType = TransformType,
  1088. ElementOrbEffect = (uint)GetElementalOrbCount(),
  1089. ElementOrbLvl = (uint)ElementsLevel,
  1090. ElementOrbMax = (uint)Settings.OrbsExpList[Settings.OrbsExpList.Count - 1],
  1091. Buffs = Buffs.Where(d => d.Info.Visible).Select(e => e.Type).ToList(),
  1092. LevelEffects = LevelEffects,
  1093. OwnerName = Owner.Name
  1094. };
  1095. }
  1096. }
  1097. }