diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 090d1a29363..5bb6fc6f74a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -618,6 +618,30 @@ local quests = tpz.quest.id.sandoria ``` insert into table_name ``` +#### Commenting in SQL +Our SQL tables are big and confusing, and they are also modified by hand. It can be very helpful to leave _short_ comments on your additions and modifications to highlight what they are. + +**Example** + +Without a comment, this entry is not easily human-readable: + +```sql +INSERT INTO `mob_droplist` VALUES (504,0,0,1000,888,340); +``` + +So we instead store it as: + +```sql +INSERT INTO `mob_droplist` VALUES (504,0,0,1000,888,340); -- (Colossal Calamari) seashell +``` + +Conversely, `Combo` weaponskill doesn't need any additional comments because it has a name field: + +```sql +INSERT INTO `weapon_skills` VALUES (1,'combo',0x02020000000200000000000002000000000202000000,1,5,0,16,2000,5,1,8,0,0,0,0); +``` + +The format of the comment isn't massively important, but it is preferred not to use ';' as a seperator in the middle of your comment. This is a little confusing, as it's the statement-terminator in SQL. ## SQL Migrations for Schema changes diff --git a/scripts/globals/abilities/assassins_charge.lua b/scripts/globals/abilities/assassins_charge.lua index 2210d8aa4aa..4a7602a5ea9 100644 --- a/scripts/globals/abilities/assassins_charge.lua +++ b/scripts/globals/abilities/assassins_charge.lua @@ -14,5 +14,10 @@ end function onUseAbility(player, target, ability) local merits = player:getMerit(tpz.merit.ASSASSINS_CHARGE) - player:addStatusEffect(tpz.effect.ASSASSINS_CHARGE, merits - 5, 0, 60, player:getMod(tpz.mod.AUGMENTS_ASSASSINS_CHARGE), merits / 5) + local crit = 0 + if player:getMod(tpz.mod.AUGMENTS_ASSASSINS_CHARGE) > 0 then + crit = merits / 5 + end + + player:addStatusEffect(tpz.effect.ASSASSINS_CHARGE, merits - 5, 0, 60, 0, crit) end diff --git a/scripts/globals/abilities/devotion.lua b/scripts/globals/abilities/devotion.lua index 30e04f3753d..b00e2464dc0 100644 --- a/scripts/globals/abilities/devotion.lua +++ b/scripts/globals/abilities/devotion.lua @@ -41,7 +41,7 @@ function onUseAbility(player, target, ability) -- printf("Devotion MP Healed: %d", healMP) damageHP = utils.stoneskin(player, damageHP) - player:takeDamage(damageHP) + player:delHP(damageHP) target:addMP(healMP) return healMP diff --git a/scripts/globals/abilities/martyr.lua b/scripts/globals/abilities/martyr.lua index d00703649b9..e15e7e9ee27 100644 --- a/scripts/globals/abilities/martyr.lua +++ b/scripts/globals/abilities/martyr.lua @@ -44,7 +44,7 @@ function onUseAbility(player, target, ability) -- printf("Martyr Healed HP: %d", healHP) damageHP = utils.stoneskin(player, damageHP) - player:takeDamage(damageHP) + player:delHP(damageHP) target:addHP(healHP) return healHP diff --git a/scripts/globals/effects/assassins_charge.lua b/scripts/globals/effects/assassins_charge.lua index 65aa5173cda..3c0508adfdd 100644 --- a/scripts/globals/effects/assassins_charge.lua +++ b/scripts/globals/effects/assassins_charge.lua @@ -9,7 +9,7 @@ require("scripts/globals/status") function onEffectGain(target, effect) target:addMod(tpz.mod.QUAD_ATTACK, effect:getPower()) target:addMod(tpz.mod.TRIPLE_ATTACK, 100) - if (effect:getSubType() > 0) then + if (effect:getSubPower() > 0) then target:addMod(tpz.mod.CRITHITRATE, effect:getSubPower()) end end diff --git a/scripts/globals/keyitems.lua b/scripts/globals/keyitems.lua index e6bb5871abd..eb3c497aa46 100644 --- a/scripts/globals/keyitems.lua +++ b/scripts/globals/keyitems.lua @@ -701,7 +701,7 @@ tpz.keyItem = MARBLE_BRIDGE_COASTER = 705, LAMP_LIGHTERS_MEMBERSHIP_CARD = 706, SHAFT_GATE_OPERATING_DIAL = 707, - MYSTERIOUS_AMULET = 708, + MYSTERIOUS_AMULET_PRISHE = 708, MIRE_INCENSE = 709, BRAND_OF_DAWN = 710, BRAND_OF_TWILIGHT = 711, diff --git a/scripts/globals/weaponskills.lua b/scripts/globals/weaponskills.lua index cb8c6fd55ce..fb4a74c06d0 100644 --- a/scripts/globals/weaponskills.lua +++ b/scripts/globals/weaponskills.lua @@ -497,7 +497,7 @@ function souleaterBonus(attacker, numhits) end hitscounted = hitscounted + 1 end - attacker:takeDamage(numhits*0.10*attacker:getHP()) + attacker:delHP(numhits*0.10*attacker:getHP()) return damage else return 0 diff --git a/scripts/zones/AlTaieu/IDs.lua b/scripts/zones/AlTaieu/IDs.lua index e4b5e1df0c6..7f323b6f7df 100644 --- a/scripts/zones/AlTaieu/IDs.lua +++ b/scripts/zones/AlTaieu/IDs.lua @@ -19,6 +19,9 @@ zones[tpz.zone.ALTAIEU] = QUASILUMIN_01 = 7365, -- This is Al'Taieu. The celestial capital overflowing with the blessings of Altana. NOTHING_OF_INTEREST = 7475, -- There is nothing of interest here. OMINOUS_SHADOW = 7476, -- An ominous shadow falls over you... + AMULET_SHATTERED = 7497, -- The mysterious amulet held by has shattered... + LIGHT_STOLEN = 7498, -- The light of was stolen by Nag'molada... + RETURN_AMULET_TO_PRISHE = 7523, -- You return the mysterious amulet to Prishe. HOMEPOINT_SET = 7564, -- Home point set! }, mob = diff --git a/scripts/zones/AlTaieu/Zone.lua b/scripts/zones/AlTaieu/Zone.lua index 0ac64b13327..b966213bf19 100644 --- a/scripts/zones/AlTaieu/Zone.lua +++ b/scripts/zones/AlTaieu/Zone.lua @@ -38,11 +38,21 @@ end function onEventFinish(player, csid, option) if (csid == 1) then + local copCraigLights = -- Nag'molada steals one random light + { + tpz.ki.LIGHT_OF_HOLLA, + tpz.ki.LIGHT_OF_DEM, + tpz.ki.LIGHT_OF_MEA + } player:setCharVar("PromathiaStatus", 1) player:addKeyItem(tpz.ki.LIGHT_OF_ALTAIEU) + player:messageSpecial(ID.text.AMULET_SHATTERED, tpz.ki.MYSTERIOUS_AMULET) + player:messageSpecial(ID.text.LIGHT_STOLEN, copCraigLights[math.random(#copCraigLights)]) player:messageSpecial(ID.text.KEYITEM_OBTAINED, tpz.ki.LIGHT_OF_ALTAIEU) player:addTitle(tpz.title.SEEKER_OF_THE_LIGHT) elseif (csid == 167) then player:setCharVar("PromathiaStatus", 1) + player:delKeyItem(tpz.ki.MYSTERIOUS_AMULET_PRISHE) + player:messageSpecial(ID.text.RETURN_AMULET_TO_PRISHE, tpz.ki.MYSTERIOUS_AMULET) end end diff --git a/scripts/zones/Carpenters_Landing/npcs/Anguenet.lua b/scripts/zones/Carpenters_Landing/npcs/Anguenet.lua index 64061796514..e25d9776d39 100644 --- a/scripts/zones/Carpenters_Landing/npcs/Anguenet.lua +++ b/scripts/zones/Carpenters_Landing/npcs/Anguenet.lua @@ -4,16 +4,45 @@ -- Type: Adventurer's Assistant -- !pos 214.672 -3.013 -527.561 2 ----------------------------------- +local ID = require("scripts/zones/Carpenters_Landing/IDs") +require("scripts/globals/npc_util") function onTrade(player, npc, trade) + if + player:getQuestStatus(SANDORIA, tpz.quest.id.sandoria.TEA_WITH_A_TONBERRY) == QUEST_ACCEPTED and + player:getCharVar('TEA_WITH_A_TONBERRY_PROG') == 1 and + npcUtil.tradeHas(trade, 1683) + then + player:startEvent(29) + end end function onTrigger(player, npc) - player:startEvent(21) + local teaGinseng = player:getCharVar('TEA_WITH_A_TONBERRY_PROG') + + if player:getQuestStatus(SANDORIA, tpz.quest.id.sandoria.TEA_WITH_A_TONBERRY) == QUEST_ACCEPTED then + if teaGinseng == 0 then + player:startEvent(27, 0, 1683) + elseif teaGinseng == 1 then + player:startEvent(28, 0, 1683) + else + player:startEvent(21) + end + else + player:startEvent(21) + end end function onEventUpdate(player, csid, option) end function onEventFinish(player, csid, option) + if csid == 27 then + player:setCharVar('TEA_WITH_A_TONBERRY_PROG', 1) + elseif csid == 29 then + player:tradeComplete() + player:addKeyItem(tpz.keyItem.TONBERRY_BLACKBOARD) + player:messageSpecial(ID.text.KEYITEM_OBTAINED, tpz.keyItem.TONBERRY_BLACKBOARD) + player:setCharVar('TEA_WITH_A_TONBERRY_PROG', 2) + end end diff --git a/scripts/zones/Davoi/IDs.lua b/scripts/zones/Davoi/IDs.lua index 66b427b65c3..5a05d1f9165 100644 --- a/scripts/zones/Davoi/IDs.lua +++ b/scripts/zones/Davoi/IDs.lua @@ -28,6 +28,8 @@ zones[tpz.zone.DAVOI] = AN_ORCISH_STORAGE_HOLE = 7450, -- An Orcish storage hole. There is something inside, but you cannot open it without a key. A_WELL = 7452, -- A well, presumably dug by Orcs. CHEST_UNLOCKED = 7471, -- You unlock the chest! + NOTHING_TO_DO = 7921, -- You have nothing left to do here. + UNDER_ATTACK = 7923, -- You are under attack! COMMON_SENSE_SURVIVAL = 7972, -- It appears that you have arrived at a new survival guide provided by the Adventurers' Mutual Aid Network. Common sense dictates that you should now be able to teleport here from similar tomes throughout the world. }, mob = @@ -69,6 +71,7 @@ zones[tpz.zone.DAVOI] = PURPLEFLASH_BRUKDOK = 17387969, ONE_EYED_GWAJBOJ = 17387970, THREE_EYED_PROZPUZ = 17387971, + HEMATIC_CYST = 17387972, }, npc = { diff --git a/scripts/zones/Davoi/mobs/Hematic_Cyst.lua b/scripts/zones/Davoi/mobs/Hematic_Cyst.lua new file mode 100644 index 00000000000..fcb28ca8389 --- /dev/null +++ b/scripts/zones/Davoi/mobs/Hematic_Cyst.lua @@ -0,0 +1,9 @@ +----------------------------------- +-- Area: Davoi +-- NM: Hematic Cyst +-- Involved in Quest: Tea with a Tonberry? +----------------------------------- + +function onMobDeath(mob, player, isKiller) + player:setCharVar("TEA_WITH_A_TONBERRY_PROG", 4) +end diff --git a/scripts/zones/Davoi/npcs/qm2.lua b/scripts/zones/Davoi/npcs/qm2.lua new file mode 100644 index 00000000000..159930968b4 --- /dev/null +++ b/scripts/zones/Davoi/npcs/qm2.lua @@ -0,0 +1,43 @@ +----------------------------------- +-- Area: Davoi +-- NPC: ??? (qm2) +-- Involved in Quest: Tea with a Tonberry +-- !pos 189.201 1.2553 -383.921 149 +----------------------------------- +require("scripts/globals/npc_util") +local ID = require("scripts/zones/Davoi/IDs") +----------------------------------- + +function onTrade(player, npc, trade) + if player:getCharVar('TEA_WITH_A_TONBERRY_PROG') == 3 then + if npcUtil.tradeHas(trade, 1682) and not GetMobByID(ID.mob.HEMATIC_CYST):isSpawned() then + -- Treasury Gold spawns Hematic Cyst + SpawnMob(ID.mob.HEMATIC_CYST):updateClaim(player) + player:confirmTrade() + player:messageText(npc, ID.text.UNDER_ATTACK, false) + end + end +end + +function onTrigger(player, npc) + local teaProg = player:getCharVar('TEA_WITH_A_TONBERRY_PROG') + + if teaProg == 3 then + player:messageSpecial(7920, 0, 1682) + elseif teaProg == 4 then + player:startEvent(126, 149, 1682) + elseif teaProg == 5 then + player:messageText(npc, ID.text.NOTHING_TO_DO, false) + else + player:messageSpecial(ID.text.NOTHING_OUT_OF_ORDINARY) + end +end + +function onEventUpdate(player, csid, option) +end + +function onEventFinish(player, csid, option) + if csid == 126 then + player:setCharVar('TEA_WITH_A_TONBERRY_PROG', 5) + end +end diff --git a/scripts/zones/Phanauet_Channel/npcs/Riche.lua b/scripts/zones/Phanauet_Channel/npcs/Riche.lua index ddf35d4cbe7..06c037e3707 100644 --- a/scripts/zones/Phanauet_Channel/npcs/Riche.lua +++ b/scripts/zones/Phanauet_Channel/npcs/Riche.lua @@ -9,11 +9,17 @@ function onTrade(player, npc, trade) end function onTrigger(player, npc) - player:startEvent(5) + if player:hasKeyItem(tpz.keyItem.TONBERRY_BLACKBOARD) then + player:startEvent(5, 1, 627, 1682, 63, 3, 30, 30, 0) + end end function onEventUpdate(player, csid, option) end function onEventFinish(player, csid, option) + if csid == 5 then + player:delKeyItem(tpz.keyItem.TONBERRY_BLACKBOARD) -- No message as the cutscene ends with the NPC taking it + player:setCharVar('TEA_WITH_A_TONBERRY_PROG', 3) + end end diff --git a/scripts/zones/Southern_San_dOria/npcs/Sobane.lua b/scripts/zones/Southern_San_dOria/npcs/Sobane.lua index e515e5ae5ae..e5f100980fe 100644 --- a/scripts/zones/Southern_San_dOria/npcs/Sobane.lua +++ b/scripts/zones/Southern_San_dOria/npcs/Sobane.lua @@ -5,7 +5,8 @@ -- Involved in quest: Sharpening the Sword, Riding on the Clouds -- !pos -190 -3 97 230 -- csid: 52 732 733 734 735 736 737 738 739 740 741 -------------------------------------- +------------------------------------ +local ID = require("scripts/zones/Southern_San_dOria/IDs") require("scripts/globals/keyitems") require("scripts/globals/npc_util") require("scripts/globals/quests") @@ -25,22 +26,35 @@ function onTrade(player, npc, trade) end function onTrigger(player, npc) - local blood = player:getQuestStatus(SANDORIA, tpz.quest.id.sandoria.SIGNED_IN_BLOOD) + local signedInBlood = player:getQuestStatus(SANDORIA, tpz.quest.id.sandoria.SIGNED_IN_BLOOD) local bloodProg = player:getCharVar("SIGNED_IN_BLOOD_Prog") + local teaWithATonberry = player:getQuestStatus(SANDORIA, tpz.quest.id.sandoria.TEA_WITH_A_TONBERRY) -- SHARPENING THE SWORD if player:getCharVar("sharpeningTheSwordCS") >= 2 then player:startEvent(52) -- SIGNED IN BLOOD - elseif blood == QUEST_AVAILABLE and player:getFameLevel(SANDORIA) >= 3 then - player:startEvent(732, 0, 1662) -- Start Quest - elseif blood == QUEST_ACCEPTED and bloodProg < 1 then - player:startEvent(733, 0, 1662) - elseif blood == QUEST_ACCEPTED and bloodProg == 3 then - player:startEvent(736) -- complete - elseif blood == QUEST_ACCEPTED and bloodProg >= 1 then - player:startEvent(735) + elseif signedInBlood == QUEST_AVAILABLE and player:getFameLevel(SANDORIA) >= 3 then + player:startEvent(732, 0, 1662) + elseif signedInBlood == QUEST_ACCEPTED then + if bloodProg < 1 then + player:startEvent(733, 0, 1662) + elseif bloodProg == 3 then + player:startEvent(736) + elseif bloodProg >= 1 then + player:startEvent(735) + end + elseif signedInBlood == QUEST_COMPLETED and player:needToZone() then + player:startEvent(737) + elseif signedInBlood == QUEST_COMPLETED and teaWithATonberry == QUEST_AVAILABLE then + player:startEvent(738) + elseif teaWithATonberry == QUEST_ACCEPTED then + if player:getCharVar("TEA_WITH_A_TONBERRY_PROG") == 5 then + player:startEvent(740) + else + player:startEvent(739) + end end end @@ -60,5 +74,11 @@ function onEventFinish(player, csid, option) elseif csid == 736 and npcUtil.completeQuest(player, SANDORIA, tpz.quest.id.sandoria.SIGNED_IN_BLOOD, {item = 14760, gil = 3500, var = "SIGNED_IN_BLOOD_Prog"}) then player:delKeyItem(tpz.ki.TORN_OUT_PAGES) player:confirmTrade() + elseif csid == 735 then + player:needToZone(true) + elseif csid == 738 then + player:addQuest(SANDORIA, tpz.quest.id.sandoria.TEA_WITH_A_TONBERRY) + elseif csid == 740 and npcUtil.completeQuest(player, SANDORIA, tpz.quest.id.sandoria.TEA_WITH_A_TONBERRY, {item = 13174}) then + player:setCharVar("TEA_WITH_A_TONBERRY_PROG", 0) end end diff --git a/scripts/zones/Tavnazian_Safehold/npcs/_0qa.lua b/scripts/zones/Tavnazian_Safehold/npcs/_0qa.lua index fa6b1e56f75..5da28a6617e 100644 --- a/scripts/zones/Tavnazian_Safehold/npcs/_0qa.lua +++ b/scripts/zones/Tavnazian_Safehold/npcs/_0qa.lua @@ -4,6 +4,7 @@ -- Involved in mission 2-4 -- !pos 111 -41 41 26 ----------------------------------- +local ID = require("scripts/zones/Tavnazian_Safehold/IDs") require("scripts/globals/keyitems") require("scripts/globals/missions") ----------------------------------- @@ -30,7 +31,11 @@ end function onEventFinish(player, csid, option) - if (csid == 104 or csid == 111) then + if csid == 104 then + player:setCharVar("PromathiaStatus", 1) + player:messageSpecial(ID.text.KEYITEM_OBTAINED, tpz.ki.MYSTERIOUS_AMULET_DRAINED) + player:addKeyItem(tpz.ki.MYSTERIOUS_AMULET_DRAINED) + elseif csid == 111 then player:setCharVar("PromathiaStatus", 1) elseif (csid == 115) then player:setCharVar("PromathiaStatus", 0) diff --git a/scripts/zones/The_Garden_of_RuHmet/Zone.lua b/scripts/zones/The_Garden_of_RuHmet/Zone.lua index a8c9a35aa1c..5d4e2f30c08 100644 --- a/scripts/zones/The_Garden_of_RuHmet/Zone.lua +++ b/scripts/zones/The_Garden_of_RuHmet/Zone.lua @@ -211,6 +211,8 @@ function onEventFinish(player, csid, option) player:setCharVar("Ru-Hmet-TP", 0) elseif (csid == 201) then player:setCharVar("PromathiaStatus", 1) + player:addKeyItem(tpz.ki.MYSTERIOUS_AMULET_PRISHE) + player:messageSpecial(ID.text.KEYITEM_OBTAINED, tpz.ki.MYSTERIOUS_AMULET) elseif (csid == 32000 and option==1) then player:setPos(420, 0, 398, 68) end diff --git a/scripts/zones/Upper_Jeuno/IDs.lua b/scripts/zones/Upper_Jeuno/IDs.lua index c1d7ea26d0c..4ad25fc0135 100644 --- a/scripts/zones/Upper_Jeuno/IDs.lua +++ b/scripts/zones/Upper_Jeuno/IDs.lua @@ -35,6 +35,7 @@ zones[tpz.zone.UPPER_JEUNO] = KIRISOMANRISO_DIALOG = 8071, -- Delivering goods to residences everywhere! ITEM_DELIVERY_DIALOG = 8071, -- Delivering goods to residences everywhere! DECIMATION_LEARNED = 8194, -- You have learned the weapon skill Decimation! + LEND_PRISHE_AMULET = 8331, -- You lend the mysterious amulet to Prishe. UNLOCK_DANCER = 11825, -- You can now become a dancer! }, mob = diff --git a/scripts/zones/Upper_Jeuno/npcs/Monberaux.lua b/scripts/zones/Upper_Jeuno/npcs/Monberaux.lua index 96620d998bd..48a0dc17b40 100644 --- a/scripts/zones/Upper_Jeuno/npcs/Monberaux.lua +++ b/scripts/zones/Upper_Jeuno/npcs/Monberaux.lua @@ -94,6 +94,8 @@ function onEventFinish(player, csid, option) player:startEvent(207) --207 elseif (csid == 82) then player:setCharVar("PromathiaStatus", 1) + player:delKeyItem(tpz.ki.MYSTERIOUS_AMULET_DRAINED) + player:messageSpecial(ID.text.LEND_PRISHE_AMULET, tpz.ki.MYSTERIOUS_AMULET_PRISHE) elseif (csid == 75) then player:setCharVar("PromathiaStatus", 0) player:completeMission(COP, tpz.mission.id.cop.DARKNESS_NAMED) diff --git a/sql/augments.sql b/sql/augments.sql index cd39315fe33..15f9b9b0cb1 100644 --- a/sql/augments.sql +++ b/sql/augments.sql @@ -1649,7 +1649,7 @@ INSERT INTO `augments` VALUES (1357, 0, 0, 0, 0, 0); -- Enhances "Paralyze II" e INSERT INTO `augments` VALUES (1358, 0, 0, 0, 0, 0); -- Enhances "Aura Steal" effect INSERT INTO `augments` VALUES (1359, 0, 0, 0, 0, 0); -- Enhances "Ambush" effect INSERT INTO `augments` VALUES (1360, 0, 0, 0, 0, 0); -- Enhances "Feint" effect -INSERT INTO `augments` VALUES (1361, 0, 0, 0, 0, 0); -- Enh. "Assassins Charge" effect +INSERT INTO `augments` VALUES (1361, 0, 886, 1, 0, 0); -- Enh. "Assassins Charge" effect INSERT INTO `augments` VALUES (1362, 0, 0, 0, 0, 0); -- 1362 currently unused. Leave at zero. Edit+move or remove this note as new augments get discovered. INSERT INTO `augments` VALUES (1363, 0, 0, 0, 0, 0); -- 1363 currently unused. Leave at zero. Edit+move or remove this note as new augments get discovered. INSERT INTO `augments` VALUES (1364, 0, 0, 0, 0, 0); -- Enhances "Iron Will" effect diff --git a/sql/mob_droplist.sql b/sql/mob_droplist.sql index 7f7f8a8dc47..ae96c143bb5 100644 --- a/sql/mob_droplist.sql +++ b/sql/mob_droplist.sql @@ -2285,7 +2285,8 @@ INSERT INTO `mob_droplist` VALUES (394,1,2,200,1324,240); -- (Byakko) a.abjurati INSERT INTO `mob_droplist` VALUES (394,1,2,200,1341,280); -- (Byakko) n.abjuration_hn. #2 INSERT INTO `mob_droplist` VALUES (394,0,0,1000,722,200); -- (Byakko) divine_log INSERT INTO `mob_droplist` VALUES (394,0,0,1000,837,200); -- (Byakko) malboro_fiber -INSERT INTO `mob_droplist` VALUES (394,0,0,1000,860,200); -- (Byakko) behemoth_hide +INSERT INTO `mob_droplist` VALUES (394,0,0,1000,860,150); -- (Byakko) behemoth_hide +INSERT INTO `mob_droplist` VALUES (394,0,0,1000,860,150); -- (Byakko) behemoth_hide #2 INSERT INTO `mob_droplist` VALUES (394,0,0,1000,1311,20); -- (Byakko) oxblood INSERT INTO `mob_droplist` VALUES (394,0,0,1000,1110,10); -- (Byakko) beetle_blood INSERT INTO `mob_droplist` VALUES (395,0,0,1000,916,140); @@ -13747,6 +13748,7 @@ INSERT INTO `mob_droplist` VALUES (2562,0,0,1000,18332,20); INSERT INTO `mob_droplist` VALUES (2562,0,0,1000,1516,30); INSERT INTO `mob_droplist` VALUES (2562,0,0,1000,1517,30); INSERT INTO `mob_droplist` VALUES (2562,0,0,1000,1519,30); +INSERT INTO `mob_droplist` VALUES (2562,0,0,1000,3382,10); -- Odious Cryptex INSERT INTO `mob_droplist` VALUES (2562,0,0,1000,3495,10); INSERT INTO `mob_droplist` VALUES (2562,0,0,1000,11388,10); INSERT INTO `mob_droplist` VALUES (2562,0,0,1000,15025,10); @@ -13794,7 +13796,8 @@ INSERT INTO `mob_droplist` VALUES (2564,0,0,1000,18332,20); INSERT INTO `mob_droplist` VALUES (2564,0,0,1000,1516,30); INSERT INTO `mob_droplist` VALUES (2564,0,0,1000,1517,30); INSERT INTO `mob_droplist` VALUES (2564,0,0,1000,1519,30); -INSERT INTO `mob_droplist` VALUES (2564,0,0,1000,3380,10); +INSERT INTO `mob_droplist` VALUES (2564,0,0,1000,3380,10); -- Odious Scale +INSERT INTO `mob_droplist` VALUES (2564,0,0,1000,3495,10); -- Forgotten Touch INSERT INTO `mob_droplist` VALUES (2564,0,0,1000,11388,10); INSERT INTO `mob_droplist` VALUES (2564,0,0,1000,15025,10); INSERT INTO `mob_droplist` VALUES (2564,0,0,1000,15040,10); @@ -16612,6 +16615,56 @@ INSERT INTO `mob_droplist` VALUES (3147,0,0,1000,2172,1000); -- (Hydra) Hydra S INSERT INTO `mob_droplist` VALUES (3147,0,0,1000,2172,100); -- (Hydra) Hydra Scale INSERT INTO `mob_droplist` VALUES (3147,0,0,1000,15530,240); -- (Hydra) Berserker's Torque 24% INSERT INTO `mob_droplist` VALUES (3147,0,0,1000,17952,240); -- (Hydra) Sirius Axe 24% +INSERT INTO `mob_droplist` VALUES (3148,2,0,1000,1452,0); -- O. Bronzepiece +INSERT INTO `mob_droplist` VALUES (3148,0,0,1000,18308,20); +INSERT INTO `mob_droplist` VALUES (3148,0,0,1000,18290,20); +INSERT INTO `mob_droplist` VALUES (3148,0,0,1000,18296,20); +INSERT INTO `mob_droplist` VALUES (3148,0,0,1000,18332,20); +INSERT INTO `mob_droplist` VALUES (3148,0,0,1000,1516,30); +INSERT INTO `mob_droplist` VALUES (3148,0,0,1000,1517,30); +INSERT INTO `mob_droplist` VALUES (3148,0,0,1000,1519,30); +INSERT INTO `mob_droplist` VALUES (3148,0,0,1000,3383,10); -- Odious Strongbox +INSERT INTO `mob_droplist` VALUES (3148,0,0,1000,3495,10); -- Forgotten Touch +INSERT INTO `mob_droplist` VALUES (3148,0,0,1000,11388,10); +INSERT INTO `mob_droplist` VALUES (3148,0,0,1000,15025,10); +INSERT INTO `mob_droplist` VALUES (3148,0,0,1000,15040,10); +INSERT INTO `mob_droplist` VALUES (3148,0,0,1000,15074,10); +INSERT INTO `mob_droplist` VALUES (3148,0,0,1000,15081,10); +INSERT INTO `mob_droplist` VALUES (3148,0,0,1000,15108,10); +INSERT INTO `mob_droplist` VALUES (3148,0,0,1000,15118,10); +INSERT INTO `mob_droplist` VALUES (3148,0,0,1000,15125,10); +INSERT INTO `mob_droplist` VALUES (3148,0,0,1000,15127,10); +INSERT INTO `mob_droplist` VALUES (3148,0,0,1000,15129,10); +INSERT INTO `mob_droplist` VALUES (3148,0,0,1000,15132,10); +INSERT INTO `mob_droplist` VALUES (3148,0,0,1000,15136,10); +INSERT INTO `mob_droplist` VALUES (3148,0,0,1000,15145,10); +INSERT INTO `mob_droplist` VALUES (3148,0,0,1000,15146,10); +INSERT INTO `mob_droplist` VALUES (3148,0,0,1000,16349,10); +INSERT INTO `mob_droplist` VALUES (3149,2,0,1000,1452,0); -- O. Bronzepiece +INSERT INTO `mob_droplist` VALUES (3149,0,0,1000,18308,20); +INSERT INTO `mob_droplist` VALUES (3149,0,0,1000,18290,20); +INSERT INTO `mob_droplist` VALUES (3149,0,0,1000,18296,20); +INSERT INTO `mob_droplist` VALUES (3149,0,0,1000,18332,20); +INSERT INTO `mob_droplist` VALUES (3149,0,0,1000,1516,30); +INSERT INTO `mob_droplist` VALUES (3149,0,0,1000,1517,30); +INSERT INTO `mob_droplist` VALUES (3149,0,0,1000,1519,30); +INSERT INTO `mob_droplist` VALUES (3149,0,0,1000,3381,10); -- Odious Leather +INSERT INTO `mob_droplist` VALUES (3149,0,0,1000,3495,10); -- Forgotten Touch +INSERT INTO `mob_droplist` VALUES (3149,0,0,1000,11388,10); +INSERT INTO `mob_droplist` VALUES (3149,0,0,1000,15025,10); +INSERT INTO `mob_droplist` VALUES (3149,0,0,1000,15040,10); +INSERT INTO `mob_droplist` VALUES (3149,0,0,1000,15074,10); +INSERT INTO `mob_droplist` VALUES (3149,0,0,1000,15081,10); +INSERT INTO `mob_droplist` VALUES (3149,0,0,1000,15108,10); +INSERT INTO `mob_droplist` VALUES (3149,0,0,1000,15118,10); +INSERT INTO `mob_droplist` VALUES (3149,0,0,1000,15125,10); +INSERT INTO `mob_droplist` VALUES (3149,0,0,1000,15127,10); +INSERT INTO `mob_droplist` VALUES (3149,0,0,1000,15129,10); +INSERT INTO `mob_droplist` VALUES (3149,0,0,1000,15132,10); +INSERT INTO `mob_droplist` VALUES (3149,0,0,1000,15136,10); +INSERT INTO `mob_droplist` VALUES (3149,0,0,1000,15145,10); +INSERT INTO `mob_droplist` VALUES (3149,0,0,1000,15146,10); +INSERT INTO `mob_droplist` VALUES (3149,0,0,1000,16349,10); /*!40000 ALTER TABLE `mob_droplist` ENABLE KEYS */; UNLOCK TABLES; diff --git a/sql/mob_groups.sql b/sql/mob_groups.sql index b2a986167af..eeeca8fe982 100644 --- a/sql/mob_groups.sql +++ b/sql/mob_groups.sql @@ -9859,7 +9859,7 @@ INSERT INTO `mob_groups` VALUES (46,964,149,'Deloknok',0,128,0,0,0,53,53,0); INSERT INTO `mob_groups` VALUES (47,3233,149,'Purpleflash_Brukdok',600,0,2044,0,0,45,45,0); INSERT INTO `mob_groups` VALUES (48,2980,149,'One-eyed_Gwajboj',0,128,1859,0,0,62,62,0); INSERT INTO `mob_groups` VALUES (49,6678,149,'Three-eyed_Prozpuz',0,128,307,0,0,60,60,0); -INSERT INTO `mob_groups` VALUES (50,1930,149,'Hematic_Cyst',0,128,0,0,0,40,40,0); +INSERT INTO `mob_groups` VALUES (50,1930,149,'Hematic_Cyst',0,128,0,2400,0,40,40,0); INSERT INTO `mob_groups` VALUES (8047,3992,149,'Treasure_Chest',0,0,0,0,0,0,0,0); @@ -11559,24 +11559,24 @@ INSERT INTO `mob_groups` VALUES (9375,4498,184,'Zeid',0,0,0,0,0,0,0,0); INSERT INTO `mob_groups` VALUES (1,3076,185,'Overlords_Tombstone',0,128,1967,12500,12500,85,85,0); INSERT INTO `mob_groups` VALUES (2,363,185,'Battlechoir_Gitchfotch',0,128,237,8000,0,83,84,0); INSERT INTO `mob_groups` VALUES (3,3705,185,'Soulsender_Fugbrag',0,128,237,8000,0,83,84,0); -INSERT INTO `mob_groups` VALUES (4,4150,185,'Vanguard_Footsoldier',600,0,2548,4000,0,75,77,0); -INSERT INTO `mob_groups` VALUES (5,4193,185,'Vanguard_Trooper',600,0,2548,4000,0,75,77,0); +INSERT INTO `mob_groups` VALUES (4,4150,185,'Vanguard_Footsoldier',600,0,2564,4000,0,75,77,0); +INSERT INTO `mob_groups` VALUES (5,4193,185,'Vanguard_Trooper',600,0,3149,4000,0,75,77,0); INSERT INTO `mob_groups` VALUES (6,4135,185,'Vanguard_Amputator',600,0,2548,4000,0,75,77,0); INSERT INTO `mob_groups` VALUES (7,4195,185,'Vanguard_Vexer',600,0,2564,4000,0,75,77,0); -INSERT INTO `mob_groups` VALUES (8,4172,185,'Vanguard_Pillager',600,0,2548,4000,0,75,77,0); -INSERT INTO `mob_groups` VALUES (9,4162,185,'Vanguard_Mesmerizer',600,0,2548,4000,0,75,77,0); -INSERT INTO `mob_groups` VALUES (10,4151,185,'Vanguard_Grappler',600,0,2548,4000,0,75,77,0); +INSERT INTO `mob_groups` VALUES (8,4172,185,'Vanguard_Pillager',600,0,2564,4000,0,75,77,0); +INSERT INTO `mob_groups` VALUES (9,4162,185,'Vanguard_Mesmerizer',600,0,3148,4000,0,75,77,0); +INSERT INTO `mob_groups` VALUES (10,4151,185,'Vanguard_Grappler',600,0,3149,4000,0,75,77,0); INSERT INTO `mob_groups` VALUES (11,4165,185,'Vanguard_Neckchopper',600,0,2562,4000,0,75,77,0); -INSERT INTO `mob_groups` VALUES (12,4140,185,'Vanguard_Bugler',600,0,2548,4000,0,75,77,0); -INSERT INTO `mob_groups` VALUES (13,4152,185,'Vanguard_Gutslasher',600,0,2548,4000,0,75,77,0); -INSERT INTO `mob_groups` VALUES (14,4156,185,'Vanguard_Impaler',600,0,2548,4000,0,75,77,0); +INSERT INTO `mob_groups` VALUES (12,4140,185,'Vanguard_Bugler',600,0,3149,4000,0,75,77,0); +INSERT INTO `mob_groups` VALUES (13,4152,185,'Vanguard_Gutslasher',600,0,3149,4000,0,75,77,0); +INSERT INTO `mob_groups` VALUES (14,4156,185,'Vanguard_Impaler',600,0,3148,4000,0,75,77,0); INSERT INTO `mob_groups` VALUES (15,4190,185,'Vanguards_Wyvern',0,128,0,2400,0,75,77,0); -INSERT INTO `mob_groups` VALUES (16,4138,185,'Vanguard_Backstabber',600,0,2548,4000,0,75,77,0); -INSERT INTO `mob_groups` VALUES (17,4154,185,'Vanguard_Hawker',600,0,2548,4000,0,75,77,0); +INSERT INTO `mob_groups` VALUES (16,4138,185,'Vanguard_Backstabber',600,0,2562,4000,0,75,77,0); +INSERT INTO `mob_groups` VALUES (17,4154,185,'Vanguard_Hawker',600,0,3148,4000,0,75,77,0); INSERT INTO `mob_groups` VALUES (18,4187,185,'Vanguards_Hecteyes',0,128,0,2400,0,75,77,0); -INSERT INTO `mob_groups` VALUES (19,4144,185,'Vanguard_Dollmaster',600,0,2548,4000,0,75,77,0); +INSERT INTO `mob_groups` VALUES (19,4144,185,'Vanguard_Dollmaster',600,0,2562,4000,0,75,77,0); INSERT INTO `mob_groups` VALUES (20,4185,185,'Vanguards_Avatar',0,128,0,2400,0,75,77,0); -INSERT INTO `mob_groups` VALUES (21,4174,185,'Vanguard_Predator',600,0,2548,4000,0,75,77,0); +INSERT INTO `mob_groups` VALUES (21,4174,185,'Vanguard_Predator',600,0,2564,4000,0,75,77,0); INSERT INTO `mob_groups` VALUES (22,3548,185,'Serjeant_Tombstone',0,128,2201,1000,1000,65,65,0); INSERT INTO `mob_groups` VALUES (23,3340,185,'Reapertongue_Gadgquok',1200,0,2083,8000,0,83,84,0); INSERT INTO `mob_groups` VALUES (24,4289,185,'Warchief_Tombstone',0,128,0,1000,1000,65,65,0); @@ -11589,23 +11589,23 @@ INSERT INTO `mob_groups` VALUES (30,6084,185,'Bloodfist_Voshgrosh',0,128,3111,0, INSERT INTO `mob_groups` VALUES (31,6085,185,'Spellspear_Djokvukk',0,128,3112,0,0,95,95,0); INSERT INTO `mob_groups` VALUES (32,6086,185,'Djokvukks_Wyvern',0,128,0,0,0,92,92,0); INSERT INTO `mob_groups` VALUES (33,6087,185,'Arch_Overlord_Tombstone',0,128,3113,0,0,99,99,0); -INSERT INTO `mob_groups` VALUES (34,4150,185,'Vanguard_Footsoldier',600,0,2548,4000,0,90,92,0); -INSERT INTO `mob_groups` VALUES (35,4151,185,'Vanguard_Grappler',600,0,2548,4000,0,90,92,0); +INSERT INTO `mob_groups` VALUES (34,4150,185,'Vanguard_Footsoldier',600,0,2564,4000,0,90,92,0); +INSERT INTO `mob_groups` VALUES (35,4151,185,'Vanguard_Grappler',600,0,3149,4000,0,90,92,0); INSERT INTO `mob_groups` VALUES (36,4135,185,'Vanguard_Amputator',600,0,2548,4000,0,90,92,0); -INSERT INTO `mob_groups` VALUES (37,4162,185,'Vanguard_Mesmerizer',600,0,2548,4000,0,90,92,0); +INSERT INTO `mob_groups` VALUES (37,4162,185,'Vanguard_Mesmerizer',600,0,3148,4000,0,90,92,0); INSERT INTO `mob_groups` VALUES (38,4195,185,'Vanguard_Vexer',600,0,2564,4000,0,90,92,0); -INSERT INTO `mob_groups` VALUES (39,4172,185,'Vanguard_Pillager',600,0,2548,4000,0,90,92,0); -INSERT INTO `mob_groups` VALUES (40,4193,185,'Vanguard_Trooper',600,0,2548,4000,0,90,92,0); +INSERT INTO `mob_groups` VALUES (39,4172,185,'Vanguard_Pillager',600,0,2564,4000,0,90,92,0); +INSERT INTO `mob_groups` VALUES (40,4193,185,'Vanguard_Trooper',600,0,3149,4000,0,90,92,0); INSERT INTO `mob_groups` VALUES (41,4165,185,'Vanguard_Neckchopper',600,0,2562,4000,0,90,92,0); -INSERT INTO `mob_groups` VALUES (42,4154,185,'Vanguard_Hawker',600,0,2548,4000,0,90,92,0); +INSERT INTO `mob_groups` VALUES (42,4154,185,'Vanguard_Hawker',600,0,3148,4000,0,90,92,0); INSERT INTO `mob_groups` VALUES (43,4187,185,'Vanguards_Hecteyes',0,128,0,2400,0,90,92,0); -INSERT INTO `mob_groups` VALUES (44,4140,185,'Vanguard_Bugler',600,0,2548,4000,0,90,92,0); -INSERT INTO `mob_groups` VALUES (45,4174,185,'Vanguard_Predator',600,0,2548,4000,0,90,92,0); -INSERT INTO `mob_groups` VALUES (46,4152,185,'Vanguard_Gutslasher',600,0,2548,4000,0,90,92,0); -INSERT INTO `mob_groups` VALUES (47,4138,185,'Vanguard_Backstabber',600,0,2548,4000,0,90,92,0); -INSERT INTO `mob_groups` VALUES (48,4156,185,'Vanguard_Impaler',600,0,2548,4000,0,90,92,0); +INSERT INTO `mob_groups` VALUES (44,4140,185,'Vanguard_Bugler',600,0,3149,4000,0,90,92,0); +INSERT INTO `mob_groups` VALUES (45,4174,185,'Vanguard_Predator',600,0,2564,4000,0,90,92,0); +INSERT INTO `mob_groups` VALUES (46,4152,185,'Vanguard_Gutslasher',600,0,3149,4000,0,90,92,0); +INSERT INTO `mob_groups` VALUES (47,4138,185,'Vanguard_Backstabber',600,0,2562,4000,0,90,92,0); +INSERT INTO `mob_groups` VALUES (48,4156,185,'Vanguard_Impaler',600,0,3148,4000,0,90,92,0); INSERT INTO `mob_groups` VALUES (49,4190,185,'Vanguards_Wyvern',0,128,0,2400,0,90,92,0); -INSERT INTO `mob_groups` VALUES (50,4144,185,'Vanguard_Dollmaster',600,0,2548,4000,0,90,92,0); +INSERT INTO `mob_groups` VALUES (50,4144,185,'Vanguard_Dollmaster',600,0,2562,4000,0,90,92,0); INSERT INTO `mob_groups` VALUES (51,4185,185,'Vanguards_Avatar',0,128,0,2400,0,90,92,0); INSERT INTO `mob_groups` VALUES (52,3548,185,'Serjeant_Tombstone',0,128,2201,1000,1000,80,80,0); INSERT INTO `mob_groups` VALUES (53,4289,185,'Warchief_Tombstone',0,128,0,1000,1000,80,80,0); diff --git a/sql/mob_spawn_points.sql b/sql/mob_spawn_points.sql index d27d2a3c2ed..70e4f359769 100644 --- a/sql/mob_spawn_points.sql +++ b/sql/mob_spawn_points.sql @@ -45703,7 +45703,7 @@ INSERT INTO `mob_spawn_points` VALUES (17387968,'Deloknok','Deloknok',46,-124,3, INSERT INTO `mob_spawn_points` VALUES (17387969,'Purpleflash_Brukdok','Purpleflash Brukdok',47,-135.469,-0.091,-184.703,127); INSERT INTO `mob_spawn_points` VALUES (17387970,'One-eyed_Gwajboj','One-eyed Gwajboj',48,-36,3,-211,57); INSERT INTO `mob_spawn_points` VALUES (17387971,'Three-eyed_Prozpuz','Three-eyed Prozpuz',49,-32,4,-212,71); -INSERT INTO `mob_spawn_points` VALUES (17387972,'Hematic_Cyst','Hematic Cyst',50,0,0,0,0); +INSERT INTO `mob_spawn_points` VALUES (17387972,'Hematic_Cyst','Hematic Cyst',50,177,3.676,-372.524,199); -- ------------------------------------------------------------ -- Monastic Cavern (Zone 150) diff --git a/src/map/ai/helpers/pathfind.cpp b/src/map/ai/helpers/pathfind.cpp index 9b0f2e74e14..407479f162d 100644 --- a/src/map/ai/helpers/pathfind.cpp +++ b/src/map/ai/helpers/pathfind.cpp @@ -347,7 +347,7 @@ bool CPathFind::FindPath(const position_t& start, const position_t& end) if (m_points.size() <= 0) { - ShowNavError("CPathFind::FindPath Entity (%d) could not find path\n", m_PTarget->id); + ShowNavError("CPathFind::FindPath Entity (%s - %d) could not find path\n", m_PTarget->GetName(), m_PTarget->id); return false; } diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index d435e7615ee..39ed20ef957 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -8082,7 +8082,7 @@ inline int32 CLuaBaseEntity::delHP(lua_State *L) /************************************************************************ * Function: takeDamage() * Purpose : Takes damage from the provided attacker. If no attacker is provided then it clears the last attacker. -* Example : target:takeDamage(500, attacker=nil, attackType=ATTACK_NONE, damageType=DAMAGE_NONE, shouldWakeUp=true) +* Example : target:takeDamage(500, attacker=nil, attackType=ATTACK_NONE, damageType=DAMAGE_NONE, flags={wakeUp=true}) * Notes : ************************************************************************/ @@ -8094,23 +8094,62 @@ inline int32 CLuaBaseEntity::takeDamage(lua_State *L) TPZ_DEBUG_BREAK_IF(lua_isnil(L, 1) || !lua_isnumber(L, 1)); int32 damage = (int32)(lua_tointeger(L, 1)); - if (damage > 0) { - // Attempt to retrieve the attacker - CBattleEntity* PAttacker = nullptr; - if (!lua_isnil(L, 2) && lua_isuserdata(L, 2)) { - CLuaBaseEntity* PLuaAttacker = Lunar::check(L, 2); - TPZ_DEBUG_BREAK_IF(PLuaAttacker == nullptr); - TPZ_DEBUG_BREAK_IF(PLuaAttacker->m_PBaseEntity->objtype == TYPE_NPC); - PAttacker = (CBattleEntity*)PLuaAttacker->m_PBaseEntity; - } + + // Attempt to retrieve the attacker + CBattleEntity* PAttacker = nullptr; + if (!lua_isnil(L, 2) && lua_isuserdata(L, 2)) + { + CLuaBaseEntity* PLuaAttacker = Lunar::check(L, 2); + TPZ_DEBUG_BREAK_IF(PLuaAttacker == nullptr); + TPZ_DEBUG_BREAK_IF(PLuaAttacker->m_PBaseEntity->objtype == TYPE_NPC); + PAttacker = dynamic_cast(PLuaAttacker->m_PBaseEntity); + } + + CBattleEntity* PDefender = dynamic_cast(m_PBaseEntity); + + // Check for special flags which may prevent damage from waking up the target + bool wakeUp = true; + bool breakBind = true; + bool removePetrify = true; + + if (!lua_isnil(L, 5) && lua_istable(L, 5)) + { + // Attempt to wake up the target unless wakeUp is provided and is false. + lua_getfield(L, 5, "wakeUp"); + wakeUp = (lua_isnil(L, -1) || !lua_isboolean(L, -1) || lua_toboolean(L, -1)); + + // Remove petrification unless removePetrify is provided and is false + lua_getfield(L, 5, "removePetrify"); + removePetrify = (lua_isnil(L, -1) || !lua_isboolean(L, -1) || lua_toboolean(L, -1)); + + // Attempt to break Bind unless breakBind is provided and is false + lua_getfield(L, 5, "breakBind"); + breakBind = (lua_isnil(L, -1) || !lua_isboolean(L, -1) || lua_toboolean(L, -1)); + } + + // Deal damage and liberate target when applicable + if (damage > 0) + { ATTACKTYPE attackType = !lua_isnil(L, 3) && lua_isnumber(L, 3) ? (ATTACKTYPE)lua_tointeger(L, 3) : ATTACK_NONE; DAMAGETYPE damageType = !lua_isnil(L, 4) && lua_isnumber(L, 4) ? (DAMAGETYPE)lua_tointeger(L, 4) : DAMAGE_NONE; - ((CBattleEntity*)m_PBaseEntity)->takeDamage(damage, PAttacker, attackType, damageType); + PDefender->takeDamage(damage, PAttacker, attackType, damageType); + + if (wakeUp) + { + PDefender->StatusEffectContainer->WakeUp(); + } + + if (removePetrify) + { + PDefender->StatusEffectContainer->DelStatusEffect(EFFECT_PETRIFICATION); + } + } - // Attempt to wake up the target unless shouldWakeUp is provided and is false - if (lua_isnil(L, 5) || !lua_isboolean(L, 5) || lua_toboolean(L, 5)) - ((CBattleEntity*)m_PBaseEntity)->StatusEffectContainer->WakeUp(); + // Bind has a chance to break from all direct attacks, even if they don't deal damage + if (PAttacker && breakBind) + { + battleutils::BindBreakCheck(PAttacker, PDefender); } return 0; diff --git a/src/map/navmesh.cpp b/src/map/navmesh.cpp index 8354e8dff1a..f6b75ea6c95 100644 --- a/src/map/navmesh.cpp +++ b/src/map/navmesh.cpp @@ -426,11 +426,14 @@ double CNavMesh::DistanceToWall(const position_t& start) bool CNavMesh::raycast(const position_t& start, const position_t& end) { TracyZoneScoped; + if (start.x == end.x && start.y == end.y && start.z == end.z) { return true; } + dtStatus status; + dtStatus status; float spos[3]; ToDetourPos(&start, spos); @@ -443,7 +446,6 @@ bool CNavMesh::raycast(const position_t& start, const position_t& end) polyPickExt[1] = 60; polyPickExt[2] = 30; - float snearest[3]; dtQueryFilter filter; // include walking @@ -451,6 +453,7 @@ bool CNavMesh::raycast(const position_t& start, const position_t& end) // exclude swim,jump,door filter.setExcludeFlags(0xE); dtPolyRef startRef; + float snearest[3]; status = m_navMeshQuery.findNearestPoly(spos, polyPickExt, &filter, &startRef, snearest); if (dtStatusFailed(status)) @@ -463,26 +466,65 @@ bool CNavMesh::raycast(const position_t& start, const position_t& end) if (!m_navMesh->isValidPolyRef(startRef)) { ShowNavError("CNavMesh::raycast startRef is invalid (%f, %f, %f) (%u)\n", start.x, start.y, start.z, m_zoneID); - return false; + return true; + } + + dtPolyRef endRef; + float enearest[3]; + + status = m_navMeshQuery.findNearestPoly(epos, polyPickExt, &filter, &endRef, enearest); + + if (dtStatusFailed(status)) + { + ShowNavError("CNavMesh::raycast end point invalid (%f, %f, %f) (%u)\n", epos[0], epos[1], epos[2], m_zoneID); + outputError(status); + return true; + } + + if (!m_navMesh->isValidPolyRef(endRef)) + { + ShowNavError("CNavMesh::raycast endRef is invalid (%f, %f, %f) (%u)\n", end.x, end.y, end.z, m_zoneID); + return true; + } + + float distanceToWall = 0.0f; + float hitPos[3]; + float hitNormal[3]; + + status = m_navMeshQuery.findDistanceToWall(endRef, enearest, 5.0f, &filter, &distanceToWall, hitPos, hitNormal); + + if (dtStatusFailed(status)) + { + ShowNavError("CNavMesh::raycast findDistanceToWall failed (%f, %f, %f) (%u)\n", epos[0], epos[1], epos[2], m_zoneID); + outputError(status); + return true; } // There is a tiny strip of walkable map at the very edge of walls that // a player can use, but is not part of the navmesh. For a point to be // raycasted to - it needs to be on the navmesh. This will check to // see if the player is "off-mesh" and raycast to the nearest "on-mesh" - // point instead. - if (DistanceToWall(end) <= 2.0) + // point instead. distanceToWall will be 0.0f if the player is "off-mesh". + if (distanceToWall < 0.01f) { - float closest[3]; - status = m_navMeshQuery.closestPointOnPolyBoundary(startRef, epos, closest); + // Overwrite epos with closest valid point + status = m_navMeshQuery.closestPointOnPolyBoundary(startRef, epos, epos); + if (dtStatusFailed(status)) { ShowNavError("CNavMesh::raycast closestPointOnPolyBoundary failed (%u)\n", m_zoneID); outputError(status); - return false; + return true; } + } - std::memcpy(epos, closest, sizeof(float) * 3); + status = m_navMeshQuery.raycast(startRef, spos, epos, &filter, 0, &m_hit); + + if (dtStatusFailed(status)) + { + ShowNavError("CNavMesh::raycast raycast failed (%f, %f, %f)->(%f, %f, %f) (%u)\n", spos[0], spos[1], spos[2], epos[0], epos[1], epos[2], m_zoneID); + outputError(status); + return true; } float t = 0; diff --git a/src/map/navmesh.h b/src/map/navmesh.h index 3252636477f..f7b169d6e30 100644 --- a/src/map/navmesh.h +++ b/src/map/navmesh.h @@ -73,10 +73,14 @@ class CNavMesh std::vector findPath(const position_t& start, const position_t& end); std::pair findRandomPosition(const position_t& start, float maxRadius); - // returns true if the point is in water + // Returns true if the point is in water bool inWater(const position_t& point); - - // returns true if no wall was hit + + // Returns true if no wall was hit + // + // Recast Detour Docs: + // Casts a 'walkability' ray along the surface of the navigation mesh from the start position toward the end position. + // Note: This is not a point-to-point in 3D space calculation, it is 2D across the navmesh! bool raycast(const position_t& start, const position_t& end); bool validPosition(const position_t& position); diff --git a/src/map/packet_system.cpp b/src/map/packet_system.cpp index d1c598ba412..3016d07466b 100644 --- a/src/map/packet_system.cpp +++ b/src/map/packet_system.cpp @@ -5981,10 +5981,17 @@ void SmallPacket0x102(map_session_data_t* session, CCharEntity* PChar, CBasicPac CBlueSpell* spell = (CBlueSpell*)spell::GetSpell(static_cast(spellInQuestion + 0x200)); // the spells in this packet are offsetted by 0x200 from their spell IDs. if (spell != nullptr) { - blueutils::SetBlueSpell(PChar, spell, spellIndex, false); + if (PChar->m_SetBlueSpells[spellIndex] == 0x00) + { + ShowExploit(CL_RED "SmallPacket0x102: Player %s trying to unset BLU spell they don't have set! \n" CL_RESET, PChar->GetName()); + } + else + { + blueutils::SetBlueSpell(PChar, spell, spellIndex, false); + } } else { - ShowDebug("Cannot resolve spell id \n"); + ShowDebug("SmallPacket0x102: Cannot resolve spell id %u \n", spellInQuestion); } } } @@ -6018,7 +6025,7 @@ void SmallPacket0x102(map_session_data_t* session, CCharEntity* PChar, CBasicPac PChar->UpdateHealth(); } else { - ShowDebug("Cannot resolve spell id \n"); + ShowDebug("SmallPacket0x102: Cannot resolve spell id %u \n", spellInQuestion); } } else { diff --git a/src/map/status_effect_container.cpp b/src/map/status_effect_container.cpp index b56e6195d30..5581937868a 100644 --- a/src/map/status_effect_container.cpp +++ b/src/map/status_effect_container.cpp @@ -1746,9 +1746,11 @@ bool CStatusEffectContainer::CheckForElevenRoll() bool CStatusEffectContainer::IsAsleep() { - return HasStatusEffect({EFFECT_SLEEP, + return HasStatusEffect({ + EFFECT_SLEEP, EFFECT_SLEEP_II, - EFFECT_LULLABY}); + EFFECT_LULLABY + }); } void CStatusEffectContainer::WakeUp() diff --git a/src/map/utils/battleutils.cpp b/src/map/utils/battleutils.cpp index 15fbb2d62a8..da7713ca481 100644 --- a/src/map/utils/battleutils.cpp +++ b/src/map/utils/battleutils.cpp @@ -4435,44 +4435,18 @@ namespace battleutils { if (PDefender->StatusEffectContainer->HasStatusEffect(EFFECT_BIND)) { - uint16 BindBreakChance = 0; // 0-1000 (100.0%) scale. Maybe change to a float later.. - EMobDifficulty mobCheck = charutils::CheckMob(PAttacker->GetMLevel(), PDefender->GetMLevel()); + uint16 BindBreakChance = 950; // 0-1000 (100.0%) scale. Maybe change to a float later.. - // Todo: replace with an actual calculated value based on level difference. Not it, Bro! - // This entire block of conditionals should not exist. Lv diff really shouldn't be handled at the exp check level either. - // It might not even be in sync with the check values the entire way from lv 1 to lv 99 for all we know. - switch (mobCheck) - { - case EMobDifficulty::TooWeak: - case EMobDifficulty::IncrediblyEasyPrey: - BindBreakChance = 10; - break; - - case EMobDifficulty::EasyPrey: - case EMobDifficulty::DecentChallenge: - BindBreakChance = 150; - break; - - case EMobDifficulty::EvenMatch: - BindBreakChance = 300; - break; - - case EMobDifficulty::Tough: - BindBreakChance = 750; - break; - - case EMobDifficulty::VeryTough: - case EMobDifficulty::IncrediblyTough: - BindBreakChance = 990; - break; - - default: - // no-op - break; - } + // Previously there was a tiered comparative level check here which gave different rates + // depending on the level difference between the attacker and the defender. + // These rates seemed very low, and have been removed, absent true research on retail. + // EMobDifficulty mobCheck = charutils::CheckMob(PAttacker->GetMLevel(), PDefender->GetMLevel()); + // The level comparison and switch has been removed. if (BindBreakChance > tpzrand::GetRandomNumber(1000)) + { PDefender->StatusEffectContainer->DelStatusEffect(EFFECT_BIND); + } } } diff --git a/src/map/utils/blueutils.cpp b/src/map/utils/blueutils.cpp index 2d41d27d145..3e00201b044 100644 --- a/src/map/utils/blueutils.cpp +++ b/src/map/utils/blueutils.cpp @@ -50,24 +50,25 @@ void SetBlueSpell(CCharEntity* PChar, CBlueSpell* PSpell, uint8 slotIndex, bool if (slotIndex < 20) { if (dynamic_cast(PSpell)) { - if (addingSpell) + // Blue spells in SetBlueSpells must be 0x200 ofsetted so it's 1 byte per spell. + if (PChar->m_SetBlueSpells[slotIndex] != 0) { - if (!IsSpellSet(PChar, PSpell) && HasEnoughSetPoints(PChar, PSpell, slotIndex)) + CBlueSpell* POldSpell = (CBlueSpell*)spell::GetSpell(static_cast(PChar->m_SetBlueSpells[slotIndex] + 0x200)); + PChar->delModifiers(&POldSpell->modList); + PChar->m_SetBlueSpells[slotIndex] = 0x00; + } + if (addingSpell && !IsSpellSet(PChar, PSpell) && HasEnoughSetPoints(PChar, PSpell, slotIndex)) + { + uint16 spellID = static_cast(PSpell->getID()); + if (charutils::hasSpell(PChar, spellID)) { - // Blue spells in SetBlueSpells must be 0x200 ofsetted so it's 1 byte per spell. - if (PChar->m_SetBlueSpells[slotIndex] != 0) - { - CBlueSpell* POldSpell = (CBlueSpell*)spell::GetSpell(static_cast(PChar->m_SetBlueSpells[slotIndex] + 0x200)); - PChar->delModifiers(&POldSpell->modList); - } - PChar->m_SetBlueSpells[slotIndex] = static_cast(PSpell->getID()) - 0x200; - PChar->addModifiers(&PSpell->modList); + PChar->m_SetBlueSpells[slotIndex] = spellID - 0x200; + PChar->addModifiers(&PSpell->modList); + } + else + { + ShowExploit(CL_RED "SetBlueSpell: Player %s trying to set spell ID %u they don't have! \n" CL_RESET, PChar->GetName(), spellID); } - } - else - { - PChar->m_SetBlueSpells[slotIndex] = 0x00; - PChar->delModifiers(&PSpell->modList); } SaveSetSpells(PChar); }