NPCScript.cs 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460
  1. using System.Drawing;
  2. using Server.MirDatabase;
  3. using Server.MirEnvir;
  4. using System.Text.RegularExpressions;
  5. using S = ServerPackets;
  6. namespace Server.MirObjects
  7. {
  8. public class NPCScript
  9. {
  10. protected static Envir Envir
  11. {
  12. get { return Envir.Main; }
  13. }
  14. protected static MessageQueue MessageQueue
  15. {
  16. get { return MessageQueue.Instance; }
  17. }
  18. public static NPCScript Get(int index)
  19. {
  20. return Envir.Scripts[index];
  21. }
  22. public static NPCScript GetOrAdd(uint loadedObjectID, string fileName, NPCScriptType type)
  23. {
  24. var script = Envir.Scripts.SingleOrDefault(x => x.Value.FileName.Equals(fileName, StringComparison.OrdinalIgnoreCase) && x.Value.LoadedObjectID == loadedObjectID).Value;
  25. if (script != null)
  26. {
  27. return script;
  28. }
  29. return new NPCScript(loadedObjectID, fileName, type);
  30. }
  31. public readonly int ScriptID;
  32. public readonly uint LoadedObjectID;
  33. public readonly NPCScriptType Type;
  34. public readonly string FileName;
  35. public const string
  36. MainKey = "[@MAIN]",
  37. BuyKey = "[@BUY]",
  38. SellKey = "[@SELL]",
  39. BuySellKey = "[@BUYSELL]",
  40. RepairKey = "[@REPAIR]",
  41. SRepairKey = "[@SREPAIR]",
  42. RefineKey = "[@REFINE]",
  43. RefineCheckKey = "[@REFINECHECK]",
  44. RefineCollectKey = "[@REFINECOLLECT]",
  45. ReplaceWedRingKey = "[@REPLACEWEDDINGRING]",
  46. BuyBackKey = "[@BUYBACK]",
  47. StorageKey = "[@STORAGE]",
  48. ConsignKey = "[@CONSIGN]",
  49. MarketKey = "[@MARKET]",
  50. CraftKey = "[@CRAFT]",
  51. GuildCreateKey = "[@CREATEGUILD]",
  52. RequestWarKey = "[@REQUESTWAR]",
  53. SendParcelKey = "[@SENDPARCEL]",
  54. CollectParcelKey = "[@COLLECTPARCEL]",
  55. AwakeningKey = "[@AWAKENING]",
  56. DisassembleKey = "[@DISASSEMBLE]",
  57. DowngradeKey = "[@DOWNGRADE]",
  58. ResetKey = "[@RESET]",
  59. PearlBuyKey = "[@PEARLBUY]",
  60. BuyUsedKey = "[@BUYUSED]",
  61. BuyNewKey = "[@BUYNEW]",
  62. BuySellNewKey = "[@BUYSELLNEW]",
  63. HeroCreateKey = "[@CREATEHERO]",
  64. HeroManageKey = "[@MANAGEHERO]",
  65. TradeKey = "[TRADE]",
  66. RecipeKey = "[RECIPE]",
  67. TypeKey = "[TYPES]",
  68. UsedTypeKey = "[USEDTYPES]",
  69. QuestKey = "[QUESTS]",
  70. SpeechKey = "[SPEECH]",
  71. GuildTerritoryKey = "[@GUILDTERRITORY]";
  72. public List<ItemType> Types = new List<ItemType>();
  73. public List<ItemType> UsedTypes = new List<ItemType>();
  74. public List<UserItem> Goods = new List<UserItem>();
  75. public List<RecipeInfo> CraftGoods = new List<RecipeInfo>();
  76. public List<NPCPage> NPCSections = new List<NPCPage>();
  77. public List<NPCPage> NPCPages = new List<NPCPage>();
  78. private NPCScript(uint loadedObjectID, string fileName, NPCScriptType type)
  79. {
  80. ScriptID = ++Envir.ScriptIndex;
  81. LoadedObjectID = loadedObjectID;
  82. FileName = fileName;
  83. Type = type;
  84. Load();
  85. Envir.Scripts.Add(ScriptID, this);
  86. }
  87. public void Load()
  88. {
  89. LoadInfo();
  90. LoadGoods();
  91. }
  92. public float PriceRate(PlayerObject player, bool baseRate = false)
  93. {
  94. var callingNPC = Envir.NPCs.SingleOrDefault(x => x.ObjectID == player.NPCObjectID);
  95. if (callingNPC == null)
  96. {
  97. return 1F;
  98. }
  99. if (callingNPC.Conq == null || baseRate)
  100. {
  101. return callingNPC.Info.Rate / 100F;
  102. }
  103. if (player.MyGuild != null && player.MyGuild.Guildindex == callingNPC.Conq.GuildInfo.Owner)
  104. {
  105. return callingNPC.Info.Rate / 100F;
  106. }
  107. else
  108. {
  109. return (((callingNPC.Info.Rate / 100F) * callingNPC.Conq.GuildInfo.NPCRate) + callingNPC.Info.Rate) / 100F;
  110. }
  111. }
  112. public void LoadInfo()
  113. {
  114. ClearInfo();
  115. if (!Directory.Exists(Settings.NPCPath)) return;
  116. string fileName = Path.Combine(Settings.NPCPath, FileName + ".txt");
  117. if (File.Exists(fileName))
  118. {
  119. List<string> lines = File.ReadAllLines(fileName).ToList();
  120. lines = ParseInsert(lines);
  121. lines = ParseInclude(lines);
  122. switch (Type)
  123. {
  124. case NPCScriptType.Normal:
  125. default:
  126. ParseScript(lines);
  127. break;
  128. case NPCScriptType.AutoPlayer:
  129. case NPCScriptType.AutoMonster:
  130. case NPCScriptType.Robot:
  131. ParseDefault(lines);
  132. break;
  133. }
  134. }
  135. else
  136. MessageQueue.Enqueue(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.ScriptNotFound), FileName));
  137. }
  138. public void ClearInfo()
  139. {
  140. Goods = new List<UserItem>();
  141. Types = new List<ItemType>();
  142. UsedTypes = new List<ItemType>();
  143. NPCPages = new List<NPCPage>();
  144. CraftGoods = new List<RecipeInfo>();
  145. if (Type == NPCScriptType.AutoPlayer)
  146. {
  147. Envir.CustomCommands.Clear();
  148. }
  149. }
  150. public void LoadGoods()
  151. {
  152. var loadedNPC = NPCObject.Get(LoadedObjectID);
  153. if (loadedNPC != null)
  154. {
  155. loadedNPC.UsedGoods.Clear();
  156. string path = Path.Combine(Settings.GoodsPath, loadedNPC.Info.Index.ToString() + ".msd");
  157. if (!File.Exists(path)) return;
  158. using (FileStream stream = File.OpenRead(path))
  159. {
  160. using (BinaryReader reader = new BinaryReader(stream))
  161. {
  162. int version = reader.ReadInt32();
  163. int count = version;
  164. int customversion = Envir.LoadCustomVersion;
  165. if (version == 9999)//the only real way to tell if the file was made before or after version code got added: assuming nobody had a config option to save more then 10000 sold items
  166. {
  167. version = reader.ReadInt32();
  168. customversion = reader.ReadInt32();
  169. count = reader.ReadInt32();
  170. }
  171. else
  172. version = Envir.LoadVersion;
  173. for (int k = 0; k < count; k++)
  174. {
  175. UserItem item = new UserItem(reader, version, customversion);
  176. if (Envir.BindItem(item))
  177. loadedNPC.UsedGoods.Add(item);
  178. }
  179. }
  180. }
  181. }
  182. }
  183. private void ParseDefault(List<string> lines)
  184. {
  185. for (int i = 0; i < lines.Count; i++)
  186. {
  187. if (!lines[i].ToUpper().StartsWith("[@_")) continue;
  188. if (Type == NPCScriptType.AutoPlayer)
  189. {
  190. if (lines[i].ToUpper().Contains("MAPCOORD"))
  191. {
  192. Regex regex = new Regex(@"\((.*?),([0-9]{1,3}),([0-9]{1,3})\)");
  193. Match match = regex.Match(lines[i]);
  194. if (!match.Success) continue;
  195. Map map = Envir.MapList.FirstOrDefault(m => m.Info.FileName == match.Groups[1].Value);
  196. if (map == null) continue;
  197. Point point = new Point(Convert.ToInt16(match.Groups[2].Value), Convert.ToInt16(match.Groups[3].Value));
  198. if (!map.Info.ActiveCoords.Contains(point))
  199. {
  200. map.Info.ActiveCoords.Add(point);
  201. }
  202. }
  203. if (lines[i].ToUpper().Contains("CUSTOMCOMMAND"))
  204. {
  205. Regex regex = new Regex(@"\((.*?)\)");
  206. Match match = regex.Match(lines[i]);
  207. if (!match.Success) continue;
  208. Envir.CustomCommands.Add(match.Groups[1].Value);
  209. }
  210. }
  211. else if (Type == NPCScriptType.AutoMonster)
  212. {
  213. MonsterInfo MobInfo;
  214. if (lines[i].ToUpper().Contains("SPAWN"))
  215. {
  216. Regex regex = new Regex(@"\((.*?)\)");
  217. Match match = regex.Match(lines[i]);
  218. if (!match.Success) continue;
  219. MobInfo = Envir.GetMonsterInfo(Convert.ToInt16(match.Groups[1].Value));
  220. if (MobInfo == null) continue;
  221. MobInfo.HasSpawnScript = true;
  222. }
  223. if (lines[i].ToUpper().Contains("DIE"))
  224. {
  225. Regex regex = new Regex(@"\((.*?)\)");
  226. Match match = regex.Match(lines[i]);
  227. if (!match.Success) continue;
  228. MobInfo = Envir.GetMonsterInfo(Convert.ToInt16(match.Groups[1].Value));
  229. if (MobInfo == null) continue;
  230. MobInfo.HasDieScript = true;
  231. }
  232. }
  233. else if (Type == NPCScriptType.Robot)
  234. {
  235. if (lines[i].ToUpper().Contains("TIME"))
  236. {
  237. Robot.AddRobot(lines[i].ToUpper());
  238. }
  239. }
  240. NPCPages.AddRange(ParsePages(lines, lines[i]));
  241. }
  242. }
  243. private void ParseScript(IList<string> lines)
  244. {
  245. NPCPages.AddRange(ParsePages(lines));
  246. ParseGoods(lines);
  247. ParseTypes(lines);
  248. ParseQuests(lines);
  249. ParseCrafting(lines);
  250. ParseSpeech(lines);
  251. }
  252. private List<string> ParseInsert(List<string> lines)
  253. {
  254. List<string> newLines = new List<string>();
  255. for (int i = 0; i < lines.Count; i++)
  256. {
  257. if (!lines[i].ToUpper().StartsWith("#INSERT")) continue;
  258. string[] split = lines[i].Split(' ');
  259. if (split.Length < 2) continue;
  260. string path = Path.Combine(Settings.EnvirPath, split[1].Substring(1, split[1].Length - 2));
  261. if (!File.Exists(path))
  262. MessageQueue.Enqueue(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.InsertScriptNotFound), path));
  263. else
  264. newLines = File.ReadAllLines(path).ToList();
  265. lines.AddRange(newLines);
  266. }
  267. lines.RemoveAll(str => str.ToUpper().StartsWith("#INSERT"));
  268. return lines;
  269. }
  270. private List<string> ParseInclude(List<string> lines)
  271. {
  272. for (int i = 0; i < lines.Count; i++)
  273. {
  274. if (!lines[i].ToUpper().StartsWith("#INCLUDE")) continue;
  275. string[] split = lines[i].Split(' ');
  276. string path = Path.Combine(Settings.EnvirPath, split[1].Substring(1, split[1].Length - 2));
  277. string page = ("[" + split[2] + "]").ToUpper();
  278. bool start = false, finish = false;
  279. var parsedLines = new List<string>();
  280. if (!File.Exists(path))
  281. {
  282. MessageQueue.Enqueue(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.IncludeScriptNotFound), path));
  283. return parsedLines;
  284. }
  285. IList<string> extLines = File.ReadAllLines(path);
  286. for (int j = 0; j < extLines.Count; j++)
  287. {
  288. if (!extLines[j].ToUpper().StartsWith(page)) continue;
  289. for (int x = j + 1; x < extLines.Count; x++)
  290. {
  291. if (extLines[x].Trim() == ("{"))
  292. {
  293. start = true;
  294. continue;
  295. }
  296. if (extLines[x].Trim() == ("}"))
  297. {
  298. finish = true;
  299. break;
  300. }
  301. parsedLines.Add(extLines[x]);
  302. }
  303. }
  304. if (start && finish)
  305. {
  306. lines.InsertRange(i + 1, parsedLines);
  307. parsedLines.Clear();
  308. }
  309. }
  310. lines.RemoveAll(str => str.ToUpper().StartsWith("#INCLUDE"));
  311. return lines;
  312. }
  313. private List<NPCPage> ParsePages(IList<string> lines, string key = MainKey)
  314. {
  315. List<NPCPage> pages = new List<NPCPage>();
  316. List<string> buttons = new List<string>();
  317. NPCPage page = ParsePage(lines, key);
  318. pages.Add(page);
  319. buttons.AddRange(page.Buttons);
  320. for (int i = 0; i < buttons.Count; i++)
  321. {
  322. string section = buttons[i];
  323. bool match = pages.Any(t => t.Key.ToUpper() == section.ToUpper());
  324. if (match) continue;
  325. page = ParsePage(lines, section);
  326. buttons.AddRange(page.Buttons);
  327. pages.Add(page);
  328. }
  329. return pages;
  330. }
  331. private NPCPage ParsePage(IList<string> scriptLines, string sectionName)
  332. {
  333. bool nextPage = false, nextSection = false;
  334. List<string> lines = scriptLines.Where(x => !string.IsNullOrEmpty(x)).ToList();
  335. NPCPage Page = new NPCPage(sectionName);
  336. //Cleans arguments out of search page name
  337. string tempSectionName = Page.ArgumentParse(sectionName);
  338. //parse all individual pages in a script, defined by sectionName
  339. for (int i = 0; i < lines.Count; i++)
  340. {
  341. string line = lines[i];
  342. if (line.StartsWith(";")) continue;
  343. if (!lines[i].ToUpper().StartsWith(tempSectionName.ToUpper())) continue;
  344. List<string> segmentLines = new List<string>();
  345. nextPage = false;
  346. //Found a page, now process that page and split it into segments
  347. for (int j = i + 1; j < lines.Count; j++)
  348. {
  349. string nextLine = lines[j];
  350. if (j < lines.Count - 1)
  351. nextLine = lines[j + 1];
  352. else
  353. nextLine = "";
  354. if (nextLine.StartsWith("[") && nextLine.EndsWith("]"))
  355. {
  356. nextPage = true;
  357. }
  358. else if (nextLine.StartsWith("#IF"))
  359. {
  360. nextSection = true;
  361. }
  362. if (nextSection || nextPage)
  363. {
  364. segmentLines.Add(lines[j]);
  365. //end of segment, so need to parse it and put into the segment list within the page
  366. if (segmentLines.Count > 0)
  367. {
  368. NPCSegment segment = ParseSegment(Page, segmentLines);
  369. List<string> currentButtons = new List<string>();
  370. currentButtons.AddRange(segment.Buttons);
  371. currentButtons.AddRange(segment.ElseButtons);
  372. currentButtons.AddRange(segment.GotoButtons);
  373. Page.Buttons.AddRange(currentButtons);
  374. Page.SegmentList.Add(segment);
  375. segmentLines.Clear();
  376. nextSection = false;
  377. }
  378. if (nextPage) break;
  379. continue;
  380. }
  381. segmentLines.Add(lines[j]);
  382. }
  383. //bottom of script reached, add all lines found to new segment
  384. if (segmentLines.Count > 0)
  385. {
  386. NPCSegment segment = ParseSegment(Page, segmentLines);
  387. List<string> currentButtons = new List<string>();
  388. currentButtons.AddRange(segment.Buttons);
  389. currentButtons.AddRange(segment.ElseButtons);
  390. currentButtons.AddRange(segment.GotoButtons);
  391. Page.Buttons.AddRange(currentButtons);
  392. Page.SegmentList.Add(segment);
  393. segmentLines.Clear();
  394. }
  395. return Page;
  396. }
  397. return Page;
  398. }
  399. private NPCSegment ParseSegment(NPCPage page, IEnumerable<string> scriptLines)
  400. {
  401. List<string>
  402. checks = new List<string>(),
  403. acts = new List<string>(),
  404. say = new List<string>(),
  405. buttons = new List<string>(),
  406. elseSay = new List<string>(),
  407. elseActs = new List<string>(),
  408. elseButtons = new List<string>(),
  409. gotoButtons = new List<string>();
  410. List<string> lines = scriptLines.ToList();
  411. List<string> currentSay = say, currentButtons = buttons;
  412. Regex regex = new Regex(@"<.*?/(\@.*?)>");
  413. for (int i = 0; i < lines.Count; i++)
  414. {
  415. if (string.IsNullOrEmpty(lines[i])) continue;
  416. if (lines[i].StartsWith(";")) continue;
  417. if (lines[i].StartsWith("#"))
  418. {
  419. string[] action = lines[i].Remove(0, 1).ToUpper().Trim().Split(' ');
  420. switch (action[0])
  421. {
  422. case "IF":
  423. currentSay = checks;
  424. currentButtons = null;
  425. continue;
  426. case "SAY":
  427. currentSay = say;
  428. currentButtons = buttons;
  429. continue;
  430. case "ACT":
  431. currentSay = acts;
  432. currentButtons = gotoButtons;
  433. continue;
  434. case "ELSESAY":
  435. currentSay = elseSay;
  436. currentButtons = elseButtons;
  437. continue;
  438. case "ELSEACT":
  439. currentSay = elseActs;
  440. currentButtons = gotoButtons;
  441. continue;
  442. default:
  443. throw new NotImplementedException();
  444. }
  445. }
  446. if (lines[i].StartsWith("[") && lines[i].EndsWith("]")) break;
  447. if (currentButtons != null)
  448. {
  449. Match match = regex.Match(lines[i]);
  450. while (match.Success)
  451. {
  452. string argu = match.Groups[1].Captures[0].Value;
  453. argu = argu.Split('/')[0];
  454. currentButtons.Add(string.Format("[{0}]", argu));
  455. match = match.NextMatch();
  456. }
  457. //Check if line has a goto command
  458. var parts = lines[i].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
  459. if (parts.Count() > 1)
  460. switch (parts[0].ToUpper())
  461. {
  462. case "GOTO":
  463. case "GROUPGOTO":
  464. gotoButtons.Add(string.Format("[{0}]", parts[1].ToUpper()));
  465. break;
  466. case "TIMERECALL":
  467. case "DELAYGOTO":
  468. case "TIMERECALLGROUP":
  469. if (parts.Length > 2)
  470. gotoButtons.Add(string.Format("[{0}]", parts[2].ToUpper()));
  471. break;
  472. case "ROLLDIE":
  473. case "ROLLYUT":
  474. buttons.Add(string.Format("[{0}]", parts[1].ToUpper()));
  475. break;
  476. }
  477. }
  478. currentSay.Add(lines[i].TrimEnd());
  479. }
  480. NPCSegment segment = new NPCSegment(page, say, buttons, elseSay, elseButtons, gotoButtons);
  481. for (int i = 0; i < checks.Count; i++)
  482. segment.ParseCheck(checks[i]);
  483. for (int i = 0; i < acts.Count; i++)
  484. segment.ParseAct(segment.ActList, acts[i]);
  485. for (int i = 0; i < elseActs.Count; i++)
  486. segment.ParseAct(segment.ElseActList, elseActs[i]);
  487. currentButtons = new List<string>();
  488. currentButtons.AddRange(buttons);
  489. currentButtons.AddRange(elseButtons);
  490. currentButtons.AddRange(gotoButtons);
  491. return segment;
  492. }
  493. private void ParseTypes(IList<string> lines)
  494. {
  495. for (int i = 0; i < lines.Count; i++)
  496. {
  497. if (!lines[i].ToUpper().StartsWith(TypeKey)) continue;
  498. while (++i < lines.Count)
  499. {
  500. if (String.IsNullOrEmpty(lines[i])) continue;
  501. if (!int.TryParse(lines[i], out int index)) break;
  502. Types.Add((ItemType)index);
  503. }
  504. }
  505. for (int i = 0; i < lines.Count; i++)
  506. {
  507. if (!lines[i].ToUpper().StartsWith(UsedTypeKey)) continue;
  508. while (++i < lines.Count)
  509. {
  510. if (String.IsNullOrEmpty(lines[i])) continue;
  511. if (!int.TryParse(lines[i], out int index)) break;
  512. UsedTypes.Add((ItemType)index);
  513. }
  514. }
  515. }
  516. private void ParseGoods(IList<string> lines)
  517. {
  518. for (int i = 0; i < lines.Count; i++)
  519. {
  520. if (!lines[i].ToUpper().StartsWith(TradeKey)) continue;
  521. while (++i < lines.Count)
  522. {
  523. if (lines[i].StartsWith("[")) return;
  524. if (String.IsNullOrEmpty(lines[i])) continue;
  525. var data = lines[i].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
  526. ItemInfo info = Envir.GetItemInfo(data[0]);
  527. if (info == null)
  528. continue;
  529. UserItem goods = Envir.CreateShopItem(info, (uint)i);
  530. if (goods == null || Goods.Contains(goods))
  531. {
  532. MessageQueue.Enqueue(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.CouldNotFindItemFile), lines[i], FileName));
  533. continue;
  534. }
  535. ushort count = 1;
  536. if (data.Length == 2)
  537. ushort.TryParse(data[1], out count);
  538. goods.Count = count;
  539. Goods.Add(goods);
  540. }
  541. }
  542. }
  543. private void ParseQuests(IList<string> lines)
  544. {
  545. for (int i = 0; i < lines.Count; i++)
  546. {
  547. if (!lines[i].ToUpper().StartsWith(QuestKey)) continue;
  548. var loadedNPC = NPCObject.Get(LoadedObjectID);
  549. if (loadedNPC == null)
  550. {
  551. return;
  552. }
  553. while (++i < lines.Count)
  554. {
  555. if (lines[i].StartsWith("[")) return;
  556. if (String.IsNullOrEmpty(lines[i])) continue;
  557. int.TryParse(lines[i], out int index);
  558. if (index == 0) continue;
  559. QuestInfo info = Envir.GetQuestInfo(Math.Abs(index));
  560. if (info == null) return;
  561. if (index > 0)
  562. info.NpcIndex = LoadedObjectID;
  563. else
  564. info.FinishNpcIndex = LoadedObjectID;
  565. if (loadedNPC.Quests.All(x => x != info))
  566. loadedNPC.Quests.Add(info);
  567. }
  568. }
  569. }
  570. private void ParseSpeech(IList<string> lines)
  571. {
  572. var loadedNPC = NPCObject.Get(LoadedObjectID);
  573. if (loadedNPC == null)
  574. {
  575. return;
  576. }
  577. for (int i = 0; i < lines.Count; i++)
  578. {
  579. if (!lines[i].ToUpper().StartsWith(SpeechKey)) continue;
  580. while (++i < lines.Count)
  581. {
  582. if (String.IsNullOrEmpty(lines[i])) continue;
  583. var parts = lines[i].Split(' ');
  584. if (parts.Length < 2 || !int.TryParse(parts[0], out int weight)) return;
  585. loadedNPC.Speech.Add(new NPCSpeech { Weight = weight, Message = lines[i].Substring(parts[0].Length + 1) });
  586. }
  587. }
  588. }
  589. private void ParseCrafting(IList<string> lines)
  590. {
  591. for (int i = 0; i < lines.Count; i++)
  592. {
  593. if (!lines[i].ToUpper().StartsWith(RecipeKey)) continue;
  594. while (++i < lines.Count)
  595. {
  596. if (lines[i].StartsWith("[")) return;
  597. if (String.IsNullOrEmpty(lines[i])) continue;
  598. var data = lines[i].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
  599. ItemInfo info = Envir.GetItemInfo(data[0]);
  600. if (info == null)
  601. continue;
  602. RecipeInfo recipe = Envir.RecipeInfoList.SingleOrDefault(x => x.MatchItem(info.Index));
  603. if (recipe == null)
  604. {
  605. MessageQueue.Enqueue(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.CouldNotFindRecipe), lines[i], FileName));
  606. continue;
  607. }
  608. if (recipe.Ingredients.Count == 0)
  609. {
  610. MessageQueue.Enqueue(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.CouldNotFindIngredients), lines[i], FileName));
  611. continue;
  612. }
  613. CraftGoods.Add(recipe);
  614. }
  615. }
  616. }
  617. public void Call(MonsterObject monster, string key)
  618. {
  619. key = key.ToUpper();
  620. for (int i = 0; i < NPCPages.Count; i++)
  621. {
  622. NPCPage page = NPCPages[i];
  623. if (!String.Equals(page.Key, key, StringComparison.CurrentCultureIgnoreCase)) continue;
  624. foreach (NPCSegment segment in page.SegmentList)
  625. {
  626. if (page.BreakFromSegments)
  627. {
  628. page.BreakFromSegments = false;
  629. break;
  630. }
  631. ProcessSegment(monster, page, segment);
  632. }
  633. }
  634. }
  635. public void Call(string key)
  636. {
  637. key = key.ToUpper();
  638. for (int i = 0; i < NPCPages.Count; i++)
  639. {
  640. NPCPage page = NPCPages[i];
  641. if (!String.Equals(page.Key, key, StringComparison.CurrentCultureIgnoreCase)) continue;
  642. foreach (NPCSegment segment in page.SegmentList)
  643. {
  644. if (page.BreakFromSegments)
  645. {
  646. page.BreakFromSegments = false;
  647. break;
  648. }
  649. ProcessSegment(page, segment);
  650. }
  651. }
  652. }
  653. public void Call(PlayerObject player, uint objectID, string key)
  654. {
  655. key = key.ToUpper();
  656. if (!player.NPCDelayed)
  657. {
  658. if (key != MainKey)
  659. {
  660. if (player.NPCObjectID != objectID) return;
  661. bool found = false;
  662. foreach (NPCSegment segment in player.NPCPage.SegmentList)
  663. {
  664. if (!player.NPCSuccess.TryGetValue(segment, out bool result)) break; //no result for segment ?
  665. if ((result ? segment.Buttons : segment.ElseButtons).Any(s => s.ToUpper() == key))
  666. {
  667. found = true;
  668. }
  669. }
  670. if (!found)
  671. {
  672. var npc = NPCObject.Get(objectID);
  673. string npcName = npc?.Info?.GameName ?? npc?.Name ?? "Unknown";
  674. MessageQueue.Enqueue(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.PlayerPreventedNpcAccess), player.Name, key, npcName));
  675. return;
  676. }
  677. }
  678. }
  679. else
  680. {
  681. player.NPCDelayed = false;
  682. }
  683. if (key.StartsWith("[@@") && !player.NPCData.TryGetValue("NPCInputStr", out object _npcInputStr))
  684. {
  685. //send off packet to request input
  686. player.Enqueue(new S.NPCRequestInput { NPCID = player.NPCObjectID, PageName = key });
  687. return;
  688. }
  689. for (int i = 0; i < NPCPages.Count; i++)
  690. {
  691. NPCPage page = NPCPages[i];
  692. if (!String.Equals(page.Key, key, StringComparison.CurrentCultureIgnoreCase)) continue;
  693. player.NPCSpeech = new List<string>();
  694. player.NPCSuccess.Clear();
  695. foreach (NPCSegment segment in page.SegmentList)
  696. {
  697. if (page.BreakFromSegments)
  698. {
  699. page.BreakFromSegments = false;
  700. break;
  701. }
  702. ProcessSegment(player, page, segment, objectID);
  703. }
  704. Response(player, page);
  705. }
  706. player.NPCData.Remove("NPCInputStr");
  707. }
  708. private void Response(PlayerObject player, NPCPage page)
  709. {
  710. player.Enqueue(new S.NPCResponse { Page = player.NPCSpeech });
  711. ProcessSpecial(player, page);
  712. }
  713. private void ProcessSegment(PlayerObject player, NPCPage page, NPCSegment segment, uint objectID)
  714. {
  715. player.NPCObjectID = objectID;
  716. player.NPCScriptID = ScriptID;
  717. player.NPCSuccess.Add(segment, segment.Check(player));
  718. player.NPCPage = page;
  719. }
  720. private void ProcessSegment(MonsterObject monster, NPCPage page, NPCSegment segment)
  721. {
  722. segment.Check(monster);
  723. }
  724. private void ProcessSegment(NPCPage page, NPCSegment segment)
  725. {
  726. segment.Check();
  727. }
  728. private void ProcessSpecial(PlayerObject player, NPCPage page)
  729. {
  730. List<UserItem> allGoods = new List<UserItem>();
  731. var key = page.Key.ToUpper();
  732. switch (key)
  733. {
  734. case BuyKey:
  735. case BuySellKey:
  736. var sentGoods = new List<UserItem>(Goods);
  737. for (int i = 0; i < Goods.Count; i++)
  738. player.CheckItem(Goods[i]);
  739. if (Settings.GoodsOn)
  740. {
  741. var callingNPC = NPCObject.Get(player.NPCObjectID);
  742. if (callingNPC != null)
  743. {
  744. for (int i = 0; i < callingNPC.UsedGoods.Count; i++)
  745. player.CheckItem(callingNPC.UsedGoods[i]);
  746. }
  747. sentGoods.AddRange(callingNPC.UsedGoods);
  748. }
  749. player.SendNPCGoods(sentGoods, PriceRate(player), PanelType.Buy, Settings.GoodsHideAddedStats);
  750. if (key == BuySellKey)
  751. {
  752. player.Enqueue(new S.NPCSell());
  753. }
  754. break;
  755. case BuyNewKey:
  756. case BuySellNewKey:
  757. sentGoods = new List<UserItem>(Goods);
  758. for (int i = 0; i < Goods.Count; i++)
  759. player.CheckItem(Goods[i]);
  760. player.SendNPCGoods(sentGoods, PriceRate(player), PanelType.Buy, Settings.GoodsHideAddedStats);
  761. if (key == BuySellNewKey)
  762. {
  763. player.Enqueue(new S.NPCSell());
  764. }
  765. break;
  766. case SellKey:
  767. player.Enqueue(new S.NPCSell());
  768. break;
  769. case RepairKey:
  770. player.Enqueue(new S.NPCRepair { Rate = PriceRate(player) });
  771. break;
  772. case SRepairKey:
  773. player.Enqueue(new S.NPCSRepair { Rate = PriceRate(player) });
  774. break;
  775. case CraftKey:
  776. for (int i = 0; i < CraftGoods.Count; i++)
  777. player.CheckItemInfo(CraftGoods[i].Item.Info);
  778. player.SendNPCGoods((from x in CraftGoods where x.CanCraft(player) select x.Item).ToList(), PriceRate(player), PanelType.Craft);
  779. break;
  780. case RefineKey:
  781. if (player.Info.CurrentRefine != null)
  782. {
  783. player.ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.YouAreRefiningItem), ChatType.System);
  784. player.Enqueue(new S.NPCRefine { Rate = (Settings.RefineCost), Refining = true });
  785. break;
  786. }
  787. else
  788. player.Enqueue(new S.NPCRefine { Rate = (Settings.RefineCost), Refining = false });
  789. break;
  790. case RefineCheckKey:
  791. player.Enqueue(new S.NPCCheckRefine());
  792. break;
  793. case RefineCollectKey:
  794. player.CollectRefine();
  795. break;
  796. case ReplaceWedRingKey:
  797. player.Enqueue(new S.NPCReplaceWedRing { Rate = Settings.ReplaceWedRingCost });
  798. break;
  799. case StorageKey:
  800. player.ResetStorageUnlock();
  801. player.SendStorage();
  802. player.Enqueue(new S.NPCStorage());
  803. break;
  804. case BuyBackKey:
  805. {
  806. if (Settings.GoodsOn)
  807. {
  808. var callingNPC = NPCObject.Get(player.NPCObjectID);
  809. if (callingNPC != null)
  810. {
  811. if (!callingNPC.BuyBack.ContainsKey(player.Name)) callingNPC.BuyBack[player.Name] = new List<UserItem>();
  812. for (int i = 0; i < callingNPC.BuyBack[player.Name].Count; i++)
  813. {
  814. player.CheckItem(callingNPC.BuyBack[player.Name][i]);
  815. }
  816. player.SendNPCGoods(callingNPC.BuyBack[player.Name], PriceRate(player), PanelType.Buy);
  817. }
  818. }
  819. }
  820. break;
  821. case BuyUsedKey:
  822. {
  823. if (Settings.GoodsOn)
  824. {
  825. var callingNPC = NPCObject.Get(player.NPCObjectID);
  826. if (callingNPC != null)
  827. {
  828. for (int i = 0; i < callingNPC.UsedGoods.Count; i++)
  829. player.CheckItem(callingNPC.UsedGoods[i]);
  830. player.SendNPCGoods(callingNPC.UsedGoods, PriceRate(player), PanelType.BuySub, Settings.GoodsHideAddedStats);
  831. }
  832. }
  833. }
  834. break;
  835. case ConsignKey:
  836. player.Enqueue(new S.NPCConsign());
  837. break;
  838. case MarketKey:
  839. player.UserMatch = false;
  840. player.GetMarket(string.Empty, ItemType.Nothing);
  841. break;
  842. case GuildCreateKey:
  843. if (player.Info.Level < Settings.Guild_RequiredLevel)
  844. {
  845. player.ReceiveChat(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.YouNeedLevelToCreateGuild), Settings.Guild_RequiredLevel), ChatType.System);
  846. }
  847. else if (player.MyGuild == null)
  848. {
  849. player.CanCreateGuild = true;
  850. player.Enqueue(new S.GuildNameRequest());
  851. }
  852. else
  853. player.ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.YouAreInGuild), ChatType.System);
  854. break;
  855. case RequestWarKey:
  856. if (player.MyGuild != null)
  857. {
  858. if (player.MyGuildRank != player.MyGuild.Ranks[0])
  859. {
  860. player.ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.YouMustBeLeaderToRequestWar), ChatType.System);
  861. return;
  862. }
  863. player.Enqueue(new S.GuildRequestWar());
  864. }
  865. else
  866. {
  867. player.ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.NotInGuild), ChatType.System);
  868. }
  869. break;
  870. case SendParcelKey:
  871. player.Enqueue(new S.MailSendRequest());
  872. break;
  873. case CollectParcelKey:
  874. sbyte result = 0;
  875. if (player.GetMailAwaitingCollectionAmount() < 1)
  876. {
  877. result = -1;
  878. }
  879. else
  880. {
  881. foreach (var mail in player.Info.Mail)
  882. {
  883. if (mail.Parcel) mail.Collected = true;
  884. }
  885. }
  886. player.Enqueue(new S.ParcelCollected { Result = result });
  887. player.GetMail();
  888. break;
  889. case AwakeningKey:
  890. player.Enqueue(new S.NPCAwakening());
  891. break;
  892. case DisassembleKey:
  893. player.Enqueue(new S.NPCDisassemble());
  894. break;
  895. case DowngradeKey:
  896. player.Enqueue(new S.NPCDowngrade());
  897. break;
  898. case ResetKey:
  899. player.Enqueue(new S.NPCReset());
  900. break;
  901. case PearlBuyKey:
  902. for (int i = 0; i < Goods.Count; i++)
  903. player.CheckItem(Goods[i]);
  904. player.Enqueue(new S.NPCPearlGoods { List = Goods, Rate = PriceRate(player), Type = PanelType.Buy });
  905. break;
  906. case HeroCreateKey:
  907. if (player.Info.Level < Settings.Hero_RequiredLevel)
  908. {
  909. player.ReceiveChat(GameLanguage.ServerTextMap.GetLocalization((ServerTextKeys.YouMustBeLevelToCreateHero), Settings.Hero_RequiredLevel), ChatType.System);
  910. break;
  911. }
  912. player.CanCreateHero = true;
  913. player.Enqueue(new S.HeroCreateRequest()
  914. {
  915. CanCreateClass = Settings.Hero_CanCreateClass
  916. });
  917. break;
  918. case HeroManageKey:
  919. player.ManageHeroes();
  920. break;
  921. case GuildTerritoryKey:
  922. player.GetGuildTerritories(0);
  923. break;
  924. }
  925. }
  926. public void Buy(PlayerObject player, ulong index, ushort count)
  927. {
  928. UserItem goods = null;
  929. for (int i = 0; i < Goods.Count; i++)
  930. {
  931. if (Goods[i].UniqueID != index) continue;
  932. goods = Goods[i];
  933. break;
  934. }
  935. bool isUsed = false;
  936. bool isBuyBack = false;
  937. var callingNPC = NPCObject.Get(player.NPCObjectID);
  938. if (callingNPC != null)
  939. {
  940. if (goods == null)
  941. {
  942. for (int i = 0; i < callingNPC.UsedGoods.Count; i++)
  943. {
  944. if (callingNPC.UsedGoods[i].UniqueID != index) continue;
  945. goods = callingNPC.UsedGoods[i];
  946. isUsed = true;
  947. break;
  948. }
  949. }
  950. if (goods == null)
  951. {
  952. if (!callingNPC.BuyBack.ContainsKey(player.Name)) callingNPC.BuyBack[player.Name] = new List<UserItem>();
  953. for (int i = 0; i < callingNPC.BuyBack[player.Name].Count; i++)
  954. {
  955. if (callingNPC.BuyBack[player.Name][i].UniqueID != index) continue;
  956. goods = callingNPC.BuyBack[player.Name][i];
  957. isBuyBack = true;
  958. break;
  959. }
  960. }
  961. }
  962. if (goods == null || count == 0 || count > goods.Info.StackSize) return;
  963. if ((isBuyBack || isUsed) && count > goods.Count)
  964. count = goods.Count;
  965. else
  966. goods.Count = count;
  967. uint cost = goods.Price();
  968. cost = (uint)(cost * PriceRate(player));
  969. uint baseCost = (uint)(goods.Price() * PriceRate(player, true));
  970. if (player.NPCPage.Key.ToUpper() == PearlBuyKey)//pearl currency
  971. {
  972. if (cost > player.Info.PearlCount) return;
  973. }
  974. else if (cost > player.Account.Gold) return;
  975. UserItem item = (isBuyBack || isUsed) ? goods : Envir.CreateFreshItem(goods.Info);
  976. item.Count = goods.Count;
  977. if (!player.CanGainItem(item)) return;
  978. if (player.NPCPage.Key.ToUpper() == PearlBuyKey)
  979. {
  980. player.IntelligentCreatureLosePearls((int)cost);
  981. }
  982. else
  983. {
  984. player.Account.Gold -= cost;
  985. player.Enqueue(new S.LoseGold { Gold = cost });
  986. if (callingNPC != null && callingNPC.Conq != null)
  987. {
  988. callingNPC.Conq.GuildInfo.GoldStorage += (cost - baseCost);
  989. }
  990. }
  991. player.GainItem(item);
  992. if (isUsed)
  993. {
  994. callingNPC.UsedGoods.Remove(goods); //If used or buyback will destroy whole stack instead of reducing to remaining quantity
  995. List<UserItem> newGoodsList = new List<UserItem>();
  996. newGoodsList.AddRange(Goods);
  997. newGoodsList.AddRange(callingNPC.UsedGoods);
  998. callingNPC.NeedSave = true;
  999. player.SendNPCGoods(newGoodsList, PriceRate(player), player.NPCPage.Key.ToUpper() == BuyUsedKey ? PanelType.BuySub : PanelType.Buy, Settings.GoodsHideAddedStats);
  1000. }
  1001. if (isBuyBack)
  1002. {
  1003. callingNPC.BuyBack[player.Name].Remove(goods); //If used or buyback will destroy whole stack instead of reducing to remaining quantity
  1004. player.SendNPCGoods(callingNPC.BuyBack[player.Name], PriceRate(player), PanelType.Buy);
  1005. }
  1006. }
  1007. public void Sell(PlayerObject player, UserItem item)
  1008. {
  1009. /* Handle Item Sale */
  1010. }
  1011. public void Craft(PlayerObject player, ulong index, ushort count, int[] slots)
  1012. {
  1013. S.CraftItem p = new S.CraftItem();
  1014. RecipeInfo recipe = null;
  1015. for (int i = 0; i < CraftGoods.Count; i++)
  1016. {
  1017. if (CraftGoods[i].Item.UniqueID != index) continue;
  1018. recipe = CraftGoods[i];
  1019. break;
  1020. }
  1021. UserItem goods = recipe.Item;
  1022. if (goods == null || count == 0 || count > goods.Info.StackSize)
  1023. {
  1024. player.Enqueue(p);
  1025. return;
  1026. }
  1027. if (player.Account.Gold < (recipe.Gold * count))
  1028. {
  1029. player.Enqueue(p);
  1030. return;
  1031. }
  1032. bool hasItems = true;
  1033. List<int> usedSlots = new List<int>();
  1034. //Check Tools
  1035. foreach (var tool in recipe.Tools)
  1036. {
  1037. for (int i = 0; i < slots.Length; i++)
  1038. {
  1039. int slot = slots[i];
  1040. if (usedSlots.Contains(slot)) continue;
  1041. if (slot < 0 || slot > player.Info.Inventory.Length) continue;
  1042. UserItem item = player.Info.Inventory[slot];
  1043. if (item == null || item.Info != tool.Info) continue;
  1044. usedSlots.Add(slot);
  1045. if ((uint)Math.Floor(item.CurrentDura / 1000M) < count)
  1046. {
  1047. hasItems = false;
  1048. break;
  1049. }
  1050. }
  1051. if (!hasItems)
  1052. {
  1053. break;
  1054. }
  1055. }
  1056. //Check Ingredients
  1057. foreach (var ingredient in recipe.Ingredients)
  1058. {
  1059. if (ingredient.Count * count > ingredient.Info.StackSize)
  1060. {
  1061. player.Enqueue(p);
  1062. return;
  1063. }
  1064. ushort amount = (ushort)(ingredient.Count * count);
  1065. for (int i = 0; i < slots.Length; i++)
  1066. {
  1067. int slot = slots[i];
  1068. if (usedSlots.Contains(slot)) continue;
  1069. if (slot < 0 || slot > player.Info.Inventory.Length) continue;
  1070. UserItem item = player.Info.Inventory[slot];
  1071. if (item == null || item.Info != ingredient.Info) continue;
  1072. usedSlots.Add(slot);
  1073. if (ingredient.CurrentDura < ingredient.MaxDura && ingredient.CurrentDura > item.CurrentDura)
  1074. {
  1075. hasItems = false;
  1076. break;
  1077. }
  1078. if (amount > item.Count)
  1079. {
  1080. hasItems = false;
  1081. break;
  1082. }
  1083. amount = 0;
  1084. break;
  1085. }
  1086. if (amount > 0)
  1087. {
  1088. hasItems = false;
  1089. break;
  1090. }
  1091. }
  1092. if (!hasItems || usedSlots.Count != (recipe.Tools.Count + recipe.Ingredients.Count))
  1093. {
  1094. player.Enqueue(p);
  1095. return;
  1096. }
  1097. if (count > (goods.Info.StackSize / goods.Count) || count < 1)
  1098. {
  1099. player.Enqueue(p);
  1100. return;
  1101. }
  1102. UserItem craftedItem = Envir.CreateFreshItem(goods.Info);
  1103. craftedItem.Count = (ushort)(goods.Count * count);
  1104. if (!player.CanGainItem(craftedItem))
  1105. {
  1106. player.Enqueue(p);
  1107. return;
  1108. }
  1109. List<int> usedSlots2 = new List<int>();
  1110. //Use Tool Durability
  1111. foreach (var tool in recipe.Tools)
  1112. {
  1113. for (int i = 0; i < slots.Length; i++)
  1114. {
  1115. int slot = slots[i];
  1116. if (usedSlots2.Contains(slot)) continue;
  1117. if (slot < 0 || slot > player.Info.Inventory.Length) continue;
  1118. UserItem item = player.Info.Inventory[slot];
  1119. if (item == null || item.Info != tool.Info) continue;
  1120. usedSlots2.Add(slot);
  1121. player.DamageItem(item, (int)(count * 1000), true);
  1122. break;
  1123. }
  1124. }
  1125. //Take Ingredients
  1126. foreach (var ingredient in recipe.Ingredients)
  1127. {
  1128. ushort amount = (ushort)(ingredient.Count * count);
  1129. for (int i = 0; i < slots.Length; i++)
  1130. {
  1131. int slot = slots[i];
  1132. if (usedSlots2.Contains(slot)) continue;
  1133. if (slot < 0 || slot > player.Info.Inventory.Length) continue;
  1134. UserItem item = player.Info.Inventory[slot];
  1135. if (item == null || item.Info != ingredient.Info) continue;
  1136. usedSlots2.Add(slot);
  1137. if (item.Count > amount)
  1138. {
  1139. player.Enqueue(new S.DeleteItem { UniqueID = item.UniqueID, Count = amount });
  1140. player.Info.Inventory[slot].Count -= amount;
  1141. break;
  1142. }
  1143. else
  1144. {
  1145. player.Enqueue(new S.DeleteItem { UniqueID = item.UniqueID, Count = item.Count });
  1146. amount -= item.Count;
  1147. player.Info.Inventory[slot] = null;
  1148. }
  1149. break;
  1150. }
  1151. }
  1152. //Take Gold
  1153. player.Account.Gold -= (recipe.Gold * count);
  1154. player.Enqueue(new S.LoseGold { Gold = (recipe.Gold * count) });
  1155. if (Envir.Random.Next(100) >= recipe.Chance + player.Stats[Stat.CraftRatePercent])
  1156. {
  1157. player.ReceiveChat(GameLanguage.ServerTextMap.GetLocalization(ServerTextKeys.CraftingAttemptFailed), ChatType.System);
  1158. }
  1159. else
  1160. {
  1161. //Give Item
  1162. player.GainItem(craftedItem);
  1163. }
  1164. p.Success = true;
  1165. player.Enqueue(p);
  1166. }
  1167. }
  1168. public enum NPCScriptType
  1169. {
  1170. Normal,
  1171. Called,
  1172. AutoPlayer,
  1173. AutoMonster,
  1174. Robot
  1175. }
  1176. }