From 62272d5f0c44968511977b2d6166a647030eedb3 Mon Sep 17 00:00:00 2001 From: Felipe Paluco <87909998+FelipePaluco@users.noreply.github.com> Date: Mon, 11 Nov 2024 19:37:38 -0300 Subject: [PATCH 01/12] feat: add soul core items (#3099) Add 660 Soulcore items, in preparation to further Soulpit Development. --- data/items/items.xml | 3300 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3300 insertions(+) diff --git a/data/items/items.xml b/data/items/items.xml index eb00ac6bdf3..72c9ba04142 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -76427,5 +76427,3305 @@ Granted by TibiaGoals.com"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 49c1540618f4f637ec7467fdf10ea56947a65d41 Mon Sep 17 00:00:00 2001 From: HT Cesta <58153179+htc16@users.noreply.github.com> Date: Mon, 11 Nov 2024 20:31:37 -0300 Subject: [PATCH 02/12] fix: freequests storages (#3044) --- .../creaturescripts/customs/freequests.lua | 745 +++++++++--------- 1 file changed, 357 insertions(+), 388 deletions(-) diff --git a/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua b/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua index 1cbca17c563..bfeac7acad0 100644 --- a/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua +++ b/data-otservbr-global/scripts/creaturescripts/customs/freequests.lua @@ -1,392 +1,354 @@ local stage = configManager.getNumber(configKeys.FREE_QUEST_STAGE) local questTable = { - { storage = Storage.Quest.U9_60.BigfootsBurden.QuestLine, storageValue = 2 }, - { storage = Storage.Quest.U9_60.BigfootsBurden.QuestLine, storageValue = 4 }, - { storage = Storage.Quest.U9_60.BigfootsBurden.QuestLine, storageValue = 7 }, - { storage = Storage.Quest.U9_60.BigfootsBurden.QuestLine, storageValue = 9 }, - { storage = Storage.Quest.U9_60.BigfootsBurden.QuestLine, storageValue = 12 }, - { storage = Storage.Quest.U9_60.BigfootsBurden.Shooting, storageValue = 5 }, - { storage = Storage.Quest.U9_60.BigfootsBurden.QuestLine, storageValue = 16 }, - { storage = Storage.Quest.U9_60.BigfootsBurden.QuestLine, storageValue = 20 }, - { storage = Storage.Quest.U9_60.BigfootsBurden.QuestLine, storageValue = 23 }, - { storage = Storage.Quest.U9_60.BigfootsBurden.QuestLineComplete, storageValue = 2 }, - { storage = Storage.Quest.U9_60.BigfootsBurden.Rank, storageValue = 1440 }, - { storage = Storage.Quest.U9_60.BigfootsBurden.Warzone1Access, storageValue = 2 }, - { storage = Storage.Quest.U9_60.BigfootsBurden.Warzone2Access, storageValue = 2 }, - { storage = Storage.Quest.U9_60.BigfootsBurden.Warzone3Access, storageValue = 2 }, - { storage = Storage.Quest.U11_50.DangerousDepths.Questline, storageValue = 10 }, - { storage = Storage.Quest.U11_50.DangerousDepths.Access.LavaPumpWarzoneVI, storageValue = 10 }, - { storage = Storage.Quest.U11_50.DangerousDepths.Access.LavaPumpWarzoneV, storageValue = 10 }, - { storage = Storage.Quest.U11_50.DangerousDepths.Access.LavaPumpWarzoneIV, storageValue = 30 }, - { storage = Storage.Quest.U11_50.DangerousDepths.Dwarves.Points, storageValue = 10 }, - { storage = Storage.Quest.U11_50.DangerousDepths.Scouts.Points, storageValue = 10 }, - { storage = Storage.Quest.U11_50.DangerousDepths.Gnomes.Points, storageValue = 10 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.Questline, storageValue = 51 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.Mission01, storageValue = 6 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.Mission02, storageValue = 8 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.Mission03, storageValue = 6 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.Mission04, storageValue = 6 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.Mission05, storageValue = 8 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.Mission06, storageValue = 5 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.Mission07, storageValue = 5 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.Mission08, storageValue = 4 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.Mission09, storageValue = 2 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.Mission10, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.SewerPipe01, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.SewerPipe02, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.SewerPipe03, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.SewerPipe04, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.DiseasedDan, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.DiseasedBill, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.DiseasedFred, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.AlchemistFormula, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.BadSide, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.GoodSide, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.MrWestDoor, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.MrWestStatus, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.TamerinStatus, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.MorikSummon, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.QuaraState, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.QuaraSplasher, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.QuaraSharptooth, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.QuaraInky, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.MatrixState, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.NotesPalimuth, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.NotesAzerus, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.DoorToAzerus, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.DoorToBog, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.DoorToLastFight, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.DoorToMatrix, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.DoorToQuara, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Questline, storageValue = 7 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Minotaurs.JamesfrancisTask, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Minotaurs.Mission, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Minotaurs.BossTimer, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.MotA.Mission, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.MotA.Stone1, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.MotA.Stone2, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.MotA.Stone3, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.MotA.Answer, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.MotA.QuestionId, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.Mission, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.sulphur, storageValue = 4 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.Tar, storageValue = 3 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.Ice, storageValue = 3 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.Objects, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.Temp, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Orcs.Mission, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Orcs.LookType, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Orcs.BossTimer, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Life.Mission, storageValue = 7 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Life.BossTimer, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Humans.Mission, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Humans.Vaporized, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Humans.Decaying, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Humans.BossTimer, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Misguided.Mission, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Misguided.Monsters, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Misguided.Exorcisms, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Misguided.Time, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Misguided.BossTimer, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Minotaurs.EntranceAccessDoor, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Minotaurs.AccessDoor, storageValue = 1 }, - { storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 1 }, - { storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 4 }, - { storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 7 }, - { storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 16 }, - { storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 26 }, - { storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 29 }, - { storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 32 }, - { storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 35 }, - { storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 38 }, - { storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 41 }, - { storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 43 }, - { storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 46 }, - { storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 47 }, - { storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 50 }, - { storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 55 }, - { storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 56 }, - { storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 58 }, - { storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 61 }, - { storage = Storage.Quest.U7_6.ExplorerSociety.CalassaQuest, storageValue = 2 }, - { storage = Storage.Quest.U11_02.ForgottenKnowledge.Tomes, storageValue = 1 }, - { storage = Storage.Quest.U11_02.ForgottenKnowledge.LastLoreKilled, storageValue = 1 }, - { storage = Storage.Quest.U11_02.ForgottenKnowledge.TimeGuardianKilled, storageValue = 1 }, - { storage = Storage.Quest.U11_02.ForgottenKnowledge.HorrorKilled, storageValue = 1 }, - { storage = Storage.Quest.U11_02.ForgottenKnowledge.DragonkingKilled, storageValue = 1 }, - { storage = Storage.Quest.U11_02.ForgottenKnowledge.ThornKnightKilled, storageValue = 1 }, - { storage = Storage.Quest.U11_02.ForgottenKnowledge.LloydKilled, storageValue = 1 }, - { storage = Storage.Quest.U11_02.ForgottenKnowledge.LadyTenebrisKilled, storageValue = 1 }, - { storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessMachine, storageValue = 1 }, - { storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessLavaTeleport, storageValue = 1 }, - { storage = Storage.Quest.U8_0.BarbarianTest.Questline, storageValue = 8 }, - { storage = Storage.Quest.U8_0.BarbarianTest.Mission01, storageValue = 3 }, - { storage = Storage.Quest.U8_0.BarbarianTest.Mission02, storageValue = 3 }, - { storage = Storage.Quest.U8_0.BarbarianTest.Mission03, storageValue = 3 }, - { storage = Storage.Quest.U8_54.ChildrenOfTheRevolution.Questline, storageValue = 21 }, - { storage = Storage.Quest.U8_54.ChildrenOfTheRevolution.Mission00, storageValue = 2 }, - { storage = Storage.Quest.U8_54.ChildrenOfTheRevolution.Mission01, storageValue = 3 }, - { storage = Storage.Quest.U8_54.ChildrenOfTheRevolution.Mission02, storageValue = 5 }, - { storage = Storage.Quest.U8_54.ChildrenOfTheRevolution.Mission03, storageValue = 3 }, - { storage = Storage.Quest.U8_54.ChildrenOfTheRevolution.Mission04, storageValue = 6 }, - { storage = Storage.Quest.U8_54.ChildrenOfTheRevolution.Mission05, storageValue = 3 }, - { storage = Storage.Quest.U8_54.ChildrenOfTheRevolution.SpyBuilding01, storageValue = 1 }, - { storage = Storage.Quest.U8_54.ChildrenOfTheRevolution.SpyBuilding02, storageValue = 1 }, - { storage = Storage.Quest.U8_54.ChildrenOfTheRevolution.SpyBuilding03, storageValue = 1 }, - { storage = Storage.Quest.U8_54.ChildrenOfTheRevolution.StrangeSymbols, storageValue = 1 }, - { storage = Storage.Quest.U7_4.DjinnWar.Faction.Greeting, storageValue = 2 }, - { storage = Storage.Quest.U7_4.DjinnWar.Faction.MaridDoor, storageValue = 2 }, - { storage = Storage.Quest.U7_4.DjinnWar.Faction.EfreetDoor, storageValue = 2 }, - { storage = Storage.Quest.U7_4.DjinnWar.EfreetFaction.Start, storageValue = 1 }, - { storage = Storage.Quest.U7_4.DjinnWar.EfreetFaction.Mission01, storageValue = 3 }, - { storage = Storage.Quest.U7_4.DjinnWar.EfreetFaction.Mission02, storageValue = 3 }, - { storage = Storage.Quest.U7_4.DjinnWar.EfreetFaction.Mission03, storageValue = 3 }, - { storage = Storage.Quest.U7_4.DjinnWar.MaridFaction.Start, storageValue = 1 }, - { storage = Storage.Quest.U7_4.DjinnWar.MaridFaction.Mission01, storageValue = 2 }, - { storage = Storage.Quest.U7_4.DjinnWar.MaridFaction.Mission02, storageValue = 2 }, - { storage = Storage.Quest.U7_4.DjinnWar.MaridFaction.RataMari, storageValue = 2 }, - { storage = Storage.Quest.U7_4.DjinnWar.MaridFaction.Mission03, storageValue = 3 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.TheWayToYalahar, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.SearoutesAroundYalahar.TownsCounter, storageValue = 5 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.SearoutesAroundYalahar.AbDendriel, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.SearoutesAroundYalahar.Darashia, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.SearoutesAroundYalahar.Venore, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.SearoutesAroundYalahar.Ankrahmun, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.SearoutesAroundYalahar.PortHope, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.SearoutesAroundYalahar.Thais, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.SearoutesAroundYalahar.LibertyBay, storageValue = 1 }, - { storage = Storage.Quest.U8_4.InServiceOfYalahar.SearoutesAroundYalahar.Carlin, storageValue = 1 }, - { storage = Storage.Quest.U8_4.TheHiddenCityOfBeregar.DefaultStart, storageValue = 1 }, - { storage = Storage.Quest.U8_4.TheHiddenCityOfBeregar.GoingDown, storageValue = 1 }, - { storage = Storage.Quest.U8_4.TheHiddenCityOfBeregar.WayToBeregar, storageValue = 1 }, - { storage = Storage.Quest.U8_0.TheIceIslands.Questline, storageValue = 40 }, - { storage = Storage.Quest.U8_0.TheIceIslands.Mission01, storageValue = 3 }, - { storage = Storage.Quest.U8_0.TheIceIslands.Mission02, storageValue = 5 }, - { storage = Storage.Quest.U8_0.TheIceIslands.Mission03, storageValue = 3 }, - { storage = Storage.Quest.U8_0.TheIceIslands.Mission04, storageValue = 2 }, - { storage = Storage.Quest.U8_0.TheIceIslands.Mission05, storageValue = 6 }, - { storage = Storage.Quest.U8_0.TheIceIslands.Mission06, storageValue = 8 }, - { storage = Storage.Quest.U8_0.TheIceIslands.Mission07, storageValue = 3 }, - { storage = Storage.Quest.U8_0.TheIceIslands.Mission08, storageValue = 4 }, - { storage = Storage.Quest.U8_0.TheIceIslands.Mission09, storageValue = 2 }, - { storage = Storage.Quest.U8_0.TheIceIslands.Mission10, storageValue = 2 }, - { storage = Storage.Quest.U8_0.TheIceIslands.Mission11, storageValue = 2 }, - { storage = Storage.Quest.U8_0.TheIceIslands.Mission12, storageValue = 6 }, - { storage = Storage.Quest.U8_0.TheIceIslands.yakchalDoor, storageValue = 1 }, - { storage = Storage.Quest.U8_2.TheInquisitionQuest.Questline, storageValue = 25 }, - { storage = Storage.Quest.U8_2.TheInquisitionQuest.Mission01, storageValue = 7 }, - { storage = Storage.Quest.U8_2.TheInquisitionQuest.Mission02, storageValue = 3 }, - { storage = Storage.Quest.U8_2.TheInquisitionQuest.Mission03, storageValue = 6 }, - { storage = Storage.Quest.U8_2.TheInquisitionQuest.Mission04, storageValue = 3 }, - { storage = Storage.Quest.U8_2.TheInquisitionQuest.Mission05, storageValue = 3 }, - { storage = Storage.Quest.U8_2.TheInquisitionQuest.Mission06, storageValue = 3 }, - { storage = Storage.Quest.U8_2.TheInquisitionQuest.Mission07, storageValue = 1 }, - { storage = Storage.Quest.U8_2.TheInquisitionQuest.GrofGuard, storageValue = 1 }, - { storage = Storage.Quest.U8_2.TheInquisitionQuest.KulagGuard, storageValue = 1 }, - { storage = Storage.Quest.U8_2.TheInquisitionQuest.TimGuard, storageValue = 1 }, - { storage = Storage.Quest.U8_2.TheInquisitionQuest.WalterGuard, storageValue = 1 }, - { storage = Storage.Quest.U8_2.TheInquisitionQuest.StorkusVampiredust, storageValue = 1 }, - { storage = Storage.Quest.U8_54.TheNewFrontier.Questline, storageValue = 29 }, - { storage = Storage.Quest.U8_54.TheNewFrontier.Mission01, storageValue = 3 }, - { storage = Storage.Quest.U8_54.TheNewFrontier.Mission02[1], storageValue = 4 }, - { storage = Storage.Quest.U8_54.TheNewFrontier.Mission03, storageValue = 3 }, - { storage = Storage.Quest.U8_54.TheNewFrontier.Mission04, storageValue = 2 }, - { storage = Storage.Quest.U8_54.TheNewFrontier.Mission05[1], storageValue = 2 }, - { storage = Storage.Quest.U8_54.TheNewFrontier.Mission06, storageValue = 5 }, - { storage = Storage.Quest.U8_54.TheNewFrontier.Mission07[1], storageValue = 2 }, - { storage = Storage.Quest.U8_54.TheNewFrontier.Mission08, storageValue = 2 }, - { storage = Storage.Quest.U8_54.TheNewFrontier.Mission09[1], storageValue = 3 }, - { storage = Storage.Quest.U8_54.TheNewFrontier.Mission10[1], storageValue = 2 }, - { storage = Storage.Quest.U8_54.TheNewFrontier.Mission10.MagicCarpetDoor, storageValue = 1 }, - { storage = Storage.Quest.U8_54.TheNewFrontier.TomeofKnowledge, storageValue = 12 }, - { storage = Storage.Quest.U8_54.TheNewFrontier.Mission02.Beaver1, storageValue = 1 }, - { storage = Storage.Quest.U8_54.TheNewFrontier.Mission02.Beaver2, storageValue = 1 }, - { storage = Storage.Quest.U8_54.TheNewFrontier.Mission02.Beaver3, storageValue = 1 }, - { storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.KingTibianus, storageValue = 1 }, - { storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.Leeland, storageValue = 1 }, - { storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.Angus, storageValue = 1 }, - { storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.Wyrdin, storageValue = 1 }, - { storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.Telas, storageValue = 1 }, - { storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.Humgolf, storageValue = 1 }, - { storage = Storage.Quest.U7_8.TheShatteredIsles.DefaultStart, storageValue = 3 }, - { storage = Storage.Quest.U7_8.TheShatteredIsles.TheGovernorDaughter, storageValue = 3 }, - { storage = Storage.Quest.U7_8.TheShatteredIsles.TheErrand, storageValue = 2 }, - { storage = Storage.Quest.U7_8.TheShatteredIsles.AccessToMeriana, storageValue = 1 }, - { storage = Storage.Quest.U7_8.TheShatteredIsles.APoemForTheMermaid, storageValue = 3 }, - { storage = Storage.Quest.U7_8.TheShatteredIsles.ADjinnInLove, storageValue = 5 }, - { storage = Storage.Quest.U7_8.TheShatteredIsles.AccessToLagunaIsland, storageValue = 1 }, - { storage = Storage.Quest.U7_8.TheShatteredIsles.AccessToGoroma, storageValue = 1 }, - { storage = Storage.Quest.U7_8.TheShatteredIsles.Shipwrecked, storageValue = 2 }, - { storage = Storage.Quest.U7_8.TheShatteredIsles.DragahsSpellbook, storageValue = 1 }, - { storage = Storage.Quest.U7_8.TheShatteredIsles.TheCounterspell, storageValue = 4 }, - { storage = Storage.Quest.U8_2.TheThievesGuildQuest.Questline, storageValue = 1 }, - { storage = Storage.Quest.U8_2.TheThievesGuildQuest.Mission01, storageValue = 2 }, - { storage = Storage.Quest.U8_2.TheThievesGuildQuest.Mission02, storageValue = 3 }, - { storage = Storage.Quest.U8_2.TheThievesGuildQuest.Mission03, storageValue = 3 }, - { storage = Storage.Quest.U8_2.TheThievesGuildQuest.Mission04, storageValue = 8 }, - { storage = Storage.Quest.U8_2.TheThievesGuildQuest.Mission05, storageValue = 2 }, - { storage = Storage.Quest.U8_2.TheThievesGuildQuest.Mission06, storageValue = 4 }, - { storage = Storage.Quest.U8_2.TheThievesGuildQuest.Mission07, storageValue = 2 }, - { storage = Storage.Quest.U8_2.TheThievesGuildQuest.Mission08, storageValue = 1 }, - { storage = Storage.Quest.U8_1.TheTravellingTrader.Mission01, storageValue = 1 }, - { storage = Storage.Quest.U8_1.TheTravellingTrader.Mission01, storageValue = 2 }, - { storage = Storage.Quest.U8_1.TheTravellingTrader.Mission02, storageValue = 5 }, - { storage = Storage.Quest.U8_1.TheTravellingTrader.Mission03, storageValue = 3 }, - { storage = Storage.Quest.U8_1.TheTravellingTrader.Mission04, storageValue = 3 }, - { storage = Storage.Quest.U8_1.TheTravellingTrader.Mission05, storageValue = 3 }, - { storage = Storage.Quest.U8_1.TheTravellingTrader.Mission06, storageValue = 2 }, - { storage = Storage.Quest.U8_1.TheTravellingTrader.Mission07, storageValue = 1 }, - { storage = Storage.Quest.U8_0.BarbarianArena.QuestLogGreenhorn, storageValue = 1 }, - { storage = Storage.Quest.U8_1.TibiaTales.DefaultStart, storageValue = 1 }, - { storage = Storage.Quest.U8_1.TibiaTales.ToAppeaseTheMightyQuest, storageValue = 1 }, - { storage = 12450, storageValue = 6 }, - { storage = 12330, storageValue = 1 }, - { storage = 12332, storageValue = 13 }, - { storage = 12333, storageValue = 3 }, - { storage = Storage.Quest.U7_8.FriendsAndTraders.DefaultStart, storageValue = 1 }, - { storage = Storage.Quest.U7_8.FriendsAndTraders.TheSweatyCyclops, storageValue = 2 }, - { storage = Storage.Quest.U7_8.FriendsAndTraders.TheMermaidMarina, storageValue = 2 }, - { storage = Storage.Quest.U7_8.FriendsAndTraders.TheBlessedStake, storageValue = 12 }, - { storage = 100157, storageValue = 1 }, - { storage = Storage.Quest.U8_6.WrathOfTheEmperor.Questline, storageValue = 29 }, - { storage = Storage.Quest.U8_6.WrathOfTheEmperor.Mission01, storageValue = 3 }, - { storage = Storage.Quest.U8_6.WrathOfTheEmperor.Mission02, storageValue = 3 }, - { storage = Storage.Quest.U8_6.WrathOfTheEmperor.Mission03, storageValue = 3 }, - { storage = Storage.Quest.U8_6.WrathOfTheEmperor.Mission04, storageValue = 3 }, - { storage = Storage.Quest.U8_6.WrathOfTheEmperor.Mission05, storageValue = 3 }, - { storage = Storage.Quest.U8_6.WrathOfTheEmperor.Mission06, storageValue = 4 }, - { storage = Storage.Quest.U8_6.WrathOfTheEmperor.Mission07, storageValue = 6 }, - { storage = Storage.Quest.U8_6.WrathOfTheEmperor.Mission08, storageValue = 2 }, - { storage = Storage.Quest.U8_6.WrathOfTheEmperor.Mission09, storageValue = 2 }, - { storage = Storage.Quest.U8_6.WrathOfTheEmperor.Mission10, storageValue = 1 }, - { storage = Storage.Quest.U7_6.TheApeCity.Started, storageValue = 1 }, - { storage = Storage.Quest.U7_6.TheApeCity.Questline, storageValue = 18 }, - { storage = Storage.BanutaSecretTunnel.DeeperBanutaShortcut, storageValue = 1 }, - { storage = Storage.Quest.U10_50.OramondQuest.QuestLine, storageValue = 1 }, - { storage = Storage.Quest.U10_50.OramondQuest.ToTakeRoots.Mission, storageValue = 3000 }, - { storage = Storage.Quest.U11_50.DangerousDepths.Questline, storageValue = 1 }, - { storage = Storage.Quest.U11_50.DangerousDepths.Dwarves.Home, storageValue = 2 }, - { storage = Storage.Quest.U11_50.DangerousDepths.Dwarves.Subterranean, storageValue = 2 }, - { storage = Storage.Quest.U11_50.DangerousDepths.Gnomes.Measurements, storageValue = 2 }, - { storage = Storage.Quest.U11_50.DangerousDepths.Gnomes.Ordnance, storageValue = 3 }, - { storage = Storage.Quest.U11_50.DangerousDepths.Gnomes.Charting, storageValue = 2 }, - { storage = Storage.Quest.U11_50.DangerousDepths.Scouts.Growth, storageValue = 2 }, - { storage = Storage.Quest.U11_50.DangerousDepths.Scouts.Diremaw, storageValue = 2 }, - { storage = Storage.Quest.U11_40.ThreatenedDreams.QuestLine, storageValue = 1 }, - { storage = Storage.Quest.U11_40.ThreatenedDreams.Mission01[1], storageValue = 16 }, - { storage = Storage.Quest.U11_40.ThreatenedDreams.Mission02.KroazurAccess, storageValue = 1 }, - { storage = Storage.Quest.U9_80.AdventurersGuild.QuestLine, storageValue = 1 }, - { storage = Storage.Quest.U10_80.TheGreatDragonHunt.WarriorSkeleton, storageValue = 1 }, - { storage = Storage.Quest.U10_80.TheGreatDragonHunt.WarriorSkeleton, storageValue = 2 }, - { storage = Storage.Quest.U10_80.TheLostBrotherQuest, storageValue = 3 }, - { storage = Storage.Quest.U10_55.Dawnport.Questline, storageValue = 1 }, - { storage = Storage.Quest.U10_55.Dawnport.GoMain, storageValue = 1 }, - { storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessDeath, storageValue = 1 }, - { storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessViolet, storageValue = 1 }, - { storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessEarth, storageValue = 1 }, - { storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessFire, storageValue = 1 }, - { storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessIce, storageValue = 1 }, - { storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessGolden, storageValue = 1 }, - { storage = Storage.Quest.U10_80.GrimvaleQuest.AncientFeudDoors, storageValue = 1 }, - { storage = Storage.Quest.U10_80.GrimvaleQuest.AncientFeudShortcut, storageValue = 1 }, - { storage = Storage.Kilmaresh.AccessDoor, storageValue = 1 }, - { storage = Storage.Kilmaresh.CatacombDoors, storageValue = 1 }, - { storage = Storage.Quest.U12_00.TheDreamCourts.AccessDoors, storageValue = 1 }, - { storage = Storage.Quest.U12_00.TheDreamCourts.HauntedHouseAccess, storageValue = 1 }, - { storage = Storage.Quest.U12_00.TheDreamCourts.BuriedCathedralAccess, storageValue = 1 }, - { storage = Storage.Quest.U12_00.TheDreamCourts.DreamScarAccess, storageValue = 1 }, - { storage = Storage.Quest.U12_40.TheOrderOfTheLion.AccessEastSide, storageValue = 1 }, - { storage = Storage.Quest.U12_40.TheOrderOfTheLion.AccessSouthernSide, storageValue = 1 }, - { storage = Storage.Quest.U12_60.APiratesTail.TentuglyDoor, storageValue = 1 }, - { storage = Storage.Quest.U12_60.APiratesTail.RascacoonShortcut, storageValue = 1 }, - { storage = Storage.Quest.U12_70.AdventuresOfGalthen.AccessDoor, storageValue = 1 }, - - { storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.AccessDoor, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.TrialAccessDoor, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.TarAccessDoor, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.BossAccessDoor, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Life.AccessDoor, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.Misguided.AccessDoor, storageValue = 1 }, - { storage = Storage.Quest.U11_40.CultsOfTibia.FinalBoss.AccessDoor, storageValue = 1 }, - - { storage = Storage.Quest.U10_90.FerumbrasAscension.FirstDoor, storageValue = 1 }, - { storage = Storage.Quest.U10_90.FerumbrasAscension.MonsterDoor, storageValue = 1 }, - { storage = Storage.Quest.U10_90.FerumbrasAscension.TarbazDoor, storageValue = 1 }, - { storage = Storage.Quest.U10_90.FerumbrasAscension.HabitatsAccess, storageValue = 1 }, - { storage = Storage.Quest.U10_90.FerumbrasAscension.TheLordOfTheLiceAccess, storageValue = 1 }, - { storage = Storage.Quest.U10_90.FerumbrasAscension.Statue, storageValue = 1 }, - - { storage = Storage.Quest.U12_00.TheDreamCourts.AndrewDoor, storageValue = 1 }, - - { storage = Storage.Quest.U8_6.WrathOfTheEmperor.TeleportAccess.AwarnessEmperor, storageValue = 1 }, - { storage = Storage.Quest.U8_6.WrathOfTheEmperor.TeleportAccess.BossRoom, storageValue = 1 }, - { storage = Storage.Quest.U8_6.WrathOfTheEmperor.TeleportAccess.InnerSanctum, storageValue = 1 }, - { storage = Storage.Quest.U8_6.WrathOfTheEmperor.TeleportAccess.Rebel, storageValue = 1 }, - { storage = Storage.Quest.U8_6.WrathOfTheEmperor.TeleportAccess.SleepingDragon, storageValue = 2 }, - { storage = Storage.Quest.U8_6.WrathOfTheEmperor.TeleportAccess.Wote10, storageValue = 1 }, - { storage = Storage.Quest.U8_6.WrathOfTheEmperor.TeleportAccess.Zizzle, storageValue = 1 }, - { storage = Storage.Quest.U8_6.WrathOfTheEmperor.TeleportAccess.Zlak, storageValue = 1 }, - - --[[ { storage = Storage.Quest.U11_80.TheSecretLibrary.FalconBastionAccess, storageValue = 1 }, - { storage = Storage.Quest.U11_80.TheSecretLibrary.LowerBastionAccess, storageValue = 1 }, - { storage = Storage.Quest.U11_80.TheSecretLibrary.UndergroundBastionAccess, storageValue = 1 }, - { storage = Storage.Quest.U11_80.TheSecretLibrary.ShortcutToBastion, storageValue = 1 }, - { storage = Storage.Quest.U11_80.TheSecretLibrary.OberonAccess, storageValue = 1 }, - { storage = Storage.Quest.U11_80.TheSecretLibrary.MotaDoor, storageValue = 1 }, - { storage = Storage.Quest.U11_80.TheSecretLibrary.BasinDoor, storageValue = 1 }, - { storage = Storage.Quest.U11_80.TheSecretLibrary.SkullDoor, storageValue = 1 }, - { storage = Storage.Quest.U11_80.TheSecretLibrary.Mota, storageValue = 1 }, - { storage = Storage.Quest.U11_80.TheSecretLibrary.MiniBosses.PreceptorLazare, storageValue = 1 }, - { storage = Storage.Quest.U11_80.TheSecretLibrary.MiniBosses.GrandCanonDominus, storageValue = 1 }, - { storage = Storage.Quest.U11_80.TheSecretLibrary.MiniBosses.GrandChaplainGaunder, storageValue = 1 }, - { storage = Storage.Quest.U11_80.TheSecretLibrary.MiniBosses.GrandCommanderSoeren, storageValue = 1 }, - { storage = Storage.Quest.U11_80.TheSecretLibrary.MiniBosses.DazedLeafGolem, storageValue = 1 }, ]] - - { storage = Storage.Quest.U7_4.DjinnWar.EfreetFaction.DoorToLamp, storageValue = 1 }, - { storage = Storage.Quest.U7_4.DjinnWar.EfreetFaction.DoorToMaridTerritory, storageValue = 1 }, - { storage = Storage.Quest.U7_4.DjinnWar.MaridFaction.DoorToLamp, storageValue = 1 }, - { storage = Storage.Quest.U7_4.DjinnWar.MaridFaction.DoorToEfreetTerritory, storageValue = 1 }, - - { storage = Storage.Quest.U12_20.GraveDanger.QuestLine, storageValue = 14 }, - { storage = Storage.Quest.U12_20.GraveDanger.Bosses.KingZelosDoor, storageValue = 1 }, - - { storage = Storage.Quest.U13_10.CradleOfMonsters.Access.Ingol, storageValue = 1 }, - { storage = Storage.Quest.U13_10.CradleOfMonsters.Access.LowerIngol, storageValue = 1 }, - { storage = Storage.Quest.U13_10.CradleOfMonsters.Access.Monster, storageValue = 1 }, - { storage = Storage.Quest.U13_10.CradleOfMonsters.Access.MutatedAbomination, storageValue = 1 }, - { storage = Storage.Quest.U8_54.TheNewFrontier.SnakeHeadTeleport, storageValue = 1 }, - { storage = Storage.Quest.U9_4.LiquidBlackQuest.Visitor, storageValue = 5 }, - { storage = Storage.Quest.U8_4.BloodBrothers.VengothAccess, storageValue = 1 }, - - -- Assassin Outfit quests - { storage = Storage.Quest.U8_5.KillingInTheNameOf.BudrikMinos, storageValue = 0 }, - { storage = Storage.Quest.U8_1.ToOutfoxAFoxQuest.Questline, storageValue = 2 }, - - -- Hunter Outfit quests - { storage = Storage.Quest.U7_8.HunterOutfits.HunterMusicSheet01, storageValue = 1 }, - { storage = Storage.Quest.U7_8.HunterOutfits.HunterMusicSheet02, storageValue = 1 }, - { storage = Storage.Quest.U7_8.HunterOutfits.HunterMusicSheet03, storageValue = 1 }, - { storage = Storage.Quest.U7_8.HunterOutfits.HunterMusicSheet04, storageValue = 1 }, - - -- Norseman - { storage = Storage.Quest.U8_0.TheIceIslands.NorsemanOutfit, storageValue = 1 }, - { storage = Storage.OutfitQuest.DefaultStart, storageValue = 1 }, - - { storage = Storage.HeroRathleton.AccessDoor, storageValue = 1 }, - { storage = Storage.HeroRathleton.AccessTeleport1, storageValue = 1 }, - { storage = Storage.HeroRathleton.AccessTeleport2, storageValue = 1 }, - { storage = Storage.HeroRathleton.AccessTeleport3, storageValue = 1 }, - - -- Sea Serpent Quest - { storage = Storage.Quest.U8_2.TheHuntForTheSeaSerpent.FishForASerpent, storageValue = 5 }, - { storage = Storage.Quest.U8_2.TheHuntForTheSeaSerpent.QuestLine, storageValue = 2 }, - - --The White Raven Monastery - { storage = Storage.Quest.U7_24.TheWhiteRavenMonastery.QuestLog, storageValue = 1 }, - { storage = Storage.Quest.U7_24.TheWhiteRavenMonastery.Passage, storageValue = 1 }, - { storage = Storage.Quest.U7_24.TheWhiteRavenMonastery.Diary, storageValue = 2 }, - { storage = Storage.Quest.U7_24.TheWhiteRavenMonastery.Door, storageValue = 1 }, + { storageName = "BigfootsBurden.QuestLine", storage = Storage.Quest.U9_60.BigfootsBurden.QuestLine, storageValue = 2 }, + { storageName = "BigfootsBurden.QuestLine", storage = Storage.Quest.U9_60.BigfootsBurden.QuestLine, storageValue = 4 }, + { storageName = "BigfootsBurden.QuestLine", storage = Storage.Quest.U9_60.BigfootsBurden.QuestLine, storageValue = 7 }, + { storageName = "BigfootsBurden.QuestLine", storage = Storage.Quest.U9_60.BigfootsBurden.QuestLine, storageValue = 9 }, + { storageName = "BigfootsBurden.QuestLine", storage = Storage.Quest.U9_60.BigfootsBurden.QuestLine, storageValue = 12 }, + { storageName = "BigfootsBurden.Shooting", storage = Storage.Quest.U9_60.BigfootsBurden.Shooting, storageValue = 5 }, + { storageName = "BigfootsBurden.QuestLine", storage = Storage.Quest.U9_60.BigfootsBurden.QuestLine, storageValue = 16 }, + { storageName = "BigfootsBurden.QuestLine", storage = Storage.Quest.U9_60.BigfootsBurden.QuestLine, storageValue = 20 }, + { storageName = "BigfootsBurden.QuestLine", storage = Storage.Quest.U9_60.BigfootsBurden.QuestLine, storageValue = 23 }, + { storageName = "BigfootsBurden.QuestLineComplete", storage = Storage.Quest.U9_60.BigfootsBurden.QuestLineComplete, storageValue = 2 }, + { storageName = "BigfootsBurden.Rank", storage = Storage.Quest.U9_60.BigfootsBurden.Rank, storageValue = 1440 }, + { storageName = "BigfootsBurden.Warzone1Access", storage = Storage.Quest.U9_60.BigfootsBurden.Warzone1Access, storageValue = 2 }, + { storageName = "BigfootsBurden.Warzone2Access", storage = Storage.Quest.U9_60.BigfootsBurden.Warzone2Access, storageValue = 2 }, + { storageName = "BigfootsBurden.Warzone3Access", storage = Storage.Quest.U9_60.BigfootsBurden.Warzone3Access, storageValue = 2 }, + { storageName = "DangerousDepths.Questline", storage = Storage.Quest.U11_50.DangerousDepths.Questline, storageValue = 10 }, + { storageName = "DangerousDepths.Access.LavaPumpWarzoneVI", storage = Storage.Quest.U11_50.DangerousDepths.Access.LavaPumpWarzoneVI, storageValue = 10 }, + { storageName = "DangerousDepths.Access.LavaPumpWarzoneV", storage = Storage.Quest.U11_50.DangerousDepths.Access.LavaPumpWarzoneV, storageValue = 10 }, + { storageName = "DangerousDepths.Access.LavaPumpWarzoneIV", storage = Storage.Quest.U11_50.DangerousDepths.Access.LavaPumpWarzoneIV, storageValue = 30 }, + { storageName = "DangerousDepths.Dwarves.Points", storage = Storage.Quest.U11_50.DangerousDepths.Dwarves.Points, storageValue = 10 }, + { storageName = "DangerousDepths.Scouts.Points", storage = Storage.Quest.U11_50.DangerousDepths.Scouts.Points, storageValue = 10 }, + { storageName = "DangerousDepths.Gnomes.Points", storage = Storage.Quest.U11_50.DangerousDepths.Gnomes.Points, storageValue = 10 }, + { storageName = "InServiceOfYalahar.Questline", storage = Storage.Quest.U8_4.InServiceOfYalahar.Questline, storageValue = 51 }, + { storageName = "InServiceOfYalahar.Mission01", storage = Storage.Quest.U8_4.InServiceOfYalahar.Mission01, storageValue = 6 }, + { storageName = "InServiceOfYalahar.Mission02", storage = Storage.Quest.U8_4.InServiceOfYalahar.Mission02, storageValue = 8 }, + { storageName = "InServiceOfYalahar.Mission03", storage = Storage.Quest.U8_4.InServiceOfYalahar.Mission03, storageValue = 6 }, + { storageName = "InServiceOfYalahar.Mission04", storage = Storage.Quest.U8_4.InServiceOfYalahar.Mission04, storageValue = 6 }, + { storageName = "InServiceOfYalahar.Mission05", storage = Storage.Quest.U8_4.InServiceOfYalahar.Mission05, storageValue = 8 }, + { storageName = "InServiceOfYalahar.Mission06", storage = Storage.Quest.U8_4.InServiceOfYalahar.Mission06, storageValue = 5 }, + { storageName = "InServiceOfYalahar.Mission07", storage = Storage.Quest.U8_4.InServiceOfYalahar.Mission07, storageValue = 5 }, + { storageName = "InServiceOfYalahar.Mission08", storage = Storage.Quest.U8_4.InServiceOfYalahar.Mission08, storageValue = 4 }, + { storageName = "InServiceOfYalahar.Mission09", storage = Storage.Quest.U8_4.InServiceOfYalahar.Mission09, storageValue = 2 }, + { storageName = "InServiceOfYalahar.Mission10", storage = Storage.Quest.U8_4.InServiceOfYalahar.Mission10, storageValue = 1 }, + { storageName = "InServiceOfYalahar.SewerPipe01", storage = Storage.Quest.U8_4.InServiceOfYalahar.SewerPipe01, storageValue = 1 }, + { storageName = "InServiceOfYalahar.SewerPipe02", storage = Storage.Quest.U8_4.InServiceOfYalahar.SewerPipe02, storageValue = 1 }, + { storageName = "InServiceOfYalahar.SewerPipe03", storage = Storage.Quest.U8_4.InServiceOfYalahar.SewerPipe03, storageValue = 1 }, + { storageName = "InServiceOfYalahar.SewerPipe04", storage = Storage.Quest.U8_4.InServiceOfYalahar.SewerPipe04, storageValue = 1 }, + { storageName = "InServiceOfYalahar.DiseasedDan", storage = Storage.Quest.U8_4.InServiceOfYalahar.DiseasedDan, storageValue = 1 }, + { storageName = "InServiceOfYalahar.DiseasedBill", storage = Storage.Quest.U8_4.InServiceOfYalahar.DiseasedBill, storageValue = 1 }, + { storageName = "InServiceOfYalahar.DiseasedFred", storage = Storage.Quest.U8_4.InServiceOfYalahar.DiseasedFred, storageValue = 1 }, + { storageName = "InServiceOfYalahar.AlchemistFormula", storage = Storage.Quest.U8_4.InServiceOfYalahar.AlchemistFormula, storageValue = 1 }, + { storageName = "InServiceOfYalahar.BadSide", storage = Storage.Quest.U8_4.InServiceOfYalahar.BadSide, storageValue = 1 }, + { storageName = "InServiceOfYalahar.GoodSide", storage = Storage.Quest.U8_4.InServiceOfYalahar.GoodSide, storageValue = 1 }, + { storageName = "InServiceOfYalahar.MrWestDoor", storage = Storage.Quest.U8_4.InServiceOfYalahar.MrWestDoor, storageValue = 1 }, + { storageName = "InServiceOfYalahar.MrWestStatus", storage = Storage.Quest.U8_4.InServiceOfYalahar.MrWestStatus, storageValue = 1 }, + { storageName = "InServiceOfYalahar.TamerinStatus", storage = Storage.Quest.U8_4.InServiceOfYalahar.TamerinStatus, storageValue = 1 }, + { storageName = "InServiceOfYalahar.MorikSummon", storage = Storage.Quest.U8_4.InServiceOfYalahar.MorikSummon, storageValue = 1 }, + { storageName = "InServiceOfYalahar.QuaraState", storage = Storage.Quest.U8_4.InServiceOfYalahar.QuaraState, storageValue = 1 }, + { storageName = "InServiceOfYalahar.QuaraSplasher", storage = Storage.Quest.U8_4.InServiceOfYalahar.QuaraSplasher, storageValue = 1 }, + { storageName = "InServiceOfYalahar.QuaraSharptooth", storage = Storage.Quest.U8_4.InServiceOfYalahar.QuaraSharptooth, storageValue = 1 }, + { storageName = "InServiceOfYalahar.QuaraInky", storage = Storage.Quest.U8_4.InServiceOfYalahar.QuaraInky, storageValue = 1 }, + { storageName = "InServiceOfYalahar.MatrixState", storage = Storage.Quest.U8_4.InServiceOfYalahar.MatrixState, storageValue = 1 }, + { storageName = "InServiceOfYalahar.NotesPalimuth", storage = Storage.Quest.U8_4.InServiceOfYalahar.NotesPalimuth, storageValue = 1 }, + { storageName = "InServiceOfYalahar.NotesAzerus", storage = Storage.Quest.U8_4.InServiceOfYalahar.NotesAzerus, storageValue = 1 }, + { storageName = "InServiceOfYalahar.DoorToAzerus", storage = Storage.Quest.U8_4.InServiceOfYalahar.DoorToAzerus, storageValue = 1 }, + { storageName = "InServiceOfYalahar.DoorToBog", storage = Storage.Quest.U8_4.InServiceOfYalahar.DoorToBog, storageValue = 1 }, + { storageName = "InServiceOfYalahar.DoorToLastFight", storage = Storage.Quest.U8_4.InServiceOfYalahar.DoorToLastFight, storageValue = 1 }, + { storageName = "InServiceOfYalahar.DoorToMatrix", storage = Storage.Quest.U8_4.InServiceOfYalahar.DoorToMatrix, storageValue = 1 }, + { storageName = "InServiceOfYalahar.DoorToQuara", storage = Storage.Quest.U8_4.InServiceOfYalahar.DoorToQuara, storageValue = 1 }, + { storageName = "CultsOfTibia.Questline", storage = Storage.Quest.U11_40.CultsOfTibia.Questline, storageValue = 7 }, + { storageName = "CultsOfTibia.Minotaurs.JamesfrancisTask", storage = Storage.Quest.U11_40.CultsOfTibia.Minotaurs.JamesfrancisTask, storageValue = 1 }, + { storageName = "CultsOfTibia.Minotaurs.Mission", storage = Storage.Quest.U11_40.CultsOfTibia.Minotaurs.Mission, storageValue = 1 }, + { storageName = "CultsOfTibia.Minotaurs.BossTimer", storage = Storage.Quest.U11_40.CultsOfTibia.Minotaurs.BossTimer, storageValue = 1 }, + { storageName = "CultsOfTibia.MotA.Mission", storage = Storage.Quest.U11_40.CultsOfTibia.MotA.Mission, storageValue = 1 }, + { storageName = "CultsOfTibia.MotA.Stone1", storage = Storage.Quest.U11_40.CultsOfTibia.MotA.Stone1, storageValue = 1 }, + { storageName = "CultsOfTibia.MotA.Stone2", storage = Storage.Quest.U11_40.CultsOfTibia.MotA.Stone2, storageValue = 1 }, + { storageName = "CultsOfTibia.MotA.Stone3", storage = Storage.Quest.U11_40.CultsOfTibia.MotA.Stone3, storageValue = 1 }, + { storageName = "CultsOfTibia.MotA.Answer", storage = Storage.Quest.U11_40.CultsOfTibia.MotA.Answer, storageValue = 1 }, + { storageName = "CultsOfTibia.MotA.QuestionId", storage = Storage.Quest.U11_40.CultsOfTibia.MotA.QuestionId, storageValue = 1 }, + { storageName = "CultsOfTibia.Barkless.Mission", storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.Mission, storageValue = 1 }, + { storageName = "CultsOfTibia.Barkless.Sulphur", storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.Sulphur, storageValue = 4 }, + { storageName = "CultsOfTibia.Barkless.Tar", storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.Tar, storageValue = 3 }, + { storageName = "CultsOfTibia.Barkless.Ice", storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.Ice, storageValue = 3 }, + { storageName = "CultsOfTibia.Barkless.Objects", storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.Objects, storageValue = 1 }, + { storageName = "CultsOfTibia.Barkless.Temp", storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.Temp, storageValue = 1 }, + { storageName = "CultsOfTibia.Orcs.Mission", storage = Storage.Quest.U11_40.CultsOfTibia.Orcs.Mission, storageValue = 1 }, + { storageName = "CultsOfTibia.Orcs.LookType", storage = Storage.Quest.U11_40.CultsOfTibia.Orcs.LookType, storageValue = 1 }, + { storageName = "CultsOfTibia.Orcs.BossTimer", storage = Storage.Quest.U11_40.CultsOfTibia.Orcs.BossTimer, storageValue = 1 }, + { storageName = "CultsOfTibia.Life.Mission", storage = Storage.Quest.U11_40.CultsOfTibia.Life.Mission, storageValue = 7 }, + { storageName = "CultsOfTibia.Life.BossTimer", storage = Storage.Quest.U11_40.CultsOfTibia.Life.BossTimer, storageValue = 1 }, + { storageName = "CultsOfTibia.Humans.Mission", storage = Storage.Quest.U11_40.CultsOfTibia.Humans.Mission, storageValue = 1 }, + { storageName = "CultsOfTibia.Humans.Vaporized", storage = Storage.Quest.U11_40.CultsOfTibia.Humans.Vaporized, storageValue = 1 }, + { storageName = "CultsOfTibia.Humans.Decaying", storage = Storage.Quest.U11_40.CultsOfTibia.Humans.Decaying, storageValue = 1 }, + { storageName = "CultsOfTibia.Humans.BossTimer", storage = Storage.Quest.U11_40.CultsOfTibia.Humans.BossTimer, storageValue = 1 }, + { storageName = "CultsOfTibia.Misguided.Mission", storage = Storage.Quest.U11_40.CultsOfTibia.Misguided.Mission, storageValue = 1 }, + { storageName = "CultsOfTibia.Misguided.Monsters", storage = Storage.Quest.U11_40.CultsOfTibia.Misguided.Monsters, storageValue = 1 }, + { storageName = "CultsOfTibia.Misguided.Exorcisms", storage = Storage.Quest.U11_40.CultsOfTibia.Misguided.Exorcisms, storageValue = 1 }, + { storageName = "CultsOfTibia.Misguided.Time", storage = Storage.Quest.U11_40.CultsOfTibia.Misguided.Time, storageValue = 1 }, + { storageName = "CultsOfTibia.Misguided.BossTimer", storage = Storage.Quest.U11_40.CultsOfTibia.Misguided.BossTimer, storageValue = 1 }, + { storageName = "CultsOfTibia.Minotaurs.BossAccessDoor", storage = Storage.Quest.U11_40.CultsOfTibia.Minotaurs.BossAccessDoor, storageValue = 1 }, + { storageName = "CultsOfTibia.Minotaurs.AccessDoor", storage = Storage.Quest.U11_40.CultsOfTibia.Minotaurs.AccessDoor, storageValue = 1 }, + { storageName = "ExplorerSociety.QuestLine", storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 1 }, + { storageName = "ExplorerSociety.QuestLine", storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 4 }, + { storageName = "ExplorerSociety.QuestLine", storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 7 }, + { storageName = "ExplorerSociety.QuestLine", storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 16 }, + { storageName = "ExplorerSociety.QuestLine", storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 26 }, + { storageName = "ExplorerSociety.QuestLine", storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 29 }, + { storageName = "ExplorerSociety.QuestLine", storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 32 }, + { storageName = "ExplorerSociety.QuestLine", storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 35 }, + { storageName = "ExplorerSociety.QuestLine", storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 38 }, + { storageName = "ExplorerSociety.QuestLine", storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 41 }, + { storageName = "ExplorerSociety.QuestLine", storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 43 }, + { storageName = "ExplorerSociety.QuestLine", storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 46 }, + { storageName = "ExplorerSociety.QuestLine", storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 47 }, + { storageName = "ExplorerSociety.QuestLine", storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 50 }, + { storageName = "ExplorerSociety.QuestLine", storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 55 }, + { storageName = "ExplorerSociety.QuestLine", storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 56 }, + { storageName = "ExplorerSociety.QuestLine", storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 58 }, + { storageName = "ExplorerSociety.QuestLine", storage = Storage.Quest.U7_6.ExplorerSociety.QuestLine, storageValue = 61 }, + { storageName = "ExplorerSociety.CalassaQuest", storage = Storage.Quest.U7_6.ExplorerSociety.CalassaQuest, storageValue = 2 }, + { storageName = "ForgottenKnowledge.Tomes", storage = Storage.Quest.U11_02.ForgottenKnowledge.Tomes, storageValue = 1 }, + { storageName = "ForgottenKnowledge.LastLoreKilled", storage = Storage.Quest.U11_02.ForgottenKnowledge.LastLoreKilled, storageValue = 1 }, + { storageName = "ForgottenKnowledge.TimeGuardianKilled", storage = Storage.Quest.U11_02.ForgottenKnowledge.TimeGuardianKilled, storageValue = 1 }, + { storageName = "ForgottenKnowledge.HorrorKilled", storage = Storage.Quest.U11_02.ForgottenKnowledge.HorrorKilled, storageValue = 1 }, + { storageName = "ForgottenKnowledge.DragonkingKilled", storage = Storage.Quest.U11_02.ForgottenKnowledge.DragonkingKilled, storageValue = 1 }, + { storageName = "ForgottenKnowledge.ThornKnightKilled", storage = Storage.Quest.U11_02.ForgottenKnowledge.ThornKnightKilled, storageValue = 1 }, + { storageName = "ForgottenKnowledge.LloydKilled", storage = Storage.Quest.U11_02.ForgottenKnowledge.LloydKilled, storageValue = 1 }, + { storageName = "ForgottenKnowledge.LadyTenebrisKilled", storage = Storage.Quest.U11_02.ForgottenKnowledge.LadyTenebrisKilled, storageValue = 1 }, + { storageName = "ForgottenKnowledge.AccessMachine", storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessMachine, storageValue = 1 }, + { storageName = "ForgottenKnowledge.AccessLavaTeleport", storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessLavaTeleport, storageValue = 1 }, + { storageName = "BarbarianTest.Questline", storage = Storage.Quest.U8_0.BarbarianTest.Questline, storageValue = 8 }, + { storageName = "BarbarianTest.Mission01", storage = Storage.Quest.U8_0.BarbarianTest.Mission01, storageValue = 3 }, + { storageName = "BarbarianTest.Mission02", storage = Storage.Quest.U8_0.BarbarianTest.Mission02, storageValue = 3 }, + { storageName = "BarbarianTest.Mission03", storage = Storage.Quest.U8_0.BarbarianTest.Mission03, storageValue = 3 }, + { storageName = "ChildrenOfTheRevolution.Questline", storage = Storage.Quest.U8_54.ChildrenOfTheRevolution.Questline, storageValue = 21 }, + { storageName = "ChildrenOfTheRevolution.Mission00", storage = Storage.Quest.U8_54.ChildrenOfTheRevolution.Mission00, storageValue = 2 }, + { storageName = "ChildrenOfTheRevolution.Mission01", storage = Storage.Quest.U8_54.ChildrenOfTheRevolution.Mission01, storageValue = 3 }, + { storageName = "ChildrenOfTheRevolution.Mission02", storage = Storage.Quest.U8_54.ChildrenOfTheRevolution.Mission02, storageValue = 5 }, + { storageName = "ChildrenOfTheRevolution.Mission03", storage = Storage.Quest.U8_54.ChildrenOfTheRevolution.Mission03, storageValue = 3 }, + { storageName = "ChildrenOfTheRevolution.Mission04", storage = Storage.Quest.U8_54.ChildrenOfTheRevolution.Mission04, storageValue = 6 }, + { storageName = "ChildrenOfTheRevolution.Mission05", storage = Storage.Quest.U8_54.ChildrenOfTheRevolution.Mission05, storageValue = 3 }, + { storageName = "ChildrenOfTheRevolution.SpyBuilding01", storage = Storage.Quest.U8_54.ChildrenOfTheRevolution.SpyBuilding01, storageValue = 1 }, + { storageName = "ChildrenOfTheRevolution.SpyBuilding02", storage = Storage.Quest.U8_54.ChildrenOfTheRevolution.SpyBuilding02, storageValue = 1 }, + { storageName = "ChildrenOfTheRevolution.SpyBuilding03", storage = Storage.Quest.U8_54.ChildrenOfTheRevolution.SpyBuilding03, storageValue = 1 }, + { storageName = "ChildrenOfTheRevolution.StrangeSymbols", storage = Storage.Quest.U8_54.ChildrenOfTheRevolution.StrangeSymbols, storageValue = 1 }, + { storageName = "DjinnWar.Faction.Greeting", storage = Storage.Quest.U7_4.DjinnWar.Faction.Greeting, storageValue = 2 }, + { storageName = "DjinnWar.Faction.MaridDoor", storage = Storage.Quest.U7_4.DjinnWar.Faction.MaridDoor, storageValue = 2 }, + { storageName = "DjinnWar.Faction.EfreetDoor", storage = Storage.Quest.U7_4.DjinnWar.Faction.EfreetDoor, storageValue = 2 }, + { storageName = "DjinnWar.EfreetFaction.Start", storage = Storage.Quest.U7_4.DjinnWar.EfreetFaction.Start, storageValue = 1 }, + { storageName = "DjinnWar.EfreetFaction.Mission01", storage = Storage.Quest.U7_4.DjinnWar.EfreetFaction.Mission01, storageValue = 3 }, + { storageName = "DjinnWar.EfreetFaction.Mission02", storage = Storage.Quest.U7_4.DjinnWar.EfreetFaction.Mission02, storageValue = 3 }, + { storageName = "DjinnWar.EfreetFaction.Mission03", storage = Storage.Quest.U7_4.DjinnWar.EfreetFaction.Mission03, storageValue = 3 }, + { storageName = "DjinnWar.MaridFaction.Start", storage = Storage.Quest.U7_4.DjinnWar.MaridFaction.Start, storageValue = 1 }, + { storageName = "DjinnWar.MaridFaction.Mission01", storage = Storage.Quest.U7_4.DjinnWar.MaridFaction.Mission01, storageValue = 2 }, + { storageName = "DjinnWar.MaridFaction.Mission02", storage = Storage.Quest.U7_4.DjinnWar.MaridFaction.Mission02, storageValue = 2 }, + { storageName = "DjinnWar.MaridFaction.RataMari", storage = Storage.Quest.U7_4.DjinnWar.MaridFaction.RataMari, storageValue = 2 }, + { storageName = "DjinnWar.MaridFaction.Mission03", storage = Storage.Quest.U7_4.DjinnWar.MaridFaction.Mission03, storageValue = 3 }, + { storageName = "InServiceOfYalahar.TheWayToYalahar", storage = Storage.Quest.U8_4.InServiceOfYalahar.TheWayToYalahar, storageValue = 1 }, + { storageName = "InServiceOfYalahar.SearoutesAroundYalahar.TownsCounter", storage = Storage.Quest.U8_4.InServiceOfYalahar.SearoutesAroundYalahar.TownsCounter, storageValue = 5 }, + { storageName = "InServiceOfYalahar.SearoutesAroundYalahar.AbDendriel", storage = Storage.Quest.U8_4.InServiceOfYalahar.SearoutesAroundYalahar.AbDendriel, storageValue = 1 }, + { storageName = "InServiceOfYalahar.SearoutesAroundYalahar.Darashia", storage = Storage.Quest.U8_4.InServiceOfYalahar.SearoutesAroundYalahar.Darashia, storageValue = 1 }, + { storageName = "InServiceOfYalahar.SearoutesAroundYalahar.Venore", storage = Storage.Quest.U8_4.InServiceOfYalahar.SearoutesAroundYalahar.Venore, storageValue = 1 }, + { storageName = "InServiceOfYalahar.SearoutesAroundYalahar.Ankrahmun", storage = Storage.Quest.U8_4.InServiceOfYalahar.SearoutesAroundYalahar.Ankrahmun, storageValue = 1 }, + { storageName = "InServiceOfYalahar.SearoutesAroundYalahar.PortHope", storage = Storage.Quest.U8_4.InServiceOfYalahar.SearoutesAroundYalahar.PortHope, storageValue = 1 }, + { storageName = "InServiceOfYalahar.SearoutesAroundYalahar.Thais", storage = Storage.Quest.U8_4.InServiceOfYalahar.SearoutesAroundYalahar.Thais, storageValue = 1 }, + { storageName = "InServiceOfYalahar.SearoutesAroundYalahar.LibertyBay", storage = Storage.Quest.U8_4.InServiceOfYalahar.SearoutesAroundYalahar.LibertyBay, storageValue = 1 }, + { storageName = "InServiceOfYalahar.SearoutesAroundYalahar.Carlin", storage = Storage.Quest.U8_4.InServiceOfYalahar.SearoutesAroundYalahar.Carlin, storageValue = 1 }, + { storageName = "TheHiddenCityOfBeregar.DefaultStart", storage = Storage.Quest.U8_4.TheHiddenCityOfBeregar.DefaultStart, storageValue = 1 }, + { storageName = "TheHiddenCityOfBeregar.GoingDown", storage = Storage.Quest.U8_4.TheHiddenCityOfBeregar.GoingDown, storageValue = 1 }, + { storageName = "TheHiddenCityOfBeregar.WayToBeregar", storage = Storage.Quest.U8_4.TheHiddenCityOfBeregar.WayToBeregar, storageValue = 1 }, + { storageName = "TheIceIslands.Questline", storage = Storage.Quest.U8_0.TheIceIslands.Questline, storageValue = 40 }, + { storageName = "TheIceIslands.Mission01", storage = Storage.Quest.U8_0.TheIceIslands.Mission01, storageValue = 3 }, + { storageName = "TheIceIslands.Mission02", storage = Storage.Quest.U8_0.TheIceIslands.Mission02, storageValue = 5 }, + { storageName = "TheIceIslands.Mission03", storage = Storage.Quest.U8_0.TheIceIslands.Mission03, storageValue = 3 }, + { storageName = "TheIceIslands.Mission04", storage = Storage.Quest.U8_0.TheIceIslands.Mission04, storageValue = 2 }, + { storageName = "TheIceIslands.Mission05", storage = Storage.Quest.U8_0.TheIceIslands.Mission05, storageValue = 6 }, + { storageName = "TheIceIslands.Mission06", storage = Storage.Quest.U8_0.TheIceIslands.Mission06, storageValue = 8 }, + { storageName = "TheIceIslands.Mission07", storage = Storage.Quest.U8_0.TheIceIslands.Mission07, storageValue = 3 }, + { storageName = "TheIceIslands.Mission08", storage = Storage.Quest.U8_0.TheIceIslands.Mission08, storageValue = 4 }, + { storageName = "TheIceIslands.Mission09", storage = Storage.Quest.U8_0.TheIceIslands.Mission09, storageValue = 2 }, + { storageName = "TheIceIslands.Mission10", storage = Storage.Quest.U8_0.TheIceIslands.Mission10, storageValue = 2 }, + { storageName = "TheIceIslands.Mission11", storage = Storage.Quest.U8_0.TheIceIslands.Mission11, storageValue = 2 }, + { storageName = "TheIceIslands.Mission12", storage = Storage.Quest.U8_0.TheIceIslands.Mission12, storageValue = 6 }, + { storageName = "TheIceIslands.yakchalDoor", storage = Storage.Quest.U8_0.TheIceIslands.yakchalDoor, storageValue = 1 }, + { storageName = "TheInquisitionQuest.Questline", storage = Storage.Quest.U8_2.TheInquisitionQuest.Questline, storageValue = 25 }, + { storageName = "TheInquisitionQuest.Mission01", storage = Storage.Quest.U8_2.TheInquisitionQuest.Mission01, storageValue = 7 }, + { storageName = "TheInquisitionQuest.Mission02", storage = Storage.Quest.U8_2.TheInquisitionQuest.Mission02, storageValue = 3 }, + { storageName = "TheInquisitionQuest.Mission03", storage = Storage.Quest.U8_2.TheInquisitionQuest.Mission03, storageValue = 6 }, + { storageName = "TheInquisitionQuest.Mission04", storage = Storage.Quest.U8_2.TheInquisitionQuest.Mission04, storageValue = 3 }, + { storageName = "TheInquisitionQuest.Mission05", storage = Storage.Quest.U8_2.TheInquisitionQuest.Mission05, storageValue = 3 }, + { storageName = "TheInquisitionQuest.Mission06", storage = Storage.Quest.U8_2.TheInquisitionQuest.Mission06, storageValue = 3 }, + { storageName = "TheInquisitionQuest.Mission07", storage = Storage.Quest.U8_2.TheInquisitionQuest.Mission07, storageValue = 1 }, + { storageName = "TheInquisitionQuest.GrofGuard", storage = Storage.Quest.U8_2.TheInquisitionQuest.GrofGuard, storageValue = 1 }, + { storageName = "TheInquisitionQuest.KulagGuard", storage = Storage.Quest.U8_2.TheInquisitionQuest.KulagGuard, storageValue = 1 }, + { storageName = "TheInquisitionQuest.TimGuard", storage = Storage.Quest.U8_2.TheInquisitionQuest.TimGuard, storageValue = 1 }, + { storageName = "TheInquisitionQuest.WalterGuard", storage = Storage.Quest.U8_2.TheInquisitionQuest.WalterGuard, storageValue = 1 }, + { storageName = "TheInquisitionQuest.StorkusVampiredust", storage = Storage.Quest.U8_2.TheInquisitionQuest.StorkusVampiredust, storageValue = 1 }, + { storageName = "TheNewFrontier.Questline", storage = Storage.Quest.U8_54.TheNewFrontier.Questline, storageValue = 29 }, + { storageName = "TheNewFrontier.Mission01", storage = Storage.Quest.U8_54.TheNewFrontier.Mission01, storageValue = 3 }, + { storageName = "TheNewFrontier.Mission02[1]", storage = Storage.Quest.U8_54.TheNewFrontier.Mission02[1], storageValue = 4 }, + { storageName = "TheNewFrontier.Mission03", storage = Storage.Quest.U8_54.TheNewFrontier.Mission03, storageValue = 3 }, + { storageName = "TheNewFrontier.Mission04", storage = Storage.Quest.U8_54.TheNewFrontier.Mission04, storageValue = 2 }, + { storageName = "TheNewFrontier.Mission05[1]", storage = Storage.Quest.U8_54.TheNewFrontier.Mission05[1], storageValue = 2 }, + { storageName = "TheNewFrontier.Mission06", storage = Storage.Quest.U8_54.TheNewFrontier.Mission06, storageValue = 5 }, + { storageName = "TheNewFrontier.Mission07[1]", storage = Storage.Quest.U8_54.TheNewFrontier.Mission07[1], storageValue = 2 }, + { storageName = "TheNewFrontier.Mission08", storage = Storage.Quest.U8_54.TheNewFrontier.Mission08, storageValue = 2 }, + { storageName = "TheNewFrontier.Mission09[1]", storage = Storage.Quest.U8_54.TheNewFrontier.Mission09[1], storageValue = 3 }, + { storageName = "TheNewFrontier.Mission10[1]", storage = Storage.Quest.U8_54.TheNewFrontier.Mission10[1], storageValue = 2 }, + { storageName = "TheNewFrontier.Mission10.MagicCarpetDoor", storage = Storage.Quest.U8_54.TheNewFrontier.Mission10.MagicCarpetDoor, storageValue = 1 }, + { storageName = "TheNewFrontier.TomeofKnowledge", storage = Storage.Quest.U8_54.TheNewFrontier.TomeofKnowledge, storageValue = 12 }, + { storageName = "TheNewFrontier.Mission02.Beaver1", storage = Storage.Quest.U8_54.TheNewFrontier.Mission02.Beaver1, storageValue = 1 }, + { storageName = "TheNewFrontier.Mission02.Beaver2", storage = Storage.Quest.U8_54.TheNewFrontier.Mission02.Beaver2, storageValue = 1 }, + { storageName = "TheNewFrontier.Mission02.Beaver3", storage = Storage.Quest.U8_54.TheNewFrontier.Mission02.Beaver3, storageValue = 1 }, + { storageName = "TheNewFrontier.Mission05.KingTibianus", storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.KingTibianus, storageValue = 1 }, + { storageName = "TheNewFrontier.Mission05.Leeland", storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.Leeland, storageValue = 1 }, + { storageName = "TheNewFrontier.Mission05.Angus", storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.Angus, storageValue = 1 }, + { storageName = "TheNewFrontier.Mission05.Wyrdin", storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.Wyrdin, storageValue = 1 }, + { storageName = "TheNewFrontier.Mission05.Telas", storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.Telas, storageValue = 1 }, + { storageName = "TheNewFrontier.Mission05.Humgolf", storage = Storage.Quest.U8_54.TheNewFrontier.Mission05.Humgolf, storageValue = 1 }, + { storageName = "TheShatteredIsles.DefaultStart", storage = Storage.Quest.U7_8.TheShatteredIsles.DefaultStart, storageValue = 3 }, + { storageName = "TheShatteredIsles.TheGovernorDaughter", storage = Storage.Quest.U7_8.TheShatteredIsles.TheGovernorDaughter, storageValue = 3 }, + { storageName = "TheShatteredIsles.TheErrand", storage = Storage.Quest.U7_8.TheShatteredIsles.TheErrand, storageValue = 2 }, + { storageName = "TheShatteredIsles.AccessToMeriana", storage = Storage.Quest.U7_8.TheShatteredIsles.AccessToMeriana, storageValue = 1 }, + { storageName = "TheShatteredIsles.APoemForTheMermaid", storage = Storage.Quest.U7_8.TheShatteredIsles.APoemForTheMermaid, storageValue = 3 }, + { storageName = "TheShatteredIsles.ADjinnInLove", storage = Storage.Quest.U7_8.TheShatteredIsles.ADjinnInLove, storageValue = 5 }, + { storageName = "TheShatteredIsles.AccessToLagunaIsland", storage = Storage.Quest.U7_8.TheShatteredIsles.AccessToLagunaIsland, storageValue = 1 }, + { storageName = "TheShatteredIsles.AccessToGoroma", storage = Storage.Quest.U7_8.TheShatteredIsles.AccessToGoroma, storageValue = 1 }, + { storageName = "TheShatteredIsles.Shipwrecked", storage = Storage.Quest.U7_8.TheShatteredIsles.Shipwrecked, storageValue = 2 }, + { storageName = "TheShatteredIsles.DragahsSpellbook", storage = Storage.Quest.U7_8.TheShatteredIsles.DragahsSpellbook, storageValue = 1 }, + { storageName = "TheShatteredIsles.TheCounterspell", storage = Storage.Quest.U7_8.TheShatteredIsles.TheCounterspell, storageValue = 4 }, + { storageName = "TheThievesGuildQuest.Questline", storage = Storage.Quest.U8_2.TheThievesGuildQuest.Questline, storageValue = 1 }, + { storageName = "TheThievesGuildQuest.Mission01", storage = Storage.Quest.U8_2.TheThievesGuildQuest.Mission01, storageValue = 2 }, + { storageName = "TheThievesGuildQuest.Mission02", storage = Storage.Quest.U8_2.TheThievesGuildQuest.Mission02, storageValue = 3 }, + { storageName = "TheThievesGuildQuest.Mission03", storage = Storage.Quest.U8_2.TheThievesGuildQuest.Mission03, storageValue = 3 }, + { storageName = "TheThievesGuildQuest.Mission04", storage = Storage.Quest.U8_2.TheThievesGuildQuest.Mission04, storageValue = 8 }, + { storageName = "TheThievesGuildQuest.Mission05", storage = Storage.Quest.U8_2.TheThievesGuildQuest.Mission05, storageValue = 2 }, + { storageName = "TheThievesGuildQuest.Mission06", storage = Storage.Quest.U8_2.TheThievesGuildQuest.Mission06, storageValue = 4 }, + { storageName = "TheThievesGuildQuest.Mission07", storage = Storage.Quest.U8_2.TheThievesGuildQuest.Mission07, storageValue = 2 }, + { storageName = "TheThievesGuildQuest.Mission08", storage = Storage.Quest.U8_2.TheThievesGuildQuest.Mission08, storageValue = 1 }, + { storageName = "TheTravellingTrader.Mission01", storage = Storage.Quest.U8_1.TheTravellingTrader.Mission01, storageValue = 1 }, + { storageName = "TheTravellingTrader.Mission01_2", storage = Storage.Quest.U8_1.TheTravellingTrader.Mission01, storageValue = 2 }, + { storageName = "TheTravellingTrader.Mission02", storage = Storage.Quest.U8_1.TheTravellingTrader.Mission02, storageValue = 5 }, + { storageName = "TheTravellingTrader.Mission03", storage = Storage.Quest.U8_1.TheTravellingTrader.Mission03, storageValue = 3 }, + { storageName = "TheTravellingTrader.Mission04", storage = Storage.Quest.U8_1.TheTravellingTrader.Mission04, storageValue = 3 }, + { storageName = "TheTravellingTrader.Mission05", storage = Storage.Quest.U8_1.TheTravellingTrader.Mission05, storageValue = 3 }, + { storageName = "TheTravellingTrader.Mission06", storage = Storage.Quest.U8_1.TheTravellingTrader.Mission06, storageValue = 2 }, + { storageName = "TheTravellingTrader.Mission07", storage = Storage.Quest.U8_1.TheTravellingTrader.Mission07, storageValue = 1 }, + { storageName = "BarbarianArena.QuestLogGreenhorn", storage = Storage.Quest.U8_0.BarbarianArena.QuestLogGreenhorn, storageValue = 1 }, + { storageName = "TibiaTales.DefaultStart", storage = Storage.Quest.U8_1.TibiaTales.DefaultStart, storageValue = 1 }, + { storageName = "TibiaTales.ToAppeaseTheMightyQuest", storage = Storage.Quest.U8_1.TibiaTales.ToAppeaseTheMightyQuest, storageValue = 1 }, + { storageName = "12450", storage = 12450, storageValue = 6 }, + { storageName = "12330", storage = 12330, storageValue = 1 }, + { storageName = "12332", storage = 12332, storageValue = 13 }, + { storageName = "12333", storage = 12333, storageValue = 3 }, + { storageName = "FriendsAndTraders.DefaultStart", storage = Storage.Quest.U7_8.FriendsAndTraders.DefaultStart, storageValue = 1 }, + { storageName = "FriendsAndTraders.TheSweatyCyclops", storage = Storage.Quest.U7_8.FriendsAndTraders.TheSweatyCyclops, storageValue = 2 }, + { storageName = "FriendsAndTraders.TheMermaidMarina", storage = Storage.Quest.U7_8.FriendsAndTraders.TheMermaidMarina, storageValue = 2 }, + { storageName = "FriendsAndTraders.TheBlessedStake", storage = Storage.Quest.U7_8.FriendsAndTraders.TheBlessedStake, storageValue = 12 }, + { storageName = "100157", storage = 100157, storageValue = 1 }, + { storageName = "WrathOfTheEmperor.Questline", storage = Storage.Quest.U8_6.WrathOfTheEmperor.Questline, storageValue = 29 }, + { storageName = "WrathOfTheEmperor.Mission01", storage = Storage.Quest.U8_6.WrathOfTheEmperor.Mission01, storageValue = 3 }, + { storageName = "WrathOfTheEmperor.Mission02", storage = Storage.Quest.U8_6.WrathOfTheEmperor.Mission02, storageValue = 3 }, + { storageName = "WrathOfTheEmperor.Mission03", storage = Storage.Quest.U8_6.WrathOfTheEmperor.Mission03, storageValue = 3 }, + { storageName = "WrathOfTheEmperor.Mission04", storage = Storage.Quest.U8_6.WrathOfTheEmperor.Mission04, storageValue = 3 }, + { storageName = "WrathOfTheEmperor.Mission05", storage = Storage.Quest.U8_6.WrathOfTheEmperor.Mission05, storageValue = 3 }, + { storageName = "WrathOfTheEmperor.Mission06", storage = Storage.Quest.U8_6.WrathOfTheEmperor.Mission06, storageValue = 4 }, + { storageName = "WrathOfTheEmperor.Mission07", storage = Storage.Quest.U8_6.WrathOfTheEmperor.Mission07, storageValue = 6 }, + { storageName = "WrathOfTheEmperor.Mission08", storage = Storage.Quest.U8_6.WrathOfTheEmperor.Mission08, storageValue = 2 }, + { storageName = "WrathOfTheEmperor.Mission09", storage = Storage.Quest.U8_6.WrathOfTheEmperor.Mission09, storageValue = 2 }, + { storageName = "WrathOfTheEmperor.Mission10", storage = Storage.Quest.U8_6.WrathOfTheEmperor.Mission10, storageValue = 1 }, + { storageName = "TheApeCity.Started", storage = Storage.Quest.U7_6.TheApeCity.Started, storageValue = 1 }, + { storageName = "TheApeCity.Questline", storage = Storage.Quest.U7_6.TheApeCity.Questline, storageValue = 18 }, + { storageName = "BanutaSecretTunnel.DeeperBanutaShortcut", storage = Storage.BanutaSecretTunnel.DeeperBanutaShortcut, storageValue = 1 }, + { storageName = "OramondQuest.QuestLine", storage = Storage.Quest.U10_50.OramondQuest.QuestLine, storageValue = 1 }, + { storageName = "OramondQuest.ToTakeRoots.Mission", storage = Storage.Quest.U10_50.OramondQuest.ToTakeRoots.Mission, storageValue = 3000 }, + { storageName = "DangerousDepths.Questline", storage = Storage.Quest.U11_50.DangerousDepths.Questline, storageValue = 1 }, + { storageName = "DangerousDepths.Dwarves.Home", storage = Storage.Quest.U11_50.DangerousDepths.Dwarves.Home, storageValue = 2 }, + { storageName = "DangerousDepths.Dwarves.Subterranean", storage = Storage.Quest.U11_50.DangerousDepths.Dwarves.Subterranean, storageValue = 2 }, + { storageName = "DangerousDepths.Gnomes.Measurements", storage = Storage.Quest.U11_50.DangerousDepths.Gnomes.Measurements, storageValue = 2 }, + { storageName = "DangerousDepths.Gnomes.Ordnance", storage = Storage.Quest.U11_50.DangerousDepths.Gnomes.Ordnance, storageValue = 3 }, + { storageName = "DangerousDepths.Gnomes.Charting", storage = Storage.Quest.U11_50.DangerousDepths.Gnomes.Charting, storageValue = 2 }, + { storageName = "DangerousDepths.Scouts.Growth", storage = Storage.Quest.U11_50.DangerousDepths.Scouts.Growth, storageValue = 2 }, + { storageName = "DangerousDepths.Scouts.Diremaw", storage = Storage.Quest.U11_50.DangerousDepths.Scouts.Diremaw, storageValue = 2 }, + { storageName = "ThreatenedDreams.QuestLine", storage = Storage.Quest.U11_40.ThreatenedDreams.QuestLine, storageValue = 1 }, + { storageName = "ThreatenedDreams.Mission01[1]", storage = Storage.Quest.U11_40.ThreatenedDreams.Mission01[1], storageValue = 16 }, + { storageName = "ThreatenedDreams.Mission02.KroazurAccess", storage = Storage.Quest.U11_40.ThreatenedDreams.Mission02.KroazurAccess, storageValue = 1 }, + { storageName = "AdventurersGuild.QuestLine", storage = Storage.Quest.U9_80.AdventurersGuild.QuestLine, storageValue = 1 }, + { storageName = "TheGreatDragonHunt.WarriorSkeleton", storage = Storage.Quest.U10_80.TheGreatDragonHunt.WarriorSkeleton, storageValue = 1 }, + { storageName = "TheGreatDragonHunt.WarriorSkeleton_2", storage = Storage.Quest.U10_80.TheGreatDragonHunt.WarriorSkeleton, storageValue = 2 }, + { storageName = "TheLostBrotherQuest", storage = Storage.Quest.U10_80.TheLostBrotherQuest, storageValue = 3 }, + { storageName = "Dawnport.Questline", storage = Storage.Quest.U10_55.Dawnport.Questline, storageValue = 1 }, + { storageName = "Dawnport.GoMain", storage = Storage.Quest.U10_55.Dawnport.GoMain, storageValue = 1 }, + { storageName = "ForgottenKnowledge.AccessDeath", storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessDeath, storageValue = 1 }, + { storageName = "ForgottenKnowledge.AccessViolet", storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessViolet, storageValue = 1 }, + { storageName = "ForgottenKnowledge.AccessEarth", storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessEarth, storageValue = 1 }, + { storageName = "ForgottenKnowledge.AccessFire", storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessFire, storageValue = 1 }, + { storageName = "ForgottenKnowledge.AccessIce", storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessIce, storageValue = 1 }, + { storageName = "ForgottenKnowledge.AccessGolden", storage = Storage.Quest.U11_02.ForgottenKnowledge.AccessGolden, storageValue = 1 }, + { storageName = "GrimvaleQuest.AncientFeudDoors", storage = Storage.Quest.U10_80.GrimvaleQuest.AncientFeudDoors, storageValue = 1 }, + { storageName = "GrimvaleQuest.AncientFeudShortcut", storage = Storage.Quest.U10_80.GrimvaleQuest.AncientFeudShortcut, storageValue = 1 }, + { storageName = "Kilmaresh.AccessDoor", storage = Storage.Kilmaresh.AccessDoor, storageValue = 1 }, + { storageName = "Kilmaresh.CatacombDoors", storage = Storage.Kilmaresh.CatacombDoors, storageValue = 1 }, + { storageName = "TheOrderOfTheLion.AccessEastSide", storage = Storage.Quest.U12_40.TheOrderOfTheLion.AccessEastSide, storageValue = 1 }, + { storageName = "TheOrderOfTheLion.AccessSouthernSide", storage = Storage.Quest.U12_40.TheOrderOfTheLion.AccessSouthernSide, storageValue = 1 }, + { storageName = "APiratesTail.TentuglyDoor", storage = Storage.Quest.U12_60.APiratesTail.TentuglyDoor, storageValue = 1 }, + { storageName = "APiratesTail.RascacoonShortcut", storage = Storage.Quest.U12_60.APiratesTail.RascacoonShortcut, storageValue = 1 }, + { storageName = "AdventuresOfGalthen.AccessDoor", storage = Storage.Quest.U12_70.AdventuresOfGalthen.AccessDoor, storageValue = 1 }, + { storageName = "CultsOfTibia.Barkless.AccessDoor", storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.AccessDoor, storageValue = 1 }, + { storageName = "CultsOfTibia.Barkless.TrialAccessDoor", storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.TrialAccessDoor, storageValue = 1 }, + { storageName = "CultsOfTibia.Barkless.TarAccessDoor", storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.TarAccessDoor, storageValue = 1 }, + { storageName = "CultsOfTibia.Barkless.BossAccessDoor", storage = Storage.Quest.U11_40.CultsOfTibia.Barkless.BossAccessDoor, storageValue = 1 }, + { storageName = "CultsOfTibia.Life.AccessDoor", storage = Storage.Quest.U11_40.CultsOfTibia.Life.AccessDoor, storageValue = 1 }, + { storageName = "CultsOfTibia.Misguided.AccessDoor", storage = Storage.Quest.U11_40.CultsOfTibia.Misguided.AccessDoor, storageValue = 1 }, + { storageName = "CultsOfTibia.FinalBoss.AccessDoor", storage = Storage.Quest.U11_40.CultsOfTibia.FinalBoss.AccessDoor, storageValue = 1 }, + { storageName = "FerumbrasAscension.FirstDoor", storage = Storage.Quest.U10_90.FerumbrasAscension.FirstDoor, storageValue = 1 }, + { storageName = "FerumbrasAscension.MonsterDoor", storage = Storage.Quest.U10_90.FerumbrasAscension.MonsterDoor, storageValue = 1 }, + { storageName = "FerumbrasAscension.TarbazDoor", storage = Storage.Quest.U10_90.FerumbrasAscension.TarbazDoor, storageValue = 1 }, + { storageName = "FerumbrasAscension.HabitatsAccess", storage = Storage.Quest.U10_90.FerumbrasAscension.HabitatsAccess, storageValue = 1 }, + { storageName = "FerumbrasAscension.TheLordOfTheLiceAccess", storage = Storage.Quest.U10_90.FerumbrasAscension.TheLordOfTheLiceAccess, storageValue = 1 }, + { storageName = "FerumbrasAscension.Statue", storage = Storage.Quest.U10_90.FerumbrasAscension.Statue, storageValue = 1 }, + { storageName = "WrathOfTheEmperor.TeleportAccess.AwarnessEmperor", storage = Storage.Quest.U8_6.WrathOfTheEmperor.TeleportAccess.AwarnessEmperor, storageValue = 1 }, + { storageName = "WrathOfTheEmperor.TeleportAccess.BossRoom", storage = Storage.Quest.U8_6.WrathOfTheEmperor.TeleportAccess.BossRoom, storageValue = 1 }, + { storageName = "WrathOfTheEmperor.TeleportAccess.InnerSanctum", storage = Storage.Quest.U8_6.WrathOfTheEmperor.TeleportAccess.InnerSanctum, storageValue = 1 }, + { storageName = "WrathOfTheEmperor.TeleportAccess.Rebel", storage = Storage.Quest.U8_6.WrathOfTheEmperor.TeleportAccess.Rebel, storageValue = 1 }, + { storageName = "WrathOfTheEmperor.TeleportAccess.SleepingDragon", storage = Storage.Quest.U8_6.WrathOfTheEmperor.TeleportAccess.SleepingDragon, storageValue = 2 }, + { storageName = "WrathOfTheEmperor.TeleportAccess.Wote10", storage = Storage.Quest.U8_6.WrathOfTheEmperor.TeleportAccess.Wote10, storageValue = 1 }, + { storageName = "WrathOfTheEmperor.TeleportAccess.Zizzle", storage = Storage.Quest.U8_6.WrathOfTheEmperor.TeleportAccess.Zizzle, storageValue = 1 }, + { storageName = "WrathOfTheEmperor.TeleportAccess.Zlak", storage = Storage.Quest.U8_6.WrathOfTheEmperor.TeleportAccess.Zlak, storageValue = 1 }, + { storageName = "DjinnWar.EfreetFaction.DoorToLamp", storage = Storage.Quest.U7_4.DjinnWar.EfreetFaction.DoorToLamp, storageValue = 1 }, + { storageName = "DjinnWar.EfreetFaction.DoorToMaridTerritory", storage = Storage.Quest.U7_4.DjinnWar.EfreetFaction.DoorToMaridTerritory, storageValue = 1 }, + { storageName = "DjinnWar.MaridFaction.DoorToLamp", storage = Storage.Quest.U7_4.DjinnWar.MaridFaction.DoorToLamp, storageValue = 1 }, + { storageName = "DjinnWar.MaridFaction.DoorToEfreetTerritory", storage = Storage.Quest.U7_4.DjinnWar.MaridFaction.DoorToEfreetTerritory, storageValue = 1 }, + { storageName = "GraveDanger.QuestLine", storage = Storage.Quest.U12_20.GraveDanger.QuestLine, storageValue = 14 }, + { storageName = "GraveDanger.Bosses.KingZelosDoor", storage = Storage.Quest.U12_20.GraveDanger.Bosses.KingZelosDoor, storageValue = 1 }, + { storageName = "CradleOfMonsters.Access.Ingol", storage = Storage.Quest.U13_10.CradleOfMonsters.Access.Ingol, storageValue = 1 }, + { storageName = "CradleOfMonsters.Access.LowerIngol", storage = Storage.Quest.U13_10.CradleOfMonsters.Access.LowerIngol, storageValue = 1 }, + { storageName = "CradleOfMonsters.Access.Monster", storage = Storage.Quest.U13_10.CradleOfMonsters.Access.Monster, storageValue = 1 }, + { storageName = "CradleOfMonsters.Access.MutatedAbomination", storage = Storage.Quest.U13_10.CradleOfMonsters.Access.MutatedAbomination, storageValue = 1 }, + { storageName = "TheNewFrontier.SnakeHeadTeleport", storage = Storage.Quest.U8_54.TheNewFrontier.SnakeHeadTeleport, storageValue = 1 }, + { storageName = "LiquidBlackQuest.Visitor", storage = Storage.Quest.U9_4.LiquidBlackQuest.Visitor, storageValue = 5 }, + { storageName = "BloodBrothers.VengothAccess", storage = Storage.Quest.U8_4.BloodBrothers.VengothAccess, storageValue = 1 }, + { storageName = "KillingInTheNameOf.BudrikMinos", storage = Storage.Quest.U8_5.KillingInTheNameOf.BudrikMinos, storageValue = 0 }, + { storageName = "ToOutfoxAFoxQuest.Questline", storage = Storage.Quest.U8_1.ToOutfoxAFoxQuest.Questline, storageValue = 2 }, + { storageName = "HunterOutfits.HunterMusicSheet01", storage = Storage.Quest.U7_8.HunterOutfits.HunterMusicSheet01, storageValue = 1 }, + { storageName = "HunterOutfits.HunterMusicSheet02", storage = Storage.Quest.U7_8.HunterOutfits.HunterMusicSheet02, storageValue = 1 }, + { storageName = "HunterOutfits.HunterMusicSheet03", storage = Storage.Quest.U7_8.HunterOutfits.HunterMusicSheet03, storageValue = 1 }, + { storageName = "HunterOutfits.HunterMusicSheet04", storage = Storage.Quest.U7_8.HunterOutfits.HunterMusicSheet04, storageValue = 1 }, + { storageName = "TheIceIslands.NorsemanOutfit", storage = Storage.Quest.U8_0.TheIceIslands.NorsemanOutfit, storageValue = 1 }, + { storageName = "OutfitQuest.DefaultStart", storage = Storage.OutfitQuest.DefaultStart, storageValue = 1 }, + { storageName = "HeroRathleton.AccessDoor", storage = Storage.HeroRathleton.AccessDoor, storageValue = 1 }, + { storageName = "HeroRathleton.AccessTeleport1", storage = Storage.HeroRathleton.AccessTeleport1, storageValue = 1 }, + { storageName = "HeroRathleton.AccessTeleport2", storage = Storage.HeroRathleton.AccessTeleport2, storageValue = 1 }, + { storageName = "HeroRathleton.AccessTeleport3", storage = Storage.HeroRathleton.AccessTeleport3, storageValue = 1 }, + { storageName = "TheHuntForTheSeaSerpent.FishForASerpent", storage = Storage.Quest.U8_2.TheHuntForTheSeaSerpent.FishForASerpent, storageValue = 5 }, + { storageName = "TheHuntForTheSeaSerpent.QuestLine", storage = Storage.Quest.U8_2.TheHuntForTheSeaSerpent.QuestLine, storageValue = 2 }, + { storageName = "TheWhiteRavenMonastery.QuestLog", storage = Storage.Quest.U7_24.TheWhiteRavenMonastery.QuestLog, storageValue = 1 }, + { storageName = "TheWhiteRavenMonastery.Passage", storage = Storage.Quest.U7_24.TheWhiteRavenMonastery.Passage, storageValue = 1 }, + { storageName = "TheWhiteRavenMonastery.Diary", storage = Storage.Quest.U7_24.TheWhiteRavenMonastery.Diary, storageValue = 2 }, + { storageName = "TheWhiteRavenMonastery.Door", storage = Storage.Quest.U7_24.TheWhiteRavenMonastery.Door, storageValue = 1 }, } -- from Position: (33201, 31762, 1) @@ -405,8 +367,15 @@ local function playerFreeQuestStart(playerId, index) return end - if player:getStorageValue(questTable[index].storage) ~= questTable[index].storageValue then - player:setStorageValue(questTable[index].storage, questTable[index].storageValue) + local questData = questTable[index] + local currentStorageValue = player:getStorageValue(questData.storage) + + if not questData.storage then + logger.warn("[Freequest System]: error storage for '" .. questData.storageName .. "' is nil for the index") + elseif currentStorageValue ~= questData.storageValue then + player:setStorageValue(questData.storage, questData.storageValue) + elseif currentStorageValue == -1 then + logger.warn("[Freequest System]: warning Storage '" .. questData.storageName .. "' currently nil for player ID " .. playerId) end end From af80b3dc8e72bbcd3270ff3737deb79696822ccb Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Tue, 12 Nov 2024 20:35:09 -0300 Subject: [PATCH 03/12] fix: safe call add creatures on tile get/creation (#3090) --- src/map/mapcache.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/map/mapcache.cpp b/src/map/mapcache.cpp index cf2f27c6ee7..79a9683cfca 100644 --- a/src/map/mapcache.cpp +++ b/src/map/mapcache.cpp @@ -132,10 +132,6 @@ std::shared_ptr MapCache::getOrCreateTileFromCache(const std::shared_ptrinternalAddThing(creature); - } - if (cachedTile->ground != nullptr) { tile->internalAddThing(createItem(cachedTile->ground, pos)); } @@ -146,7 +142,11 @@ std::shared_ptr MapCache::getOrCreateTileFromCache(const std::shared_ptrsetFlag(static_cast(cachedTile->flags)); - tile->safeCall([tile, pos] { + tile->safeCall([tile, pos, movedOldCreatureList = std::move(oldCreatureList)]() { + for (const auto &creature : movedOldCreatureList) { + tile->internalAddThing(creature); + } + for (const auto &zone : Zone::getZones(pos)) { tile->addZone(zone); } From ee946aa9a4b3edeae9c12b0568e5c864805797f6 Mon Sep 17 00:00:00 2001 From: Felipe Paluco <87909998+FelipePaluco@users.noreply.github.com> Date: Tue, 12 Nov 2024 20:35:38 -0300 Subject: [PATCH 04/12] fix: combat type out of index in ashes item (#3106) Fixes #2874 --- data/items/items.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/items/items.xml b/data/items/items.xml index 72c9ba04142..c1b8e8a63ba 100644 --- a/data/items/items.xml +++ b/data/items/items.xml @@ -4435,6 +4435,9 @@ + + + From 350b04e8beb2c39e56897d3f069de3286935b753 Mon Sep 17 00:00:00 2001 From: Felipe Muniz Date: Tue, 12 Nov 2024 22:03:56 -0300 Subject: [PATCH 05/12] fix: remove bit_functions from vcxproj (#3108) At some point, the bit_functions.cpp and bit_functions.h files were removed from the project, but they continued to be referenced in the Visual Studio .vcxproj file, causing an error when trying to compile using it. --- vcproj/canary.vcxproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/vcproj/canary.vcxproj b/vcproj/canary.vcxproj index 7cbdd118ccc..6436dd704a9 100644 --- a/vcproj/canary.vcxproj +++ b/vcproj/canary.vcxproj @@ -137,7 +137,6 @@ - @@ -336,7 +335,6 @@ - From 10b19dfceb0dc97f930503303165e17c3e1e1a04 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Tue, 12 Nov 2024 22:05:25 -0300 Subject: [PATCH 06/12] fix: add source location to combatTypeToIndex (#3095) --- src/creatures/players/player.cpp | 2 -- src/utils/tools.cpp | 4 ++-- src/utils/tools.hpp | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index a8ef732b900..b538d042367 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -8820,8 +8820,6 @@ void Player::triggerTranscendance() { } // Forge system -// Forge system - void Player::forgeFuseItems(ForgeAction_t actionType, uint16_t firstItemId, uint8_t tier, uint16_t secondItemId, bool success, bool reduceTierLoss, bool convergence, uint8_t bonus, uint8_t coreCount) { if (getFreeBackpackSlots() == 0) { sendCancelMessage(RETURNVALUE_NOTENOUGHROOM); diff --git a/src/utils/tools.cpp b/src/utils/tools.cpp index f393f5332ae..fff20962d15 100644 --- a/src/utils/tools.cpp +++ b/src/utils/tools.cpp @@ -1145,12 +1145,12 @@ CombatType_t getCombatTypeByName(const std::string &combatname) { return it != combatTypeNames.end() ? it->first : COMBAT_NONE; } -size_t combatTypeToIndex(CombatType_t combatType) { +size_t combatTypeToIndex(CombatType_t combatType, std::source_location location) { const auto enum_index_opt = magic_enum::enum_index(combatType); if (enum_index_opt.has_value() && enum_index_opt.value() < COMBAT_COUNT) { return enum_index_opt.value(); } else { - g_logger().error("[{}] Combat type {} is out of range", __FUNCTION__, fmt::underlying(combatType)); + g_logger().error("[{}] Combat type {} is out of range, called line '{}:{}' in '{}'", __FUNCTION__, fmt::underlying(combatType), location.line(), location.column(), location.function_name()); // Uncomment for catch the function call with debug // throw std::out_of_range("Combat is out of range"); } diff --git a/src/utils/tools.hpp b/src/utils/tools.hpp index 7a7d4a22bdd..9f7f15dfb5d 100644 --- a/src/utils/tools.hpp +++ b/src/utils/tools.hpp @@ -137,7 +137,7 @@ CombatType_t getCombatTypeByName(const std::string &combatname); * @return The corresponding index of the CombatType_t enumeration. * If the CombatType_t is out of range, this function will log an error and return an empty size_t. */ -size_t combatTypeToIndex(CombatType_t combatType); +size_t combatTypeToIndex(CombatType_t combatType, std::source_location location = std::source_location::current()); /** * @brief Convert the CombatType_t enumeration to its corresponding string representation. From 1382b7767907d55ce7f19753de4de4cf2cefa981 Mon Sep 17 00:00:00 2001 From: Pedro Cruz Date: Tue, 12 Nov 2024 22:06:10 -0300 Subject: [PATCH 07/12] fix: wheel bonuses (#3058) This fixes all vocations wheel spell and gem bonuses, const references and refactors. Fixes #3032 Fixes #2150 Fixes #2039 --- data/scripts/lib/register_spells.lua | 2 +- data/scripts/spells/attack/divine_grenade.lua | 2 +- data/scripts/spells/attack/energy_beam.lua | 7 +- .../spells/attack/executioners_throw.lua | 23 +- .../spells/attack/great_death_beam.lua | 28 +- .../spells/attack/great_energy_beam.lua | 12 +- data/scripts/spells/attack/ice_burst.lua | 37 +- data/scripts/spells/attack/terra_burst.lua | 37 +- .../spells/support/divine_empowerment.lua | 14 +- src/creatures/combat/combat.cpp | 5 - src/creatures/combat/spells.cpp | 5 + src/creatures/creatures_definitions.hpp | 2 + src/creatures/players/player.cpp | 8 +- src/creatures/players/wheel/player_wheel.cpp | 574 ++++++++++-------- src/creatures/players/wheel/player_wheel.hpp | 80 +-- .../players/wheel/wheel_definitions.hpp | 8 +- src/creatures/players/wheel/wheel_gems.cpp | 116 ++-- src/game/game.cpp | 4 +- src/io/io_wheel.cpp | 34 +- src/utils/tools.cpp | 6 + 20 files changed, 476 insertions(+), 528 deletions(-) diff --git a/data/scripts/lib/register_spells.lua b/data/scripts/lib/register_spells.lua index 8c549859640..f2e7cacee3d 100644 --- a/data/scripts/lib/register_spells.lua +++ b/data/scripts/lib/register_spells.lua @@ -386,7 +386,7 @@ AREA_RING1_BURST3 = { { 0, 0, 1, 1, 1, 1, 1, 0, 0 }, { 0, 1, 1, 1, 1, 1, 1, 1, 0 }, { 1, 1, 1, 0, 0, 0, 1, 1, 1 }, - { 1, 1, 1, 0, 3, 0, 1, 1, 1 }, + { 1, 1, 1, 0, 2, 0, 1, 1, 1 }, { 1, 1, 1, 0, 0, 0, 1, 1, 1 }, { 0, 1, 1, 1, 1, 1, 1, 1, 0 }, { 0, 0, 1, 1, 1, 1, 1, 0, 0 }, diff --git a/data/scripts/spells/attack/divine_grenade.lua b/data/scripts/spells/attack/divine_grenade.lua index b1dc6399c7d..488962cb2f8 100644 --- a/data/scripts/spells/attack/divine_grenade.lua +++ b/data/scripts/spells/attack/divine_grenade.lua @@ -35,7 +35,7 @@ local explodeGrenade = function(position, playerId) end local var = {} - var.instantName = "Divine Grenade Explode" + var.instantName = "Divine Grenade" var.runeName = "" var.type = 2 -- VARIANT_POSITION var.pos = position diff --git a/data/scripts/spells/attack/energy_beam.lua b/data/scripts/spells/attack/energy_beam.lua index d3be218c0ca..049470f0ecb 100644 --- a/data/scripts/spells/attack/energy_beam.lua +++ b/data/scripts/spells/attack/energy_beam.lua @@ -28,11 +28,10 @@ local spell = Spell("instant") function spell.onCastSpell(creature, var) local player = creature:getPlayer() - if creature and player and player:instantSkillWOD("Beam Mastery") then - var.runeName = "Beam Mastery" - return combatWOD:execute(creature, var) + if not creature or not player then + return false end - return combat:execute(creature, var) + return player:instantSkillWOD("Beam Mastery") and combatWOD:execute(creature, var) or combat:execute(creature, var) end spell:group("attack") diff --git a/data/scripts/spells/attack/executioners_throw.lua b/data/scripts/spells/attack/executioners_throw.lua index 2f2220625c5..08d700bfe54 100644 --- a/data/scripts/spells/attack/executioners_throw.lua +++ b/data/scripts/spells/attack/executioners_throw.lua @@ -40,28 +40,11 @@ function spell.onCastSpell(creature, var) local grade = creature:revelationStageWOD("Executioner's Throw") if grade == 0 then - creature:sendCancelMessage("You cannot cast this spell") + creature:sendCancelMessage("You need to learn this spell first") creature:getPosition():sendMagicEffect(CONST_ME_POFF) return false end - - local cooldown = 0 - if grade >= 3 then - cooldown = 10 - elseif grade >= 2 then - cooldown = 14 - elseif grade >= 1 then - cooldown = 18 - end - - var.instantName = "Executioner's Throw" - if combat:execute(creature, var) then - local condition = Condition(CONDITION_SPELLCOOLDOWN, CONDITIONID_DEFAULT, 261) - condition:setTicks((cooldown * 1000) / configManager.getFloat(configKeys.RATE_SPELL_COOLDOWN)) - creature:addCondition(condition) - return true - end - return false + return combat:execute(creature, var) end spell:group("attack") @@ -75,7 +58,7 @@ spell:range(5) spell:needTarget(true) spell:blockWalls(true) spell:needWeapon(true) -spell:cooldown(1000) -- Cooldown is calculated on the casting +spell:cooldown(18 * 1000) spell:groupCooldown(2 * 1000) spell:needLearn(true) spell:vocation("knight;true", "elite knight;true") diff --git a/data/scripts/spells/attack/great_death_beam.lua b/data/scripts/spells/attack/great_death_beam.lua index 02a17c78f41..84c75132d76 100644 --- a/data/scripts/spells/attack/great_death_beam.lua +++ b/data/scripts/spells/attack/great_death_beam.lua @@ -17,6 +17,7 @@ end local combat1 = createCombat(initCombat, AREA_BEAM6) local combat2 = createCombat(initCombat, AREA_BEAM7) local combat3 = createCombat(initCombat, AREA_BEAM8) +local combat = { combat1, combat2, combat3 } local spell = Spell("instant") @@ -28,32 +29,15 @@ function spell.onCastSpell(creature, var) local grade = creature:upgradeSpellsWOD("Great Death Beam") if grade == WHEEL_GRADE_NONE then - creature:sendCancelMessage("You cannot cast this spell") + creature:sendCancelMessage("You need to learn this spell first") creature:getPosition():sendMagicEffect(CONST_ME_POFF) return false end - local cooldown = { 10, 8, 6 } - var.runeName = "Beam Mastery" - local executed = false - - local combat = { combat1, combat2, combat3 } - - executed = combat[grade]:execute(creature, var) - - if executed then - local condition = Condition(CONDITION_SPELLCOOLDOWN, CONDITIONID_DEFAULT, 260) - local executedCooldown = cooldown[grade] - if executedCooldown ~= nil then - condition:setTicks((executedCooldown * 1000)) - end - creature:addCondition(condition) - return true - end - return false + return combat[grade]:execute(creature, var) end -spell:group("attack") +spell:group("attack", "greatbeams") spell:id(260) spell:name("Great Death Beam") spell:words("exevo max mort") @@ -62,8 +46,8 @@ spell:mana(140) spell:isPremium(false) spell:needDirection(true) spell:blockWalls(true) -spell:cooldown(1000) -- Cooldown is calculated on the casting -spell:groupCooldown(2 * 1000) +spell:cooldown(10 * 1000) +spell:groupCooldown(2 * 1000, 6 * 1000) spell:needLearn(true) spell:vocation("sorcerer;true", "master sorcerer;true") spell:register() diff --git a/data/scripts/spells/attack/great_energy_beam.lua b/data/scripts/spells/attack/great_energy_beam.lua index aae93e95c9a..f6b61f19fa4 100644 --- a/data/scripts/spells/attack/great_energy_beam.lua +++ b/data/scripts/spells/attack/great_energy_beam.lua @@ -28,15 +28,13 @@ local spell = Spell("instant") function spell.onCastSpell(creature, var) local player = creature:getPlayer() - if creature and player and player:instantSkillWOD("Beam Mastery") then - var.runeName = "Beam Mastery" - return combatWOD:execute(creature, var) + if not creature or not player then + return false end - - return combat:execute(creature, var) + return player:instantSkillWOD("Beam Mastery") and combatWOD:execute(creature, var) or combat:execute(creature, var) end -spell:group("attack") +spell:group("attack", "greatbeams") spell:id(23) spell:name("Great Energy Beam") spell:words("exevo gran vis lux") @@ -47,7 +45,7 @@ spell:isPremium(false) spell:needDirection(true) spell:blockWalls(true) spell:cooldown(6 * 1000) -spell:groupCooldown(2 * 1000) +spell:groupCooldown(2 * 1000, 6 * 1000) spell:needLearn(false) spell:vocation("sorcerer;true", "master sorcerer;true") spell:register() diff --git a/data/scripts/spells/attack/ice_burst.lua b/data/scripts/spells/attack/ice_burst.lua index 9e1560f9dc9..fedcd9d5a04 100644 --- a/data/scripts/spells/attack/ice_burst.lua +++ b/data/scripts/spells/attack/ice_burst.lua @@ -14,50 +14,27 @@ combat:setCallback(CALLBACK_PARAM_LEVELMAGICVALUE, "onGetFormulaValues") local spell = Spell("instant") function spell.onCastSpell(creature, var) - if not creature or not creature:isPlayer() then - return false - end - local grade = creature:revelationStageWOD("Twin Burst") if grade == 0 then - creature:sendCancelMessage("You cannot cast this spell") + creature:sendCancelMessage("You need to learn this spell first") creature:getPosition():sendMagicEffect(CONST_ME_POFF) return false end - local cooldown = 0 - if grade >= 3 then - cooldown = 14 - elseif grade >= 2 then - cooldown = 18 - elseif grade >= 1 then - cooldown = 22 - end - - var.instantName = "Twin Burst" - if combat:execute(creature, var) then - -- Ice cooldown - local condition1 = Condition(CONDITION_SPELLCOOLDOWN, CONDITIONID_DEFAULT, 262) - condition1:setTicks((cooldown * 1000) / configManager.getFloat(configKeys.RATE_SPELL_COOLDOWN)) - creature:addCondition(condition1) - -- Earth cooldown - local condition2 = Condition(CONDITION_SPELLCOOLDOWN, CONDITIONID_DEFAULT, 263) - condition2:setTicks((cooldown * 1000) / configManager.getFloat(configKeys.RATE_SPELL_COOLDOWN)) - creature:addCondition(condition2) - return true - end - return false + return combat:execute(creature, var) end -spell:group("attack") +spell:group("attack", "burstsofnature") spell:id(262) spell:name("Ice Burst") spell:words("exevo ulus frigo") +spell:castSound(SOUND_EFFECT_TYPE_SPELL_ETERNAL_WINTER) spell:level(300) spell:mana(230) spell:isPremium(true) -spell:cooldown(1000) -- Cooldown is calculated on the casting -spell:groupCooldown(2 * 1000) +spell:isSelfTarget(true) +spell:cooldown(22 * 1000) +spell:groupCooldown(2 * 1000, 22 * 1000) spell:needLearn(true) spell:vocation("druid;true", "elder druid;true") spell:register() diff --git a/data/scripts/spells/attack/terra_burst.lua b/data/scripts/spells/attack/terra_burst.lua index 8a805c0ba48..15ce35c4912 100644 --- a/data/scripts/spells/attack/terra_burst.lua +++ b/data/scripts/spells/attack/terra_burst.lua @@ -14,50 +14,27 @@ combat:setCallback(CALLBACK_PARAM_LEVELMAGICVALUE, "onGetFormulaValues") local spell = Spell("instant") function spell.onCastSpell(creature, var) - if not creature or not creature:isPlayer() then - return false - end - local grade = creature:revelationStageWOD("Twin Burst") if grade == 0 then - creature:sendCancelMessage("You cannot cast this spell") + creature:sendCancelMessage("You need to learn this spell first") creature:getPosition():sendMagicEffect(CONST_ME_POFF) return false end - local cooldown = 0 - if grade >= 3 then - cooldown = 14 - elseif grade >= 2 then - cooldown = 18 - elseif grade >= 1 then - cooldown = 22 - end - - var.instantName = "Twin Burst" - if combat:execute(creature, var) then - -- Ice cooldown - local condition1 = Condition(CONDITION_SPELLCOOLDOWN, CONDITIONID_DEFAULT, 262) - condition1:setTicks((cooldown * 1000) / configManager.getFloat(configKeys.RATE_SPELL_COOLDOWN)) - creature:addCondition(condition1) - -- Earth cooldown - local condition2 = Condition(CONDITION_SPELLCOOLDOWN, CONDITIONID_DEFAULT, 263) - condition2:setTicks((cooldown * 1000) / configManager.getFloat(configKeys.RATE_SPELL_COOLDOWN)) - creature:addCondition(condition2) - return true - end - return false + return combat:execute(creature, var) end -spell:group("attack") +spell:group("attack", "burstsofnature") spell:id(263) spell:name("Terra Burst") spell:words("exevo ulus tera") +spell:castSound(SOUND_EFFECT_TYPE_SPELL_WRATH_OF_NATURE) spell:level(300) spell:mana(230) spell:isPremium(true) -spell:cooldown(1000) -- Cooldown is calculated on the casting -spell:groupCooldown(2 * 1000) +spell:isSelfTarget(true) +spell:cooldown(22 * 1000) +spell:groupCooldown(2 * 1000, 22 * 1000) spell:needLearn(true) spell:vocation("druid;true", "elder druid;true") spell:register() diff --git a/data/scripts/spells/support/divine_empowerment.lua b/data/scripts/spells/support/divine_empowerment.lua index e05203359f8..248f01a6e0f 100644 --- a/data/scripts/spells/support/divine_empowerment.lua +++ b/data/scripts/spells/support/divine_empowerment.lua @@ -26,18 +26,6 @@ function spell.onCastSpell(creature, var) return false end - local cooldown = 0 - if grade >= 3 then - cooldown = 24 - elseif grade >= 2 then - cooldown = 28 - elseif grade >= 1 then - cooldown = 32 - end - local condition = Condition(CONDITION_SPELLCOOLDOWN, CONDITIONID_DEFAULT, 268) - condition:setTicks((cooldown * 1000) / configManager.getFloat(configKeys.RATE_SPELL_COOLDOWN)) - creature:addCondition(condition) - local position = creature:getPosition() for x = -1, 1 do for y = -1, 1 do @@ -63,7 +51,7 @@ spell:isPremium(true) spell:range(7) spell:isSelfTarget(true) spell:isAggressive(false) -spell:cooldown(1000) -- Cooldown is calculated on the casting +spell:cooldown(32 * 1000) spell:groupCooldown(2 * 1000) spell:needLearn(true) spell:vocation("paladin;true", "royal paladin;true") diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index 11b5bceb563..823d94ab519 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -1281,11 +1281,6 @@ void Combat::CombatFunc(const std::shared_ptr &caster, const Position combatTileEffects(spectators.data(), caster, tile, params); } - // Wheel of destiny update beam mastery damage - if (casterPlayer) { - casterPlayer->wheel()->updateBeamMasteryDamage(tmpDamage, beamAffectedTotal, beamAffectedCurrent); - } - postCombatEffects(caster, origin, pos, params); } diff --git a/src/creatures/combat/spells.cpp b/src/creatures/combat/spells.cpp index 47ae56c7dd6..327d97f3284 100644 --- a/src/creatures/combat/spells.cpp +++ b/src/creatures/combat/spells.cpp @@ -749,7 +749,10 @@ void Spell::applyCooldownConditions(const std::shared_ptr &player) const g_logger().debug("[{}] spell name: {}, spellCooldown: {}, bonus: {}, augment {}", __FUNCTION__, name, spellCooldown, player->wheel()->getSpellBonus(name, WheelSpellBoost_t::COOLDOWN), augmentCooldownReduction); spellCooldown -= player->wheel()->getSpellBonus(name, WheelSpellBoost_t::COOLDOWN); spellCooldown -= augmentCooldownReduction; + const int32_t halfBaseCooldown = cooldown / 2; + spellCooldown = halfBaseCooldown > spellCooldown ? halfBaseCooldown : spellCooldown; // The cooldown should never be reduced less than half (50%) of its base cooldown if (spellCooldown > 0) { + player->wheel()->handleTwinBurstsCooldown(player, name, spellCooldown, rateCooldown); const auto &condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_SPELLCOOLDOWN, spellCooldown / rateCooldown, 0, false, m_spellId); player->addCondition(condition); } @@ -771,7 +774,9 @@ void Spell::applyCooldownConditions(const std::shared_ptr &player) const if (isUpgraded) { spellSecondaryGroupCooldown -= getWheelOfDestinyBoost(WheelSpellBoost_t::SECONDARY_GROUP_COOLDOWN, spellGrade); } + spellSecondaryGroupCooldown -= player->wheel()->getSpellBonus(name, WheelSpellBoost_t::SECONDARY_GROUP_COOLDOWN); if (spellSecondaryGroupCooldown > 0) { + player->wheel()->handleBeamMasteryCooldown(player, name, spellSecondaryGroupCooldown, rateCooldown); const auto &condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_SPELLGROUPCOOLDOWN, spellSecondaryGroupCooldown / rateCooldown, 0, false, secondaryGroup); player->addCondition(condition); } diff --git a/src/creatures/creatures_definitions.hpp b/src/creatures/creatures_definitions.hpp index f7ba1e203b1..dd554c086b3 100644 --- a/src/creatures/creatures_definitions.hpp +++ b/src/creatures/creatures_definitions.hpp @@ -706,6 +706,8 @@ enum SpellGroup_t : uint8_t { SPELLGROUP_CRIPPLING = 6, SPELLGROUP_FOCUS = 7, SPELLGROUP_ULTIMATESTRIKES = 8, + SPELLGROUP_BURSTS_OF_NATURE = 9, + SPELLGROUP_GREAT_BEAMS = 10, }; enum ChannelEvent_t : uint8_t { diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index b538d042367..823bd38e9e3 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -6339,7 +6339,7 @@ uint32_t Player::getMagicLevel() const { uint32_t magic = std::max(0, getLoyaltyMagicLevel() + varStats[STAT_MAGICPOINTS]); // Wheel of destiny magic bonus magic += m_wheelPlayer->getStat(WheelStat_t::MAGIC); // Regular bonus - magic += m_wheelPlayer->getMajorStatConditional("Positional Tatics", WheelMajor_t::MAGIC); // Revelation bonus + magic += m_wheelPlayer->getMajorStatConditional("Positional Tactics", WheelMajor_t::MAGIC); // Revelation bonus return magic; } @@ -6397,12 +6397,12 @@ uint16_t Player::getSkillLevel(skills_t skill) const { skillLevel += m_wheelPlayer->getStat(WheelStat_t::MELEE); skillLevel += m_wheelPlayer->getMajorStatConditional("Battle Instinct", WheelMajor_t::MELEE); } else if (skill == SKILL_DISTANCE) { - skillLevel += m_wheelPlayer->getMajorStatConditional("Positional Tatics", WheelMajor_t::DISTANCE); + skillLevel += m_wheelPlayer->getMajorStatConditional("Positional Tactics", WheelMajor_t::DISTANCE); skillLevel += m_wheelPlayer->getStat(WheelStat_t::DISTANCE); } else if (skill == SKILL_SHIELD) { skillLevel += m_wheelPlayer->getMajorStatConditional("Battle Instinct", WheelMajor_t::SHIELD); } else if (skill == SKILL_MAGLEVEL) { - skillLevel += m_wheelPlayer->getMajorStatConditional("Positional Tatics", WheelMajor_t::MAGIC); + skillLevel += m_wheelPlayer->getMajorStatConditional("Positional Tactics", WheelMajor_t::MAGIC); skillLevel += m_wheelPlayer->getStat(WheelStat_t::MAGIC); } else if (skill == SKILL_LIFE_LEECH_AMOUNT) { skillLevel += m_wheelPlayer->getStat(WheelStat_t::LIFE_LEECH); @@ -6504,7 +6504,7 @@ void Player::setPerfectShotDamage(uint8_t range, int32_t damage) { } int32_t Player::getSpecializedMagicLevel(CombatType_t combat, bool useCharges) const { - int32_t result = specializedMagicLevel[combatTypeToIndex(combat)]; + int32_t result = specializedMagicLevel[combatTypeToIndex(combat)] + m_wheelPlayer->getSpecializedMagic(combat); for (const auto &item : getEquippedItems()) { const ItemType &itemType = Item::items[item->getID()]; if (!itemType.abilities) { diff --git a/src/creatures/players/wheel/player_wheel.cpp b/src/creatures/players/wheel/player_wheel.cpp index 6d3ee2713a7..7d39720dc4d 100644 --- a/src/creatures/players/wheel/player_wheel.cpp +++ b/src/creatures/players/wheel/player_wheel.cpp @@ -9,6 +9,8 @@ #include "creatures/players/wheel/player_wheel.hpp" +#include "map/spectators.hpp" +#include "creatures/monsters/monster.hpp" #include "config/configmanager.hpp" #include "creatures/combat/condition.hpp" #include "creatures/combat/spells.hpp" @@ -957,6 +959,45 @@ int PlayerWheel::getSpellAdditionalDuration(const std::string &spellName) const return 0; } +bool PlayerWheel::handleTwinBurstsCooldown(const std::shared_ptr &player, const std::string &spellName, int spellCooldown, int rateCooldown) const { + // Map of spell pairs for Twin Bursts + static const std::unordered_map spellPairs = { + { "Terra Burst", "Ice Burst" }, + { "Ice Burst", "Terra Burst" } + }; + + auto it = spellPairs.find(spellName); + if (it != spellPairs.end()) { + const auto &spell = g_spells().getSpellByName(it->second); + if (spell) { + const auto spellId = spell->getSpellId(); + const auto &condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_SPELLCOOLDOWN, spellCooldown / rateCooldown, 0, false, spellId); + return player->addCondition(condition); + } + } + + return false; +} + +bool PlayerWheel::handleBeamMasteryCooldown(const std::shared_ptr &player, const std::string &spellName, int spellCooldown, int rateCooldown) const { + static const std::unordered_map spellPairs = { + { "Great Death Beam", "Great Energy Beam" }, + { "Great Energy Beam", "Great Death Beam" } + }; + + auto it = spellPairs.find(spellName); + if (it != spellPairs.end()) { + const auto &spell = g_spells().getSpellByName(it->second); + if (spell) { + const auto spellId = spell->getSpellId(); + const auto &condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_SPELLCOOLDOWN, spellCooldown / rateCooldown, 0, false, spellId); + return player->addCondition(condition); + } + } + + return false; +} + void PlayerWheel::addPromotionScrolls(NetworkMessage &msg) const { std::vector unlockedScrolls; @@ -988,7 +1029,7 @@ std::shared_ptr PlayerWheel::gemsGradeKV(WheelFragmentType_t type, uint8_t p uint8_t PlayerWheel::getGemGrade(WheelFragmentType_t type, uint8_t pos) const { uint8_t grade = 0; - auto gradeKV = gemsGradeKV(type, pos)->get("grade"); + const auto gradeKV = gemsGradeKV(type, pos)->get("grade"); if (gradeKV.has_value()) { grade = static_cast(gradeKV->get()); @@ -1190,7 +1231,7 @@ void PlayerWheel::destroyGem(uint16_t index) const { if (greaterFragments > 0) { const auto &fragmentsItem = Item::CreateItem(ITEM_GREATER_FRAGMENT, greaterFragments); - auto returnValue = g_game().internalPlayerAddItem(m_player.getPlayer(), fragmentsItem, false, CONST_SLOT_BACKPACK); + auto returnValue = g_game().internalPlayerAddItem(m_player.getPlayer(), fragmentsItem, false, CONST_SLOT_WHEREEVER); if (returnValue != RETURNVALUE_NOERROR) { g_logger().error("Failed to add {} greater fragments to player with name {}", greaterFragments, m_player.getName()); m_player.sendCancelMessage(getReturnMessage(RETURNVALUE_CONTACTADMINISTRATOR)); @@ -1201,8 +1242,11 @@ void PlayerWheel::destroyGem(uint16_t index) const { gem.remove(gemsKV()); - m_player.client->sendResourceBalance(RESOURCE_LESSER_FRAGMENT, m_player.getItemTypeCount(ITEM_LESSER_FRAGMENT)); - m_player.client->sendResourceBalance(RESOURCE_GREATER_FRAGMENT, m_player.getItemTypeCount(ITEM_GREATER_FRAGMENT)); + const auto totalLesserFragment = m_player.getItemTypeCount(ITEM_LESSER_FRAGMENT) + m_player.getStashItemCount(ITEM_LESSER_FRAGMENT); + const auto totalGreaterFragment = m_player.getItemTypeCount(ITEM_GREATER_FRAGMENT) + m_player.getStashItemCount(ITEM_GREATER_FRAGMENT); + + m_player.client->sendResourceBalance(RESOURCE_LESSER_FRAGMENT, totalLesserFragment); + m_player.client->sendResourceBalance(RESOURCE_GREATER_FRAGMENT, totalGreaterFragment); sendOpenWheelWindow(m_player.getID()); } @@ -1233,7 +1277,7 @@ void PlayerWheel::toggleGemLock(uint16_t index) const { } void PlayerWheel::setActiveGem(WheelGemAffinity_t affinity, uint16_t index) const { - const auto gem = getGem(index); + auto gem = getGem(index); if (gem.uuid.empty()) { g_logger().error("[{}] Failed to load gem with index {}", __FUNCTION__, index); return; @@ -1251,6 +1295,67 @@ void PlayerWheel::removeActiveGem(WheelGemAffinity_t affinity) const { gemsKV()->scoped("active")->remove(key); } +void PlayerWheel::addRevelationBonus(WheelGemAffinity_t affinity, uint16_t points) { + m_bonusRevelationPoints[static_cast(affinity)] += points; +} + +void PlayerWheel::resetRevelationBonus() { + m_bonusRevelationPoints = { 0, 0, 0, 0 }; +} + +void PlayerWheel::addSpellBonus(const std::string &spellName, const WheelSpells::Bonus &bonus) { + if (m_spellsBonuses.contains(spellName)) { + m_spellsBonuses[spellName].decrease.cooldown += bonus.decrease.cooldown; + m_spellsBonuses[spellName].decrease.manaCost += bonus.decrease.manaCost; + m_spellsBonuses[spellName].decrease.secondaryGroupCooldown += bonus.decrease.secondaryGroupCooldown; + m_spellsBonuses[spellName].increase.aditionalTarget += bonus.increase.aditionalTarget; + m_spellsBonuses[spellName].increase.area = bonus.increase.area; + m_spellsBonuses[spellName].increase.criticalChance += bonus.increase.criticalChance; + m_spellsBonuses[spellName].increase.criticalDamage += bonus.increase.criticalDamage; + m_spellsBonuses[spellName].increase.damage += bonus.increase.damage; + m_spellsBonuses[spellName].increase.damageReduction += bonus.increase.damageReduction; + m_spellsBonuses[spellName].increase.duration += bonus.increase.duration; + m_spellsBonuses[spellName].increase.heal += bonus.increase.heal; + m_spellsBonuses[spellName].leech.life += bonus.leech.life; + m_spellsBonuses[spellName].leech.mana += bonus.leech.mana; + return; + } + m_spellsBonuses[spellName] = bonus; +} + +int32_t PlayerWheel::getSpellBonus(const std::string &spellName, WheelSpellBoost_t boost) const { + using enum WheelSpellBoost_t; + + if (!m_spellsBonuses.contains(spellName)) { + return 0; + } + const auto &[leech, increase, decrease] = m_spellsBonuses.at(spellName); + switch (boost) { + case COOLDOWN: + return decrease.cooldown; + case MANA: + return decrease.manaCost; + case SECONDARY_GROUP_COOLDOWN: + return decrease.secondaryGroupCooldown; + case CRITICAL_CHANCE: + return increase.criticalChance; + case CRITICAL_DAMAGE: + return increase.criticalDamage; + case DAMAGE: + return increase.damage; + case DAMAGE_REDUCTION: + return increase.damageReduction; + case HEAL: + return increase.heal; + case LIFE_LEECH: + return leech.life; + case MANA_LEECH: + return leech.mana; + default: + return 0; + } +} + void PlayerWheel::addGems(NetworkMessage &msg) const { const auto activeGems = getActiveGems(); msg.addByte(activeGems.size()); @@ -1343,7 +1448,7 @@ void PlayerWheel::improveGemGrade(WheelFragmentType_t fragmentType, uint8_t pos) return; } - if (!m_player.hasItemCountById(fragmentId, quantity, false)) { + if (!m_player.hasItemCountById(fragmentId, quantity, true)) { g_logger().error("[{}] Player {} does not have the required {} fragments with id {}", __FUNCTION__, m_player.getName(), quantity, fragmentId); return; } @@ -1353,7 +1458,7 @@ void PlayerWheel::improveGemGrade(WheelFragmentType_t fragmentType, uint8_t pos) return; } - if (!m_player.removeItemCountById(fragmentId, quantity, false)) { + if (!m_player.removeItemCountById(fragmentId, quantity, true)) { g_logger().error("[{}] Failed to remove {} fragments with id {} from player {}", std::source_location::current().function_name(), quantity, fragmentId, m_player.getName()); return; } @@ -1414,20 +1519,23 @@ void PlayerWheel::sendOpenWheelWindow(NetworkMessage &msg, uint32_t ownerId) { addPromotionScrolls(msg); addGems(msg); addGradeModifiers(msg); - // TODO: read items from inventory + const auto &voc = m_player.getVocation(); if (!voc) { g_logger().error("[{}] Failed to get vocation for player {}", __FUNCTION__, m_player.getName()); return; } + const auto totalLesserFragment = m_player.getItemTypeCount(ITEM_LESSER_FRAGMENT) + m_player.getStashItemCount(ITEM_LESSER_FRAGMENT); + const auto totalGreaterFragment = m_player.getItemTypeCount(ITEM_GREATER_FRAGMENT) + m_player.getStashItemCount(ITEM_GREATER_FRAGMENT); + m_player.client->sendResourceBalance(RESOURCE_BANK, m_player.getBankBalance()); m_player.client->sendResourceBalance(RESOURCE_INVENTORY_MONEY, m_player.getMoney()); m_player.client->sendResourceBalance(RESOURCE_LESSER_GEMS, m_player.getItemTypeCount(voc->getWheelGemId(WheelGemQuality_t::Lesser))); m_player.client->sendResourceBalance(RESOURCE_REGULAR_GEMS, m_player.getItemTypeCount(voc->getWheelGemId(WheelGemQuality_t::Regular))); m_player.client->sendResourceBalance(RESOURCE_GREATER_GEMS, m_player.getItemTypeCount(voc->getWheelGemId(WheelGemQuality_t::Greater))); - m_player.client->sendResourceBalance(RESOURCE_LESSER_FRAGMENT, m_player.getItemTypeCount(ITEM_LESSER_FRAGMENT)); - m_player.client->sendResourceBalance(RESOURCE_GREATER_FRAGMENT, m_player.getItemTypeCount(ITEM_GREATER_FRAGMENT)); + m_player.client->sendResourceBalance(RESOURCE_LESSER_FRAGMENT, totalLesserFragment); + m_player.client->sendResourceBalance(RESOURCE_GREATER_FRAGMENT, totalGreaterFragment); } void PlayerWheel::sendGiftOfLifeCooldown() const { @@ -1569,7 +1677,7 @@ void PlayerWheel::saveSlotPointsOnPressSaveButton(NetworkMessage &msg) { */ void PlayerWheel::loadDBPlayerSlotPointsOnLogin() { auto resultString = fmt::format("SELECT `slot` FROM `player_wheeldata` WHERE `player_id` = {}", m_player.getGUID()); - const DBResult_ptr &result = Database::getInstance().storeQuery(resultString); + const DBResult_ptr &result = g_database().storeQuery(resultString); // Ignore if player not have nothing inserted in the table if (!result) { return; @@ -1590,17 +1698,12 @@ void PlayerWheel::loadDBPlayerSlotPointsOnLogin() { } bool PlayerWheel::saveDBPlayerSlotPointsOnLogout() const { - const Database &db = Database::getInstance(); - std::ostringstream query; DBInsert insertWheelData("INSERT INTO `player_wheeldata` (`player_id`, `slot`) VALUES "); insertWheelData.upsert({ "slot" }); PropWriteStream stream; const auto wheelSlots = getSlots(); for (uint8_t i = 1; i < wheelSlots.size(); ++i) { auto value = wheelSlots[i]; - if (value == 0) { - continue; - } stream.write(i); stream.write(value); @@ -1610,7 +1713,7 @@ bool PlayerWheel::saveDBPlayerSlotPointsOnLogout() const { size_t attributesSize; const char* attributes = stream.getStream(attributesSize); if (attributesSize > 0) { - query << m_player.getGUID() << ',' << db.escapeBlob(attributes, static_cast(attributesSize)); + const auto query = fmt::format("{}, {}", m_player.getGUID(), g_database().escapeBlob(attributes, static_cast(attributesSize))); if (!insertWheelData.addRow(query)) { g_logger().debug("[{}] failed to insert row data", __FUNCTION__); return false; @@ -1819,7 +1922,7 @@ void PlayerWheel::reloadPlayerData() const { void PlayerWheel::registerPlayerBonusData() { addStat(WheelStat_t::HEALTH, m_playerBonusData.stats.health); addStat(WheelStat_t::MANA, m_playerBonusData.stats.mana); - addStat(WheelStat_t::CAPACITY, m_playerBonusData.stats.capacity * 100); + addStat(WheelStat_t::CAPACITY, m_playerBonusData.stats.capacity); addStat(WheelStat_t::MITIGATION, m_playerBonusData.mitigation * 100); addStat(WheelStat_t::DAMAGE, m_playerBonusData.stats.damage); addStat(WheelStat_t::HEALING, m_playerBonusData.stats.healing); @@ -1836,7 +1939,7 @@ void PlayerWheel::registerPlayerBonusData() { // Instant setSpellInstant("Battle Instinct", m_playerBonusData.instant.battleInstinct); setSpellInstant("Battle Healing", m_playerBonusData.instant.battleHealing); - setSpellInstant("Positional Tatics", m_playerBonusData.instant.positionalTatics); + setSpellInstant("Positional Tactics", m_playerBonusData.instant.positionalTactics); setSpellInstant("Ballistic Mastery", m_playerBonusData.instant.ballisticMastery); setSpellInstant("Healing Link", m_playerBonusData.instant.healingLink); setSpellInstant("Runic Mastery", m_playerBonusData.instant.runicMastery); @@ -1871,14 +1974,13 @@ void PlayerWheel::registerPlayerBonusData() { for (int i = 0; i < m_playerBonusData.stages.divineEmpowerment; ++i) { setSpellInstant("Divine Empowerment", true); } + WheelSpells::Bonus bonus; + bonus.decrease.cooldown = 4 * 1000; + if (m_playerBonusData.stages.divineEmpowerment >= 2) { - WheelSpells::Bonus bonus; - bonus.decrease.cooldown = 4000; addSpellBonus("Divine Empowerment", bonus); } if (m_playerBonusData.stages.divineEmpowerment >= 3) { - WheelSpells::Bonus bonus; - bonus.decrease.cooldown = 4000; addSpellBonus("Divine Empowerment", bonus); } } else { @@ -1891,12 +1993,12 @@ void PlayerWheel::registerPlayerBonusData() { } if (m_playerBonusData.stages.divineGrenade >= 2) { WheelSpells::Bonus bonus; - bonus.decrease.cooldown = 4000; + bonus.decrease.cooldown = 4 * 1000; addSpellBonus("Divine Grenade", bonus); } if (m_playerBonusData.stages.divineGrenade >= 3) { WheelSpells::Bonus bonus; - bonus.decrease.cooldown = 6000; + bonus.decrease.cooldown = 6 * 1000; addSpellBonus("Divine Grenade", bonus); } } else { @@ -1911,9 +2013,21 @@ void PlayerWheel::registerPlayerBonusData() { setSpellInstant("Drain Body", false); } if (m_playerBonusData.stages.beamMastery > 0) { + m_beamMasterySpells.emplace("Energy Beam"); + m_beamMasterySpells.emplace("Great Death Beam"); + m_beamMasterySpells.emplace("Great Energy Beam"); for (int i = 0; i < m_playerBonusData.stages.beamMastery; ++i) { setSpellInstant("Beam Mastery", true); } + WheelSpells::Bonus deathBeamBonus; + deathBeamBonus.decrease.cooldown = 2 * 1000; + deathBeamBonus.increase.damage = 6; + if (m_playerBonusData.stages.beamMastery >= 2) { + addSpellBonus("Great Death Beam", deathBeamBonus); + } + if (m_playerBonusData.stages.beamMastery >= 3) { + addSpellBonus("Great Death Beam", deathBeamBonus); + } } else { setSpellInstant("Beam Mastery", false); } @@ -1922,6 +2036,17 @@ void PlayerWheel::registerPlayerBonusData() { for (int i = 0; i < m_playerBonusData.stages.twinBurst; ++i) { setSpellInstant("Twin Burst", true); } + WheelSpells::Bonus bonus; + bonus.decrease.cooldown = 4 * 1000; + bonus.decrease.secondaryGroupCooldown = 4 * 1000; + if (m_playerBonusData.stages.twinBurst >= 2) { + addSpellBonus("Ice Burst", bonus); + addSpellBonus("Terra Burst", bonus); + } + if (m_playerBonusData.stages.twinBurst >= 3) { + addSpellBonus("Ice Burst", bonus); + addSpellBonus("Terra Burst", bonus); + } } else { setSpellInstant("Twin Burst", false); } @@ -1930,6 +2055,14 @@ void PlayerWheel::registerPlayerBonusData() { for (int i = 0; i < m_playerBonusData.stages.executionersThrow; ++i) { setSpellInstant("Executioner's Throw", true); } + WheelSpells::Bonus bonus; + bonus.decrease.cooldown = 4 * 1000; + if (m_playerBonusData.stages.executionersThrow >= 2) { + addSpellBonus("Executioner's Throw", bonus); + } + if (m_playerBonusData.stages.executionersThrow >= 3) { + addSpellBonus("Executioner's Throw", bonus); + } } else { setSpellInstant("Executioner's Throw", false); } @@ -1939,14 +2072,13 @@ void PlayerWheel::registerPlayerBonusData() { for (int i = 0; i < m_playerBonusData.avatar.light; ++i) { setSpellInstant("Avatar of Light", true); } + WheelSpells::Bonus bonus; + bonus.decrease.cooldown = 30 * 60 * 1000; // 30 minutes + if (m_playerBonusData.avatar.light >= 2) { - WheelSpells::Bonus bonus; - bonus.decrease.cooldown = 30 * 60 * 1000; // 30 minutes addSpellBonus("Avatar of Light", bonus); } if (m_playerBonusData.avatar.light >= 3) { - WheelSpells::Bonus bonus; - bonus.decrease.cooldown = 30 * 60 * 1000; // 30 minutes addSpellBonus("Avatar of Light", bonus); } } else { @@ -1957,14 +2089,13 @@ void PlayerWheel::registerPlayerBonusData() { for (int i = 0; i < m_playerBonusData.avatar.nature; ++i) { setSpellInstant("Avatar of Nature", true); } + WheelSpells::Bonus bonus; + bonus.decrease.cooldown = 30 * 60 * 1000; // 30 minutes + if (m_playerBonusData.avatar.nature >= 2) { - WheelSpells::Bonus bonus; - bonus.decrease.cooldown = 30 * 60 * 1000; // 30 minutes addSpellBonus("Avatar of Nature", bonus); } if (m_playerBonusData.avatar.nature >= 3) { - WheelSpells::Bonus bonus; - bonus.decrease.cooldown = 30 * 60 * 1000; // 30 minutes addSpellBonus("Avatar of Nature", bonus); } } else { @@ -1975,14 +2106,12 @@ void PlayerWheel::registerPlayerBonusData() { for (int i = 0; i < m_playerBonusData.avatar.steel; ++i) { setSpellInstant("Avatar of Steel", true); } + WheelSpells::Bonus bonus; + bonus.decrease.cooldown = 30 * 60 * 1000; // 30 minutes if (m_playerBonusData.avatar.steel >= 2) { - WheelSpells::Bonus bonus; - bonus.decrease.cooldown = 30 * 60 * 1000; // 30 minutes addSpellBonus("Avatar of Steel", bonus); } if (m_playerBonusData.avatar.steel >= 3) { - WheelSpells::Bonus bonus; - bonus.decrease.cooldown = 30 * 60 * 1000; // 30 minutes addSpellBonus("Avatar of Steel", bonus); } } else { @@ -1993,14 +2122,12 @@ void PlayerWheel::registerPlayerBonusData() { for (int i = 0; i < m_playerBonusData.avatar.storm; ++i) { setSpellInstant("Avatar of Storm", true); } + WheelSpells::Bonus bonus; + bonus.decrease.cooldown = 30 * 60 * 1000; // 30 minutes if (m_playerBonusData.avatar.storm >= 2) { - WheelSpells::Bonus bonus; - bonus.decrease.cooldown = 30 * 60 * 1000; // 30 minutes addSpellBonus("Avatar of Storm", bonus); } if (m_playerBonusData.avatar.storm >= 3) { - WheelSpells::Bonus bonus; - bonus.decrease.cooldown = 30 * 60 * 1000; // 30 minutes addSpellBonus("Avatar of Storm", bonus); } } else { @@ -2100,8 +2227,8 @@ void PlayerWheel::printPlayerWheelMethodsBonusData(const PlayerWheelMethodsBonus if (bonusData.instant.battleHealing) { g_logger().debug(" battleHealing: {}", bonusData.instant.battleHealing); } - if (bonusData.instant.positionalTatics) { - g_logger().debug(" positionalTatics: {}", bonusData.instant.positionalTatics); + if (bonusData.instant.positionalTactics) { + g_logger().debug(" positionalTactics: {}", bonusData.instant.positionalTactics); } if (bonusData.instant.ballisticMastery) { g_logger().debug(" ballisticMastery: {}", bonusData.instant.ballisticMastery); @@ -2167,10 +2294,10 @@ void PlayerWheel::printPlayerWheelMethodsBonusData(const PlayerWheelMethodsBonus g_logger().debug("mitigation: {}", bonusData.mitigation); } - auto &spellsVector = bonusData.spells; + const auto &spellsVector = bonusData.spells; if (!spellsVector.empty()) { g_logger().debug("Spells:"); - for (const auto &spell : bonusData.spells) { + for (const auto &spell : spellsVector) { g_logger().debug(" {}", spell); } } @@ -2180,7 +2307,7 @@ void PlayerWheel::printPlayerWheelMethodsBonusData(const PlayerWheelMethodsBonus void PlayerWheel::loadDedicationAndConvictionPerks() { using VocationBonusFunction = std::function &, uint16_t, uint8_t, PlayerWheelMethodsBonusData &)>; - auto wheelFunctions = g_game().getIOWheel()->getWheelMapFunctions(); + const auto &wheelFunctions = g_game().getIOWheel()->getWheelMapFunctions(); const auto vocationCipId = m_player.getPlayerVocationEnum(); if (vocationCipId < VOCATION_KNIGHT_CIP || vocationCipId > VOCATION_DRUID_CIP) { return; @@ -2245,7 +2372,7 @@ void PlayerWheel::processActiveGems() { if (count >= 3 && quality >= WheelGemQuality_t::Greater) { uint8_t grade = getGemGrade(WheelFragmentType_t::Greater, static_cast(supremeModifier)); std::string modifierName(magic_enum::enum_name(supremeModifier)); - g_logger().info("[{}] Adding supreme modifier {} to player {} from {} gem affinity {}", __FUNCTION__, modifierName, playerName, magic_enum::enum_name(quality), magic_enum::enum_name(affinity)); + g_logger().debug("[{}] Adding supreme modifier {} to player {} from {} gem affinity {}", __FUNCTION__, modifierName, playerName, magic_enum::enum_name(quality), magic_enum::enum_name(affinity)); m_modifierContext->addStrategies(supremeModifier, grade); } } @@ -2267,7 +2394,7 @@ void PlayerWheel::applyStageBonusForColor(const std::string &color) { return; } - auto [statsDamage, statsHealing] = g_game().getIOWheel()->getRevelationStatByStage(stageEnum); + const auto &[statsDamage, statsHealing] = g_game().getIOWheel()->getRevelationStatByStage(stageEnum); m_playerBonusData.stats.damage += statsDamage; m_playerBonusData.stats.healing += statsHealing; @@ -2345,7 +2472,6 @@ void PlayerWheel::applyBlueStageBonus(uint8_t stageValue, Vocation_t vocationEnu } else if (vocationEnum == Vocation_t::VOCATION_DRUID_CIP) { m_playerBonusData.stages.twinBurst = stageValue; for (uint8_t i = 1; i <= stageValue; ++i) { - addSpellToVector("Twin Burst"); addSpellToVector("Terra Burst"); addSpellToVector("Ice Burst"); } @@ -2429,7 +2555,7 @@ WheelStageEnum_t PlayerWheel::getPlayerSliceStage(const std::string &color) cons const auto modsSupremeIt = modsSupremePositionByVocation.find(vocationBaseId); if (modsSupremeIt != modsSupremePositionByVocation.end()) { - for (auto modPosition : modsSupremeIt->second.get()) { + for (const auto &modPosition : modsSupremeIt->second.get()) { const auto pos = static_cast(modPosition); uint8_t grade = 0; auto gradeKV = gemsGradeKV(WheelFragmentType_t::Greater, pos)->get("grade"); @@ -2465,7 +2591,7 @@ void PlayerWheel::checkAbilities() { if (getInstant("Battle Instinct") && getOnThinkTimer(WheelOnThink_t::BATTLE_INSTINCT) < OTSYS_TIME() && checkBattleInstinct()) { reloadClient = true; } - if (getInstant("Positional Tatics") && getOnThinkTimer(WheelOnThink_t::POSITIONAL_TATICS) < OTSYS_TIME() && checkPositionalTatics()) { + if (getInstant("Positional Tactics") && getOnThinkTimer(WheelOnThink_t::POSITIONAL_TACTICS) < OTSYS_TIME() && checkPositionalTactics()) { reloadClient = true; } if (getInstant("Ballistic Mastery") && getOnThinkTimer(WheelOnThink_t::BALLISTIC_MASTERY) < OTSYS_TIME() && checkBallisticMastery()) { @@ -2482,35 +2608,7 @@ bool PlayerWheel::checkBattleInstinct() { setOnThinkTimer(WheelOnThink_t::BATTLE_INSTINCT, OTSYS_TIME() + 2000); bool updateClient = false; m_creaturesNearby = 0; - uint16_t creaturesNearby = 0; - for (int offsetX = -1; offsetX <= 1; offsetX++) { - if (creaturesNearby >= 8) { - break; - } - for (int offsetY = -1; offsetY <= 1; offsetY++) { - if (creaturesNearby >= 8) { - break; - } - - const auto playerPositionOffSet = Position( - m_player.getPosition().x + offsetX, - m_player.getPosition().y + offsetY, - m_player.getPosition().z - ); - const auto &tile = g_game().map.getTile(playerPositionOffSet); - if (!tile) { - continue; - } - - const auto &creature = tile->getTopVisibleCreature(m_player.getPlayer()); - if (!creature || creature == m_player.getPlayer() || (creature->getMaster() && creature->getMaster()->getPlayer() == m_player.getPlayer())) { - continue; - } - - creaturesNearby++; - } - } - + uint16_t creaturesNearby = Spectators().find(m_player.getPosition(), false, 1, 1, 1, 1).excludePlayerMaster().size(); if (creaturesNearby >= 5) { m_creaturesNearby = creaturesNearby; creaturesNearby -= 4; @@ -2530,36 +2628,13 @@ bool PlayerWheel::checkBattleInstinct() { return updateClient; } -bool PlayerWheel::checkPositionalTatics() { - setOnThinkTimer(WheelOnThink_t::POSITIONAL_TATICS, OTSYS_TIME() + 2000); +bool PlayerWheel::checkPositionalTactics() { + setOnThinkTimer(WheelOnThink_t::POSITIONAL_TACTICS, OTSYS_TIME() + 2000); m_creaturesNearby = 0; bool updateClient = false; - uint16_t creaturesNearby = 0; - for (int offsetX = -1; offsetX <= 1; offsetX++) { - if (creaturesNearby > 0) { - break; - } - for (int offsetY = -1; offsetY <= 1; offsetY++) { - const auto playerPositionOffSet = Position( - m_player.getPosition().x + offsetX, - m_player.getPosition().y + offsetY, - m_player.getPosition().z - ); - const auto &tile = g_game().map.getTile(playerPositionOffSet); - if (!tile) { - continue; - } - - const auto &creature = tile->getTopVisibleCreature(m_player.getPlayer()); - if (!creature || creature == m_player.getPlayer() || !creature->getMonster() || (creature->getMaster() && creature->getMaster()->getPlayer())) { - continue; - } - - creaturesNearby++; - break; - } - } - constexpr uint16_t magicSkill = 3; + uint16_t creaturesNearby = Spectators().find(m_player.getPosition(), false, 1, 1, 1, 1).excludePlayerMaster().size(); + constexpr uint16_t holyMagicSkill = 3; + constexpr uint16_t healingMagicSkill = 3; constexpr uint16_t distanceSkill = 3; if (creaturesNearby == 0) { m_creaturesNearby = creaturesNearby; @@ -2567,8 +2642,12 @@ bool PlayerWheel::checkPositionalTatics() { setMajorStat(WheelMajor_t::DISTANCE, distanceSkill); updateClient = true; } - if (getMajorStat(WheelMajor_t::MAGIC) != 0) { - setMajorStat(WheelMajor_t::MAGIC, 0); + if (getSpecializedMagic(COMBAT_HOLYDAMAGE) != 0) { + setSpecializedMagic(COMBAT_HOLYDAMAGE, 0); + updateClient = true; + } + if (getSpecializedMagic(COMBAT_HEALING) != 0) { + setSpecializedMagic(COMBAT_HEALING, 0); updateClient = true; } } else { @@ -2576,8 +2655,12 @@ bool PlayerWheel::checkPositionalTatics() { setMajorStat(WheelMajor_t::DISTANCE, 0); updateClient = true; } - if (getMajorStat(WheelMajor_t::MAGIC) != magicSkill) { - setMajorStat(WheelMajor_t::MAGIC, magicSkill); + if (getSpecializedMagic(COMBAT_HOLYDAMAGE) != holyMagicSkill) { + setSpecializedMagic(COMBAT_HOLYDAMAGE, holyMagicSkill); + updateClient = true; + } + if (getSpecializedMagic(COMBAT_HEALING) != healingMagicSkill) { + setSpecializedMagic(COMBAT_HEALING, healingMagicSkill); updateClient = true; } } @@ -2954,7 +3037,7 @@ void PlayerWheel::onThink(bool force /* = false*/) { if (getGiftOfCooldown() > 0 /*getInstant("Gift of Life")*/ && getOnThinkTimer(WheelOnThink_t::GIFT_OF_LIFE) <= OTSYS_TIME()) { decreaseGiftOfCooldown(1); } - if (!m_player.hasCondition(CONDITION_INFIGHT) || m_player.getZoneType() == ZONE_PROTECTION || (!getInstant("Battle Instinct") && !getInstant("Positional Tatics") && !getInstant("Ballistic Mastery") && !getInstant("Gift of Life") && !getInstant("Combat Mastery") && !getInstant("Divine Empowerment") && getGiftOfCooldown() == 0)) { + if (!m_player.hasCondition(CONDITION_INFIGHT) || m_player.getZoneType() == ZONE_PROTECTION || (!getInstant("Battle Instinct") && !getInstant("Positional Tactics") && !getInstant("Ballistic Mastery") && !getInstant("Gift of Life") && !getInstant("Combat Mastery") && !getInstant("Divine Empowerment") && getGiftOfCooldown() == 0)) { bool mustReset = false; for (int i = 0; i < static_cast(WheelMajor_t::TOTAL_COUNT); i++) { if (getMajorStat(static_cast(i)) != 0) { @@ -2979,8 +3062,8 @@ void PlayerWheel::onThink(bool force /* = false*/) { if (getInstant("Battle Instinct") && (force || getOnThinkTimer(WheelOnThink_t::BATTLE_INSTINCT) < OTSYS_TIME()) && checkBattleInstinct()) { updateClient = true; } - // Positional Tatics - if (getInstant("Positional Tatics") && (force || getOnThinkTimer(WheelOnThink_t::POSITIONAL_TATICS) < OTSYS_TIME()) && checkPositionalTatics()) { + // Positional Tactics + if (getInstant("Positional Tactics") && (force || getOnThinkTimer(WheelOnThink_t::POSITIONAL_TACTICS) < OTSYS_TIME()) && checkPositionalTactics()) { updateClient = true; } // Ballistic Mastery @@ -3003,12 +3086,33 @@ void PlayerWheel::onThink(bool force /* = false*/) { void PlayerWheel::reduceAllSpellsCooldownTimer(int32_t value) const { for (const auto &condition : m_player.getConditionsByType(CONDITION_SPELLCOOLDOWN)) { - if (condition->getTicks() <= value) { - m_player.sendSpellCooldown(condition->getSubId(), 0); - condition->endCondition(m_player.getPlayer()); - } else { - condition->setTicks(condition->getTicks() - value); - m_player.sendSpellCooldown(condition->getSubId(), condition->getTicks()); + const auto spellId = condition->getSubId(); + const auto &spell = g_spells().getInstantSpellById(spellId); + if (!spell) { + continue; + } + + const auto spellSecondaryGroup = spell->getSecondaryGroup(); + const auto &secondCondition = m_player.getCondition(CONDITION_SPELLGROUPCOOLDOWN, CONDITIONID_DEFAULT, spellSecondaryGroup); + + if (secondCondition) { + if (secondCondition->getTicks() <= value) { + m_player.sendSpellGroupCooldown(spellSecondaryGroup, 0); + secondCondition->endCondition(m_player.getPlayer()); + } else { + secondCondition->setTicks(secondCondition->getTicks() - value); + m_player.sendSpellGroupCooldown(spellSecondaryGroup, secondCondition->getTicks()); + } + } + + if (condition) { + if (condition->getTicks() <= value) { + m_player.sendSpellCooldown(spellId, 0); + condition->endCondition(m_player.getPlayer()); + } else { + condition->setTicks(condition->getTicks() - value); + m_player.sendSpellCooldown(spellId, condition->getTicks()); + } } } } @@ -3022,6 +3126,7 @@ void PlayerWheel::resetUpgradedSpells() { m_creaturesNearby = 0; m_spellsSelected.clear(); m_learnedSpellsSelected.clear(); + m_beamMasterySpells.clear(); for (int i = 0; i < static_cast(WheelMajor_t::TOTAL_COUNT); i++) { setMajorStat(static_cast(i), 0); } @@ -3087,17 +3192,15 @@ std::shared_ptr PlayerWheel::getCombatDataSpell(CombatDamage &damage) { damage.lifeLeechChance += spell->getWheelOfDestinyBoost(WheelSpellBoost_t::LIFE_LEECH_CHANCE, spellGrade); } - if (m_spellsBonuses.contains(spellName)) { - damage.criticalDamage += (getSpellBonus(spellName, WheelSpellBoost_t::CRITICAL_DAMAGE) * 100); - damage.criticalChance += getSpellBonus(spellName, WheelSpellBoost_t::CRITICAL_CHANCE); - damage.damageMultiplier += getSpellBonus(spellName, WheelSpellBoost_t::DAMAGE); - damage.damageReductionMultiplier += getSpellBonus(spellName, WheelSpellBoost_t::DAMAGE_REDUCTION); - damage.healingMultiplier += getSpellBonus(spellName, WheelSpellBoost_t::HEAL); - damage.manaLeech += getSpellBonus(spellName, WheelSpellBoost_t::MANA_LEECH); - damage.manaLeechChance += getSpellBonus(spellName, WheelSpellBoost_t::MANA_LEECH_CHANCE); - damage.lifeLeech += getSpellBonus(spellName, WheelSpellBoost_t::LIFE_LEECH); - damage.lifeLeechChance += getSpellBonus(spellName, WheelSpellBoost_t::LIFE_LEECH_CHANCE); - } + damage.criticalDamage += (getSpellBonus(spellName, WheelSpellBoost_t::CRITICAL_DAMAGE) * 100); + damage.criticalChance += getSpellBonus(spellName, WheelSpellBoost_t::CRITICAL_CHANCE); + damage.damageMultiplier += getSpellBonus(spellName, WheelSpellBoost_t::DAMAGE); + damage.damageReductionMultiplier += getSpellBonus(spellName, WheelSpellBoost_t::DAMAGE_REDUCTION); + damage.healingMultiplier += getSpellBonus(spellName, WheelSpellBoost_t::HEAL); + damage.manaLeech += getSpellBonus(spellName, WheelSpellBoost_t::MANA_LEECH); + damage.manaLeechChance += getSpellBonus(spellName, WheelSpellBoost_t::MANA_LEECH_CHANCE); + damage.lifeLeech += getSpellBonus(spellName, WheelSpellBoost_t::LIFE_LEECH); + damage.lifeLeechChance += getSpellBonus(spellName, WheelSpellBoost_t::LIFE_LEECH_CHANCE); } return spell; @@ -3131,6 +3234,15 @@ void PlayerWheel::setMajorStat(WheelMajor_t type, int32_t value) { } } +void PlayerWheel::setSpecializedMagic(CombatType_t type, int32_t value) { + auto enumValue = static_cast(type); + try { + m_specializedMagic.at(enumValue) = value; + } catch (const std::out_of_range &e) { + g_logger().error("[{}]. Type {} is out of range, value {}. Error message: {}", __FUNCTION__, enumValue, value, e.what()); + } +} + void PlayerWheel::setInstant(WheelInstant_t type, bool toggle) { auto enumValue = static_cast(type); try { @@ -3167,9 +3279,9 @@ void PlayerWheel::setSpellInstant(const std::string &name, bool value) { } } else if (name == "Battle Healing") { setInstant(WheelInstant_t::BATTLE_HEALING, value); - } else if (name == "Positional Tatics") { - setInstant(WheelInstant_t::POSITIONAL_TATICS, value); - if (!getInstant(WheelInstant_t::POSITIONAL_TATICS)) { + } else if (name == "Positional Tactics") { + setInstant(WheelInstant_t::POSITIONAL_TACTICS, value); + if (!getInstant(WheelInstant_t::POSITIONAL_TACTICS)) { setMajorStat(WheelMajor_t::MAGIC, 0); setMajorStat(WheelMajor_t::HOLY_RESISTANCE, 0); } @@ -3296,65 +3408,38 @@ bool PlayerWheel::getInstant(WheelInstant_t type) const { uint8_t PlayerWheel::getStage(std::string_view name) const { using enum WheelInstant_t; using enum WheelStage_t; - if (name == "Battle Instinct") { - return PlayerWheel::getInstant(BATTLE_INSTINCT); - } - if (name == "Battle Healing") { - return PlayerWheel::getInstant(BATTLE_HEALING); - } - if (name == "Positional Tatics") { - return PlayerWheel::getInstant(POSITIONAL_TATICS); - } - if (name == "Ballistic Mastery") { - return PlayerWheel::getInstant(BALLISTIC_MASTERY); - } - if (name == "Healing Link") { - return PlayerWheel::getInstant(HEALING_LINK); - } - if (name == "Runic Mastery") { - return PlayerWheel::getInstant(RUNIC_MASTERY); - } - if (name == "Focus Mastery") { - return PlayerWheel::getInstant(FOCUS_MASTERY); - } - if (name == "Beam Mastery") { - return PlayerWheel::getStage(BEAM_MASTERY); - } - if (name == "Combat Mastery") { - return PlayerWheel::getStage(COMBAT_MASTERY); - } - if (name == "Gift of Life") { - return PlayerWheel::getStage(GIFT_OF_LIFE); - } - if (name == "Blessing of the Grove") { - return PlayerWheel::getStage(BLESSING_OF_THE_GROVE); - } - if (name == "Drain Body") { - return PlayerWheel::getStage(DRAIN_BODY); - } - if (name == "Divine Empowerment") { - return PlayerWheel::getStage(DIVINE_EMPOWERMENT); - } - if (name == "Divine Grenade") { - return PlayerWheel::getStage(DIVINE_GRENADE); - } - if (name == "Twin Burst") { - return PlayerWheel::getStage(TWIN_BURST); - } - if (name == "Executioner's Throw") { - return PlayerWheel::getStage(EXECUTIONERS_THROW); - } - if (name == "Avatar of Light") { - return PlayerWheel::getStage(AVATAR_OF_LIGHT); - } - if (name == "Avatar of Nature") { - return PlayerWheel::getStage(AVATAR_OF_NATURE); - } - if (name == "Avatar of Steel") { - return PlayerWheel::getStage(AVATAR_OF_STEEL); + + static const std::unordered_map instantMapping = { + { "Battle Instinct", BATTLE_INSTINCT }, + { "Battle Healing", BATTLE_HEALING }, + { "Positional Tatics", POSITIONAL_TACTICS }, + { "Ballistic Mastery", BALLISTIC_MASTERY }, + { "Healing Link", HEALING_LINK }, + { "Runic Mastery", RUNIC_MASTERY }, + { "Focus Mastery", FOCUS_MASTERY } + }; + + static const std::unordered_map stageMapping = { + { "Beam Mastery", BEAM_MASTERY }, + { "Combat Mastery", COMBAT_MASTERY }, + { "Gift of Life", GIFT_OF_LIFE }, + { "Blessing of the Grove", BLESSING_OF_THE_GROVE }, + { "Drain Body", DRAIN_BODY }, + { "Divine Empowerment", DIVINE_EMPOWERMENT }, + { "Divine Grenade", DIVINE_GRENADE }, + { "Twin Burst", TWIN_BURST }, + { "Executioner's Throw", EXECUTIONERS_THROW }, + { "Avatar of Light", AVATAR_OF_LIGHT }, + { "Avatar of Nature", AVATAR_OF_NATURE }, + { "Avatar of Steel", AVATAR_OF_STEEL }, + { "Avatar of Storm", AVATAR_OF_STORM } + }; + + if (auto it = instantMapping.find(name); it != instantMapping.end()) { + return PlayerWheel::getInstant(it->second); } - if (name == "Avatar of Storm") { - return PlayerWheel::getStage(AVATAR_OF_STORM); + if (auto it = stageMapping.find(name); it != stageMapping.end()) { + return PlayerWheel::getStage(it->second); } return false; @@ -3380,6 +3465,16 @@ int32_t PlayerWheel::getMajorStat(WheelMajor_t type) const { return 0; } +int32_t PlayerWheel::getSpecializedMagic(CombatType_t type) const { + auto enumValue = static_cast(type); + try { + return m_specializedMagic.at(enumValue); + } catch (const std::out_of_range &e) { + g_logger().error("[{}]. Instant type {}. Error message: {}", __FUNCTION__, enumValue, e.what()); + } + return 0; +} + int32_t PlayerWheel::getStat(WheelStat_t type) const { auto enumValue = static_cast(type); try { @@ -3440,65 +3535,39 @@ int64_t PlayerWheel::getOnThinkTimer(WheelOnThink_t type) const { bool PlayerWheel::getInstant(std::string_view name) const { using enum WheelInstant_t; - if (name == "Battle Instinct") { - return PlayerWheel::getInstant(BATTLE_INSTINCT); - } - if (name == "Battle Healing") { - return PlayerWheel::getInstant(BATTLE_HEALING); - } - if (name == "Positional Tatics") { - return PlayerWheel::getInstant(POSITIONAL_TATICS); - } - if (name == "Ballistic Mastery") { - return PlayerWheel::getInstant(BALLISTIC_MASTERY); - } - if (name == "Healing Link") { - return PlayerWheel::getInstant(HEALING_LINK); - } - if (name == "Runic Mastery") { - return PlayerWheel::getInstant(RUNIC_MASTERY); - } - if (name == "Focus Mastery") { - return PlayerWheel::getInstant(FOCUS_MASTERY); - } - if (name == "Beam Mastery") { - return PlayerWheel::getStage(WheelStage_t::BEAM_MASTERY); - } - if (name == "Combat Mastery") { - return PlayerWheel::getStage(WheelStage_t::COMBAT_MASTERY); - } - if (name == "Gift of Life") { - return PlayerWheel::getStage(WheelStage_t::GIFT_OF_LIFE); - } - if (name == "Blessing of the Grove") { - return PlayerWheel::getStage(WheelStage_t::BLESSING_OF_THE_GROVE); - } - if (name == "Drain Body") { - return PlayerWheel::getStage(WheelStage_t::DRAIN_BODY); - } - if (name == "Divine Empowerment") { - return PlayerWheel::getStage(WheelStage_t::DIVINE_EMPOWERMENT); - } - if (name == "Divine Grenade") { - return PlayerWheel::getStage(WheelStage_t::DIVINE_GRENADE); - } - if (name == "Twin Burst") { - return PlayerWheel::getStage(WheelStage_t::TWIN_BURST); - } - if (name == "Executioner's Throw") { - return PlayerWheel::getStage(WheelStage_t::EXECUTIONERS_THROW); - } - if (name == "Avatar of Light") { - return PlayerWheel::getStage(WheelStage_t::AVATAR_OF_LIGHT); - } - if (name == "Avatar of Nature") { - return PlayerWheel::getStage(WheelStage_t::AVATAR_OF_NATURE); - } - if (name == "Avatar of Steel") { - return PlayerWheel::getStage(WheelStage_t::AVATAR_OF_STEEL); + using enum WheelStage_t; + + static const std::unordered_map instantMapping = { + { "Battle Instinct", BATTLE_INSTINCT }, + { "Battle Healing", BATTLE_HEALING }, + { "Positional Tactics", POSITIONAL_TACTICS }, + { "Ballistic Mastery", BALLISTIC_MASTERY }, + { "Healing Link", HEALING_LINK }, + { "Runic Mastery", RUNIC_MASTERY }, + { "Focus Mastery", FOCUS_MASTERY } + }; + + static const std::unordered_map stageMapping = { + { "Beam Mastery", BEAM_MASTERY }, + { "Combat Mastery", COMBAT_MASTERY }, + { "Gift of Life", GIFT_OF_LIFE }, + { "Blessing of the Grove", BLESSING_OF_THE_GROVE }, + { "Drain Body", DRAIN_BODY }, + { "Divine Empowerment", DIVINE_EMPOWERMENT }, + { "Divine Grenade", DIVINE_GRENADE }, + { "Twin Burst", TWIN_BURST }, + { "Executioner's Throw", EXECUTIONERS_THROW }, + { "Avatar of Light", AVATAR_OF_LIGHT }, + { "Avatar of Nature", AVATAR_OF_NATURE }, + { "Avatar of Steel", AVATAR_OF_STEEL }, + { "Avatar of Storm", AVATAR_OF_STORM } + }; + + if (auto it = instantMapping.find(name); it != instantMapping.end()) { + return PlayerWheel::getInstant(it->second); } - if (name == "Avatar of Storm") { - return PlayerWheel::getStage(WheelStage_t::AVATAR_OF_STORM); + if (auto it = stageMapping.find(name); it != stageMapping.end()) { + return PlayerWheel::getStage(it->second); } return false; @@ -3599,7 +3668,7 @@ void PlayerWheel::setWheelBonusData(const PlayerWheelMethodsBonusData &newBonusD // Functions used to Manage Combat uint8_t PlayerWheel::getBeamAffectedTotal(const CombatDamage &tmpDamage) const { uint8_t beamAffectedTotal = 0; // Removed const - if (tmpDamage.runeSpellName == "Beam Mastery" && getInstant("Beam Mastery")) { + if (m_beamMasterySpells.contains(tmpDamage.instantSpellName) && getInstant("Beam Mastery")) { beamAffectedTotal = 3; } return beamAffectedTotal; @@ -3608,6 +3677,7 @@ uint8_t PlayerWheel::getBeamAffectedTotal(const CombatDamage &tmpDamage) const { void PlayerWheel::updateBeamMasteryDamage(CombatDamage &tmpDamage, uint8_t &beamAffectedTotal, uint8_t &beamAffectedCurrent) const { if (beamAffectedTotal > 0) { tmpDamage.damageMultiplier += checkBeamMasteryDamage(); + reduceAllSpellsCooldownTimer(1000); // Reduces all spell cooldown by 1 second per target hit (max 3 seconds) --beamAffectedTotal; beamAffectedCurrent++; } diff --git a/src/creatures/players/wheel/player_wheel.hpp b/src/creatures/players/wheel/player_wheel.hpp index 840cc72d8dc..7c3bd2348a6 100644 --- a/src/creatures/players/wheel/player_wheel.hpp +++ b/src/creatures/players/wheel/player_wheel.hpp @@ -107,6 +107,9 @@ class PlayerWheel { int getSpellAdditionalDuration(const std::string &spellName) const; bool getSpellAdditionalArea(const std::string &spellName) const; + bool handleTwinBurstsCooldown(const std::shared_ptr &player, const std::string &spellName, int spellCooldown, int rateCooldown) const; + bool handleBeamMasteryCooldown(const std::shared_ptr &player, const std::string &spellName, int spellCooldown, int rateCooldown) const; + /* * Functions for manage slots */ @@ -209,7 +212,7 @@ class PlayerWheel { void checkAbilities(); void checkGiftOfLife(); bool checkBattleInstinct(); - bool checkPositionalTatics(); + bool checkPositionalTactics(); bool checkBallisticMastery(); bool checkCombatMastery(); bool checkDivineEmpowerment(); @@ -260,6 +263,16 @@ class PlayerWheel { */ void setMajorStat(WheelMajor_t type, int32_t value); + /** + * @brief Sets the value of a specific specialized magic in the Wheel of Destiny. + * + * This function sets the value of the specified specialized magic in the Wheel of Destiny to the provided value. + * + * @param type The type of the combat to set the specialized magic. + * @param value The value to set for the specialized magic. + */ + void setSpecializedMagic(CombatType_t type, int32_t value); + /** * @brief Sets the value of a specific instant in the Wheel of Destiny. * @@ -310,6 +323,7 @@ class PlayerWheel { uint8_t getStage(WheelStage_t type) const; WheelSpellGrade_t getSpellUpgrade(const std::string &name) const; int32_t getMajorStat(WheelMajor_t type) const; + int32_t getSpecializedMagic(CombatType_t type) const; int32_t getStat(WheelStat_t type) const; int32_t getResistance(CombatType_t type) const; int32_t getMajorStatConditional(const std::string &instant, WheelMajor_t major) const; @@ -379,63 +393,11 @@ class PlayerWheel { void toggleGemLock(uint16_t index) const; void setActiveGem(WheelGemAffinity_t affinity, uint16_t index) const; void removeActiveGem(WheelGemAffinity_t affinity) const; - void addRevelationBonus(WheelGemAffinity_t affinity, uint16_t points) { - m_bonusRevelationPoints[static_cast(affinity)] += points; - } - void resetRevelationBonus() { - m_bonusRevelationPoints = { 0, 0, 0, 0 }; - } - - void addSpellBonus(const std::string &spellName, const WheelSpells::Bonus &bonus) { - if (m_spellsBonuses.contains(spellName)) { - m_spellsBonuses[spellName].decrease.cooldown += bonus.decrease.cooldown; - m_spellsBonuses[spellName].decrease.manaCost += bonus.decrease.manaCost; - m_spellsBonuses[spellName].decrease.secondaryGroupCooldown += bonus.decrease.secondaryGroupCooldown; - m_spellsBonuses[spellName].increase.aditionalTarget += bonus.increase.aditionalTarget; - m_spellsBonuses[spellName].increase.area = bonus.increase.area; - m_spellsBonuses[spellName].increase.criticalChance += bonus.increase.criticalChance; - m_spellsBonuses[spellName].increase.criticalDamage += bonus.increase.criticalDamage; - m_spellsBonuses[spellName].increase.damage += bonus.increase.damage; - m_spellsBonuses[spellName].increase.damageReduction += bonus.increase.damageReduction; - m_spellsBonuses[spellName].increase.duration += bonus.increase.duration; - m_spellsBonuses[spellName].increase.heal += bonus.increase.heal; - m_spellsBonuses[spellName].leech.life += bonus.leech.life; - m_spellsBonuses[spellName].leech.mana += bonus.leech.mana; - return; - } - m_spellsBonuses[spellName] = bonus; - } - - int32_t getSpellBonus(const std::string &spellName, WheelSpellBoost_t boost) const { - if (!m_spellsBonuses.contains(spellName)) { - return 0; - } - auto [leech, increase, decrease] = m_spellsBonuses.at(spellName); - switch (boost) { - case WheelSpellBoost_t::COOLDOWN: - return decrease.cooldown; - case WheelSpellBoost_t::MANA: - return decrease.manaCost; - case WheelSpellBoost_t::SECONDARY_GROUP_COOLDOWN: - return decrease.secondaryGroupCooldown; - case WheelSpellBoost_t::CRITICAL_CHANCE: - return increase.criticalChance; - case WheelSpellBoost_t::CRITICAL_DAMAGE: - return increase.criticalDamage; - case WheelSpellBoost_t::DAMAGE: - return increase.damage; - case WheelSpellBoost_t::DAMAGE_REDUCTION: - return increase.damageReduction; - case WheelSpellBoost_t::HEAL: - return increase.heal; - case WheelSpellBoost_t::LIFE_LEECH: - return leech.life; - case WheelSpellBoost_t::MANA_LEECH: - return leech.mana; - default: - return 0; - } - } + void addRevelationBonus(WheelGemAffinity_t affinity, uint16_t points); + void resetRevelationBonus(); + void addSpellBonus(const std::string &spellName, const WheelSpells::Bonus &bonus); + + int32_t getSpellBonus(const std::string &spellName, WheelSpellBoost_t boost) const; WheelGemBasicModifier_t selectBasicModifier2(WheelGemBasicModifier_t modifier1) const; @@ -465,9 +427,11 @@ class PlayerWheel { std::array(WheelMajor_t::TOTAL_COUNT)> m_majorStats = { 0 }; std::array(WheelInstant_t::INSTANT_COUNT)> m_instant = { false }; std::array m_resistance = { 0 }; + std::array m_specializedMagic = { 0 }; int32_t m_creaturesNearby = 0; std::map m_spellsSelected; std::vector m_learnedSpellsSelected; std::unordered_map m_spellsBonuses; + std::unordered_set m_beamMasterySpells; }; diff --git a/src/creatures/players/wheel/wheel_definitions.hpp b/src/creatures/players/wheel/wheel_definitions.hpp index 0aa22c5a7b9..a136bd620d3 100644 --- a/src/creatures/players/wheel/wheel_definitions.hpp +++ b/src/creatures/players/wheel/wheel_definitions.hpp @@ -97,7 +97,7 @@ enum class WheelStage_t : uint8_t { enum class WheelOnThink_t : uint8_t { BATTLE_INSTINCT = 0, - POSITIONAL_TATICS = 1, + POSITIONAL_TACTICS = 1, BALLISTIC_MASTERY = 2, COMBAT_MASTERY = 3, FOCUS_MASTERY = 4, @@ -148,7 +148,7 @@ enum class WheelMajor_t : uint8_t { enum class WheelInstant_t : uint8_t { BATTLE_INSTINCT = 0, BATTLE_HEALING = 1, - POSITIONAL_TATICS = 2, + POSITIONAL_TACTICS = 2, BALLISTIC_MASTERY = 3, HEALING_LINK = 4, RUNIC_MASTERY = 5, @@ -217,7 +217,7 @@ struct PlayerWheelMethodsBonusData { struct Instant { bool battleInstinct = false; // Knight bool battleHealing = false; // Knight - bool positionalTatics = false; // Paladin + bool positionalTactics = false; // Paladin bool ballisticMastery = false; // Paladin bool healingLink = false; // Druid bool runicMastery = false; // Druid/sorcerer @@ -282,7 +282,7 @@ namespace WheelSpells { struct Decrease { int cooldown = 0; int manaCost = 0; - uint8_t secondaryGroupCooldown = 0; + int secondaryGroupCooldown = 0; }; struct Leech { diff --git a/src/creatures/players/wheel/wheel_gems.cpp b/src/creatures/players/wheel/wheel_gems.cpp index a2c7a898766..766fa9ee049 100644 --- a/src/creatures/players/wheel/wheel_gems.cpp +++ b/src/creatures/players/wheel/wheel_gems.cpp @@ -238,16 +238,16 @@ void WheelModifierContext::addStrategies(WheelGemSupremeModifier_t modifier, uin switch (modifier) { case WheelGemSupremeModifier_t::General_Dodge: - m_strategies.emplace_back(std::make_unique(m_wheel, WheelStat_t::DODGE, 25 * gradeMultiplier)); + m_strategies.emplace_back(std::make_unique(m_wheel, WheelStat_t::DODGE, 28 * gradeMultiplier)); break; case WheelGemSupremeModifier_t::General_LifeLeech: - m_strategies.emplace_back(std::make_unique(m_wheel, WheelStat_t::LIFE_LEECH, 120 * gradeMultiplier)); + m_strategies.emplace_back(std::make_unique(m_wheel, WheelStat_t::LIFE_LEECH, 200 * gradeMultiplier)); break; case WheelGemSupremeModifier_t::General_ManaLeech: - m_strategies.emplace_back(std::make_unique(m_wheel, WheelStat_t::MANA_LEECH, 40 * gradeMultiplier)); + m_strategies.emplace_back(std::make_unique(m_wheel, WheelStat_t::MANA_LEECH, 80 * gradeMultiplier)); break; case WheelGemSupremeModifier_t::General_CriticalDamage: - m_strategies.emplace_back(std::make_unique(m_wheel, WheelStat_t::CRITICAL_DAMAGE, 150 * gradeMultiplier)); + m_strategies.emplace_back(std::make_unique(m_wheel, WheelStat_t::CRITICAL_DAMAGE, 200 * gradeMultiplier)); break; case WheelGemSupremeModifier_t::General_RevelationMastery_GiftOfLife: m_strategies.emplace_back(std::make_unique(m_wheel, WheelGemAffinity_t::Green, 150 * gradeMultiplier)); @@ -255,7 +255,7 @@ void WheelModifierContext::addStrategies(WheelGemSupremeModifier_t modifier, uin break; case WheelGemSupremeModifier_t::SorcererDruid_UltimateHealing: - bonus.increase.heal = 10 * gradeMultiplier; + bonus.increase.heal = 5 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Ultimate Healing", bonus)); break; @@ -312,25 +312,25 @@ void WheelModifierContext::addStrategies(WheelGemSupremeModifier_t modifier, uin break; case WheelGemSupremeModifier_t::Knight_AvatarOfSteel_Cooldown: - bonus.decrease.cooldown = 300 * 1000; + bonus.decrease.cooldown = 900 * 1000; wheelBonus.momentum += grade < 3 ? 0.33 * grade : 1; m_strategies.emplace_back(std::make_unique(m_wheel, "Avatar of Steel", bonus)); break; case WheelGemSupremeModifier_t::Knight_ExecutionersThrow_Cooldown: - bonus.decrease.cooldown = 1 * 1000; + bonus.decrease.cooldown = 2 * 1000; wheelBonus.momentum += grade < 3 ? 0.33 * grade : 1; m_strategies.emplace_back(std::make_unique(m_wheel, "Executioner's Throw", bonus)); break; case WheelGemSupremeModifier_t::Knight_ExecutionersThrow_DamageIncrease: - bonus.increase.damage = 25 * gradeMultiplier; + bonus.increase.damage = 6 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Executioner's Throw", bonus)); break; case WheelGemSupremeModifier_t::Knight_ExecutionersThrow_CriticalExtraDamage: - bonus.increase.criticalDamage = 8 * gradeMultiplier; + bonus.increase.criticalDamage = 12 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Executioner's Throw", bonus)); break; case WheelGemSupremeModifier_t::Knight_Fierce_Berserk_DamageIncrease: - bonus.increase.damage = 25 * gradeMultiplier; + bonus.increase.damage = 5 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Fierce Berserk", bonus)); break; case WheelGemSupremeModifier_t::Knight_Fierce_Berserk_CriticalExtraDamage: @@ -338,35 +338,35 @@ void WheelModifierContext::addStrategies(WheelGemSupremeModifier_t modifier, uin m_strategies.emplace_back(std::make_unique(m_wheel, "Fierce Berserk", bonus)); break; case WheelGemSupremeModifier_t::Knight_Berserk_DamageIncrease: - bonus.increase.damage = 25 * gradeMultiplier; + bonus.increase.damage = 5 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Berserk", bonus)); break; case WheelGemSupremeModifier_t::Knight_Berserk_CriticalExtraDamage: - bonus.increase.criticalDamage = 8 * gradeMultiplier; + bonus.increase.criticalDamage = 12 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Berserk", bonus)); break; case WheelGemSupremeModifier_t::Knight_Front_Sweep_CriticalExtraDamage: - bonus.increase.criticalDamage = 8 * gradeMultiplier; + bonus.increase.criticalDamage = 12 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Front Sweep", bonus)); break; case WheelGemSupremeModifier_t::Knight_Front_Sweep_DamageIncrease: - bonus.increase.damage = 25 * gradeMultiplier; + bonus.increase.damage = 8 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Front Sweep", bonus)); break; case WheelGemSupremeModifier_t::Knight_Groundshaker_DamageIncrease: - bonus.increase.damage = 25 * gradeMultiplier; + bonus.increase.damage = static_cast(std::round(6.5 * gradeMultiplier)); m_strategies.emplace_back(std::make_unique(m_wheel, "Groundshaker", bonus)); break; case WheelGemSupremeModifier_t::Knight_Groundshaker_CriticalExtraDamage: - bonus.increase.criticalDamage = 8 * gradeMultiplier; + bonus.increase.criticalDamage = 12 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Groundshaker", bonus)); break; case WheelGemSupremeModifier_t::Knight_Annihilation_CriticalExtraDamage: - bonus.increase.criticalDamage = 8 * gradeMultiplier; + bonus.increase.criticalDamage = 15 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Annihilation", bonus)); break; case WheelGemSupremeModifier_t::Knight_Annihilation_DamageIncrease: - bonus.increase.damage = 25 * gradeMultiplier; + bonus.increase.damage = 12 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Annihilation", bonus)); break; case WheelGemSupremeModifier_t::Knight_FairWoundCleansing_HealingIncrease: @@ -375,72 +375,72 @@ void WheelModifierContext::addStrategies(WheelGemSupremeModifier_t modifier, uin break; case WheelGemSupremeModifier_t::Paladin_AvatarOfLight_Cooldown: - bonus.decrease.cooldown = 300 * 1000; + bonus.decrease.cooldown = 900 * 1000; wheelBonus.momentum += grade < 3 ? 0.33 * grade : 1; m_strategies.emplace_back(std::make_unique(m_wheel, "Avatar of Light", bonus)); break; case WheelGemSupremeModifier_t::Paladin_DivineDazzle_Cooldown: - bonus.decrease.cooldown = 2 * 1000; + bonus.decrease.cooldown = 4 * 1000; wheelBonus.momentum += grade < 3 ? 0.33 * grade : 1; m_strategies.emplace_back(std::make_unique(m_wheel, "Divine Dazzle", bonus)); break; case WheelGemSupremeModifier_t::Paladin_DivineGrenade_DamageIncrease: - bonus.increase.damage = 25 * gradeMultiplier; + bonus.increase.damage = 6 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Divine Grenade", bonus)); break; case WheelGemSupremeModifier_t::Paladin_DivineGrenade_CriticalExtraDamage: - bonus.increase.criticalDamage = 8 * gradeMultiplier; + bonus.increase.criticalDamage = 12 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Divine Grenade", bonus)); break; case WheelGemSupremeModifier_t::Paladin_DivineCaldera_DamageIncrease: - bonus.increase.damage = 25 * gradeMultiplier; + bonus.increase.damage = 5 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Divine Caldera", bonus)); break; case WheelGemSupremeModifier_t::Paladin_DivineCaldera_CriticalExtraDamage: - bonus.increase.criticalDamage = 8 * gradeMultiplier; + bonus.increase.criticalDamage = 12 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Divine Caldera", bonus)); break; case WheelGemSupremeModifier_t::Paladin_DivineMissile_DamageIncrease: - bonus.increase.damage = 25 * gradeMultiplier; + bonus.increase.damage = 8 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Divine Missile", bonus)); break; case WheelGemSupremeModifier_t::Paladin_DivineMissile_CriticalExtraDamage: - bonus.increase.criticalDamage = 8 * gradeMultiplier; + bonus.increase.criticalDamage = 12 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Divine Missile", bonus)); break; case WheelGemSupremeModifier_t::Paladin_EtherealSpear_DamageIncrease: - bonus.increase.damage = 25 * gradeMultiplier; + bonus.increase.damage = 10 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Ethereal Spear", bonus)); break; case WheelGemSupremeModifier_t::Paladin_EtherealSpear_CriticalExtraDamage: - bonus.increase.criticalDamage = 8 * gradeMultiplier; + bonus.increase.criticalDamage = 15 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Ethereal Spear", bonus)); break; case WheelGemSupremeModifier_t::Paladin_StrongEtherealSpear_DamageIncrease: - bonus.increase.damage = 25 * gradeMultiplier; + bonus.increase.damage = 8 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Strong Ethereal Spear", bonus)); break; case WheelGemSupremeModifier_t::Paladin_StrongEtherealSpear_CriticalExtraDamage: - bonus.increase.criticalDamage = 8 * gradeMultiplier; + bonus.increase.criticalDamage = 12 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Strong Ethereal Spear", bonus)); break; case WheelGemSupremeModifier_t::Paladin_DivineEmpowerment_Cooldown: - bonus.decrease.cooldown = 3 * 1000; + bonus.decrease.cooldown = 6 * 1000; wheelBonus.momentum += grade < 3 ? 0.33 * grade : 1; m_strategies.emplace_back(std::make_unique(m_wheel, "Divine Empowerment", bonus)); break; case WheelGemSupremeModifier_t::Paladin_DivineGrenade_Cooldown: - bonus.decrease.cooldown = 1 * 1000; + bonus.decrease.cooldown = 2 * 1000; wheelBonus.momentum += grade < 3 ? 0.33 * grade : 1; m_strategies.emplace_back(std::make_unique(m_wheel, "Divine Grenade", bonus)); break; case WheelGemSupremeModifier_t::Paladin_Salvation_HealingIncrease: - bonus.increase.heal = 10 * gradeMultiplier; + bonus.increase.heal = 6 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Salvation", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_AvatarOfStorm_Cooldown: - bonus.decrease.cooldown = 300 * 1000; + bonus.decrease.cooldown = 900 * 1000; wheelBonus.momentum += grade < 3 ? 0.33 * grade : 1; m_strategies.emplace_back(std::make_unique(m_wheel, "Avatar of Storm", bonus)); break; @@ -450,31 +450,31 @@ void WheelModifierContext::addStrategies(WheelGemSupremeModifier_t modifier, uin m_strategies.emplace_back(std::make_unique(m_wheel, "Energy Wave", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_GreatDeathBeam_DamageIncrease: - bonus.increase.damage = 25 * gradeMultiplier; + bonus.increase.damage = 10 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Great Death Beam", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_GreatDeathBeam_CriticalExtraDamage: - bonus.increase.criticalDamage = 8 * gradeMultiplier; + bonus.increase.criticalDamage = 15 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Great Death Beam", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_HellsCore_DamageIncrease: - bonus.increase.damage = 25 * gradeMultiplier; + bonus.increase.damage = 8 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Hell's Core", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_HellsCore_CriticalExtraDamage: - bonus.increase.criticalDamage = 8 * gradeMultiplier; + bonus.increase.criticalDamage = 12 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Hell's Core", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_EnergyWave_DamageIncrease: - bonus.increase.damage = 25 * gradeMultiplier; + bonus.increase.damage = 5 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Energy Wave", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_EnergyWave_CriticalExtraDamage: - bonus.increase.criticalDamage = 8 * gradeMultiplier; + bonus.increase.criticalDamage = 12 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Energy Wave", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_GreatFireWave_DamageIncrease: - bonus.increase.damage = 25 * gradeMultiplier; + bonus.increase.damage = 5 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Great Fire Wave", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_GreatFireWave_CriticalExtraDamage: @@ -482,24 +482,24 @@ void WheelModifierContext::addStrategies(WheelGemSupremeModifier_t modifier, uin m_strategies.emplace_back(std::make_unique(m_wheel, "Great Fire Wave", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_RageOfTheSkies_DamageIncrease: - bonus.increase.damage = 25 * gradeMultiplier; + bonus.increase.damage = 8 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Rage of the Skies", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_RageOfTheSkies_CriticalExtraDamage: - bonus.increase.criticalDamage = 8 * gradeMultiplier; + bonus.increase.criticalDamage = 12 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Rage of the Skies", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_GreatEnergyBeam_DamageIncrease: - bonus.increase.damage = 25 * gradeMultiplier; + bonus.increase.damage = 10 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Great Energy Beam", bonus)); break; case WheelGemSupremeModifier_t::Sorcerer_GreatEnergyBeam_CriticalExtraDamage: - bonus.increase.criticalDamage = 8 * gradeMultiplier; + bonus.increase.criticalDamage = 15 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Great Energy Beam", bonus)); break; case WheelGemSupremeModifier_t::Druid_AvatarOfNature_Cooldown: - bonus.decrease.cooldown = 300 * 1000; + bonus.decrease.cooldown = 900 * 1000; wheelBonus.momentum += grade < 3 ? 0.33 * grade : 1; m_strategies.emplace_back(std::make_unique(m_wheel, "Avatar of Nature", bonus)); break; @@ -509,51 +509,51 @@ void WheelModifierContext::addStrategies(WheelGemSupremeModifier_t modifier, uin m_strategies.emplace_back(std::make_unique(m_wheel, "Nature's Embrace", bonus)); break; case WheelGemSupremeModifier_t::Druid_TerraBurst_DamageIncrease: - bonus.increase.damage = 25 * gradeMultiplier; + bonus.increase.damage = 7 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Terra Burst", bonus)); break; case WheelGemSupremeModifier_t::Druid_TerraBurst_CriticalExtraDamage: - bonus.increase.criticalDamage = 8 * gradeMultiplier; + bonus.increase.criticalDamage = 12 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Terra Burst", bonus)); break; case WheelGemSupremeModifier_t::Druid_IceBurst_DamageIncrease: - bonus.increase.damage = 25 * gradeMultiplier; + bonus.increase.damage = 7 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Ice Burst", bonus)); break; case WheelGemSupremeModifier_t::Druid_IceBurst_CriticalExtraDamage: - bonus.increase.criticalDamage = 8 * gradeMultiplier; + bonus.increase.criticalDamage = 12 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Ice Burst", bonus)); break; case WheelGemSupremeModifier_t::Druid_EternalWinter_CriticalExtraDamage: - bonus.increase.criticalDamage = 8 * gradeMultiplier; + bonus.increase.criticalDamage = 12 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Eternal Winter", bonus)); break; case WheelGemSupremeModifier_t::Druid_EternalWinter_DamageIncrease: - bonus.increase.damage = 25 * gradeMultiplier; + bonus.increase.damage = 8 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Eternal Winter", bonus)); break; case WheelGemSupremeModifier_t::Druid_TerraWave_DamageIncrease: - bonus.increase.damage = 25 * gradeMultiplier; + bonus.increase.damage = 5 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Terra Wave", bonus)); break; case WheelGemSupremeModifier_t::Druid_TerraWave_CriticalExtraDamage: - bonus.increase.criticalDamage = 8 * gradeMultiplier; + bonus.increase.criticalDamage = 12 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Terra Wave", bonus)); break; case WheelGemSupremeModifier_t::Druid_StrongIceWave_DamageIncrease: - bonus.increase.damage = 25 * gradeMultiplier; + bonus.increase.damage = 8 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Strong Ice Wave", bonus)); break; case WheelGemSupremeModifier_t::Druid_StrongIceWave_CriticalExtraDamage: - bonus.increase.criticalDamage = 8 * gradeMultiplier; + bonus.increase.criticalDamage = 15 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Strong Ice Wave", bonus)); break; case WheelGemSupremeModifier_t::Druid_HealFriend_HealingIncrease: - bonus.increase.heal = 10 * gradeMultiplier; + bonus.increase.heal = 5 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Heal Friend", bonus)); break; case WheelGemSupremeModifier_t::Druid_MassHealing_HealingIncrease: - bonus.increase.heal = 10 * gradeMultiplier; + bonus.increase.heal = 5 * gradeMultiplier; m_strategies.emplace_back(std::make_unique(m_wheel, "Mass Healing", bonus)); break; default: diff --git a/src/game/game.cpp b/src/game/game.cpp index 1f6ce9951f5..79b96b697cd 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -7002,7 +7002,7 @@ void Game::applyWheelOfDestinyEffectsToDamage(CombatDamage &damage, const std::s if (damage.secondary.value != 0) { damage.secondary.value -= attackerPlayer->wheel()->getStat(WheelStat_t::DAMAGE); } - if (damage.instantSpellName == "Twin Burst") { + if (damage.instantSpellName == "Ice Burst" || damage.instantSpellName == "Terra Burst") { int32_t damageBonus = attackerPlayer->wheel()->checkTwinBurstByTarget(target); if (damageBonus != 0) { damage.primary.value += (damage.primary.value * damageBonus) / 100.; @@ -9834,7 +9834,7 @@ void Game::playerSaveWheel(uint32_t playerId, NetworkMessage &msg) { return; } - if (player->isUIExhausted()) { + if (player->isUIExhausted(1000)) { player->sendCancelMessage(RETURNVALUE_YOUAREEXHAUSTED); return; } diff --git a/src/io/io_wheel.cpp b/src/io/io_wheel.cpp index 72d5e207d4c..2e1b57c79bc 100644 --- a/src/io/io_wheel.cpp +++ b/src/io/io_wheel.cpp @@ -181,7 +181,7 @@ const VocationBonusMap &IOWheel::getWheelMapFunctions() const { std::pair IOWheel::getRevelationStatByStage(WheelStageEnum_t stageType) const { // Let's remove one, because the std::array starts with 0 and the stages with 1 - auto array = m_wheelBonusData.revelation.stats[static_cast(stageType) - 1]; + const auto &array = m_wheelBonusData.revelation.stats[static_cast(stageType) - 1]; return std::make_pair(array.damage, array.healing); } @@ -222,32 +222,32 @@ void IOWheel::initializeMapData() { void IOWheel::initializeDruidSpells() { m_wheelBonusData.spells.druid[0].name = "Strong Ice Wave"; m_wheelBonusData.spells.druid[0].grade[1].leech.mana = 3; - m_wheelBonusData.spells.druid[0].grade[2].increase.damage = 30; + m_wheelBonusData.spells.druid[0].grade[2].increase.damage = 10; m_wheelBonusData.spells.druid[1].name = "Mass Healing"; - m_wheelBonusData.spells.druid[1].grade[1].increase.heal = 10; + m_wheelBonusData.spells.druid[1].grade[1].increase.heal = 4; m_wheelBonusData.spells.druid[1].grade[2].increase.area = true; m_wheelBonusData.spells.druid[2].name = "Nature's Embrace"; - m_wheelBonusData.spells.druid[2].grade[1].increase.heal = 10; + m_wheelBonusData.spells.druid[2].grade[1].increase.heal = 11; m_wheelBonusData.spells.druid[2].grade[2].decrease.cooldown = 10; m_wheelBonusData.spells.druid[3].name = "Terra Wave"; - m_wheelBonusData.spells.druid[3].grade[1].increase.damage = 25; + m_wheelBonusData.spells.druid[3].grade[1].increase.damage = static_cast(std::round(6.5)); m_wheelBonusData.spells.druid[3].grade[2].leech.life = 5; m_wheelBonusData.spells.druid[4].name = "Heal Friend"; m_wheelBonusData.spells.druid[4].grade[1].decrease.manaCost = 10; - m_wheelBonusData.spells.druid[4].grade[2].increase.heal = 10; + m_wheelBonusData.spells.druid[4].grade[2].increase.heal = static_cast(std::round(5.5)); } void IOWheel::initializeKnightSpells() { m_wheelBonusData.spells.knight[0].name = "Front Sweep"; m_wheelBonusData.spells.knight[0].grade[1].leech.life = 5; - m_wheelBonusData.spells.knight[0].grade[2].increase.damage = 30; + m_wheelBonusData.spells.knight[0].grade[2].increase.damage = 14; m_wheelBonusData.spells.knight[1].name = "Groundshaker"; - m_wheelBonusData.spells.knight[1].grade[1].increase.damage = 25; + m_wheelBonusData.spells.knight[1].grade[1].increase.damage = static_cast(std::round(12.5)); m_wheelBonusData.spells.knight[1].grade[2].decrease.cooldown = 2; m_wheelBonusData.spells.knight[2].name = "Chivalrous Challenge"; @@ -255,12 +255,12 @@ void IOWheel::initializeKnightSpells() { m_wheelBonusData.spells.knight[2].grade[2].increase.aditionalTarget = 1; m_wheelBonusData.spells.knight[3].name = "Intense Wound Cleansing"; - m_wheelBonusData.spells.knight[3].grade[1].increase.heal = 10; + m_wheelBonusData.spells.knight[3].grade[1].increase.heal = 125; m_wheelBonusData.spells.knight[3].grade[2].decrease.cooldown = 300; m_wheelBonusData.spells.knight[4].name = "Fierce Berserk"; m_wheelBonusData.spells.knight[4].grade[1].decrease.manaCost = 30; - m_wheelBonusData.spells.knight[4].grade[2].increase.damage = 25; + m_wheelBonusData.spells.knight[4].grade[2].increase.damage = 10; } void IOWheel::initializePaladinSpells() { @@ -270,7 +270,7 @@ void IOWheel::initializePaladinSpells() { m_wheelBonusData.spells.paladin[1].name = "Strong Ethereal Spear"; m_wheelBonusData.spells.paladin[1].grade[1].decrease.cooldown = 2; - m_wheelBonusData.spells.paladin[1].grade[2].increase.damage = 25; + m_wheelBonusData.spells.paladin[1].grade[2].increase.damage = 380; m_wheelBonusData.spells.paladin[2].name = "Divine Dazzle"; m_wheelBonusData.spells.paladin[2].grade[1].increase.aditionalTarget = 1; @@ -283,7 +283,7 @@ void IOWheel::initializePaladinSpells() { m_wheelBonusData.spells.paladin[4].name = "Divine Caldera"; m_wheelBonusData.spells.paladin[4].grade[1].decrease.manaCost = 20; - m_wheelBonusData.spells.paladin[4].grade[2].increase.damage = 25; + m_wheelBonusData.spells.paladin[4].grade[2].increase.damage = static_cast(std::round(8.5)); } void IOWheel::initializeSorcererSpells() { @@ -292,19 +292,19 @@ void IOWheel::initializeSorcererSpells() { m_wheelBonusData.spells.sorcerer[1].name = "Sap Strength"; m_wheelBonusData.spells.sorcerer[1].grade[1].increase.area = true; - m_wheelBonusData.spells.sorcerer[1].grade[2].increase.damageReduction = 10; + m_wheelBonusData.spells.sorcerer[1].grade[2].increase.damageReduction = 1; m_wheelBonusData.spells.sorcerer[2].name = "Energy Wave"; - m_wheelBonusData.spells.sorcerer[2].grade[1].increase.damage = 25; + m_wheelBonusData.spells.sorcerer[2].grade[1].increase.damage = 5; m_wheelBonusData.spells.sorcerer[2].grade[2].increase.area = true; m_wheelBonusData.spells.sorcerer[3].name = "Great Fire Wave"; m_wheelBonusData.spells.sorcerer[3].grade[1].increase.criticalDamage = 15; m_wheelBonusData.spells.sorcerer[3].grade[1].increase.criticalChance = 10; - m_wheelBonusData.spells.sorcerer[3].grade[2].increase.damage = 25; + m_wheelBonusData.spells.sorcerer[3].grade[2].increase.damage = 5; m_wheelBonusData.spells.sorcerer[4].name = "Any_Focus_Mage_Spell"; - m_wheelBonusData.spells.sorcerer[4].grade[1].increase.damage = 25; + m_wheelBonusData.spells.sorcerer[4].grade[1].increase.damage = 5; m_wheelBonusData.spells.sorcerer[4].grade[2].decrease.cooldown = 4; m_wheelBonusData.spells.sorcerer[4].grade[2].decrease.secondaryGroupCooldown = 4; } @@ -399,7 +399,7 @@ void IOWheel::slotGreen200(const std::shared_ptr &player, uint16_t point bonusData.stats.health += 2 * points; bonusData.stats.mana += 3 * points; if (pointsInSlot) { - bonusData.instant.positionalTatics = true; + bonusData.instant.positionalTactics = true; } } else { bonusData.stats.health += 1 * points; diff --git a/src/utils/tools.cpp b/src/utils/tools.cpp index fff20962d15..09a18b37439 100644 --- a/src/utils/tools.cpp +++ b/src/utils/tools.cpp @@ -1583,6 +1583,12 @@ SpellGroup_t stringToSpellGroup(const std::string &value) { if (tmpStr == "ultimatestrikes" || tmpStr == "8") { return SPELLGROUP_ULTIMATESTRIKES; } + if (tmpStr == "burstsofnature" || tmpStr == "9") { + return SPELLGROUP_BURSTS_OF_NATURE; + } + if (tmpStr == "greatbeams" || tmpStr == "10") { + return SPELLGROUP_GREAT_BEAMS; + } return SPELLGROUP_NONE; } From 982f1842192e25e304e03208a335082b18b67dcd Mon Sep 17 00:00:00 2001 From: Luan Luciano Date: Tue, 12 Nov 2024 23:55:15 -0300 Subject: [PATCH 08/12] fix: sending items to stash interrupted by items obtained from store (#2886) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • When send items from a container to the stash, the process is interrupted when there are items obtained from the store. • Fixed issue with sending items from the loot pouch to the stash --- src/creatures/players/player.cpp | 2 +- src/game/game.cpp | 32 ++++++++++++++++++-------------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 823bd38e9e3..4eca148a1e7 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -8018,7 +8018,7 @@ void sendStowItems(const std::shared_ptr &item, const std::shared_ptr &item, uint32_t count, bool allItems) { - if (!item || !item->isItemStorable()) { + if (!item || (!item->isItemStorable() && item->getID() != ITEM_GOLD_POUCH)) { sendCancelMessage("This item cannot be stowed here."); return; } diff --git a/src/game/game.cpp b/src/game/game.cpp index 79b96b697cd..1ec06abd02b 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -1667,7 +1667,7 @@ void Game::playerMoveItemByPlayerID(uint32_t playerId, const Position &fromPos, void Game::playerMoveItem(const std::shared_ptr &player, const Position &fromPos, uint16_t itemId, uint8_t fromStackPos, const Position &toPos, uint8_t count, std::shared_ptr item, std::shared_ptr toCylinder) { if (!player->canDoAction()) { - uint32_t delay = player->getNextActionTime(); + const uint32_t delay = player->getNextActionTime(); const auto &task = createPlayerTask( delay, [this, playerId = player->getID(), fromPos, itemId, fromStackPos, toPos, count] { @@ -1742,7 +1742,7 @@ void Game::playerMoveItem(const std::shared_ptr &player, const Position } // check if we can move this item - if (ReturnValue ret = checkMoveItemToCylinder(player, fromCylinder, toCylinder, item, toPos); ret != RETURNVALUE_NOERROR) { + if (auto ret = checkMoveItemToCylinder(player, fromCylinder, toCylinder, item, toPos); ret != RETURNVALUE_NOERROR) { player->sendCancelMessage(ret); return; } @@ -1774,8 +1774,8 @@ void Game::playerMoveItem(const std::shared_ptr &player, const Position return; } - std::shared_ptr toCylinderTile = toCylinder->getTile(); - const Position &mapToPos = toCylinderTile->getPosition(); + const auto toCylinderTile = toCylinder->getTile(); + const auto &mapToPos = toCylinderTile->getPosition(); // hangable item specific code if (item->isHangable() && toCylinderTile->hasFlag(TILESTATE_SUPPORTS_HANGABLE)) { @@ -1794,14 +1794,14 @@ void Game::playerMoveItem(const std::shared_ptr &player, const Position } if (!Position::areInRange<1, 1, 0>(playerPos, mapToPos)) { - Position walkPos = mapToPos; + auto walkPos = mapToPos; if (vertical) { walkPos.x++; } else { walkPos.y++; } - Position itemPos = fromPos; + auto itemPos = fromPos; uint8_t itemStackPos = fromStackPos; if (fromPos.x != 0xFFFF && Position::areInRange<1, 1>(mapFromPos, playerPos) @@ -1809,7 +1809,7 @@ void Game::playerMoveItem(const std::shared_ptr &player, const Position // need to pickup the item first std::shared_ptr moveItem = nullptr; - ReturnValue ret = internalMoveItem(fromCylinder, player, INDEX_WHEREEVER, item, count, &moveItem); + const auto ret = internalMoveItem(fromCylinder, player, INDEX_WHEREEVER, item, count, &moveItem); if (ret != RETURNVALUE_NOERROR) { player->sendCancelMessage(ret); return; @@ -1837,7 +1837,7 @@ void Game::playerMoveItem(const std::shared_ptr &player, const Position } } - auto throwRange = item->getThrowRange(); + const auto throwRange = item->getThrowRange(); if ((Position::getDistanceX(playerPos, mapToPos) > throwRange) || (Position::getDistanceY(playerPos, mapToPos) > throwRange) || (Position::getDistanceZ(mapFromPos, mapToPos) * 4 > throwRange)) { player->sendCancelMessage(RETURNVALUE_DESTINATIONOUTOFREACH); return; @@ -1866,8 +1866,8 @@ void Game::playerMoveItem(const std::shared_ptr &player, const Position } if (item->isWrapable() || item->isStoreItem() || (item->hasOwner() && !item->isOwner(player))) { - auto toHouseTile = map.getTile(mapToPos)->dynamic_self_cast(); - auto fromHouseTile = map.getTile(mapFromPos)->dynamic_self_cast(); + const auto toHouseTile = map.getTile(mapToPos)->dynamic_self_cast(); + const auto fromHouseTile = map.getTile(mapFromPos)->dynamic_self_cast(); if (fromHouseTile && (!toHouseTile || toHouseTile->getHouse()->getId() != fromHouseTile->getHouse()->getId())) { player->sendCancelMessage("You cannot move this item out of this house."); return; @@ -1882,12 +1882,14 @@ void Game::playerMoveItem(const std::shared_ptr &player, const Position player->sendCancelMessage(RETURNVALUE_NOTMOVABLE); return; } - ReturnValue ret = internalMoveItem(fromCylinder, toCylinder, toIndex, item, count, nullptr, 0, player); + + const auto ret = internalMoveItem(fromCylinder, toCylinder, toIndex, item, count, nullptr, 0, player); if (ret != RETURNVALUE_NOERROR) { player->sendCancelMessage(ret); } else if (toCylinder->getContainer() && fromCylinder->getContainer() && fromCylinder->getContainer()->countsToLootAnalyzerBalance() && toCylinder->getContainer()->getTopParent() == player) { player->sendLootStats(item, count); } + player->cancelPush(); item->checkDecayMapItemOnMove(); @@ -1916,7 +1918,9 @@ ReturnValue Game::checkMoveItemToCylinder(const std::shared_ptr &player, } } - if (containerID == ITEM_GOLD_POUCH) { + const auto containerToStow = isTryingToStow(toPos, toCylinder); + + if (containerID == ITEM_GOLD_POUCH && !containerToStow) { if (g_configManager().getBoolean(TOGGLE_GOLD_POUCH_QUICKLOOT_ONLY)) { return RETURNVALUE_CONTAINERNOTENOUGHROOM; } @@ -1942,7 +1946,7 @@ ReturnValue Game::checkMoveItemToCylinder(const std::shared_ptr &player, return RETURNVALUE_NOTBOUGHTINSTORE; } - if (item->isStoreItem()) { + if (item->isStoreItem() && !containerToStow) { bool isValidMoveItem = false; auto fromHouseTile = fromCylinder->getTile(); auto house = fromHouseTile ? fromHouseTile->getHouse() : nullptr; @@ -1973,7 +1977,7 @@ ReturnValue Game::checkMoveItemToCylinder(const std::shared_ptr &player, if (item->getContainer() && !item->isStoreItem()) { for (const std::shared_ptr &containerItem : item->getContainer()->getItems(true)) { - if (containerItem->isStoreItem() && ((containerID != ITEM_GOLD_POUCH && containerID != ITEM_DEPOT && containerID != ITEM_STORE_INBOX) || (topParentContainer->getParent() && topParentContainer->getParent()->getContainer() && (!topParentContainer->getParent()->getContainer()->isDepotChest() || topParentContainer->getParent()->getContainer()->getID() != ITEM_STORE_INBOX)))) { + if (containerItem->isStoreItem() && !containerToStow && ((containerID != ITEM_GOLD_POUCH && containerID != ITEM_DEPOT && containerID != ITEM_STORE_INBOX) || (topParentContainer->getParent() && topParentContainer->getParent()->getContainer() && (!topParentContainer->getParent()->getContainer()->isDepotChest() || topParentContainer->getParent()->getContainer()->getID() != ITEM_STORE_INBOX)))) { return RETURNVALUE_NOTPOSSIBLE; } } From d360a63e1068dce15ae3b5db36589b2ce76cb13d Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Wed, 13 Nov 2024 00:02:22 -0300 Subject: [PATCH 09/12] fix: crash concurrent modification/casting in tile management (#3107) This adds the implementation for dynamically determining the type of tile (HouseTile, StaticTile, DynamicTile) when creating a new tile object. The motivation is to correctly identify and assign specific tile types, which enhances code readability and maintainability. The change also ensures that houses have associated HouseTile objects for better data integrity. --- src/items/tile.hpp | 4 ++++ src/map/house/housetile.cpp | 3 +++ src/map/house/housetile.hpp | 1 + src/map/mapcache.cpp | 19 ++++++++++++------- 4 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/items/tile.hpp b/src/items/tile.hpp index ba3834d9914..a719f5dcef3 100644 --- a/src/items/tile.hpp +++ b/src/items/tile.hpp @@ -288,6 +288,8 @@ class DynamicTile : public Tile { CreatureVector creatures; public: + explicit DynamicTile(const Position &position) : + Tile(position.x, position.y, position.z) { } DynamicTile(uint16_t x, uint16_t y, uint8_t z) : Tile(x, y, z) { } @@ -323,6 +325,8 @@ class StaticTile final : public Tile { std::unique_ptr creatures; public: + explicit StaticTile(const Position &position) : + Tile(position.x, position.y, position.z) { } StaticTile(uint16_t x, uint16_t y, uint8_t z) : Tile(x, y, z) { } diff --git a/src/map/house/housetile.cpp b/src/map/house/housetile.cpp index ce9229ce42c..71d6ee241ed 100644 --- a/src/map/house/housetile.cpp +++ b/src/map/house/housetile.cpp @@ -17,6 +17,9 @@ #include "map/house/house.hpp" #include "utils/tools.hpp" +HouseTile::HouseTile(const Position &position, std::shared_ptr newHouse) : + DynamicTile(position.x, position.y, position.z), house(std::move(newHouse)) { } + HouseTile::HouseTile(int32_t initX, int32_t initY, int32_t initZ, std::shared_ptr initHouse) : DynamicTile(initX, initY, initZ), house(std::move(initHouse)) { } diff --git a/src/map/house/housetile.hpp b/src/map/house/housetile.hpp index 6a191d0f84d..293fc0074ee 100644 --- a/src/map/house/housetile.hpp +++ b/src/map/house/housetile.hpp @@ -17,6 +17,7 @@ class HouseTile final : public DynamicTile { public: using Tile::addThing; + HouseTile(const Position &position, std::shared_ptr house); HouseTile(int32_t x, int32_t y, int32_t z, std::shared_ptr house); // cylinder implementations diff --git a/src/map/mapcache.cpp b/src/map/mapcache.cpp index 79a9683cfca..f505c544fe2 100644 --- a/src/map/mapcache.cpp +++ b/src/map/mapcache.cpp @@ -120,18 +120,23 @@ std::shared_ptr MapCache::getOrCreateTileFromCache(const std::shared_ptr tile = nullptr; + auto pos = Position(x, y, z); + if (cachedTile->isHouse()) { - const auto &house = map->houses.getHouse(cachedTile->houseId); - tile = std::make_shared(x, y, z, house); - house->addTile(std::static_pointer_cast(tile)); + if (const auto &house = map->houses.getHouse(cachedTile->houseId)) { + tile = std::make_shared(pos, house); + tile->safeCall([tile] { + tile->getHouse()->addTile(tile->static_self_cast()); + }); + } else { + g_logger().error("[{}] house not found for houseId {}", std::source_location::current().function_name(), cachedTile->houseId); + } } else if (cachedTile->isStatic) { - tile = std::make_shared(x, y, z); + tile = std::make_shared(pos); } else { - tile = std::make_shared(x, y, z); + tile = std::make_shared(pos); } - auto pos = Position(x, y, z); - if (cachedTile->ground != nullptr) { tile->internalAddThing(createItem(cachedTile->ground, pos)); } From a72c5d05bfc603b507dea363edd9d5831f65139d Mon Sep 17 00:00:00 2001 From: murilo09 <78226931+murilo09@users.noreply.github.com> Date: Wed, 13 Nov 2024 00:02:48 -0300 Subject: [PATCH 10/12] refactor: move blessing bytes to cpp (#3064) Moving bytes from modules to cpp. --- data-otservbr-global/npc/kais.lua | 44 ++- data-otservbr-global/npc/nomad.lua | 44 ++- .../creaturescripts/others/droploot.lua | 5 +- data/libs/compat/compat.lua | 4 +- .../systems/blessing.lua} | 322 ++++++------------ data/libs/systems/load.lua | 1 + data/modules/modules.xml | 4 - data/modules/scripts/blessings/assets.lua | 47 --- data/npclib/npc.lua | 2 +- data/npclib/npc_system/modules.lua | 6 +- .../scripts/actions/items/blessing_charms.lua | 2 +- data/scripts/actions/items/check_bless.lua | 2 +- .../player/adventure_blessing_login.lua | 2 +- data/scripts/systems/item_tiers.lua | 2 +- data/scripts/talkactions/gm/bless_status.lua | 4 +- src/creatures/players/player.cpp | 54 ++- src/enums/player_blessings.hpp | 4 +- .../creatures/player/player_functions.cpp | 15 + .../creatures/player/player_functions.hpp | 1 + src/server/network/protocol/protocolgame.cpp | 167 ++++++--- src/server/network/protocol/protocolgame.hpp | 3 + src/utils/tools.cpp | 41 +++ src/utils/tools.hpp | 3 + 23 files changed, 384 insertions(+), 395 deletions(-) rename data/{modules/scripts/blessings/blessings.lua => libs/systems/blessing.lua} (52%) delete mode 100644 data/modules/scripts/blessings/assets.lua diff --git a/data-otservbr-global/npc/kais.lua b/data-otservbr-global/npc/kais.lua index d7fe97f0e75..5da84b1d9cf 100644 --- a/data-otservbr-global/npc/kais.lua +++ b/data-otservbr-global/npc/kais.lua @@ -46,8 +46,8 @@ npcType.onCloseChannel = function(npc, creature) end -- Blood of the Mountain -local blessKeyword = keywordHandler:addKeyword({ "blood" }, StdModule.say, { npcHandler = npcHandler, text = "Would you like to receive that protection for a sacrifice of |BLESSCOST| gold, child?" }) -blessKeyword:addChildKeyword({ "yes" }, StdModule.bless, { npcHandler = npcHandler, text = "So receive the Blood of the Mountain, pilgrim.", cost = "|BLESSCOST|", bless = 7 }) +local blessKeyword = keywordHandler:addKeyword({ "enhanced" }, StdModule.say, { npcHandler = npcHandler, text = "I have the power to grant you the blood of the mountain's blessing. But I must ask of you to sacrifice |BLESSCOST| gold. Are you prepared for that?" }) +blessKeyword:addChildKeyword({ "yes" }, StdModule.bless, { npcHandler = npcHandler, text = "So receive the blood of the mountain, master.", cost = "|BLESSCOST|", bless = 8 }) blessKeyword:addChildKeyword({ "" }, StdModule.say, { npcHandler = npcHandler, text = "Fine. You are free to decline my offer.", reset = true }) -- Healing @@ -58,6 +58,7 @@ local function addHealKeyword(text, condition, effect) player:removeCondition(condition) player:getPosition():sendMagicEffect(effect) end) + keywordHandler:addAliasKeyword({ "help" }) end addHealKeyword("You are burning. Let me quench those flames.", CONDITION_FIRE, CONST_ME_MAGIC_GREEN) @@ -74,26 +75,19 @@ end, function(player) player:getPosition():sendMagicEffect(CONST_ME_MAGIC_GREEN) end) keywordHandler:addKeyword({ "heal" }, StdModule.say, { npcHandler = npcHandler, text = "You aren't looking that bad. Sorry, I can't help you. But if you are looking for additional protection you should go on the {pilgrimage} of ashes or get the protection of the {twist of fate} here." }) +keywordHandler:addAliasKeyword({ "help" }) -- Basic -keywordHandler:addKeyword({ "pilgrimage" }, StdModule.say, { npcHandler = npcHandler, text = "Whenever you receive a lethal wound, your vital force is damaged and there is a chance that you lose some of your equipment. With every single of the five {blessings} you have, this damage and chance of loss will be reduced." }) -keywordHandler:addKeyword({ "blessings" }, StdModule.say, { npcHandler = npcHandler, text = "There are five blessings available in five sacred places: the {spiritual} shielding, the spark of the {phoenix}, the {embrace} of Tibia, the fire of the {suns} and the wisdom of {solitude}. Additionally, you can receive the {twist of fate} here." }) -keywordHandler:addKeyword({ "spiritual" }, StdModule.say, { npcHandler = npcHandler, text = "I see you received the spiritual shielding in the whiteflower temple south of Thais." }, function(player) - return player:hasBlessing(1) -end) -keywordHandler:addAliasKeyword({ "shield" }) -keywordHandler:addKeyword({ "suns" }, StdModule.say, { npcHandler = npcHandler, text = "I can see you received the blessing of the two suns in the suntower near Ab'Dendriel." }, function(player) - return player:hasBlessing(3) -end) -keywordHandler:addAliasKeyword({ "fire" }) -keywordHandler:addKeyword({ "phoenix" }, StdModule.say, { npcHandler = npcHandler, text = "I can sense that the spark of the phoenix already was given to you by the dwarven priests of earth and fire in Kazordoon." }, function(player) - return player:hasBlessing(4) -end) -keywordHandler:addAliasKeyword({ "spark" }) -keywordHandler:addKeyword({ "solitude" }, StdModule.say, { npcHandler = npcHandler, text = "I can sense you already talked to the hermit Eremo on the isle of Cormaya and received this blessing." }, function(player) - return player:hasBlessing(5) -end) -keywordHandler:addAliasKeyword({ "wisdom" }) +keywordHandler:addKeyword({ "Kais" }, StdModule.say, { npcHandler = npcHandler, text = "I am Kais, Kais the Bound. Eternally {fixed} to the wretched place, unless... unless I prove my worth in aiding all those who seek my {help}." }) + +keywordHandler:addKeyword({ "pilgrimage" }, StdModule.say, { npcHandler = npcHandler, text = "Well, as I am quite in a fix currently, my only hope to escape this situation may be to grant you {healing} or an {enhanced} blessing if you wish. You must desire SOMETHING, right? Sure you do." }) +keywordHandler:addAliasKeyword({ "job" }) + +keywordHandler:addKeyword({ "blessing" }, StdModule.say, { + npcHandler = npcHandler, + text = "Besides the {enhanced} blessing available from me, I know of one other, granted by a solitary {nomad} far west of Svargrond. There are also five different other blessings available, each in a sacred place. ...\nThese blessings are: the {spiritual} shielding, the spark of the {phoenix}, the {embrace} of Tibia, the fire of the {suns} and the wisdom of {solitude}.", +}) + keywordHandler:addKeyword({ "spiritual" }, StdModule.say, { npcHandler = npcHandler, text = "You can ask for the blessing of spiritual shielding in the whiteflower temple south of Thais." }) keywordHandler:addAliasKeyword({ "shield" }) keywordHandler:addKeyword({ "suns" }, StdModule.say, { npcHandler = npcHandler, text = "You can ask for the blessing of the two suns in the suntower near Ab'Dendriel." }) @@ -102,10 +96,14 @@ keywordHandler:addKeyword({ "phoenix" }, StdModule.say, { npcHandler = npcHandle keywordHandler:addAliasKeyword({ "spark" }) keywordHandler:addKeyword({ "solitude" }, StdModule.say, { npcHandler = npcHandler, text = "Talk to the hermit Eremo on the isle of Cormaya about this blessing." }) keywordHandler:addAliasKeyword({ "wisdom" }) +keywordHandler:addKeyword({ "embrace" }, StdModule.say, { npcHandler = npcHandler, text = "The druids north of Carlin will provide you with the embrace of Tibia." }) +keywordHandler:addAliasKeyword({ "tibia" }) + +keywordHandler:addKeyword({ "nomad" }, StdModule.say, { npcHandler = npcHandler, text = "I know everyone and everything. It is certain that there is another enhanced blessing, the 'heart of the mountain'. Talk to a nomad far to the west of Svargrond, hiding slightly above ground." }) -npcHandler:setMessage(MESSAGE_GREET, "Welcome, noble |PLAYERNAME|") -npcHandler:setMessage(MESSAGE_WALKAWAY, "Good Bye, noble |PLAYERNAME|") -npcHandler:setMessage(MESSAGE_FAREWELL, "Good Bye, noble |PLAYERNAME|") +npcHandler:setMessage(MESSAGE_GREET, "Hmm... surely you are in need of {help} - will you let me? I am {Kais} the Bound and I can lend you a hand in {healing} your body and soul or even grant an {enhanced} blessing!") +npcHandler:setMessage(MESSAGE_WALKAWAY, "Fare you well... |PLAYERNAME|") +npcHandler:setMessage(MESSAGE_FAREWELL, "Fare you well... |PLAYERNAME|") npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true) diff --git a/data-otservbr-global/npc/nomad.lua b/data-otservbr-global/npc/nomad.lua index 79dee9cc229..2e33e91a872 100644 --- a/data-otservbr-global/npc/nomad.lua +++ b/data-otservbr-global/npc/nomad.lua @@ -50,8 +50,8 @@ npcType.onCloseChannel = function(npc, creature) end -- Heart of the Mountain -local blessKeyword = keywordHandler:addKeyword({ "heart" }, StdModule.say, { npcHandler = npcHandler, text = "Would you like to receive that protection for a sacrifice of |BLESSCOST| gold, child?" }) -blessKeyword:addChildKeyword({ "yes" }, StdModule.bless, { npcHandler = npcHandler, text = "So receive the Heart of the Mountain, pilgrim.", cost = "|BLESSCOST|", bless = 8 }) +local blessKeyword = keywordHandler:addKeyword({ "enhanced" }, StdModule.say, { npcHandler = npcHandler, text = "I am able to grant you the heart of the mountain's blessing. But I must ask of you to sacrifice |BLESSCOST| gold. Are you willing to part with that amount of wealth?" }) +blessKeyword:addChildKeyword({ "yes" }, StdModule.bless, { npcHandler = npcHandler, text = "Receive the heart of the mountain then.", cost = "|BLESSCOST|", bless = 7 }) blessKeyword:addChildKeyword({ "" }, StdModule.say, { npcHandler = npcHandler, text = "Fine. You are free to decline my offer.", reset = true }) -- Healing @@ -62,6 +62,7 @@ local function addHealKeyword(text, condition, effect) player:removeCondition(condition) player:getPosition():sendMagicEffect(effect) end) + keywordHandler:addAliasKeyword({ "help" }) end addHealKeyword("You are burning. Let me quench those flames.", CONDITION_FIRE, CONST_ME_MAGIC_GREEN) @@ -78,26 +79,17 @@ end, function(player) player:getPosition():sendMagicEffect(CONST_ME_MAGIC_GREEN) end) keywordHandler:addKeyword({ "heal" }, StdModule.say, { npcHandler = npcHandler, text = "You aren't looking that bad. Sorry, I can't help you. But if you are looking for additional protection you should go on the {pilgrimage} of ashes or get the protection of the {twist of fate} here." }) +keywordHandler:addAliasKeyword({ "help" }) -- Basic -keywordHandler:addKeyword({ "pilgrimage" }, StdModule.say, { npcHandler = npcHandler, text = "Whenever you receive a lethal wound, your vital force is damaged and there is a chance that you lose some of your equipment. With every single of the five {blessings} you have, this damage and chance of loss will be reduced." }) -keywordHandler:addKeyword({ "blessings" }, StdModule.say, { npcHandler = npcHandler, text = "There are five blessings available in five sacred places: the {spiritual} shielding, the spark of the {phoenix}, the {embrace} of Tibia, the fire of the {suns} and the wisdom of {solitude}. Additionally, you can receive the {twist of fate} here." }) -keywordHandler:addKeyword({ "spiritual" }, StdModule.say, { npcHandler = npcHandler, text = "I see you received the spiritual shielding in the whiteflower temple south of Thais." }, function(player) - return player:hasBlessing(1) -end) -keywordHandler:addAliasKeyword({ "shield" }) -keywordHandler:addKeyword({ "suns" }, StdModule.say, { npcHandler = npcHandler, text = "I can see you received the blessing of the two suns in the suntower near Ab'Dendriel." }, function(player) - return player:hasBlessing(3) -end) -keywordHandler:addAliasKeyword({ "fire" }) -keywordHandler:addKeyword({ "phoenix" }, StdModule.say, { npcHandler = npcHandler, text = "I can sense that the spark of the phoenix already was given to you by the dwarven priests of earth and fire in Kazordoon." }, function(player) - return player:hasBlessing(4) -end) -keywordHandler:addAliasKeyword({ "spark" }) -keywordHandler:addKeyword({ "solitude" }, StdModule.say, { npcHandler = npcHandler, text = "I can sense you already talked to the hermit Eremo on the isle of Cormaya and received this blessing." }, function(player) - return player:hasBlessing(5) -end) -keywordHandler:addAliasKeyword({ "wisdom" }) +keywordHandler:addKeyword({ "blessing" }, StdModule.say, { + npcHandler = npcHandler, + text = "Besides the {enhanced} blessing available from me, I know of one other, granted by a solitary {nomad} far west of Svargrond. There are also five different other blessings available, each in a sacred place. ...\nThese blessings are: the {spiritual} shielding, the spark of the {phoenix}, the {embrace} of Tibia, the fire of the {suns} and the wisdom of {solitude}.", +}) + +keywordHandler:addKeyword({ "pilgrimage" }, StdModule.say, { npcHandler = npcHandler, text = "Well, as I am quite in a {fix} currently, my only hope to escape this situation may be to grant you {healing} or an {enhanced} blessing if you wish. You must desire SOMETHING, right? Sure you do." }) +keywordHandler:addAliasKeyword({ "job" }) + keywordHandler:addKeyword({ "spiritual" }, StdModule.say, { npcHandler = npcHandler, text = "You can ask for the blessing of spiritual shielding in the whiteflower temple south of Thais." }) keywordHandler:addAliasKeyword({ "shield" }) keywordHandler:addKeyword({ "suns" }, StdModule.say, { npcHandler = npcHandler, text = "You can ask for the blessing of the two suns in the suntower near Ab'Dendriel." }) @@ -106,10 +98,16 @@ keywordHandler:addKeyword({ "phoenix" }, StdModule.say, { npcHandler = npcHandle keywordHandler:addAliasKeyword({ "spark" }) keywordHandler:addKeyword({ "solitude" }, StdModule.say, { npcHandler = npcHandler, text = "Talk to the hermit Eremo on the isle of Cormaya about this blessing." }) keywordHandler:addAliasKeyword({ "wisdom" }) +keywordHandler:addKeyword({ "embrace" }, StdModule.say, { npcHandler = npcHandler, text = "The druids north of Carlin will provide you with the embrace of Tibia." }) +keywordHandler:addAliasKeyword({ "tibia" }) + +keywordHandler:addKeyword({ "djinn" }, StdModule.say, { npcHandler = npcHandler, text = "I know of a mysterious djinn, bound to an existence of slavery far to the north of Tiquanda's jungles. He is bound to tell you his secrets and offer you the 'blood of the mountain'. ...\nI do not know where exactly but I guess you must first dig that one out to actually talk to him." }) +keywordHandler:addKeyword({ "nomad" }, StdModule.say, { npcHandler = npcHandler, text = "I am one with nature, one with the ice and the cold. Names are of no worth out here. Out here, you face isolation and loneliness. Only your strength, willpower and {training} can keep you alive." }) +keywordHandler:addKeyword({ "training" }, StdModule.say, { npcHandler = npcHandler, text = "My life is one of hardship, hardship I chose freely. Endurance, strength and the power of the will are my only companions in this frigid wilderness. My strength comes from disciplined training and knowledge of the outdoors." }) -npcHandler:setMessage(MESSAGE_GREET, "Welcome, noble |PLAYERNAME|") -npcHandler:setMessage(MESSAGE_WALKAWAY, "Good Bye, noble |PLAYERNAME|") -npcHandler:setMessage(MESSAGE_FAREWELL, "Good Bye, noble |PLAYERNAME|") +npcHandler:setMessage(MESSAGE_GREET, "Ah, another diciple of the extreme... surviving the icy outdoors? Let me {help}! If you need some first aid out here, I can provide {healing} or grant you an {enhanced} blessing!") +npcHandler:setMessage(MESSAGE_WALKAWAY, "Keep a stiff upper lip!") +npcHandler:setMessage(MESSAGE_FAREWELL, "Keep a stiff upper lip!") npcHandler:addModule(FocusModule:new(), npcConfig.name, true, true, true) diff --git a/data-otservbr-global/scripts/creaturescripts/others/droploot.lua b/data-otservbr-global/scripts/creaturescripts/others/droploot.lua index 7be3564048d..9bf77afa71b 100644 --- a/data-otservbr-global/scripts/creaturescripts/others/droploot.lua +++ b/data-otservbr-global/scripts/creaturescripts/others/droploot.lua @@ -1,5 +1,7 @@ -dofile(CORE_DIRECTORY .. "/modules/scripts/blessings/blessings.lua") +dofile(CORE_DIRECTORY .. "/libs/systems/blessing.lua") + local dropLoot = CreatureEvent("DropLoot") + function dropLoot.onDeath(player, corpse, killer, mostDamage, unjustified, mostDamage_unjustified) local town = player:getTown() if town and town:getId() == TOWNS_LIST.DAWNPORT and player:getLevel() < 8 then @@ -9,6 +11,7 @@ function dropLoot.onDeath(player, corpse, killer, mostDamage, unjustified, mostD if player:hasFlag(PlayerFlag_NotGenerateLoot) then return true end + Blessings.DebugPrint("onDeath DROPLOOT EVENT DropLoot") return Blessings.PlayerDeath(player, corpse, killer) end diff --git a/data/libs/compat/compat.lua b/data/libs/compat/compat.lua index 061d1dbe47e..33aa0290841 100644 --- a/data/libs/compat/compat.lua +++ b/data/libs/compat/compat.lua @@ -572,8 +572,8 @@ function isPremium(cid) return p ~= nil and p:isPremium() or false end -function getBlessingsCost(level, byCommand) - return Blessings.getBlessingsCost(level, byCommand) +function getBlessingCost(level, byCommand, blessId) + return Blessings.getBlessingCost(level, byCommand, blessId) end function getPvpBlessingCost(level, byCommand) diff --git a/data/modules/scripts/blessings/blessings.lua b/data/libs/systems/blessing.lua similarity index 52% rename from data/modules/scripts/blessings/blessings.lua rename to data/libs/systems/blessing.lua index e061501a330..fee0e6fe8e8 100644 --- a/data/modules/scripts/blessings/blessings.lua +++ b/data/libs/systems/blessing.lua @@ -6,29 +6,44 @@ Blessings.Credits = { lastUpdate = "08/04/2020", todo = { "Insert & Select query in blessings_history", - "Add unfair fight reductio (convert the get killer is pvp fight with getDamageMap of dead player)", - "Gamestore buy blessing", - "Test ank print text", - "Test all functions", - "Test henricus prices/blessings", - "Add data \\movements\\scripts\\quests\\cults of tibia\\icedeath.lua blessing information", - "WotE data\\movements\\scripts\\quests\\wrath of the emperor\\realmTeleport.lua has line checking if player has bless 1??? wtf", - "add blessings module support npc\\lib\\npcsystem\\modules.lua", - "Fix store buying bless", - "Check if store is inside lua or source...", }, } Blessings.Config = { AdventurerBlessingLevel = configManager.getNumber(configKeys.ADVENTURERSBLESSING_LEVEL), -- Free full bless until level HasToF = not configManager.getBoolean(configKeys.TOGGLE_SERVER_IS_RETRO), -- Enables/disables twist of fate - InquisitonBlessPriceMultiplier = 1.1, -- Bless price multiplied by henricus + InquisitonBlessPriceMultiplier = 1.1, -- Bless price multiplier by henricus SkulledDeathLoseStoreItem = configManager.getBoolean(configKeys.SKULLED_DEATH_LOSE_STORE_ITEM), -- Destroy all items on store when dying with red/blackskull - InventoryGlowOnFiveBless = configManager.getBoolean(configKeys.INVENTORY_GLOW), -- Glow in yellow inventory items when the player has 5 or more bless, - Debug = false, -- Prin debug messages in console if enabled } -dofile(CORE_DIRECTORY .. "/modules/scripts/blessings/assets.lua") +Blessings.Types = { + REGULAR = 1, + ENHANCED = 2, + PvP = 3, +} + +Blessings.All = { + [1] = { id = 1, name = "Twist of Fate", type = Blessings.Types.PvP }, + [2] = { id = 2, name = "The Wisdom of Solitude", charm = 10345, type = Blessings.Types.REGULAR, losscount = true, inquisition = true }, + [3] = { id = 3, name = "The Spark of the Phoenix", charm = 10341, type = Blessings.Types.REGULAR, losscount = true, inquisition = true }, + [4] = { id = 4, name = "The Fire of the Suns", charm = 10344, type = Blessings.Types.REGULAR, losscount = true, inquisition = true }, + [5] = { id = 5, name = "The Spiritual Shielding", charm = 10343, type = Blessings.Types.REGULAR, losscount = true, inquisition = true }, + [6] = { id = 6, name = "The Embrace of Tibia", charm = 10342, type = Blessings.Types.REGULAR, losscount = true, inquisition = true }, + [7] = { id = 7, name = "Heart of the Mountain", charm = 25360, type = Blessings.Types.ENHANCED, losscount = true, inquisition = false }, + [8] = { id = 8, name = "Blood of the Mountain", charm = 25361, type = Blessings.Types.ENHANCED, losscount = true, inquisition = false }, +} + +Blessings.LossPercent = { + [0] = { item = 100, skill = 0 }, + [1] = { item = 70, skill = 8 }, + [2] = { item = 45, skill = 16 }, + [3] = { item = 25, skill = 24 }, + [4] = { item = 10, skill = 32 }, + [5] = { item = 0, skill = 40 }, + [6] = { item = 0, skill = 48 }, + [7] = { item = 0, skill = 56 }, + [8] = { item = 0, skill = 56 }, +} --[=====[ -- @@ -45,9 +60,6 @@ CREATE TABLE IF NOT EXISTS `blessings_history` ( --]=====] Blessings.DebugPrint = function(content, pre, pos) - if not Blessings.Config.Debug then - return - end if pre == nil then pre = "" else @@ -67,145 +79,93 @@ Blessings.DebugPrint = function(content, pre, pos) end end -Blessings.C_Packet = { - OpenWindow = 0xCF, -} - -Blessings.S_Packet = { - BlessDialog = 0x9B, - BlessStatus = 0x9C, -} - -function onRecvbyte(player, msg, byte) - if byte == Blessings.C_Packet.OpenWindow then - Blessings.sendBlessDialog(player) - end -end - -Blessings.sendBlessStatus = function(player, curBless) - if player:getClient().version < 1200 then - return true - end - - -- why not using ProtocolGame::sendBlessStatus ? - local msg = NetworkMessage() - msg:addByte(Blessings.S_Packet.BlessStatus) - callback = function(k) - return true - end - if curBless == nil then - curBless = player:getBlessings(callback) -- ex: {1, 2, 5, 7} - end - Blessings.DebugPrint(#curBless, "sendBlessStatus curBless") - local bitWiseCurrentBless = 0 - local blessCount = 0 +Blessings.PlayerDeath = function(player, corpse, killer) + local hasAol = (player:getSlotItem(CONST_SLOT_NECKLACE) and player:getSlotItem(CONST_SLOT_NECKLACE):getId() == ITEM_AMULETOFLOSS) + local hasSkull = table.contains({ SKULL_RED, SKULL_BLACK }, player:getSkull()) + local currBlessCount = player:getBlessings() - for i = 1, #curBless do - if curBless[i].losscount then - blessCount = blessCount + 1 - end - if (not curBless[i].losscount and Blessings.Config.HasToF) or curBless[i].losscount then - bitWiseCurrentBless = bit.bor(bitWiseCurrentBless, Blessings.BitWiseTable[curBless[i].id]) - end + if hasSkull then + Blessings.DropLoot(player, corpse, 100, true) + elseif #currBlessCount < 5 and not hasAol then + local equipLossChance = Blessings.LossPercent[#currBlessCount].item + Blessings.DropLoot(player, corpse, equipLossChance) end - if blessCount > 5 and Blessings.Config.InventoryGlowOnFiveBless then - bitWiseCurrentBless = bit.bor(bitWiseCurrentBless, 1) + if not player:getSlotItem(CONST_SLOT_BACKPACK) then + player:addItem(ITEM_BAG, 1, false, CONST_SLOT_BACKPACK) end - msg:addU16(bitWiseCurrentBless) - msg:addByte(blessCount >= 7 and 3 or (blessCount > 0 and 2 or 1)) -- Bless dialog button colour 1 = Disabled | 2 = normal | 3 = green - - -- if #curBless >= 5 then - -- msg:addU16(1) -- TODO ? - -- else - -- msg:addU16(0) - -- end - - msg:sendToPlayer(player) + return true end -Blessings.sendBlessDialog = function(player) - -- TODO: Migrate to protocolgame.cpp - local msg = NetworkMessage() - msg:addByte(Blessings.S_Packet.BlessDialog) +Blessings.DropLoot = function(player, corpse, chance, skulled) + local multiplier = 100 + math.randomseed(os.time()) + chance = chance * multiplier + Blessings.DebugPrint("DropLoot chance " .. chance) + for i = CONST_SLOT_HEAD, CONST_SLOT_AMMO do + local item = player:getSlotItem(i) + if item then + local thisChance = item:isContainer() and chance or (chance / 10) + local thisRandom = math.random(100 * multiplier) - callback = function(k) - return true - end - local curBless = player:getBlessings() - - msg:addByte(Blessings.Config.HasToF and #Blessings.All or (#Blessings.All - 1)) -- total blessings - for k = 1, #Blessings.All do - v = Blessings.All[k] - if v.type ~= Blessings.Types.PvP or Blessings.Config.HasToF then - msg:addU16(Blessings.BitWiseTable[v.id]) - msg:addByte(player:getBlessingCount(v.id)) - if player:getClient().version > 1200 then - msg:addByte(player:getBlessingCount(v.id, true)) -- Store Blessings Count + Blessings.DebugPrint(thisChance / multiplier .. "%" .. " | thisRandom " .. thisRandom / multiplier .. "%", "DropLoot item " .. item:getName() .. " |") + if skulled or thisRandom <= thisChance then + Blessings.DebugPrint("Dropped " .. item:getName()) + item:moveTo(corpse) end end end - local promotion = (player:isPremium() and player:isPromoted()) and 30 or 0 - local PvPminXPLoss = Blessings.LossPercent[#curBless].skill + promotion - local PvPmaxXPLoss = PvPminXPLoss - if Blessings.Config.HasToF then - PvPmaxXPLoss = math.floor(PvPminXPLoss * 1.15) - end - local PvEXPLoss = PvPminXPLoss - - local playerAmulet = player:getSlotItem(CONST_SLOT_NECKLACE) - local haveSkull = player:getSkull() >= 4 - hasAol = (playerAmulet and playerAmulet:getId() == ITEM_AMULETOFLOSS) - - equipLoss = Blessings.LossPercent[#curBless].item - if haveSkull then - equipLoss = 100 - elseif hasAol then - equipLoss = 0 - end - - msg:addByte(2) -- BYTE PREMIUM (only work with premium days) - msg:addByte(promotion) -- XP Loss Lower POR SER PREMIUM - msg:addByte(PvPminXPLoss) -- XP/Skill loss min pvp death - msg:addByte(PvPmaxXPLoss) -- XP/Skill loss max pvp death - msg:addByte(PvEXPLoss) -- XP/Skill pve death - msg:addByte(equipLoss) -- Equip container lose pvp death - msg:addByte(equipLoss) -- Equip container pve death - - msg:addByte(haveSkull and 1 or 0) -- is red/black skull - msg:addByte(hasAol and 1 or 0) - - -- History - local historyAmount = 1 - msg:addByte(historyAmount) -- History log count - for i = 1, historyAmount do - msg:addU32(os.time()) -- timestamp - msg:addByte(0) -- Color message (1 - Red | 0 = White loss) - msg:addString("Blessing Purchased", "Blessings.sendBlessDialog - Blessing Purchased") -- History message + if skulled and Blessings.Config.SkulledDeathLoseStoreItem then + local inbox = player:getStoreInbox() + local toBeDeleted = {} + if inbox and inbox:getSize() > 0 then + for i = 0, inbox:getSize() do + local item = inbox:getItem(i) + if item then + toBeDeleted[#toBeDeleted + 1] = item.uid + end + end + if #toBeDeleted > 0 then + for i, v in pairs(toBeDeleted) do + local item = Item(v) + if item then + item:remove() + end + end + end + end end - - msg:sendToPlayer(player) end +-- Blessing Helpers -- Blessings.getCommandFee = function(cost) local fee = configManager.getNumber(configKeys.BUY_BLESS_COMMAND_FEE) or 0 return cost + cost * (((fee > 100 and 100) or fee) / 100) end -Blessings.getBlessingsCost = function(level, byCommand) +Blessings.getBlessingCost = function(level, byCommand, enhanced) if byCommand == nil then byCommand = false end + + if enhanced == nil then + enhanced = false + end + local cost if level <= 30 then cost = 2000 elseif level >= 120 then - cost = 20000 + local base_cost = enhanced and 26000 or 20000 + local multiplier = enhanced and 100 or 75 + cost = base_cost + multiplier * (level - 120) else - cost = (level - 20) * 200 + local multiplier = enhanced and 260 or 200 + cost = multiplier * (level - 20) end + return byCommand and Blessings.getCommandFee(cost) or cost end @@ -213,6 +173,7 @@ Blessings.getPvpBlessingCost = function(level, byCommand) if byCommand == nil then byCommand = false end + local cost if level <= 30 then cost = 2000 @@ -221,6 +182,7 @@ Blessings.getPvpBlessingCost = function(level, byCommand) else cost = (level - 20) * 200 end + return byCommand and Blessings.getCommandFee(cost) or cost end @@ -250,6 +212,7 @@ Blessings.checkBless = function(player) for k, v in pairs(Blessings.All) do result = player:hasBlessing(k) and result .. "\n" .. v.name or result end + player:sendTextMessage(MESSAGE_EVENT_ADVANCE, 20 > result:len() and "No blessings received." or result) return true end @@ -258,6 +221,7 @@ Blessings.doAdventurerBlessing = function(player) if player:getLevel() > Blessings.Config.AdventurerBlessingLevel then return true end + player:addMissingBless(true, true) player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have adventurer's blessings for being level lower than " .. Blessings.Config.AdventurerBlessingLevel .. "!") @@ -270,70 +234,16 @@ Blessings.getInquisitionPrice = function(player) inquifilter = function(b) return b.inquisition end + donthavefilter = function(p, b) return not p:hasBlessing(b) end + local missing = #player:getBlessings(inquifilter, donthavefilter) - local totalBlessPrice = Blessings.getBlessingsCost(player:getLevel(), false) * missing * Blessings.Config.InquisitonBlessPriceMultiplier + local totalBlessPrice = Blessings.getBlessingCost(player:getLevel(), false) * missing * Blessings.Config.InquisitonBlessPriceMultiplier return missing, totalBlessPrice end -Blessings.DropLoot = function(player, corpse, chance, skulled) - local multiplier = 100 -- Improve the loot randomness spectrum - math.randomseed(os.time()) -- Improve the loot randomness spectrum - chance = chance * multiplier - Blessings.DebugPrint(chance, "DropLoot chance") - for i = CONST_SLOT_HEAD, CONST_SLOT_AMMO do - local item = player:getSlotItem(i) - if item then - local thisChance = chance - local thisRandom = math.random(100 * multiplier) - if not item:isContainer() then - thisChance = chance / 10 - end - Blessings.DebugPrint(thisChance / multiplier .. "%" .. " | thisRandom " .. thisRandom / multiplier .. "%", "DropLoot item " .. item:getName() .. " |") - if skulled or thisRandom <= thisChance then - Blessings.DebugPrint("Dropped " .. item:getName()) - item:moveTo(corpse) - end - end - end - if skulled and Blessings.Config.SkulledDeathLoseStoreItem then - local inbox = player:getStoreInbox() - local toBeDeleted = {} - if inbox and inbox:getSize() > 0 then - for i = 0, inbox:getSize() do - local item = inbox:getItem(i) - if item then - toBeDeleted[#toBeDeleted + 1] = item.uid - end - end - if #toBeDeleted > 0 then - for i, v in pairs(toBeDeleted) do - local item = Item(v) - if item then - item:remove() - end - end - end - end - end -end - -Blessings.ClearBless = function(player, killer, currentBless) - Blessings.DebugPrint(#currentBless, "ClearBless #currentBless") - local hasToF = Blessings.Config.HasToF and player:hasBlessing(1) or false - if hasToF and killer(killer:isPlayer() or (killer:getMaster() and killer:getMaster():isPlayer())) then -- TODO add better check if its pvp or pve - player:removeBlessing(1) - return - end - for i = 1, #currentBless do - Blessings.DebugPrint(i, "ClearBless curBless i", " | " .. currentBless[i].name) - player:removeBlessing(currentBless[i].id, 1) - end -end - --- bought by command Blessings.BuyAllBlesses = function(player) if not Tile(player:getPosition()):hasFlag(TILESTATE_PROTECTIONZONE) and (player:isPzLocked() or player:getCondition(CONDITION_INFIGHT, CONDITIONID_DEFAULT)) then player:sendCancelMessage("You can't buy bless while in battle.") @@ -341,23 +251,26 @@ Blessings.BuyAllBlesses = function(player) return true end - local blessCost = Blessings.getBlessingsCost(player:getLevel(), true) - local PvPBlessCost = Blessings.getPvpBlessingCost(player:getLevel(), true) - local hasToF = Blessings.Config.HasToF and player:hasBlessing(1) or true donthavefilter = function(p, b) return not p:hasBlessing(b) end + + local hasToF = Blessings.Config.HasToF and player:hasBlessing(1) or true local missingBless = player:getBlessings(nil, donthavefilter) local missingBlessAmt = #missingBless + (hasToF and 0 or 1) - local totalCost = blessCost * #missingBless + local totalCost + for i, bless in ipairs(missingBless) do + totalCost = totalCost + Blessings.getBlessingCost(player:getLevel(), true, bless.id >= 7) + end if missingBlessAmt == 0 then player:sendCancelMessage("You are already blessed.") player:getPosition():sendMagicEffect(CONST_ME_POFF) return true end + if not hasToF then - totalCost = totalCost + PvPBlessCost + totalCost = totalCost + Blessings.getPvpBlessingCost(player:getLevel(), true) end if player:removeMoneyBank(totalCost) then @@ -365,9 +278,11 @@ Blessings.BuyAllBlesses = function(player) player = player:getName(), context = "blessings", }) + for i, v in ipairs(missingBless) do player:addBlessing(v.id, 1) end + player:sendCancelMessage("You received the remaining " .. missingBlessAmt .. " blesses for a total of " .. totalCost .. " gold.") player:getPosition():sendMagicEffect(CONST_ME_HOLYAREA) else @@ -378,29 +293,6 @@ Blessings.BuyAllBlesses = function(player) return true end -Blessings.PlayerDeath = function(player, corpse, killer) - local hasToF = Blessings.Config.HasToF and player:hasBlessing(1) or false - local hasAol = (player:getSlotItem(CONST_SLOT_NECKLACE) and player:getSlotItem(CONST_SLOT_NECKLACE):getId() == ITEM_AMULETOFLOSS) - local haveSkull = table.contains({ SKULL_RED, SKULL_BLACK }, player:getSkull()) - local curBless = player:getBlessings() - - if haveSkull then -- lose all bless + drop all items - Blessings.DropLoot(player, corpse, 100, true) - elseif #curBless < 5 and not hasAol then -- lose all items - local equipLoss = Blessings.LossPercent[#curBless].item - Blessings.DropLoot(player, corpse, equipLoss) - elseif #curBless < 5 and hasAol and not hasToF then - player:removeItem(ITEM_AMULETOFLOSS, 1, -1, false) - end - --Blessings.ClearBless(player, killer, curBless) IMPLEMENTED IN SOURCE BECAUSE THIS WAS HAPPENING BEFORE SKILL/EXP CALCULATIONS - - if not player:getSlotItem(CONST_SLOT_BACKPACK) then - player:addItem(ITEM_BAG, 1, false, CONST_SLOT_BACKPACK) - end - - return true -end - function Player.getBlessings(self, filter, hasblessingFilter) local blessings = {} if filter == nil then @@ -414,11 +306,13 @@ function Player.getBlessings(self, filter, hasblessingFilter) return p:hasBlessing(b) end end + for k, v in pairs(Blessings.All) do if filter(v) and hasblessingFilter(self, k) then table.insert(blessings, v) end end + return blessings end @@ -426,11 +320,13 @@ function Player.addMissingBless(self, all, tof) if all == nil then all = true end + if tof == nil then tof = false elseif tof then tof = tof and Blessings.Config.HasToF end + for k, v in pairs(Blessings.All) do if all or (v.type == Blessings.Types.REGULAR) or (tof and v.type == Blessings.Types.PvP) then if not self:hasBlessing(k) then @@ -438,5 +334,5 @@ function Player.addMissingBless(self, all, tof) end end end - Blessings.sendBlessStatus(self) + self:sendBlessStatus() end diff --git a/data/libs/systems/load.lua b/data/libs/systems/load.lua index 281b8b1d466..ffe02320b96 100644 --- a/data/libs/systems/load.lua +++ b/data/libs/systems/load.lua @@ -1,4 +1,5 @@ -- Load systems functions +dofile(CORE_DIRECTORY .. "/libs/systems/blessing.lua") dofile(CORE_DIRECTORY .. "/libs/systems/concoctions.lua") dofile(CORE_DIRECTORY .. "/libs/systems/daily_reward.lua") dofile(CORE_DIRECTORY .. "/libs/systems/encounters.lua") diff --git a/data/modules/modules.xml b/data/modules/modules.xml index c45646183ac..e51bf055f2a 100644 --- a/data/modules/modules.xml +++ b/data/modules/modules.xml @@ -10,9 +10,6 @@ - - - @@ -26,5 +23,4 @@ - diff --git a/data/modules/scripts/blessings/assets.lua b/data/modules/scripts/blessings/assets.lua deleted file mode 100644 index 73d02a272f3..00000000000 --- a/data/modules/scripts/blessings/assets.lua +++ /dev/null @@ -1,47 +0,0 @@ -Blessings.Types = { - REGULAR = 1, - ENHANCED = 2, - PvP = 3, -} - -Blessings.All = { - [1] = { id = 1, name = "Twist of Fate", type = Blessings.Types.PvP }, - [2] = { id = 2, name = "The Wisdom of Solitude", charm = 10345, type = Blessings.Types.REGULAR, losscount = true, inquisition = true }, - [3] = { id = 3, name = "The Spark of the Phoenix", charm = 10341, type = Blessings.Types.REGULAR, losscount = true, inquisition = true }, - [4] = { id = 4, name = "The Fire of the Suns", charm = 10344, type = Blessings.Types.REGULAR, losscount = true, inquisition = true }, - [5] = { id = 5, name = "The Spiritual Shielding", charm = 10343, type = Blessings.Types.REGULAR, losscount = true, inquisition = true }, - [6] = { id = 6, name = "The Embrace of Tibia", charm = 10342, type = Blessings.Types.REGULAR, losscount = true, inquisition = true }, - [7] = { id = 7, name = "Blood of the Mountain", charm = 25360, type = Blessings.Types.ENHANCED, losscount = true, inquisition = false }, - [8] = { id = 8, name = "Heart of the Mountain", charm = 25361, type = Blessings.Types.ENHANCED, losscount = true, inquisition = false }, -} - -Blessings.LossPercent = { - [0] = { item = 100, skill = 0 }, - [1] = { item = 70, skill = 8 }, - [2] = { item = 45, skill = 16 }, - [3] = { item = 25, skill = 24 }, - [4] = { item = 10, skill = 32 }, - [5] = { item = 0, skill = 40 }, - [6] = { item = 0, skill = 48 }, - [7] = { item = 0, skill = 56 }, - [8] = { item = 0, skill = 56 }, -} - -Blessings.BitWiseTable = { - [0] = 1, - [1] = 2, - [2] = 4, - [3] = 8, - [4] = 16, - [5] = 32, - [6] = 64, - [7] = 128, - [8] = 256, - [9] = 512, - [10] = 1024, - [11] = 2048, - [12] = 4096, - [13] = 8192, - [14] = 16384, - [15] = 32768, -} diff --git a/data/npclib/npc.lua b/data/npclib/npc.lua index 4de60c45c49..4a8097ff27f 100644 --- a/data/npclib/npc.lua +++ b/data/npclib/npc.lua @@ -89,7 +89,7 @@ function SayEvent(npcId, playerId, messageDelayed, npcHandler, textType) local parseInfo = { [TAG_PLAYERNAME] = player:getName(), [TAG_TIME] = getFormattedWorldTime(), - [TAG_BLESSCOST] = Blessings.getBlessingsCost(player:getLevel(), false), + [TAG_BLESSCOST] = Blessings.getBlessingCost(player:getLevel(), false, (npc:getName() == "Kais" or npc:getName() == "Nomad") and true), [TAG_PVPBLESSCOST] = Blessings.getPvpBlessingCost(player:getLevel(), false), } npc:say(npcHandler:parseMessage(messageDelayed, parseInfo), textType or TALKTYPE_PRIVATE_NP, false, player, npc:getPosition()) diff --git a/data/npclib/npc_system/modules.lua b/data/npclib/npc_system/modules.lua index aca92804e80..08027a67d2b 100644 --- a/data/npclib/npc_system/modules.lua +++ b/data/npclib/npc_system/modules.lua @@ -60,7 +60,7 @@ if Modules == nil then local parseInfo = { [TAG_PLAYERNAME] = player:getName(), [TAG_TIME] = getFormattedWorldTime(), - [TAG_BLESSCOST] = Blessings.getBlessingsCost(player:getLevel(), false), + [TAG_BLESSCOST] = Blessings.getBlessingCost(player:getLevel(), false, (npc:getName() == "Kais" or npc:getName() == "Nomad") and true), [TAG_PVPBLESSCOST] = Blessings.getPvpBlessingCost(player:getLevel(), false), [TAG_TRAVELCOST] = costMessage, } @@ -160,7 +160,7 @@ if Modules == nil then end local parseInfo = { - [TAG_BLESSCOST] = Blessings.getBlessingsCost(player:getLevel(), false), + [TAG_BLESSCOST] = Blessings.getBlessingCost(player:getLevel(), false, (npc:getName() == "Kais" or npc:getName() == "Nomad") and true), [TAG_PVPBLESSCOST] = Blessings.getPvpBlessingCost(player:getLevel(), false), } if player:hasBlessing(parameters.bless) then @@ -175,7 +175,7 @@ if Modules == nil then npc, player ) - elseif not player:removeMoneyBank(type(parameters.cost) == "string" and npcHandler:parseMessage(parameters.cost, parseInfo) or parameters.cost) then + elseif not player:removeMoneyBank(type(parameters.cost) == "string" and tonumber(npcHandler:parseMessage(parameters.cost, parseInfo)) or parameters.cost) then npcHandler:say("Oh. You do not have enough money.", npc, player) else npcHandler:say(parameters.text or "You have been blessed by one of the seven gods!", npc, player) diff --git a/data/scripts/actions/items/blessing_charms.lua b/data/scripts/actions/items/blessing_charms.lua index 7e70c5a6719..fdca561c3a2 100644 --- a/data/scripts/actions/items/blessing_charms.lua +++ b/data/scripts/actions/items/blessing_charms.lua @@ -1,4 +1,4 @@ -dofile(CORE_DIRECTORY .. "/modules/scripts/blessings/blessings.lua") +dofile(CORE_DIRECTORY .. "/libs/systems/blessing.lua") local blessingCharms = Action() diff --git a/data/scripts/actions/items/check_bless.lua b/data/scripts/actions/items/check_bless.lua index 0d2478f5f11..09d07fffd85 100644 --- a/data/scripts/actions/items/check_bless.lua +++ b/data/scripts/actions/items/check_bless.lua @@ -1,4 +1,4 @@ -dofile(CORE_DIRECTORY .. "/modules/scripts/blessings/blessings.lua") +dofile(CORE_DIRECTORY .. "/libs/systems/blessing.lua") local checkBless = Action() diff --git a/data/scripts/creaturescripts/player/adventure_blessing_login.lua b/data/scripts/creaturescripts/player/adventure_blessing_login.lua index 8ebf88a72e9..79c694d73bc 100644 --- a/data/scripts/creaturescripts/player/adventure_blessing_login.lua +++ b/data/scripts/creaturescripts/player/adventure_blessing_login.lua @@ -1,4 +1,4 @@ -dofile(CORE_DIRECTORY .. "/modules/scripts/blessings/blessings.lua") +dofile(CORE_DIRECTORY .. "/libs/systems/blessing.lua") local adventurerBlessingLogin = CreatureEvent("AdventurerBlessingLogin") diff --git a/data/scripts/systems/item_tiers.lua b/data/scripts/systems/item_tiers.lua index 06a8321a034..cc7a2a2697d 100644 --- a/data/scripts/systems/item_tiers.lua +++ b/data/scripts/systems/item_tiers.lua @@ -90,7 +90,7 @@ local itemTierClassifications = { }, } --- Item tier with gold price for uprading it +-- Item tier with gold price for upgrading it for classificationId, classificationTable in ipairs(itemTierClassifications) do local itemClassification = Game.createItemClassification(classificationId) local classification = {} diff --git a/data/scripts/talkactions/gm/bless_status.lua b/data/scripts/talkactions/gm/bless_status.lua index 7cb861947dd..a316330db31 100644 --- a/data/scripts/talkactions/gm/bless_status.lua +++ b/data/scripts/talkactions/gm/bless_status.lua @@ -1,4 +1,4 @@ -dofile(CORE_DIRECTORY .. "/modules/scripts/blessings/blessings.lua") +dofile(CORE_DIRECTORY .. "/libs/systems/blessing.lua") local blessStatus = TalkAction("/bless") @@ -6,7 +6,7 @@ function blessStatus.onSay(player, words, param) -- create log logCommand(player, words, param) - Blessings.sendBlessStatus(player) + player:sendBlessStatus() return true end diff --git a/src/creatures/players/player.cpp b/src/creatures/players/player.cpp index 4eca148a1e7..d838f8b96a0 100644 --- a/src/creatures/players/player.cpp +++ b/src/creatures/players/player.cpp @@ -3450,7 +3450,6 @@ void Player::death(const std::shared_ptr &lastHitCreature) { g_game().sendSingleSoundEffect(static_self_cast()->getPosition(), sex == PLAYERSEX_FEMALE ? SoundEffect_t::HUMAN_FEMALE_DEATH : SoundEffect_t::HUMAN_MALE_DEATH, getPlayer()); if (skillLoss) { - uint8_t unfairFightReduction = 100; int playerDmg = 0; int othersDmg = 0; uint32_t sumLevels = 0; @@ -3467,10 +3466,13 @@ void Player::death(const std::shared_ptr &lastHitCreature) { } } } + bool pvpDeath = false; if (playerDmg > 0 || othersDmg > 0) { pvpDeath = (Player::lastHitIsPlayer(lastHitCreature) || playerDmg / (playerDmg + static_cast(othersDmg)) >= 0.05); } + + uint8_t unfairFightReduction = 100; if (pvpDeath && sumLevels > level) { double reduce = level / static_cast(sumLevels); unfairFightReduction = std::max(20, std::floor((reduce * 100) + 0.5)); @@ -3480,7 +3482,7 @@ void Player::death(const std::shared_ptr &lastHitCreature) { uint64_t sumMana = 0; uint64_t lostMana = 0; - // sum up all the mana + // Sum up all the mana for (uint32_t i = 1; i <= magLevel; ++i) { sumMana += vocation->getReqMana(i); } @@ -3490,12 +3492,10 @@ void Player::death(const std::shared_ptr &lastHitCreature) { double deathLossPercent = getLostPercent() * (unfairFightReduction / 100.); // Charm bless bestiary - if (lastHitCreature && lastHitCreature->getMonster()) { - if (charmRuneBless != 0) { - const auto mType = g_monsters().getMonsterType(lastHitCreature->getName()); - if (mType && mType->info.raceid == charmRuneBless) { - deathLossPercent = (deathLossPercent * 90) / 100; - } + if (lastHitCreature && lastHitCreature->getMonster() && charmRuneBless != 0) { + const auto &mType = g_monsters().getMonsterType(lastHitCreature->getName()); + if (mType && mType->info.raceid == charmRuneBless) { + deathLossPercent = (deathLossPercent * 90) / 100; } } @@ -3517,7 +3517,9 @@ void Player::death(const std::shared_ptr &lastHitCreature) { } // Level loss - auto expLoss = static_cast(experience * deathLossPercent); + auto expLoss = static_cast(std::ceil((experience * deathLossPercent) / 100.)); + g_logger().debug("[{}] - experience lost {}", __FUNCTION__, expLoss); + g_events().eventPlayerOnLoseExperience(static_self_cast(), expLoss); g_callbacks().executeCallback(EventCallback_t::playerOnLoseExperience, &EventCallback::playerOnLoseExperience, getPlayer(), expLoss); @@ -3526,9 +3528,9 @@ void Player::death(const std::shared_ptr &lastHitCreature) { lostExp << "You lost " << expLoss << " experience."; // Skill loss - for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { // for each skill + for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { // For each skill uint64_t sumSkillTries = 0; - for (uint16_t c = 11; c <= skills[i].level; ++c) { // sum up all required tries for all skill levels + for (uint16_t c = 11; c <= skills[i].level; ++c) { // Sum up all required tries for all skill levels sumSkillTries += vocation->getReqSkillTries(i, c); } @@ -3604,14 +3606,21 @@ void Player::death(const std::shared_ptr &lastHitCreature) { bless.empty() ? blessOutput << "You weren't protected with any blessings." : blessOutput << "You were blessed with " << bless; - // Make player lose bless + const auto playerSkull = getSkull(); + bool hasSkull = (playerSkull == Skulls_t::SKULL_RED || playerSkull == Skulls_t::SKULL_BLACK); uint8_t maxBlessing = 8; - if (pvpDeath && hasBlessing(1)) { + if (!hasSkull && pvpDeath && hasBlessing(1)) { removeBlessing(1, 1); // Remove TOF only } else { for (int i = 2; i <= maxBlessing; i++) { removeBlessing(i, 1); } + + const auto &playerAmulet = getThing(CONST_SLOT_NECKLACE); + bool usingAol = (playerAmulet && playerAmulet->getItem()->getID() == ITEM_AMULETOFLOSS); + if (usingAol) { + removeItemOfType(ITEM_AMULETOFLOSS, 1, -1); + } } } sendTextMessage(MESSAGE_EVENT_ADVANCE, blessOutput.str()); @@ -6293,21 +6302,30 @@ double Player::getLostPercent() const { return std::max(0, deathLosePercent) / 100.; } + bool isRetro = g_configManager().getBoolean(TOGGLE_SERVER_IS_RETRO); + const auto factor = (isRetro ? 6.31 : 8); + double percentReduction = (blessingCount * factor) / 100.; + double lossPercent; if (level >= 24) { const double tmpLevel = level + (levelPercent / 100.); lossPercent = ((tmpLevel + 50) * 50 * ((tmpLevel * tmpLevel) - (5 * tmpLevel) + 8)) / experience; } else { - lossPercent = 5; + percentReduction = (percentReduction >= 0.40 ? 0.50 : percentReduction); + lossPercent = 10; } - double percentReduction = 0; + g_logger().debug("[{}] - lossPercent {}", __FUNCTION__, lossPercent); + g_logger().debug("[{}] - before promotion {}", __FUNCTION__, percentReduction); + if (isPromoted()) { - percentReduction += 30; + percentReduction += 30 / 100.; + g_logger().debug("[{}] - after promotion {}", __FUNCTION__, percentReduction); } - percentReduction += blessingCount * 8; - return lossPercent * (1 - (percentReduction / 100.)) / 100.; + g_logger().debug("[{}] - total lost percent {}", __FUNCTION__, lossPercent - (lossPercent * percentReduction)); + + return lossPercent - (lossPercent * percentReduction); } [[nodiscard]] const std::string &Player::getGuildNick() const { diff --git a/src/enums/player_blessings.hpp b/src/enums/player_blessings.hpp index 086f04a6442..f71396f71b3 100644 --- a/src/enums/player_blessings.hpp +++ b/src/enums/player_blessings.hpp @@ -20,6 +20,6 @@ enum class Blessings : uint8_t { TheFireOfTheSuns = 4, TheSpiritualShielding = 5, TheEmbraceOfTibia = 6, - BloodOfTheMountain = 7, - HearthOfTheMountain = 8 + HearthOfTheMountain = 7, + BloodOfTheMountain = 8 }; diff --git a/src/lua/functions/creatures/player/player_functions.cpp b/src/lua/functions/creatures/player/player_functions.cpp index 34c665f2fe3..08045f1de00 100644 --- a/src/lua/functions/creatures/player/player_functions.cpp +++ b/src/lua/functions/creatures/player/player_functions.cpp @@ -249,6 +249,7 @@ void PlayerFunctions::init(lua_State* L) { Lua::registerMethod(L, "Player", "addTransferableCoins", PlayerFunctions::luaPlayerAddTransferableCoins); Lua::registerMethod(L, "Player", "removeTransferableCoins", PlayerFunctions::luaPlayerRemoveTransferableCoins); + Lua::registerMethod(L, "Player", "sendBlessStatus", PlayerFunctions::luaPlayerSendBlessStatus); Lua::registerMethod(L, "Player", "hasBlessing", PlayerFunctions::luaPlayerHasBlessing); Lua::registerMethod(L, "Player", "addBlessing", PlayerFunctions::luaPlayerAddBlessing); Lua::registerMethod(L, "Player", "removeBlessing", PlayerFunctions::luaPlayerRemoveBlessing); @@ -3075,6 +3076,20 @@ int PlayerFunctions::luaPlayerRemoveTransferableCoins(lua_State* L) { return 1; } +int PlayerFunctions::luaPlayerSendBlessStatus(lua_State* L) { + // player:sendBlessStatus() + const auto &player = Lua::getUserdataShared(L, 1); + if (!player) { + Lua::reportErrorFunc(Lua::getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); + Lua::pushBoolean(L, false); + return 0; + } + + player->sendBlessStatus(); + Lua::pushBoolean(L, true); + return 1; +} + int PlayerFunctions::luaPlayerHasBlessing(lua_State* L) { // player:hasBlessing(blessing) const uint8_t blessing = Lua::getNumber(L, 2); diff --git a/src/lua/functions/creatures/player/player_functions.hpp b/src/lua/functions/creatures/player/player_functions.hpp index 8ecb42b83aa..ab617c35132 100644 --- a/src/lua/functions/creatures/player/player_functions.hpp +++ b/src/lua/functions/creatures/player/player_functions.hpp @@ -229,6 +229,7 @@ class PlayerFunctions { static int luaPlayerAddTransferableCoins(lua_State* L); static int luaPlayerRemoveTransferableCoins(lua_State* L); + static int luaPlayerSendBlessStatus(lua_State* L); static int luaPlayerHasBlessing(lua_State* L); static int luaPlayerAddBlessing(lua_State* L); static int luaPlayerRemoveBlessing(lua_State* L); diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 466a2413353..fead3665db3 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -64,16 +64,6 @@ // This "getIteration" function will allow us to get the total number of iterations that run within a specific map // Very useful to send the total amount in certain bytes in the ProtocolGame class namespace { - template - uint16_t getIterationIncreaseCount(T &map) { - uint16_t totalIterationCount = 0; - for ([[maybe_unused]] const auto &[first, second] : map) { - totalIterationCount++; - } - - return totalIterationCount; - } - template uint16_t getVectorIterationIncreaseCount(T &vector) { uint16_t totalIterationCount = 0; @@ -1013,24 +1003,24 @@ void ProtocolGame::parsePacketFromDispatcher(NetworkMessage &msg, uint8_t recvby case 0x1E: g_game().playerReceivePing(player->getID()); break; - case 0x2a: + case 0x28: + parseStashWithdraw(msg); + break; + case 0x29: + parseRetrieveDepotSearch(msg); + break; + case 0x2A: parseCyclopediaMonsterTracker(msg); break; case 0x2B: parsePartyAnalyzerAction(msg); break; - case 0x2c: + case 0x2C: parseLeaderFinderWindow(msg); break; - case 0x2d: + case 0x2D: parseMemberFinderWindow(msg); break; - case 0x28: - parseStashWithdraw(msg); - break; - case 0x29: - parseRetrieveDepotSearch(msg); - break; case 0x32: parseExtendedOpcode(msg); break; // otclient extended opcode @@ -1275,6 +1265,9 @@ void ProtocolGame::parsePacketFromDispatcher(NetworkMessage &msg, uint8_t recvby case 0xCD: parseInspectionObject(msg); break; + case 0xCF: + sendBlessingWindow(); + break; case 0xD2: g_game().playerRequestOutfit(player->getID()); break; @@ -2314,7 +2307,7 @@ void ProtocolGame::parseBestiarysendRaces() { } NetworkMessage msg; - msg.addByte(0xd5); + msg.addByte(0xD5); msg.add(BESTY_RACE_LAST); std::map mtype_list = g_game().getBestiaryList(); for (uint8_t i = BESTY_RACE_FIRST; i <= BESTY_RACE_LAST; i++) { @@ -2346,7 +2339,7 @@ void ProtocolGame::sendBestiaryEntryChanged(uint16_t raceid) { } NetworkMessage msg; - msg.addByte(0xd9); + msg.addByte(0xD9); msg.add(raceid); writeToOutputBuffer(msg); } @@ -2380,7 +2373,7 @@ void ProtocolGame::parseBestiarysendMonsterData(NetworkMessage &msg) { uint8_t currentLevel = g_iobestiary().getKillStatus(mtype, killCounter); NetworkMessage newmsg; - newmsg.addByte(0xd7); + newmsg.addByte(0xD7); newmsg.add(raceId); newmsg.addString(Class); @@ -2442,7 +2435,7 @@ void ProtocolGame::parseBestiarysendMonsterData(NetworkMessage &msg) { } newmsg.addByte(attackmode); - newmsg.addByte(0x2); + newmsg.addByte(0x02); newmsg.add(mtype->info.healthMax); newmsg.add(mtype->info.experience); newmsg.add(mtype->getBaseSpeed()); @@ -2914,7 +2907,7 @@ void ProtocolGame::BestiarysendCharms() { removeRuneCost = (removeRuneCost * 75) / 100; } NetworkMessage msg; - msg.addByte(0xd8); + msg.addByte(0xD8); msg.add(player->getCharmPoints()); const auto charmList = g_game().getCharmList(); @@ -2993,7 +2986,7 @@ void ProtocolGame::parseBestiarysendCreatures(NetworkMessage &msg) { text = raceName; } NetworkMessage newmsg; - newmsg.addByte(0xd6); + newmsg.addByte(0xD6); newmsg.addString(text); newmsg.add(race.size()); std::map creaturesKilled = g_iobestiary().getBestiaryKillCountByMonsterIDs(player, race); @@ -4345,28 +4338,96 @@ void ProtocolGame::sendBasicData() { writeToOutputBuffer(msg); } -void ProtocolGame::sendBlessStatus() { +void ProtocolGame::sendBlessingWindow() { if (!player) { return; } NetworkMessage msg; - // uint8_t maxClientBlessings = (player->operatingSystem == CLIENTOS_NEW_WINDOWS) ? 8 : 6; (compartability for the client 10) - // Ignore ToF (bless 1) + msg.addByte(0x9B); + + bool isRetro = g_configManager().getBoolean(TOGGLE_SERVER_IS_RETRO); + + msg.addByte(isRetro ? 0x07 : 0x08); + for (auto blessing : magic_enum::enum_values()) { + if (isRetro && blessing == Blessings::TwistOfFate) { + continue; + } + + const auto blessingValue = enumToValue(blessing); + const auto blessingId = 1 << blessingValue; + msg.add(blessingId); + msg.addByte(player->getBlessingCount(blessingValue)); + msg.addByte(player->getBlessingCount(blessingValue, true)); + } + + // Start at "The Wisdom Of Solitude" uint8_t blessCount = 0; - uint16_t flag = 0; - uint16_t pow2 = 2; - for (int i = 1; i <= 8; i++) { - if (player->hasBlessing(i)) { - if (i > 1) { - blessCount++; - } + for (auto bless : magic_enum::enum_values()) { + if (bless == Blessings::TwistOfFate) { + continue; + } - flag |= pow2; + if (player->hasBlessing(enumToValue(bless))) { + blessCount++; + } + } + + const auto isPromoted = player->isPromoted(); + const auto factor = (isRetro ? 6.31 : 8); + const auto skillReduction = factor * blessCount; + const auto promotionReduction = (isPromoted ? 30 : 0); + const auto minReduction = skillReduction + promotionReduction; + const auto maxLossPvpDeath = calculateMaxPvpReduction(blessCount, isPromoted); + + msg.addByte(isPromoted); + msg.addByte(30); // Reduction bonus with promotion + msg.addByte(minReduction); + msg.addByte(isRetro ? minReduction : maxLossPvpDeath); + msg.addByte(minReduction); + + const auto playerSkull = player->getSkull(); + const auto &playerAmulet = player->getThing(CONST_SLOT_NECKLACE); + bool hasSkull = (playerSkull == Skulls_t::SKULL_RED || playerSkull == Skulls_t::SKULL_BLACK); + bool usingAol = (playerAmulet && playerAmulet->getItem()->getID() == ITEM_AMULETOFLOSS); + if (hasSkull) { + msg.addByte(100); + msg.addByte(100); + } else if (usingAol) { + msg.addByte(0); + msg.addByte(0); + } else { + msg.addByte(calculateEquipmentLoss(blessCount, true)); + msg.addByte(calculateEquipmentLoss(blessCount, true)); + } + + msg.addByte(hasSkull); + msg.addByte(usingAol); + + msg.addByte(0x00); + + writeToOutputBuffer(msg); +} + +void ProtocolGame::sendBlessStatus() { + if (!player) { + return; + } + + // Ignore Twist of Fate (Id 1) + uint8_t blessCount = 0; + for (auto bless : magic_enum::enum_values()) { + if (bless == Blessings::TwistOfFate) { + continue; + } + if (player->hasBlessing(enumToValue(bless))) { + blessCount++; } } + NetworkMessage msg; msg.addByte(0x9C); + if (oldProtocol) { msg.add(blessCount >= 5 ? 0x01 : 0x00); } else { @@ -4379,16 +4440,20 @@ void ProtocolGame::sendBlessStatus() { } void ProtocolGame::sendPremiumTrigger() { - if (!g_configManager().getBoolean(FREE_PREMIUM) && !g_configManager().getBoolean(VIP_SYSTEM_ENABLED)) { - NetworkMessage msg; - msg.addByte(0x9E); - msg.addByte(16); - for (uint16_t i = 0; i <= 15; i++) { - // PREMIUM_TRIGGER_TRAIN_OFFLINE = false, PREMIUM_TRIGGER_XP_BOOST = false, PREMIUM_TRIGGER_MARKET = false, PREMIUM_TRIGGER_VIP_LIST = false, PREMIUM_TRIGGER_DEPOT_SPACE = false, PREMIUM_TRIGGER_INVITE_PRIVCHAT = false - msg.addByte(0x01); - } - writeToOutputBuffer(msg); + if (g_configManager().getBoolean(FREE_PREMIUM) || g_configManager().getBoolean(VIP_SYSTEM_ENABLED)) { + return; } + + NetworkMessage msg; + msg.addByte(0x9E); + + msg.addByte(16); + for (uint16_t i = 0; i <= 15; i++) { + // PREMIUM_TRIGGER_TRAIN_OFFLINE = false, PREMIUM_TRIGGER_XP_BOOST = false, PREMIUM_TRIGGER_MARKET = false, PREMIUM_TRIGGER_VIP_LIST = false, PREMIUM_TRIGGER_DEPOT_SPACE = false, PREMIUM_TRIGGER_INVITE_PRIVCHAT = false + msg.addByte(0x01); + } + + writeToOutputBuffer(msg); } void ProtocolGame::sendTextMessage(const TextMessage &message) { @@ -5481,8 +5546,8 @@ void ProtocolGame::sendOpenForge() { msg.add(convergenceFusionCount); msg.setBufferPosition(transferTotalCountPosition); - auto transferTotalCount = getIterationIncreaseCount(donorTierItemMap); - msg.addByte(static_cast(transferTotalCount)); + auto transferTotalCount = donorTierItemMap.size(); + msg.addByte(transferTotalCount); if (transferTotalCount > 0) { for (const auto &[itemId, tierAndCountMap] : donorTierItemMap) { // Let's access the itemType to check the item's (donator of tier) classification level @@ -5494,7 +5559,7 @@ void ProtocolGame::sendOpenForge() { } // Total count of item (donator of tier) - auto donorTierTotalItemsCount = getIterationIncreaseCount(tierAndCountMap); + auto donorTierTotalItemsCount = tierAndCountMap.size(); msg.add(donorTierTotalItemsCount); for (const auto &[donorItemTier, donorItemCount] : tierAndCountMap) { msg.add(itemId); @@ -5679,8 +5744,7 @@ void ProtocolGame::sendForgeResult(ForgeAction_t actionType, uint16_t leftItemId void ProtocolGame::sendForgeHistory(uint8_t page) { page = page + 1; auto historyVector = player->getForgeHistory(); - auto historyVectorLen = getVectorIterationIncreaseCount(historyVector); - + auto historyVectorLen = historyVector.size(); uint16_t lastPage = (1 < std::floor((historyVectorLen - 1) / 9) + 1) ? static_cast(std::floor((historyVectorLen - 1) / 9) + 1) : 1; uint16_t currentPage = (lastPage < page) ? lastPage : page; @@ -5691,8 +5755,7 @@ void ProtocolGame::sendForgeHistory(uint8_t page) { historyPerPage.emplace_back(historyVector[entry - 1]); } - auto historyPageToSend = getVectorIterationIncreaseCount(historyPerPage); - + auto historyPageToSend = historyPerPage.size(); NetworkMessage msg; msg.addByte(0x88); msg.add(currentPage - 1); // Current page diff --git a/src/server/network/protocol/protocolgame.hpp b/src/server/network/protocol/protocolgame.hpp index 27fa6c8716c..2048bea7c16 100644 --- a/src/server/network/protocol/protocolgame.hpp +++ b/src/server/network/protocol/protocolgame.hpp @@ -466,7 +466,10 @@ class ProtocolGame final : public Protocol { void AddPlayerStats(NetworkMessage &msg); void AddOutfit(NetworkMessage &msg, const Outfit_t &outfit, bool addMount = true); void AddPlayerSkills(NetworkMessage &msg); + // Blessing + void sendBlessingWindow(); void sendBlessStatus(); + // End Blessing void sendPremiumTrigger(); void sendMessageDialog(const std::string &message); void AddWorldLight(NetworkMessage &msg, LightInfo lightInfo); diff --git a/src/utils/tools.cpp b/src/utils/tools.cpp index 09a18b37439..57c9c873282 100644 --- a/src/utils/tools.cpp +++ b/src/utils/tools.cpp @@ -2085,3 +2085,44 @@ const std::map &getMaxValuePerSkill() { return maxValuePerSkill; } + +float calculateEquipmentLoss(uint8_t blessingAmount, bool isContainer /* = false*/) { + float lossPercent = 0; + switch (blessingAmount) { + case 0: + lossPercent = 10; + break; + case 1: + lossPercent = 7; + break; + case 2: + lossPercent = 4.5; + break; + case 3: + lossPercent = 2.5; + break; + case 4: + lossPercent = 1; + break; + default: + // Blessing Amount >= 5 + lossPercent = 0; + break; + } + + return isContainer ? lossPercent * 10 : lossPercent; +} + +uint8_t calculateMaxPvpReduction(uint8_t blessCount, bool isPromoted /* = false*/) { + uint8_t result = 80 + (2 * blessCount) - (blessCount / 3); + + if (blessCount == 5) { + result -= 1; + } + + if (isPromoted) { + result += 6; + } + + return result; +} diff --git a/src/utils/tools.hpp b/src/utils/tools.hpp index 9f7f15dfb5d..64fd2e8c1e6 100644 --- a/src/utils/tools.hpp +++ b/src/utils/tools.hpp @@ -212,3 +212,6 @@ bool caseInsensitiveCompare(std::string_view str1, std::string_view str2, size_t void printStackTrace(); const std::map &getMaxValuePerSkill(); + +float calculateEquipmentLoss(uint8_t blessingAmount, bool isContainer = false); +uint8_t calculateMaxPvpReduction(uint8_t blessCount, bool isPromoted = false); From 1e751154154329b5acd2e60a1bcb56b8bd315a6d Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Wed, 13 Nov 2024 00:16:04 -0300 Subject: [PATCH 11/12] fix: teleport effect from "onUse" actions (#3109) Fixed teleport effect from "onUse" actions and passed some pointers to const ref --- data-canary/scripts/actions/other/ladder_up.lua | 2 +- .../quests/the_hidden_city_of_beregar/actions_ladder.lua | 2 +- src/creatures/combat/combat.cpp | 2 +- src/game/game.cpp | 8 ++++---- src/server/network/protocol/protocolgame.cpp | 6 +++--- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/data-canary/scripts/actions/other/ladder_up.lua b/data-canary/scripts/actions/other/ladder_up.lua index efea920c9b8..80d811fa741 100644 --- a/data-canary/scripts/actions/other/ladder_up.lua +++ b/data-canary/scripts/actions/other/ladder_up.lua @@ -14,7 +14,7 @@ function ladder.onUse(player, item, fromPosition, target, toPosition, isHotkey) return true end - player:teleportTo(fromPosition, false) + player:teleportTo(fromPosition, true) return true end diff --git a/data-otservbr-global/scripts/quests/the_hidden_city_of_beregar/actions_ladder.lua b/data-otservbr-global/scripts/quests/the_hidden_city_of_beregar/actions_ladder.lua index 5260bc4469a..83074b595e3 100644 --- a/data-otservbr-global/scripts/quests/the_hidden_city_of_beregar/actions_ladder.lua +++ b/data-otservbr-global/scripts/quests/the_hidden_city_of_beregar/actions_ladder.lua @@ -1,7 +1,7 @@ local dwarvenLadder = Action() function dwarvenLadder.onUse(player, item, fromPosition, itemEx, toPosition) if player:getStorageValue(Storage.DwarvenLegs) < 1 then - player:teleportTo({ x = 32681, y = 31507, z = 10 }) + player:teleportTo({ x = 32681, y = 31507, z = 10 }, true) player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) return true else diff --git a/src/creatures/combat/combat.cpp b/src/creatures/combat/combat.cpp index 823d94ab519..34a28398e9e 100644 --- a/src/creatures/combat/combat.cpp +++ b/src/creatures/combat/combat.cpp @@ -1089,7 +1089,7 @@ bool Combat::doCombatChain(const std::shared_ptr &caster, const std::s auto delay = i * std::max(50, g_configManager().getNumber(COMBAT_CHAIN_DELAY)); ++i; for (const auto &to : toVector) { - auto nextTarget = g_game().getCreatureByID(to); + const auto &nextTarget = g_game().getCreatureByID(to); if (!nextTarget) { continue; } diff --git a/src/game/game.cpp b/src/game/game.cpp index 1ec06abd02b..77a277c69ad 100644 --- a/src/game/game.cpp +++ b/src/game/game.cpp @@ -1419,7 +1419,7 @@ void Game::playerMoveCreatureByID(uint32_t playerId, uint32_t movingCreatureId, return; } - std::shared_ptr movingCreature = getCreatureByID(movingCreatureId); + const auto &movingCreature = getCreatureByID(movingCreatureId); if (!movingCreature) { return; } @@ -5455,7 +5455,7 @@ void Game::playerLookInBattleList(uint32_t playerId, uint32_t creatureId) { return; } - std::shared_ptr creature = getCreatureByID(creatureId); + const auto &creature = getCreatureByID(creatureId); if (!creature) { return; } @@ -5867,7 +5867,7 @@ void Game::playerSetAttackedCreature(uint32_t playerId, uint32_t creatureId) { return; } - std::shared_ptr attackCreature = getCreatureByID(creatureId); + const auto &attackCreature = getCreatureByID(creatureId); if (!attackCreature) { player->setAttackedCreature(nullptr); player->sendCancelTarget(); @@ -9428,7 +9428,7 @@ void Game::parsePlayerExtendedOpcode(uint32_t playerId, uint8_t opcode, const st } void Game::forceRemoveCondition(uint32_t creatureId, ConditionType_t conditionType, ConditionId_t conditionId) { - std::shared_ptr creature = getCreatureByID(creatureId); + const auto &creature = getCreatureByID(creatureId); if (!creature) { return; } diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index fead3665db3..727c1cc0097 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -1515,9 +1515,9 @@ void ProtocolGame::checkCreatureAsKnown(uint32_t id, bool &known, uint32_t &remo continue; } // We need to protect party players from removing - std::shared_ptr creature = g_game().getCreatureByID(*it); - if (std::shared_ptr checkPlayer; - creature && (checkPlayer = creature->getPlayer()) != nullptr) { + const auto &creature = g_game().getCreatureByID(*it); + const auto &checkPlayer = creature ? creature->getPlayer() : nullptr; + if (checkPlayer) { if (player->getParty() != checkPlayer->getParty() && !canSee(creature)) { removedKnown = *it; knownCreatureSet.erase(it); From 6239786d7efc5e64902da3024916b45c9bda4906 Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Wed, 13 Nov 2024 00:16:57 -0300 Subject: [PATCH 12/12] fix: transfer house crash (#3104) This addresses an issue in the 'House::resetTransferItem function where the 'transferItem' pointer was being set to 'nullptr' before being used later in the function, leading to potential undefined behavior or crashes. The solution involves reordering the operations to ensure that 'transferItem' is only set to 'nullptr' after all required operations are completed. This change ensures that the object remains accessible until all necessary manipulations are performed. --- src/items/functions/item/item_parse.cpp | 8 +------- src/map/house/house.cpp | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/items/functions/item/item_parse.cpp b/src/items/functions/item/item_parse.cpp index 49f23609807..357e0c03e8b 100644 --- a/src/items/functions/item/item_parse.cpp +++ b/src/items/functions/item/item_parse.cpp @@ -676,17 +676,11 @@ void ItemParse::parseFieldCombatDamage(const std::shared_ptr &c void ItemParse::parseField(const std::string &stringValue, pugi::xml_node attributeNode, pugi::xml_attribute valueAttribute, ItemType &itemType) { if (stringValue == "field") { CombatType_t combatType = COMBAT_NONE; - std::shared_ptr conditionDamage = nullptr; - // Parse fields conditions (fire/energy/poison/drown/physical) combatType = parseFieldCombatType(valueAttribute); auto [conditionId, conditionType] = parseFieldConditions(valueAttribute); - if (combatType != COMBAT_NONE) { - if (conditionDamage) { - } - - conditionDamage = std::make_shared(conditionId, conditionType); + auto conditionDamage = std::make_shared(conditionId, conditionType); itemType.combatType = combatType; itemType.conditionDamage = conditionDamage; diff --git a/src/map/house/house.cpp b/src/map/house/house.cpp index f519e9a46c8..4d6cd59df3b 100644 --- a/src/map/house/house.cpp +++ b/src/map/house/house.cpp @@ -472,7 +472,7 @@ std::shared_ptr House::getTransferItem() { void House::resetTransferItem() { if (transferItem) { - const auto &tmpItem = transferItem; + auto tmpItem = transferItem; transferItem = nullptr; transfer_container->resetParent(); transfer_container->removeThing(tmpItem, tmpItem->getItemCount());