| @@ -0,0 +1,5 @@ | |||
| 1 | + | <?xml version="1.0" encoding="UTF-8"?> | |
| 2 | + | <npc name="Muriel" walkinterval="2000" floorchange="0" script="default.lua" speechbubble="4" attackable = "1"> | |
| 3 | + | <health now="100" max="100"/> | |
| 4 | + | <look type="130" head="115" body="94" legs="97" feet="76" addons="0"/> | |
| 5 | + | </npc> |
| @@ -0,0 +1,5 @@ | |||
| 1 | + | <?xml version="1.0" encoding="UTF-8"?> | |
| 2 | + | <npc name="Naji" walkinterval="2000" floorchange="0" script="default.lua" speechbubble="4" attackable = "1"> | |
| 3 | + | <health now="100" max="100"/> | |
| 4 | + | <look type="129" head="57" body="113" legs="95" feet="113" addons="0"/> | |
| 5 | + | </npc> |
| @@ -0,0 +1,5 @@ | |||
| 1 | + | <?xml version="1.0" encoding="UTF-8"?> | |
| 2 | + | <npc name="Norf" walkinterval="2000" floorchange="0" script="default.lua" speechbubble="4" attackable = "1"> | |
| 3 | + | <health now="100" max="100"/> | |
| 4 | + | <look type="57"/> | |
| 5 | + | </npc> |
| @@ -0,0 +1,5 @@ | |||
| 1 | + | <?xml version="1.0" encoding="UTF-8"?> | |
| 2 | + | <npc name="Palomino" walkinterval="2000" floorchange="0" script="default.lua" speechbubble="4" attackable = "1"> | |
| 3 | + | <health now="100" max="100"/> | |
| 4 | + | <look type="128" head="116" body="39" legs="12" feet="97" addons="2"/> | |
| 5 | + | </npc> |
| @@ -0,0 +1,5 @@ | |||
| 1 | + | <?xml version="1.0" encoding="UTF-8"?> | |
| 2 | + | <npc name="Partos" walkinterval="2000" floorchange="0" script="default.lua" speechbubble="4" attackable = "1"> | |
| 3 | + | <health now="100" max="100"/> | |
| 4 | + | <look type="128" head="116" body="56" legs="95" feet="121" addons="0"/> | |
| 5 | + | </npc> |
| @@ -0,0 +1,5 @@ | |||
| 1 | + | <?xml version="1.0" encoding="UTF-8"?> | |
| 2 | + | <npc name="Robin" walkinterval="0" floorchange="0" speed="130" script="default.lua"> | |
| 3 | + | <health now="100" max="100"/> | |
| 4 | + | <look type="129" head="77" body="118" legs="118" feet="115" addons="0"/> | |
| 5 | + | </npc> |
| @@ -0,0 +1,5 @@ | |||
| 1 | + | <?xml version="1.0" encoding="UTF-8"?> | |
| 2 | + | <npc name="Sam" walkinterval="0" floorchange="0" script="default.lua"> | |
| 3 | + | <health now="100" max="100"/> | |
| 4 | + | <look type="131" head="38" body="113" legs="67" feet="95" addons="0"/> | |
| 5 | + | </npc> |
| @@ -0,0 +1,7 @@ | |||
| 1 | + | <?xml version="1.0" encoding="UTF-8"?> | |
| 2 | + | <npc name="Scott" walkinterval="0" floorchange="0" script="default.lua"> | |
| 3 | + | <health now="100" max="100"/> | |
| 4 | + | <look type="131" head="75" body="38" legs="77" feet="116" addons="0"/> | |
| 5 | + | <parameters> | |
| 6 | + | </parameters> | |
| 7 | + | </npc> |
| @@ -0,0 +1,10 @@ | |||
| 1 | + | local keywordHandler = KeywordHandler:new() | |
| 2 | + | local npcHandler = NpcHandler:new(keywordHandler) | |
| 3 | + | NpcSystem.parseParameters(npcHandler) | |
| 4 | + | ||
| 5 | + | function onCreatureAppear(cid) npcHandler:onCreatureAppear(cid) end | |
| 6 | + | function onCreatureDisappear(cid) npcHandler:onCreatureDisappear(cid) end | |
| 7 | + | function onCreatureSay(cid, type, msg) npcHandler:onCreatureSay(cid, type, msg) end | |
| 8 | + | function onThink() npcHandler:onThink() end | |
| 9 | + | ||
| 10 | + | npcHandler:addModule(FocusModule:new()) |
| @@ -0,0 +1,76 @@ | |||
| 1 | + | local keywordHandler = KeywordHandler:new() | |
| 2 | + | local npcHandler = NpcHandler:new(keywordHandler) | |
| 3 | + | NpcSystem.parseParameters(npcHandler) | |
| 4 | + | ||
| 5 | + | function onCreatureAppear(cid) npcHandler:onCreatureAppear(cid) end | |
| 6 | + | function onCreatureDisappear(cid) npcHandler:onCreatureDisappear(cid) end | |
| 7 | + | function onCreatureSay(cid, type, msg) npcHandler:onCreatureSay(cid, type, msg) end | |
| 8 | + | function onThink() npcHandler:onThink() end | |
| 9 | + | ||
| 10 | + | local function creatureSayCallback(cid, type, msg) | |
| 11 | + | if(not npcHandler:isFocused(cid)) then | |
| 12 | + | return false | |
| 13 | + | end | |
| 14 | + | local player = Player(cid) | |
| 15 | + | if msg:lower() == "discover" then | |
| 16 | + | npcHandler:say("Ssshh puff, ssshh puff, so ahem, ahem, sorry about that, you want {explore} the ocean and found lost treasures ?.", cid) | |
| 17 | + | elseif msg:lower() == "explore" then | |
| 18 | + | npcHandler:say("Before explore the ocean, you need a boat, let's construct a boat, for this you need some {materials}, some exquisite planks for the hull and deck, some nails to hold the boat together, metal fittings to reinforce and connect various components, a silk, important to stretch the sail of the boat, a small golden anchor to keep the boat in place when not sailing and some paint to protect the wood. Of course, you can buy all of this items to an old merchant. A whimsical merchant, an elusive ally, hoarding every ship part to sell to any daring enough to brave the tumultuous seas, called Shipwright Trader. You can found him on at north of Thais at exit.", cid) | |
| 19 | + | elseif msg:lower() == "materials" then | |
| 20 | + | npcHandler:say("Here is the list about what we need. You can found Shipwright Trader at north of Thais, come back when you get the materials for we start construction of the boat.\nFor Start we need some exquisite {planks}, for the hull and deck.", cid) | |
| 21 | + | player:addItem(7844, 1) | |
| 22 | + | elseif msg:lower() == "planks" then | |
| 23 | + | if player:removeItem(12503, 10) then | |
| 24 | + | npcHandler:say("Now for hold the boat together we need some {nails}", cid) | |
| 25 | + | player:setStorageValue(BoatMissions.MagnusBlackWaterFirst, 1) | |
| 26 | + | else | |
| 27 | + | npcHandler:say("For this you need 10 exquisite planks.", cid) | |
| 28 | + | end | |
| 29 | + | elseif msg:lower() == "nails" then | |
| 30 | + | if player:removeItem(8309, 10) and player:getStorageValue(BoatMissions.MagnusBlackWaterFirst) == 1 then | |
| 31 | + | npcHandler:say("We need also some extra {metal} fitting parts.", cid) | |
| 32 | + | player:setStorageValue(BoatMissions.MagnusBlackWaterSecond, 1) | |
| 33 | + | else | |
| 34 | + | npcHandler:say("You need 10 nails to continue the construction.", cid) | |
| 35 | + | end | |
| 36 | + | elseif msg:lower() == "metal" then | |
| 37 | + | if player:removeItem(10034, 5) and player:getStorageValue(BoatMissions.MagnusBlackWaterSecond) == 1 then | |
| 38 | + | npcHandler:say("Great, to continue we need one exquisite {silk} to connect various components.", cid) | |
| 39 | + | player:setStorageValue(BoatMissions.MagnusBlackWaterThird, 1) | |
| 40 | + | else | |
| 41 | + | npcHandler:say("You need 5 mettal fitting to reinforce the boat.", cid) | |
| 42 | + | end | |
| 43 | + | elseif msg:lower() == "silk" then | |
| 44 | + | if player:removeItem(12501, 1) and player:getStorageValue(BoatMissions.MagnusBlackWaterThird) == 1 then | |
| 45 | + | npcHandler:say("It's taking a while to construct the boat, is not so easy, well now we need a small golden {anchor} to keep the boat in place.", cid) | |
| 46 | + | player:setStorageValue(BoatMissions.MagnusBlackWaterFourth, 1) | |
| 47 | + | else | |
| 48 | + | npcHandler:say("You need 1 Silk to connect various components.", cid) | |
| 49 | + | end | |
| 50 | + | elseif msg:lower() == "anchor" then | |
| 51 | + | if player:removeItem(15432, 1) and player:getStorageValue(BoatMissions.MagnusBlackWaterFourth) == 1 then | |
| 52 | + | npcHandler:say("Perfect, now the last thing we need some red {paint} to protect the wood.", cid) | |
| 53 | + | player:setStorageValue(BoatMissions.MagnusBlackWaterFifth, 1) | |
| 54 | + | else | |
| 55 | + | npcHandler:say("You need 1 small golden anchor to keep the boat in place.", cid) | |
| 56 | + | end | |
| 57 | + | elseif msg:lower() == "paint" then | |
| 58 | + | if player:getStorageValue(BoatMissions.MagnusBlackWaterSixth) == 1 then | |
| 59 | + | npcHandler:say("You already have construct your boat.", cid) | |
| 60 | + | elseif player:removeItem(25308, 1) and player:getStorageValue(BoatMissions.MagnusBlackWaterFifth) == 1 then | |
| 61 | + | npcHandler:say("Fantastic! The boat is now ready, to start navigating, place the explorer to the boat slot on your inventory and walk through the deck that leads to the water.", cid) | |
| 62 | + | player:say("Get ready to explore the ocean. Equip the explorer on the Boat Slot of your inventory, and walk to the ocean through the deck that leads to the water.", TALKTYPE_MONSTER_SAY) | |
| 63 | + | player:setStorageValue(BoatMissions.MagnusBlackWaterSixth, 1) | |
| 64 | + | player:addItem(27799, 1) | |
| 65 | + | else | |
| 66 | + | npcHandler:say("You need 1 Red Paint to protect the wood.", cid) | |
| 67 | + | end | |
| 68 | + | end | |
| 69 | + | return true | |
| 70 | + | end | |
| 71 | + | ||
| 72 | + | npcHandler:setMessage(MESSAGE_GREET, "Psst.... Come here... You want {discover} the vast ocean and found treasures ?.") | |
| 73 | + | npcHandler:setMessage(MESSAGE_FAREWELL, "Venture forth, my hearty crew! The boundless ocean awaits, where treasures lie hidden beneath each cresting wave. Hoist the sails high and let the winds of fortune guide our course to riches beyond measure!") | |
| 74 | + | npcHandler:setMessage(MESSAGE_WALKAWAY, "Psst.... Come here... You want {discover} the vast ocean and found treasures ?") | |
| 75 | + | npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback) | |
| 76 | + | npcHandler:addModule(FocusModule:new()) |
| @@ -0,0 +1,5 @@ | |||
| 1 | + | <?xml version="1.0" encoding="UTF-8"?> | |
| 2 | + | <npc name="Stutch" walkinterval="0" floorchange="0" speed="130" script="default.lua"> | |
| 3 | + | <health now="100" max="100"/> | |
| 4 | + | <look type="131" head="79" body="79" legs="79" feet="79" addons="0"/> | |
| 5 | + | </npc> |
| @@ -0,0 +1,5 @@ | |||
| 1 | + | <?xml version="1.0" encoding="UTF-8"?> | |
| 2 | + | <npc name="Thanita" script="default.lua" walkinterval="0" floorchange="0"> | |
| 3 | + | <health now="100" max="100" /> | |
| 4 | + | <look type="139" head="77" body="71" legs="82" feet="71" addons="0" /> | |
| 5 | + | </npc> |
| @@ -0,0 +1,5 @@ | |||
| 1 | + | <?xml version="1.0" encoding="UTF-8"?> | |
| 2 | + | <npc name="Topsy" walkinterval="0" floorchange="0" script="default.lua"> | |
| 3 | + | <health now="100" max="100"/> | |
| 4 | + | <look type="139" head="78" body="52" legs="100" feet="115" addons="0"/> | |
| 5 | + | </npc> |
| @@ -0,0 +1,5 @@ | |||
| 1 | + | <?xml version="1.0" encoding="UTF-8"?> | |
| 2 | + | <npc name="Towncryer" walkinterval="0" floorchange="0" script="default.lua"> | |
| 3 | + | <health now="100" max="100"/> | |
| 4 | + | <look type="131" head="95" body="86" legs="10" feet="114" addons="1"/> | |
| 5 | + | </npc> |
| @@ -0,0 +1,5 @@ | |||
| 1 | + | <?xml version="1.0" encoding="utf-8"?> | |
| 2 | + | <npc name="Victor" walkinterval="0" floorchange="0" script="default.lua"> | |
| 3 | + | <health now="100" max="100"/> | |
| 4 | + | <look type="134" head="21" body="78" legs="38" feet="95" addons="3"/> | |
| 5 | + | </npc> |
| @@ -0,0 +1,5 @@ | |||
| 1 | + | <?xml version="1.0" encoding="UTF-8"?> | |
| 2 | + | <npc name="Wyat" walkinterval="0" floorchange="0" speed="130" script="default.lua"> | |
| 3 | + | <health now="100" max="100"/> | |
| 4 | + | <look type="129" head="98" body="96" legs="95" feet="116" addons="0"/> | |
| 5 | + | </npc> |
| @@ -0,0 +1,7 @@ | |||
| 1 | + | <?xml version="1.0" encoding="UTF-8"?> | |
| 2 | + | <raids> | |
| 3 | + | <!-- | |
| 4 | + | <raid name="Testraid" file="testraid.xml" interval2="30" margin="0" /> | |
| 5 | + | executed on average once every 30 minutes | |
| 6 | + | --> | |
| 7 | + | </raids> |
| @@ -0,0 +1,35 @@ | |||
| 1 | + | Name: Sonnyz | |
| 2 | + | Ter�a, 8 de Fevereiro, as 16:34 | |
| 3 | + | Tipo: Fazendo Pedido [ID: 1] | |
| 4 | + | O jogador [ Felipe da Silva Oliveira ] realizou um pedido de pagamento no valor de [ R$ 5.76 ] usando [ 96 ] criptomoedas sob o pre�o fixo de [ 0.06 ] e estando no Level [ 103 (0.06)]. | |
| 5 | + | ||
| 6 | + | ------------------------------------------------ | |
| 7 | + | ||
| 8 | + | Name: Sonnyz | |
| 9 | + | Ter�a, 8 de Fevereiro, as 16:34 | |
| 10 | + | Tipo: Editando Pedido [ID: 1] | |
| 11 | + | O jogador [ Felipe da Silva Oliveira ] alterou um pedido de pagamento de [ R$ 5.76 para R$ 5.7 ] gastando [ 95 ] criptomoedas. Seu saldo final de criptomoedas � de [ 1 ]. | |
| 12 | + | ||
| 13 | + | ------------------------------------------------ | |
| 14 | + | ||
| 15 | + | Name: Sonnyz | |
| 16 | + | Ter�a, 8 de Fevereiro, as 16:35 | |
| 17 | + | Tipo: Editando Pedido [ID: 1] | |
| 18 | + | O jogador [ Felipe da Silva Oliveira ] alterou um pedido de pagamento de [ R$ 5.7 para R$ 1.44 ] gastando [ 24 ] criptomoedas. Seu saldo final de criptomoedas � de [ 72 ]. | |
| 19 | + | ||
| 20 | + | ------------------------------------------------ | |
| 21 | + | ||
| 22 | + | Name: Sonnyz | |
| 23 | + | Ter�a, 8 de Fevereiro, as 16:36 | |
| 24 | + | Tipo: Editando Pedido [ID: 1] | |
| 25 | + | O jogador [ Felipe da Silva Oliveira ] alterou um pedido de pagamento de [ R$ 1.44 para R$ 5.76 ] gastando [ 96 ] criptomoedas. Seu saldo final de criptomoedas � de [ 0 ]. | |
| 26 | + | ||
| 27 | + | ------------------------------------------------ | |
| 28 | + | ||
| 29 | + | Name: Frenesy | |
| 30 | + | S�bado, 19 de Fevereiro, as 22:37 | |
| 31 | + | Tipo: Fazendo Pedido [ID: 2] | |
| 32 | + | O jogador [ Felipe da Silva Oliveira ] realizou um pedido de pagamento no valor de [ R$ 13.14 ] usando [ 219 ] criptomoedas sob o pre�o fixo de [ 0.06 ] e estando no Level [ 150 (0.06)]. | |
| 33 | + | ||
| 34 | + | ------------------------------------------------ | |
| 35 | + |
| @@ -0,0 +1,27 @@ | |||
| 1 | + | --CREATURESCRIPT-- | |
| 2 | + | ||
| 3 | + | creatureEvent = CreatureEvent("LoginSpellBar") | |
| 4 | + | ||
| 5 | + | function creatureEvent.onLogin(player) | |
| 6 | + | ||
| 7 | + | player:registerEvent("CreateSpellBar") | |
| 8 | + | ||
| 9 | + | return true | |
| 10 | + | end | |
| 11 | + | ||
| 12 | + | creatureEvent:register() | |
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | creatureEvent = CreatureEvent("CreateSpellBar") | |
| 17 | + | ||
| 18 | + | function creatureEvent.onExtendedOpcode(player, opcode, buffer) | |
| 19 | + | ||
| 20 | + | if opcode == 193 then | |
| 21 | + | player:sendExtendedOpcode(193, ''..player:getVocation():getId()..'') | |
| 22 | + | end | |
| 23 | + | ||
| 24 | + | return true | |
| 25 | + | end | |
| 26 | + | ||
| 27 | + | creatureEvent:register() |
| @@ -0,0 +1,28 @@ | |||
| 1 | + | local config = { | |
| 2 | + | [8888] = {wingId = 1, effect = CONST_ME_FIREWORK_RED, storage = 2929}, -- ItemID in [8888] | |
| 3 | + | [8887] = {wingId = 2, effect = CONST_ME_FIREWORK_RED, storage = 2930}, | |
| 4 | + | } | |
| 5 | + | ||
| 6 | + | local addWingsSystem = Action() | |
| 7 | + | ||
| 8 | + | function addWingsSystem.onUse(player, item, fromPosition, target, toPosition, isHotkey) | |
| 9 | + | local wings = config[item.itemid] | |
| 10 | + | if not wings then | |
| 11 | + | return true | |
| 12 | + | end | |
| 13 | + | if player:getStorageValue(wings.storage) == 1 then | |
| 14 | + | player:sendTextMessage(MESSAGE_INFO_DESCR, 'You alredy received those wings!') | |
| 15 | + | return true | |
| 16 | + | end | |
| 17 | + | player:addWings(wings.wingId) | |
| 18 | + | player:setStorageValue(wings.storage, 1) | |
| 19 | + | player:getPosition():sendMagicEffect(wings.effect) | |
| 20 | + | player:sendTextMessage(MESSAGE_INFO_DESCR, 'You received new wings!') | |
| 21 | + | player:removeItem() | |
| 22 | + | return true | |
| 23 | + | end | |
| 24 | + | ||
| 25 | + | for v, _ in pairs(config) do | |
| 26 | + | addWingsSystem:id(v) | |
| 27 | + | end | |
| 28 | + | addWingsSystem:register() |
| @@ -0,0 +1,75 @@ | |||
| 1 | + | local config = { | |
| 2 | + | maxClones = 3, -- Número máximo de clones que podem ser criados | |
| 3 | + | healthPercent = 0.10, -- Porcentagem da vida do jogador que o clone terá (10%) | |
| 4 | + | spellName = "Clone das Sombras", -- Nome fixo da magia (não altere esta linha) | |
| 5 | + | words = "utamo clone", -- Nome da magia, pode ser alterado para o nome preferido (ex.: diga no jogo "utamo clone") | |
| 6 | + | cooldown = 30, -- Tempo de espera em segundos para usar novamente | |
| 7 | + | manaCost = 500, -- Quantidade de mana necessária para usar | |
| 8 | + | minLevel = 20, -- Nível mínimo necessário para usar a magia | |
| 9 | + | monsterName = "clone", -- Nome do monstro base que será usado como clone | |
| 10 | + | exhaustGroup = "support", -- Grupo de exaustão da magia | |
| 11 | + | -- Lista de vocações que podem usar a magia | |
| 12 | + | vocations = { | |
| 13 | + | "Soldier", "elder Soldier", | |
| 14 | + | "Regen", "master Regen", | |
| 15 | + | "Shooter", "royal Shooter", | |
| 16 | + | "vital", "royal vital", | |
| 17 | + | "lucky", "royal lucky", | |
| 18 | + | "Tanker", "elite Tanker" | |
| 19 | + | }, | |
| 20 | + | needPremium = false -- Define se precisa ser conta premium para usar | |
| 21 | + | } | |
| 22 | + | ||
| 23 | + | local spell = Spell("instant") | |
| 24 | + | ||
| 25 | + | function spell.onCastSpell(creature, variant) | |
| 26 | + | local player = creature:getPlayer() | |
| 27 | + | if not player then | |
| 28 | + | return false | |
| 29 | + | end | |
| 30 | + | ||
| 31 | + | local summons = player:getSummons() | |
| 32 | + | if #summons >= config.maxClones then | |
| 33 | + | player:sendCancelMessage("Você já atingiu o número máximo de clones permitidos!") | |
| 34 | + | return false | |
| 35 | + | end | |
| 36 | + | ||
| 37 | + | local remainingClones = config.maxClones - #summons | |
| 38 | + | local playerHealth = player:getHealth() | |
| 39 | + | local playerOutfit = player:getOutfit() | |
| 40 | + | local playerSpeed = player:getSpeed() | |
| 41 | + | ||
| 42 | + | for i = 1, remainingClones do | |
| 43 | + | local clone = Game.createMonster(config.monsterName, player:getPosition()) | |
| 44 | + | if clone then | |
| 45 | + | clone:setMaster(player) | |
| 46 | + | clone:rename(player:getName()) | |
| 47 | + | ||
| 48 | + | player:addSummon(clone) | |
| 49 | + | ||
| 50 | + | local cloneHealth = math.ceil(playerHealth * config.healthPercent) | |
| 51 | + | clone:setMaxHealth(cloneHealth) | |
| 52 | + | clone:addHealth(cloneHealth) | |
| 53 | + | ||
| 54 | + | clone:setOutfit(playerOutfit) | |
| 55 | + | clone:changeSpeed(playerSpeed) | |
| 56 | + | end | |
| 57 | + | end | |
| 58 | + | ||
| 59 | + | return true | |
| 60 | + | end | |
| 61 | + | ||
| 62 | + | spell:name(config.spellName) | |
| 63 | + | spell:words(config.words) | |
| 64 | + | spell:group(config.exhaustGroup) | |
| 65 | + | spell:vocation(unpack(config.vocations)) | |
| 66 | + | spell:id(24) | |
| 67 | + | spell:cooldown(config.cooldown * 1000) | |
| 68 | + | spell:groupCooldown(2 * 1000) | |
| 69 | + | spell:level(config.minLevel) | |
| 70 | + | spell:mana(config.manaCost) | |
| 71 | + | spell:isSelfTarget(true) | |
| 72 | + | spell:isAggressive(false) | |
| 73 | + | spell:needLearn(false) | |
| 74 | + | spell:isPremium(config.needPremium) | |
| 75 | + | spell:register() |
| @@ -0,0 +1,63 @@ | |||
| 1 | + | -- Left-click on the minimap teleports gamemasters and above. | |
| 2 | + | -- The client (gamelib/ui/uiminimap.lua) only sends the request after the | |
| 3 | + | -- server says on login that this player is allowed to use it. | |
| 4 | + | local OPCODE_ADMIN_MINIMAP_TELEPORT = 215 | |
| 5 | + | ||
| 6 | + | local function canUseMinimapTeleport(player) | |
| 7 | + | return player:getGroup():getAccess() | |
| 8 | + | end | |
| 9 | + | ||
| 10 | + | local loginEvent = CreatureEvent("AdminMinimapTeleportLogin") | |
| 11 | + | ||
| 12 | + | function loginEvent.onLogin(player) | |
| 13 | + | player:registerEvent("AdminMinimapTeleportOpcode") | |
| 14 | + | player:sendExtendedOpcode(OPCODE_ADMIN_MINIMAP_TELEPORT, json.encode({ | |
| 15 | + | action = "access", | |
| 16 | + | enabled = canUseMinimapTeleport(player) | |
| 17 | + | })) | |
| 18 | + | return true | |
| 19 | + | end | |
| 20 | + | ||
| 21 | + | loginEvent:register() | |
| 22 | + | ||
| 23 | + | local opcodeEvent = CreatureEvent("AdminMinimapTeleportOpcode") | |
| 24 | + | ||
| 25 | + | function opcodeEvent.onExtendedOpcode(player, opcode, buffer) | |
| 26 | + | if opcode ~= OPCODE_ADMIN_MINIMAP_TELEPORT then | |
| 27 | + | return true | |
| 28 | + | end | |
| 29 | + | ||
| 30 | + | if not canUseMinimapTeleport(player) then | |
| 31 | + | return true | |
| 32 | + | end | |
| 33 | + | ||
| 34 | + | local ok, data = pcall(json.decode, buffer) | |
| 35 | + | if not ok or type(data) ~= "table" or data.action ~= "teleport" or type(data.position) ~= "table" then | |
| 36 | + | return true | |
| 37 | + | end | |
| 38 | + | ||
| 39 | + | local x = tonumber(data.position.x) | |
| 40 | + | local y = tonumber(data.position.y) | |
| 41 | + | local z = tonumber(data.position.z) | |
| 42 | + | if not x or not y or not z then | |
| 43 | + | return true | |
| 44 | + | end | |
| 45 | + | ||
| 46 | + | local destination = Position(math.floor(x), math.floor(y), math.floor(z)) | |
| 47 | + | if not Tile(destination) then | |
| 48 | + | player:sendCancelMessage("There is no tile there.") | |
| 49 | + | return true | |
| 50 | + | end | |
| 51 | + | ||
| 52 | + | local ghost = player:isInGhostMode() | |
| 53 | + | if not ghost then | |
| 54 | + | player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) | |
| 55 | + | end | |
| 56 | + | player:teleportTo(destination) | |
| 57 | + | if not ghost then | |
| 58 | + | destination:sendMagicEffect(CONST_ME_TELEPORT) | |
| 59 | + | end | |
| 60 | + | return true | |
| 61 | + | end | |
| 62 | + | ||
| 63 | + | opcodeEvent:register() |
| @@ -0,0 +1,13 @@ | |||
| 1 | + | local attackAnimationOutfitId = 1423 | |
| 2 | + | local attackAnimationTotalTick = 1200 | |
| 3 | + | local attackAnimationDamageTime = 450 | |
| 4 | + | ||
| 5 | + | local damageMin = 100 | |
| 6 | + | local damageMax = 200 | |
| 7 | + | ||
| 8 | + | function onCastSpell(cid) | |
| 9 | + | local originalOutfit = cid:getOutfit() | |
| 10 | + | cid:setOutfit(attackAnimationOutfitId) | |
| 11 | + | addEvent(function() cid:setOutfit(originalOutfit) end, attackAnimationTotalTick) | |
| 12 | + | addEvent(function() doCombatDamage(cid, COMBAT_PHYSICALDAMAGE, damageMin, damageMax) end, attackAnimationDamageTime) | |
| 13 | + | end |
| @@ -0,0 +1,131 @@ | |||
| 1 | + | -- ===================================================== | |
| 2 | + | -- BOAT NAVIGATION SYSTEM | |
| 3 | + | -- AID 40000 = Embark (dock tile -> water) | |
| 4 | + | -- AID 40001 = Disembark (water tile -> dock) | |
| 5 | + | -- ===================================================== | |
| 6 | + | ||
| 7 | + | -- Helper: apply boat outfit and store original | |
| 8 | + | local function applyBoatOutfit(player, item, position, fromPosition) | |
| 9 | + | local playerOutfit = player:getOutfit() | |
| 10 | + | ||
| 11 | + | -- Store current outfit | |
| 12 | + | player:setStorageValue(709919, playerOutfit.lookType) | |
| 13 | + | player:setStorageValue(709920, playerOutfit.lookAddons) | |
| 14 | + | player:setStorageValue(709921, playerOutfit.lookWings or 0) | |
| 15 | + | player:setStorageValue(709922, playerOutfit.lookAura or 0) | |
| 16 | + | player:setStorageValue(709923, playerOutfit.lookShader or 0) | |
| 17 | + | ||
| 18 | + | -- Check equipped ship using the custom inventory storage system | |
| 19 | + | -- _SYSTEM.INVENTARIO[07] = RING slot, UID_01 = storage 1061 | |
| 20 | + | local shipId = player:getStorageValue(_SYSTEM.INVENTARIO[07].UID_01) | |
| 21 | + | ||
| 22 | + | local boatLookType = 0 | |
| 23 | + | if shipId == 42430 then boatLookType = 9017 | |
| 24 | + | elseif shipId == 42431 then boatLookType = 9018 | |
| 25 | + | elseif shipId == 42432 then boatLookType = 9019 | |
| 26 | + | elseif shipId == 42433 then boatLookType = 9541 | |
| 27 | + | elseif shipId == 42434 then boatLookType = 9542 | |
| 28 | + | elseif shipId == 42435 then boatLookType = 9543 | |
| 29 | + | elseif shipId == 42436 then boatLookType = 9544 | |
| 30 | + | end | |
| 31 | + | ||
| 32 | + | if boatLookType > 0 then | |
| 33 | + | playerOutfit.lookType = boatLookType | |
| 34 | + | playerOutfit.lookAddons = 0 | |
| 35 | + | playerOutfit.lookWings = 0 | |
| 36 | + | playerOutfit.lookAura = 0 | |
| 37 | + | playerOutfit.lookShader = 0 | |
| 38 | + | player:setOutfit(playerOutfit) | |
| 39 | + | return true | |
| 40 | + | end | |
| 41 | + | ||
| 42 | + | return false | |
| 43 | + | end | |
| 44 | + | ||
| 45 | + | -- Helper: restore original outfit | |
| 46 | + | local function restoreOutfit(player) | |
| 47 | + | local storedLookType = player:getStorageValue(709919) | |
| 48 | + | local storedLookAddons = player:getStorageValue(709920) | |
| 49 | + | local storedLookWings = player:getStorageValue(709921) | |
| 50 | + | local storedLookAura = player:getStorageValue(709922) | |
| 51 | + | local storedLookShader = player:getStorageValue(709923) | |
| 52 | + | ||
| 53 | + | if storedLookType > 0 then | |
| 54 | + | local playerOutfit = player:getOutfit() | |
| 55 | + | playerOutfit.lookType = storedLookType | |
| 56 | + | playerOutfit.lookAddons = storedLookAddons | |
| 57 | + | playerOutfit.lookWings = storedLookWings | |
| 58 | + | playerOutfit.lookAura = storedLookAura | |
| 59 | + | playerOutfit.lookShader = storedLookShader | |
| 60 | + | player:setOutfit(playerOutfit) | |
| 61 | + | end | |
| 62 | + | end | |
| 63 | + | ||
| 64 | + | -- ===================================================== | |
| 65 | + | -- TELEPORT EVENT (AID 40000 = embark / AID 40001 = disembark) | |
| 66 | + | -- ===================================================== | |
| 67 | + | local boatTeleport = MoveEvent() | |
| 68 | + | ||
| 69 | + | function boatTeleport.onStepIn(creature, item, position, fromPosition) | |
| 70 | + | local player = creature:getPlayer() | |
| 71 | + | if not player then | |
| 72 | + | return true | |
| 73 | + | end | |
| 74 | + | ||
| 75 | + | local actionId = item:getActionId() | |
| 76 | + | ||
| 77 | + | -- Prevent trigger if player was already on a tile with the same AID | |
| 78 | + | -- (avoids loop when teleporting onto another AID tile) | |
| 79 | + | local fromTile = Tile(fromPosition) | |
| 80 | + | if fromTile then | |
| 81 | + | local items = fromTile:getItems() | |
| 82 | + | if items then | |
| 83 | + | for _, i in ipairs(items) do | |
| 84 | + | if i:getActionId() == actionId then | |
| 85 | + | return true | |
| 86 | + | end | |
| 87 | + | end | |
| 88 | + | end | |
| 89 | + | end | |
| 90 | + | ||
| 91 | + | -- Calculate movement direction (1 step = 1 tile difference) | |
| 92 | + | local diffX = position.x - fromPosition.x | |
| 93 | + | local diffY = position.y - fromPosition.y | |
| 94 | + | ||
| 95 | + | if diffX == 0 and diffY == 0 then | |
| 96 | + | return true | |
| 97 | + | end | |
| 98 | + | ||
| 99 | + | -- Project destination 4 tiles ahead in walking direction | |
| 100 | + | local targetPos = Position( | |
| 101 | + | position.x + (diffX * 4), | |
| 102 | + | position.y + (diffY * 4), | |
| 103 | + | position.z | |
| 104 | + | ) | |
| 105 | + | ||
| 106 | + | if actionId == 40000 then | |
| 107 | + | -- EMBARK: verify boat equipped, apply outfit, teleport to water (Z+1) | |
| 108 | + | if not applyBoatOutfit(player, item, position, fromPosition) then | |
| 109 | + | player:teleportTo(fromPosition, true) | |
| 110 | + | player:getPosition():sendMagicEffect(CONST_ME_TELEPORT) | |
| 111 | + | player:say("You need to equip a boat in your ring slot to sail.", TALKTYPE_MONSTER_SAY) | |
| 112 | + | return false | |
| 113 | + | end | |
| 114 | + | ||
| 115 | + | targetPos.z = position.z + 1 | |
| 116 | + | player:teleportTo(targetPos) | |
| 117 | + | targetPos:sendMagicEffect(CONST_ME_TELEPORT) | |
| 118 | + | ||
| 119 | + | elseif actionId == 40001 then | |
| 120 | + | -- DISEMBARK: restore outfit and teleport to dock (Z-1) | |
| 121 | + | restoreOutfit(player) | |
| 122 | + | targetPos.z = position.z - 1 | |
| 123 | + | player:teleportTo(targetPos) | |
| 124 | + | targetPos:sendMagicEffect(CONST_ME_TELEPORT) | |
| 125 | + | end | |
| 126 | + | ||
| 127 | + | return true | |
| 128 | + | end | |
| 129 | + | ||
| 130 | + | boatTeleport:aid(40000, 40001) | |
| 131 | + | boatTeleport:register() |