diff --git a/docs/mychanges_ptbr.txt b/docs/mychanges_ptbr.txt index 47aa8d5567..0a5cbcaa5c 100644 --- a/docs/mychanges_ptbr.txt +++ b/docs/mychanges_ptbr.txt @@ -842,10 +842,20 @@ Corrigido autoassigner para piratas somente preenchendo requisitos de gunslinger Mobs agora podem dropar mais de um equip de mesmo tipo, sistema utiliza quantidades min/max na tabela de drops. Nova ferramenta: MapleQuestItemCountFetcher. Localiza no Quest.wz possíveis locais onde um item deveria estar sendo tirado ao completar determinada quest, mas não o é (caso comum onde campo "count" não existe). -28 - 29 Março 2018, +28 - 30 Março 2018, Adicionado itens de pirata nas lojas de NPC em Singapore. Adicionado drop data para Flaming Racoon e Big Cloud Fox. Adicionado ganho de EXP em várias quests da região de Zipangu. Adicionado sistema de bonus para expedição de Showa, atingível se nenhum jogador morrer no evento. Nova ferramenta: MapleBossHpBarFetcher. Localiza no Mob.wz ids de mobs que possuem um boss hp bar mas não são labelados como "boss". -Incrementado quiz de 3rd job, agora utilizando um pool de 40 perguntas com escolha arbitrária. \ No newline at end of file +Incrementado quiz de 3rd job, agora utilizando um pool de 40 perguntas com escolha arbitrária. + +31 Março 2018, +Resolvido bug com diseases ao trocar de canais/entrar Cash Shop. + +05 Março 2018, +Corrigido Holy Symbol atuando descomunalmente para um jogador. +Corrigido sistema tirando mesos do jogador que tenta expandir guild com capacidade máxima alcançada. + +06 - 10 Março 2018, +Implementado todo o sistema de marriages (rings, relacionamentos, spouse chat, etc). \ No newline at end of file diff --git a/scripts/event/AmoriaPQ.js b/scripts/event/AmoriaPQ.js index 0d3c85218a..46ea46a64b 100644 --- a/scripts/event/AmoriaPQ.js +++ b/scripts/event/AmoriaPQ.js @@ -135,8 +135,19 @@ function setup(level, lobbyid) { return eim; } -function isTeamAllCouple(eim) { // all players married each other, not implemented - return false; +function isTeamAllCouple(eim) { + var eventPlayers = eim.getPlayers(); + + for (var iterator = eventPlayers.iterator(); iterator.hasNext();) { + var chr = iterator.next(); + + var pid = chr.getPartnerId(); + if(pid <= 0 || eim.getPlayerById(pid) == null) { + return false; + } + } + + return true; } function afterSetup(eim) { diff --git a/scripts/event/CathedralWedding.js b/scripts/event/CathedralWedding.js deleted file mode 100644 index cf61b0b07c..0000000000 --- a/scripts/event/CathedralWedding.js +++ /dev/null @@ -1,194 +0,0 @@ -/* - This file is part of the OdinMS Maple Story Server - Copyright (C) 2008 Patrick Huy - Matthias Butz - Jan Christian Meyer - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as - published by the Free Software Foundation version 3 as published by - the Free Software Foundation. You may not use, modify or distribute - this program under any other version of the GNU Affero General Public - License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ -/* - * @Author Jvlaple - * - * Wedding for odinMS - */ -importPackage(java.lang); - -importPackage(Packages.world); -importPackage(Packages.client); -importPackage(Packages.server.maps); - -var exitMap; -var altarMap; -var cakeMap; -var instanceId; -var minPlayers = 1; - -function init() { - exitMap = em.getChannelServer().getMapFactory().getMap(680000500); //Teh exit map :) <---------t - altarMap = em.getChannelServer().getMapFactory().getMap(680000210); //Teh altar map - cakeMap = em.getChannelServer().getMapFactory().getMap(680000300); //Teh cake - instanceId = 1; -} - -function monsterValue(eim, mobId) { - return 1; -} - - - -function setup(eim) { - var instanceName = "CathedralWedding" + instanceId; - var eim = em.newInstance(instanceName); - instanceId++; - - var eim = em.newInstance(instanceName); - - var mf = eim.getMapFactory(); - - - var map = mf.getMap(680000200);//wutt - //Lets make the clock continue through all maps xD - em.schedule("playerAltar", 3 * 60000); - eim.setProperty("hclicked", 0); - eim.setProperty("wclicked", 0); - eim.setProperty("entryTimestamp",System.currentTimeMillis() + (3 * 60000)); - - return eim; -} - -function afterSetup(eim) {} - -function playerEntry(eim, player) { - var map = eim.getMapInstance(680000200); - player.changeMap(map, map.getPortal(0)); - - //1st - 20 min 2nd - 5 min 3rd 5 min xD - //player.getClient().getSession().write(net.sf.odinms.tools.MaplePacketCreator.getClock(1200)); - //player.getClient().getSession().write(net.sf.odinms.tools.MaplePacketCreator.getClock(180)); - player.getClient().getSession().write(net.sf.odinms.tools.MaplePacketCreator.getClock((Long.parseLong(eim.getProperty("entryTimestamp")) - System.currentTimeMillis()) / 1000)); -} - -//lets forget this bullshit... -function playerDead(eim, player) { -} - -function playerRevive(eim, player) { -//how the fuck can this happen? o.O -} - -function playerDisconnected(eim, player) { - playerExit(eim, player);//kick him/her -} - -function leftParty(eim, player) { //this doesnt fucking matter... -} - -function disbandParty(eim) { -} - -function playerUnregistered(eim, player) {} - -function playerExit(eim, player) { - eim.unregisterPlayer(player); - player.changeMap(exitMap, exitMap.getPortal(0)); -} - -function playerWarpAltar(eim, player) { - if ((player.getName() != eim.getProperty("husband")) && (player.getName() != eim.getProperty("wife"))){ - player.changeMap(altarMap, altarMap.getPortal(0)); - player.getClient().getSession().write(net.sf.odinms.tools.MaplePacketCreator.getClock(300)); - }else{ - player.changeMap(altarMap, altarMap.getPortal(2)); - player.getClient().getSession().write(net.sf.odinms.tools.MaplePacketCreator.getClock(300)); - player.getClient().getSession().write(net.sf.odinms.tools.MaplePacketCreator.serverNotice(6, "Please talk to High Priest John now!")); - } -} - -function playerWarpCake(eim, player) { - player.changeMap(cakeMap, cakeMap.getPortal(0)); - player.getClient().getSession().write(net.sf.odinms.tools.MaplePacketCreator.getClock(300)); -} - -function playerAltar(eim, player) { - var iter = em.getInstances().iterator(); - while (iter.hasNext()) { - var eim = iter.next(); - if (eim.getPlayerCount() > 0) { - var pIter = eim.getPlayers().iterator(); - while (pIter.hasNext()) { - playerWarpAltar(eim, pIter.next()); - } - } - em.schedule("playerCake", 5 * 60000); - //eim.dispose(); - } -} - -function playerCake(eim, player) { - var iter = em.getInstances().iterator(); - while (iter.hasNext()) { - var eim = iter.next(); - if (eim.getPlayerCount() > 0) { - var pIter = eim.getPlayers().iterator(); - while (pIter.hasNext()) { - playerWarpCake(eim, pIter.next()); - } - } - em.schedule("timeOut", 5 * 60000); - //eim.dispose(); - } -} - -//Those offline cuntts -function removePlayer(eim, player) { - eim.unregisterPlayer(player); - player.getMap().removePlayer(player); - player.setMap(exitMap); -} - -function clearPQ(eim) { - //Wedding? IDK about gifts o.O - var party = eim.getPlayers(); - for (var i = 0; i < party.size(); i++) { - playerExit(eim, party.get(i)); - } - eim.dispose(); -} - -function monsterKilled(mob, eim) {} - -function allMonstersDead(eim) {} - -function cancelSchedule() {} - -function timeOut() { - var iter = em.getInstances().iterator(); - while (iter.hasNext()) { - var eim = iter.next(); - if (eim.getPlayerCount() > 0) { - var pIter = eim.getPlayers().iterator(); - while (pIter.hasNext()) { - playerExit(eim, pIter.next()); - } - } - eim.dispose(); - } -} - - -function dispose() { - -} \ No newline at end of file diff --git a/scripts/event/WeddingCathedral.js b/scripts/event/WeddingCathedral.js new file mode 100644 index 0000000000..80dd248669 --- /dev/null +++ b/scripts/event/WeddingCathedral.js @@ -0,0 +1,252 @@ +/** + * @author: Ronan + * @event: Cathedral Wedding +*/ + +var entryMap = 680000200; +var exitMap = 680000500; +var recruitMap = 680000000; +var clearMap = 680000500; + +var minMapId = 680000100; +var maxMapId = 680000401; + +var startMsgTime = 4; +var blessMsgTime = 5; + +var eventTime = 10; // 10 minutes gathering +var ceremonyTime = 20; // 20 minutes ceremony +var blessingsTime = 15;// blessings are held until the 15th minute from the ceremony start +var partyTime = 45; // 45 minutes party + +var forceHideMsgTime = 20; // unfortunately, EIM weddings don't send wedding talk packets to the server... this will need to suffice + +var eventBoss = true; // spawns a Cake boss at the hunting ground +var isCathedral = true; + +var lobbyRange = [0, 0]; + +function init() {} + +function setLobbyRange() { + return lobbyRange; +} + +function setEventExclusives(eim) { + var itemSet = [4031217, 4000313]; // golden key, golden maple leaf + eim.setExclusiveItems(itemSet); +} + +function setEventRewards(eim) { + var itemSet, itemQty, evLevel, expStages; + + evLevel = 1; //Rewards at clear PQ + itemSet = []; + itemQty = []; + eim.setEventRewards(evLevel, itemSet, itemQty); + + expStages = []; //bonus exp given on CLEAR stage signal + eim.setEventClearStageExp(expStages); +} + +function spawnCakeBoss(eim) { + var mapObj = eim.getMapInstance(680000400); + var mobObj = Packages.server.life.MapleLifeFactory.getMonster(9400606); + + mapObj.spawnMonsterOnGroundBelow(mobObj, new Packages.java.awt.Point(777, -177)); +} + +function setup(level, lobbyid) { + var eim = em.newInstance("Wedding" + lobbyid); + eim.setProperty("weddingId", "0"); + eim.setProperty("weddingStage", "0"); // 0: gathering time, 1: wedding time, 2: ready to fulfill the wedding, 3: just married + eim.setProperty("guestBlessings", "0"); + eim.setProperty("isPremium", "1"); + eim.setProperty("canJoin", "1"); + eim.setProperty("groomId", "0"); + eim.setProperty("brideId", "0"); + + eim.getInstanceMap(680000400).resetPQ(level); + if(eventBoss) spawnCakeBoss(eim); + + respawnStages(eim); + eim.startEventTimer(eventTime * 60000); + setEventRewards(eim); + setEventExclusives(eim); + return eim; +} + +function afterSetup(eim) {} + +function respawnStages(eim) { + eim.getMapInstance(680000400).instanceMapRespawn(); + eim.schedule("respawnStages", 15 * 1000); +} + +function playerEntry(eim, player) { + var map = eim.getMapInstance(entryMap); + + player.getClient().getAbstractPlayerInteraction().gainItem(4000313, 1); + player.changeMap(map, map.getPortal(0)); +} + +function stopBlessings(eim) { + var mapobj = eim.getMapInstance(entryMap + 10); + mapobj.dropMessage(6, "Wedding Assistant: Alright, now the couple is preparing to make it official. Tighten your seatbelts!"); + + eim.setIntProperty("weddingStage", 2); +} + +function sendWeddingAction(eim, type) { + var chr = eim.getLeader(); + if(chr.getGender() == 0) { + chr.getMap().broadcastMessage(Packages.tools.packets.Wedding.OnWeddingProgress(type == 2, eim.getIntProperty("groomId"), eim.getIntProperty("brideId"), type + 1)); + } else { + chr.getMap().broadcastMessage(Packages.tools.packets.Wedding.OnWeddingProgress(type == 2, eim.getIntProperty("brideId"), eim.getIntProperty("groomId"), type + 1)); + } +} + +function hidePriestMsg(eim) { + sendWeddingAction(eim, 2); +} + +function showStartMsg(eim) { + eim.getMapInstance(entryMap + 10).broadcastMessage(Packages.tools.packets.Wedding.OnWeddingProgress(false, 0, 0, 0)); + eim.schedule("hidePriestMsg", forceHideMsgTime * 1000); +} + +function showBlessMsg(eim) { + eim.getMapInstance(entryMap + 10).broadcastMessage(Packages.tools.packets.Wedding.OnWeddingProgress(false, 0, 0, 1)); + eim.setIntProperty("guestBlessings", 1); + eim.schedule("hidePriestMsg", forceHideMsgTime * 1000); +} + +function showMarriedMsg(eim) { + sendWeddingAction(eim, 3); + eim.schedule("hidePriestMsg", 10 * 1000); + + eim.restartEventTimer(partyTime * 60000); +} + +function scheduledTimeout(eim) { + if(eim.getIntProperty("canJoin") == 1) { + em.getChannelServer().closeOngoingWedding(isCathedral); + eim.setIntProperty("canJoin", 0); + + var mapobj = eim.getMapInstance(entryMap); + var chr = mapobj.getCharacterById(eim.getIntProperty("groomId")); + if(chr != null) { + chr.changeMap(entryMap + 10, "we00"); + } + + chr = mapobj.getCharacterById(eim.getIntProperty("brideId")); + if(chr != null) { + chr.changeMap(entryMap + 10, "we00"); + } + + mapobj.dropMessage(6, "Wedding Assistant: The couple are heading to the altar, hurry hurry talk to me to arrange your seat."); + + eim.setIntProperty("weddingStage", 1); + eim.schedule("showStartMsg", startMsgTime * 60 * 1000); + eim.schedule("showBlessMsg", blessMsgTime * 60 * 1000); + eim.schedule("stopBlessings", blessingsTime * 60 * 1000); + eim.startEventTimer(ceremonyTime * 60000); + } else { + end(eim); + } +} + +function playerUnregistered(eim, player) {} + +function playerExit(eim, player) { + eim.unregisterPlayer(player); + player.changeMap(exitMap, 0); +} + +function playerLeft(eim, player) { + if(!eim.isEventCleared()) { + playerExit(eim, player); + } +} + +function isMarrying(eim, player) { + var playerid = player.getId(); + return playerid == eim.getIntProperty("groomId") || playerid == eim.getIntProperty("brideId"); +} + +function changedMap(eim, player, mapid) { + if (mapid < minMapId || mapid > maxMapId) { + if (isMarrying(eim, player)) { + eim.unregisterPlayer(player); + end(eim); + } + else + eim.unregisterPlayer(player); + } +} + +function changedLeader(eim, leader) {} + +function playerDead(eim, player) {} + +function playerRevive(eim, player) { // player presses ok on the death pop up. + if (isMarrying(eim, player)) { + eim.unregisterPlayer(player); + end(eim); + } + else + eim.unregisterPlayer(player); +} + +function playerDisconnected(eim, player) { + if (isMarrying(eim, player)) { + eim.unregisterPlayer(player); + end(eim); + } + else + eim.unregisterPlayer(player); +} + +function leftParty(eim, player) {} + +function disbandParty(eim) {} + +function monsterValue(eim, mobId) { + return 1; +} + +function end(eim) { + var party = eim.getPlayers(); + + for (var i = 0; i < party.size(); i++) { + playerExit(eim, party.get(i)); + } + eim.dispose(); +} + +function giveRandomEventReward(eim, player) { + eim.giveEventReward(player); +} + +function clearPQ(eim) { + eim.stopEventTimer(); + eim.setEventCleared(); +} + +function isCakeBoss(mob) { + return mob.getId() == 9400606; +} + +function monsterKilled(mob, eim) { + if(isCakeBoss(mob)) { + eim.showClearEffect(); + eim.clearPQ(); + } +} + +function allMonstersDead(eim) {} + +function cancelSchedule() {} + +function dispose(eim) {} + diff --git a/scripts/event/WeddingChapel.js b/scripts/event/WeddingChapel.js new file mode 100644 index 0000000000..114372f948 --- /dev/null +++ b/scripts/event/WeddingChapel.js @@ -0,0 +1,252 @@ +/** + * @author: Ronan + * @event: Chapel Wedding +*/ + +var entryMap = 680000100; +var exitMap = 680000500; +var recruitMap = 680000000; +var clearMap = 680000500; + +var minMapId = 680000100; +var maxMapId = 680000401; + +var startMsgTime = 4; +var blessMsgTime = 5; + +var eventTime = 10; // 10 minutes gathering +var ceremonyTime = 20; // 20 minutes ceremony +var blessingsTime = 15;// blessings are held until the 15th minute from the ceremony start +var partyTime = 45; // 45 minutes party + +var forceHideMsgTime = 20; // unfortunately, EIM weddings don't send wedding talk packets to the server... this will need to suffice + +var eventBoss = true; // spawns a Cake boss at the hunting ground +var isCathedral = false; + +var lobbyRange = [0, 0]; + +function init() {} + +function setLobbyRange() { + return lobbyRange; +} + +function setEventExclusives(eim) { + var itemSet = [4031217, 4000313]; // golden key, golden maple leaf + eim.setExclusiveItems(itemSet); +} + +function setEventRewards(eim) { + var itemSet, itemQty, evLevel, expStages; + + evLevel = 1; //Rewards at clear PQ + itemSet = []; + itemQty = []; + eim.setEventRewards(evLevel, itemSet, itemQty); + + expStages = []; //bonus exp given on CLEAR stage signal + eim.setEventClearStageExp(expStages); +} + +function spawnCakeBoss(eim) { + var mapObj = eim.getMapInstance(680000400); + var mobObj = Packages.server.life.MapleLifeFactory.getMonster(9400606); + + mapObj.spawnMonsterOnGroundBelow(mobObj, new Packages.java.awt.Point(777, -177)); +} + +function setup(level, lobbyid) { + var eim = em.newInstance("Wedding" + lobbyid); + eim.setProperty("weddingId", "0"); + eim.setProperty("weddingStage", "0"); // 0: gathering time, 1: wedding time, 2: ready to fulfill the wedding, 3: just married + eim.setProperty("guestBlessings", "0"); + eim.setProperty("isPremium", "1"); + eim.setProperty("canJoin", "1"); + eim.setProperty("groomId", "0"); + eim.setProperty("brideId", "0"); + + eim.getInstanceMap(680000400).resetPQ(level); + if(eventBoss) spawnCakeBoss(eim); + + respawnStages(eim); + eim.startEventTimer(eventTime * 60000); + setEventRewards(eim); + setEventExclusives(eim); + return eim; +} + +function afterSetup(eim) {} + +function respawnStages(eim) { + eim.getMapInstance(680000400).instanceMapRespawn(); + eim.schedule("respawnStages", 15 * 1000); +} + +function playerEntry(eim, player) { + var map = eim.getMapInstance(entryMap); + + player.getClient().getAbstractPlayerInteraction().gainItem(4000313, 1); + player.changeMap(map, map.getPortal(0)); +} + +function stopBlessings(eim) { + var mapobj = eim.getMapInstance(entryMap + 10); + mapobj.dropMessage(6, "Wedding Assistant: Alright, now the couple is preparing to make it official. Tighten your seatbelts!"); + + eim.setIntProperty("weddingStage", 2); +} + +function sendWeddingAction(eim, type) { + var chr = eim.getLeader(); + if(chr.getGender() == 0) { + chr.getMap().broadcastMessage(Packages.tools.packets.Wedding.OnWeddingProgress(type == 2, eim.getIntProperty("groomId"), eim.getIntProperty("brideId"), type + 1)); + } else { + chr.getMap().broadcastMessage(Packages.tools.packets.Wedding.OnWeddingProgress(type == 2, eim.getIntProperty("brideId"), eim.getIntProperty("groomId"), type + 1)); + } +} + +function hidePriestMsg(eim) { + sendWeddingAction(eim, 2); +} + +function showStartMsg(eim) { + eim.getMapInstance(entryMap + 10).broadcastMessage(Packages.tools.packets.Wedding.OnWeddingProgress(false, 0, 0, 0)); + eim.schedule("hidePriestMsg", forceHideMsgTime * 1000); +} + +function showBlessMsg(eim) { + eim.getMapInstance(entryMap + 10).broadcastMessage(Packages.tools.packets.Wedding.OnWeddingProgress(false, 0, 0, 1)); + eim.setIntProperty("guestBlessings", 1); + eim.schedule("hidePriestMsg", forceHideMsgTime * 1000); +} + +function showMarriedMsg(eim) { + sendWeddingAction(eim, 1); + eim.schedule("hidePriestMsg", 10 * 1000); + + eim.restartEventTimer(partyTime * 60000); +} + +function scheduledTimeout(eim) { + if(eim.getIntProperty("canJoin") == 1) { + em.getChannelServer().closeOngoingWedding(isCathedral); + eim.setIntProperty("canJoin", 0); + + var mapobj = eim.getMapInstance(entryMap); + var chr = mapobj.getCharacterById(eim.getIntProperty("groomId")); + if(chr != null) { + chr.changeMap(entryMap + 10, "we00"); + } + + chr = mapobj.getCharacterById(eim.getIntProperty("brideId")); + if(chr != null) { + chr.changeMap(entryMap + 10, "we00"); + } + + mapobj.dropMessage(6, "Wedding Assistant: The couple are heading to the altar, hurry hurry talk to me to arrange your seat."); + + eim.setIntProperty("weddingStage", 1); + eim.schedule("showStartMsg", startMsgTime * 60 * 1000); + eim.schedule("showBlessMsg", blessMsgTime * 60 * 1000); + eim.schedule("stopBlessings", blessingsTime * 60 * 1000); + eim.startEventTimer(ceremonyTime * 60000); + } else { + end(eim); + } +} + +function playerUnregistered(eim, player) {} + +function playerExit(eim, player) { + eim.unregisterPlayer(player); + player.changeMap(exitMap, 0); +} + +function playerLeft(eim, player) { + if(!eim.isEventCleared()) { + playerExit(eim, player); + } +} + +function isMarrying(eim, player) { + var playerid = player.getId(); + return playerid == eim.getIntProperty("groomId") || playerid == eim.getIntProperty("brideId"); +} + +function changedMap(eim, player, mapid) { + if (mapid < minMapId || mapid > maxMapId) { + if (isMarrying(eim, player)) { + eim.unregisterPlayer(player); + end(eim); + } + else + eim.unregisterPlayer(player); + } +} + +function changedLeader(eim, leader) {} + +function playerDead(eim, player) {} + +function playerRevive(eim, player) { // player presses ok on the death pop up. + if (isMarrying(eim, player)) { + eim.unregisterPlayer(player); + end(eim); + } + else + eim.unregisterPlayer(player); +} + +function playerDisconnected(eim, player) { + if (isMarrying(eim, player)) { + eim.unregisterPlayer(player); + end(eim); + } + else + eim.unregisterPlayer(player); +} + +function leftParty(eim, player) {} + +function disbandParty(eim) {} + +function monsterValue(eim, mobId) { + return 1; +} + +function end(eim) { + var party = eim.getPlayers(); + + for (var i = 0; i < party.size(); i++) { + playerExit(eim, party.get(i)); + } + eim.dispose(); +} + +function giveRandomEventReward(eim, player) { + eim.giveEventReward(player); +} + +function clearPQ(eim) { + eim.stopEventTimer(); + eim.setEventCleared(); +} + +function isCakeBoss(mob) { + return mob.getId() == 9400606; +} + +function monsterKilled(mob, eim) { + if(isCakeBoss(mob)) { + eim.showClearEffect(); + eim.clearPQ(); + } +} + +function allMonstersDead(eim) {} + +function cancelSchedule() {} + +function dispose(eim) {} + diff --git a/scripts/npc/9201000.js b/scripts/npc/9201000.js index 9f5827aff7..d7c5c871ce 100644 --- a/scripts/npc/9201000.js +++ b/scripts/npc/9201000.js @@ -1,8 +1,6 @@ /* - This file is part of the OdinMS Maple Story Server - Copyright (C) 2008 Patrick Huy - Matthias Butz - Jan Christian Meyer + This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server + Copyleft (L) 2017 RonanLana This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as @@ -19,123 +17,202 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -/** - *Moony - 9201000.js - *@author Jvlaple - *For HurricaneMS v.59 +/* Moony + Amoria (680000000) + Engagement ring NPC. */ -//var numberOfLoves = 0; -//var ringSelection = -1; - -function start() { - // status = -1; - // action(1, 0, 0); - //} - //function action(mode, type, selection) { - // if (mode == -1) { - // cm.dispose(); - // } else { - // if (mode == 0) { - // cm.dispose(); - // return; - // } - // if (mode == 1) { - // status++; - // } else { - // status--; - // } - // if (status == 0) { - // if (cm.getPlayer().getMarriageQuestLevel() == 0 && cm.getPlayer().getLevel() >= 10) { - // cm.sendNext("Hey, I'm Moony, and I make engagement rings for marriage."); - // } else if (cm.getPlayer().getMarriageQuestLevel() == 1) { - // for (var i = 4031367; i < 4031373; i++) - // numberOfLoves += cm.getPlayer().countItem(i); - // if (numberOfLoves >= 4) { - // cm.sendNext("Wow, you're back pretty early. Got the #bProof of Loves#k? Lets see..."); - // } else { - // cm.sendOk("Please come back when you got 4 different #bProof of Loves#k."); - // cm.dispose(); - // } - // } else if (cm.getPlayer().getMarriageQuestLevel() == 2) { - // cm.sendSimple("Hey, your'e back! Ready to choose your ring?\r\n#b#L0#Moonstone Ring#l\r\n#L1#Star Gem Ring#l\r\n#L2#Golden Heart Ring#l\r\n#L3#Silver Swan Ring#l#k"); - // } else { - cm.sendOk("I hate making rings..."); - cm.dispose(); -// } -// } else if (status == 1) { -// if (cm.getPlayer().getMarriageQuestLevel() == 0 && cm.getPlayer().getLevel() >= 10) { -// cm.sendYesNo("Hey, you look like you might want to be married! Want to make an engagement ring?"); -// } else if (cm.getPlayer().getMarriageQuestLevel() == 1) { -// cm.sendNext("Great work getting the #bProof of Loves#k! Now we can make the #bEngagement Ring#k."); -// } else if (cm.getPlayer().getMarriageQuestLevel() == 2) { -// ringSelection = selection; -// if (ringSelection == 0) { -// if (cm.haveItem(4011007, 1) && cm.haveItem(4021007, 1) && cm.getPlayer().getMeso() >= 3000000) { -// cm.gainItem(4011007, -1); -// cm.gainItem(4021007, -1); -// cm.gainMeso(-3000000); -// cm.gainItem(2240000, 1); -// cm.sendOk("Here's the ring as promised! Have fun!"); -// cm.getPlayer().setMarriageQuestLevel(50); -// cm.dispose(); -// } else { -// cm.sendNext("You did not get all the right materials. To make an engagement ring, I need one of the following:\r\n\r\n#e#dMoonstone Ring:#k\r\n#v4011007#Moon Rock 1,#v4021007#Diamond 1, 3,000,000 Meso\r\n#dStar Gem Ring:#k\r\n#v4021009#Star Rock 1,#v4021007#Diamond 1, 2,000,000 Meso\r\n#dGolden Heart Ring:#k\r\n#v4011006#Gold Plate 1,#v4021007#Diamond 1, 1,000,000 Meso\r\n#dSilver Swan Ring:#k\r\n#v4011004#Silver Plate 1,#v4021007#Diamond 1, 500,000 Meso\r\n"); -// cm.dispose(); -// } -// } else if (ringSelection == 1) { -// if (cm.haveItem(4021009, 1) && cm.haveItem(4021007, 1) && cm.getPlayer().getMeso() >= 2000000) { -// cm.gainItem(4021009, -1); -// cm.gainItem(4021007, -1); -// cm.gainMeso(-2000000); -// cm.gainItem(2240001, 1); -// cm.sendOk("Here's the ring as promised! Have fun!"); -// cm.getPlayer().setMarriageQuestLevel(50); -// cm.dispose(); -// } else { -// cm.sendNext("You did not get all the right materials. To make an engagement ring, I need one of the following:\r\n\r\n#e#dMoonstone Ring:#k\r\n#v4011007#Moon Rock 1,#v4021007#Diamond 1, 3,000,000 Meso\r\n#dStar Gem Ring:#k\r\n#v4021009#Star Rock 1,#v4021007#Diamond 1, 2,000,000 Meso\r\n#dGolden Heart Ring:#k\r\n#v4011006#Gold Plate 1,#v4021007#Diamond 1, 1,000,000 Meso\r\n#dSilver Swan Ring:#k\r\n#v4011004#Silver Plate 1,#v4021007#Diamond 1, 500,000 Meso\r\n"); -// cm.dispose(); -// } -// } else if (ringSelection == 2) { -// if (cm.haveItem(4011006, 1) && cm.haveItem(4021007, 1) && cm.getPlayer().getMeso() >= 1000000) { -// cm.gainItem(4011006, -1); -// cm.gainItem(4021007, -1); -// cm.gainMeso(-1000000); -// cm.gainItem(2240002, 1); -// cm.sendOk("Here's the ring as promised! Have fun!"); -// cm.getPlayer().setMarriageQuestLevel(50); -// cm.dispose(); -// } else { -// cm.sendNext("You did not get all the right materials. To make an engagement ring, I need one of the following:\r\n\r\n#e#dMoonstone Ring:#k\r\n#v4011007#Moon Rock 1,#v4021007#Diamond 1, 3,000,000 Meso\r\n#dStar Gem Ring:#k\r\n#v4021009#Star Rock 1,#v4021007#Diamond 1, 2,000,000 Meso\r\n#dGolden Heart Ring:#k\r\n#v4011006#Gold Plate 1,#v4021007#Diamond 1, 1,000,000 Meso\r\n#dSilver Swan Ring:#k\r\n#v4011004#Silver Plate 1,#v4021007#Diamond 1, 500,000 Meso\r\n"); -// cm.dispose(); -// } -// } else if (ringSelection == 3) { -// if (cm.haveItem(4011004, 1) && cm.haveItem(4021007, 1) && cm.getPlayer().getMeso() >= 500000) { -// cm.gainItem(4011004, -1); -// cm.gainItem(4021007, -1); -// cm.gainMeso(-500000); -// cm.gainItem(2240003, 1); -// cm.sendOk("Here's the ring as promised! Have fun!"); -// cm.getPlayer().setMarriageQuestLevel(50); -// cm.dispose(); -// } else { -// cm.sendNext("You did not get all the right materials. To make an engagement ring, I need one of the following:\r\n\r\n#e#dMoonstone Ring:#k\r\n#v4011007#Moon Rock 1,#v4021007#Diamond 1, 3,000,000 Meso\r\n#dStar Gem Ring:#k\r\n#v4021009#Star Rock 1,#v4021007#Diamond 1, 2,000,000 Meso\r\n#dGolden Heart Ring:#k\r\n#v4011006#Gold Plate 1,#v4021007#Diamond 1, 1,000,000 Meso\r\n#dSilver Swan Ring:#k\r\n#v4011004#Silver Plate 1,#v4021007#Diamond 1, 500,000 Meso\r\n"); -// cm.dispose(); -// } -// } -// } -// } else if (status == 2) { -// if (cm.getPlayer().getMarriageQuestLevel() == 0 && cm.getPlayer().getLevel() >= 10) { -// cm.getPlayer().addMarriageQuestLevel(); -// cm.sendOk("Okay, first bring me back any four colored #bProof of Loves#k. You can get them from talking to #bNana the Love Fairy#k in any town. Also, only one of you, either the Groom or Bride will do this quest."); -// cm.dispose(); -// } else if (cm.getPlayer().getMarriageQuestLevel() == 1) { -// for (var j = 4031367; j < 4031373; j++) -// cm.removeAll(j); -// cm.getPlayer().addMarriageQuestLevel(); -// cm.sendNextPrev("You need the following raw materials to make an\r\n#bEngagement Ring#k.\r\n\r\n#e#dMoonstone Ring:#k\r\n#v4011007#Moon Rock 1,#v4021007#Diamond 1, 3,000,000 Meso\r\n#dStar Gem Ring:#k\r\n#v4021009#Star Rock 1,#v4021007#Diamond 1, 2,000,000 Meso\r\n#dGolden Heart Ring:#k\r\n#v4011006#Gold Plate 1,#v4021007#Diamond 1, 1,000,000 Meso\r\n#dSilver Swan Ring:#k\r\n#v4011004#Silver Plate 1,#v4021007#Diamond 1, 500,000 Meso\r\n"); -// cm.dispose(); -// } -// } -// } +var status; +var state; + +var item; +var mats; +var matQty; +var cost; + +var options; + +function hasProofOfLoves(player) { + var count = 0; + + for(var i = 4031367; i <= 4031372; i++) { + if(player.haveItem(i)) { + count++; + } + } + + return count >= 4; +} + +function hasEngagementBox(player) { + for(var i = 2240000; i <= 2240003; i++) { + if(player.haveItem(i)) { + return true; + } + } + + return false; +} + +function start() { + status = -1; + action(1, 0, 0); +} + +function action(mode, type, selection) { + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && type > 0) { + cm.dispose(); + return; + } + if (mode == 1) + status++; + else + status--; + + if(status == 0) { + options = ["I want to make a ring.", "I want to discard the ring box I have."]; + cm.sendSimple("I'm #p9201000#, the #bengagement ring maker#k. How can I help you?\r\n\r\n#b" + generateSelectionMenu(options)); + } else if(status == 1) { + if(selection == 0) { + if(!cm.isQuestCompleted(100400)) { + if(!cm.isQuestStarted(100400)) { + state = 0; + cm.sendAcceptDecline("So you want to make a engagement ring, huh? Very well, I can provide one for you if you pass my test. Certainly you must have already seen #rNanas, the fairies of Love#k, around the Maple world. From 4 of them, collect #b4 #t4031367#'s#k and bring them here. Only then I'll accept you as a proper ring holder. Are you up to it?"); + } else { + if(!hasProofOfLoves(cm.getPlayer())) { + cm.sendOk("Please bring here #b4 #t4031367#'s#k. That's a must for me to accept you as a proper holder for the wedding ring."); + } else { + cm.completeQuest(100400); + cm.gainExp(20000 * cm.getPlayer().getExpRate()); + + for(var i = 4031367; i <= 4031372; i++) { + cm.removeAll(i); + } + + cm.sendOk("You brought the #t4031367#'s, good. From now on you are eligible for holding the rings I make. Talk to me again to start forging the kind of ring you want."); + } + + cm.dispose(); + } + } else { + if(hasEngagementBox(cm.getPlayer())) { + cm.sendOk("Sorry, you already have an engagement box. I cannot provide you more than one box per time."); + cm.dispose(); + return; + } + if(cm.getPlayer().getGender() != 0) { + cm.sendOk("Sorry, but the ring box is currently available only for males."); + cm.dispose(); + return; + } + + state = 1; + options = ["Moonstone","Star Gem","Golden Heart", "Silver Swan"]; + var selStr = "So, what kind of engagement ring you want me to craft?\r\n\r\n#b" + generateSelectionMenu(options); + cm.sendSimple(selStr); + } + } else { + if(hasEngagementBox(cm.getPlayer())) { + for(var i = 2240000; i <= 2240003; i++) { + cm.removeAll(i); + } + + cm.sendOk("Your ring box has been discarded."); + } else { + cm.sendOk("You have no ring box to discard."); + } + + cm.dispose(); + } + } else if(status == 2) { + if(state == 0) { + cm.startQuest(100400); + cm.sendOk("Very well, then go after these #t4031367#'s. I will be waiting here."); + + cm.dispose(); + } else { + var itemSet = new Array(2240000,2240001,2240002,2240003); + var matSet = new Array(new Array(4011007,4021007),new Array(4021009,4021007),new Array(4011006,4021007),new Array(4011004,4021007)); + var matQtySet = new Array(new Array(1,1),new Array(1,1),new Array(1,1),new Array(1,1)); + var costSet = new Array (30000,20000,10000,5000); + + item = itemSet[selection]; + mats = matSet[selection]; + matQty = matQtySet[selection]; + cost = costSet[selection]; + + var prompt = "Then I'm going to craft you a #b#t" + item + "##k, is that right?"; + prompt += " In that case, I'm going to need specific items from you in order to make it. Make sure you have room in your inventory, though!#b"; + + if (mats instanceof Array){ + for(var i = 0; i < mats.length; i++){ + prompt += "\r\n#i"+mats[i]+"# " + matQty[i] + " #t" + mats[i] + "#"; + } + } + else { + prompt += "\r\n#i"+mats+"# " + matQty + " #t" + mats + "#"; + } + + if (cost > 0) + prompt += "\r\n#i4031138# " + cost + " meso"; + + cm.sendYesNo(prompt); + } + } else if(status == 3) { + var complete = true; + var recvItem = item, recvQty = 1, qty = 1; + + if(!cm.canHold(recvItem, recvQty)) { + cm.sendOk("Check your inventory for a free slot first."); + cm.dispose(); + return; + } + else if (cm.getMeso() < cost * qty) + { + cm.sendOk("I'm sorry but there's a fee for my services. Please bring me the right amount of mesos here before trying to forge a ring."); + cm.dispose(); + return; + } + else + { + if (mats instanceof Array) { + for(var i = 0; complete && i < mats.length; i++) + if (!cm.haveItem(mats[i], matQty[i] * qty)) + complete = false; + } + else if (!cm.haveItem(mats, matQty * qty)) + complete = false; + } + + if (!complete) + cm.sendOk("Hm, it seems you're lacking some ingredients for the engagement ring. Please provide them first, will you?"); + else { + if (mats instanceof Array) { + for (var i = 0; i < mats.length; i++){ + cm.gainItem(mats[i], -matQty[i] * qty); + } + } + else + cm.gainItem(mats, -matQty * qty); + + if (cost > 0) + cm.gainMeso(-cost * qty); + + cm.gainItem(recvItem, recvQty); + cm.sendOk("All done, the engagement ring came out just right. I wish you a happy engagement."); + } + cm.dispose(); + } + } +} + +function generateSelectionMenu(array) { + var menu = ""; + for (var i = 0; i < array.length; i++) { + menu += "#L" + i + "#" + array[i] + "#l\r\n"; + } + return menu; } \ No newline at end of file diff --git a/scripts/npc/9201001.js b/scripts/npc/9201001.js index 6c3a4ca1cb..e03ad10988 100644 --- a/scripts/npc/9201001.js +++ b/scripts/npc/9201001.js @@ -1,17 +1,83 @@ /* -Credits go to Travis of DeanMS ( xKillsAlotx on RaGEZONE) -Item Exchanger for scrolls + This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server + Copyleft (L) 2017 RonanLana -Modified by SharpAceX (Alan) for MapleSolaxia + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +/* Nana, the Love fairy + Amoria (680000000) + Engagement ring NPC. + */ -importPackage(Packages.tools); +var status; +var state; -var status = 0; -var leaf = 4001126; -var chairs = new Array(3010000, 3010001, 3010002, 3010003, 3010004, 3010005, 3010006, 3010007, 3010008, 3010009, 3010010, 3010011, 3010012, 3010013, 3010015, 3010016, 3010017, 3010018, 3010019, 3010022, 3010023, 3010024, 3010025, 3010026, 3010028, 3010040, 3010041, 3010043, 3010045, 3010046, 3010047,3010057,3010058,3010060,3010061,3010062,3010063, 3010064,3010065,3010066,3010067,3010069,3010071,3010072,3010073,3010080,3010081,3010082,3010083, 3010084,3010085,3010097,3010098,3010099,3010101,3010106,3010116,3011000,3012005,3012010,3012011); -var scrolls = new Array(2040603,2044503,2041024,2041025,2044703,2044603,2043303,2040807,2040806,2040006,2040007,2043103,2043203,2043003,2040506,2044403,2040903,2040709,2040710,2040711,2044303,2043803,2040403,2044103,2044203,2044003,2043703); -var weapons = new Array(1302020, 1302030, 1302033, 1302058, 1302064, 1302080, 1312032, 1322054, 1332025, 1332055, 1332056, 1372034, 1382009, 1382012, 1382039, 1402039, 1412011, 1412027, 1422014, 1422029, 1432012, 1432040, 1432046, 1442024, 1442030, 1442051, 1452016, 1452022, 1452045, 1462014, 1462019, 1462040, 1472030, 1472032, 1472055, 1482020, 1482021, 1482022, 1492020, 1492021, 1492022, 1092030, 1092045, 1092046, 1092047); +var item; +var mats; +var matQty; +var cost; + +var options; + +function hasProofOfLoves(player) { + var count = 0; + + for(var i = 4031367; i <= 4031372; i++) { + if(player.haveItem(i)) { + count++; + } + } + + return count >= 4; +} + +function getNanaLocation(player) { + var mapid = player.getMap().getId(); + + for(var i = 0; i < mapids.length; i++) { + if(mapid == mapids[i]) { + return i; + } + } + + return -1; +} + +var nanaLoc; +var mapids = [100000000, 103000000, 102000000, 101000000, 200000000, 220000000]; +var questItems = [4000001, 4000037, 4000215, 4000026, 4000070, 4000128]; +var questExp = [2000, 5000, 10000, 17000, 22000, 30000]; + +function processNanaQuest() { + if(cm.haveItem(questItems[nanaLoc], 50)) { + if(cm.canHold(4031367 + nanaLoc, 1)) { + cm.gainItem(questItems[nanaLoc], -50); + cm.gainItem(4031367 + nanaLoc, 1); + + cm.sendOk("Kyaaaa~ Thank you a lot, here get the #b#t4031367##k."); + return true; + } else { + cm.sendOk("Please have a free ETC slot available to hold the token of love."); + } + } else { + cm.sendOk("Please gather to me #b50 #t" + questItems[nanaLoc] + "##k."); + } + + return false; +} function start() { status = -1; @@ -19,102 +85,61 @@ function start() { } function action(mode, type, selection) { - if (mode == -1) + if (mode == -1) { cm.dispose(); - else { - if (mode == 0 && status == 0) + } else { + if (mode == 0 && type > 0) { cm.dispose(); + return; + } if (mode == 1) status++; else status--; - if (status == 0) { - cm.sendSimple("Hello#b #h ##k, you currently have #b#c4001126# maple leaves.#k \r\nWhat would you like to do?\r\n#k#L1# Trade 1 leaf for 5,000 NX#l\r\n\#L2# Trade 1 leaf for 1 random chair #l\r\n\#L3# Trade 1 leaf for 3 random Maple Weapons #l\r\n\#L4# Trade 1 leaf for 3 Swiss Cheese and Onyx Apples#l\r\n#L5#Trade 1 leaf for a 10 day Hired Merchant#l"); - } else if (status == 1) { - if (selection == 1) { - if(cm.haveItem(leaf, 1)) { - cm.getPlayer().getCashShop().gainCash(1, 5000); - cm.getPlayer().announce(MaplePacketCreator.earnTitleMessage("You have earned 5,000 NX")); - cm.gainItem(leaf, -1); - cm.sendOk("Here is your 5,000 NX!"); - cm.logLeaf("5k NX"); + + if(status == 0) { + if(!cm.isQuestStarted(100400)) { + cm.sendOk("Hello #b#h0##k, I'm #p9201001# the fairy of Love."); + cm.dispose(); + return; + } + + nanaLoc = getNanaLocation(cm.getPlayer()); + if(nanaLoc == -1) { + cm.sendOk("Hello #b#h0##k, I'm #p9201001# the fairy of Love."); + cm.dispose(); + return; + } + + if(!cm.haveItem(4031367 + nanaLoc, 1)) { + if(cm.isQuestCompleted(100401 + nanaLoc)) { + state = 1; + cm.sendAcceptDecline("Did you lost the #k#t4031367##k I gave to you? Well, I can share another one with you, but you will need to redo the favor I asked last time, is that ok? I need you to bring me #r50 #t" + questItems[nanaLoc] + "#'s.#k"); + } else if(cm.isQuestStarted(100401 + nanaLoc)) { + if(processNanaQuest()) { + cm.gainExp(questExp[nanaLoc] * cm.getPlayer().getExpRate()); + cm.completeQuest(100401 + nanaLoc); + } + + cm.dispose(); } else { - cm.sendOk("Sorry, you don't have a maple leaf!"); - } + state = 0; + cm.sendAcceptDecline("Are you searching for #k#t4031367#'s#k? I can share one with you, but you must do a favor for me, is that ok?"); + } + } else { + cm.sendOk("Hey there. Did you get the #t4031367# from the other Nana's already?"); cm.dispose(); - } else if (selection == 2) { - if(cm.haveItem(leaf, 1)) { - var chair1 = chairs[Math.floor(Math.random()*chairs.length)]; - if(cm.canHold(chair1)){ - cm.gainItem(chair1); - cm.gainItem(leaf, -1); - cm.sendOk("Here is your random chair!"); - cm.logLeaf("Chair ID: " + chair1); - } else { - cm.sendOk("Please make sure you have enough space to hold this chair!"); - } - } else { - cm.sendOk("Sorry, you don't have a maple leaf!"); - } + } + } else if(status == 1) { + if(state == 0) { + cm.startQuest(100401 + nanaLoc); + + cm.sendOk("I need you to collect #r50 #t" + questItems[nanaLoc] + "##k."); cm.dispose(); - } else if (selection == 3) { - if(cm.haveItem(leaf, 1)) { - var weapon1 = weapons[Math.floor(Math.random()*weapons.length)]; - var weapon2 = weapons[Math.floor(Math.random()*weapons.length)]; - var weapon3 = weapons[Math.floor(Math.random()*weapons.length)]; - if(!cm.getPlayer().getInventory(Packages.client.inventory.MapleInventoryType.EQUIP).isFull(3)) { - cm.gainItem(weapon1, 1, true, true); - cm.gainItem(weapon2, 1, true, true); - cm.gainItem(weapon3, 1, true, true); - cm.gainItem(leaf, -1); - cm.sendOk("Here are your 3 random weapons!"); - cm.logLeaf("Maple Weapons IDs: " + weapon1 + "," + weapon2 + "," + weapon3); - } else { - cm.sendOk("Please make sure you have enough space to hold these weapons!"); - } - } else { - cm.sendOk("Sorry, you don't have a maple leaf!"); - } + } else { + processNanaQuest(); cm.dispose(); - } else if (selection == 4) { - if(cm.haveItem(leaf, 1)) { - var cheese = 2022273; - var apple = 2022179; - if(!cm.getPlayer().getInventory(Packages.client.inventory.MapleInventoryType.EQUIP).isFull(2)){ - cm.gainItem(apple, 3); - cm.gainItem(cheese, 3); - cm.gainItem(leaf, -1); - cm.sendOk("Here are your 3 cheeses and apples!"); - cm.logLeaf("3 cheeses and apples"); - } else { - cm.sendOk("Please make sure you have enough space to hold these items!"); - } - } else { - cm.sendOk("Sorry, you don't have a maple leaf!"); - } - cm.dispose(); - } else if(selection == 5) { - if(cm.haveItem(leaf, 1)) { - if(!cm.haveItem(5030000, 1)) { - if(!cm.getPlayer().getInventory(Packages.client.inventory.MapleInventoryType.CASH).isFull(1)){ - cm.gainItem(5030000, 1, false, true, 1000 * 60 * 60 * 24 * 10); - cm.gainItem(leaf, -1); - cm.sendOk("Here is your Hired Merchant!"); - cm.logLeaf("10 day hired merchant"); - } else { - cm.sendOk("Please make sure you have enough space to hold these items!"); - } - } else { - cm.sendOk("I can't give you a merchant if you already have one!"); - } - } else { - cm.sendOk("Sorry, you don't have a maple leaf!"); - } - cm.dispose(); - } else { - cm.sendOk("Come back later!"); - cm.dispose(); - } + } } } } \ No newline at end of file diff --git a/scripts/npc/9201002.js b/scripts/npc/9201002.js index 2ebeae654a..419f3a574c 100644 --- a/scripts/npc/9201002.js +++ b/scripts/npc/9201002.js @@ -1,8 +1,6 @@ /* - This file is part of the OdinMS Maple Story Server - Copyright (C) 2008 Patrick Huy - Matthias Butz - Jan Christian Meyer + This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server + Copyleft (L) 2017 RonanLana This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as @@ -19,34 +17,369 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -/* NPC Base - Map Name (Map ID) - Extra NPC info. +/* High Priest John + Marriage NPC */ +importPackage(Packages.tools.packets); +importPackage(Packages.net.server.channel.handlers); + var status; - -function start() { - status = -1; - action(1, 0, 0); +var state; +var eim; +var weddingEventName = "WeddingCathedral"; +var cathedralWedding = true; +var weddingIndoors; +var weddingBlessingExp = 30000; + +function isWeddingIndoors(mapid) { + return mapid >= 680000100 && mapid <= 680000500; +} + +function getMarriageInstance(player) { + var em = cm.getEventManager(weddingEventName); + + for (var iterator = em.getInstances().iterator(); iterator.hasNext();) { + var eim = iterator.next(); + if(eim.isEventLeader(player)) { + return eim; + } + } + + return null; +} + +function detectPlayerItemid(player) { + for (var x = 4031357; x <= 4031364; x++) { + if (player.haveItem(x)) { + return x; + } + } + + return -1; +} + +function getRingId(boxItemId) { + return boxItemId == 4031357 ? 1112803 : (boxItemId == 4031359 ? 1112806 : (boxItemId == 4031361 ? 1112807 : (boxItemId == 4031363 ? 1112809 : -1))); +} + +function isSuitedForWedding(player, equipped) { + var baseid = (player.getGender() == 0) ? 1050131 : 1051150; + + if(equipped) { + for(var i = 0; i < 4; i++) { + if(player.haveItemEquipped(baseid + i)) { + return true; + } + } + } else { + for(var i = 0; i < 4; i++) { + if(player.haveItemWithId(baseid + i, true)) { + return true; + } + } + } + + return false; +} + +function getWeddingPreparationStatus(player, partner) { + if(!player.haveItem(4000313)) return -3; + if(!partner.haveItem(4000313)) return 3; + + if(!isSuitedForWedding(player, true)) return -4; + if(!isSuitedForWedding(partner, true)) return 4; + + var hasEngagement = false; + for (var x = 4031357; x <= 4031364; x++) { + if (player.haveItem(x)) { + hasEngagement = true; + break; + } + } + if(!hasEngagement) return -1; + + hasEngagement = false; + for (var x = 4031357; x <= 4031364; x++) { + if (partner.haveItem(x)) { + hasEngagement = true; + break; + } + } + if(!hasEngagement) return -2; + + if(!player.canHold(1112803)) return 1; + if(!partner.canHold(1112803)) return 2; + + return 0; +} + +function giveCoupleBlessings(eim, player, partner) { + var blessCount = eim.gridSize(); + + player.gainExp(blessCount * weddingBlessingExp); + partner.gainExp(blessCount * weddingBlessingExp); +} + +function start() { + weddingIndoors = isWeddingIndoors(cm.getMapId()); + if(weddingIndoors) eim = cm.getEventInstance(); + + status = -1; + action(1, 0, 0); } function action(mode, type, selection) { - if (mode == -1) { - cm.dispose(); - } else { - if (mode == 0 && type > 0) { - cm.dispose(); - return; - } - if (mode == 1) - status++; - else - status--; - - if(status == 0) { - cm.sendOk("Wedding is currently closed."); - cm.dispose(); - } + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && type > 0) { + cm.dispose(); + return; } + if (mode == 1) + status++; + else + status--; + + if(!weddingIndoors) { + if(status == 0) { + var hasEngagement = false; + for (var x = 4031357; x <= 4031364; x++) { + if (cm.haveItem(x, 1)) { + hasEngagement = true; + break; + } + } + + if(hasEngagement) { + var text = "Hi there. How can I help you?"; + var choice = new Array("We're ready to get married."); + for (x = 0; x < choice.length; x++) { + text += "\r\n#L" + x + "##b" + choice[x] + "#l"; + } + cm.sendSimple(text); + } else { + cm.sendOk("Hmm, today two fluttering hearts are about to be joined together by the blessings of love."); + cm.dispose(); + } + } else if(status == 1) { + var wid = cm.getClient().getWorldServer().getRelationshipId(cm.getPlayer().getId()); + var cserv = cm.getClient().getChannelServer(); + + if(cserv.isWeddingReserved(wid)) { + if(wid == cserv.getOngoingWedding(cathedralWedding)) { + var partner = cserv.getPlayerStorage().getCharacterById(cm.getPlayer().getPartnerId()); + if(!(partner == null || !cm.getMap().equals(partner.getMap()))) { + if(!cm.canHold(4000313)) { + cm.sendOk("Please have a free ETC slot available to get the #b#t4000313##k."); + cm.dispose(); + return; + } else if(!partner.canHold(4000313)) { + cm.sendOk("Please let your partner know they must have a free ETC slot available to get the #b#t4000313##k."); + cm.dispose(); + return; + } else if(!isSuitedForWedding(cm.getPlayer(), false)) { + cm.sendOk("Please purchase a #rwedding garment#k for the ceremony, quickly! Without it I am not able to marry you."); + cm.dispose(); + return; + } else if(!isSuitedForWedding(partner, false)) { + cm.sendOk("Please let your partner know they must have a #rwedding garment#k ready for the ceremony."); + cm.dispose(); + return; + } + + cm.sendOk("Very well, the preparatives here are finished too. This indeed is a beautiful day, you two are truly blessed to marry on such a day. Let us begin the marriage!!"); + } else { + cm.sendOk("Hmm, it seems your partner is elsewhere... Please let them come here before starting the ceremony."); + cm.dispose(); + } + } else { + var placeTime = cserv.getWeddingReservationTimeLeft(wid); + + cm.sendOk("Have patience. Your wedding is set to happen at the #r" + placeTime + "#k."); + cm.dispose(); + } + } else { + cm.sendOk("Hmm, I'm sorry but there are no reservations made for you at this channel for the time being."); + cm.dispose(); + } + } else if(status == 2) { + var cserv = cm.getClient().getChannelServer(); + var partner = cserv.getPlayerStorage().getCharacterById(cm.getPlayer().getPartnerId()); + if(!(partner == null || !cm.getMap().equals(partner.getMap()))) { + if(cserv.acceptOngoingWedding(cathedralWedding)) { + var wid = cm.getClient().getWorldServer().getRelationshipId(cm.getPlayer().getId()); + if(wid > 0) { + var em = cm.getEventManager(weddingEventName); + if(em.startInstance(cm.getPlayer())) { + eim = getMarriageInstance(cm.getPlayer()); + if(eim != null) { + eim.setIntProperty("weddingId", wid); + eim.setIntProperty("groomId", cm.getPlayer().getId()); + eim.setIntProperty("brideId", cm.getPlayer().getPartnerId()); + eim.setIntProperty("isPremium", cserv.getOngoingWeddingType(cathedralWedding) ? 1 : 0); + + eim.registerPlayer(partner); + } else { + cm.sendOk("An unexpected error happened when locating the wedding event. Please try again later."); + } + + cm.dispose(); + } else { + cm.sendOk("An unexpected error happened before the wedding preparations. Please try again later."); + cm.dispose(); + } + } else { + cm.sendOk("An unexpected error happened before the wedding preparations. Please try again later."); + cm.dispose(); + } + } else { // partner already decided to start + cm.dispose(); + } + } else { + cm.sendOk("Hmm, it seems your partner is elsewhere... Please let them come here before starting the ceremony."); + cm.dispose(); + } + } + } else { + if (status == 0) { + if(eim == null) { + cm.warp(680000000,0); + cm.dispose(); + return; + } + + var playerId = cm.getPlayer().getId(); + if(playerId == eim.getIntProperty("groomId") || playerId == eim.getIntProperty("brideId")) { + var wstg = eim.getIntProperty("weddingStage"); + + if(wstg == 2) { + cm.sendYesNo("Very well, the guests has bestowed all their blessings to you now. The time has come, #rshould I make you Husband and Wife#k?"); + state = 1; + } else if(wstg == 1) { + cm.sendOk("While you two are making your wedding vows to each other, your guests are currently giving their blessings to you. This is a time of happiness for both of you, please stay awhile."); + cm.dispose(); + } else { + cm.sendOk("Congratulations on your wedding! Our ceremony is now complete, you can head to #b#p9201007##k now, she will lead you and your guests to the afterparty. Cheers for your love!"); + cm.dispose(); + } + } else { + var wstg = eim.getIntProperty("weddingStage"); + if(wstg == 1) { + if(eim.gridCheck(cm.getPlayer()) != -1) { + cm.sendOk("Everyone give your blessings to this lovely couple!"); + cm.dispose(); + } else { + if(eim.getIntProperty("guestBlessings") == 1) { + cm.sendYesNo("Do you want to bless this couple?"); + state = 0; + } else { + cm.sendOk("Today we are gathered here to reunite this lively couple in matrimony!"); + cm.dispose(); + } + } + } else if(wstg == 3) { + cm.sendOk("The two loving birds are now married. What a lively day! Please #rget ready for the afterparty#k, it should start soon. Follow the married couple's lead."); + cm.dispose(); + } else { + cm.sendOk("The guest's blessing time has ended. Hang on, the couple will renew their vows very soon now. What a sight to see!"); + cm.dispose(); + } + } + } else if (status == 1) { + if(state == 0) { // give player blessings + eim.gridInsert(cm.getPlayer(), 1); + + cm.sendOk("Your blessings have been added to their love. What a noble act for a lovely couple!"); + cm.dispose(); + } else { // couple wants to complete the wedding + var wstg = eim.getIntProperty("weddingStage"); + + if(wstg == 2) { + var pid = cm.getPlayer().getPartnerId(); + if(pid <= 0) { + cm.sendOk("It seems you are no longer engaged to your partner, just before the altar... Where did all that happiness you two had sported a while ago went?"); + cm.dispose(); + return; + } + + var player = cm.getPlayer(); + var partner = cm.getMap().getCharacterById(cm.getPlayer().getPartnerId()); + if(partner != null) { + state = getWeddingPreparationStatus(player, partner); + + switch(state) { + case 0: + eim.setIntProperty("weddingStage", 3); + var cmPartner = partner.getClient().getAbstractPlayerInteraction(); + + var playerItemId = detectPlayerItemid(player); + var partnerItemId = (playerItemId % 2 == 1) ? playerItemId + 1 : playerItemId - 1; + + var marriageRingId = getRingId((playerItemId % 2 == 1) ? playerItemId : partnerItemId); + + cm.gainItem(playerItemId, -1); + cmPartner.gainItem(partnerItemId, -1); + + RingActionHandler.giveMarriageRings(player, partner, marriageRingId); + player.setMarriageItemId(marriageRingId); + partner.setMarriageItemId(marriageRingId); + + //var marriageId = eim.getIntProperty("weddingId"); + //player.announce(Wedding.OnMarriageResult(marriageId, player, true)); + //partner.announce(Wedding.OnMarriageResult(marriageId, player, true)); + + giveCoupleBlessings(eim, player, partner); + + cm.getMap().dropMessage(6, "High Priest John: By the power vested in me through the mighty Maple tree, I now pronounce you Husband and Wife. You may kiss the bride!"); + eim.schedule("showMarriedMsg", 2 * 1000); + break; + + case -1: + cm.sendOk("It seems you no longer have the ring/ring box you and your partner shared at the engagement time. Sorry, but that was needed for the wedding..."); + break; + + case -2: + cm.sendOk("It seems your partner no longer has the ring/ring box you two shared at the engagement time. Sorry, but that was needed for the wedding..."); + break; + + case -3: + cm.sendOk("It seems you don't have the #r#t4000313##k given at the entrance... Please find it, I can't marry you without that item in hands."); + break; + + case -4: + cm.sendOk("Pardon my rudiness, but the garments are a essential part of the ceremony. Please #rsuit yourself properly#k for a wedding."); + break; + + case 1: + cm.sendOk("Please make an EQUIP slot available to get the marriage ring, will you?"); + break; + + case 2: + cm.sendOk("Please let your partner know to make an EQUIP slot available to get the marriage ring, will you?"); + break; + + case 3: + cm.sendOk("It seems your partner don't have the #r#t4000313##k given at the entrance... Please find it, I can't marry you without that item in hands."); + break; + + case 4: + cm.sendOk("It seems your partner is not properly dressed for the wedding... Pardon my rudiness, but the garments are a essential part of the ceremony."); + break; + } + + cm.dispose(); + } else { + cm.sendOk("Hmm, it seems your partner is not here, before the altar... It is a pity, but I can't fulfill the wedding if your partner is not here."); + cm.dispose(); + } + } else { + cm.sendOk("You are now #bhusband and wife#k. Congratulations!"); + cm.dispose(); + } + } + } + } + } } \ No newline at end of file diff --git a/scripts/npc/9201004.js b/scripts/npc/9201004.js index cb21e30d7f..d49c9e7c05 100644 --- a/scripts/npc/9201004.js +++ b/scripts/npc/9201004.js @@ -1,8 +1,6 @@ /* - This file is part of the OdinMS Maple Story Server - Copyright (C) 2008 Patrick Huy - Matthias Butz - Jan Christian Meyer + This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server + Copyleft (L) 2017 RonanLana This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as @@ -19,11 +17,127 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -/*9201091 - Ames - *@author Moogra -*/ +/* Amos the Wise + Amoria (680000000) + Wedding info. + */ + +importPackage(Packages.net.server.channel.handlers); + +var status; + +var rings = [1112806, 1112803, 1112807, 1112809]; +var divorceFee = 500000; +var ringObj; + +function getWeddingRingItemId(player) { + for (var i = 0; i < rings.length; i++) { + if (player.haveItemWithId(rings[i], false)) { + return rings[i]; + } + } + + return null; +} + +function hasEquippedWeddingRing(player) { + for (var i = 0; i < rings.length; i++) { + if (player.haveItemEquipped(rings[i])) { + return true; + } + } + + return false; +} function start() { - cm.warp(680000000, 0); - cm.dispose(); + status = -1; + action(1, 0, 0); +} + +function action(mode, type, selection) { + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && type > 0) { + cm.dispose(); + return; + } + if (mode == 1) + status++; + else + status--; + + if(status == 0) { + var questionStr = ["How can I engage someone?", "How can I marry?", "How can I divorce?"] + + if(!(!cm.getPlayer().isMarried() && getWeddingRingItemId(cm.getPlayer()))) questionStr.push("I want a divorce..."); + else questionStr.push("I wanna remove my old wedding ring..."); + + cm.sendSimple("Hello, welcome to #bAmoria#k, a beautiful land where maplers can find love and, if inspired enough, even marry. Do you have any questions about Amoria? Talk it to me.#b\r\n\r\n" + generateSelectionMenu(questionStr)); + } else if(status == 1) { + switch(selection) { + case 0: + cm.sendOk("The #bengagement process#k is as straightforward as it can be. Firstly one must start a prequest from the #bring maker, #p9201000##k. They must gather #b#t4031367#'s#k thoughout the Maple world.\r\nFrom the completion of the quest, the player will gain an engagement ring. With that in hand, declare yourself to someone you become fond of. Then, hope the person accepts your proposal."); + cm.dispose(); + break; + + case 1: + cm.sendOk("For the #bmarriage process#k you must be already engaged. The loving couple must choose a venue they want to hold their marriage. Amoria offers two: the #rCathedral#k and the #rChapel#k.\r\nThen, one of the partners must buy a #bWedding Ticket#k, available through the Cash Shop, and book their ceremony with the Wedding Assistant. Each partner will receive #rguest tickets#k to be distributed to their acquaintances."); + cm.dispose(); + break; + + case 2: + cm.sendOk("Unfortunately the love of long may fizzle someday. Well, I hope that's not the case for any loving couple that once married, is marrying today or is going to do so tomorrow. But, if that ever happens, I myself will be at service to make a safe divorce, by the fee of #r" + divorceFee + "#k mesos."); + cm.dispose(); + break; + + case 3: + ringObj = cm.getPlayer().getMarriageRing(); + if(ringObj == null) { + var itemid = getWeddingRingItemId(cm.getPlayer()); + + if(itemid != null) { + cm.sendOk("There you go, I've removed your old wedding ring."); + cm.gainItem(itemid, -1); + } else if(hasEquippedWeddingRing(cm.getPlayer())) { + cm.sendOk("If ou want your old wedding ring removed, please unequip it before talking to me."); + } else { + cm.sendOk("You're not married to require a divorce from it."); + } + + cm.dispose(); + return; + } + + cm.sendYesNo("So, you want to divorce from your partner? Be sure, this process #bcannot be rollbacked#k by any means, it's supposed to be an ultimatum from which your ring will be destroyed as consequence. That said, do you #rreally want to divorce#k?"); + break; + } + } else if(status == 2) { + if(cm.getMeso() < divorceFee) { + cm.sendOk("You don't have the required amount of #r" + divorceFee + " mesos#k for the divorce fee."); + cm.dispose(); + return; + } else if(ringObj.equipped()) { + cm.sendOk("Please unequip your ring before trying to divorce."); + cm.dispose(); + return; + } + + cm.gainMeso(-divorceFee); + RingActionHandler.breakMarriageRing(cm.getPlayer(), ringObj.getItemId()); + cm.gainItem(ringObj.getItemId(), -1); + + cm.sendOk("You have divorced from your partner."); + cm.dispose(); + } + } +} + +function generateSelectionMenu(array) { + var menu = ""; + for (var i = 0; i < array.length; i++) { + menu += "#L" + i + "#" + array[i] + "#l\r\n"; + } + return menu; } \ No newline at end of file diff --git a/scripts/npc/9201005.js b/scripts/npc/9201005.js index eceec8ce7f..3d924e3580 100644 --- a/scripts/npc/9201005.js +++ b/scripts/npc/9201005.js @@ -1,8 +1,6 @@ /* - This file is part of the OdinMS Maple Story Server - Copyright (C) 2008 Patrick Huy - Matthias Butz - Jan Christian Meyer + This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server + Copyleft (L) 2017 RonanLana This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as @@ -19,85 +17,257 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -/** - Nicole --- By --------------------------------------------------------------------------------------------- - Angel (get31720 ragezone) --- Extra Info ------------------------------------------------------------------------------------- - Fixed by [happydud3] & [XotiCraze] ---------------------------------------------------------------------------------------------------- -**/ +/* Assistant Nicole + Marriage NPC + */ var status; -var x; -var hasEngageRing = false; +var wid; +var isMarrying; + +var cathedralWedding = true; +var weddingEventName = "WeddingCathedral"; +var weddingEntryTicketCommon = 5251000; +var weddingEntryTicketPremium = 5251003; +var weddingSendTicket = 4031395; +var weddingGuestTicket = 4031407; +var weddingAltarMapid = 680000210; +var weddingIndoors; + +function isWeddingIndoors(mapid) { + return mapid >= 680000100 && mapid <= 680000500; +} + +function hasSuitForWedding(player) { + var baseid = (player.getGender() == 0) ? 1050131 : 1051150; + + for(var i = 0; i < 4; i++) { + if(player.haveItemWithId(baseid + i, true)) { + return true; + } + } + + return false; +} + +function getMarriageInstance(weddingId) { + var em = cm.getEventManager(weddingEventName); + + for (var iterator = em.getInstances().iterator(); iterator.hasNext();) { + var eim = iterator.next(); + + if(eim.getIntProperty("weddingId") == weddingId) { + return eim; + } + } + + return null; +} + +function hasWeddingRing(player) { + var rings = [1112806, 1112803, 1112807, 1112809]; + for (var i = 0; i < rings.length; i++) { + if (player.haveItemWithId(rings[i], true)) { + return true; + } + } + + return false; +} function start() { - status = -1; + weddingIndoors = isWeddingIndoors(cm.getMapId()); + status = -1; + action(1, 0, 0); } function action(mode, type, selection) { - if (mode == -1 || mode == 0) { - cm.sendOk("Goodbye then"); + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && type > 0) { cm.dispose(); - return; - } else if (mode == 1) { - status++; - } else { - status--; + return; } - var item = new Array(4031360, 4031358, 4031362, 4031364); - for (x = 0; x < item.length && !hasEngageRing; x++) { - if (cm.haveItem(item[x], 1)) - hasEngageRing = true; - } - if (status == 0) { - var text = "I'm here to assist you on weddings !"; - var choice = new Array("How do I prepare a wedding?", "I have an engagement ring and need invites for my guests", "I am the bride/groom and I'd like to start the wedding", "I am the guest and I'd like to go into the wedding"); - for (x = 0; x < choice.length; x++) { - text += "\r\n#L" + x + "##b" + choice[x] + "#l"; - } - cm.sendSimple(text); - } else if (status == 1) { - switch(selection) { - case 0: - cm.sendOk("Moony makes the engagement ring. The engagement ring is required throughout the wedding so never lose it. To invite your guests into the wedding you need to show me your engagement ring and then I'll give you 15 Gold Maple Leaves. They need 1 each to enter the wedding. Enjoy!"); - cm.dispose(); - break; - case 1: - if (cm.haveItem(4000313)) { - cm.sendOk("You already have a Gold Maple Leaf. Go give them to your guests before you go into the wedding."); - cm.dispose(); - } else if (hasEngageRing) { - cm.sendOk("You have received 15 Gold Maple Leaves."); - cm.gainItem(4000313,15); - cm.dispose(); - } else { - cm.sendOk("You do not have an engagement ring."); - cm.dispose(); - } - break; - case 2: - if (hasEngageRing) { - cm.warp(680000210, 2); - cm.sendOk("Talk to High Priest John when you're ready to be married."); - cm.dispose(); - } else { - cm.sendOk("You do not have an engagement ring."); - cm.dispose(); - } - break; - case 3: - if (cm.haveItem(4000313)) { - cm.warp(680000210, 0); - cm.sendOk("Enjoy the wedding. Don't drop your Gold Maple Leaf or you won't be able to finish the whole wedding."); - cm.dispose(); - } else { - cm.sendOk("You do not have a Gold Maple Leaf."); - cm.dispose(); - } - break; - } - } + if (mode == 1) + status++; + else + status--; + + if(!weddingIndoors) { + var hasEngagement = false; + for (var x = 4031357; x <= 4031364; x++) { + if (cm.haveItem(x, 1)) { + hasEngagement = true; + break; + } + } + + if (status == 0) { + var text = "Welcome to the #bCathedral#k! How can I help you?"; + var choice = ["How do I prepare a wedding?", "I have an engagement and want to arrange the wedding", "I am the guest and I'd like to go into the wedding"]; + for (x = 0; x < choice.length; x++) { + text += "\r\n#L" + x + "##b" + choice[x] + "#l"; + } + cm.sendSimple(text); + } else if (status == 1) { + switch(selection) { + case 0: + cm.sendOk("Firstly you need to be #bengaged#k to someone. #p9201000# makes the engagement ring. Once attained the engagement status, purchase a #b#t" + weddingEntryTicketCommon + "##k.\r\nShow me your engagement ring and a wedding ticket, and I will book a reservation for you along with #r15 Wedding Tickets#k. Use them to invite your guests into the wedding. They need 1 each to enter."); + cm.dispose(); + break; + + case 1: + if (hasEngagement) { + var wserv = cm.getClient().getWorldServer(); + var cserv = cm.getClient().getChannelServer(); + var weddingId = wserv.getRelationshipId(cm.getPlayer().getId()); + + if(weddingId > 0) { + if(cserv.isWeddingReserved(weddingId)) { // registration check + var placeTime = cserv.getWeddingReservationTimeLeft(weddingId); + cm.sendOk("Your wedding is set to start at the #r" + placeTime + "#k. Don't be late!"); + } else { + var partner = wserv.getPlayerStorage().getCharacterById(cm.getPlayer().getPartnerId()); + if(partner == null) { + cm.sendOk("Your partner seems to be offline right now... Make sure to get both gathered here when the time comes!"); + cm.dispose(); + return; + } + + if(hasWeddingRing(cm.getPlayer()) || hasWeddingRing(partner)) { + cm.sendOk("Either you or your partner already has a marriage ring."); + cm.dispose(); + return; + } + + if(!cm.getMap().equals(partner.getMap())) { + cm.sendOk("Please let your partner come here as well to register the reservation."); + cm.dispose(); + return; + } + + if(!cm.canHold(weddingSendTicket, 15) || !partner.canHold(weddingSendTicket, 15)) { + cm.sendOk("Either you or your partner doesn't have a free ETC slot for the Wedding tickets! Please make some room before trying to register a reservation."); + cm.dispose(); + return; + } + + var hasCommon = cm.haveItem(weddingEntryTicketCommon); + var hasPremium = cm.haveItem(weddingEntryTicketPremium); + + if(hasCommon || hasPremium) { + var weddingType = (hasPremium ? true : false); + + var player = cm.getPlayer(); + var resStatus = cserv.pushWeddingReservation(weddingId, cathedralWedding, weddingType, player.getId(), player.getPartnerId()); + if(resStatus > 0) { + cm.gainItem((weddingType) ? weddingEntryTicketPremium : weddingEntryTicketCommon, -1); + + var expirationTime = cserv.getRelativeWeddingTicketExpireTime(resStatus); + cm.gainItem(weddingSendTicket,15,false,true,expirationTime); + partner.getClient().getAbstractPlayerInteraction().gainItem(weddingSendTicket,15,false,true,expirationTime); + + var placeTime = cserv.getWeddingReservationTimeLeft(weddingId); + + var wedType = weddingType ? "Premium" : "Regular"; + cm.sendOk("You both have received 15 Wedding Tickets, to be given to your guests. #bDouble-click the ticket#k to send it to someone. Invitations can only be sent #rbefore the wedding start time#k. Your #b" + wedType + " wedding#k is set to start at the #r" + placeTime + "#k. Don't be late!"); + + player.dropMessage(6, "Wedding Assistant: You both have received 15 Wedding Tickets. Invitations can only be sent before the wedding start time. Your " + wedType + " wedding is set to start at the " + placeTime + ". Don't be late!"); + partner.dropMessage(6, "Wedding Assistant: You both have received 15 Wedding Tickets. Invitations can only be sent before the wedding start time. Your " + wedType + " wedding is set to start at the " + placeTime + ". Don't be late!"); + + if(!hasSuitForWedding(player)) { + player.dropMessage(5, "Wedding Assistant: Please purchase a wedding garment before showing up for the ceremony. One can be bought at the Wedding Shop left-most Amoria."); + } + + if(!hasSuitForWedding(partner)) { + partner.dropMessage(5, "Wedding Assistant: Please purchase a wedding garment before showing up for the ceremony. One can be bought at the Wedding Shop left-most Amoria."); + } + } else { + cm.sendOk("Your wedding reservation must have been processed recently. Please try again later."); + } + } else { + cm.sendOk("Please have a #b#t" + weddingEntryTicketCommon + "##k available on your CASH inventory before trying to register a reservation."); + } + } + } else { + cm.sendOk("Wedding reservation encountered an error, try again later."); + } + + cm.dispose(); + } else { + cm.sendOk("You do not have an engagement ring."); + cm.dispose(); + } + break; + + default: + if (cm.haveItem(weddingGuestTicket)) { + var cserv = cm.getClient().getChannelServer(); + + wid = cserv.getOngoingWedding(cathedralWedding); + if(wid > 0) { + if(cserv.isOngoingWeddingGuest(cathedralWedding, cm.getPlayer().getId())) { + cm.sendOk("Enjoy the wedding. Don't drop your Gold Maple Leaf or you won't be able to finish the whole wedding."); + } else { + cm.sendOk("Sorry, but you have not been invited for this wedding."); + cm.dispose(); + } + } else { + cm.sendOk("There is no wedding booked right now."); + cm.dispose(); + } + } else { + cm.sendOk("You do not have a #b#t" + weddingGuestTicket + "##k."); + cm.dispose(); + } + } + } else if (status == 2) { // registering guest + var eim = getMarriageInstance(wid); + + if(eim != null) { + if(!cm.canHold(4000313)) { + cm.sendOk("Please have a free ETC slot available to get the #b#t4000313##k."); + cm.dispose(); + return; + } + + cm.gainItem(weddingGuestTicket, -1); + eim.registerPlayer(cm.getPlayer()); //cm.warp(680000210, 0); + } else { + cm.sendOk("The marriage event could not be found."); + } + + cm.dispose(); + } + } else { + if (status == 0) { + var eim = cm.getEventInstance(); + if(eim == null) { + cm.warp(680000000,0); + cm.dispose(); + return; + } + + isMarrying = (cm.getPlayer().getId() == eim.getIntProperty("groomId") || cm.getPlayer().getId() == eim.getIntProperty("brideId")); + + if(eim.getIntProperty("weddingStage") == 0) { + if(!isMarrying) { + cm.sendOk("Welcome to the #b#m" + cm.getMapId() + "##k. Please hang around with the groom and bride while the other guests are gathering here.\r\n\r\nWhen the timer reach it's end the couple will head to the altar, at that time you will be allowed to root over them from the #bguests area#k."); + } else { + cm.sendOk("Welcome to the #b#m" + cm.getMapId() + "##k. Please greet the guests that are already here while the others are coming. When the timer reach it's end the couple will head to the altar."); + } + + cm.dispose(); + } else { + cm.sendYesNo("The #bbride and groom#k are already on their way to the altar. Would you like to join them now?"); + } + } else if (status == 1) { + cm.warp(weddingAltarMapid,"sp"); + cm.dispose(); + } + } + + } } \ No newline at end of file diff --git a/scripts/npc/9201006.js b/scripts/npc/9201006.js index 2abb66c0e2..811054e617 100644 --- a/scripts/npc/9201006.js +++ b/scripts/npc/9201006.js @@ -37,7 +37,7 @@ function start() { function action(mode, type, selection) { if (mode == -1 || mode == 0) { - cm.sendOk("Goodbye then"); + cm.sendOk("Goodbye then."); cm.dispose(); return; } else if (mode == 1) { @@ -45,28 +45,53 @@ function action(mode, type, selection) { } else { status--; } + + var eim = cm.getEventInstance(); + if(eim == null) { + cm.warp(680000000,0); + cm.dispose(); + return; + } + + var isMarrying = (cm.getPlayer().getId() == eim.getIntProperty("groomId") || cm.getPlayer().getId() == eim.getIntProperty("brideId")); switch (status) { case 0: - cm.sendNext("I only warp out people who are here by accident."); - break; - case 1: - var engagementRings = new Array(4031360, 4031358, 4031362, 4031364); var hasEngagement = false; - for (var x = 0; x < engagementRings.length && !hasEngagement; x++) { - if (cm.haveItem(engagementRings[x], 1)) + for (var x = 4031357; x <= 4031364; x++) { + if (cm.haveItem(x, 1)) { hasEngagement = true; + break; + } } - if (cm.haveItem(4000313) && hasEngagement) { - cm.sendOk("Please continue with the wedding."); - cm.dispose(); + + if (cm.haveItem(4000313) && isMarrying) { + if(eim.getIntProperty("weddingStage") == 3) { + cm.sendOk("Congratulations on your wedding. Please talk to #b#p9201007##k to start the afterparty."); + cm.dispose(); + } else if(hasEngagement) { + cm.sendOk("Please continue with the wedding."); + cm.dispose(); + } else { + cm.sendOk("You do not have the required item to continue through this wedding. Unfortunately, it's over..."); + } } else { - cm.warp(680000000,0); - cm.dispose(); + if(eim.getIntProperty("weddingStage") == 3) { + if(!isMarrying) { + cm.sendYesNo("The couple #rhas just married#k, and soon #bthey will start the afterparty#k. You should wait here for them. Are you really ready to #rquit this wedding#k and return to #bAmoria#k?"); + } else { + cm.sendOk("Congratulations on your wedding. Please talk to #b#p9201007##k to start the afterparty."); + cm.dispose(); + } + } else { + cm.sendYesNo("Are you sure you want to #rquit this wedding#k and return to #bAmoria#k?"); + } } break; - case 2: - cm.sendOk("You do not have the required item to continue through this wedding."); + + case 1: + cm.warp(680000000,0); + cm.dispose(); break; } } diff --git a/scripts/npc/9201007.js b/scripts/npc/9201007.js index 6ee9895aa2..811897fdbe 100644 --- a/scripts/npc/9201007.js +++ b/scripts/npc/9201007.js @@ -25,20 +25,64 @@ Angel (get31720 ragezone) -- Extra Info ------------------------------------------------------------------------------------- Fixed by [happydud3] & [XotiCraze] +-- Content Improved by ---------------------------------------------------------------------------- + RonanLana (HeavenMS) --------------------------------------------------------------------------------------------------- **/ var status; -var i; +var eim; +var hasEngage; +var hasRing; function start() { - status = -1; - action(1, 0, 0); + eim = cm.getEventInstance(); + if(eim == null) { + cm.warp(680000000,0); + cm.dispose(); + return; + } + + if(cm.getMapId() == 680000200) { + if(eim.getIntProperty("weddingStage") == 0) { + cm.sendNext("The guests are gathering here right now. Please wait awhile, the ceremony will start soon enough."); + } else { + cm.warp(680000210, "sp"); + cm.sendNext("Pick your seat over here and good show!"); + } + + cm.dispose(); + } else { + if(cm.getPlayer().getId() != eim.getIntProperty("groomId") && cm.getPlayer().getId() != eim.getIntProperty("brideId")) { + cm.sendNext("Sorry, only the marrying couple should be talking to me right now."); + cm.dispose(); + return; + } + + hasEngage = false; + for(var i = 4031357; i <= 4031364; i++) { + if(cm.haveItem(i)) { + hasEngage = true; + break; + } + } + + var rings = [1112806, 1112803, 1112807, 1112809]; + hasRing = false; + for (i = 0; i < rings.length; i++) { + if (cm.getPlayer().haveItemWithId(rings[i], true)) { + hasRing = true; + } + } + + status = -1; + action(1, 0, 0); + } } function action(mode, type, selection) { if (mode == -1 || mode == 0) { - cm.sendOk("Goodbye then"); + cm.sendOk("Goodbye then."); cm.dispose(); return; } else if (mode == 1) { @@ -46,28 +90,16 @@ function action(mode, type, selection) { } else { status--; } - - var engagementRings = Array(4031360, 4031358, 4031362, 4031364); - var hasEngage = false; - for (i = 0; i < engagementRings.length && !hasEngage; i++) { - if (cm.haveItem(engagementRings[i])) - hasEngage = true; - } - var Rings = Array(1112806, 1112803, 1112807, 1112809); - var hasRing = false; - for (i = 0; i < Rings.length; i++) { - if (cm.haveItem(Rings[i])) { - hasRing = true; - } - } - + if (status == 0) { - if (cm.haveItem(4000313) && hasEngage) { + var hasGoldenLeaf = cm.haveItem(4000313); + + if (hasGoldenLeaf && hasEngage) { cm.sendOk("You can't leave yet! You need to click High Priest John and get married before I can let you leave."); cm.dispose(); - } else if (cm.haveItem(4000313) && hasRing) { - var choice = Array("Go to the Cherished Visage Photos", "What should I be doing"); - var msg = "What can I help you with?"; + } else if (hasGoldenLeaf && hasRing) { + var choice = Array("Go to the Afterparty", "What should I be doing"); + var msg = "What can I help you with?#b"; for (i = 0; i < choice.length; i++) { msg += "\r\n#L" + i + "#" + choice[i] + "#l"; } @@ -79,14 +111,22 @@ function action(mode, type, selection) { } else if (status == 1) { switch(selection) { case 0: - cm.warp(680000300, 0); - cm.sendOk("Enjoy! Cherish your Photos Forever!"); + if(eim.getIntProperty("isPremium") == 1) { + eim.warpEventTeam(680000300); + cm.sendOk("Enjoy! Cherish your Photos Forever!"); + } else { // skip the party-time (premium only) + eim.warpEventTeam(680000500); + cm.sendOk("Congratulations for the newly-wed! I will escort you to the exit."); + } + cm.dispose(); break; + case 1: - cm.sendOk("The Bride and Groom must click High Priest John to be wed. When you are ready you can click me to go to the Cherished Visage Photos"); + cm.sendOk("The Bride and Groom must receive the blessings of High Priest John to be wed. When you are ready you can click me to go to the Afterparty."); cm.dispose(); break; + default: cm.warp(680000000,0); cm.dispose(); diff --git a/scripts/npc/9201008.js b/scripts/npc/9201008.js index 2ebeae654a..6dfab10d36 100644 --- a/scripts/npc/9201008.js +++ b/scripts/npc/9201008.js @@ -1,8 +1,6 @@ /* - This file is part of the OdinMS Maple Story Server - Copyright (C) 2008 Patrick Huy - Matthias Butz - Jan Christian Meyer + This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server + Copyleft (L) 2017 RonanLana This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as @@ -19,34 +17,256 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -/* NPC Base - Map Name (Map ID) - Extra NPC info. +/* Assistant Bonnie + Marriage NPC */ var status; - -function start() { - status = -1; - action(1, 0, 0); +var wid; +var isMarrying; + +var cathedralWedding = false; +var weddingEventName = "WeddingChapel"; +var weddingEntryTicketCommon = 5251001; +var weddingEntryTicketPremium = 5251002; +var weddingSendTicket = 4031377; +var weddingGuestTicket = 4031406; +var weddingAltarMapid = 680000110; +var weddingIndoors; + +function isWeddingIndoors(mapid) { + return mapid >= 680000100 && mapid <= 680000500; } -function action(mode, type, selection) { - if (mode == -1) { - cm.dispose(); - } else { - if (mode == 0 && type > 0) { +function hasSuitForWedding(player) { + var baseid = (player.getGender() == 0) ? 1050131 : 1051150; + + for(var i = 0; i < 4; i++) { + if(player.haveItemWithId(baseid + i, true)) { + return true; + } + } + + return false; +} + +function getMarriageInstance(weddingId) { + var em = cm.getEventManager(weddingEventName); + + for (var iterator = em.getInstances().iterator(); iterator.hasNext();) { + var eim = iterator.next(); + + if(eim.getIntProperty("weddingId") == weddingId) { + return eim; + } + } + + return null; +} + +function hasWeddingRing(player) { + var rings = [1112806, 1112803, 1112807, 1112809]; + for (var i = 0; i < rings.length; i++) { + if (player.haveItemWithId(rings[i], true)) { + return true; + } + } + + return false; +} + +function start() { + weddingIndoors = isWeddingIndoors(cm.getMapId()); + status = -1; + + action(1, 0, 0); +} + +function action(mode, type, selection) { + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && type > 0) { + cm.dispose(); + return; + } + if (mode == 1) + status++; + else + status--; + + if(!weddingIndoors) { + var hasEngagement = false; + for (var x = 4031357; x <= 4031364; x++) { + if (cm.haveItem(x, 1)) { + hasEngagement = true; + break; + } + } + + if (status == 0) { + var text = "Welcome to the #bChapel#k! How can I help you?"; + var choice = ["How do I prepare a wedding?", "I have an engagement and want to arrange the wedding", "I am the guest and I'd like to go into the wedding"]; + for (x = 0; x < choice.length; x++) { + text += "\r\n#L" + x + "##b" + choice[x] + "#l"; + } + cm.sendSimple(text); + } else if (status == 1) { + switch(selection) { + case 0: + cm.sendOk("Firstly you need to be #bengaged#k to someone. #p9201000# makes the engagement ring. Once attained the engagement status, purchase a #b#t" + weddingEntryTicketCommon + "##k.\r\nShow me your engagement ring and a wedding ticket, and I will book a reservation for you along with #r15 Wedding Tickets#k. Use them to invite your guests into the wedding. They need 1 each to enter."); + cm.dispose(); + break; + + case 1: + if (hasEngagement) { + var wserv = cm.getClient().getWorldServer(); + var cserv = cm.getClient().getChannelServer(); + var weddingId = wserv.getRelationshipId(cm.getPlayer().getId()); + + if(weddingId > 0) { + if(cserv.isWeddingReserved(weddingId)) { // registration check + var placeTime = cserv.getWeddingReservationTimeLeft(weddingId); + cm.sendOk("Your wedding is set to start at the #r" + placeTime + "#k. Don't be late!"); + } else { + var partner = wserv.getPlayerStorage().getCharacterById(cm.getPlayer().getPartnerId()); + if(partner == null) { + cm.sendOk("Your partner seems to be offline right now... Make sure to get both gathered here when the time comes!"); + cm.dispose(); + return; + } + + if(hasWeddingRing(cm.getPlayer()) || hasWeddingRing(partner)) { + cm.sendOk("Either you or your partner already has a marriage ring."); + cm.dispose(); + return; + } + + if(!cm.getMap().equals(partner.getMap())) { + cm.sendOk("Please let your partner come here as well to register the reservation."); + cm.dispose(); + return; + } + + if(!cm.canHold(weddingSendTicket, 15) || !partner.canHold(weddingSendTicket, 15)) { + cm.sendOk("Either you or your partner doesn't have a free ETC slot for the Wedding tickets! Please make some room before trying to register a reservation."); + cm.dispose(); + return; + } + + var hasCommon = cm.haveItem(weddingEntryTicketCommon); + var hasPremium = cm.haveItem(weddingEntryTicketPremium); + + if(hasCommon || hasPremium) { + var weddingType = (hasPremium ? true : false); + + var player = cm.getPlayer(); + var resStatus = cserv.pushWeddingReservation(weddingId, cathedralWedding, weddingType, player.getId(), player.getPartnerId()); + if(resStatus > 0) { + cm.gainItem((weddingType) ? weddingEntryTicketPremium : weddingEntryTicketCommon, -1); + + var expirationTime = cserv.getRelativeWeddingTicketExpireTime(resStatus); + cm.gainItem(weddingSendTicket,15,false,true,expirationTime); + partner.getClient().getAbstractPlayerInteraction().gainItem(weddingSendTicket,15,false,true,expirationTime); + + var placeTime = cserv.getWeddingReservationTimeLeft(weddingId); + + var wedType = weddingType ? "Premium" : "Regular"; + cm.sendOk("You both have received 15 Wedding Tickets, to be given to your guests. #bDouble-click the ticket#k to send it to someone. Invitations can only be sent #rbefore the wedding start time#k. Your #b" + wedType + " wedding#k is set to start at the #r" + placeTime + "#k. Don't be late!"); + + player.dropMessage(6, "Wedding Assistant: You both have received 15 Wedding Tickets. Invitations can only be sent before the wedding start time. Your " + wedType + " wedding is set to start at the " + placeTime + ". Don't be late!"); + partner.dropMessage(6, "Wedding Assistant: You both have received 15 Wedding Tickets. Invitations can only be sent before the wedding start time. Your " + wedType + " wedding is set to start at the " + placeTime + ". Don't be late!"); + + if(!hasSuitForWedding(player)) { + player.dropMessage(5, "Wedding Assistant: Please purchase a wedding garment before showing up for the ceremony. One can be bought at the Wedding Shop left-most Amoria."); + } + + if(!hasSuitForWedding(partner)) { + partner.dropMessage(5, "Wedding Assistant: Please purchase a wedding garment before showing up for the ceremony. One can be bought at the Wedding Shop left-most Amoria."); + } + } else { + cm.sendOk("Your wedding reservation must have been processed recently. Please try again later."); + } + } else { + cm.sendOk("Please have a #b#t" + weddingEntryTicketCommon + "##k available on your CASH inventory before trying to register a reservation."); + } + } + } else { + cm.sendOk("Wedding reservation encountered an error, try again later."); + } + + cm.dispose(); + } else { + cm.sendOk("You do not have an engagement ring."); + cm.dispose(); + } + break; + + default: + if (cm.haveItem(weddingGuestTicket)) { + var cserv = cm.getClient().getChannelServer(); + + wid = cserv.getOngoingWedding(cathedralWedding); + if(wid > 0) { + if(cserv.isOngoingWeddingGuest(cathedralWedding, cm.getPlayer().getId())) { + cm.sendOk("Enjoy the wedding. Don't drop your Gold Maple Leaf or you won't be able to finish the whole wedding."); + } else { + cm.sendOk("Sorry, but you have not been invited for this wedding."); + cm.dispose(); + } + } else { + cm.sendOk("There is no wedding booked right now."); + cm.dispose(); + } + } else { + cm.sendOk("You do not have a #b#t" + weddingGuestTicket + "##k."); + cm.dispose(); + } + } + } else if (status == 2) { // registering guest + var eim = getMarriageInstance(wid); + + if(eim != null) { + if(!cm.canHold(4000313)) { + cm.sendOk("Please have a free ETC slot available to get the #b#t4000313##k."); cm.dispose(); return; + } + + cm.gainItem(weddingGuestTicket, -1); + eim.registerPlayer(cm.getPlayer()); //cm.warp(680000210, 0); + } else { + cm.sendOk("The marriage event could not be found."); } - if (mode == 1) - status++; - else - status--; - - if(status == 0) { - cm.sendOk("Wedding is currently closed."); - cm.dispose(); + + cm.dispose(); + } + } else { + if (status == 0) { + var eim = cm.getEventInstance(); + if(eim == null) { + cm.warp(680000000,0); + cm.dispose(); + return; } + + isMarrying = (cm.getPlayer().getId() == eim.getIntProperty("groomId") || cm.getPlayer().getId() == eim.getIntProperty("brideId")); + + if(eim.getIntProperty("weddingStage") == 0) { + if(!isMarrying) { + cm.sendOk("Welcome to the #b#m" + cm.getMapId() + "##k. Please hang around with the groom and bride while the other guests are gathering here.\r\n\r\nWhen the timer reach it's end the couple will head to the altar, at that time you will be allowed to root over them from the #bguests area#k."); + } else { + cm.sendOk("Welcome to the #b#m" + cm.getMapId() + "##k. Please greet the guests that are already here while the others are coming. When the timer reach it's end the couple will head to the altar."); + } + + cm.dispose(); + } else { + cm.sendYesNo("The #bbride and groom#k are already on their way to the altar. Would you like to join them now?"); + } + } else if (status == 1) { + cm.warp(weddingAltarMapid,"sp"); + cm.dispose(); + } } + } } \ No newline at end of file diff --git a/scripts/npc/9201009.js b/scripts/npc/9201009.js new file mode 100644 index 0000000000..5899b2bdf5 --- /dev/null +++ b/scripts/npc/9201009.js @@ -0,0 +1,136 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +/** + Assistant Nancy +-- By --------------------------------------------------------------------------------------------- + Angel (get31720 ragezone) +-- Extra Info ------------------------------------------------------------------------------------- + Fixed by [happydud3] & [XotiCraze] +-- Content Improved by ---------------------------------------------------------------------------- + RonanLana (HeavenMS) +--------------------------------------------------------------------------------------------------- +**/ + +var status; +var eim; +var hasEngage; +var hasRing; + +function start() { + eim = cm.getEventInstance(); + if(eim == null) { + cm.warp(680000000,0); + cm.dispose(); + return; + } + + if(cm.getMapId() == 680000200) { + if(eim.getIntProperty("weddingStage") == 0) { + cm.sendNext("The guests are gathering here right now. Please wait awhile, the ceremony will start soon enough."); + } else { + cm.warp(680000210, "sp"); + cm.sendNext("Pick your seat over here and good show!"); + } + + cm.dispose(); + } else { + if(cm.getPlayer().getId() != eim.getIntProperty("groomId") && cm.getPlayer().getId() != eim.getIntProperty("brideId")) { + cm.sendNext("Sorry, only the marrying couple should be talking to me right now."); + cm.dispose(); + return; + } + + hasEngage = false; + for(var i = 4031357; i <= 4031364; i++) { + if(cm.haveItem(i)) { + hasEngage = true; + break; + } + } + + var rings = [1112806, 1112803, 1112807, 1112809]; + hasRing = false; + for (i = 0; i < rings.length; i++) { + if (cm.getPlayer().haveItemWithId(rings[i], true)) { + hasRing = true; + } + } + + status = -1; + action(1, 0, 0); + } +} + +function action(mode, type, selection) { + if (mode == -1 || mode == 0) { + cm.sendOk("Goodbye then."); + cm.dispose(); + return; + } else if (mode == 1) { + status++; + } else { + status--; + } + + if (status == 0) { + var hasGoldenLeaf = cm.haveItem(4000313); + + if (hasGoldenLeaf && hasEngage) { + cm.sendOk("You can't leave yet! You need to click Pelvis Bebop and get his word before I can let you leave."); + cm.dispose(); + } else if (hasGoldenLeaf && hasRing) { + var choice = Array("Go to the Afterparty", "What should I be doing"); + var msg = "What can I help you with?#b"; + for (i = 0; i < choice.length; i++) { + msg += "\r\n#L" + i + "#" + choice[i] + "#l"; + } + cm.sendSimple(msg); + } else { + cm.sendNext("You don't seem to have a Gold Maple Leaf, engagement ring, or wedding ring. You must not belong here, so I will take you to Amoria."); + selection = 20; // Random. + } + } else if (status == 1) { + switch(selection) { + case 0: + if(eim.getIntProperty("isPremium") == 1) { + eim.warpEventTeam(680000300); + cm.sendOk("Enjoy! Cherish your Photos Forever!"); + } else { // skip the party-time (premium only) + eim.warpEventTeam(680000500); + cm.sendOk("Congratulations for the newly-wed! I will escort you to the exit."); + } + + cm.dispose(); + break; + + case 1: + cm.sendOk("The superstars must receive the word of Pelvis Bebop to be united. When you are ready you can click me to go to the Afterparty."); + cm.dispose(); + break; + + default: + cm.warp(680000000,0); + cm.dispose(); + break; + } + } +} diff --git a/scripts/npc/9201010.js b/scripts/npc/9201010.js index 38c122af57..8967cf303d 100644 --- a/scripts/npc/9201010.js +++ b/scripts/npc/9201010.js @@ -29,17 +29,37 @@ **/ var status; - + function start() { - if (cm.haveItem(4000313)) { - cm.sendOk("You are a guest. Please continue with the wedding. I only warp out people who are here by accident."); - cm.dispose(); - } else - cm.sendNext("I warp people out. If you are the newly wed don't click next or you will not be able to collect your prize at the end."); + status = -1; + action(1, 0, 0); } function action(mode, type, selection) { - if (mode > 1) - cm.warp(680000000); - cm.dispose(); + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && type > 0) { + cm.dispose(); + return; + } + if (mode == 1) + status++; + else + status--; + + if(status == 0) { + var eim = cm.getEventInstance(); + if(eim == null) { + cm.warp(680000000,0); + cm.dispose(); + return; + } + + cm.sendYesNo("Are you sure you want to #rquit this wedding#k and return to #bAmoria#k? You will be #rskipping the bonus stages#k if you accept to quit."); + } else if(status == 1) { + cm.warp(680000000,0); + cm.dispose(); + } + } } \ No newline at end of file diff --git a/scripts/npc/9201011.js b/scripts/npc/9201011.js new file mode 100644 index 0000000000..e4ce97f8ca --- /dev/null +++ b/scripts/npc/9201011.js @@ -0,0 +1,265 @@ +/* + This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server + Copyleft (L) 2017 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +/* Pelvis Bebop + Marriage NPC + */ + +importPackage(Packages.tools.packets); +importPackage(Packages.net.server.channel.handlers); + +var status; +var state; +var eim; +var weddingEventName = "WeddingChapel"; +var cathedralWedding = false; +var weddingIndoors; +var weddingBlessingExp = 30000; + +function detectPlayerItemid(player) { + for (var x = 4031357; x <= 4031364; x++) { + if (player.haveItem(x)) { + return x; + } + } + + return -1; +} + +function getRingId(boxItemId) { + return boxItemId == 4031357 ? 1112803 : (boxItemId == 4031359 ? 1112806 : (boxItemId == 4031361 ? 1112807 : (boxItemId == 4031363 ? 1112809 : -1))); +} + +function isSuitedForWedding(player, equipped) { + var baseid = (player.getGender() == 0) ? 1050131 : 1051150; + + if(equipped) { + for(var i = 0; i < 4; i++) { + if(player.haveItemEquipped(baseid + i)) { + return true; + } + } + } else { + for(var i = 0; i < 4; i++) { + if(player.haveItemWithId(baseid + i, true)) { + return true; + } + } + } + + return false; +} + +function getWeddingPreparationStatus(player, partner) { + if(!player.haveItem(4000313)) return -3; + if(!partner.haveItem(4000313)) return 3; + + if(!isSuitedForWedding(player, true)) return -4; + if(!isSuitedForWedding(partner, true)) return 4; + + var hasEngagement = false; + for (var x = 4031357; x <= 4031364; x++) { + if (player.haveItem(x)) { + hasEngagement = true; + break; + } + } + if(!hasEngagement) return -1; + + hasEngagement = false; + for (var x = 4031357; x <= 4031364; x++) { + if (partner.haveItem(x)) { + hasEngagement = true; + break; + } + } + if(!hasEngagement) return -2; + + if(!player.canHold(1112803)) return 1; + if(!partner.canHold(1112803)) return 2; + + return 0; +} + +function giveCoupleBlessings(eim, player, partner) { + var blessCount = eim.gridSize(); + + player.gainExp(blessCount * weddingBlessingExp); + partner.gainExp(blessCount * weddingBlessingExp); +} + +function start() { + eim = cm.getEventInstance(); + + status = -1; + action(1, 0, 0); +} + +function action(mode, type, selection) { + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && type > 0) { + cm.dispose(); + return; + } + if (mode == 1) + status++; + else + status--; + + if (status == 0) { + if(eim == null) { + cm.warp(680000000,0); + cm.dispose(); + return; + } + + var playerId = cm.getPlayer().getId(); + if(playerId == eim.getIntProperty("groomId") || playerId == eim.getIntProperty("brideId")) { + var wstg = eim.getIntProperty("weddingStage"); + + if(wstg == 2) { + cm.sendYesNo("Awhoooooooooosh~, the guests have proclaimed their love to y'all. The time has come baby~, #rshould I make you Husband and Wife#k?"); + state = 1; + } else if(wstg == 1) { + cm.sendOk("W-whoah wait a bit alright? Your guests are currently giving their live to y'all. Let's shake this place up, baby~~."); + cm.dispose(); + } else { + cm.sendOk("Wheeeeeeeeeeeeeew! Our festival here is now complete, give a sweet talk to #b#p9201009##k, she will lead you and your folks to the afterparty. Cheers for your love!"); + cm.dispose(); + } + } else { + var wstg = eim.getIntProperty("weddingStage"); + if(wstg == 1) { + if(eim.gridCheck(cm.getPlayer()) != -1) { + cm.sendOk("Everyone let's shake this place up! Let's rock 'n' roll!!"); + cm.dispose(); + } else { + if(eim.getIntProperty("guestBlessings") == 1) { + cm.sendYesNo("Will you manifest your love to the superstars here present?"); + state = 0; + } else { + cm.sendOk("Our superstars are gathered down here. Everyone, let's give them some nice, nicey party~!"); + cm.dispose(); + } + } + } else if(wstg == 3) { + cm.sendOk("Whooooooo-hoo! The couple's love now are like one super big shiny heart right now! And it shall go on ever after this festival. Please #rget ready for the afterparty#k, baby~. Follow the married couple's lead!"); + cm.dispose(); + } else { + cm.sendOk("It's now guys... Stay with your eyes and ears keened up! They are about to smooch it all over the place!!!"); + cm.dispose(); + } + } + } else if (status == 1) { + if(state == 0) { // give player blessings + eim.gridInsert(cm.getPlayer(), 1); + + cm.sendOk("Way to go, my friend! Your LOVE has been added to theirs, now in one bigger heart-shaped sentiment that will remain lively in our hearts forever! Who-hoo~!"); + cm.dispose(); + } else { // couple wants to complete the wedding + var wstg = eim.getIntProperty("weddingStage"); + + if(wstg == 2) { + var pid = cm.getPlayer().getPartnerId(); + if(pid <= 0) { + cm.sendOk("Huh~.... Wait wait, did you just break that thing you had right now?? Oh my, what happened?"); + cm.dispose(); + return; + } + + var player = cm.getPlayer(); + var partner = cm.getMap().getCharacterById(cm.getPlayer().getPartnerId()); + if(partner != null) { + state = getWeddingPreparationStatus(player, partner); + + switch(state) { + case 0: + eim.setIntProperty("weddingStage", 3); + var cmPartner = partner.getClient().getAbstractPlayerInteraction(); + + var playerItemId = detectPlayerItemid(player); + var partnerItemId = (playerItemId % 2 == 1) ? playerItemId + 1 : playerItemId - 1; + + var marriageRingId = getRingId((playerItemId % 2 == 1) ? playerItemId : partnerItemId); + + cm.gainItem(playerItemId, -1); + cmPartner.gainItem(partnerItemId, -1); + + RingActionHandler.giveMarriageRings(player, partner, marriageRingId); + player.setMarriageItemId(marriageRingId); + partner.setMarriageItemId(marriageRingId); + + //var marriageId = eim.getIntProperty("weddingId"); + //player.announce(Wedding.OnMarriageResult(marriageId, player, true)); + //partner.announce(Wedding.OnMarriageResult(marriageId, player, true)); + + giveCoupleBlessings(eim, player, partner); + + cm.getMap().dropMessage(6, "Wayne: I'll call it out right now, and it shall go on: you guys are the key of the other's lock, a lace of a pendant. That's it, snog yourselves!"); + eim.schedule("showMarriedMsg", 2 * 1000); + break; + + case -1: + cm.sendOk("Well, it seems you no longer have the ring/ring box you guys exchanged at the engagement. Awww man~"); + break; + + case -2: + cm.sendOk("Well, it seems your partner no longer has the ring/ring box you guys exchanged at the engagement. Awww man~"); + break; + + case -3: + cm.sendOk("Well, it seems you don't have the #r#t4000313##k given at the entrance... Please find it, baby~"); + break; + + case -4: + cm.sendOk("Aww I know that shucks, but the fashionable wedding clothes does a essential part here. Please wear it before talking to me."); + break; + + case 1: + cm.sendOk("Please make an EQUIP slot available to get the marriage ring, will you?"); + break; + + case 2: + cm.sendOk("Please let your partner know to make an EQUIP slot available to get the marriage ring, will you?"); + break; + + case 3: + cm.sendOk("Well, it seems your partner don't have the #r#t4000313##k given at the entrance... Please find it, I can't call the finally without it."); + break; + + case 4: + cm.sendOk("Aww I know that shucks, but it seems your partner is not using the fashionable wedding clothes. Please tell them to wear it before talking to me."); + break; + } + + cm.dispose(); + } else { + cm.sendOk("Oof, is that it that your partner is not here, right now? ... Oh noes, I'm afraid I can't call the finally if your partner is not here."); + cm.dispose(); + } + } else { + cm.sendOk("Wheeeeeeeeeeeeew~ You are now #bofficially one couple#k, and a brilliant one. Your moves fitted in outstandingly, congratulations!"); + cm.dispose(); + } + } + } + } +} \ No newline at end of file diff --git a/scripts/npc/9201012.js b/scripts/npc/9201012.js index 2ebeae654a..55ad88b159 100644 --- a/scripts/npc/9201012.js +++ b/scripts/npc/9201012.js @@ -1,8 +1,6 @@ /* - This file is part of the OdinMS Maple Story Server - Copyright (C) 2008 Patrick Huy - Matthias Butz - Jan Christian Meyer + This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server + Copyleft (L) 2017 RonanLana This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as @@ -19,34 +17,166 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -/* NPC Base - Map Name (Map ID) - Extra NPC info. +/* Wayne + Marriage NPC */ var status; - -function start() { - status = -1; - action(1, 0, 0); +var state; +var eim; +var weddingEventName = "WeddingChapel"; +var cathedralWedding = false; + + +function isSuitedForWedding(player, equipped) { + var baseid = (player.getGender() == 0) ? 1050131 : 1051150; + + if(equipped) { + for(var i = 0; i < 4; i++) { + if(player.haveItemEquipped(baseid + i)) { + return true; + } + } + } else { + for(var i = 0; i < 4; i++) { + if(player.haveItemWithId(baseid + i, true)) { + return true; + } + } + } + + return false; +} + +function getMarriageInstance(player) { + var em = cm.getEventManager(weddingEventName); + + for (var iterator = em.getInstances().iterator(); iterator.hasNext();) { + var eim = iterator.next(); + if(eim.isEventLeader(player)) { + return eim; + } + } + + return null; +} + +function start() { + status = -1; + action(1, 0, 0); } function action(mode, type, selection) { - if (mode == -1) { - cm.dispose(); - } else { - if (mode == 0 && type > 0) { - cm.dispose(); - return; - } - if (mode == 1) - status++; - else - status--; - - if(status == 0) { - cm.sendOk("Wedding is currently closed."); - cm.dispose(); - } + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && type > 0) { + cm.dispose(); + return; } + if (mode == 1) + status++; + else + status--; + + if(status == 0) { + var hasEngagement = false; + for (var x = 4031357; x <= 4031364; x++) { + if (cm.haveItem(x, 1)) { + hasEngagement = true; + break; + } + } + + if(hasEngagement) { + var text = "Hi there. How about skyrocket the day with your fiancee baby~?"; + var choice = new Array("We're ready to get married."); + for (x = 0; x < choice.length; x++) { + text += "\r\n#L" + x + "##b" + choice[x] + "#l"; + } + cm.sendSimple(text); + } else { + cm.sendOk("Hi there, folks. Even thought of having a wedding held on Amoria? When the talk is about wedding, everyone firstly thinks about Amoria, there is no miss to it. Our chapel here is renowned around the Maple world for offering the best wedding services for maplers!"); + cm.dispose(); + } + } else if(status == 1) { + var wid = cm.getClient().getWorldServer().getRelationshipId(cm.getPlayer().getId()); + var cserv = cm.getClient().getChannelServer(); + + if(cserv.isWeddingReserved(wid)) { + if(wid == cserv.getOngoingWedding(cathedralWedding)) { + var partner = cserv.getPlayerStorage().getCharacterById(cm.getPlayer().getPartnerId()); + if(!(partner == null || !cm.getMap().equals(partner.getMap()))) { + if(!cm.canHold(4000313)) { + cm.sendOk("Please have a free ETC slot available to get the #b#t4000313##k."); + cm.dispose(); + return; + } else if(!partner.canHold(4000313)) { + cm.sendOk("Please let your partner know they must have a free ETC slot available to get the #b#t4000313##k."); + cm.dispose(); + return; + } else if(!isSuitedForWedding(cm.getPlayer(), false)) { + cm.sendOk("Please purchase fashionable #rwedding clothes#k for the wedding, quickly! It's time to shine, baby~!"); + cm.dispose(); + return; + } else if(!isSuitedForWedding(partner, false)) { + cm.sendOk("Your partner must know they must have fashionable #rwedding clothes#k for the wedding. It's time to shine, baby~!"); + cm.dispose(); + return; + } + + cm.sendOk("Alright! The couple appeared here stylish as ever. Let's go folks, let's rock 'n' roll!!!"); + } else { + cm.sendOk("Aww, your partner is elsewhere... Both must be here for the wedding, else it's going to be sooooo lame."); + cm.dispose(); + } + } else { + var placeTime = cserv.getWeddingReservationTimeLeft(wid); + + cm.sendOk("Yo. Your wedding is set to happen at the #r" + placeTime + "#k, don't be late will you?"); + cm.dispose(); + } + } else { + cm.sendOk("Aawww, I'm sorry but there are no reservations made for you at this channel for the time being."); + cm.dispose(); + } + } else if(status == 2) { + var cserv = cm.getClient().getChannelServer(); + var partner = cserv.getPlayerStorage().getCharacterById(cm.getPlayer().getPartnerId()); + if(!(partner == null || !cm.getMap().equals(partner.getMap()))) { + if(cserv.acceptOngoingWedding(cathedralWedding)) { + var wid = cm.getClient().getWorldServer().getRelationshipId(cm.getPlayer().getId()); + if(wid > 0) { + var em = cm.getEventManager(weddingEventName); + if(em.startInstance(cm.getPlayer())) { + eim = getMarriageInstance(cm.getPlayer()); + if(eim != null) { + eim.setIntProperty("weddingId", wid); + eim.setIntProperty("groomId", cm.getPlayer().getId()); + eim.setIntProperty("brideId", cm.getPlayer().getPartnerId()); + eim.setIntProperty("isPremium", cserv.getOngoingWeddingType(cathedralWedding) ? 1 : 0); + + eim.registerPlayer(partner); + } else { + cm.sendOk("An unexpected error happened when locating the wedding event. Please try again later."); + } + + cm.dispose(); + } else { + cm.sendOk("An unexpected error happened before the wedding preparations. Please try again later."); + cm.dispose(); + } + } else { + cm.sendOk("An unexpected error happened before the wedding preparations. Please try again later."); + cm.dispose(); + } + } else { // partner already decided to start + cm.dispose(); + } + } else { + cm.sendOk("Aww, it seems your partner is elsewhere... Both must be here for the wedding, else it's going to be sooooo lame."); + cm.dispose(); + } + } + } } \ No newline at end of file diff --git a/scripts/npc/9201013.js b/scripts/npc/9201013.js index ba041d3b3c..f5ddbebf4e 100644 --- a/scripts/npc/9201013.js +++ b/scripts/npc/9201013.js @@ -19,51 +19,11 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -/* victora -by Angel (get31720 ragezone) +/* Victoria +by Ronan */ -var wui = 0; - function start() { - cm.sendSimple ("Welcome to the Cathedral. What would you like to do? \r\n#L0##bI need invitations for my guests#k #l\r\n#L1##bI'd like to prepare a wedding#k #l\r\n#L2##bCan you explain how I should prepare a wedding?#k #l\r\n#L3##bI am either the groom or bride and I'd like to go in#k #l\r\n#L4##bI am a guest and I'd like to go in#k #l"); -} - -function action(mode, type, selection) { + cm.sendOk("Welcome to the Cathedral.\r\n\r\n#bCouples#k wanting to marry on the Cathedral should first #barrange a reservation#k with #r#p9201005##k. When the arranged time comes #bboth#k must show up here, on the same channel from the reservation, and start the ceremony (there is a 10-minutes fault policy to this) by talking to #r#p9201002##k. Once arranged, both can #bdistribute tickets#k to friends or acquainteds to become the guests for the marriage.\r\n\r\nThe ceremony will start accepting #bguests#k after the groom and the bride has entered the building. Show the #b#t4000313##k to #r#p9201005##k to access the inner rooms. No one without a ticket is allowed to enter the stage!"); cm.dispose(); - if (selection == 0) { - if (cm.haveItem(4214002)) { - cm.sendNext("Alright here are you invitations make sure your guest have them or they can't come in!"); - cm.gainItem(4031395,10); - - } else { - cm.sendOk("Sorry but please make sure you have your premium wedding receipt or you won't be able to have your wedding"); - status = 9; - } - } else if (selection == 1) { - if (cm.haveItem(5251003)) { - cm.sendNext("Alright, I'll give you your premium wedding receipt and make sure you don't lose it! If you lose your receipt you won't be able to get invitations or enter the cathedral!"); - cm.gainItem(4214002,1); - } else if (selection == 2) { - cm.sendNext("Have both the bride and groom buy a premium cathedral wedding ticket from the cash shop. Then ask me to prepare your wedding and i'll give you a wedding receipt. Talk to me if you want invitations so other guests can join. When you're ready just have everyone come to me and i'll let you or the guests in. Inside Debbie will warp you out to Amoria if you chose to leave. Nicole will warp you to the next map."); - } else if (selection == 3) { - if (cm.haveItem(4214002)) { - cm.sendNext("Okay go on in. Once you're ready click the Priest and he'll get you married."); - cm.warp(680000210, 2); - } else { - cm.sendOk("Sorry but you don't have a wedding receipt."); - status = 9; - } - } else if (selection == 4) { - if (cm.haveItem(4031395)) { - cm.sendNext("Okay go on in. Once the bride and groom is ready click Nicole on the bottom to warp to the next map. Or use Debbie to leave to Amoria."); - cm.warp(680000210,0); - - } else { - cm.sendOk("Sorry but you don't have a premium wedding invitation."); - status = 9; - } - cm.dispose(); - } - } -} \ No newline at end of file +} diff --git a/scripts/npc/9201014.js b/scripts/npc/9201014.js index 5b1f37854f..24d16259b8 100644 --- a/scripts/npc/9201014.js +++ b/scripts/npc/9201014.js @@ -25,9 +25,13 @@ Angel (get31720 ragezone) -- Extra Info ------------------------------------------------------------------------------------- Fixed by [happydud3] & [XotiCraze] + Improved by [RonanLana] --------------------------------------------------------------------------------------------------- **/ +var bgPrizes = [[2022179,10], [2022282,10], [2210005,5], [2210003,5]]; +var cmPrizes = [[2022011,10], [2000005,50], [2022273,10], [2022179,3]]; + var status; function start() { @@ -37,7 +41,7 @@ function start() { function action(mode, type, selection) { if (mode == -1 || mode == 0) { - cm.sendOk("Goodbye then"); + cm.sendOk("Goodbye then."); cm.dispose(); return; } else if (mode == 1) { @@ -47,7 +51,7 @@ function action(mode, type, selection) { } if (status == 0) { - var msg = "Hello I exchange Onyx Chest for Bride and Groom and the Onyx Chest for prizes!"; + var msg = "Hello I exchange Onyx Chest for Bride and Groom and the Onyx Chest for prizes!#b"; var choice1 = new Array("I have an Onyx Chest for Bride and Groom", "I have an Onyx Chest"); for (var i = 0; i < choice1.length; i++) { msg += "\r\n#L" + i + "#" + choice1[i] + "#l"; @@ -56,38 +60,41 @@ function action(mode, type, selection) { } else if (status == 1) { if (selection == 0) { if (cm.haveItem(4031424)) { - var rand = Math.floor(Math.random() * 4); - if (rand == 0) - cm.gainItem(2022179,10); - else if (rand == 1) - cm.gainItem(2022282,10); - else if (rand == 2) - cm.gainItem(2210005,5); - else if (rand == 3) - cm.gainItem(2210003,5); - cm.gainItem(4031424,-1); + if (cm.isMarried()) { + if(cm.getInventory(2).getNextFreeSlot() >= 0) { + var rand = Math.floor(Math.random() * bgPrizes.length); + cm.gainItem(bgPrizes[rand][0], bgPrizes[rand][1]); + + cm.gainItem(4031424,-1); + cm.dispose(); + } else { + cm.sendOk("You don't have a free USE slot right now."); + cm.dispose(); + } + } else { + cm.sendOk("You must be married to claim the prize for this box."); + cm.dispose(); + } } else { cm.sendOk("You don't have an Onyx Chest for Bride and Groom."); cm.dispose(); } } else if (selection == 1) { if (cm.haveItem(4031423)) { - cm.sendSimple("You may choose your prize.\r\n#L0#Triangular Sushi#l\r\n#L1#50 power elixers#l\r\n#L2#10 Swiss Cheese#l\r\n#L3#3 Onyx Apples#l"); + if(cm.getInventory(2).getNextFreeSlot() >= 0) { + var rand = Math.floor(Math.random() * cmPrizes.length); + cm.gainItem(cmPrizes[rand][0], cmPrizes[rand][1]); + + cm.gainItem(4031423,-1); + cm.dispose(); + } else { + cm.sendOk("You don't have a free USE slot right now."); + cm.dispose(); + } } else { - cm.sendOk("You don't have an Onyx Chest"); + cm.sendOk("You don't have an Onyx Chest."); cm.dispose(); } } - } else if (status == 2) { - if (selection == 0) - cm.gainItem(2022011,10); - else if (selection == 1) - cm.gainItem(2000005,50); - else if (selection == 2) - cm.gainItem(2022273,10); - else if (selection == 3) - cm.gainItem(2022179,3); - cm.gainItem(4031423,-1); - cm.dispose(); } } diff --git a/scripts/npc/9201021.js b/scripts/npc/9201021.js index dd6e86a3bf..2198fcd7ab 100644 --- a/scripts/npc/9201021.js +++ b/scripts/npc/9201021.js @@ -22,7 +22,8 @@ var status = 0; function start() { - cm.sendSimple("Hello, where would you like to go?\r\n#L0#Untamed Hearts Hunting Ground#l\r\n#L1#I have 7 keys. Bring me to smash boxes#l\r\n#L2#Please warp me out.#l"); + if(cm.getMapId() != 680000401) cm.sendSimple("Hello, where would you like to go?\r\n#b" + ((cm.getMapId() != 680000400) ? "#L0#Untamed Hearts Hunting Ground#l\r\n" : "") + ((cm.getMapId() == 680000400) ? "#L1#I have 7 keys. Bring me to smash boxes#l\r\n" : "") + "#L2#Please warp me out.#l#k"); + else cm.sendSimple("Hello, do you want to go back now? Returning here again will cost you #rother 7 keys#k.\r\n#b#L2#Please warp me back to the training grounds.#l#k"); } function action(mode, type, selection) { @@ -36,17 +37,30 @@ function action(mode, type, selection) { else status--; if (status == 1) { - if (selection < 1) + if (selection < 1) { + if(!cm.haveItem(4000313, 1)) { + cm.sendOk("It seems like you lost your #b#t4000313##k. I'm sorry, but I can't let you proceed to the hunting grounds without that."); + cm.dispose(); + return; + } + cm.warp(680000400, 0); - else if (selection < 2) { - if (cm.haveItem(4031217,7)) + } else if (selection < 2) { + if (cm.haveItem(4031217,7)) { cm.gainItem(4031217, -7); - else - cm.sendOk("It seems like you don't have 7 Keys. Kill the cakes and candles in the Untamed Heart Hunting Ground to get keys. "); + cm.warp(680000401, 0); + } else { + cm.sendOk("It seems like you don't have 7 Keys. Kill the cakes and candles in the Untamed Heart Hunting Ground to get keys."); + } } else if (selection > 1) { - cm.warp(680000500, 0); - cm.sendOk("Goodbye. I hope you enjoyed the wedding!"); + if(cm.getMapId() != 680000401) { + cm.warp(680000500, 0); + cm.sendOk("Goodbye. I hope you enjoyed the wedding!"); + } else { + cm.warp(680000400, 0); + } } + cm.dispose(); } } \ No newline at end of file diff --git a/scripts/npc/9201023.js b/scripts/npc/9201023.js index 640434c8ac..e03ad10988 100644 --- a/scripts/npc/9201023.js +++ b/scripts/npc/9201023.js @@ -1,8 +1,6 @@ /* - This file is part of the OdinMS Maple Story Server - Copyright (C) 2008 Patrick Huy - Matthias Butz - Jan Christian Meyer + This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server + Copyleft (L) 2017 RonanLana This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as @@ -19,47 +17,129 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -/** - *9201023 - Nana(K) - *@author Jvlaple +/* Nana, the Love fairy + Amoria (680000000) + Engagement ring NPC. */ - -function start() { - cm.sendOk("Hi, I'm Nana the love fairy... Hows it going?"); - cm.dispose(); + +var status; +var state; + +var item; +var mats; +var matQty; +var cost; + +var options; + +function hasProofOfLoves(player) { + var count = 0; + + for(var i = 4031367; i <= 4031372; i++) { + if(player.haveItem(i)) { + count++; + } + } + + return count >= 4; } -// -//function action(mode, type, selection) { -// if (mode == -1) { -// cm.dispose(); -// } else { -// if (mode == 0 && status == 0) { -// cm.dispose(); -// return; -// } -// if (mode == 1) -// status++; -// else -// status--; -// if (cm.getPlayer().getMarriageQuestLevel() == 1 || cm.getPlayer().getMarriageQuestLevel() == 52) { -// if (!cm.haveItem(4000083, 20)) { -// if (status == 0) { -// cm.sendNext("Hey, you look like you need proofs of love? I can get them for you."); -// } else if (status == 1) { -// cm.sendNext("All you have to do is bring me 20 #bJr. Sentinel Pieces.#k."); -// cm.dispose(); -// } -// } else { -// if (status == 0) { -// cm.sendNext("Wow, you were quick! Heres the proof of love..."); -// cm.gainItem(4000083, -20) -// cm.gainItem(4031369, 1); -// cm.dispose(); -// } -// } -// } else { -// cm.sendOk("Hi, I'm Nana the love fairy... Hows it going?"); -// cm.dispose(); -// } -// } -//} \ No newline at end of file + +function getNanaLocation(player) { + var mapid = player.getMap().getId(); + + for(var i = 0; i < mapids.length; i++) { + if(mapid == mapids[i]) { + return i; + } + } + + return -1; +} + +var nanaLoc; +var mapids = [100000000, 103000000, 102000000, 101000000, 200000000, 220000000]; +var questItems = [4000001, 4000037, 4000215, 4000026, 4000070, 4000128]; +var questExp = [2000, 5000, 10000, 17000, 22000, 30000]; + +function processNanaQuest() { + if(cm.haveItem(questItems[nanaLoc], 50)) { + if(cm.canHold(4031367 + nanaLoc, 1)) { + cm.gainItem(questItems[nanaLoc], -50); + cm.gainItem(4031367 + nanaLoc, 1); + + cm.sendOk("Kyaaaa~ Thank you a lot, here get the #b#t4031367##k."); + return true; + } else { + cm.sendOk("Please have a free ETC slot available to hold the token of love."); + } + } else { + cm.sendOk("Please gather to me #b50 #t" + questItems[nanaLoc] + "##k."); + } + + return false; +} + +function start() { + status = -1; + action(1, 0, 0); +} + +function action(mode, type, selection) { + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && type > 0) { + cm.dispose(); + return; + } + if (mode == 1) + status++; + else + status--; + + if(status == 0) { + if(!cm.isQuestStarted(100400)) { + cm.sendOk("Hello #b#h0##k, I'm #p9201001# the fairy of Love."); + cm.dispose(); + return; + } + + nanaLoc = getNanaLocation(cm.getPlayer()); + if(nanaLoc == -1) { + cm.sendOk("Hello #b#h0##k, I'm #p9201001# the fairy of Love."); + cm.dispose(); + return; + } + + if(!cm.haveItem(4031367 + nanaLoc, 1)) { + if(cm.isQuestCompleted(100401 + nanaLoc)) { + state = 1; + cm.sendAcceptDecline("Did you lost the #k#t4031367##k I gave to you? Well, I can share another one with you, but you will need to redo the favor I asked last time, is that ok? I need you to bring me #r50 #t" + questItems[nanaLoc] + "#'s.#k"); + } else if(cm.isQuestStarted(100401 + nanaLoc)) { + if(processNanaQuest()) { + cm.gainExp(questExp[nanaLoc] * cm.getPlayer().getExpRate()); + cm.completeQuest(100401 + nanaLoc); + } + + cm.dispose(); + } else { + state = 0; + cm.sendAcceptDecline("Are you searching for #k#t4031367#'s#k? I can share one with you, but you must do a favor for me, is that ok?"); + } + } else { + cm.sendOk("Hey there. Did you get the #t4031367# from the other Nana's already?"); + cm.dispose(); + } + } else if(status == 1) { + if(state == 0) { + cm.startQuest(100401 + nanaLoc); + + cm.sendOk("I need you to collect #r50 #t" + questItems[nanaLoc] + "##k."); + cm.dispose(); + } else { + processNanaQuest(); + cm.dispose(); + } + } + } +} \ No newline at end of file diff --git a/scripts/npc/9201024.js b/scripts/npc/9201024.js index c70634b9da..e03ad10988 100644 --- a/scripts/npc/9201024.js +++ b/scripts/npc/9201024.js @@ -1,8 +1,6 @@ /* - This file is part of the OdinMS Maple Story Server - Copyright (C) 2008 Patrick Huy - Matthias Butz - Jan Christian Meyer + This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server + Copyleft (L) 2017 RonanLana This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as @@ -19,47 +17,129 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -/** - *9201024 - Nana(E) - *@author Jvlaple +/* Nana, the Love fairy + Amoria (680000000) + Engagement ring NPC. */ - -function start() { - cm.sendOk("Hi, I'm Nana the love fairy... How's it going?"); - cm.dispose(); + +var status; +var state; + +var item; +var mats; +var matQty; +var cost; + +var options; + +function hasProofOfLoves(player) { + var count = 0; + + for(var i = 4031367; i <= 4031372; i++) { + if(player.haveItem(i)) { + count++; + } + } + + return count >= 4; } -// -//function action(mode, type, selection) { -// if (mode == -1) { -// cm.dispose(); -// } else { -// if (mode == 0 && status == 0) { -// cm.dispose(); -// return; -// } -// if (mode == 1) -// status++; -// else -// status--; -// if (cm.getPlayer().getMarriageQuestLevel() == 1 || cm.getPlayer().getMarriageQuestLevel() == 52) { -// if (!cm.haveItem(4003005, 20)) { -// if (status == 0) { -// cm.sendNext("Hey, you look like you need proofs of love? I can get them for you."); -// } else if (status == 1) { -// cm.sendNext("All you have to do is bring me 20 #bSoft Feathers#k."); -// cm.dispose(); -// } -// } else { -// if (status == 0) { -// cm.sendNext("Wow, you were quick! Heres the proof of love..."); -// cm.gainItem(4003005, -20) -// cm.gainItem(4031368, 1); -// cm.dispose(); -// } -// } -// } else { -// cm.sendOk("Hi, I'm Nana the love fairy... Hows it going?"); -// cm.dispose(); -// } -// } -//} \ No newline at end of file + +function getNanaLocation(player) { + var mapid = player.getMap().getId(); + + for(var i = 0; i < mapids.length; i++) { + if(mapid == mapids[i]) { + return i; + } + } + + return -1; +} + +var nanaLoc; +var mapids = [100000000, 103000000, 102000000, 101000000, 200000000, 220000000]; +var questItems = [4000001, 4000037, 4000215, 4000026, 4000070, 4000128]; +var questExp = [2000, 5000, 10000, 17000, 22000, 30000]; + +function processNanaQuest() { + if(cm.haveItem(questItems[nanaLoc], 50)) { + if(cm.canHold(4031367 + nanaLoc, 1)) { + cm.gainItem(questItems[nanaLoc], -50); + cm.gainItem(4031367 + nanaLoc, 1); + + cm.sendOk("Kyaaaa~ Thank you a lot, here get the #b#t4031367##k."); + return true; + } else { + cm.sendOk("Please have a free ETC slot available to hold the token of love."); + } + } else { + cm.sendOk("Please gather to me #b50 #t" + questItems[nanaLoc] + "##k."); + } + + return false; +} + +function start() { + status = -1; + action(1, 0, 0); +} + +function action(mode, type, selection) { + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && type > 0) { + cm.dispose(); + return; + } + if (mode == 1) + status++; + else + status--; + + if(status == 0) { + if(!cm.isQuestStarted(100400)) { + cm.sendOk("Hello #b#h0##k, I'm #p9201001# the fairy of Love."); + cm.dispose(); + return; + } + + nanaLoc = getNanaLocation(cm.getPlayer()); + if(nanaLoc == -1) { + cm.sendOk("Hello #b#h0##k, I'm #p9201001# the fairy of Love."); + cm.dispose(); + return; + } + + if(!cm.haveItem(4031367 + nanaLoc, 1)) { + if(cm.isQuestCompleted(100401 + nanaLoc)) { + state = 1; + cm.sendAcceptDecline("Did you lost the #k#t4031367##k I gave to you? Well, I can share another one with you, but you will need to redo the favor I asked last time, is that ok? I need you to bring me #r50 #t" + questItems[nanaLoc] + "#'s.#k"); + } else if(cm.isQuestStarted(100401 + nanaLoc)) { + if(processNanaQuest()) { + cm.gainExp(questExp[nanaLoc] * cm.getPlayer().getExpRate()); + cm.completeQuest(100401 + nanaLoc); + } + + cm.dispose(); + } else { + state = 0; + cm.sendAcceptDecline("Are you searching for #k#t4031367#'s#k? I can share one with you, but you must do a favor for me, is that ok?"); + } + } else { + cm.sendOk("Hey there. Did you get the #t4031367# from the other Nana's already?"); + cm.dispose(); + } + } else if(status == 1) { + if(state == 0) { + cm.startQuest(100401 + nanaLoc); + + cm.sendOk("I need you to collect #r50 #t" + questItems[nanaLoc] + "##k."); + cm.dispose(); + } else { + processNanaQuest(); + cm.dispose(); + } + } + } +} \ No newline at end of file diff --git a/scripts/npc/9201025.js b/scripts/npc/9201025.js index 26b0e72564..e03ad10988 100644 --- a/scripts/npc/9201025.js +++ b/scripts/npc/9201025.js @@ -1,8 +1,6 @@ /* - This file is part of the OdinMS Maple Story Server - Copyright (C) 2008 Patrick Huy - Matthias Butz - Jan Christian Meyer + This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server + Copyleft (L) 2017 RonanLana This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as @@ -19,47 +17,129 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -/** - *9201025 - Nana(O) - *@author Jvlaple +/* Nana, the Love fairy + Amoria (680000000) + Engagement ring NPC. */ - -function start() { - cm.sendOk("Hi, I'm Nana the love fairy... Hows it going?"); - cm.dispose(); + +var status; +var state; + +var item; +var mats; +var matQty; +var cost; + +var options; + +function hasProofOfLoves(player) { + var count = 0; + + for(var i = 4031367; i <= 4031372; i++) { + if(player.haveItem(i)) { + count++; + } + } + + return count >= 4; } -// -//function action(mode, type, selection) { -// if (mode == -1) { -// cm.dispose(); -// } else { -// if (mode == 0 && status == 0) { -// cm.dispose(); -// return; -// } -// if (mode == 1) -// status++; -// else -// status--; -// if (cm.getPlayer().getMarriageQuestLevel() == 1 || cm.getPlayer().getMarriageQuestLevel() == 52) { -// if (!cm.haveItem(4000083, 20)) { -// if (status == 0) { -// cm.sendNext("Hey, you look like you need proofs of love? I can get them for you."); -// } else if (status == 1) { -// cm.sendNext("All you have to do is bring me 20 #bJr. Sentinel Pieces.#k."); -// cm.dispose(); -// } -// } else { -// if (status == 0) { -// cm.sendNext("Wow, you were quick! Heres the proof of love..."); -// cm.gainItem(4000083, -20) -// cm.gainItem(4031369, 1); -// cm.dispose(); -// } -// } -// } else { -// cm.sendOk("Hi, I'm Nana the love fairy... Hows it going?"); -// cm.dispose(); -// } -// } -//} \ No newline at end of file + +function getNanaLocation(player) { + var mapid = player.getMap().getId(); + + for(var i = 0; i < mapids.length; i++) { + if(mapid == mapids[i]) { + return i; + } + } + + return -1; +} + +var nanaLoc; +var mapids = [100000000, 103000000, 102000000, 101000000, 200000000, 220000000]; +var questItems = [4000001, 4000037, 4000215, 4000026, 4000070, 4000128]; +var questExp = [2000, 5000, 10000, 17000, 22000, 30000]; + +function processNanaQuest() { + if(cm.haveItem(questItems[nanaLoc], 50)) { + if(cm.canHold(4031367 + nanaLoc, 1)) { + cm.gainItem(questItems[nanaLoc], -50); + cm.gainItem(4031367 + nanaLoc, 1); + + cm.sendOk("Kyaaaa~ Thank you a lot, here get the #b#t4031367##k."); + return true; + } else { + cm.sendOk("Please have a free ETC slot available to hold the token of love."); + } + } else { + cm.sendOk("Please gather to me #b50 #t" + questItems[nanaLoc] + "##k."); + } + + return false; +} + +function start() { + status = -1; + action(1, 0, 0); +} + +function action(mode, type, selection) { + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && type > 0) { + cm.dispose(); + return; + } + if (mode == 1) + status++; + else + status--; + + if(status == 0) { + if(!cm.isQuestStarted(100400)) { + cm.sendOk("Hello #b#h0##k, I'm #p9201001# the fairy of Love."); + cm.dispose(); + return; + } + + nanaLoc = getNanaLocation(cm.getPlayer()); + if(nanaLoc == -1) { + cm.sendOk("Hello #b#h0##k, I'm #p9201001# the fairy of Love."); + cm.dispose(); + return; + } + + if(!cm.haveItem(4031367 + nanaLoc, 1)) { + if(cm.isQuestCompleted(100401 + nanaLoc)) { + state = 1; + cm.sendAcceptDecline("Did you lost the #k#t4031367##k I gave to you? Well, I can share another one with you, but you will need to redo the favor I asked last time, is that ok? I need you to bring me #r50 #t" + questItems[nanaLoc] + "#'s.#k"); + } else if(cm.isQuestStarted(100401 + nanaLoc)) { + if(processNanaQuest()) { + cm.gainExp(questExp[nanaLoc] * cm.getPlayer().getExpRate()); + cm.completeQuest(100401 + nanaLoc); + } + + cm.dispose(); + } else { + state = 0; + cm.sendAcceptDecline("Are you searching for #k#t4031367#'s#k? I can share one with you, but you must do a favor for me, is that ok?"); + } + } else { + cm.sendOk("Hey there. Did you get the #t4031367# from the other Nana's already?"); + cm.dispose(); + } + } else if(status == 1) { + if(state == 0) { + cm.startQuest(100401 + nanaLoc); + + cm.sendOk("I need you to collect #r50 #t" + questItems[nanaLoc] + "##k."); + cm.dispose(); + } else { + processNanaQuest(); + cm.dispose(); + } + } + } +} \ No newline at end of file diff --git a/scripts/npc/9201026.js b/scripts/npc/9201026.js index 7e5a8b7610..e03ad10988 100644 --- a/scripts/npc/9201026.js +++ b/scripts/npc/9201026.js @@ -1,8 +1,6 @@ /* - This file is part of the OdinMS Maple Story Server - Copyright (C) 2008 Patrick Huy - Matthias Butz - Jan Christian Meyer + This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server + Copyleft (L) 2017 RonanLana This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as @@ -19,47 +17,129 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -/** - *9201026 - Nana(L) - *@author Jvlaple +/* Nana, the Love fairy + Amoria (680000000) + Engagement ring NPC. */ - -function start() { - cm.sendOk("Hi, I'm Nana the love fairy... How's it going?"); - cm.dispose(); + +var status; +var state; + +var item; +var mats; +var matQty; +var cost; + +var options; + +function hasProofOfLoves(player) { + var count = 0; + + for(var i = 4031367; i <= 4031372; i++) { + if(player.haveItem(i)) { + count++; + } + } + + return count >= 4; } -// -//function action(mode, type, selection) { -// if (mode == -1) { -// cm.dispose(); -// } else { -// if (mode == 0 && status == 0) { -// cm.dispose(); -// return; -// } -// if (mode == 1) -// status++; -// else -// status--; -// if (cm.getPlayer().getMarriageQuestLevel() == 1 || cm.getPlayer().getMarriageQuestLevel() == 52) { -// if (!cm.haveItem(4000106, 30)) { -// if (status == 0) { -// cm.sendNext("Hey, you look like you need proofs of love? I can get them for you."); -// } else if (status == 1) { -// cm.sendNext("All you have to do is bring me 30 #bTeddy Cotton#k."); -// cm.dispose(); -// } -// } else { -// if (status == 0) { -// cm.sendNext("Wow, you were quick! Heres the proof of love..."); -// cm.gainItem(4000106, -30) -// cm.gainItem(4031370, 1); -// cm.dispose(); -// } -// } -// } else { -// cm.sendOk("Hi, I'm Nana the love fairy... Hows it going?"); -// cm.dispose(); -// } -// } -//} \ No newline at end of file + +function getNanaLocation(player) { + var mapid = player.getMap().getId(); + + for(var i = 0; i < mapids.length; i++) { + if(mapid == mapids[i]) { + return i; + } + } + + return -1; +} + +var nanaLoc; +var mapids = [100000000, 103000000, 102000000, 101000000, 200000000, 220000000]; +var questItems = [4000001, 4000037, 4000215, 4000026, 4000070, 4000128]; +var questExp = [2000, 5000, 10000, 17000, 22000, 30000]; + +function processNanaQuest() { + if(cm.haveItem(questItems[nanaLoc], 50)) { + if(cm.canHold(4031367 + nanaLoc, 1)) { + cm.gainItem(questItems[nanaLoc], -50); + cm.gainItem(4031367 + nanaLoc, 1); + + cm.sendOk("Kyaaaa~ Thank you a lot, here get the #b#t4031367##k."); + return true; + } else { + cm.sendOk("Please have a free ETC slot available to hold the token of love."); + } + } else { + cm.sendOk("Please gather to me #b50 #t" + questItems[nanaLoc] + "##k."); + } + + return false; +} + +function start() { + status = -1; + action(1, 0, 0); +} + +function action(mode, type, selection) { + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && type > 0) { + cm.dispose(); + return; + } + if (mode == 1) + status++; + else + status--; + + if(status == 0) { + if(!cm.isQuestStarted(100400)) { + cm.sendOk("Hello #b#h0##k, I'm #p9201001# the fairy of Love."); + cm.dispose(); + return; + } + + nanaLoc = getNanaLocation(cm.getPlayer()); + if(nanaLoc == -1) { + cm.sendOk("Hello #b#h0##k, I'm #p9201001# the fairy of Love."); + cm.dispose(); + return; + } + + if(!cm.haveItem(4031367 + nanaLoc, 1)) { + if(cm.isQuestCompleted(100401 + nanaLoc)) { + state = 1; + cm.sendAcceptDecline("Did you lost the #k#t4031367##k I gave to you? Well, I can share another one with you, but you will need to redo the favor I asked last time, is that ok? I need you to bring me #r50 #t" + questItems[nanaLoc] + "#'s.#k"); + } else if(cm.isQuestStarted(100401 + nanaLoc)) { + if(processNanaQuest()) { + cm.gainExp(questExp[nanaLoc] * cm.getPlayer().getExpRate()); + cm.completeQuest(100401 + nanaLoc); + } + + cm.dispose(); + } else { + state = 0; + cm.sendAcceptDecline("Are you searching for #k#t4031367#'s#k? I can share one with you, but you must do a favor for me, is that ok?"); + } + } else { + cm.sendOk("Hey there. Did you get the #t4031367# from the other Nana's already?"); + cm.dispose(); + } + } else if(status == 1) { + if(state == 0) { + cm.startQuest(100401 + nanaLoc); + + cm.sendOk("I need you to collect #r50 #t" + questItems[nanaLoc] + "##k."); + cm.dispose(); + } else { + processNanaQuest(); + cm.dispose(); + } + } + } +} \ No newline at end of file diff --git a/scripts/npc/9201027.js b/scripts/npc/9201027.js index dba1fc6694..e03ad10988 100644 --- a/scripts/npc/9201027.js +++ b/scripts/npc/9201027.js @@ -1,8 +1,6 @@ /* - This file is part of the OdinMS Maple Story Server - Copyright (C) 2008 Patrick Huy - Matthias Butz - Jan Christian Meyer + This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server + Copyleft (L) 2017 RonanLana This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as @@ -19,48 +17,129 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -/** - *9201027 - Nana(P) - *@author Jvlaple +/* Nana, the Love fairy + Amoria (680000000) + Engagement ring NPC. */ - -function start() { - cm.sendOk("Hi, I'm Nana the love fairy... How's it going?"); - cm.dispose(); + +var status; +var state; + +var item; +var mats; +var matQty; +var cost; + +var options; + +function hasProofOfLoves(player) { + var count = 0; + + for(var i = 4031367; i <= 4031372; i++) { + if(player.haveItem(i)) { + count++; + } + } + + return count >= 4; } -//function action(mode, type, selection) { -// if (mode == -1) { -// cm.dispose(); -// } else { -// if (mode == 0 && status == 0) { -// cm.dispose(); -// return; -// } -// if (mode == 1) -// status++; -// else -// status--; -// if (cm.getPlayer().getMarriageQuestLevel() == 1 || cm.getPlayer().getMarriageQuestLevel() == 52) { -// if (!cm.haveItem(4000018, 40)) { -// if (status == 0) { -// cm.sendNext("Hey, you look like you need proofs of love? I can get them for you."); -// } else if (status == 1) { -// cm.sendOk("All you have to do is bring me 40 #bFirewood#k."); -// cm.dispose(); -// } -// } else { -// if (status == 0) { -// cm.sendNext("Wow, you were quick! Heres the proof of love..."); -// cm.gainItem(4000018, -40) -// cm.gainItem(4031371, 1); -// cm.dispose(); -// } -// } -// } -// else { -// cm.sendOk("Hi, I'm Nana the love fairy... Hows it going?"); -// cm.dispose(); -// } -// } -//} \ No newline at end of file +function getNanaLocation(player) { + var mapid = player.getMap().getId(); + + for(var i = 0; i < mapids.length; i++) { + if(mapid == mapids[i]) { + return i; + } + } + + return -1; +} + +var nanaLoc; +var mapids = [100000000, 103000000, 102000000, 101000000, 200000000, 220000000]; +var questItems = [4000001, 4000037, 4000215, 4000026, 4000070, 4000128]; +var questExp = [2000, 5000, 10000, 17000, 22000, 30000]; + +function processNanaQuest() { + if(cm.haveItem(questItems[nanaLoc], 50)) { + if(cm.canHold(4031367 + nanaLoc, 1)) { + cm.gainItem(questItems[nanaLoc], -50); + cm.gainItem(4031367 + nanaLoc, 1); + + cm.sendOk("Kyaaaa~ Thank you a lot, here get the #b#t4031367##k."); + return true; + } else { + cm.sendOk("Please have a free ETC slot available to hold the token of love."); + } + } else { + cm.sendOk("Please gather to me #b50 #t" + questItems[nanaLoc] + "##k."); + } + + return false; +} + +function start() { + status = -1; + action(1, 0, 0); +} + +function action(mode, type, selection) { + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && type > 0) { + cm.dispose(); + return; + } + if (mode == 1) + status++; + else + status--; + + if(status == 0) { + if(!cm.isQuestStarted(100400)) { + cm.sendOk("Hello #b#h0##k, I'm #p9201001# the fairy of Love."); + cm.dispose(); + return; + } + + nanaLoc = getNanaLocation(cm.getPlayer()); + if(nanaLoc == -1) { + cm.sendOk("Hello #b#h0##k, I'm #p9201001# the fairy of Love."); + cm.dispose(); + return; + } + + if(!cm.haveItem(4031367 + nanaLoc, 1)) { + if(cm.isQuestCompleted(100401 + nanaLoc)) { + state = 1; + cm.sendAcceptDecline("Did you lost the #k#t4031367##k I gave to you? Well, I can share another one with you, but you will need to redo the favor I asked last time, is that ok? I need you to bring me #r50 #t" + questItems[nanaLoc] + "#'s.#k"); + } else if(cm.isQuestStarted(100401 + nanaLoc)) { + if(processNanaQuest()) { + cm.gainExp(questExp[nanaLoc] * cm.getPlayer().getExpRate()); + cm.completeQuest(100401 + nanaLoc); + } + + cm.dispose(); + } else { + state = 0; + cm.sendAcceptDecline("Are you searching for #k#t4031367#'s#k? I can share one with you, but you must do a favor for me, is that ok?"); + } + } else { + cm.sendOk("Hey there. Did you get the #t4031367# from the other Nana's already?"); + cm.dispose(); + } + } else if(status == 1) { + if(state == 0) { + cm.startQuest(100401 + nanaLoc); + + cm.sendOk("I need you to collect #r50 #t" + questItems[nanaLoc] + "##k."); + cm.dispose(); + } else { + processNanaQuest(); + cm.dispose(); + } + } + } +} \ No newline at end of file diff --git a/scripts/npc/9201049.js b/scripts/npc/9201049.js index 42f8ed44dc..f054f065ec 100644 --- a/scripts/npc/9201049.js +++ b/scripts/npc/9201049.js @@ -1,8 +1,6 @@ /* - This file is part of the OdinMS Maple Story Server - Copyright (C) 2008 Patrick Huy - Matthias Butz - Jan Christian Meyer + This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server + Copyleft (L) 2017 RonanLana This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as @@ -19,54 +17,52 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -/** - Ames the Wise --- By --------------------------------------------------------------------------------------------- - Xelkin --- Edited by -------------------------------------------------------------------------------------- - Angel (get31720 ragezone --- Extra Info ------------------------------------------------------------------------------------- - Fixed by [happydud3 (BENG)] & [XotiCraze] --- Fixed Dispose ---------------------------------------------------------------------------------- - Fixed by Moogra ---------------------------------------------------------------------------------------------------- -**/ -var status = -1; +/* Ames the Wise + Wedding exit map + Gives Onyx Chest to anyone completing the wedding event. + */ +var status; + function start() { - var rings = new Array(1112806, 1112803, 1112807, 1112809); - var hasRing = false; - for (var x = 0; x < rings.length && !hasRing; x++) - if (cm.haveItem(rings[x])) { - hasRing = true; - break; - } - if (hasRing) - cm.sendNext("You've reached the end of the wedding. You will receive an Onyx Chest for Bride and Groom and an Onyx Chest. Exchange them at Pila, she is at the top of Amoria."); - else if (cm.haveItem(4000313)) { - cm.sendNext("Wow the end of the wedding already ? Good bye then.!"); - status = 20; - } else { - cm.sendNext("You do not have the Gold Maple Leaf and you do not have a wedding ring so I will take you to Amoria."); - status = 21; - } + status = -1; + action(1, 0, 0); } function action(mode, type, selection) { - if (mode < 1) { - cm.sendOk("Goodbye then."); - cm.dispose(); - } else { - status++; - if (status == 1) { - cm.gainItem(4031424,1); - cm.gainItem(4031423,1); + if (mode == -1) { + cm.dispose(); + } else { + if (mode == 0 && type > 0) { + cm.dispose(); + return; + } + if (mode == 1) + status++; + else + status--; + + if(status == 0) { + cm.sendOk("Hey there, did you enjoy the wedding? I will head you back to #bAmoria#k now."); + } else if(status == 1) { + var eim = cm.getEventInstance(); + if(eim != null) { + var boxId = (cm.getPlayer().getId() == eim.getIntProperty("groomId") || cm.getPlayer().getId() == eim.getIntProperty("brideId")) ? 4031424 : 4031423; + + if(cm.canHold(boxId, 1)) { + cm.gainItem(boxId, 1); + cm.warp(680000000); + cm.sendOk("You just received an Onyx Chest. Search for #b#p9201014##k, she is at the top of Amoria, she knows how to open these."); + } else { + cm.sendOk("Please make room on your ETC inventory to receive the Onyx Chest."); + cm.dispose(); + return; + } + } else { + cm.warp(680000000); + } + cm.dispose(); - } else if (status == 21) { - cm.gainItem(4000313,-1); - cm.gainItem(4031423,1); } - cm.warp(680000000); - cm.dispose(); } -} +} \ No newline at end of file diff --git a/scripts/npc/commands.js b/scripts/npc/commands.js index 45c6b0b443..78d92ccf56 100644 --- a/scripts/npc/commands.js +++ b/scripts/npc/commands.js @@ -71,6 +71,7 @@ function writeSolaxiaCommandsLv5() { //Developer addCommand("debugcoupons", ""); addCommand("debugplayercoupons", ""); addCommand("debugtimer", ""); + addCommand("debugmarriage", ""); } function writeSolaxiaCommandsLv4() { //SuperGM diff --git a/scripts/reactor/6802000.js b/scripts/reactor/6802000.js index bbe8dce9bb..2497b8d049 100644 --- a/scripts/reactor/6802000.js +++ b/scripts/reactor/6802000.js @@ -20,9 +20,5 @@ along with this program. If not, see . */ function act(){ - for (var i = 0; i < 3; i++) { - rm.spawnMonster(9400506); - rm.spawnMonster(9400507); - } - rm.spawnMonster(9400507); + rm.sprayItems(true, 1, 100, 400, 15); } \ No newline at end of file diff --git a/scripts/reactor/6802001.js b/scripts/reactor/6802001.js index 0424252522..2497b8d049 100644 --- a/scripts/reactor/6802001.js +++ b/scripts/reactor/6802001.js @@ -20,8 +20,5 @@ along with this program. If not, see . */ function act(){ - for (var i = 0; i < 5; i++) - rm.spawnMonster(9400506); - for (var j = 0; j < 7; j++) - rm.spawnMonster(9400507); + rm.sprayItems(true, 1, 100, 400, 15); } \ No newline at end of file diff --git a/sql/db_database.sql b/sql/db_database.sql index ddfc68660b..1ae33e7eb9 100644 --- a/sql/db_database.sql +++ b/sql/db_database.sql @@ -177,6 +177,7 @@ CREATE TABLE IF NOT EXISTS `characters` ( `vanquisherKills` int(11) unsigned NOT NULL DEFAULT '0', `summonValue` int(11) unsigned NOT NULL DEFAULT '0', `partnerId` int(11) NOT NULL DEFAULT '0', + `marriageItemId` int(11) NOT NULL DEFAULT '0', `reborns` int(5) NOT NULL DEFAULT '0', `PQPoints` int(11) NOT NULL DEFAULT '0', `dataString` varchar(64) NOT NULL DEFAULT '', diff --git a/sql/db_drops.sql b/sql/db_drops.sql index 27e0fadcca..2c397ab131 100644 --- a/sql/db_drops.sql +++ b/sql/db_drops.sql @@ -20183,7 +20183,11 @@ USE `heavenms`; (9400114, 2022063, 10, 30, 0, 200000), (9400114, 2022064, 10, 30, 0, 200000), (9400120, 4000094, 1, 1, 0, 400000), -(9400122, 4000094, 1, 1, 0, 400000); +(9400122, 4000094, 1, 1, 0, 400000), +(9400506, 2020020, 1, 1, 0, 100000), +(9400506, 4031217, 1, 1, 0, 40000), +(9400507, 2020020, 1, 1, 0, 100000), +(9400507, 4031217, 1, 1, 0, 40000); # (dropperid, itemid, minqty, maxqty, questid, chance) @@ -21447,6 +21451,81 @@ USE `heavenms`; UPDATE reactordrops SET questid=1008 WHERE itemid=4031161; UPDATE reactordrops SET questid=1008 WHERE itemid=4031162; + # add Amoria Wedding reward boxes + INSERT INTO `reactordrops` (`reactorid`, `itemid`, `chance`, `questid`) VALUES +(6802000, 4031423, 5, -1), +(6802000, 4031424, 10, -1), +(6802000, 1442047, 10, -1), +(6802000, 1442049, 17, -1), +(6802000, 1302071, 20, -1), +(6802000, 1312034, 20, -1), +(6802000, 1322056, 20, -1), +(6802000, 1332059, 20, -1), +(6802000, 1382042, 20, -1), +(6802000, 1402041, 20, -1), +(6802000, 1412029, 20, -1), +(6802000, 1422033, 20, -1), +(6802000, 2022180, 3, -1), +(6802000, 2022181, 3, -1), +(6802000, 2000005, 3, -1), +(6802000, 2022181, 3, -1), +(6802000, 2020013, 3, -1), +(6802000, 2022015, 3, -1), +(6802000, 2020010, 3, -1), +(6802000, 2022130, 8, -1), +(6802000, 2022196, 3, -1), +(6802000, 2022197, 5, -1), +(6802000, 2022200, 8, -1), +(6802000, 2020020, 5, -1), +(6802000, 2022190, 5, -1), +(6802000, 2001001, 4, -1), +(6802000, 2001002, 4, -1), +(6802000, 4010004, 4, -1), +(6802000, 4010005, 4, -1), +(6802000, 4010006, 6, -1), +(6802000, 4020004, 4, -1), +(6802000, 4020005, 4, -1), +(6802000, 4020007, 6, -1), +(6802000, 2040804, 10, -1), +(6802000, 2040815, 10, -1), +(6802000, 2040908, 10, -1), +(6802000, 2040931, 10, -1), +(6802001, 1442048, 14, -1), +(6802001, 1442050, 20, -1), +(6802001, 1432042, 20, -1), +(6802001, 1442053, 20, -1), +(6802001, 1452048, 20, -1), +(6802001, 1462043, 20, -1), +(6802001, 1472058, 20, -1), +(6802001, 1482025, 20, -1), +(6802001, 1492026, 20, -1), +(6802001, 2022180, 3, -1), +(6802001, 2022181, 3, -1), +(6802001, 2000005, 3, -1), +(6802001, 2022181, 3, -1), +(6802001, 2020013, 3, -1), +(6802001, 2022015, 3, -1), +(6802001, 2020010, 3, -1), +(6802001, 2022130, 8, -1), +(6802001, 2022196, 3, -1), +(6802001, 2022197, 5, -1), +(6802001, 2022200, 8, -1), +(6802001, 2020020, 5, -1), +(6802001, 2022190, 5, -1), +(6802001, 2001001, 4, -1), +(6802001, 2001002, 4, -1), +(6802001, 4010004, 4, -1), +(6802001, 4010005, 4, -1), +(6802001, 4010006, 6, -1), +(6802001, 4020004, 4, -1), +(6802001, 4020005, 4, -1), +(6802001, 4020007, 6, -1), +(6802001, 2041010, 10, -1), +(6802001, 2041028, 10, -1), +(6802001, 2041023, 10, -1), +(6802001, 2048012, 10, -1), +(6802001, 2040502, 10, -1); + # update Amoria PQ reward boxes DELETE FROM `reactordrops` WHERE `reactorid` >= 6702003 AND `reactorid` <= 6702012; INSERT INTO `reactordrops` (`reactorid`, `itemid`, `chance`, `questid`) VALUES diff --git a/src/client/MapleCharacter.java b/src/client/MapleCharacter.java index a1a611005a..25a7f2d244 100644 --- a/src/client/MapleCharacter.java +++ b/src/client/MapleCharacter.java @@ -106,6 +106,7 @@ import tools.FilePrinter; import tools.MaplePacketCreator; import tools.Pair; import tools.Randomizer; +import tools.packets.Wedding; import client.autoban.AutobanManager; import client.inventory.Equip; import client.inventory.Item; @@ -237,7 +238,6 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { private MapleStorage storage = null; private MapleTrade trade = null; private MonsterBook monsterbook; - private MapleRing marriageRing; private CashShop cashshop; private Set newyears = new LinkedHashSet<>(); private SavedLocation savedLocations[]; @@ -279,8 +279,6 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { private Lock prtLock = new MonitoredReentrantLock(MonitoredLockType.PRT); private Map> excluded = new LinkedHashMap<>(); private Set excludedItems = new LinkedHashSet<>(); - private List crushRings = new ArrayList<>(); - private List friendshipRings = new ArrayList<>(); private static String[] ariantroomleader = new String[3]; private static int[] ariantroomslot = new int[3]; private long portaldelay = 0, lastcombo = 0; @@ -295,8 +293,13 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { private List viptrockmaps = new ArrayList<>(); private Map events = new LinkedHashMap<>(); private PartyQuest partyQuest = null; - private boolean loggedIn = false; private MapleDragon dragon = null; + private MapleRing marriageRing; + private int marriageItemid = -1; + private int partnerId = -1; + private List crushRings = new ArrayList<>(); + private List friendshipRings = new ArrayList<>(); + private boolean loggedIn = false; private boolean useCS; //chaos scroll upon crafting item. private long npcCd; private long petLootCd; @@ -498,12 +501,39 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { return ring; } } - if (getMarriageRing().getRingId() == id) { - return getMarriageRing(); + + if(marriageRing != null) { + if (marriageRing.getRingId() == id) { + return marriageRing; + } } return null; } + + public int getMarriageItemId() { + return marriageItemid; + } + + public void setMarriageItemId(int itemid) { + marriageItemid = itemid; + } + + public int getPartnerId() { + return partnerId; + } + + public void setPartnerId(int partnerid) { + partnerId = partnerid; + } + + public int getRelationshipId() { + return client.getWorldServer().getRelationshipId(id); + } + + public boolean isMarried() { + return marriageRing != null && partnerId > 0; + } public int addDojoPointsByMap(int mapid) { int pts = 0; @@ -542,6 +572,10 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { public void addFriendshipRing(MapleRing r) { friendshipRings.add(r); } + + public void addMarriageRing(MapleRing r) { + marriageRing = r; + } public void addHP(int delta) { setHp(hp + delta); @@ -1286,6 +1320,15 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { return false; } + public void notifyMapTransferToPartner(int mapid) { + if(partnerId > 0) { + final MapleCharacter partner = client.getWorldServer().getPlayerStorage().getCharacterById(partnerId); + if(partner != null && !partner.isAwayFromWorld()) { + partner.announce(Wedding.OnNotifyWeddingPartnerTransfer(id, mapid)); + } + } + } + private void changeMapInternal(final MapleMap to, final Point pos, final byte[] warpPacket) { if(!canWarpMap) return; @@ -1321,6 +1364,8 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { FilePrinter.printError(FilePrinter.MAPLE_MAP, "Character " + this.getName() + " got stuck when moving to map " + map.getId() + "."); } + notifyMapTransferToPartner(map.getId()); + //alas, new map has been specified when a warping was being processed... if(newWarpMap != -1) { canWarpMap = true; @@ -3913,7 +3958,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } public MapleRing getMarriageRing() { - return marriageRing; + return partnerId > 0 ? marriageRing : null; } public int getMarried() { @@ -5275,6 +5320,17 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } } + public void addPlayerRing(MapleRing ring) { + int ringItemId = ring.getItemId(); + if (ItemConstants.isWeddingRing(ringItemId)) { + this.addMarriageRing(ring); + } else if (ring.getItemId() > 1112012) { + this.addFriendshipRing(ring); + } else { + this.addCrushRing(ring); + } + } + public static MapleCharacter loadCharFromDB(int charid, MapleClient client, boolean channelserver) throws SQLException { try { MapleCharacter ret = new MapleCharacter(); @@ -5374,15 +5430,23 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { if (item.getRight().equals(MapleInventoryType.EQUIPPED)) { ring.equip(); } - if (ring.getItemId() > 1112012) { - ret.addFriendshipRing(ring); - } else { - ret.addCrushRing(ring); - } + + ret.addPlayerRing(ring); } } } + World wserv = Server.getInstance().getWorld(ret.world); + + ret.partnerId = rs.getInt("partnerId"); + ret.marriageItemid = rs.getInt("marriageItemId"); + if(ret.marriageItemid > 0 && ret.partnerId <= 0) { + ret.marriageItemid = -1; + } else if(ret.partnerId > 0 && wserv.getRelationshipId(ret.id) <= 0) { + ret.marriageItemid = -1; + ret.partnerId = -1; + } + NewYearCardRecord.loadPlayerNewYearCards(ret); PreparedStatement ps2, ps3; @@ -6335,7 +6399,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { con.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED); con.setAutoCommit(false); PreparedStatement ps; - ps = con.prepareStatement("UPDATE characters SET level = ?, fame = ?, str = ?, dex = ?, luk = ?, `int` = ?, exp = ?, gachaexp = ?, hp = ?, mp = ?, maxhp = ?, maxmp = ?, sp = ?, ap = ?, gm = ?, skincolor = ?, gender = ?, job = ?, hair = ?, face = ?, map = ?, meso = ?, hpMpUsed = ?, spawnpoint = ?, party = ?, buddyCapacity = ?, messengerid = ?, messengerposition = ?, mountlevel = ?, mountexp = ?, mounttiredness= ?, equipslots = ?, useslots = ?, setupslots = ?, etcslots = ?, monsterbookcover = ?, vanquisherStage = ?, dojoPoints = ?, lastDojoStage = ?, finishedDojoTutorial = ?, vanquisherKills = ?, matchcardwins = ?, matchcardlosses = ?, matchcardties = ?, omokwins = ?, omoklosses = ?, omokties = ?, dataString = ?, fquest = ?, jailexpire = ? WHERE id = ?", Statement.RETURN_GENERATED_KEYS); + ps = con.prepareStatement("UPDATE characters SET level = ?, fame = ?, str = ?, dex = ?, luk = ?, `int` = ?, exp = ?, gachaexp = ?, hp = ?, mp = ?, maxhp = ?, maxmp = ?, sp = ?, ap = ?, gm = ?, skincolor = ?, gender = ?, job = ?, hair = ?, face = ?, map = ?, meso = ?, hpMpUsed = ?, spawnpoint = ?, party = ?, buddyCapacity = ?, messengerid = ?, messengerposition = ?, mountlevel = ?, mountexp = ?, mounttiredness= ?, equipslots = ?, useslots = ?, setupslots = ?, etcslots = ?, monsterbookcover = ?, vanquisherStage = ?, dojoPoints = ?, lastDojoStage = ?, finishedDojoTutorial = ?, vanquisherKills = ?, matchcardwins = ?, matchcardlosses = ?, matchcardties = ?, omokwins = ?, omoklosses = ?, omokties = ?, dataString = ?, fquest = ?, jailexpire = ?, partnerId = ?, marriageItemId = ? WHERE id = ?", Statement.RETURN_GENERATED_KEYS); if (gmLevel < 1 && level > 199) { ps.setInt(1, isCygnus() ? 120 : 200); } else { @@ -6437,7 +6501,9 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { ps.setString(48, dataString); ps.setInt(49, quest_fame); ps.setLong(50, jailExpiration); - ps.setInt(51, id); + ps.setInt(51, partnerId); + ps.setInt(52, marriageItemid); + ps.setInt(53, id); int updateRows = ps.executeUpdate(); if (updateRows < 1) { @@ -7147,9 +7213,14 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { if (item == null){ //Basic check return(0); } - if (ItemConstants.isRechargable(item.getItemId())) { + + int itemid = item.getItemId(); + if (ItemConstants.isRechargable(itemid)) { quantity = item.getQuantity(); + } else if (ItemConstants.isWeddingToken(itemid) || ItemConstants.isWeddingRing(itemid)) { + return(0); } + if (quantity < 0) { return(0); } @@ -8012,6 +8083,18 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { this.showHint("#ePLAYER EQUIPMENTS:#n\r\n\r\n" + showMsg, 400); } } + + public void broadcastMarriageMessage() { + MapleGuild guild = this.getGuild(); + if(guild != null) { + guild.broadcast(MaplePacketCreator.marriageMessage(0, name)); + } + + MapleFamily family = this.getFamily(); + if(family != null) { + family.broadcast(MaplePacketCreator.marriageMessage(1, name)); + } + } public Map getEvents() { return events; diff --git a/src/client/MapleClient.java b/src/client/MapleClient.java index 9082436d3c..b21e6d2ed4 100644 --- a/src/client/MapleClient.java +++ b/src/client/MapleClient.java @@ -783,6 +783,7 @@ public class MapleClient { private void removePlayer() { try { player.setAwayFromWorld(true); + player.notifyMapTransferToPartner(-1); player.cancelAllBuffs(true); player.cancelAllDebuffs(); @@ -1294,6 +1295,7 @@ public class MapleClient { player.unregisterChairBuff(); server.getPlayerBuffStorage().addBuffsToStorage(player.getId(), player.getAllBuffs()); player.setAwayFromWorld(true); + player.notifyMapTransferToPartner(-1); player.cancelAllBuffs(true); player.cancelBuffExpireTask(); player.cancelDiseaseExpireTask(); diff --git a/src/client/MapleFamily.java b/src/client/MapleFamily.java index 781cbcb87a..903c06b77e 100644 --- a/src/client/MapleFamily.java +++ b/src/client/MapleFamily.java @@ -92,4 +92,8 @@ public class MapleFamily { public Map getMembers() { return members; } + + public void broadcast(byte[] packet) { + // family currently not developed + } } diff --git a/src/client/MapleRing.java b/src/client/MapleRing.java index 12c4fbde2e..e05200fd6b 100644 --- a/src/client/MapleRing.java +++ b/src/client/MapleRing.java @@ -68,6 +68,40 @@ public class MapleRing implements Comparable { } } + public static void removeRing(final MapleRing ring) { + try { + if (ring == null) { + return; + } + + Connection con = DatabaseConnection.getConnection(); + + PreparedStatement ps = con.prepareStatement("DELETE FROM rings WHERE id=?"); + ps.setInt(1, ring.getRingId()); + ps.addBatch(); + + ps.setInt(1, ring.getPartnerRingId()); + ps.addBatch(); + + ps.executeBatch(); + ps.close(); + + ps = con.prepareStatement("UPDATE inventoryequipment SET ringid=-1 WHERE ringid=?"); + ps.setInt(1, ring.getRingId()); + ps.addBatch(); + + ps.setInt(1, ring.getPartnerRingId()); + ps.addBatch(); + + ps.executeBatch(); + ps.close(); + + con.close(); + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + public static int createRing(int itemid, final MapleCharacter partner1, final MapleCharacter partner2) { try { if (partner1 == null) { diff --git a/src/client/command/Commands.java b/src/client/command/Commands.java index 8e2fa5b4bd..6919be9ea6 100644 --- a/src/client/command/Commands.java +++ b/src/client/command/Commands.java @@ -2434,8 +2434,6 @@ public class Commands { } break; - - case "debugpacket": player.getMap().broadcastMessage(MaplePacketCreator.customPacket(joinStringFrom(sub, 1))); break; @@ -2519,6 +2517,10 @@ public class Commands { player.dropMessage(6, "Total Task: " + tMan.getTaskCount() + " Current Task: " + tMan.getQueuedTasks() + " Active Task: " + tMan.getActiveCount() + " Completed Task: " + tMan.getCompletedTaskCount()); break; + case "debugmarriage": + c.getChannelServer().debugMarriageStatus(); + break; + default: return false; } @@ -2726,7 +2728,7 @@ public class Commands { else return true; } - public static boolean executeSolaxiaPlayerCommand(MapleClient c, String[] sub, char heading) { + public static boolean executeHeavenMsPlayerCommand(MapleClient c, String[] sub, char heading) { Channel cserv = c.getChannelServer(); Server srv = Server.getInstance(); diff --git a/src/constants/ItemConstants.java b/src/constants/ItemConstants.java index 8d4f1fbb0c..1a456c2d1b 100644 --- a/src/constants/ItemConstants.java +++ b/src/constants/ItemConstants.java @@ -179,4 +179,12 @@ public final class ItemConstants { public static boolean isEquipment(int itemId) { return itemId < 2000000 && itemId != 0; } + + public static boolean isWeddingRing(int itemId) { + return itemId >= 1112803 && itemId <= 1112809; + } + + public static boolean isWeddingToken(int itemId) { + return itemId >= 4031357 && itemId <= 4031364; + } } diff --git a/src/constants/ServerConstants.java b/src/constants/ServerConstants.java index d0efb470a5..bc16ae18f2 100644 --- a/src/constants/ServerConstants.java +++ b/src/constants/ServerConstants.java @@ -158,6 +158,11 @@ public class ServerConstants { public static final boolean USE_DEADLY_DOJO = false; //Should bosses really use 1HP,1MP attacks in dojo? public static final int DOJO_ENERGY_ATK = 100; //Dojo energy gain when deal attack public static final int DOJO_ENERGY_DMG = 20; //Dojo energy gain when recv attack + + //Wedding Configuration + public static final int WEDDING_RESERVATION_DELAY = 3; //Minimum idle slots before processing a wedding reservation. + public static final int WEDDING_RESERVATION_TIMEOUT = 10; //Limit time in minutes for the couple to show up before cancelling the wedding reservation. + public static final int WEDDING_RESERVATION_INTERVAL = 60; //Time between wedding starts in minutes. //Event End Timestamp public static final long EVENT_END_TIMESTAMP = 1428897600000L; diff --git a/src/net/PacketProcessor.java b/src/net/PacketProcessor.java index 793729d974..d97cb245c4 100644 --- a/src/net/PacketProcessor.java +++ b/src/net/PacketProcessor.java @@ -248,6 +248,8 @@ public final class PacketProcessor { registerHandler(RecvOpcode.MONSTER_CARNIVAL, new MonsterCarnivalHandler()); registerHandler(RecvOpcode.REMOTE_STORE, new RemoteStoreHandler()); registerHandler(RecvOpcode.WEDDING_ACTION, new WeddingHandler()); + registerHandler(RecvOpcode.WEDDING_TALK, new WeddingTalkHandler()); + registerHandler(RecvOpcode.WEDDING_TALK_MORE, new WeddingTalkMoreHandler()); registerHandler(RecvOpcode.WATER_OF_LIFE, new UseWaterOfLifeHandler()); registerHandler(RecvOpcode.ADMIN_CHAT, new AdminChatHandler()); registerHandler(RecvOpcode.MOVE_DRAGON, new MoveDragonHandler()); diff --git a/src/net/RecvOpcode.java b/src/net/RecvOpcode.java index 0d4fab95e2..87b27459c6 100644 --- a/src/net/RecvOpcode.java +++ b/src/net/RecvOpcode.java @@ -114,7 +114,6 @@ public enum RecvOpcode { QUEST_ACTION(0x6B), //lolno SKILL_MACRO(0x6E), - SPOUSE_CHAT(0x6F), USE_ITEM_REWARD(0x70), MAKER_SKILL(0x71), USE_REMOTE(0x74), @@ -122,6 +121,7 @@ public enum RecvOpcode { ADMIN_CHAT(0x76), PARTYCHAT(0x77), WHISPER(0x78), + SPOUSE_CHAT(0x79), MESSENGER(0x7A), PLAYER_INTERACTION(0x7B), PARTY_OPERATION(0x7C), @@ -137,6 +137,8 @@ public enum RecvOpcode { RPS_ACTION(0x88), RING_ACTION(0x89), WEDDING_ACTION(0x8A), + WEDDING_TALK(0x8B), + WEDDING_TALK_MORE(0x8B), ALLIANCE_OPERATION(0x8F), OPEN_FAMILY(0x92), ADD_FAMILY(0x93), diff --git a/src/net/SendOpcode.java b/src/net/SendOpcode.java index ca6190921e..be660357c6 100644 --- a/src/net/SendOpcode.java +++ b/src/net/SendOpcode.java @@ -67,7 +67,7 @@ public enum SendOpcode { OPEN_FULL_CLIENT_DOWNLOAD_LINK(0x28), MEMO_RESULT(0x29), MAP_TRANSFER_RESULT(0x2A), - ANTI_MACRO_RESULT(0x2B), + WEDDING_PHOTO(0x2B), //ANTI_MACRO_RESULT(0x2B), CLAIM_RESULT(0x2D), CLAIM_AVAILABLE_TIME(0x2E), CLAIM_STATUS_CHANGED(0x2F), diff --git a/src/net/server/Server.java b/src/net/server/Server.java index 79969d3446..4ddfea2eb0 100644 --- a/src/net/server/Server.java +++ b/src/net/server/Server.java @@ -340,9 +340,10 @@ public class Server implements Runnable { worldRecommendedList.add(new Pair<>(i, p.getProperty("whyamirecommended" + i))); worlds.add(world); channels.add(new HashMap()); + long bootTime = System.currentTimeMillis(); for (int j = 0; j < Integer.parseInt(p.getProperty("channels" + i)); j++) { int channelid = j + 1; - Channel channel = new Channel(i, channelid); + Channel channel = new Channel(i, channelid, bootTime); world.addChannel(channel); channels.get(i).put(channelid, channel.getIP()); } diff --git a/src/net/server/channel/Channel.java b/src/net/server/channel/Channel.java index 0aea6fb72c..e126e447a5 100644 --- a/src/net/server/channel/Channel.java +++ b/src/net/server/channel/Channel.java @@ -24,6 +24,7 @@ package net.server.channel; import java.io.File; import java.net.InetSocketAddress; import java.util.ArrayList; +import java.util.LinkedList; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -41,6 +42,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import net.MapleServerHandler; import net.mina.MapleCodecFactory; +import net.server.Server; +import net.server.world.World; import net.server.PlayerStorage; import net.server.world.MapleParty; import net.server.world.MaplePartyCharacter; @@ -65,6 +68,7 @@ import server.maps.MapleMap; import server.maps.MapleMapFactory; import server.maps.MapleMiniDungeon; import tools.MaplePacketCreator; +import tools.Pair; import client.MapleCharacter; import constants.ServerConstants; import server.maps.MapleMiniDungeonInfo; @@ -92,15 +96,30 @@ public final class Channel { private Map dojoParty = new HashMap<>(); private Map dungeons = new HashMap<>(); + private List chapelReservationQueue = new LinkedList<>(); + private List cathedralReservationQueue = new LinkedList<>(); + private ScheduledFuture chapelReservationTask; + private ScheduledFuture cathedralReservationTask; + + private Integer ongoingChapel = null; + private Boolean ongoingChapelType = null; + private Set ongoingChapelGuests = null; + private Integer ongoingCathedral = null; + private Boolean ongoingCathedralType = null; + private Set ongoingCathedralGuests = null; + private long ongoingStartTime; + private ReentrantReadWriteLock merchantLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.MERCHANT, true); private ReadLock merchRlock = merchantLock.readLock(); private WriteLock merchWlock = merchantLock.writeLock(); private Lock lock = new MonitoredReentrantLock(MonitoredLockType.CHANNEL, true); - public Channel(final int world, final int channel) { + public Channel(final int world, final int channel, long startTime) { this.world = world; this.channel = channel; + + this.ongoingStartTime = startTime + 10000; // rude approach to a world's last channel boot time, placeholder for the 1st wedding reservation ever this.mapFactory = new MapleMapFactory(null, MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Map.wz")), MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/String.wz")), world, channel); try { eventSM = new EventScriptManager(this, getEvents()); @@ -528,4 +547,297 @@ public final class Channel { lock.unlock(); } } + + public Pair>> getNextWeddingReservation(boolean cathedral) { + Integer ret; + + lock.lock(); + try { + List weddingReservationQueue = (cathedral ? cathedralReservationQueue : chapelReservationQueue); + if(weddingReservationQueue.isEmpty()) return null; + + ret = weddingReservationQueue.remove(0); + if(ret == null) return null; + } finally { + lock.unlock(); + } + + World wserv = Server.getInstance().getWorld(world); + + Pair coupleId = wserv.getMarriageQueuedCouple(ret); + Pair> typeGuests = wserv.removeMarriageQueued(ret); + + Pair couple = new Pair<>(MapleCharacter.getNameById(coupleId.getLeft()), MapleCharacter.getNameById(coupleId.getRight())); + wserv.dropMessage(6, "[WEDDING] " + couple.getLeft() + " and " + couple.getRight() + "'s wedding will start soon, and it will be held at the " + (cathedral ? "Cathedral" : "Chapel") + " in Amoria, on channel " + channel + ". Cheers for their love!!"); + + return new Pair<>(typeGuests.getLeft(), new Pair<>(ret, typeGuests.getRight())); + } + + public boolean isWeddingReserved(Integer weddingId) { + World wserv = Server.getInstance().getWorld(world); + + lock.lock(); + try { + return wserv.isMarriageQueued(weddingId) || weddingId.equals(ongoingCathedral) || weddingId.equals(ongoingChapel); + } finally { + lock.unlock(); + } + } + + public int getWeddingReservationStatus(Integer weddingId, boolean cathedral) { + if(weddingId == null) return -1; + + lock.lock(); + try { + if(cathedral) { + if(weddingId.equals(ongoingCathedral)) return 0; + + for(int i = 0; i < cathedralReservationQueue.size(); i++) { + if(weddingId.equals(cathedralReservationQueue.get(i))) { + return i + 1; + } + } + } else { + if(weddingId.equals(ongoingChapel)) return 0; + + for(int i = 0; i < chapelReservationQueue.size(); i++) { + if(weddingId.equals(chapelReservationQueue.get(i))) { + return i + 1; + } + } + } + + return -1; + } finally { + lock.unlock(); + } + } + + public int pushWeddingReservation(Integer weddingId, boolean cathedral, boolean premium, Integer groomId, Integer brideId) { + if(weddingId == null || isWeddingReserved(weddingId)) return -1; + + World wserv = Server.getInstance().getWorld(world); + wserv.putMarriageQueued(weddingId, cathedral, premium, groomId, brideId); + + lock.lock(); + try { + List weddingReservationQueue = (cathedral ? cathedralReservationQueue : chapelReservationQueue); + + int delay = ServerConstants.WEDDING_RESERVATION_DELAY - 1 - weddingReservationQueue.size(); + for(int i = 0; i < delay; i++) { + weddingReservationQueue.add(null); // push empty slots to fill the waiting time + } + + weddingReservationQueue.add(weddingId); + return weddingReservationQueue.size(); + } finally { + lock.unlock(); + } + } + + public boolean isOngoingWeddingGuest(boolean cathedral, int playerId) { + lock.lock(); + try { + if(cathedral) { + return ongoingCathedralGuests != null && ongoingCathedralGuests.contains(playerId); + } else { + return ongoingChapelGuests != null && ongoingChapelGuests.contains(playerId); + } + } finally { + lock.unlock(); + } + } + + public Integer getOngoingWedding(boolean cathedral) { + lock.lock(); + try { + return cathedral ? ongoingCathedral : ongoingChapel; + } finally { + lock.unlock(); + } + } + + public Boolean getOngoingWeddingType(boolean cathedral) { + lock.lock(); + try { + return cathedral ? ongoingCathedralType : ongoingChapelType; + } finally { + lock.unlock(); + } + } + + public void closeOngoingWedding(boolean cathedral) { + lock.lock(); + try { + if(cathedral) { + ongoingCathedral = null; + ongoingCathedralType = null; + ongoingCathedralGuests = null; + } else { + ongoingChapel = null; + ongoingChapelType = null; + ongoingChapelGuests = null; + } + } finally { + lock.unlock(); + } + } + + public void setOngoingWedding(final boolean cathedral, Boolean premium, Integer weddingId, Set guests) { + lock.lock(); + try { + if(cathedral) { + ongoingCathedral = weddingId; + ongoingCathedralType = premium; + ongoingCathedralGuests = guests; + } else { + ongoingChapel = weddingId; + ongoingChapelType = premium; + ongoingChapelGuests = guests; + } + } finally { + lock.unlock(); + } + + ongoingStartTime = System.currentTimeMillis(); + if(weddingId != null) { + ScheduledFuture weddingTask = TimerManager.getInstance().schedule(new Runnable() { + @Override + public void run() { + closeOngoingWedding(cathedral); + } + }, ServerConstants.WEDDING_RESERVATION_TIMEOUT * 60 * 1000); + + if(cathedral) { + cathedralReservationTask = weddingTask; + } else { + chapelReservationTask = weddingTask; + } + } + } + + public synchronized boolean acceptOngoingWedding(final boolean cathedral) { // couple succeeded to show up and started the ceremony + if(cathedral) { + if(cathedralReservationTask == null) return false; + + cathedralReservationTask.cancel(false); + cathedralReservationTask = null; + } else { + if(chapelReservationTask == null) return false; + + chapelReservationTask.cancel(false); + chapelReservationTask = null; + } + + return true; + } + + private static String getTimeLeft(long futureTime) { + StringBuilder str = new StringBuilder(); + long leftTime = futureTime - System.currentTimeMillis(); + + if(leftTime < 0) { + return null; + } + + byte mode = 0; + if(leftTime / (60*1000) > 0) { + mode++; //counts minutes + + if(leftTime / (60*60*1000) > 0) + mode++; //counts hours + } + + switch(mode) { + case 2: + int hours = (int) ((leftTime / (1000*60*60))); + str.append(hours + " hours, "); + + case 1: + int minutes = (int) ((leftTime / (1000*60)) % 60); + str.append(minutes + " minutes, "); + + default: + int seconds = (int) (leftTime / 1000) % 60 ; + str.append(seconds + " seconds"); + } + + return str.toString(); + } + + public long getWeddingTicketExpireTime(int resSlot) { + return ongoingStartTime + getRelativeWeddingTicketExpireTime(resSlot); + } + + public static long getRelativeWeddingTicketExpireTime(int resSlot) { + return (resSlot * ServerConstants.WEDDING_RESERVATION_INTERVAL * 60 * 1000); + } + + public String getWeddingReservationTimeLeft(Integer weddingId) { + if(weddingId == null) return null; + + lock.lock(); + try { + boolean cathedral = true; + + int resStatus; + resStatus = getWeddingReservationStatus(weddingId, true); + if(resStatus < 0) { + cathedral = false; + resStatus = getWeddingReservationStatus(weddingId, false); + + if(resStatus < 0) { + return null; + } + } + + String venue = (cathedral ? "Cathedral" : "Chapel"); + if(resStatus == 0) { + return venue + " - RIGHT NOW"; + } + + return venue + " - " + getTimeLeft(ongoingStartTime + (resStatus * ServerConstants.WEDDING_RESERVATION_INTERVAL * 60 * 1000)) + " from now"; + } finally { + lock.unlock(); + } + } + + public Pair getWeddingCoupleForGuest(int guestId, boolean cathedral) { + lock.lock(); + try { + return (isOngoingWeddingGuest(cathedral, guestId)) ? Server.getInstance().getWorld(world).getRelationshipCouple(getOngoingWedding(cathedral)) : null; + } finally { + lock.unlock(); + } + } + + public void dropMessage(int type, String message) { + for (MapleCharacter player : getPlayerStorage().getAllCharacters()) { + player.dropMessage(type, message); + } + } + + public void debugMarriageStatus() { + System.out.println(" ----- WORLD DATA -----"); + Server.getInstance().getWorld(world).debugMarriageStatus(); + + System.out.println(" ----- CH. " + channel + " -----"); + System.out.println(" ----- CATHEDRAL -----"); + System.out.println("Current Queue: " + cathedralReservationQueue); + System.out.println("Cancel Task: " + (cathedralReservationTask != null)); + System.out.println("Ongoing wid: " + ongoingCathedral); + System.out.println(); + System.out.println("Ongoing wid: " + ongoingCathedral + " isPremium: " + ongoingCathedralType); + System.out.println("Guest list: " + ongoingCathedralGuests); + System.out.println(); + System.out.println(" ----- CHAPEL -----"); + System.out.println("Current Queue: " + chapelReservationQueue); + System.out.println("Cancel Task: " + (chapelReservationTask != null)); + System.out.println("Ongoing wid: " + ongoingChapel); + System.out.println(); + System.out.println("Ongoing wid: " + ongoingChapel + " isPremium: " + ongoingChapelType); + System.out.println("Guest list: " + ongoingChapelGuests); + System.out.println(); + System.out.println("Starttime: " + ongoingStartTime); + } } \ No newline at end of file diff --git a/src/net/server/channel/handlers/CashOperationHandler.java b/src/net/server/channel/handlers/CashOperationHandler.java index 2c35567bc5..913078b1cb 100644 --- a/src/net/server/channel/handlers/CashOperationHandler.java +++ b/src/net/server/channel/handlers/CashOperationHandler.java @@ -204,11 +204,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler { Equip equip = (Equip) item; if(equip.getRingId() >= 0) { MapleRing ring = MapleRing.loadFromDb(equip.getRingId()); - if (ring.getItemId() > 1112012) { - chr.addFriendshipRing(ring); - } else { - chr.addCrushRing(ring); - } + chr.addPlayerRing(ring); } } } @@ -281,8 +277,8 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler { } c.announce(MaplePacketCreator.showCash(c.getPlayer())); } else if (action == 0x23) { //Friendship :3 - slea.readInt(); //Birthday - // if (checkBirthday(c, birthday)) { + slea.readInt(); //Birthday + // if (checkBirthday(c, birthday)) { int payment = slea.readByte(); slea.skip(3); //0s int snID = slea.readInt(); diff --git a/src/net/server/channel/handlers/EnterCashShopHandler.java b/src/net/server/channel/handlers/EnterCashShopHandler.java index 268034ccbe..ee01509f08 100644 --- a/src/net/server/channel/handlers/EnterCashShopHandler.java +++ b/src/net/server/channel/handlers/EnterCashShopHandler.java @@ -60,6 +60,7 @@ public class EnterCashShopHandler extends AbstractMaplePacketHandler { mc.unregisterChairBuff(); Server.getInstance().getPlayerBuffStorage().addBuffsToStorage(mc.getId(), mc.getAllBuffs()); mc.setAwayFromWorld(true); + mc.notifyMapTransferToPartner(-1); mc.cancelAllBuffs(true); mc.cancelBuffExpireTask(); mc.cancelDiseaseExpireTask(); diff --git a/src/net/server/channel/handlers/EnterMTSHandler.java b/src/net/server/channel/handlers/EnterMTSHandler.java index c40bf00ca7..22b090d1c3 100644 --- a/src/net/server/channel/handlers/EnterMTSHandler.java +++ b/src/net/server/channel/handlers/EnterMTSHandler.java @@ -69,6 +69,7 @@ public final class EnterMTSHandler extends AbstractMaplePacketHandler { chr.unregisterChairBuff(); Server.getInstance().getPlayerBuffStorage().addBuffsToStorage(chr.getId(), chr.getAllBuffs()); chr.setAwayFromWorld(true); + chr.notifyMapTransferToPartner(-1); chr.cancelAllBuffs(true); chr.cancelBuffExpireTask(); chr.cancelDiseaseExpireTask(); diff --git a/src/net/server/channel/handlers/GeneralChatHandler.java b/src/net/server/channel/handlers/GeneralChatHandler.java index e304e2dae4..17abca6b78 100644 --- a/src/net/server/channel/handlers/GeneralChatHandler.java +++ b/src/net/server/channel/handlers/GeneralChatHandler.java @@ -51,7 +51,7 @@ public final class GeneralChatHandler extends net.AbstractMaplePacketHandler { String[] sp = s.split(" "); sp[0] = sp[0].toLowerCase().substring(1); - if(Commands.executeSolaxiaPlayerCommand(c, sp, heading)) { + if(Commands.executeHeavenMsPlayerCommand(c, sp, heading)) { String command = ""; for (String used : sp) { command += used + " "; diff --git a/src/net/server/channel/handlers/PlayerLoggedinHandler.java b/src/net/server/channel/handlers/PlayerLoggedinHandler.java index b2f6238a53..038d1790bb 100644 --- a/src/net/server/channel/handlers/PlayerLoggedinHandler.java +++ b/src/net/server/channel/handlers/PlayerLoggedinHandler.java @@ -54,6 +54,7 @@ import constants.GameConstants; import constants.ServerConstants; import java.util.Collections; import java.util.Comparator; +import tools.packets.Wedding; public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler { @@ -255,6 +256,16 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler { player.updateCouponRates(); player.receivePartyMemberHP(); + + if(player.getPartnerId() > 0) { + int partnerId = player.getPartnerId(); + final MapleCharacter partner = c.getWorldServer().getPlayerStorage().getCharacterById(partnerId); + + if(partner != null && !partner.isAwayFromWorld()) { + player.announce(Wedding.OnNotifyWeddingPartnerTransfer(partnerId, partner.getMapId())); + partner.announce(Wedding.OnNotifyWeddingPartnerTransfer(player.getId(), player.getMapId())); + } + } } private static void showDueyNotification(MapleClient c, MapleCharacter player) { diff --git a/src/net/server/channel/handlers/RingActionHandler.java b/src/net/server/channel/handlers/RingActionHandler.java index b9ee5d093d..727d7f7237 100644 --- a/src/net/server/channel/handlers/RingActionHandler.java +++ b/src/net/server/channel/handlers/RingActionHandler.java @@ -25,86 +25,477 @@ package net.server.channel.handlers; //import java.sql.PreparedStatement; import client.MapleClient; import client.MapleCharacter; +import client.inventory.MapleInventoryType; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; //import tools.DatabaseConnection; import net.AbstractMaplePacketHandler; +import server.MapleInventoryManipulator; +import tools.DatabaseConnection; import tools.data.input.SeekableLittleEndianAccessor; //import scripting.npc.NPCScriptManager; +import tools.Pair; import tools.MaplePacketCreator; +import tools.packets.Wedding; +import net.server.world.World; +import net.server.channel.Channel; +import server.MapleItemInformationProvider; +import client.MapleRing; +import client.inventory.Equip; +import client.inventory.Item; /** * @author Jvlaple + * @author Ronan (major overhaul on Ring handling mechanics) */ public final class RingActionHandler extends AbstractMaplePacketHandler { - public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { - byte mode = slea.readByte(); - MapleCharacter player = c.getPlayer(); - switch (mode) { - case 0: //Send - String partnerName = slea.readMapleAsciiString(); - MapleCharacter partner = c.getChannelServer().getPlayerStorage().getCharacterByName(partnerName); - if (partnerName.equalsIgnoreCase(player.getName())) { - c.getPlayer().dropMessage(1, "You cannot put your own name in it."); - return; - } else if (partner == null) { - c.getPlayer().dropMessage(1, partnerName + " was not found on this channel. If you are both logged in, please make sure you are in the same channel."); - return; - } else if (partner.getGender() == player.getGender()) { - c.getPlayer().dropMessage(1, "Your partner is the same gender as you."); - return; - } //else if (player.isMarried() && partner.isMarried()) - // NPCScriptManager.getInstance().start(partner.getClient(), 9201002, "marriagequestion", player); - break; - case 1: //Cancel send - c.getPlayer().dropMessage(1, "You've cancelled the request."); - boolean accepted = slea.readByte() > 0; - String proposerName = slea.readMapleAsciiString(); - if (accepted) { - c.announce(MaplePacketCreator.sendEngagementRequest(proposerName)); - } - break; - case 2: - slea.readByte(); //type - case 3: //Drop Ring - /* - if (player.getPartner() != null) { - try { - Connection con = DatabaseConnection.getConnection(); - int pid = 0; - if (player.getGender() == 0) - pid = player.getId(); - else - pid = player.getPartner().getId();//we have an engagements SQL? - PreparedStatement ps = con.prepareStatement("DELETE FROM engagements WHERE husbandid = ?"); - ps.setInt(1, pid); - ps.executeUpdate(); - ps.close(); - ps = con.prepareStatement("UPDATE characters SET marriagequest = 0 WHERE id = ?, and WHERE id = ?"); - ps.setInt(1, player.getId()); - ps.setInt(2, player.getPartner().getId()); - ps.executeUpdate(); - ps.close(); - con.close(); - } catch (Exception ex) { - ex.printStackTrace(); - } - c.getPlayer().dropMessage(1, "Your engagement has been broken up."); - break; - }*/ - break; - case 9: // groom's wishlist - int amount = slea.readShort(); - if (amount > 10) { - amount = 10; - } - String[] items = new String[10]; - for (int i = 0; i < amount; i++) { - items[i] = slea.readMapleAsciiString(); - } - c.announce(MaplePacketCreator.sendGroomWishlist()); //WTF< - break; - default: - System.out.println("NEW RING ACTION " + mode); - break; + private static int getBoxId(int useItemId) { + return useItemId == 2240000 ? 4031357 : (useItemId == 2240001 ? 4031359 : (useItemId == 2240002 ? 4031361 : (useItemId == 2240003 ? 4031363 : (1112300 + (useItemId - 2240004))))); + } + + public static void sendEngageProposal(final MapleClient c, final String name, final int itemid) { + final int newBoxId = getBoxId(itemid); + final MapleCharacter target = c.getChannelServer().getPlayerStorage().getCharacterByName(name); + final MapleCharacter source = c.getPlayer(); + + // TODO: get the correct packet bytes for these popups + if (source.isMarried()) { + source.dropMessage(1, "You're already married!"); + source.announce(Wedding.OnMarriageResult((byte) 0)); + return; + } else if (source.getPartnerId() > 0) { + source.dropMessage(1, "You're already engaged!"); + source.announce(Wedding.OnMarriageResult((byte) 0)); + return; + } else if (source.getMarriageItemId() > 0) { + source.dropMessage(1, "You're already engaging someone!"); + source.announce(Wedding.OnMarriageResult((byte) 0)); + return; + } else if (target == null) { + source.dropMessage(1, "Unable to find " + name + " on this channel."); + source.announce(Wedding.OnMarriageResult((byte) 0)); + return; + } else if (target == source) { + source.dropMessage(1, "You can't engage yourself."); + source.announce(Wedding.OnMarriageResult((byte) 0)); + return; + } else if (!target.getMap().equals(source.getMap())) { + source.dropMessage(1, "Make sure your partner is on the same map!"); + source.announce(Wedding.OnMarriageResult((byte) 0)); + return; + } else if (!source.haveItem(itemid) || itemid < 2240000 || itemid > 2240015) { + source.announce(Wedding.OnMarriageResult((byte) 0)); + return; + } else if (target.isMarried()) { + source.dropMessage(1, "The player is already married!"); + source.announce(Wedding.OnMarriageResult((byte) 0)); + return; + } else if (target.getPartnerId() > 0 || target.getMarriageItemId() > 0) { + source.dropMessage(1, "The player is already engaged!"); + source.announce(Wedding.OnMarriageResult((byte) 0)); + return; + } else if (target.getGender() != 1) { + source.dropMessage(1, "You may only propose to a girl!"); + source.announce(Wedding.OnMarriageResult((byte) 0)); + return; + } else if (!MapleInventoryManipulator.checkSpace(c, newBoxId, 1, "")) { + source.dropMessage(5, "You don't have a ETC slot available right now!"); + source.announce(Wedding.OnMarriageResult((byte) 0)); + return; + } else if (!MapleInventoryManipulator.checkSpace(target.getClient(), newBoxId + 1, 1, "")) { + source.dropMessage(5, "The girl you proposed doesn't have a ETC slot available right now."); + source.announce(Wedding.OnMarriageResult((byte) 0)); + return; + } + + source.setMarriageItemId(itemid); + target.announce(Wedding.OnMarriageRequest(source.getName(), source.getId())); + } + + private static void eraseEngagementOffline(int characterId) { + try { + Connection con = DatabaseConnection.getConnection(); + eraseEngagementOffline(characterId, con); + con.close(); + } catch(SQLException sqle) { + sqle.printStackTrace(); } } + + private static void eraseEngagementOffline(int characterId, Connection con) throws SQLException { + PreparedStatement ps = con.prepareStatement("UPDATE characters SET marriageItemId=-1, partnerId=-1 WHERE id=?"); + ps.setInt(1, characterId); + ps.executeUpdate(); + + ps.close(); + } + + private static void breakEngagementOffline(int characterId) { + try { + Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("SELECT marriageItemId FROM characters WHERE id=?"); + ps.setInt(1, characterId); + ResultSet rs = ps.executeQuery(); + if (rs.next()) { + int marriageItemId = rs.getInt("marriageItemId"); + + if (marriageItemId > 0) { + PreparedStatement ps2 = con.prepareStatement("UPDATE inventoryitems SET expiration=0 WHERE itemid=? AND characterid=?"); + ps2.setInt(1, marriageItemId); + ps2.setInt(2, characterId); + + ps2.executeUpdate(); + ps2.close(); + } + } + rs.close(); + ps.close(); + + eraseEngagementOffline(characterId, con); + + con.close(); + } catch (SQLException ex) { + System.out.println("Error updating offline breakup " + ex.getMessage()); + } + } + + private synchronized static void breakMarriage(MapleCharacter chr) { + int partnerid = chr.getPartnerId(); + if(partnerid <= 0) return; + + chr.getClient().getWorldServer().deleteRelationship(chr.getId(), partnerid); + MapleRing.removeRing(chr.getMarriageRing()); + + MapleCharacter partner = chr.getClient().getWorldServer().getPlayerStorage().getCharacterById(partnerid); + if(partner == null) { + eraseEngagementOffline(partnerid); + } else { + partner.dropMessage(5, chr.getName() + " has decided to break up the marriage."); + + //partner.announce(Wedding.OnMarriageResult((byte) 0)); ok, how to gracefully unengage someone without the need to cc? + + resetRingId(partner); + partner.setPartnerId(-1); + partner.setMarriageItemId(-1); + partner.addMarriageRing(null); + } + + chr.dropMessage(5, "You have successfully break the marriage with " + MapleCharacter.getNameById(partnerid) + "."); + //chr.announce(Wedding.OnMarriageResult((byte) 0)); + + resetRingId(chr); + chr.setPartnerId(-1); + chr.setMarriageItemId(-1); + chr.addMarriageRing(null); + } + + private static void resetRingId(MapleCharacter player) { + int ringitemid = player.getMarriageRing().getItemId(); + + Item it = player.getInventory(MapleInventoryType.EQUIP).findById(ringitemid); + if(it == null) { + it = player.getInventory(MapleInventoryType.EQUIPPED).findById(ringitemid); + } + + if(it != null) { + Equip eqp = (Equip) it; + eqp.setRingId(-1); + } + } + + private synchronized static void breakEngagement(MapleCharacter chr) { + int partnerid = chr.getPartnerId(); + int marriageitemid = chr.getMarriageItemId(); + + chr.getClient().getWorldServer().deleteRelationship(chr.getId(), partnerid); + + MapleCharacter partner = chr.getClient().getWorldServer().getPlayerStorage().getCharacterById(partnerid); + if(partner == null) { + breakEngagementOffline(partnerid); + } else { + partner.dropMessage(5, chr.getName() + " has decided to break up the engagement."); + + int partnerMarriageitemid = marriageitemid + ((chr.getGender() == 0) ? 1 : -1); + if(partner.haveItem(partnerMarriageitemid)) { + MapleInventoryManipulator.removeById(partner.getClient(), MapleInventoryType.ETC, partnerMarriageitemid, (short) 1, false, false); + } + //partner.announce(Wedding.OnMarriageResult((byte) 0)); ok, how to gracefully unengage someone without the need to cc? + partner.setPartnerId(-1); + partner.setMarriageItemId(-1); + } + + if(chr.haveItem(marriageitemid)) { + MapleInventoryManipulator.removeById(chr.getClient(), MapleInventoryType.ETC, marriageitemid, (short) 1, false, false); + } + chr.dropMessage(5, "You have successfully break the engagement with " + MapleCharacter.getNameById(partnerid) + "."); + //chr.announce(Wedding.OnMarriageResult((byte) 0)); + chr.setPartnerId(-1); + chr.setMarriageItemId(-1); + } + + public static void breakMarriageRing(MapleCharacter chr, final int wItemId) { + final MapleInventoryType type = MapleInventoryType.getByType((byte) (wItemId / 1000000)); + final Item wItem = chr.getInventory(type).findById(wItemId); + final boolean weddingToken = (wItem != null && type == MapleInventoryType.ETC && wItemId / 10000 == 403); + final boolean weddingRing = (wItem != null && wItemId / 10 == 111280); + + if (weddingRing) { + if(chr.getPartnerId() > 0) { + breakMarriage(chr); + } + + chr.getMap().disappearingItemDrop(chr, chr, wItem, chr.getPosition()); + } else if (weddingToken) { + if (chr.getPartnerId() > 0) { + breakEngagement(chr); + } + + chr.getMap().disappearingItemDrop(chr, chr, wItem, chr.getPosition()); + } + } + + public static void giveMarriageRings(MapleCharacter player, MapleCharacter partner, int marriageRingId) { + int ringid = MapleRing.createRing(marriageRingId, player, partner); + MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance(); + + Item ringObj = ii.getEquipById(marriageRingId); + Equip ringEqp = (Equip) ringObj; + ringEqp.setRingId(ringid); + player.addMarriageRing(MapleRing.loadFromDb(ringid)); + MapleInventoryManipulator.addFromDrop(player.getClient(), ringEqp, false, -1); + player.broadcastMarriageMessage(); + + ringObj = ii.getEquipById(marriageRingId); + ringEqp = (Equip) ringObj; + ringEqp.setRingId(ringid + 1); + partner.addMarriageRing(MapleRing.loadFromDb(ringid + 1)); + MapleInventoryManipulator.addFromDrop(partner.getClient(), ringEqp, false, -1); + partner.broadcastMarriageMessage(); + } + + @Override + public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { + byte mode = slea.readByte(); + String name; + byte slot; + switch(mode) { + case 0: // Send Proposal + sendEngageProposal(c, slea.readMapleAsciiString(), slea.readInt()); + break; + + case 1: // Cancel Proposal + System.out.println("Cancel Ring Action: " + slea.toString()); // log packet and see if any bytes + if(c.getPlayer().getMarriageItemId() / 1000000 != 4) { + c.getPlayer().setMarriageItemId(-1); + } + break; + + case 2: // Accept/Deny Proposal + final boolean accepted = slea.readByte() > 0; + name = slea.readMapleAsciiString(); + final int id = slea.readInt(); + + final MapleCharacter source = c.getWorldServer().getPlayerStorage().getCharacterByName(name); + final MapleCharacter target = c.getPlayer(); + + if (source == null) { + target.announce(MaplePacketCreator.enableActions()); + return; + } + + final int itemid = source.getMarriageItemId(); + if (target.getPartnerId() > 0 || source.getId() != id || itemid <= 0 || !source.haveItem(itemid) || source.getPartnerId() > 0 || !source.isAlive() || !target.isAlive()) { + target.announce(MaplePacketCreator.enableActions()); + return; + } + + if (accepted) { + final int newItemId = getBoxId(itemid); + if (!MapleInventoryManipulator.checkSpace(c, newItemId, 1, "") || !MapleInventoryManipulator.checkSpace(source.getClient(), newItemId, 1, "")) { + target.announce(MaplePacketCreator.enableActions()); + return; + } + + try { + MapleInventoryManipulator.removeById(source.getClient(), MapleInventoryType.USE, itemid, 1, false, false); + + int marriageId = c.getWorldServer().createRelationship(source.getId(), target.getId()); + source.setPartnerId(target.getId()); // engage them (new marriageitemid, partnerid for both) + target.setPartnerId(source.getId()); + + source.setMarriageItemId(newItemId); + target.setMarriageItemId(newItemId + 1); + + MapleInventoryManipulator.addById(source.getClient(), newItemId, (short) 1); + MapleInventoryManipulator.addById(c, (newItemId + 1), (short) 1); + + source.announce(Wedding.OnMarriageResult(marriageId, source, false)); + target.announce(Wedding.OnMarriageResult(marriageId, source, false)); + + source.announce(Wedding.OnNotifyWeddingPartnerTransfer(target.getId(), target.getMapId())); + target.announce(Wedding.OnNotifyWeddingPartnerTransfer(source.getId(), source.getMapId())); + } catch (Exception e) { + System.out.println("Error with engagement " + e.getMessage()); + } + } else { + source.dropMessage(1, "She has politely declined your engagement request."); + source.announce(Wedding.OnMarriageResult((byte) 0)); + + source.setMarriageItemId(-1); + } + break; + + case 3: // Break Engagement + breakMarriageRing(c.getPlayer(), slea.readInt()); + break; + + case 5: // Invite %s to Wedding + name = slea.readMapleAsciiString(); + int marriageId = slea.readInt(); + slot = slea.readByte(); // this is an int + + int itemId; + try { + itemId = c.getPlayer().getInventory(MapleInventoryType.ETC).getItem(slot).getItemId(); + } catch(NullPointerException npe) { + c.announce(MaplePacketCreator.enableActions()); + return; + } + + if((itemId != 4031377 && itemId != 4031395) || !c.getPlayer().haveItem(itemId)) { + c.announce(MaplePacketCreator.enableActions()); + return; + } + + String groom = c.getPlayer().getName(), bride = MapleCharacter.getNameById(c.getPlayer().getPartnerId()); + int guest = MapleCharacter.getIdByName(name); + if (groom == null || bride == null || groom.equals("") || bride.equals("") || guest <= 0) { + c.getPlayer().dropMessage(5, "Unable to find " + name + "!"); + return; + } + + try { + World wserv = c.getWorldServer(); + Pair registration = wserv.getMarriageQueuedLocation(marriageId); + + if(registration != null) { + if(wserv.addMarriageGuest(marriageId, guest)) { + boolean cathedral = registration.getLeft(); + int newItemId = cathedral ? 4031407 : 4031406; + + Channel cserv = c.getChannelServer(); + int resStatus = cserv.getWeddingReservationStatus(marriageId, cathedral); + if(resStatus > 0) { + long expiration = cserv.getWeddingTicketExpireTime(resStatus + 1); + + MapleCharacter guestChr = c.getWorldServer().getPlayerStorage().getCharacterById(guest); + if(guestChr != null && MapleInventoryManipulator.checkSpace(guestChr.getClient(), newItemId, 1, "") && MapleInventoryManipulator.addById(guestChr.getClient(), newItemId, (short) 1, expiration)) { + guestChr.dropMessage(6, "[WEDDING] You've been invited to " + groom + " and " + bride + "'s Wedding!"); + } else { + c.getPlayer().sendNote(name, "You've been invited to " + groom + " and " + bride + "'s Wedding! Receive your invitation from Duey!", (byte) 0); + + Item weddingTicket = new Item(newItemId, (short) 0, (short) 1); + weddingTicket.setExpiration(expiration); + + DueyHandler.addItemToDB(weddingTicket, 1, 0, groom, guest); + } + } else { + c.getPlayer().dropMessage(5, "Wedding is already under way. You cannot invite any more guests for the event."); + } + } else { + c.getPlayer().dropMessage(5, "'" + name + "' is already invited for your marriage."); + } + } else { + c.getPlayer().dropMessage(5, "Invitation was not sent to '" + name + "'. Either the time for your marriage reservation already came or it was not found."); + } + + } catch (SQLException ex) { + ex.printStackTrace(); + return; + } + + c.getAbstractPlayerInteraction().gainItem(itemId, (short) -1); + break; + + case 6: // Open Wedding Invitation + slot = (byte) slea.readInt(); + int invitationid = slea.readInt(); + + if(invitationid == 4031406 || invitationid == 4031407) { + Item item = c.getPlayer().getInventory(MapleInventoryType.ETC).getItem(slot); + if(item == null || item.getItemId() != invitationid) { + c.announce(MaplePacketCreator.enableActions()); + return; + } + + // collision case: most soon-to-come wedding will show up + Pair coupleId = c.getWorldServer().getWeddingCoupleForGuest(c.getPlayer().getId(), invitationid == 4031407); + if (coupleId != null) { + int groomId = coupleId.getLeft(), brideId = coupleId.getRight(); + c.announce(Wedding.sendWeddingInvitation(MapleCharacter.getNameById(groomId), MapleCharacter.getNameById(brideId))); + } + } + + break; + + case 9: // Groom and Bride's Wishlist + short size = slea.readShort(); + List itemnames = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + itemnames.add(slea.readMapleAsciiString()); + } + + System.out.println("G&B WISHLIST: " + itemnames); + + /* + if (c.getPlayer().getMarriageItemId() > -1) { + switch(c.getPlayer().getMarriageItemId()) { + case 10: // Premium Cathedral + c.getAbstractPlayerInteraction().gainItem(4031375, (short)1); + c.getAbstractPlayerInteraction().gainItem(4031395, (short)15); + break; + case 11: // Normal Cathedral + c.getAbstractPlayerInteraction().gainItem(4031480, (short)1); + c.getAbstractPlayerInteraction().gainItem(4031395, (short)15); + break; + case 20: // Premium Chapel + c.getAbstractPlayerInteraction().gainItem(4031376, (short)1); + c.getAbstractPlayerInteraction().gainItem(4031377, (short)15); + break; + case 21: // Normal Chapel + c.getAbstractPlayerInteraction().gainItem(4031481, (short)1); + c.getAbstractPlayerInteraction().gainItem(4031377, (short)15); + break; + default: { + System.out.println("Invalid Wedding Type for player " + c.getPlayer().getName() + "!"); + break; + } + } + + //c.getPlayer().setMarriageItemId(-1); ????? + } + + if (c.getPlayer().getWishlist() == null) { + c.getPlayer().registerWishlist(itemnames); + } + + if (c.getPlayer().getWedding() != null) { + if (c.getPlayer().getGender() == 0 ? c.getPlayer().getWedding().isExistantGroom(c.getPlayer().getId()) : c.getPlayer().getWedding().isExistantBride(c.getPlayer().getId())) { + c.getPlayer().getWedding().registerWishlist(c.getPlayer().getGender() == 1, itemnames); + } + } + */ + break; + + default: + System.out.println("Unhandled RING_ACTION Mode: " + slea.toString()); + break; + } + + c.getSession().write(MaplePacketCreator.enableActions()); + } } diff --git a/src/net/server/channel/handlers/SpouseChatHandler.java b/src/net/server/channel/handlers/SpouseChatHandler.java index c9640ad59a..10b822fec9 100644 --- a/src/net/server/channel/handlers/SpouseChatHandler.java +++ b/src/net/server/channel/handlers/SpouseChatHandler.java @@ -21,36 +21,29 @@ */ package net.server.channel.handlers; -//import client.MapleCharacter; +import client.MapleCharacter; import client.MapleClient; -//import client.command.CommandProcessor; import net.AbstractMaplePacketHandler; -//import tools.MaplePacketCreator; +import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; public final class SpouseChatHandler extends AbstractMaplePacketHandler { + @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { - System.out.println(slea.toString()); -// slea.readMapleAsciiString();//recipient -// String msg = slea.readMapleAsciiString(); -// if (!CommandProcessor.processCommand(c, msg)) -// if (c.getPlayer().isMarried()) { -// MapleCharacter wife = c.getChannelServer().getPlayerStorage().getCharacterById(c.getPlayer().getPartnerId()); -// if (wife != null) { -// wife.getClient().announce(MaplePacketCreator.sendSpouseChat(c.getPlayer(), msg)); -// c.announce(MaplePacketCreator.sendSpouseChat(c.getPlayer(), msg)); -// } else -// try { -// if (c.getChannelServer().getWorldInterface().isConnected(wife.getName())) { -// c.getChannelServer().getWorldInterface().sendSpouseChat(c.getPlayer().getName(), wife.getName(), msg); -// c.announce(MaplePacketCreator.sendSpouseChat(c.getPlayer(), msg)); -// } else -// c.getPlayer().message("You are either not married or your spouse is currently offline."); -// } catch (Exception e) { -// e.printStackTrace(); -// c.getPlayer().message("You are either not married or your spouse is currently offline."); -// c.getChannelServer().reconnectWorld(); -// } -// } + slea.readMapleAsciiString();//recipient + String msg = slea.readMapleAsciiString(); + + int partnerId = c.getPlayer().getPartnerId(); + if (partnerId > 0) { // yay marriage + MapleCharacter spouse = c.getWorldServer().getPlayerStorage().getCharacterById(partnerId); + if (spouse != null) { + spouse.announce(MaplePacketCreator.OnCoupleMessage(c.getPlayer().getName(), msg, true)); + c.announce(MaplePacketCreator.OnCoupleMessage(c.getPlayer().getName(), msg, true)); + } else { + c.getPlayer().dropMessage(5, "Your spouse is currently offline."); + } + } else { + c.getPlayer().dropMessage(5, "You don't have a spouse."); + } } } diff --git a/src/net/server/channel/handlers/WeddingHandler.java b/src/net/server/channel/handlers/WeddingHandler.java index 8ea4397480..872f01f565 100644 --- a/src/net/server/channel/handlers/WeddingHandler.java +++ b/src/net/server/channel/handlers/WeddingHandler.java @@ -1,39 +1,93 @@ /* - * To change this template, choose Tools | Templates + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates * and open the template in the editor. */ + package net.server.channel.handlers; -import client.MapleCharacter; -import client.MapleClient; import client.inventory.Item; import client.inventory.MapleInventoryType; +import client.MapleCharacter; +import client.MapleClient; import constants.ItemConstants; +import tools.DatabaseConnection; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; import net.AbstractMaplePacketHandler; +import server.MapleInventoryManipulator; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; +import tools.packets.Wedding; /** * - * @author Kevin + * @author Eric */ -public class WeddingHandler extends AbstractMaplePacketHandler { - - @Override - public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { - //System.out.println("Wedding Packet: " + slea); - MapleCharacter chr = c.getPlayer(); - byte operation = slea.readByte(); - switch (operation) { - case 0x06://Add an item to the Wedding Registry - short slot = slea.readShort(); - int itemid = slea.readInt(); - short quantity = slea.readShort(); - MapleInventoryType type = ItemConstants.getInventoryType(itemid); - Item item = chr.getInventory(type).getItem(slot); - if (itemid == item.getItemId() && quantity <= item.getQuantity()) { - c.announce(MaplePacketCreator.addItemToWeddingRegistry(chr, item)); +public final class WeddingHandler extends AbstractMaplePacketHandler { + /* + public static final void OnWeddingProgress(byte action, MapleClient c) { + // -- Pelvis Bebop: + // 0x00: "We are gathered here today..." + // 0x01: "Very well! I pronounce you..." + // 0x02: "You two truly are a sight to..." + // 0x03: Wedding Ceremony Ended, initialize the Wedding Effect upon the two married characters + // -- High Priest John: (Unknown action bytes) + // 0x00: " " + // 0x01: " " + // 0x02: "Do you wish to bless this couple?..." + // 0x03: Wedding Ceremony Ended, initialize the Wedding Effect upon the two married characters + if (c.getPlayer().getWedding() != null) { + if (c.getPlayer().getGender() == 0 ? c.getPlayer().getWedding().isExistantGroom(c.getPlayer().getId()) : c.getPlayer().getWedding().isExistantBride(c.getPlayer().getId())) { + c.getPlayer().getMap().broadcastMessage(Wedding.OnWeddingProgress(action == 2, c.getPlayer().getId(), c.getPlayer().getPartnerId(), (byte)(action+1))); + c.getPlayer().getWedding().incrementStage(); + c.getPlayer().getPartner().getWedding().incrementStage(); // pls don't b a bitch and throw npe ):< + if (action == 2) { + c.getPlayer().setMarried(true); + c.getChannelServer().getPlayerStorage().getCharacterById(c.getPlayer().getPartnerId()).setMarried(true); } + } + } + c.announce(MaplePacketCreator.enableActions()); + } + + public static final void OnWeddingGiftResult(SeekableLittleEndianAccessor slea, MapleClient c) { + System.out.println("New WEDDING_GIFT_RESULT: " + slea.toString()); + byte mode = slea.readByte(); + switch(mode) { + case 0x06: // "SEND ITEM" + short slot = slea.readShort(); // isn't this a byte? o.O + int itemId = slea.readInt(); + short quantity = slea.readShort(); + if (c.getPlayer().getInventory(ItemConstants.getInventoryType(itemId)).getItem((byte)slot).getItemId() == itemId && c.getPlayer().getInventory(InventoryConstants.getInventoryType(itemId)).getItem((byte)slot).getQuantity() >= quantity) { + if (c.getPlayer().getWedding() == null) { + c.getPlayer().startWedding(); // TODO + } + List itemnames = new ArrayList<>(); + Item item = c.getPlayer().getInventory(ItemConstants.getInventoryType(itemId)).getItem((byte)slot); + boolean bride = false; + c.getPlayer().getWedding().registerWishlistItem(item, bride); + c.announce(Wedding.OnWeddingGiftResult((byte)11, itemnames, c.getPlayer().getWedding().getWishlistItems(bride))); // todo: remove item from inventory if success + } + case 0x08: // "EXIT" + if (slea.available() != 0) { + System.out.println("WEDDING_GIFT_RESULT: " + slea.toString()); + } + c.announce(MaplePacketCreator.enableActions()); + break; + default: { + System.out.println("Unknown Mode Found: " + mode + " : " + slea.toString()); + } } } -} + */ + + @Override + public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { + c.announce(MaplePacketCreator.enableActions()); + } +} \ No newline at end of file diff --git a/src/net/server/channel/handlers/WeddingTalkHandler.java b/src/net/server/channel/handlers/WeddingTalkHandler.java new file mode 100644 index 0000000000..62be770393 --- /dev/null +++ b/src/net/server/channel/handlers/WeddingTalkHandler.java @@ -0,0 +1,53 @@ +/* + This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server + Copyleft (L) 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package net.server.channel.handlers; + +import client.MapleClient; +import net.AbstractMaplePacketHandler; +import scripting.event.EventInstanceManager; +import tools.MaplePacketCreator; +import tools.data.input.SeekableLittleEndianAccessor; +import tools.packets.Wedding; + +/** + * + * @author Ronan + */ +public final class WeddingTalkHandler extends AbstractMaplePacketHandler { + + @Override + public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { + byte action = slea.readByte(); + if(action == 1) { + EventInstanceManager eim = c.getPlayer().getEventInstance(); + + if(eim != null && !(c.getPlayer().getId() == eim.getIntProperty("groomId") || c.getPlayer().getId() == eim.getIntProperty("brideId"))) { + c.announce(Wedding.OnWeddingProgress(false, 0, 0, (byte) 2)); + } else { + c.announce(Wedding.OnWeddingProgress(true, 0, 0, (byte) 3)); + } + } else { + c.announce(Wedding.OnWeddingProgress(true, 0, 0, (byte) 3)); + } + + c.announce(MaplePacketCreator.enableActions()); + } +} \ No newline at end of file diff --git a/src/net/server/channel/handlers/WeddingTalkMoreHandler.java b/src/net/server/channel/handlers/WeddingTalkMoreHandler.java new file mode 100644 index 0000000000..8d85fd727b --- /dev/null +++ b/src/net/server/channel/handlers/WeddingTalkMoreHandler.java @@ -0,0 +1,47 @@ +/* + This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server + Copyleft (L) 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +package net.server.channel.handlers; + +import client.MapleClient; +import net.AbstractMaplePacketHandler; +import scripting.event.EventInstanceManager; +import tools.MaplePacketCreator; +import tools.data.input.SeekableLittleEndianAccessor; +import tools.packets.Wedding; + +/** + * + * @author Ronan + */ +public final class WeddingTalkMoreHandler extends AbstractMaplePacketHandler { + + @Override + public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { + EventInstanceManager eim = c.getPlayer().getEventInstance(); + if(eim != null && !(c.getPlayer().getId() == eim.getIntProperty("groomId") || c.getPlayer().getId() == eim.getIntProperty("brideId"))) { + eim.gridInsert(c.getPlayer(), 1); + c.getPlayer().dropMessage(5, "High Priest John: Your blessings have been added to their love. What a noble act for a lovely couple!"); + } + + c.announce(Wedding.OnWeddingProgress(true, 0, 0, (byte) 3)); + c.announce(MaplePacketCreator.enableActions()); + } +} \ No newline at end of file diff --git a/src/net/server/worker/WeddingReservationWorker.java b/src/net/server/worker/WeddingReservationWorker.java new file mode 100644 index 0000000000..cfa93fa193 --- /dev/null +++ b/src/net/server/worker/WeddingReservationWorker.java @@ -0,0 +1,56 @@ +/* + This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server + Copyleft (L) 2017 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.worker; + +import java.util.Set; +import net.server.world.World; +import net.server.channel.Channel; +import tools.Pair; + +/** + * @author Ronan + */ +public class WeddingReservationWorker extends BaseWorker implements Runnable { + + @Override + public void run() { + for(Channel ch : wserv.getChannels()) { + Pair>> wedding; + + wedding = ch.getNextWeddingReservation(true); // start cathedral + if(wedding != null) { + ch.setOngoingWedding(true, wedding.getLeft(), wedding.getRight().getLeft(), wedding.getRight().getRight()); + } else { + ch.setOngoingWedding(true, null, null, null); + } + + wedding = ch.getNextWeddingReservation(false); // start chapel + if(wedding != null) { + ch.setOngoingWedding(false, wedding.getLeft(), wedding.getRight().getLeft(), wedding.getRight().getRight()); + } else { + ch.setOngoingWedding(false, null, null, null); + } + } + } + + public WeddingReservationWorker(World world) { + super(world); + } +} diff --git a/src/net/server/world/World.java b/src/net/server/world/World.java index 5b1e8c0e55..9654388fb1 100644 --- a/src/net/server/world/World.java +++ b/src/net/server/world/World.java @@ -31,7 +31,9 @@ import constants.ServerConstants; import java.sql.Connection; import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Statement; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -58,6 +60,7 @@ import server.maps.AbstractMapleMapObject; import net.server.worker.CharacterAutosaverWorker; import net.server.worker.MountTirednessWorker; import net.server.worker.PetFullnessWorker; +import net.server.worker.WeddingReservationWorker; import net.server.PlayerStorage; import net.server.Server; import net.server.channel.Channel; @@ -83,10 +86,14 @@ public class World { private Map messengers = new HashMap<>(); private AtomicInteger runningMessengerId = new AtomicInteger(); private Map families = new LinkedHashMap<>(); + private Map relationships = new HashMap<>(); + private Map> relationshipCouples = new HashMap<>(); private Map gsStore = new HashMap<>(); private PlayerStorage players = new PlayerStorage(); private Set queuedGuilds = new HashSet<>(); + private Map, Pair>> queuedMarriages = new HashMap<>(); + private Map> marriageGuests = new HashMap<>(); private Map parties = new HashMap<>(); private AtomicInteger runningPartyId = new AtomicInteger(); @@ -113,6 +120,7 @@ public class World { private long merchantUpdate; private ScheduledFuture charactersSchedule; + private ScheduledFuture marriagesSchedule; public World(int world, int flag, String eventmsg, int exprate, int droprate, int mesorate, int questrate) { this.id = world; @@ -128,9 +136,11 @@ public class World { petUpdate = System.currentTimeMillis(); mountUpdate = petUpdate; - petsSchedule = TimerManager.getInstance().register(new PetFullnessWorker(this), 60 * 1000, 60 * 1000); - mountsSchedule = TimerManager.getInstance().register(new MountTirednessWorker(this), 60 * 1000, 60 * 1000); - charactersSchedule = TimerManager.getInstance().register(new CharacterAutosaverWorker(this), 60 * 60 * 1000, 60 * 60 * 1000); + TimerManager tman = TimerManager.getInstance(); + petsSchedule = tman.register(new PetFullnessWorker(this), 60 * 1000, 60 * 1000); + mountsSchedule = tman.register(new MountTirednessWorker(this), 60 * 1000, 60 * 1000); + charactersSchedule = tman.register(new CharacterAutosaverWorker(this), 60 * 60 * 1000, 60 * 60 * 1000); + marriagesSchedule = tman.register(new WeddingReservationWorker(this), ServerConstants.WEDDING_RESERVATION_INTERVAL * 60 * 1000, ServerConstants.WEDDING_RESERVATION_INTERVAL * 60 * 1000); } public List getChannels() { @@ -371,6 +381,96 @@ public class World { queuedGuilds.remove(guildId); } + public boolean isMarriageQueued(int marriageId) { + return queuedMarriages.containsKey(marriageId); + } + + public Pair getMarriageQueuedLocation(int marriageId) { + Pair, Pair> qm = queuedMarriages.get(marriageId); + return (qm != null) ? qm.getLeft() : null; + } + + public Pair getMarriageQueuedCouple(int marriageId) { + Pair, Pair> qm = queuedMarriages.get(marriageId); + return (qm != null) ? qm.getRight() : null; + } + + public void putMarriageQueued(int marriageId, boolean cathedral, boolean premium, int groomId, int brideId) { + queuedMarriages.put(marriageId, new Pair<>(new Pair<>(cathedral, premium), new Pair<>(groomId, brideId))); + marriageGuests.put(marriageId, new HashSet()); + } + + public Pair> removeMarriageQueued(int marriageId) { + Boolean type = queuedMarriages.remove(marriageId).getLeft().getRight(); + Set guests = marriageGuests.remove(marriageId); + + return new Pair<>(type, guests); + } + + public synchronized boolean addMarriageGuest(int marriageId, int playerId) { + Set guests = marriageGuests.get(marriageId); + if(guests != null) { + if(guests.contains(playerId)) return false; + + guests.add(playerId); + return true; + } + + return false; + } + + public Pair getWeddingCoupleForGuest(int guestId, Boolean cathedral) { + for(Channel ch : channels) { + Pair p = ch.getWeddingCoupleForGuest(guestId, cathedral); + if(p != null) { + return p; + } + } + + List possibleWeddings = new LinkedList<>(); + for(Entry> mg : new HashSet<>(marriageGuests.entrySet())) { + if(mg.getValue().contains(guestId)) { + Pair loc = getMarriageQueuedLocation(mg.getKey()); + if(loc != null && cathedral.equals(loc.getLeft())) { + possibleWeddings.add(mg.getKey()); + } + } + } + + int pwSize = possibleWeddings.size(); + if(pwSize == 0) { + return null; + } else if(pwSize > 1) { + int selectedPw = -1; + int selectedPos = Integer.MAX_VALUE; + + for(Integer pw : possibleWeddings) { + for(Channel ch : channels) { + int pos = ch.getWeddingReservationStatus(pw, cathedral); + if(pos != -1) { + if(pos < selectedPos) { + selectedPos = pos; + selectedPw = pw; + break; + } + } + } + } + + if(selectedPw == -1) return null; + + possibleWeddings.clear(); + possibleWeddings.add(selectedPw); + } + + return getMarriageQueuedCouple(possibleWeddings.get(0)); + } + + public void debugMarriageStatus() { + System.out.println("Queued marriages: " + queuedMarriages); + System.out.println("Guest list: " + marriageGuests); + } + public MapleParty createParty(MaplePartyCharacter chrfor) { int partyid = runningPartyId.getAndIncrement(); MapleParty party = new MapleParty(partyid, chrfor); @@ -1028,33 +1128,160 @@ public class World { } public List> getAvailableItemBundles(int itemid) { - List> hmsAvailable = new ArrayList<>(); + List> hmsAvailable = new ArrayList<>(); - for (MapleHiredMerchant hm : getActiveMerchants()) { - List itemBundles = hm.sendAvailableBundles(itemid); - - for(MaplePlayerShopItem mpsi : itemBundles) { - hmsAvailable.add(new Pair<>(mpsi, (AbstractMapleMapObject) hm)); - } + for (MapleHiredMerchant hm : getActiveMerchants()) { + List itemBundles = hm.sendAvailableBundles(itemid); + + for(MaplePlayerShopItem mpsi : itemBundles) { + hmsAvailable.add(new Pair<>(mpsi, (AbstractMapleMapObject) hm)); + } + } + + for (MaplePlayerShop ps : getActivePlayerShops()) { + List itemBundles = ps.sendAvailableBundles(itemid); + + for(MaplePlayerShopItem mpsi : itemBundles) { + hmsAvailable.add(new Pair<>(mpsi, (AbstractMapleMapObject) ps)); + } + } + + Collections.sort(hmsAvailable, new Comparator>() { + @Override + public int compare(Pair p1, Pair p2) { + return p1.getLeft().getPrice() - p2.getLeft().getPrice(); + } + }); + + hmsAvailable.subList(0, Math.min(hmsAvailable.size(), 200)); //truncates the list to have up to 200 elements + return hmsAvailable; + } + + private void pushRelationshipCouple(Pair> couple) { + int mid = couple.getLeft(), hid = couple.getRight().getLeft(), wid = couple.getRight().getRight(); + relationshipCouples.put(mid, couple.getRight()); + relationships.put(hid, mid); + relationships.put(wid, mid); + } + + public Pair getRelationshipCouple(int relationshipId) { + Pair rc = relationshipCouples.get(relationshipId); + + if(rc == null) { + Pair> couple = getRelationshipCoupleFromDb(relationshipId, true); + if(couple == null) return null; + + pushRelationshipCouple(couple); + rc = couple.getRight(); + } + + return rc; + } + + public int getRelationshipId(int playerId) { + Integer ret = relationships.get(playerId); + + if(ret == null) { + Pair> couple = getRelationshipCoupleFromDb(playerId, false); + if(couple == null) return -1; + + pushRelationshipCouple(couple); + ret = couple.getLeft(); + } + + return ret; + } + + private static Pair> getRelationshipCoupleFromDb(int id, boolean usingMarriageId) { + try { + Connection con = DatabaseConnection.getConnection(); + Integer mid = null, hid = null, wid = null; + + PreparedStatement ps; + if(usingMarriageId) { + ps = con.prepareStatement("SELECT * FROM marriages WHERE marriageid = ?"); + ps.setInt(1, id); + } else { + ps = con.prepareStatement("SELECT * FROM marriages WHERE husbandid = ? OR wifeid = ?"); + ps.setInt(1, id); + ps.setInt(2, id); } - for (MaplePlayerShop ps : getActivePlayerShops()) { - List itemBundles = ps.sendAvailableBundles(itemid); - - for(MaplePlayerShopItem mpsi : itemBundles) { - hmsAvailable.add(new Pair<>(mpsi, (AbstractMapleMapObject) ps)); - } + ResultSet rs = ps.executeQuery(); + if(rs.next()) { + mid = rs.getInt("marriageid"); + hid = rs.getInt("husbandid"); + wid = rs.getInt("wifeid"); } + + rs.close(); + ps.close(); + con.close(); + + return (mid == null) ? null : new Pair<>(mid, new Pair<>(hid, wid)); + } catch (SQLException se) { + se.printStackTrace(); + return null; + } + } + + public int createRelationship(int groomId, int brideId) { + int ret = addRelationshipToDb(groomId, brideId); + + pushRelationshipCouple(new Pair<>(ret, new Pair<>(groomId, brideId))); + return ret; + } + + private static int addRelationshipToDb(int groomId, int brideId) { + try { + Connection con = DatabaseConnection.getConnection(); - Collections.sort(hmsAvailable, new Comparator>() { - @Override - public int compare(Pair p1, Pair p2) { - return p1.getLeft().getPrice() - p2.getLeft().getPrice(); - } - }); + PreparedStatement ps = con.prepareStatement("INSERT INTO marriages (husbandid, wifeid) VALUES (?, ?)", Statement.RETURN_GENERATED_KEYS); + ps.setInt(1, groomId); + ps.setInt(2, brideId); + ps.executeUpdate(); - hmsAvailable.subList(0, Math.min(hmsAvailable.size(), 200)); //truncates the list to have up to 200 elements - return hmsAvailable; + ResultSet rs = ps.getGeneratedKeys(); + rs.next(); + int ret = rs.getInt(1); + + rs.close(); + ps.close(); + con.close(); + return ret; + } catch (SQLException se) { + se.printStackTrace(); + return -1; + } + } + + public void deleteRelationship(int playerId, int partnerId) { + int relationshipId = relationships.get(playerId); + deleteRelationshipFromDb(relationshipId); + + relationshipCouples.remove(relationshipId); + relationships.remove(playerId); + relationships.remove(partnerId); + } + + private static void deleteRelationshipFromDb(int playerId) { + try { + Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("DELETE FROM marriages WHERE marriageid = ?"); + ps.setInt(1, playerId); + ps.executeUpdate(); + + ps.close(); + con.close(); + } catch (SQLException se) { + se.printStackTrace(); + } + } + + public void dropMessage(int type, String message) { + for (MapleCharacter player : getPlayerStorage().getAllCharacters()) { + player.dropMessage(type, message); + } } public final void shutdown() { diff --git a/src/server/MapleInventoryManipulator.java b/src/server/MapleInventoryManipulator.java index 5163945ee3..6b41ab2aa7 100644 --- a/src/server/MapleInventoryManipulator.java +++ b/src/server/MapleInventoryManipulator.java @@ -32,6 +32,7 @@ import client.inventory.ModifyInventory; import client.newyear.NewYearCardRecord; import constants.ItemConstants; import constants.ServerConstants; +import net.server.channel.handlers.RingActionHandler; import java.awt.Point; import java.util.ArrayList; @@ -580,10 +581,7 @@ public class MapleInventoryManipulator { NewYearCardRecord.removeAllNewYearCard(false, c.getPlayer()); c.getAbstractPlayerInteraction().removeAll(4301000); } - } - - boolean weddingRing = source.getItemId() == 1112803 || source.getItemId() == 1112806 || source.getItemId() == 1112807 || source.getItemId() == 1112809; - if (weddingRing) { + } else if (ItemConstants.isWeddingRing(source.getItemId())) { c.getPlayer().getMap().disappearingItemDrop(c.getPlayer(), c.getPlayer(), target, dropPos); } else if (c.getPlayer().getMap().getEverlast()) { if (ii.isDropRestricted(target.getItemId()) || ii.isCash(target.getItemId()) || isDroppedItemRestricted(target)) { diff --git a/src/server/maps/MapleMap.java b/src/server/maps/MapleMap.java index 701e540cbc..f4cd34bff4 100644 --- a/src/server/maps/MapleMap.java +++ b/src/server/maps/MapleMap.java @@ -1047,35 +1047,19 @@ public class MapleMap { } public void broadcastBalrogVictory(String leaderName) { - for (Channel cserv : Server.getInstance().getWorld(world).getChannels()) { - for (MapleCharacter player : cserv.getPlayerStorage().getAllCharacters()) { - player.dropMessage(6, "[VICTORY] " + leaderName + "'s party has successfully defeated the Balrog! Praise to them, they finished with " + countAlivePlayers() + " players alive."); - } - } + Server.getInstance().getWorld(world).dropMessage(6, "[VICTORY] " + leaderName + "'s party has successfully defeated the Balrog! Praise to them, they finished with " + countAlivePlayers() + " players alive."); } public void broadcastHorntailVictory() { - for (Channel cserv : Server.getInstance().getWorld(world).getChannels()) { - for (MapleCharacter player : cserv.getPlayerStorage().getAllCharacters()) { - player.dropMessage(6, "[VICTORY] To the crew that have finally conquered Horned Tail after numerous attempts, I salute thee! You are the true heroes of Leafre!!"); - } - } + Server.getInstance().getWorld(world).dropMessage(6, "[VICTORY] To the crew that have finally conquered Horned Tail after numerous attempts, I salute thee! You are the true heroes of Leafre!!"); } public void broadcastZakumVictory() { - for (Channel cserv : Server.getInstance().getWorld(world).getChannels()) { - for (MapleCharacter player : cserv.getPlayerStorage().getAllCharacters()) { - player.dropMessage(6, "[VICTORY] At last, the tree of evil that for so long overwhelmed Ossyria has fallen. To the crew that managed to finally conquer Zakum, after numerous attempts, victory! You are the true heroes of Ossyria!!"); - } - } + Server.getInstance().getWorld(world).dropMessage(6, "[VICTORY] At last, the tree of evil that for so long overwhelmed Ossyria has fallen. To the crew that managed to finally conquer Zakum, after numerous attempts, victory! You are the true heroes of Ossyria!!"); } public void broadcastPinkBeanVictory(int channel) { - for (Channel cserv : Server.getInstance().getWorld(world).getChannels()) { - for (MapleCharacter player : cserv.getPlayerStorage().getAllCharacters()) { - player.dropMessage(6, "[VICTORY] In a swift stroke of sorts, the crew that has attempted Pink Bean at channel " + channel + " has ultimately defeated it. The Temple of Time shines radiantly once again, the day finally coming back, as the crew that managed to finally conquer it returns victoriously from the battlefield!!"); - } - } + Server.getInstance().getWorld(world).dropMessage(6, "[VICTORY] In a swift stroke of sorts, the crew that has attempted Pink Bean at channel " + channel + " has ultimately defeated it. The Temple of Time shines radiantly once again, the day finally coming back, as the crew that managed to finally conquer it returns victoriously from the battlefield!!"); } public void killMonster(final MapleMonster monster, final MapleCharacter chr, final boolean withDrops) { diff --git a/src/tools/MaplePacketCreator.java b/src/tools/MaplePacketCreator.java index 397bf0dbc2..77c7a2aafa 100644 --- a/src/tools/MaplePacketCreator.java +++ b/src/tools/MaplePacketCreator.java @@ -189,7 +189,7 @@ public class MaplePacketCreator { mplew.writeInt(0); } - private static void addCharLook(final MaplePacketLittleEndianWriter mplew, MapleCharacter chr, boolean mega) { + protected static void addCharLook(final MaplePacketLittleEndianWriter mplew, MapleCharacter chr, boolean mega) { mplew.write(chr.getGender()); mplew.write(chr.getSkinColor().getId()); // skin color mplew.writeInt(chr.getFace()); // face @@ -346,15 +346,15 @@ public class MaplePacketCreator { } } - private static void addItemInfo(final MaplePacketLittleEndianWriter mplew, Item item) { - addItemInfo(mplew, item, false); - } - private static void addExpirationTime(final MaplePacketLittleEndianWriter mplew, long time) { mplew.writeLong(getTime(time)); } - private static void addItemInfo(final MaplePacketLittleEndianWriter mplew, Item item, boolean zeroPosition) { + private static void addItemInfo(final MaplePacketLittleEndianWriter mplew, Item item) { + addItemInfo(mplew, item, false); + } + + protected static void addItemInfo(final MaplePacketLittleEndianWriter mplew, Item item, boolean zeroPosition) { MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance(); boolean isCash = ii.isCash(item.getItemId()); boolean isPet = item.getPetId() > -1; @@ -4367,13 +4367,27 @@ public class MaplePacketCreator { return mplew.getPacket(); } - public static byte[] sendSpouseChat(MapleCharacter wife, String msg) { + /* + public static byte[] sendSpouseChat(MapleCharacter partner, String msg) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.SPOUSE_CHAT.getValue()); - mplew.writeMapleAsciiString(wife.getName()); + mplew.writeMapleAsciiString(partner.getName()); mplew.writeMapleAsciiString(msg); return mplew.getPacket(); } + */ + + public static byte[] OnCoupleMessage(String fiance, String text, boolean spouse) { + MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.SPOUSE_CHAT.getValue()); + mplew.write(spouse ? 5 : 4); // v2 = CInPacket::Decode1(a1) - 4; + if (spouse) { // if ( v2 ) { + mplew.writeMapleAsciiString(fiance); + } + mplew.write(spouse ? 5 : 1); + mplew.writeMapleAsciiString(text); + return mplew.getPacket(); + } public static byte[] addMessengerPlayer(String from, MapleCharacter chr, int position, int channel) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); @@ -6430,49 +6444,6 @@ public class MaplePacketCreator { return mplew.getPacket(); } - public static byte[] sendEngagementRequest(String name) { - final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); - mplew.writeShort(SendOpcode.MARRIAGE_REQUEST.getValue()); // has requested engagement. Will you accept this proposal? - mplew.write(0); - mplew.writeMapleAsciiString(name); // name - mplew.writeInt(10); // playerid - return mplew.getPacket(); - } - - public static byte[] sendGroomWishlist() { - final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); - mplew.writeShort(SendOpcode.MARRIAGE_REQUEST.getValue()); // has requested engagement. Will you accept this proposal? - mplew.write(9); - return mplew.getPacket(); - } - - public static byte[] sendBrideWishList(List items) { - final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); - mplew.writeShort(SendOpcode.WEDDING_GIFT_RESULT.getValue()); - mplew.write(0x0A); - mplew.writeLong(-1); // ? - mplew.writeInt(0); // ? - mplew.write(items.size()); - for (Item item : items) { - addItemInfo(mplew, item, true); - } - return mplew.getPacket(); - } - - public static byte[] addItemToWeddingRegistry(MapleCharacter chr, Item item) { - final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); - mplew.writeShort(SendOpcode.WEDDING_GIFT_RESULT.getValue()); - mplew.write(0x0B); - mplew.writeInt(0); - for (int i = 0; i < 0; i++) // f4 - { - mplew.write(0); - } - - addItemInfo(mplew, item, true); - return mplew.getPacket(); - } - public static byte[] sendFamilyJoinResponse(boolean accepted, String added) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.FAMILY_JOIN_REQUEST_RESULT.getValue()); @@ -6660,7 +6631,7 @@ public class MaplePacketCreator { public static byte[] marriageMessage(int type, String charname) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.NOTIFY_MARRIAGE.getValue()); - mplew.write(type); + mplew.write(type); // 0: guild, 1: family mplew.writeMapleAsciiString("> " + charname); //To fix the stupid packet lol return mplew.getPacket(); @@ -6780,17 +6751,26 @@ public class MaplePacketCreator { mplew.writeInt(0); mplew.writeInt(ring.getItemId()); } - mplew.writeShort(chr.getMarriageRing() != null ? 1 : 0); - int marriageId = 30000; - if (chr.getMarriageRing() != null) { - mplew.writeInt(marriageId); - mplew.writeInt(chr.getId()); - mplew.writeInt(chr.getMarriageRing().getPartnerChrId()); - mplew.writeShort(3); - mplew.writeInt(chr.getMarriageRing().getRingId()); - mplew.writeInt(chr.getMarriageRing().getPartnerRingId()); - mplew.writeAsciiString(StringUtil.getRightPaddedStr(chr.getGender() == 0 ? chr.getName() : chr.getMarriageRing().getPartnerName(), '\0', 13)); - mplew.writeAsciiString(StringUtil.getRightPaddedStr(chr.getGender() == 0 ? chr.getMarriageRing().getPartnerName() : chr.getName(), '\0', 13)); + + if(chr.getPartnerId() > 0) { + MapleRing marriageRing = chr.getMarriageRing(); + + mplew.writeShort(1); + mplew.writeInt(chr.getRelationshipId()); + mplew.writeInt(chr.getGender() == 0 ? chr.getId() : chr.getPartnerId()); + mplew.writeInt(chr.getGender() == 0 ? chr.getPartnerId() : chr.getId()); + mplew.writeShort((marriageRing != null) ? 3 : 1); + if (marriageRing != null) { + mplew.writeInt(marriageRing.getItemId()); + mplew.writeInt(marriageRing.getItemId()); + } else { + mplew.writeInt(1112803); // Engagement Ring's Outcome (doesn't matter for engagement) + mplew.writeInt(1112803); // Engagement Ring's Outcome (doesn't matter for engagement) + } + mplew.writeAsciiString(StringUtil.getRightPaddedStr(chr.getGender() == 0 ? chr.getName() : MapleCharacter.getNameById(chr.getPartnerId()), '\0', 13)); + mplew.writeAsciiString(StringUtil.getRightPaddedStr(chr.getGender() == 0 ? MapleCharacter.getNameById(chr.getPartnerId()) : chr.getName(), '\0', 13)); + } else { + mplew.writeShort(0); } } diff --git a/src/tools/packets/Wedding.java b/src/tools/packets/Wedding.java new file mode 100644 index 0000000000..97ef9bb347 --- /dev/null +++ b/src/tools/packets/Wedding.java @@ -0,0 +1,452 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package tools.packets; + +import client.inventory.Item; +import client.inventory.MapleInventoryType; +import client.MapleCharacter; +import java.util.ArrayList; +import java.util.List; +import tools.MaplePacketCreator; +import tools.StringUtil; +import tools.data.output.MaplePacketLittleEndianWriter; + +/** + * CField_Wedding, CField_WeddingPhoto, CWeddingMan, OnMarriageResult, and all Wedding/Marriage enum/structs. + * + * @author Eric + */ +public class Wedding extends MaplePacketCreator { + private static final short MARRIAGE_REQUEST = 0x48; + private static final short MARRIAGE_RESULT = 0x49; + private static final short WEDDING_GIFT_RESULT = 0x4A; + private static final short NOTIFY_MARRIED_PARTNER_MAP_TRANSFER = 0x4B; + private static final short WEDDING_PHOTO = 0x2B; + private static final short WEDDING_PROGRESS = 0x140; + private static final short WEDDING_CEREMONY_END = 0x141; + + /* + 00000000 CWeddingMan struc ; (sizeof=0x104) + 00000000 vfptr dd ? ; offset + 00000004 ___u1 $01CBC6800BD386B8A8FD818EAD990BEC ? + 0000000C m_mCharIDToMarriageNo ZMap ? + 00000024 m_mReservationPending ZMap,unsigned long> ? + 0000003C m_mReservationPendingGroom ZMap,unsigned long> ? + 00000054 m_mReservationPendingBride ZMap,unsigned long> ? + 0000006C m_mReservationStartUser ZMap ? + 00000084 m_mReservationCompleted ZMap,unsigned long> ? + 0000009C m_mGroomWishList ZMap > >,unsigned long> ? + 000000B4 m_mBrideWishList ZMap > >,unsigned long> ? + 000000CC m_mEngagementPending ZMap,unsigned long> ? + 000000E4 m_nCurrentWeddingState dd ? + 000000E8 m_dwCurrentWeddingNo dd ? + 000000EC m_dwCurrentWeddingMap dd ? + 000000F0 m_bIsReservationLoaded dd ? + 000000F4 m_dwNumGuestBless dd ? + 000000F8 m_bPhotoSuccess dd ? + 000000FC m_tLastUpdate dd ? + 00000100 m_bStartWeddingCeremony dd ? + 00000104 CWeddingMan ends + */ + + public class Field_Wedding { + public int m_nNoticeCount; + public int m_nCurrentStep; + public int m_nBlessStartTime; + } + + public class Field_WeddingPhoto { + public boolean m_bPictureTook; + } + + public class GW_WeddingReservation { + public int dwReservationNo; + public int dwGroom, dwBride; + public String sGroomName, sBrideName; + public int usWeddingType; + } + + public class WeddingWishList { + public MapleCharacter pUser; + public int dwMarriageNo; + public int nGender; + public int nWLType; + public int nSlotCount; + public List asWishList = new ArrayList<>(); + public int usModifiedFlag; // dword + public boolean bLoaded; + } + + public class GW_WeddingWishList { + public final int WEDDINGWL_MAX = 0xA; // enum WEDDINGWL + public int dwReservationNo; + public byte nGender; + public String sItemName; + } + + public enum MarriageStatus { + SINGLE(0x0), + ENGAGED(0x1), + RESERVED(0x2), + MARRIED(0x3); + private int ms; + private MarriageStatus(int ms) { + this.ms = ms; + } + + public int getMarriageStatus() { + return ms; + } + } + + public enum MarriageRequest { + AddMarriageRecord(0x0), + SetMarriageRecord(0x1), + DeleteMarriageRecord(0x2), + LoadReservation(0x3), + AddReservation(0x4), + DeleteReservation(0x5), + GetReservation(0x6); + private int req; + private MarriageRequest(int req) { + this.req = req; + } + + public int getMarriageRequest() { + return req; + } + } + + public enum WeddingType { + CATHEDRAL(0x1), + VEGAS(0x2), + CATHEDRAL_PREMIUM(0xA), + CATHEDRAL_NORMAL(0xB), + VEGAS_PREMIUM(0x14), + VEGAS_NORMAL(0x15); + private int wt; + private WeddingType(int wt) { + this.wt = wt; + } + + public int getType() { + return wt; + } + } + + public enum WeddingMap { + WEDDINGTOWN(680000000), + CHAPEL_STARTMAP(680000110), + CATHEDRAL_STARTMAP(680000210), + PHOTOMAP(680000300), + EXITMAP(680000500); + private int wm; + private WeddingMap(int wm) { + this.wm = wm; + } + + public int getMap() { + return wm; + } + } + + public enum WeddingItem { + WR_MOONSTONE(1112803), // Wedding Ring + WR_STARGEM(1112806), + WR_GOLDENHEART(1112807), + WR_SILVERSWAN(1112809), + ERB_MOONSTONE(2240000), // Engagement Ring Box + ERB_STARGEM(2240001), + ERB_GOLDENHEART(2240002), + ERB_SILVERSWAN(2240003), + ERBE_MOONSTONE(4031357), // Engagement Ring Box (Empty) + ER_MOONSTONE(4031358), // Engagement Ring + ERBE_STARGEM(4031359), + ER_STARGEM(4031360), + ERBE_GOLDENHEART(4031361), + ER_GOLDENHEART(4031362), + ERBE_SILVERSWAN(4031363), + ER_SILVERSWAN(4031364), + PARENTS_BLESSING(4031373), // Parents Blessing + OFFICIATORS_PERMISSION(4031374), // Officiator's Permission + WR_CATHEDRAL_PREMIUM(4031375), // Wedding Ring? + WR_VEGAS_PREMIUM(4031376), + IB_VEGAS(4031377), // toSend invitation + IB_CATHEDRAL(4031395), // toSend invitation + IG_VEGAS(4031406), // rcvd invitation + IG_CATHEDRAL(4031407), // rcvd invitation + OB_FORCOUPLE(4031424), // Onyx Box? For Couple + WR_CATHEDRAL_NORMAL(4031480), // Wedding Ring? + WR_VEGAS_NORMAL(4031481), + WT_CATHEDRAL_NORMAL(5251000), // Wedding Ticket + WT_VEGAS_NORMAL(5251001), + WT_VEGAS_PREMIUM(5251002), + WT_CATHEDRAL_PREMIUM(5251003); + private int wi; + private WeddingItem(int wi) { + this.wi = wi; + } + + public int getItem() { + return wi; + } + } + + /** + * has requested engagement. Will you accept this proposal? + * + * @param name + * @param playerid + * @return mplew + */ + public static byte[] OnMarriageRequest(String name, int playerid) { + MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(MARRIAGE_REQUEST); + mplew.write(0); //mode, 0 = engage, 1 = cancel, 2 = answer.. etc + mplew.writeMapleAsciiString(name); // name + mplew.writeInt(playerid); // playerid + return mplew.getPacket(); + } + + /** + * A quick rundown of how (I think based off of enough BMS searching) WeddingPhoto_OnTakePhoto works: + * - We send this packet with (first) the Groom / Bride IGNs + * - We then send a fieldId (unsure about this part at the moment, 90% sure it's the id of the map) + * - After this, we write an integer of the amount of characters within the current map (which is the Cake Map -- exclude users within Exit Map) + * - Once we've retrieved the size of the characters, we begin to write information about them (Encode their name, guild, etc info) + * - Now that we've Encoded our character data, we begin to Encode the ScreenShotPacket which requires a TemplateID, IGN, and their positioning + * - Finally, after encoding all of our data, we send this packet out to a MapGen application server + * - The MapGen server will then retrieve the packet byte array and convert the bytes into a ImageIO 2D JPG output + * - The result after converting into a JPG will then be remotely uploaded to /weddings/ with ReservedGroomName_ReservedBrideName to be displayed on the web server. + * + * - Will no longer continue Wedding Photos, needs a WvsMapGen :( + * + * @param ReservedGroomName The groom IGN of the wedding + * @param ReservedBrideName The bride IGN of the wedding + * @param m_dwField The current field id (the id of the cake map, ex. 680000300) + * @param m_uCount The current user count (equal to m_dwUsers.size) + * @param m_dwUsers The List of all MapleCharacter guests within the current cake map to be encoded + * @return mplew (MaplePacket) Byte array to be converted and read for byte[]->ImageIO + */ + public static byte[] OnTakePhoto(String ReservedGroomName, String ReservedBrideName, int m_dwField, List m_dwUsers) { // OnIFailedAtWeddingPhotos + MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(WEDDING_PHOTO); // v53 header, convert -> v83 + mplew.writeMapleAsciiString(ReservedGroomName); + mplew.writeMapleAsciiString(ReservedBrideName); + mplew.writeInt(m_dwField); // field id? + mplew.writeInt(m_dwUsers.size()); + + for (MapleCharacter guest : m_dwUsers) { + // Begin Avatar Encoding + addCharLook(mplew, guest, false); // CUser::EncodeAvatar + mplew.writeInt(30000); // v20 = *(_DWORD *)(v13 + 2192) -- new groom marriage ID?? + mplew.writeInt(30000); // v20 = *(_DWORD *)(v13 + 2192) -- new bride marriage ID?? + mplew.writeMapleAsciiString(guest.getName()); + mplew.writeMapleAsciiString(guest.getGuildId() > 0 && guest.getGuild() != null ? guest.getGuild().getName() : ""); + mplew.writeShort(guest.getGuildId() > 0 && guest.getGuild() != null ? guest.getGuild().getLogoBG() : 0); + mplew.write(guest.getGuildId() > 0 && guest.getGuild() != null ? guest.getGuild().getLogoBGColor() : 0); + mplew.writeShort(guest.getGuildId() > 0 && guest.getGuild() != null ? guest.getGuild().getLogo() : 0); + mplew.write(guest.getGuildId() > 0 && guest.getGuild() != null ? guest.getGuild().getLogoColor() : 0); + mplew.writeShort(guest.getPosition().x); // v18 = *(_DWORD *)(v13 + 3204); + mplew.writeShort(guest.getPosition().y); // v20 = *(_DWORD *)(v13 + 3208); + // Begin Screenshot Encoding + mplew.write(1); // // if ( *(_DWORD *)(v13 + 288) ) { COutPacket::Encode1(&thisa, v20); + // CPet::EncodeScreenShotPacket(*(CPet **)(v13 + 288), &thisa); + mplew.writeInt(1); // dwTemplateID + mplew.writeMapleAsciiString(guest.getName()); // m_sName + mplew.writeShort(guest.getPosition().x); // m_ptCurPos.x + mplew.writeShort(guest.getPosition().y); // m_ptCurPos.y + mplew.write(guest.getStance()); // guest.m_bMoveAction + } + + return mplew.getPacket(); + } + + /** + * Enable spouse chat and their engagement ring without @relog + * + * @param marriageId + * @param chr + * @param wedding + * @return mplew + */ + public static byte[] OnMarriageResult(int marriageId, MapleCharacter chr, boolean wedding) { + MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(MARRIAGE_RESULT); + mplew.write(11); + mplew.writeInt(marriageId); + mplew.writeInt(chr.getGender() == 0 ? chr.getId() : chr.getPartnerId()); + mplew.writeInt(chr.getGender() == 0 ? chr.getPartnerId() : chr.getId()); + mplew.writeShort(wedding ? 3 : 1); + if (wedding) { + mplew.writeInt(chr.getMarriageItemId()); + mplew.writeInt(chr.getMarriageItemId()); + } else { + mplew.writeInt(1112803); // Engagement Ring's Outcome (doesn't matter for engagement) + mplew.writeInt(1112803); // Engagement Ring's Outcome (doesn't matter for engagement) + } + mplew.writeAsciiString(StringUtil.getRightPaddedStr(chr.getGender() == 0 ? chr.getName() : MapleCharacter.getNameById(chr.getPartnerId()), '\0', 13)); + mplew.writeAsciiString(StringUtil.getRightPaddedStr(chr.getGender() == 0 ? MapleCharacter.getNameById(chr.getPartnerId()) : chr.getName(), '\0', 13)); + + return mplew.getPacket(); + } + + /** + * To exit the Engagement Window (Waiting for her response...), we send a GMS-like pop-up. + * + * @param msg + * @return mplew + */ + public static byte[] OnMarriageResult(final byte msg) { + MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(MARRIAGE_RESULT); + mplew.write(msg); + if (msg == 36) { + mplew.write(1); + mplew.writeMapleAsciiString("You are now engaged."); + } + return mplew.getPacket(); + } + + /** + * The World Map includes 'loverPos' in which this packet controls + * + * @param partner + * @param mapid + * @return mplew + */ + public static byte[] OnNotifyWeddingPartnerTransfer(int partner, int mapid) { + MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(NOTIFY_MARRIED_PARTNER_MAP_TRANSFER); + mplew.writeInt(mapid); + mplew.writeInt(partner); + + return mplew.getPacket(); + } + + /** + * The wedding packet to display Pelvis Bebop and enable the Wedding Ceremony Effect between two characters + * CField_Wedding::OnWeddingProgress - Stages + * CField_Wedding::OnWeddingCeremonyEnd - Wedding Ceremony Effect + * + * @param SetBlessEffect + * @param groom + * @param bride + * @param step + * @return mplew + */ + public static byte[] OnWeddingProgress(boolean SetBlessEffect, int groom, int bride, byte step) { + MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SetBlessEffect ? WEDDING_CEREMONY_END : WEDDING_PROGRESS); + if (!SetBlessEffect) { // in order for ceremony packet to send, byte step = 2 must be sent first + mplew.write(step); + } + mplew.writeInt(groom); + mplew.writeInt(bride); + return mplew.getPacket(); + } + + /** + * When we open a Wedding Invitation, we display the Bride & Groom + * + * @param groom + * @param bride + * @return mplew + */ + public static byte[] sendWeddingInvitation(String groom, String bride) { + MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(MARRIAGE_RESULT); + mplew.write(15); + mplew.writeMapleAsciiString(groom); + mplew.writeMapleAsciiString(bride); + mplew.writeShort(1); // 0 = Cathedral Normal?, 1 = Cathedral Premium?, 2 = Chapel Normal? + return mplew.getPacket(); + } + + public static byte[] sendWishList() { // fuck my life + MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(MARRIAGE_REQUEST); + mplew.write(9); + return mplew.getPacket(); + } + + /** + * Handles all of WeddingWishlist packets + * + * @param mode + * @param itemnames + * @param items + * @return mplew + */ + public static byte[] OnWeddingGiftResult(byte mode, List itemnames, List items) { + // if (itemnames == null || itemnames.size() < 1) { // for now lol + // itemnames = new ArrayList<>(); + // itemnames.add("mesos"); + // itemnames.add("rare items"); + // itemnames.add("more mesos"); + // } + MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(WEDDING_GIFT_RESULT); + mplew.write(mode); + switch(mode) { + case 0x09: { // Load Wedding Registry + mplew.write(itemnames.size()); + for (String names : itemnames) { + mplew.writeMapleAsciiString(names); + } + mplew.write(itemnames.size()); + for (String names : itemnames) { + mplew.writeMapleAsciiString(names); + } + // need to load items somehow + break; + } + case 0xA: // Load Bride's Wishlist + case 0xF: // 10, 15, 16 = CWishListRecvDlg::OnPacket + case 0xB: { // Add Item to Wedding Registry + // 11 : You have sent a gift | 12 : You cannot give more than one present for each wishlist | 13 : Failed to send the gift. | 14 : Failed to send the gift. + if (mode == 0xB) { + mplew.write(itemnames.size()); + for (String names : itemnames) { + mplew.writeMapleAsciiString(names); + } + } + switch (items.get((items.size() - 1)).getInventoryType()) { + case EQUIP: + mplew.writeLong(4); + break; + case USE: + mplew.writeLong(8); + break; + case SETUP: + mplew.writeLong(16); + break; + case ETC: + mplew.writeLong(32); + break; + default: // impossible flag, cash item can't be sent + if (items.get((items.size() - 1)).getInventoryType() != MapleInventoryType.CASH) { + mplew.writeLong(0); + } + } + if (mode == 0xA) { // random unknown bytes involved within Bride's Wishlist + mplew.writeInt(0); + } + mplew.write(items.size()); + for (Item item : items) { + MaplePacketCreator.addItemInfo(mplew, item, true); + } + break; + } + default: { + System.out.println("Unknown Wishlist Mode: " + mode); + break; + } + } + return mplew.getPacket(); + } +} \ No newline at end of file