MonsterInfo.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. using System.Text.RegularExpressions;
  2. using Server.MirEnvir;
  3. namespace Server.MirDatabase
  4. {
  5. public class MonsterInfo
  6. {
  7. protected static Envir Envir
  8. {
  9. get { return Envir.Main; }
  10. }
  11. protected static Envir EditEnvir
  12. {
  13. get { return Envir.Edit; }
  14. }
  15. protected static MessageQueue MessageQueue
  16. {
  17. get { return MessageQueue.Instance; }
  18. }
  19. public int Index;
  20. public string Name = string.Empty;
  21. public Monster Image;
  22. public byte AI, Effect, ViewRange = 7, CoolEye;
  23. public ushort Level;
  24. public byte Light;
  25. public ushort AttackSpeed = 2500, MoveSpeed = 1800;
  26. public uint Experience;
  27. public string DropPath = "";
  28. public List<DropInfo> Drops = new List<DropInfo>();
  29. public bool CanTame = true, CanPush = true, AutoRev = true, Undead = false;
  30. public bool CanRecall = false;
  31. public bool IsBoss = false;
  32. public bool HasSpawnScript;
  33. public bool HasDieScript;
  34. public Stats Stats;
  35. public MonsterInfo()
  36. {
  37. Stats = new Stats();
  38. }
  39. public MonsterInfo(BinaryReader reader)
  40. {
  41. Index = reader.ReadInt32();
  42. Name = reader.ReadString();
  43. Image = (Monster) reader.ReadUInt16();
  44. AI = reader.ReadByte();
  45. Effect = reader.ReadByte();
  46. if (Envir.LoadVersion < 62)
  47. {
  48. Level = (ushort)reader.ReadByte();
  49. }
  50. else
  51. {
  52. Level = reader.ReadUInt16();
  53. }
  54. ViewRange = reader.ReadByte();
  55. CoolEye = reader.ReadByte();
  56. if (Envir.LoadVersion > 84)
  57. {
  58. Stats = new Stats(reader);
  59. }
  60. if (Envir.LoadVersion <= 84)
  61. {
  62. Stats = new Stats();
  63. Stats[Stat.HP] = (int)reader.ReadUInt32(); //Monster form prevented greater than ushort, so this should never overflow.
  64. }
  65. if (Envir.LoadVersion < 62)
  66. {
  67. Stats[Stat.MinAC] = reader.ReadByte();
  68. Stats[Stat.MaxAC] = reader.ReadByte();
  69. Stats[Stat.MinMAC] = reader.ReadByte();
  70. Stats[Stat.MaxMAC] = reader.ReadByte();
  71. Stats[Stat.MinDC] = reader.ReadByte();
  72. Stats[Stat.MaxDC] = reader.ReadByte();
  73. Stats[Stat.MinMC] = reader.ReadByte();
  74. Stats[Stat.MaxMC] = reader.ReadByte();
  75. Stats[Stat.MinSC] = reader.ReadByte();
  76. Stats[Stat.MaxSC] = reader.ReadByte();
  77. }
  78. else
  79. {
  80. if (Envir.LoadVersion <= 84)
  81. {
  82. Stats[Stat.MinAC] = reader.ReadUInt16();
  83. Stats[Stat.MaxAC] = reader.ReadUInt16();
  84. Stats[Stat.MinMAC] = reader.ReadUInt16();
  85. Stats[Stat.MaxMAC] = reader.ReadUInt16();
  86. Stats[Stat.MinDC] = reader.ReadUInt16();
  87. Stats[Stat.MaxDC] = reader.ReadUInt16();
  88. Stats[Stat.MinMC] = reader.ReadUInt16();
  89. Stats[Stat.MaxMC] = reader.ReadUInt16();
  90. Stats[Stat.MinSC] = reader.ReadUInt16();
  91. Stats[Stat.MaxSC] = reader.ReadUInt16();
  92. }
  93. }
  94. if (Envir.LoadVersion <= 84)
  95. {
  96. Stats[Stat.Accuracy] = reader.ReadByte();
  97. Stats[Stat.Agility] = reader.ReadByte();
  98. }
  99. Light = reader.ReadByte();
  100. AttackSpeed = reader.ReadUInt16();
  101. MoveSpeed = reader.ReadUInt16();
  102. Experience = reader.ReadUInt32();
  103. CanPush = reader.ReadBoolean();
  104. CanTame = reader.ReadBoolean();
  105. if (Envir.LoadVersion < 18) return;
  106. AutoRev = reader.ReadBoolean();
  107. Undead = reader.ReadBoolean();
  108. if (Envir.LoadVersion < 89) return;
  109. DropPath = reader.ReadString();
  110. if (Envir.LoadVersion >= 115)
  111. {
  112. CanRecall = reader.ReadBoolean();
  113. }
  114. if (Envir.LoadVersion >= 116)
  115. {
  116. IsBoss = reader.ReadBoolean();
  117. }
  118. }
  119. public string GameName
  120. {
  121. get { return Regex.Replace(Name, @"[\d-]", string.Empty); }
  122. }
  123. public ClientMonsterInfo ClientInformation
  124. {
  125. get
  126. {
  127. Stats tooltipStats = new Stats();
  128. tooltipStats[Stat.HP] = Stats[Stat.HP];
  129. tooltipStats[Stat.MinAC] = Stats[Stat.MinAC];
  130. tooltipStats[Stat.MaxAC] = Stats[Stat.MaxAC];
  131. tooltipStats[Stat.MinMAC] = Stats[Stat.MinMAC];
  132. tooltipStats[Stat.MaxMAC] = Stats[Stat.MaxMAC];
  133. tooltipStats[Stat.MinDC] = Stats[Stat.MinDC];
  134. tooltipStats[Stat.MaxDC] = Stats[Stat.MaxDC];
  135. tooltipStats[Stat.MinMC] = Stats[Stat.MinMC];
  136. tooltipStats[Stat.MaxMC] = Stats[Stat.MaxMC];
  137. tooltipStats[Stat.MinSC] = Stats[Stat.MinSC];
  138. tooltipStats[Stat.MaxSC] = Stats[Stat.MaxSC];
  139. return new ClientMonsterInfo
  140. {
  141. Index = Index,
  142. Name = Name,
  143. GameName = GameName,
  144. Image = Image,
  145. AI = AI,
  146. Effect = Effect,
  147. ViewRange = ViewRange,
  148. CoolEye = CoolEye,
  149. Level = Level,
  150. Light = Light,
  151. AttackSpeed = AttackSpeed,
  152. MoveSpeed = MoveSpeed,
  153. Experience = Experience,
  154. CanTame = CanTame,
  155. CanPush = CanPush,
  156. AutoRev = AutoRev,
  157. Undead = Undead,
  158. Stats = tooltipStats
  159. };
  160. }
  161. }
  162. public void Save(BinaryWriter writer)
  163. {
  164. writer.Write(Index);
  165. writer.Write(Name);
  166. writer.Write((ushort) Image);
  167. writer.Write(AI);
  168. writer.Write(Effect);
  169. writer.Write(Level);
  170. writer.Write(ViewRange);
  171. writer.Write(CoolEye);
  172. Stats.Save(writer);
  173. writer.Write(Light);
  174. writer.Write(AttackSpeed);
  175. writer.Write(MoveSpeed);
  176. writer.Write(Experience);
  177. writer.Write(CanPush);
  178. writer.Write(CanTame);
  179. writer.Write(AutoRev);
  180. writer.Write(Undead);
  181. writer.Write(DropPath);
  182. writer.Write(CanRecall);
  183. writer.Write(IsBoss);
  184. }
  185. public static void FromText(string text)
  186. {
  187. string[] data = text.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
  188. if (data.Length < 28) return; //28
  189. MonsterInfo info = new MonsterInfo {Name = data[0]};
  190. ushort image;
  191. if (!ushort.TryParse(data[1], out image)) return;
  192. info.Image = (Monster) image;
  193. if (!byte.TryParse(data[2], out info.AI)) return;
  194. if (!byte.TryParse(data[3], out info.Effect)) return;
  195. if (!ushort.TryParse(data[4], out info.Level)) return;
  196. if (!byte.TryParse(data[5], out info.ViewRange)) return;
  197. //if (!int.TryParse(data[6], out info.HP)) return;
  198. //if (!ushort.TryParse(data[7], out info.MinAC)) return;
  199. //if (!ushort.TryParse(data[8], out info.MaxAC)) return;
  200. //if (!ushort.TryParse(data[9], out info.MinMAC)) return;
  201. //if (!ushort.TryParse(data[10], out info.MaxMAC)) return;
  202. //if (!ushort.TryParse(data[11], out info.MinDC)) return;
  203. //if (!ushort.TryParse(data[12], out info.MaxDC)) return;
  204. //if (!ushort.TryParse(data[13], out info.MinMC)) return;
  205. //if (!ushort.TryParse(data[14], out info.MaxMC)) return;
  206. //if (!ushort.TryParse(data[15], out info.MinSC)) return;
  207. //if (!ushort.TryParse(data[16], out info.MaxSC)) return;
  208. //if (!byte.TryParse(data[17], out info.Accuracy)) return;
  209. //if (!byte.TryParse(data[18], out info.Agility)) return;
  210. if (!byte.TryParse(data[19], out info.Light)) return;
  211. if (!ushort.TryParse(data[20], out info.AttackSpeed)) return;
  212. if (!ushort.TryParse(data[21], out info.MoveSpeed)) return;
  213. if (!uint.TryParse(data[22], out info.Experience)) return;
  214. if (!bool.TryParse(data[23], out info.CanTame)) return;
  215. if (!bool.TryParse(data[24], out info.CanPush)) return;
  216. if (!bool.TryParse(data[25], out info.AutoRev)) return;
  217. if (!bool.TryParse(data[26], out info.Undead)) return;
  218. if (!byte.TryParse(data[27], out info.CoolEye)) return;
  219. //int count;
  220. //if (!int.TryParse(data[27], out count)) return;
  221. //if (28 + count * 3 > data.Length) return;
  222. info.Index = ++EditEnvir.MonsterIndex;
  223. EditEnvir.MonsterInfoList.Add(info);
  224. }
  225. public string ToText()
  226. {
  227. return "";// string.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},{17},{18},{19},{20},{21},{22},{23},{24},{25},{26},{27}", Name, (ushort)Image, AI, Effect, Level, ViewRange,
  228. // HP,
  229. //MinAC, MaxAC, MinMAC, MaxMAC, MinDC, MaxDC, MinMC, MaxMC, MinSC, MaxSC,
  230. // Accuracy, Agility, Light, AttackSpeed, MoveSpeed, Experience, CanTame, CanPush, AutoRev, Undead, CoolEye);
  231. }
  232. public override string ToString()
  233. {
  234. return string.Format("{0}: {1}", Index, Name);
  235. //return string.Format("{0}", Name);
  236. }
  237. }
  238. public class DropRewardInfo
  239. {
  240. public List<ItemInfo> Items;
  241. public uint Gold;
  242. }
  243. public class GroupDropInfo : List<DropInfo>
  244. {
  245. public bool Random;
  246. public bool First;
  247. }
  248. public class DropInfo
  249. {
  250. protected static Envir Envir
  251. {
  252. get { return Envir.Main; }
  253. }
  254. protected static MessageQueue MessageQueue
  255. {
  256. get { return MessageQueue.Instance; }
  257. }
  258. public int Chance;
  259. public ItemInfo Item;
  260. public uint Gold;
  261. public GroupDropInfo GroupedDrop;
  262. public byte Type;
  263. public bool QuestRequired;
  264. public static DropInfo FromLine(string s)
  265. {
  266. string[] parts = s.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries);
  267. if (parts.Length < 2)
  268. {
  269. return null;
  270. }
  271. DropInfo info = new DropInfo();
  272. if (!int.TryParse(parts[0].Substring(2), out info.Chance)) return null;
  273. if (string.Compare(parts[1], "Gold", StringComparison.OrdinalIgnoreCase) == 0)
  274. {
  275. if (parts.Length < 3) return null;
  276. if (!uint.TryParse(parts[2], out info.Gold) || info.Gold == 0) return null;
  277. }
  278. else if (parts[1].ToUpper().StartsWith("GROUP"))
  279. {
  280. info.GroupedDrop = new GroupDropInfo
  281. {
  282. Random = parts[1].EndsWith("*"),
  283. First = parts[1].EndsWith("^")
  284. };
  285. }
  286. else
  287. {
  288. info.Item = Envir.GetItemInfo(parts[1]);
  289. if (info.Item == null) return null;
  290. if (parts.Length > 2)
  291. {
  292. string dropRequirement = parts[2];
  293. if (dropRequirement.ToUpper() == "Q")
  294. {
  295. info.QuestRequired = true;
  296. }
  297. }
  298. }
  299. return info;
  300. }
  301. public static void Load(List<DropInfo> list, string name, string path, byte type = 0, bool createIfNotExists = true)
  302. {
  303. if (!File.Exists(path))
  304. {
  305. if (createIfNotExists)
  306. {
  307. string[] contents = new[]
  308. {
  309. ";Pots + Other", string.Empty, string.Empty,
  310. ";Weapons", string.Empty, string.Empty,
  311. ";Armour", string.Empty, string.Empty,
  312. ";Helmets", string.Empty, string.Empty,
  313. ";Necklace", string.Empty, string.Empty,
  314. ";Bracelets", string.Empty, string.Empty,
  315. ";Rings", string.Empty, string.Empty,
  316. ";Shoes", string.Empty, string.Empty,
  317. ";Belts", string.Empty, string.Empty,
  318. ";Stone",
  319. };
  320. File.WriteAllLines(path, contents);
  321. }
  322. return;
  323. }
  324. var lines = File.ReadAllLines(path).ToList();
  325. lines = ParseInsert(lines);
  326. for (int i = 0; i < lines.Count; i++)
  327. {
  328. if (lines[i].StartsWith(";") || string.IsNullOrWhiteSpace(lines[i])) continue;
  329. DropInfo drop = DropInfo.FromLine(lines[i]);
  330. if (drop == null)
  331. {
  332. MessageQueue.Enqueue(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.CouldNotLoadDropLine), name, lines[i]));
  333. continue;
  334. }
  335. if (drop.GroupedDrop != null)
  336. {
  337. ParseGroup(drop, name, lines, i + 1);
  338. }
  339. drop.Type = type;
  340. list.Add(drop);
  341. }
  342. list.Sort((drop1, drop2) =>
  343. {
  344. if (drop1.Gold > 0 && drop2.Gold == 0)
  345. return 1;
  346. if (drop1.Gold == 0 && drop2.Gold > 0)
  347. return -1;
  348. if (drop1.Item == null || drop2.Item == null) return 0;
  349. return drop1.Item.Type.CompareTo(drop2.Item.Type);
  350. });
  351. }
  352. private static void ParseGroup(DropInfo parentDrop, string name, List<string> lines, int startLine)
  353. {
  354. bool start = false, finish = false;
  355. var drops = new List<DropInfo>();
  356. for (int j = startLine; j < lines.Count; j++)
  357. {
  358. var line = lines[j].Trim();
  359. lines[j] = "";
  360. if (line.Trim() == ("{"))
  361. {
  362. start = true;
  363. continue;
  364. }
  365. if (line.Trim() == ("}"))
  366. {
  367. finish = true;
  368. break;
  369. }
  370. if (line.StartsWith(";") || string.IsNullOrWhiteSpace(line)) continue;
  371. var drop = DropInfo.FromLine(line);
  372. if (drop == null)
  373. {
  374. MessageQueue.Enqueue(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.CouldNotLoadDropLine), name, line));
  375. continue;
  376. }
  377. if (drop.GroupedDrop != null)
  378. {
  379. ParseGroup(drop, name, lines, j + 1);
  380. }
  381. drops.Add(drop);
  382. }
  383. if (start && finish)
  384. {
  385. parentDrop.GroupedDrop.AddRange(drops);
  386. }
  387. }
  388. private static List<string> ParseInsert(List<string> lines)
  389. {
  390. Regex regex = new Regex($"#INSERT \\[(.*?)\\]", RegexOptions.IgnoreCase);
  391. List<string> newLines = new List<string>();
  392. for (int i = 0; i < lines.Count; i++)
  393. {
  394. var match = regex.Match(lines[i]);
  395. if (!match.Success) continue;
  396. var subPath = match.Groups[1].Value;
  397. string path = Path.Combine(Settings.DropPath, subPath);
  398. newLines = File.ReadAllLines(path).ToList();
  399. lines.AddRange(newLines);
  400. }
  401. lines.RemoveAll(str => str.ToUpper().StartsWith("#INSERT"));
  402. return lines;
  403. }
  404. public DropRewardInfo AttemptDrop(int itemDropRatePercentOffset = 0, int goldDropRatePercentOffset = 0)
  405. {
  406. int rate = (int)(Chance / (Settings.DropRate));
  407. if (itemDropRatePercentOffset > 0)
  408. {
  409. rate -= (rate * itemDropRatePercentOffset) / 100;
  410. }
  411. if (rate < 1) rate = 1;
  412. if (Envir.Random.Next(rate) != 0)
  413. {
  414. return null;
  415. }
  416. uint gold = 0;
  417. var items = new List<ItemInfo>();
  418. if (Gold > 0)
  419. {
  420. int lowerGoldRange = (int)(Gold / 2);
  421. int upperGoldRange = (int)(Gold + Gold / 2);
  422. if (goldDropRatePercentOffset > 0)
  423. {
  424. lowerGoldRange += (lowerGoldRange * goldDropRatePercentOffset) / 100;
  425. }
  426. if (lowerGoldRange > upperGoldRange) lowerGoldRange = upperGoldRange;
  427. gold = (uint)Envir.Random.Next(lowerGoldRange, upperGoldRange);
  428. }
  429. else if (Item != null)
  430. {
  431. items.Add(Item);
  432. }
  433. else if (GroupedDrop != null)
  434. {
  435. var tempItems = new List<ItemInfo>();
  436. foreach (var item in GroupedDrop)
  437. {
  438. var reward = item.AttemptDrop(itemDropRatePercentOffset, goldDropRatePercentOffset);
  439. if (reward != null)
  440. {
  441. gold += reward.Gold;
  442. tempItems.AddRange(reward.Items);
  443. if (GroupedDrop.First)
  444. {
  445. break;
  446. }
  447. }
  448. }
  449. if (GroupedDrop.Random)
  450. {
  451. if (tempItems.Count > 0)
  452. {
  453. items.Add(tempItems[Envir.Random.Next(tempItems.Count)]);
  454. }
  455. }
  456. else
  457. {
  458. items.AddRange(tempItems);
  459. }
  460. }
  461. return new DropRewardInfo
  462. {
  463. Gold = gold,
  464. Items = items
  465. };
  466. }
  467. }
  468. }