QuestInfo.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. using Server.MirObjects;
  2. using System.Text.RegularExpressions;
  3. using Server.MirEnvir;
  4. namespace Server.MirDatabase
  5. {
  6. public class QuestInfo
  7. {
  8. protected static Envir Envir
  9. {
  10. get { return Envir.Main; }
  11. }
  12. protected static Envir EditEnvir
  13. {
  14. get { return Envir.Edit; }
  15. }
  16. protected static MessageQueue MessageQueue
  17. {
  18. get { return MessageQueue.Instance; }
  19. }
  20. public int Index;
  21. public uint NpcIndex;
  22. public NPCInfo NpcInfo;
  23. private uint _finishNpcIndex;
  24. public uint FinishNpcIndex
  25. {
  26. get { return _finishNpcIndex == 0 ? NpcIndex : _finishNpcIndex; }
  27. set { _finishNpcIndex = value; }
  28. }
  29. public NPCObject FinishNPC
  30. {
  31. get
  32. {
  33. return Envir.NPCs.Single(x => x.ObjectID == FinishNpcIndex);
  34. }
  35. }
  36. public string
  37. Name = string.Empty,
  38. Group = string.Empty,
  39. FileName = string.Empty,
  40. GotoMessage = string.Empty,
  41. KillMessage = string.Empty,
  42. ItemMessage = string.Empty,
  43. FlagMessage = string.Empty;
  44. public List<string> Description = new List<string>();
  45. public List<string> TaskDescription = new List<string>();
  46. public List<string> ReturnDescription = new List<string>();
  47. public List<string> CompletionDescription = new List<string>();
  48. public int RequiredMinLevel, RequiredMaxLevel, RequiredQuest;
  49. public RequiredClass RequiredClass = RequiredClass.None;
  50. public QuestType Type;
  51. public int TimeLimitInSeconds = 0;
  52. public List<QuestItemTask> CarryItems = new List<QuestItemTask>();
  53. public List<QuestKillTask> KillTasks = new List<QuestKillTask>();
  54. public List<QuestItemTask> ItemTasks = new List<QuestItemTask>();
  55. public List<QuestFlagTask> FlagTasks = new List<QuestFlagTask>();
  56. //TODO: ZoneTasks
  57. //TODO: EscortTasks
  58. public uint GoldReward;
  59. public uint ExpReward;
  60. public uint CreditReward;
  61. public List<QuestItemReward> FixedRewards = new List<QuestItemReward>();
  62. public List<QuestItemReward> SelectRewards = new List<QuestItemReward>();
  63. private Regex _regexMessage = new Regex("\"([^\"]*)\"");
  64. public QuestInfo() { }
  65. public QuestInfo(BinaryReader reader)
  66. {
  67. Index = reader.ReadInt32();
  68. Name = reader.ReadString();
  69. Group = reader.ReadString();
  70. FileName = reader.ReadString();
  71. RequiredMinLevel = reader.ReadInt32();
  72. RequiredMaxLevel = reader.ReadInt32();
  73. if (RequiredMaxLevel == 0) RequiredMaxLevel = ushort.MaxValue;
  74. RequiredQuest = reader.ReadInt32();
  75. RequiredClass = (RequiredClass)reader.ReadByte();
  76. Type = (QuestType)reader.ReadByte();
  77. GotoMessage = reader.ReadString();
  78. KillMessage = reader.ReadString();
  79. ItemMessage = reader.ReadString();
  80. FlagMessage = reader.ReadString();
  81. if (Envir.LoadVersion > 90)
  82. {
  83. TimeLimitInSeconds = reader.ReadInt32();
  84. }
  85. LoadInfo();
  86. }
  87. public void Save(BinaryWriter writer)
  88. {
  89. writer.Write(Index);
  90. writer.Write(Name);
  91. writer.Write(Group);
  92. writer.Write(FileName);
  93. writer.Write(RequiredMinLevel);
  94. writer.Write(RequiredMaxLevel);
  95. writer.Write(RequiredQuest);
  96. writer.Write((byte)RequiredClass);
  97. writer.Write((byte)Type);
  98. writer.Write(GotoMessage);
  99. writer.Write(KillMessage);
  100. writer.Write(ItemMessage);
  101. writer.Write(FlagMessage);
  102. writer.Write(TimeLimitInSeconds);
  103. }
  104. public void LoadInfo(bool clear = false)
  105. {
  106. if (clear) ClearInfo();
  107. if (!Directory.Exists(Settings.QuestPath)) return;
  108. string fileName = Path.Combine(Settings.QuestPath, FileName + ".txt");
  109. if (File.Exists(fileName))
  110. {
  111. List<string> lines = File.ReadAllLines(fileName).ToList();
  112. ParseFile(lines);
  113. }
  114. else
  115. MessageQueue.Enqueue(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.FileNotFoundQuest), fileName, Name));
  116. }
  117. public void ClearInfo()
  118. {
  119. Description.Clear();
  120. KillTasks = new List<QuestKillTask>();
  121. ItemTasks = new List<QuestItemTask>();
  122. FlagTasks = new List<QuestFlagTask>();
  123. FixedRewards = new List<QuestItemReward>();
  124. SelectRewards = new List<QuestItemReward>();
  125. ExpReward = 0;
  126. GoldReward = 0;
  127. CreditReward = 0;
  128. }
  129. public void ParseFile(List<string> lines)
  130. {
  131. const string
  132. descriptionCollectKey = "[@DESCRIPTION]",
  133. descriptionTaskKey = "[@TASKDESCRIPTION]",
  134. descriptionReturnKey = "[@RETURNDESCRIPTION]",
  135. descriptionCompletionKey = "[@COMPLETION]",
  136. carryItemsKey = "[@CARRYITEMS]",
  137. killTasksKey = "[@KILLTASKS]",
  138. itemTasksKey = "[@ITEMTASKS]",
  139. flagTasksKey = "[@FLAGTASKS]",
  140. fixedRewardsKey = "[@FIXEDREWARDS]",
  141. selectRewardsKey = "[@SELECTREWARDS]",
  142. expRewardKey = "[@EXPREWARD]",
  143. goldRewardKey = "[@GOLDREWARD]",
  144. creditRewardKey = "[@CREDITREWARD]";
  145. List<string> headers = new List<string>
  146. {
  147. descriptionCollectKey, descriptionTaskKey, descriptionCompletionKey,
  148. carryItemsKey, killTasksKey, itemTasksKey, flagTasksKey,
  149. fixedRewardsKey, selectRewardsKey, expRewardKey, goldRewardKey, creditRewardKey, descriptionReturnKey
  150. };
  151. int currentHeader = 0;
  152. while (currentHeader < headers.Count)
  153. {
  154. for (int i = 0; i < lines.Count; i++)
  155. {
  156. string line = lines[i].ToUpper();
  157. if (line != headers[currentHeader].ToUpper()) continue;
  158. for (int j = i + 1; j < lines.Count; j++)
  159. {
  160. string innerLine = lines[j];
  161. if (innerLine.StartsWith("[") || innerLine.StartsWith("//")) break;
  162. if (string.IsNullOrEmpty(lines[j])) continue;
  163. switch (line)
  164. {
  165. case descriptionCollectKey:
  166. Description.Add(innerLine);
  167. break;
  168. case descriptionTaskKey:
  169. TaskDescription.Add(innerLine);
  170. break;
  171. case descriptionReturnKey:
  172. ReturnDescription.Add(innerLine);
  173. break;
  174. case descriptionCompletionKey:
  175. CompletionDescription.Add(innerLine);
  176. break;
  177. case carryItemsKey:
  178. QuestItemTask t = ParseItem(innerLine);
  179. if (t != null) CarryItems.Add(t);
  180. break;
  181. case killTasksKey:
  182. QuestKillTask t1 = ParseKill(innerLine);
  183. if (t1 != null) KillTasks.Add(t1);
  184. break;
  185. case itemTasksKey:
  186. QuestItemTask t2 = ParseItem(innerLine);
  187. if (t2 != null) ItemTasks.Add(t2);
  188. break;
  189. case flagTasksKey:
  190. QuestFlagTask t3 = ParseFlag(innerLine);
  191. if (t3 != null) FlagTasks.Add(t3);
  192. break;
  193. case fixedRewardsKey:
  194. {
  195. ParseReward(FixedRewards, innerLine);
  196. break;
  197. }
  198. case selectRewardsKey:
  199. {
  200. ParseReward(SelectRewards, innerLine);
  201. break;
  202. }
  203. case expRewardKey:
  204. uint.TryParse(innerLine, out ExpReward);
  205. break;
  206. case goldRewardKey:
  207. uint.TryParse(innerLine, out GoldReward);
  208. break;
  209. case creditRewardKey:
  210. uint.TryParse(innerLine, out CreditReward);
  211. break;
  212. }
  213. }
  214. }
  215. currentHeader++;
  216. }
  217. }
  218. public void ParseReward(List<QuestItemReward> list, string line)
  219. {
  220. if (line.Length < 1) return;
  221. string[] split = line.Split(' ');
  222. ushort count = 1;
  223. if (split.Length > 1) ushort.TryParse(split[1], out count);
  224. ItemInfo mInfo = Envir.GetItemInfo(split[0]);
  225. if (mInfo == null)
  226. {
  227. mInfo = Envir.GetItemInfo(split[0] + "(M)");
  228. if (mInfo != null) list.Add(new QuestItemReward() { Item = mInfo, Count = count });
  229. mInfo = Envir.GetItemInfo(split[0] + "(F)");
  230. if (mInfo != null) list.Add(new QuestItemReward() { Item = mInfo, Count = count });
  231. }
  232. else
  233. {
  234. list.Add(new QuestItemReward() { Item = mInfo, Count = count });
  235. }
  236. }
  237. public QuestKillTask ParseKill(string line)
  238. {
  239. if (line.Length < 1) return null;
  240. string[] split = line.Split(' ');
  241. int count = 1;
  242. string message = "";
  243. MonsterInfo mInfo = Envir.GetMonsterInfo(split[0]);
  244. if (split.Length > 1) int.TryParse(split[1], out count);
  245. var match = _regexMessage.Match(line);
  246. if (match.Success)
  247. {
  248. message = match.Groups[1].Captures[0].Value;
  249. }
  250. return mInfo == null ? null : new QuestKillTask() { Monster = mInfo, Count = count, Message = message };
  251. }
  252. public QuestItemTask ParseItem(string line)
  253. {
  254. if (line.Length < 1) return null;
  255. string[] split = line.Split(' ');
  256. ushort count = 1;
  257. string message = "";
  258. ItemInfo mInfo = Envir.GetItemInfo(split[0]);
  259. if (split.Length > 1) ushort.TryParse(split[1], out count);
  260. var match = _regexMessage.Match(line);
  261. if (match.Success)
  262. {
  263. message = match.Groups[1].Captures[0].Value;
  264. }
  265. //if (mInfo.StackSize <= 1)
  266. //{
  267. // //recursively add item if cant stack???
  268. //}
  269. return mInfo == null ? null : new QuestItemTask { Item = mInfo, Count = count, Message = message };
  270. }
  271. public QuestFlagTask ParseFlag(string line)
  272. {
  273. if (line.Length < 1) return null;
  274. string[] split = line.Split(' ');
  275. int number = -1;
  276. string message = "";
  277. int.TryParse(split[0], out number);
  278. if (number < 0 || number > Globals.FlagIndexCount - 1000) return null;
  279. var match = _regexMessage.Match(line);
  280. if (match.Success)
  281. {
  282. message = match.Groups[1].Captures[0].Value;
  283. }
  284. return new QuestFlagTask { Number = number, Message = message };
  285. }
  286. public bool CanAccept(PlayerObject player)
  287. {
  288. if (RequiredMinLevel > player.Level || RequiredMaxLevel < player.Level)
  289. return false;
  290. if (RequiredQuest > 0 && !player.CompletedQuests.Contains(RequiredQuest))
  291. return false;
  292. switch (player.Class)
  293. {
  294. case MirClass.Warrior:
  295. if (!RequiredClass.HasFlag(RequiredClass.Warrior))
  296. return false;
  297. break;
  298. case MirClass.Wizard:
  299. if (!RequiredClass.HasFlag(RequiredClass.Wizard))
  300. return false;
  301. break;
  302. case MirClass.Taoist:
  303. if (!RequiredClass.HasFlag(RequiredClass.Taoist))
  304. return false;
  305. break;
  306. case MirClass.Assassin:
  307. if (!RequiredClass.HasFlag(RequiredClass.Assassin))
  308. return false;
  309. break;
  310. case MirClass.Archer:
  311. if (!RequiredClass.HasFlag(RequiredClass.Archer))
  312. return false;
  313. break;
  314. }
  315. return true;
  316. }
  317. public ClientQuestInfo CreateClientQuestInfo(PlayerObject viewer = null)
  318. {
  319. var description = LinkFormatter.ReplacePlaceholders(Description, (type, index) => EnsureLinkInfo(viewer, type, index)) ?? new List<string>();
  320. var taskDescription = LinkFormatter.ReplacePlaceholders(TaskDescription, (type, index) => EnsureLinkInfo(viewer, type, index)) ?? new List<string>();
  321. var returnDescription = LinkFormatter.ReplacePlaceholders(ReturnDescription, (type, index) => EnsureLinkInfo(viewer, type, index)) ?? new List<string>();
  322. var completionDescription = LinkFormatter.ReplacePlaceholders(CompletionDescription, (type, index) => EnsureLinkInfo(viewer, type, index)) ?? new List<string>();
  323. return new ClientQuestInfo
  324. {
  325. Index = Index,
  326. NPCIndex = NpcIndex,
  327. FinishNPCIndex = FinishNpcIndex,
  328. Name = Name,
  329. Group = Group,
  330. Description = description,
  331. TaskDescription = taskDescription,
  332. ReturnDescription = returnDescription,
  333. CompletionDescription = completionDescription,
  334. MinLevelNeeded = RequiredMinLevel,
  335. MaxLevelNeeded = RequiredMaxLevel,
  336. ClassNeeded = RequiredClass,
  337. QuestNeeded = RequiredQuest,
  338. Type = Type,
  339. TimeLimitInSeconds = TimeLimitInSeconds,
  340. RewardGold = GoldReward,
  341. RewardExp = ExpReward,
  342. RewardCredit = CreditReward,
  343. RewardsFixedItem = FixedRewards,
  344. RewardsSelectItem = SelectRewards
  345. };
  346. }
  347. private void EnsureLinkInfo(PlayerObject viewer, string linkType, int index)
  348. {
  349. if (viewer?.Connection == null) return;
  350. switch (linkType)
  351. {
  352. case "ITEM":
  353. var itemInfo = Envir.GetItemInfo(index);
  354. if (itemInfo != null)
  355. viewer.Connection.CheckItemInfo(itemInfo);
  356. break;
  357. case "MONSTER":
  358. viewer.Connection.CheckMonsterInfo(index);
  359. break;
  360. case "NPC":
  361. viewer.Connection.CheckNPCInfo(index);
  362. break;
  363. }
  364. }
  365. public static void FromText(string text)
  366. {
  367. string[] data = text.Split(new[] { ',' });
  368. if (data.Length < 10) return;
  369. QuestInfo info;
  370. bool isNew = false;
  371. if (!int.TryParse(data[0], out var index))
  372. {
  373. index = -1;
  374. }
  375. if (index == -1 || (info = EditEnvir.QuestInfoList.FirstOrDefault(it => it.Index == index)) == null)
  376. {
  377. info = new QuestInfo() { Index = ++EditEnvir.QuestIndex };
  378. isNew = true;
  379. }
  380. info.Name = data[1];
  381. info.Group = data[2];
  382. byte temp;
  383. byte.TryParse(data[3], out temp);
  384. info.Type = (QuestType)temp;
  385. info.FileName = data[4];
  386. info.GotoMessage = data[5];
  387. info.KillMessage = data[6];
  388. info.ItemMessage = data[7];
  389. info.FlagMessage = data[8];
  390. int.TryParse(data[9], out info.RequiredMinLevel);
  391. int.TryParse(data[10], out info.RequiredMaxLevel);
  392. int.TryParse(data[11], out info.RequiredQuest);
  393. byte.TryParse(data[12], out temp);
  394. info.RequiredClass = (RequiredClass)temp;
  395. if (isNew) EditEnvir.QuestInfoList.Add(info);
  396. }
  397. public string ToText()
  398. {
  399. return string.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12}",
  400. Index, Name, Group, (byte)Type, FileName, GotoMessage, KillMessage, ItemMessage, FlagMessage, RequiredMinLevel, RequiredMaxLevel, RequiredQuest, (byte)RequiredClass);
  401. }
  402. public override string ToString()
  403. {
  404. return string.Format("{0}: {1}", Index, Name);
  405. }
  406. }
  407. public class QuestKillTask
  408. {
  409. public MonsterInfo Monster;
  410. public int Count;
  411. public string Message;
  412. }
  413. public class QuestItemTask
  414. {
  415. public ItemInfo Item;
  416. public ushort Count;
  417. public string Message;
  418. }
  419. public class QuestFlagTask
  420. {
  421. public int Number;
  422. public string Message;
  423. }
  424. }