Enhanced map bounds + HolidayPQ + Battleship

Fixed Abdula not showing book info for Arans.
Fixed map bounds, now it hopefully doesn't let items pass through the walkable area anymore.
Implemented HolidayPQ.
Added a custom item sandbox system, to be used with items generated by the "drop" command.
Added the whole-map buff when using the Happy Birthday item.
Fixed several battleship issues:
 - Battleship now shows HP properly at the buff icon.
 - Cleared some inconsistencies on battleship when interacting with the enhanced buff system.
 - Battleship now hands out defensive buffs properly.
This commit is contained in:
ronancpl
2018-06-13 00:03:46 -03:00
parent 0b8d3a0b2b
commit dddfdcf315
46 changed files with 5494 additions and 3796 deletions

View File

@@ -9,7 +9,7 @@ Regarding distributability and usage of the code presented here: like it was bef
This is a NetBeans 8.0.2 Project, that MUST be built and run under JDK/JRE 7 in order to run properly. This means that it's easier to install the project via opening the server project folder inside NetBeans' IDE. Once installed, build this project on your machine and run the server using the "launch.bat" application.
In this project, many gameplay-wise issues generated from either the original WZ files and the server source have been partially or completely solved. Considering the use of the provided edited WZ's and server-side wz.xml files should be of the greatest importance when dealing with this instance of private server, in order to perceive it at it's full potential. My opinion, though! Refer to "README_wzchanges.txt" for more information on what has been changed from Nexon's v83 WZ files.
In this project, many gameplay-wise issues generated from either the original WZ files and the server source have been partially or completely solved. Considering the use of the provided edited WZ's and server-side wz.xml files should be of the greatest importance when dealing with this instance of server source, in order to perceive it at it's full potential. My opinion, though! Refer to "README_wzchanges.txt" for more information on what has been changed from Nexon's v83 WZ files.
The main objective of this project is to try as best as possible to recreate what once was the original MapleStory v83, while adding up some flavors that spices up the gameplay. In other words, aim to get the best of the MapleStory of that era.
@@ -71,7 +71,7 @@ Paypal: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3K8
* HeavenMS staff has __no current intention__ to publicly open a server with this source, if that ever comes to happen this note will be lifted. __Don't be scammed!__
* This server source is __NOT intended to be stable__ as is. Proper deadlock review and other maintenance checks are needed in order to make it stable for production use.
* This server source is __NOT intended to be stable__ as is. Proper deadlock review and other maintenance checks are needed in order to make it suitable for production use.
---
### Preparing the ambient

View File

@@ -22,7 +22,7 @@ Feature list:
PQs:
* HPQ/KPQ/LPQ/LMPQ/OPQ/EllinPQ/PiratePQ/MagatiaPQ/HorntailPQ/AmoriaPQ/TreasurePQ/ElnathPQ.
* HPQ/KPQ/LPQ/LMPQ/OPQ/EllinPQ/PiratePQ/MagatiaPQ/HorntailPQ/AmoriaPQ/TreasurePQ/ElnathPQ/HolidayPQ.
* CWKPQ as Expedition-based event.
* Expeditions: Scarga/Horntail/Showa/Balrog/Zakum/Pinkbean.
* GuildPQ + Guild queue with multi-lobby systems available.
@@ -34,8 +34,10 @@ Skills:
* Some skills behaving oddly have been patched, such as Steal, Venomous Star/Stab and Mystic Doors.
* Maker skill features properly developed.
* Improved current Battleship skill, now showing the HP properly on buff tab and making visible for others after changing maps.
* Server is using heuristics to calculate fee costs for the Maker (errors sums up to 8k mesos, reagent errors stacks up comformant with it's level).
* New skill: Chair Mastery (max lv 1) - Players having this passive skill can gain a significant boost of HP/MP recovery when sitting on a field/map chair.
* Mu Lung Dojo skills functional.
Quests:
@@ -71,6 +73,7 @@ Cash & Items:
* New scroll: antibanish. For use only in cases where bosses send a player back to town.
* Inventory system properly checks for item slot free space and ownership.
* Storage with "Arrange Items" feature functional.
* Close-quarters evaluation mode for items (sandbox).
* Spikes on shoes.
* Vega's spell.
* Owl of Minerva.
@@ -87,6 +90,7 @@ Monsters, Maps & Reactors:
* Monsterbook displays drop data info conformant with the underlying DB (needs custom wz). See more on the MobBookUpdate feature.
* Every skill/mastery book is now droppable by mobs.
* Mobs now can drop more than one of the same equipment (number of possible drops defined at droptime, uses the minimum/maximum quantity fields on DB).
* Redesigned HT mechanics for spawn and linked damage to the sponge.
* Improved map bounding checks for item drop points, assuring most of the items dropped will be available to pickup inside the accessible map area.
* Limited item count on maps, smartly expiring oldest registered items, preventing potential item flooding.
* Implemented Zombify disease status.
@@ -95,6 +99,7 @@ Monsters, Maps & Reactors:
* Boats, elevator and other travelling mechanics fully working.
* HP decreasing overtime on maps and mechanics to prevent them (consumables, equips) fully functional.
* Crimson Balrog boat approaching visual effect made functional.
* Maps having everlasting items no longer expires them.
* PQs, Taxis and other event-driven situations warps players at random spawnpoints, GMS-like.
* Some reactors (PQ bonus boxes) spraying items on the map, instead of dropping everything at once.
* Reactors pick items up smartly, checking for an option to pick up on many-items-nearby scenario.
@@ -116,6 +121,7 @@ Player potentials:
* Player level rates.
* Gain fame by quests.
* Pet evolutions functional (not GMS-like).
* Reviewed keybinding system.
Server potentials:
@@ -141,6 +147,14 @@ Server potentials:
* Accounts can be created automatically when trying to login on an inexistent account -- credits to shavit.
* Usage of Bcrypt (up-to-date) as the main password hashing algorithm, replacing old SHA's -- credits to shavit.
Custom NPCs:
* Spiegelmann: automatized rock-refiner.
* Abdula: lists droppers of needed skill/mastery books.
* Agent E: accessory crafter.
* Donation Box: automatized item-buyer.
* Coco & Ace of Hearts: C. scroll crafters.
Admin/GM commands:
* Server commands layered by GM levels.
@@ -175,6 +189,7 @@ Project:
* Fixed/added some missing packets for MoveEnvironment, summons and others.
* Uncovered many Send/Recv opcodes throughout the source.
* Reviewed many Java object aspects that needed concurrency protection.
* Reviewed SQL data, eliminating duplicated entries on the tables.
* Usage of HikariCP to improve the DB connection management.
* Protected many flaws with login management system.
* Heavily reviewed future task management inside the project. Way less trivial schedules are spawned now, relieving task overload on the TimerManager.
@@ -199,11 +214,4 @@ Localhost:
* Removed the AP assign block for novices.
* Removed a block that would show up when trying to apply an attack gem on equipments that aren't weapons.
Custom NPCs:
* Agent E: Accessory crafter.
* Donation Box: Instant-sell NPC.
* Ace of Hearts & Coco: C. scroll crafter.
* Spiegelmann: Instant-ore refiner NPC.
---------------------------

View File

@@ -11,6 +11,7 @@ Known issues:
- Some criticals (e.g. from Aran skills) will not show up as crit for other players.
- Deadlocks may start appearing if the server stays online long enough with many players logged in.
- If there are multiple bosses that shows HPBar on the map, if a player hits more than one the HPBar may start flickering on the screen.
- Sometimes battleship may behave oddly with the enhanced buff system, making the character d/c in certain scenarios.
---------------------------
---------------------------

View File

@@ -2,6 +2,8 @@
Spiegelmann -> 2042000
Coco -> 9000017
Agent E -> 9000036
Donation Box -> 9000041
Abdula -> 9209000
CUSTOM NPC SHOPS:
Spindle -> 9201082
@@ -1025,4 +1027,16 @@ Adicionado comportamento de substituição de itens ao expirar, cortesia do Gabr
Corrigido ponteiro nulo ao tentar usar return scroll em mapas como Mu Lung.
Corrigido um crítico problema de deadlock com o MaplePacketEncoder.
Corrigido um crítico problema de vazamento de dados na DB referente ao quest status.
4th job Aran agora usa script de evento, evitando múltiplos jogadores lutando contra múltiplas Mahas.
4th job Aran agora usa script de evento, evitando múltiplos jogadores lutando contra múltiplas Mahas.
08 - 10 Junho 2018,
Corrigido Abdula não mostrando skill/mastery books para Aran.
Corrigido fronteiras de mapa não mais deixando passar itens para fora do campo.
Corrigido battleship não mostrando HP corretamente.
Corrigido battleship não atuando corretamente com o sistema de buffs, deixando jogadores sem poder ativar a skill em certos casos.
Corrigido battleship não dando buffs na defesa corretamente.
11 - 12 Junho 2018,
Adicionado efeito do usável Happy Birthday pra todos no mapa.
Implementado HolidayPQ.
Adicionado sistema de sandbox para itens.

View File

@@ -0,0 +1,356 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 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 <http://www.gnu.org/licenses/>.
*/
/**
* @author: Ronan
* @event: Holiday PQ
*/
// GMS-like event string data thanks to iHealForLove
importPackage(Packages.client.inventory);
importPackage(Packages.server.life);
var isPq = true;
var minPlayers = 3, maxPlayers = 6;
var minLevel = 21, maxLevel = 30;
var entryMap = 889100001;
var exitMap = 889100002;
var recruitMap = 889100000;
var clearMap = 889100002;
var minMapId = 889100001;
var maxMapId = 889100001;
var eventTime = 15; // 15 minutes
var lobbyRange = [0, 0];
function init() {
setEventRequirements();
}
function setLobbyRange() {
return lobbyRange;
}
function setEventRequirements() {
var reqStr = "";
reqStr += "\r\n Number of players: ";
if(maxPlayers - minPlayers >= 1) reqStr += minPlayers + " ~ " + maxPlayers;
else reqStr += minPlayers;
reqStr += "\r\n Level range: ";
if(maxLevel - minLevel >= 1) reqStr += minLevel + " ~ " + maxLevel;
else reqStr += minLevel;
reqStr += "\r\n Time limit: ";
reqStr += eventTime + " minutes";
em.setProperty("party", reqStr);
}
function setEventExclusives(eim) {
var itemSet = [4032094, 4032095];
eim.setExclusiveItems(itemSet);
}
function setEventRewards(eim) {
var itemSet, itemQty, evLevel, expStages;
evLevel = 3; //Rewards at Hard difficulty
itemSet = [1302080, 1002033, 2022153, 2022042, 2020006, 2020009, 2020016, 2020024, 4010006, 4010007, 4020004, 4020005, 4003002];
itemQty = [1, 1, 1, 5, 20, 15, 10, 10, 2, 4, 4, 4, 1];
eim.setEventRewards(evLevel, itemSet, itemQty);
evLevel = 2; //Rewards at Normal difficulty
itemSet = [1302080, 1002033, 2012005, 2012006, 2020002, 2020025, 2020026, 4010003, 4010004, 4010005, 4020002, 4020003, 4020007];
itemQty = [1, 1, 15, 15, 15, 10, 10, 3, 3, 3, 3, 3, 3];
eim.setEventRewards(evLevel, itemSet, itemQty);
evLevel = 1; //Rewards at Easy difficulty
itemSet = [1002033, 2012005, 2012006, 2020002, 2022006, 2022002, 4010000, 4010001, 4010002, 4020000, 4020001, 4020006];
itemQty = [1, 15, 15, 10, 5, 5, 2, 2, 2, 2, 2, 2];
eim.setEventRewards(evLevel, itemSet, itemQty);
expStages = [210, 620, 500, 1400, 950, 2200]; //bonus exp given on CLEAR stage signal
eim.setEventClearStageExp(expStages);
}
function getEligibleParty(party) { //selects, from the given party, the team that is allowed to attempt this event
var eligible = [];
var hasLeader = false;
if(party.size() > 0) {
var partyList = party.toArray();
for(var i = 0; i < party.size(); i++) {
var ch = partyList[i];
if(ch.getMapId() == recruitMap && ch.getLevel() >= minLevel && ch.getLevel() <= maxLevel) {
if(ch.isLeader()) hasLeader = true;
eligible.push(ch);
}
}
}
if(!(hasLeader && eligible.length >= minPlayers && eligible.length <= maxPlayers)) eligible = [];
return eligible;
}
function setup(level, lobbyid) {
var eim = em.newInstance("Holiday1_" + lobbyid);
eim.setProperty("level", level);
eim.setProperty("stage", "0");
eim.setProperty("statusStg1", "-1");
eim.setProperty("missingDrops", "0");
eim.setProperty("snowmanLevel", "0");
eim.setProperty("snowmanStep", "0");
eim.setProperty("spawnedBoss", "0");
var mapobj = eim.getInstanceMap(entryMap);
mapobj.resetPQ(level);
mapobj.allowSummonState(false);
respawnStages(eim);
eim.startEventTimer(eventTime * 60000);
setEventRewards(eim);
setEventExclusives(eim);
return eim;
}
function afterSetup(eim) {}
function respawnStages(eim) {
eim.getInstanceMap(entryMap).instanceMapRespawn();
eim.schedule("respawnStages", 10 * 1000);
}
function snowmanHeal(eim) {
var difficulty = eim.getIntProperty("level");
var snowman = eim.getInstanceMap(entryMap).getMonsterById(9400316 + (5 * difficulty) + 5);
snowman.heal(200 + 200 * difficulty, 0);
eim.schedule("snowmanHeal", 10 * 1000);
}
function playerEntry(eim, player) {
var map = eim.getMapInstance(entryMap);
player.changeMap(map, map.getPortal(0));
}
function scheduledTimeout(eim) {
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 changedMap(eim, player, mapid) {
if (mapid < minMapId || mapid > maxMapId) {
if (eim.isEventTeamLackingNow(true, minPlayers, player)) {
eim.unregisterPlayer(player);
end(eim);
}
else
eim.unregisterPlayer(player);
}
}
function changedLeader(eim, leader) {
var mapid = leader.getMapId();
if (!eim.isEventCleared() && (mapid < minMapId || mapid > maxMapId)) {
end(eim);
}
}
function playerDead(eim, player) {}
function playerRevive(eim, player) { // player presses ok on the death pop up.
if (eim.isEventTeamLackingNow(true, minPlayers, player)) {
eim.unregisterPlayer(player);
end(eim);
}
else
eim.unregisterPlayer(player);
}
function playerDisconnected(eim, player) {
if (eim.isEventTeamLackingNow(true, minPlayers, player)) {
eim.unregisterPlayer(player);
end(eim);
}
else
eim.unregisterPlayer(player);
}
function leftParty(eim, player) {
if (eim.isEventTeamLackingNow(false, minPlayers, player)) {
end(eim);
}
else
playerLeft(eim, player);
}
function disbandParty(eim) {
if (!eim.isEventCleared()) {
end(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 isScrooge(mob) {
var mobid = mob.getId();
return mobid >= 9400319 && mobid <= 9400321;
}
function monsterKilled(mob, eim) {
try {
if(eim.isEventCleared()) return;
else if(isScrooge(mob)) {
eim.giveEventPlayersStageReward(2 * eim.getIntProperty("level"));
eim.showClearEffect();
eim.clearPQ();
return;
}
var rnd = Math.random();
var forceDrop = false;
if(rnd >= 0.42) { // 42% chance of dropping token
var miss = eim.getIntProperty("missingDrops");
if(miss < 5) {
eim.setIntProperty("missingDrops", miss + 1);
return;
}
forceDrop = true;
}
var mapObj = mob.getMap();
var itemObj = new Item((forceDrop || Math.random() < 0.77) ? 4032094 : 4032095, 0, 1); // 77% chance of not fake
var dropper = eim.getPlayers().get(0);
mapObj.spawnItemDrop(mob, dropper, itemObj, mob.getPosition(), true, false);
eim.setIntProperty("missingDrops", 0);
} catch(err) {} // PQ not started yet
}
function allMonstersDead(eim) {}
function friendlyKilled(mob, eim) {
eim.setIntProperty("snowmanStep", 0);
var snowmanLevel = eim.getIntProperty("snowmanLevel");
if(snowmanLevel <= 1) {
end(eim);
} else {
eim.setIntProperty("snowmanLevel", snowmanLevel - 1);
}
}
function snowmanEvolve(eim, curLevel) {
var mapobj = eim.getInstanceMap(entryMap);
var difficulty = eim.getIntProperty("level");
var snowman = mapobj.getMonsterById(9400317 + (5 * difficulty) + (curLevel - 1));
eim.setIntProperty("snowmanLevel", curLevel + 2); // increment by 2 to decrement by 1 on friendlyKilled
mapobj.killMonster(snowman, null, false, 2);
var snowman = MapleLifeFactory.getMonster(9400317 + (5 * difficulty) + curLevel);
mapobj.spawnMonsterOnGroundBelow(snowman, new java.awt.Point(-180, 15));
if(curLevel >= 4) {
mapobj.allowSummonState(false);
mapobj.killAllMonstersNotFriendly();
mapobj.setReactorState();
eim.giveEventPlayersStageReward(2 * difficulty - 1);
eim.showClearEffect();
}
}
function snowmanSnack(eim) {
if(eim.getIntProperty("snowmanLevel") >= 5) return;
var step = eim.getIntProperty("snowmanStep");
var snowmanLevel = eim.getIntProperty("snowmanLevel");
if(step >= 2 + (eim.getIntProperty("level") * snowmanLevel)) {
step = 0;
snowmanEvolve(eim, snowmanLevel);
} else {
var mapobj = eim.getInstanceMap(entryMap);
var difficulty = eim.getIntProperty("level");
var snowman = mapobj.getMonsterById(9400316 + (5 * difficulty) + snowmanLevel);
snowman.heal(200 + (200 * snowmanLevel), 0);
step += 1;
}
eim.setIntProperty("snowmanStep", step);
}
function snowmanSnackFake(eim) {
if(eim.getIntProperty("snowmanLevel") >= 5) return;
var step = eim.getIntProperty("snowmanStep");
if(step > 0) {
eim.setIntProperty("snowmanStep", step - 1);
}
eim.dropMessage(5, "The snowman absorbed a Fake Snow Vigor!");
}
function cancelSchedule() {}
function dispose(eim) {}

View File

@@ -0,0 +1,356 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 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 <http://www.gnu.org/licenses/>.
*/
/**
* @author: Ronan
* @event: Holiday PQ
*/
// GMS-like event string data thanks to iHealForLove
importPackage(Packages.client.inventory);
importPackage(Packages.server.life);
var isPq = true;
var minPlayers = 3, maxPlayers = 6;
var minLevel = 31, maxLevel = 40;
var entryMap = 889100011;
var exitMap = 889100012;
var recruitMap = 889100010;
var clearMap = 889100012;
var minMapId = 889100011;
var maxMapId = 889100011;
var eventTime = 20; // 20 minutes
var lobbyRange = [0, 0];
function init() {
setEventRequirements();
}
function setLobbyRange() {
return lobbyRange;
}
function setEventRequirements() {
var reqStr = "";
reqStr += "\r\n Number of players: ";
if(maxPlayers - minPlayers >= 1) reqStr += minPlayers + " ~ " + maxPlayers;
else reqStr += minPlayers;
reqStr += "\r\n Level range: ";
if(maxLevel - minLevel >= 1) reqStr += minLevel + " ~ " + maxLevel;
else reqStr += minLevel;
reqStr += "\r\n Time limit: ";
reqStr += eventTime + " minutes";
em.setProperty("party", reqStr);
}
function setEventExclusives(eim) {
var itemSet = [4032094, 4032095];
eim.setExclusiveItems(itemSet);
}
function setEventRewards(eim) {
var itemSet, itemQty, evLevel, expStages;
evLevel = 3; //Rewards at Hard difficulty
itemSet = [1302080, 1002033, 2022153, 2022042, 2020006, 2020009, 2020016, 2020024, 4010006, 4010007, 4020004, 4020005, 4003002];
itemQty = [1, 1, 1, 5, 20, 15, 10, 10, 2, 4, 4, 4, 1];
eim.setEventRewards(evLevel, itemSet, itemQty);
evLevel = 2; //Rewards at Normal difficulty
itemSet = [1302080, 1002033, 2012005, 2012006, 2020002, 2020025, 2020026, 4010003, 4010004, 4010005, 4020002, 4020003, 4020007];
itemQty = [1, 1, 15, 15, 15, 10, 10, 3, 3, 3, 3, 3, 3];
eim.setEventRewards(evLevel, itemSet, itemQty);
evLevel = 1; //Rewards at Easy difficulty
itemSet = [1002033, 2012005, 2012006, 2020002, 2022006, 2022002, 4010000, 4010001, 4010002, 4020000, 4020001, 4020006];
itemQty = [1, 15, 15, 10, 5, 5, 2, 2, 2, 2, 2, 2];
eim.setEventRewards(evLevel, itemSet, itemQty);
expStages = [210, 620, 500, 1400, 950, 2200]; //bonus exp given on CLEAR stage signal
eim.setEventClearStageExp(expStages);
}
function getEligibleParty(party) { //selects, from the given party, the team that is allowed to attempt this event
var eligible = [];
var hasLeader = false;
if(party.size() > 0) {
var partyList = party.toArray();
for(var i = 0; i < party.size(); i++) {
var ch = partyList[i];
if(ch.getMapId() == recruitMap && ch.getLevel() >= minLevel && ch.getLevel() <= maxLevel) {
if(ch.isLeader()) hasLeader = true;
eligible.push(ch);
}
}
}
if(!(hasLeader && eligible.length >= minPlayers && eligible.length <= maxPlayers)) eligible = [];
return eligible;
}
function setup(level, lobbyid) {
var eim = em.newInstance("Holiday2_" + lobbyid);
eim.setProperty("level", level);
eim.setProperty("stage", "0");
eim.setProperty("statusStg1", "-1");
eim.setProperty("missingDrops", "0");
eim.setProperty("snowmanLevel", "0");
eim.setProperty("snowmanStep", "0");
eim.setProperty("spawnedBoss", "0");
var mapobj = eim.getInstanceMap(entryMap);
mapobj.resetPQ(level);
mapobj.allowSummonState(false);
respawnStages(eim);
eim.startEventTimer(eventTime * 60000);
setEventRewards(eim);
setEventExclusives(eim);
return eim;
}
function afterSetup(eim) {}
function respawnStages(eim) {
eim.getInstanceMap(entryMap).instanceMapRespawn();
eim.schedule("respawnStages", 10 * 1000);
}
function snowmanHeal(eim) {
var difficulty = eim.getIntProperty("level");
var snowman = eim.getInstanceMap(entryMap).getMonsterById(9400316 + (5 * difficulty) + 5);
snowman.heal(200 + 200 * difficulty, 0);
eim.schedule("snowmanHeal", 10 * 1000);
}
function playerEntry(eim, player) {
var map = eim.getMapInstance(entryMap);
player.changeMap(map, map.getPortal(0));
}
function scheduledTimeout(eim) {
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 changedMap(eim, player, mapid) {
if (mapid < minMapId || mapid > maxMapId) {
if (eim.isEventTeamLackingNow(true, minPlayers, player)) {
eim.unregisterPlayer(player);
end(eim);
}
else
eim.unregisterPlayer(player);
}
}
function changedLeader(eim, leader) {
var mapid = leader.getMapId();
if (!eim.isEventCleared() && (mapid < minMapId || mapid > maxMapId)) {
end(eim);
}
}
function playerDead(eim, player) {}
function playerRevive(eim, player) { // player presses ok on the death pop up.
if (eim.isEventTeamLackingNow(true, minPlayers, player)) {
eim.unregisterPlayer(player);
end(eim);
}
else
eim.unregisterPlayer(player);
}
function playerDisconnected(eim, player) {
if (eim.isEventTeamLackingNow(true, minPlayers, player)) {
eim.unregisterPlayer(player);
end(eim);
}
else
eim.unregisterPlayer(player);
}
function leftParty(eim, player) {
if (eim.isEventTeamLackingNow(false, minPlayers, player)) {
end(eim);
}
else
playerLeft(eim, player);
}
function disbandParty(eim) {
if (!eim.isEventCleared()) {
end(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 isScrooge(mob) {
var mobid = mob.getId();
return mobid >= 9400319 && mobid <= 9400321;
}
function monsterKilled(mob, eim) {
try {
if(eim.isEventCleared()) return;
else if(isScrooge(mob)) {
eim.giveEventPlayersStageReward(2 * eim.getIntProperty("level"));
eim.showClearEffect();
eim.clearPQ();
return;
}
var rnd = Math.random();
var forceDrop = false;
if(rnd >= 0.42) { // 42% chance of dropping token
var miss = eim.getIntProperty("missingDrops");
if(miss < 5) {
eim.setIntProperty("missingDrops", miss + 1);
return;
}
forceDrop = true;
}
var mapObj = mob.getMap();
var itemObj = new Item((forceDrop || Math.random() < 0.77) ? 4032094 : 4032095, 0, 1); // 77% chance of not fake
var dropper = eim.getPlayers().get(0);
mapObj.spawnItemDrop(mob, dropper, itemObj, mob.getPosition(), true, false);
eim.setIntProperty("missingDrops", 0);
} catch(err) {} // PQ not started yet
}
function allMonstersDead(eim) {}
function friendlyKilled(mob, eim) {
eim.setIntProperty("snowmanStep", 0);
var snowmanLevel = eim.getIntProperty("snowmanLevel");
if(snowmanLevel <= 1) {
end(eim);
} else {
eim.setIntProperty("snowmanLevel", snowmanLevel - 1);
}
}
function snowmanEvolve(eim, curLevel) {
var mapobj = eim.getInstanceMap(entryMap);
var difficulty = eim.getIntProperty("level");
var snowman = mapobj.getMonsterById(9400317 + (5 * difficulty) + (curLevel - 1));
eim.setIntProperty("snowmanLevel", curLevel + 2); // increment by 2 to decrement by 1 on friendlyKilled
mapobj.killMonster(snowman, null, false, 2);
var snowman = MapleLifeFactory.getMonster(9400317 + (5 * difficulty) + curLevel);
mapobj.spawnMonsterOnGroundBelow(snowman, new java.awt.Point(-180, 15));
if(curLevel >= 4) {
mapobj.allowSummonState(false);
mapobj.killAllMonstersNotFriendly();
mapobj.setReactorState();
eim.giveEventPlayersStageReward(2 * difficulty - 1);
eim.showClearEffect();
}
}
function snowmanSnack(eim) {
if(eim.getIntProperty("snowmanLevel") >= 5) return;
var step = eim.getIntProperty("snowmanStep");
var snowmanLevel = eim.getIntProperty("snowmanLevel");
if(step >= 2 + (eim.getIntProperty("level") * snowmanLevel)) {
step = 0;
snowmanEvolve(eim, snowmanLevel);
} else {
var mapobj = eim.getInstanceMap(entryMap);
var difficulty = eim.getIntProperty("level");
var snowman = mapobj.getMonsterById(9400316 + (5 * difficulty) + snowmanLevel);
snowman.heal(200 + (200 * snowmanLevel), 0);
step += 1;
}
eim.setIntProperty("snowmanStep", step);
}
function snowmanSnackFake(eim) {
if(eim.getIntProperty("snowmanLevel") >= 5) return;
var step = eim.getIntProperty("snowmanStep");
if(step > 0) {
eim.setIntProperty("snowmanStep", step - 1);
}
eim.dropMessage(5, "The snowman absorbed a Fake Snow Vigor!");
}
function cancelSchedule() {}
function dispose(eim) {}

View File

@@ -0,0 +1,356 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 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 <http://www.gnu.org/licenses/>.
*/
/**
* @author: Ronan
* @event: Holiday PQ
*/
// GMS-like event string data thanks to iHealForLove
importPackage(Packages.client.inventory);
importPackage(Packages.server.life);
var isPq = true;
var minPlayers = 3, maxPlayers = 6;
var minLevel = 41, maxLevel = 50;
var entryMap = 889100021;
var exitMap = 889100022;
var recruitMap = 889100020;
var clearMap = 889100022;
var minMapId = 889100021;
var maxMapId = 889100021;
var eventTime = 25; // 25 minutes
var lobbyRange = [0, 0];
function init() {
setEventRequirements();
}
function setLobbyRange() {
return lobbyRange;
}
function setEventRequirements() {
var reqStr = "";
reqStr += "\r\n Number of players: ";
if(maxPlayers - minPlayers >= 1) reqStr += minPlayers + " ~ " + maxPlayers;
else reqStr += minPlayers;
reqStr += "\r\n Level range: ";
if(maxLevel - minLevel >= 1) reqStr += minLevel + " ~ " + maxLevel;
else reqStr += minLevel;
reqStr += "\r\n Time limit: ";
reqStr += eventTime + " minutes";
em.setProperty("party", reqStr);
}
function setEventExclusives(eim) {
var itemSet = [4032094, 4032095];
eim.setExclusiveItems(itemSet);
}
function setEventRewards(eim) {
var itemSet, itemQty, evLevel, expStages;
evLevel = 3; //Rewards at Hard difficulty
itemSet = [1302080, 1002033, 2022153, 2022042, 2020006, 2020009, 2020016, 2020024, 4010006, 4010007, 4020004, 4020005, 4003002];
itemQty = [1, 1, 1, 5, 20, 15, 10, 10, 2, 4, 4, 4, 1];
eim.setEventRewards(evLevel, itemSet, itemQty);
evLevel = 2; //Rewards at Normal difficulty
itemSet = [1302080, 1002033, 2012005, 2012006, 2020002, 2020025, 2020026, 4010003, 4010004, 4010005, 4020002, 4020003, 4020007];
itemQty = [1, 1, 15, 15, 15, 10, 10, 3, 3, 3, 3, 3, 3];
eim.setEventRewards(evLevel, itemSet, itemQty);
evLevel = 1; //Rewards at Easy difficulty
itemSet = [1002033, 2012005, 2012006, 2020002, 2022006, 2022002, 4010000, 4010001, 4010002, 4020000, 4020001, 4020006];
itemQty = [1, 15, 15, 10, 5, 5, 2, 2, 2, 2, 2, 2];
eim.setEventRewards(evLevel, itemSet, itemQty);
expStages = [210, 620, 500, 1400, 950, 2200]; //bonus exp given on CLEAR stage signal
eim.setEventClearStageExp(expStages);
}
function getEligibleParty(party) { //selects, from the given party, the team that is allowed to attempt this event
var eligible = [];
var hasLeader = false;
if(party.size() > 0) {
var partyList = party.toArray();
for(var i = 0; i < party.size(); i++) {
var ch = partyList[i];
if(ch.getMapId() == recruitMap && ch.getLevel() >= minLevel && ch.getLevel() <= maxLevel) {
if(ch.isLeader()) hasLeader = true;
eligible.push(ch);
}
}
}
if(!(hasLeader && eligible.length >= minPlayers && eligible.length <= maxPlayers)) eligible = [];
return eligible;
}
function setup(level, lobbyid) {
var eim = em.newInstance("Holiday3_" + lobbyid);
eim.setProperty("level", level);
eim.setProperty("stage", "0");
eim.setProperty("statusStg1", "-1");
eim.setProperty("missingDrops", "0");
eim.setProperty("snowmanLevel", "0");
eim.setProperty("snowmanStep", "0");
eim.setProperty("spawnedBoss", "0");
var mapobj = eim.getInstanceMap(entryMap);
mapobj.resetPQ(level);
mapobj.allowSummonState(false);
respawnStages(eim);
eim.startEventTimer(eventTime * 60000);
setEventRewards(eim);
setEventExclusives(eim);
return eim;
}
function afterSetup(eim) {}
function respawnStages(eim) {
eim.getInstanceMap(entryMap).instanceMapRespawn();
eim.schedule("respawnStages", 10 * 1000);
}
function snowmanHeal(eim) {
var difficulty = eim.getIntProperty("level");
var snowman = eim.getInstanceMap(entryMap).getMonsterById(9400316 + (5 * difficulty) + 5);
snowman.heal(200 + 200 * difficulty, 0);
eim.schedule("snowmanHeal", 10 * 1000);
}
function playerEntry(eim, player) {
var map = eim.getMapInstance(entryMap);
player.changeMap(map, map.getPortal(0));
}
function scheduledTimeout(eim) {
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 changedMap(eim, player, mapid) {
if (mapid < minMapId || mapid > maxMapId) {
if (eim.isEventTeamLackingNow(true, minPlayers, player)) {
eim.unregisterPlayer(player);
end(eim);
}
else
eim.unregisterPlayer(player);
}
}
function changedLeader(eim, leader) {
var mapid = leader.getMapId();
if (!eim.isEventCleared() && (mapid < minMapId || mapid > maxMapId)) {
end(eim);
}
}
function playerDead(eim, player) {}
function playerRevive(eim, player) { // player presses ok on the death pop up.
if (eim.isEventTeamLackingNow(true, minPlayers, player)) {
eim.unregisterPlayer(player);
end(eim);
}
else
eim.unregisterPlayer(player);
}
function playerDisconnected(eim, player) {
if (eim.isEventTeamLackingNow(true, minPlayers, player)) {
eim.unregisterPlayer(player);
end(eim);
}
else
eim.unregisterPlayer(player);
}
function leftParty(eim, player) {
if (eim.isEventTeamLackingNow(false, minPlayers, player)) {
end(eim);
}
else
playerLeft(eim, player);
}
function disbandParty(eim) {
if (!eim.isEventCleared()) {
end(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 isScrooge(mob) {
var mobid = mob.getId();
return mobid >= 9400319 && mobid <= 9400321;
}
function monsterKilled(mob, eim) {
try {
if(eim.isEventCleared()) return;
else if(isScrooge(mob)) {
eim.giveEventPlayersStageReward(2 * eim.getIntProperty("level"));
eim.showClearEffect();
eim.clearPQ();
return;
}
var rnd = Math.random();
var forceDrop = false;
if(rnd >= 0.42) { // 42% chance of dropping token
var miss = eim.getIntProperty("missingDrops");
if(miss < 5) {
eim.setIntProperty("missingDrops", miss + 1);
return;
}
forceDrop = true;
}
var mapObj = mob.getMap();
var itemObj = new Item((forceDrop || Math.random() < 0.77) ? 4032094 : 4032095, 0, 1); // 77% chance of not fake
var dropper = eim.getPlayers().get(0);
mapObj.spawnItemDrop(mob, dropper, itemObj, mob.getPosition(), true, false);
eim.setIntProperty("missingDrops", 0);
} catch(err) {} // PQ not started yet
}
function allMonstersDead(eim) {}
function friendlyKilled(mob, eim) {
eim.setIntProperty("snowmanStep", 0);
var snowmanLevel = eim.getIntProperty("snowmanLevel");
if(snowmanLevel <= 1) {
end(eim);
} else {
eim.setIntProperty("snowmanLevel", snowmanLevel - 1);
}
}
function snowmanEvolve(eim, curLevel) {
var mapobj = eim.getInstanceMap(entryMap);
var difficulty = eim.getIntProperty("level");
var snowman = mapobj.getMonsterById(9400317 + (5 * difficulty) + (curLevel - 1));
eim.setIntProperty("snowmanLevel", curLevel + 2); // increment by 2 to decrement by 1 on friendlyKilled
mapobj.killMonster(snowman, null, false, 2);
var snowman = MapleLifeFactory.getMonster(9400317 + (5 * difficulty) + curLevel);
mapobj.spawnMonsterOnGroundBelow(snowman, new java.awt.Point(-180, 15));
if(curLevel >= 4) {
mapobj.allowSummonState(false);
mapobj.killAllMonstersNotFriendly();
mapobj.setReactorState();
eim.giveEventPlayersStageReward(2 * difficulty - 1);
eim.showClearEffect();
}
}
function snowmanSnack(eim) {
if(eim.getIntProperty("snowmanLevel") >= 5) return;
var step = eim.getIntProperty("snowmanStep");
var snowmanLevel = eim.getIntProperty("snowmanLevel");
if(step >= 2 + (eim.getIntProperty("level") * snowmanLevel)) {
step = 0;
snowmanEvolve(eim, snowmanLevel);
} else {
var mapobj = eim.getInstanceMap(entryMap);
var difficulty = eim.getIntProperty("level");
var snowman = mapobj.getMonsterById(9400316 + (5 * difficulty) + snowmanLevel);
snowman.heal(200 + (200 * snowmanLevel), 0);
step += 1;
}
eim.setIntProperty("snowmanStep", step);
}
function snowmanSnackFake(eim) {
if(eim.getIntProperty("snowmanLevel") >= 5) return;
var step = eim.getIntProperty("snowmanStep");
if(step > 0) {
eim.setIntProperty("snowmanStep", step - 1);
}
eim.dropMessage(5, "The snowman absorbed a Fake Snow Vigor!");
}
function cancelSchedule() {}
function dispose(eim) {}

View File

@@ -39,7 +39,7 @@ function action(mode, type, selection) {
} else if (selection == 1) {
cm.sendSimple("There are many games for this event. It will help you a lot to know how to play the game before you play it. Choose the one you want to know more of! #b\r\n#L0# Ola Ola#l\r\n#L1# MapleStory Maple Physical Fitness Test#l\r\n#L2# Snow Ball#l\r\n#L3# Coconut Harvest#l\r\n#L4# OX Quiz#l\r\n#L5# Treasure Hunt#l#k");
} else if (selection == 2) {
var marr = cm.getQuestRecord(160200);
var marr = cm.getQuestRecord(100295);
if (marr.getCustomData() == null) {
marr.setCustomData("0");
}

230
scripts/npc/9105004.js Normal file
View File

@@ -0,0 +1,230 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 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 <http://www.gnu.org/licenses/>.
*/
/*
* @Author Ronan
* Snow Spirit
Maplemas PQ coordinator
*/
importPackage(Packages.server.life);
var prizeTree = [[[2000002, 1002850], [20, 1]], [[2000006, 1012011], [20, 1]]];
var state;
var status;
var gift;
var pqType;
function start() {
pqType = ((cm.getMapId() / 10) % 10) + 1;
state = (cm.getMapId() % 10 > 0) ? 1 : 0;
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(state > 0) {
insidePqAction(mode, type, selection);
} else {
recruitPqAction(mode, type, selection);
}
}
}
function recruitPqAction(mode, type, selection) {
if (status == 0) {
em = cm.getEventManager("HolidayPQ_" + pqType);
if(em == null) {
cm.sendOk("The Holiday PQ " + pqType + " has encountered an error.");
cm.dispose();
} else if(cm.isUsingOldPqNpcStyle()) {
action(1, 0, 0);
return;
}
cm.sendSimple("#e#b<Party Quest: Holiday>\r\n#k#n" + em.getProperty("party") + "\r\n\r\nHow about you and your party members collectively beating a quest? Here you'll find obstacles and problems where you won't be able to beat it without great teamwork. If you want to try it, please tell the #bleader of your party#k to talk to me.#b\r\n#L0#I want to participate in the party quest.\r\n#L1#I want to find party members.\r\n#L2#I would like to hear more details.");
} else if (status == 1) {
if (selection == 0) {
if (cm.getParty() == null) {
cm.sendOk("You can participate in the party quest only if you are in a party.");
cm.dispose();
} else if(!cm.isLeader()) {
cm.sendOk("Your party leader must talk to me to start this party quest.");
cm.dispose();
} else {
var eli = em.getEligibleParty(cm.getParty());
if(eli.size() > 0) {
if(!em.startInstance(cm.getParty(), cm.getPlayer().getMap(), pqType)) {
cm.sendOk("Another party has already entered the #rParty Quest#k in this channel. Please try another channel, or wait for the current party to finish.");
}
}
else {
cm.sendOk("You cannot start this party quest yet, because either your party is not in the range size, some of your party members are not eligible to attempt it or they are not in this map. If you're having trouble finding party members, try Party Search.");
}
cm.dispose();
}
} else if (selection == 1) {
cm.sendOk("Try using a Super Megaphone or asking your buddies or guild to join!");
cm.dispose();
} else {
cm.sendOk("#e#b<Party Quest: Holiday>#k#n\r\n\r\nJoin in with your team to build up the Snowman that will protect Happyville from the misdoings of Scrooge. While inside, work out with your team to protect it at any means necessary while collecting Snow Vigor that will help on the build up of the Snowman.");
cm.dispose();
}
}
}
function insidePqAction(mode, type, selection) {
var eim = cm.getEventInstance();
var difficulty = eim.getIntProperty("level");
var stg = eim.getIntProperty("statusStg1");
var mapobj = eim.getInstanceMap(889100001 + 10 * (difficulty - 1));
if(status == 0) {
if(stg == -1) {
cm.sendNext("#b#h0##k... you're finally here. This is the place where the residents of Happyville build the giant snowman. But Scrooge's subordinates are attacking it right now. Now Hurry! Our mission is for you and your party to protect the snowman from Scrooge's men within the time limit. If you eliminate them, then they'll drop an item called Snow Vigor. Gather them up and drop them on the snowman, and you'll literally see it grow. Once it returns to its original size, then your task is complete. Just beware of one thing. Some of the subordinates may drop a fake Snow Vigor. A fake Snow Vigor will actually cause the snowman to melt even faster than usual. Best of luck to you.");
} else if(stg == 0) {
if(cm.getMap().getMonsterById(9400321 + 5 * difficulty) == null) {
cm.sendNext("Please, defeat Scrooge's underlings and make the snowman grow, so that Scrooge has no other way to avoid showing himself up.");
cm.dispose();
} else {
cm.sendNext("Awesome! Just as I expected, you managed to defeat Scrooge's subordinates. Thank you so much! (Stands silent for a while...) Unfortunately, Scrooge doesn't seem like he's going to stop right here. One of his men have already told him what happened, which means... he'll show up soon. Please keep fighting, and again, best of luck to you.");
}
} else {
if(!eim.isEventCleared()) {
cm.sendNext("Please defeat the Scrooge, so our Maplemas keeps safe from harm!");
cm.dispose();
} else {
cm.sendNext("Wow!! You defeated Scrooge! Thank you so much! You have managed to make this Maplemas safe and sound! Thanks!!");
}
}
} else if(status == 1) {
if(stg == -1) {
if(!cm.isEventLeader()) {
cm.sendOk("Please let your party leader talk to me for further details on the mission.");
cm.dispose();
return;
}
mapobj.allowSummonState(true);
var snowman = MapleLifeFactory.getMonster(9400317 + (5 * difficulty));
mapobj.spawnMonsterOnGroundBelow(snowman, new java.awt.Point(-180, 15));
eim.setIntProperty("snowmanLevel", 1);
eim.dropMessage(5, "The snowman appeared on the field! Protect it using all means necessary!");
eim.setIntProperty("statusStg1", 0);
cm.dispose();
return;
} else if(stg == 0) {
if(!cm.isEventLeader()) {
cm.sendOk("Please let your party leader talk to me for further details on the mission.");
cm.dispose();
return;
}
mapobj.broadcastStringMessage(5, "As the snowman grows to it's prime, the Scrooge appears!");
eim.getEm().getIv().invokeFunction("snowmanHeal", eim);
var boss = MapleLifeFactory.getMonster(9400318 + difficulty);
mapobj.spawnMonsterOnGroundBelow(boss, new java.awt.Point(-180, 15));
eim.setProperty("spawnedBoss", "true");
eim.setIntProperty("statusStg1", 1);
cm.dispose();
} else {
gift = cm.haveItem(4032092, 1);
if(gift) {
var optStr = generateSelectionMenu(generatePrizeString());
cm.sendSimple("Oh, you brought a #b#t4032092##k with you? That's nice, hold on a bit... Here's your Maplemas gift. Please select the one you'd like to receive:\r\n\r\n" + optStr);
} else if(eim.gridCheck(cm.getPlayer()) == -1) {
cm.sendNext("Here's your Maplemas gift. Enjoy~");
} else {
cm.sendOk("Happy Maplemas!!");
cm.dispose();
}
}
} else if(status == 2) {
if(gift) {
var selItems = prizeTree[selection];
if(cm.canHoldAll(selItems[0], selItems[1])) {
cm.gainItem(4032092, -1);
cm.gainItem(selItems[0][0], selItems[1][0]);
if(selection == 1) {
var rnd = (Math.random() * 9) | 0;
cm.gainItem(selItems[0][1] + rnd, selItems[1][1]);
} else {
cm.gainItem(selItems[0][1], selItems[1][1]);
}
} else {
cm.sendOk("Please make sure you have room in your EQUIP and USE inventories before proceeding.");
}
} else {
if(eim.giveEventReward(cm.getPlayer(), difficulty)) {
eim.gridInsert(cm.getPlayer(), 1);
} else {
cm.sendOk("Please make sure you have room in your EQUIP, USE and ETC inventories before proceeding.");
}
}
cm.dispose();
}
}
function generatePrizeString() {
var strTree = [];
for(var i = 0; i < prizeTree.length; i++) {
var items = prizeTree[i][0];
var qtys = prizeTree[i][1];
var strSel = "";
for(var j = 0; j < items.length; j++) {
strSel += ("#i" + items[j] + "# #t" + items[j] + "#" + (qtys[j] > 1 ? (" : " + qtys[j]) : ""));
}
strTree.push(strSel);
}
return strTree;
}
function generateSelectionMenu(array) {
var menu = "";
for (var i = 0; i < array.length; i++) {
menu += "#L" + i + "#" + array[i] + "#l\r\n";
}
return menu;
}

59
scripts/npc/9105005.js Normal file
View File

@@ -0,0 +1,59 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 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 <http://www.gnu.org/licenses/>.
*/
var status;
var area;
function start() {
area = cm.getMapId() % 10;
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(area > 0) {
cm.sendYesNo("Do you wish to leave this place?");
} else {
cm.sendYesNo("Do you wish to return to #bHappyville#k?");
}
} else {
if(area > 0) {
cm.warp(cm.getMapId() + 1, 0);
} else {
cm.warp(209000000);
}
cm.dispose();
}
}
}

View File

@@ -260,12 +260,6 @@ function action(mode, type, selection) {
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 {

View File

@@ -260,12 +260,6 @@ function action(mode, type, selection) {
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 {

View File

@@ -1,3 +1,22 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 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 <http://www.gnu.org/licenses/>.
*/
/**
* @author: Ronan
* @npc: Abdula

View File

@@ -32,7 +32,7 @@ var ambientSong = "Bgm04/Shinin'Harbor";
var feature_tree = [];
var feature_cursor;
var tabs = ["PQs", "Skills", "Quests", "Player Social Network", "Cash & Items", "Monsters, Maps & Reactors", "PQ potentials", "Player potentials", "Server potentials", "Admin/GM commands", "Localhost edits", "Project"];
var tabs = ["PQs", "Skills", "Quests", "Player Social Network", "Cash & Items", "Monsters, Maps & Reactors", "PQ potentials", "Player potentials", "Server potentials", "Admin/GM commands", "Custom NPCs", "Localhost edits", "Project"];
function addFeature(feature) {
feature_cursor.push(feature);
@@ -40,7 +40,7 @@ function addFeature(feature) {
function writeFeatureTab_PQs() {
addFeature("HPQ/KPQ/LPQ/LMPQ/OPQ/APQ/EllinPQ/PiratePQ.");
addFeature("MagatiaPQ/HorntailPQ/TreasurePQ/ElnathPQ.");
addFeature("RnJPQ/HorntailPQ/TreasurePQ/ElnathPQ/HolidayPQ.");
addFeature("CWKPQ as Expedition-based event.");
addFeature("Scarga/Horntail/Showa/Balrog/Zakum/Pinkbean.");
addFeature("GuildPQ & queue with multi-lobby systems available.");
@@ -51,8 +51,10 @@ function writeFeatureTab_PQs() {
function writeFeatureTab_Skills() {
addFeature("Reviewed many skills, such as Steal and M. Door.");
addFeature("Improved battleship: HP visible and map-persistent.");
addFeature("Maker skill features properly developed.");
addFeature("Chair Mastery - map chair boosts HP/MP rec.");
addFeature("Mu Lung Dojo skills functional.");
}
function writeFeatureTab_Quests() {
@@ -85,6 +87,7 @@ function writeFeatureTab_CashItems() {
addFeature("New town scroll: antibanish. Counters boss banishes.");
addFeature("Inventory system checks for free slot & stack space.");
addFeature("Storage with 'Arrange Items' feature functional.");
addFeature("Close-quarters evaluation mode for items.");
addFeature("Further improved Karma scissors.");
addFeature("Scroll for Spikes on Shoes.");
addFeature("Scroll for Cold Protection.");
@@ -103,12 +106,14 @@ function writeFeatureTab_MonstersMapsReactors() {
addFeature("Monsterbook displays updated drop data info.");
addFeature("Every skill/mastery book is now obtainable.");
addFeature("Mobs now can drop more than one of the same equip.");
addFeature("Redesigned HT mechanics: assemble & dmg taken.");
addFeature("Implemented Zombify disease status.");
addFeature("Added Boss HP Bar for dozens of bosses.");
addFeature("Game will favor showing the targeted boss HPbar.");
addFeature("Dmg overtime on maps and neutralizers functional.");
addFeature("Boats, elevator and other travel mechanics functional.");
addFeature("C. Balrog's boat approaching visual effect functional.");
addFeature("Maps having everlasting items no longer expires them.");
addFeature("PQs, Taxis and events warps players to random SPs.");
addFeature("PQ boxes sprays items when opened, GMS-like.");
addFeature("Reactors pick items up smartly from the field.");
@@ -129,6 +134,7 @@ function writeFeatureTab_Playerpotentials() {
addFeature("Player level rates.");
addFeature("Gain fame by quests.");
addFeature("Pet evolutions functional (not GMS-like).");
addFeature("Reviewed keybinding system.");
}
function writeFeatureTab_Serverpotentials() {
@@ -160,6 +166,14 @@ function writeFeatureTab_AdminGMcommands() {
addFeature("Several new commands.");
}
function writeFeatureTab_CustomNPCs() {
addFeature("Spiegelmann: automatized rock-refiner.");
addFeature("Abdula: lists droppers of needed skill/mastery books.");
addFeature("Agent E: accessory crafter.");
addFeature("Donation Box: automatized item-buyer.");
addFeature("Coco & Ace of Hearts: C. scroll crafters.");
}
function writeFeatureTab_Localhostedits() {
addFeature("Removed the 'n' NPC dialog issue.");
addFeature("Removed caps for MATK, WMDEF, ACC and AVOID.");
@@ -180,6 +194,7 @@ function writeFeatureTab_Project() {
addFeature("Fixed/added many missing packet opcodes.");
addFeature("Uncovered many opcodes throughout the source.");
addFeature("Reviewed many Java aspects that needed attention.");
addFeature("Reviewed SQL data, eliminating duplicated entries.");
addFeature("Protected many flaws with login management system.");
addFeature("ThreadTracker: runtime tool for deadlock detection.");
addFeature("Heavily reviewed future task management, spawning much less threads and relieving task overload on the TimerManager.");

View File

@@ -0,0 +1,25 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 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 <http://www.gnu.org/licenses/>.
*/
function enter(pi) {
pi.playPortalSound();
pi.warp(pi.getMapId() - 2, 0);
return true;
}

54
scripts/quest/28004.js Normal file
View File

@@ -0,0 +1,54 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 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 <http://www.gnu.org/licenses/>.
*/
var status = -1;
function start(mode, type, selection) {
if (mode == -1) {
qm.dispose();
} else {
if(mode == 0 && type > 0) {
qm.dispose();
return;
}
if (mode == 1)
status++;
else
status--;
if (status == 0) {
if(qm.getPlayer().getLevel() > 50) {
qm.forceCompleteQuest();
qm.dispose();
return;
}
qm.sendNext("Okay... so here's our plan to defeat Scrooge and his dastardly plans. The Force of the Spirit I gave you is an item packed with mana. It's an item you'll definitely use at the map I am about to send you. In order to do that, you'll have to bring your party members with you as well. You should bring your party members here or form one right now!");
} else if (status == 1) {
qm.sendAcceptDecline("Would you like to move forward?");
} else {
var level = qm.getPlayer().getLevel();
qm.warp(level <= 30 ? 889100000 : (level <= 40 ? 889100010 : 889100020));
qm.dispose();
}
}
}

View File

@@ -0,0 +1,29 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 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 <http://www.gnu.org/licenses/>.
*/
/* @Author Ronan
*
* 9400300.js: assimilate Snow Vigor
*/
function act() {
var eim = rm.getEventInstance();
eim.getEm().getIv().invokeFunction("snowmanSnack", eim);
}

View File

@@ -0,0 +1,29 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 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 <http://www.gnu.org/licenses/>.
*/
/* @Author Ronan
*
* 9400301.js: assimilate Fake Snow Vigor
*/
function act() {
var eim = rm.getEventInstance();
eim.getEm().getIv().invokeFunction("snowmanSnackFake", eim);
}

View File

@@ -65,7 +65,6 @@ public enum MapleBuffStat {
SPARK(0x20000000L),
MAP_CHAIR(0x40000000L),
FINALATTACK(0x80000000L),
BATTLESHIP(0xA00000040L), // weird one
WATK(0x100000000L),
WDEF(0x200000000L),
MATK(0x400000000L),

View File

@@ -200,7 +200,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
private int owlSearch;
private long lastfametime, lastUsedCashItem, lastHealed, lastBuyback, lastDeathtime, lastMesoDrop = -1, jailExpiration = -1;
private transient int localmaxhp, localmaxmp, localstr, localdex, localluk, localint_, magic, watk;
private boolean hidden, canDoor = true, berserk, hasMerchant, whiteChat = false;
private boolean hidden, canDoor = true, berserk, hasMerchant, hasSandboxItem = false, whiteChat = false;
private int linkedLevel = 0;
private String linkedName = null;
private boolean finishedDojoTutorial;
@@ -479,9 +479,6 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
effLock.lock();
chrLock.lock();
try {
if (this.coolDowns.containsKey(Integer.valueOf(skillId))) {
this.coolDowns.remove(Integer.valueOf(skillId));
}
this.coolDowns.put(Integer.valueOf(skillId), new MapleCoolDownValueHolder(skillId, startTime, length));
} finally {
chrLock.unlock();
@@ -961,6 +958,33 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
public boolean canDoor() {
return canDoor;
}
public void setHasSandboxItem() {
hasSandboxItem = true;
}
public void removeSandboxItems() { // sandbox idea thanks to Morty
if(!hasSandboxItem) return;
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
for(MapleInventoryType invType : MapleInventoryType.values()) {
MapleInventory inv = this.getInventory(invType);
inv.lockInventory();
try {
for(Item item : new ArrayList<>(inv.list())) {
if(MapleInventoryManipulator.isSandboxItem(item)) {
MapleInventoryManipulator.removeFromSlot(client, invType, item.getPosition(), item.getQuantity(), false);
dropMessage(5, "[" + ii.getName(item.getItemId()) + "] has passed its trial conditions and will be removed from your inventory.");
}
}
} finally {
inv.unlockInventory();
}
}
hasSandboxItem = false;
}
public FameStatus canGiveFame(MapleCharacter from) {
if (gmLevel > 0) {
@@ -1789,10 +1813,18 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
return getInventory(ItemConstants.getInventoryType(itemid)).getNextFreeSlot() > -1;
}
public boolean isRidingBattleship() {
Integer bv = getBuffedValue(MapleBuffStat.MONSTER_RIDING);
return bv != null && bv.equals(Corsair.BATTLE_SHIP);
}
public void announceBattleshipHp() {
announce(MaplePacketCreator.skillCooldown(5221999, battleshipHp));
}
public void decreaseBattleshipHp(int decrease) {
this.battleshipHp -= decrease;
if (battleshipHp <= 0) {
this.battleshipHp = 0;
Skill battleship = SkillFactory.getSkill(Corsair.BATTLE_SHIP);
int cooldown = battleship.getEffect(getSkillLevel(battleship)).getCooldown();
announce(MaplePacketCreator.skillCooldown(Corsair.BATTLE_SHIP, cooldown));
@@ -1800,7 +1832,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
removeCooldown(5221999);
cancelEffectFromBuffStat(MapleBuffStat.MONSTER_RIDING);
} else {
announce(MaplePacketCreator.skillCooldown(5221999, battleshipHp / 10)); //:D
announceBattleshipHp();
addCooldown(5221999, 0, Long.MAX_VALUE);
}
}
@@ -2575,8 +2607,9 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
Pair<Integer, String> replace = ii.getReplaceOnExpire(item.getItemId());
if (replace.left > 0) {
toadd.add(replace.left);
if (replace.right != null)
if (!replace.right.isEmpty()) {
dropMessage(replace.right);
}
}
for (Integer itemid : toadd) {
MapleInventoryManipulator.addById(client, itemid, (short) 1);
@@ -3032,7 +3065,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
}
private void debugListAllBuffs() {
public void debugListAllBuffs() {
effLock.lock();
chrLock.lock();
try {
@@ -3057,7 +3090,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
}
private void debugListAllBuffsCount() {
public void debugListAllBuffsCount() {
effLock.lock();
chrLock.lock();
try {
@@ -3568,6 +3601,13 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
Pair<MapleStatEffect, Long> msel = lmse.getRight();
msel.getLeft().updateBuffEffect(this, getActiveStatupsFromSourceid(lmse.getLeft()), msel.getRight());
}
if (this.isRidingBattleship()) {
List<Pair<MapleBuffStat, Integer>> statups = new ArrayList<>(1);
statups.add(new Pair<>(MapleBuffStat.MONSTER_RIDING, 0));
this.announce(MaplePacketCreator.giveBuff(1932000, 5221006, statups));
this.announceBattleshipHp();
}
}
private static MapleBuffStat getSingletonStatupFromEffect(MapleStatEffect mse) {
@@ -5805,6 +5845,10 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
ret.getInventory(MapleInventoryType.SETUP).setSlotLimit(rs.getByte("setupslots"));
ret.getInventory(MapleInventoryType.ETC).setSlotLimit(rs.getByte("etcslots"));
for (Pair<Item, MapleInventoryType> item : ItemFactory.INVENTORY.loadItems(ret.id, !channelserver)) {
if(MapleInventoryManipulator.isSandboxItem(item.getLeft())) {
ret.setHasSandboxItem();
}
ret.getInventory(item.getRight()).addFromDB(item.getLeft());
Item itemz = item.getLeft();
if (itemz.getPetId() > -1) {
@@ -6015,12 +6059,6 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
}
/*
for(MapleQuestStatus mqs : loadedQuestStatus.values()) {
mqs.resetUpdated();
}
*/
loadedQuestStatus.clear();
ps = con.prepareStatement("SELECT skillid,skilllevel,masterlevel,expiration FROM skills WHERE characterid = ?");
@@ -6550,11 +6588,11 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
statup.add(new Pair<>(MapleStat.LUK, tluk));
announce(MaplePacketCreator.updatePlayerStats(statup, this));
}
public void resetBattleshipHp() {
this.battleshipHp = 4000 * getSkillLevel(SkillFactory.getSkill(Corsair.BATTLE_SHIP)) + ((getLevel() - 120) * 2000);
this.battleshipHp = 400 * getSkillLevel(SkillFactory.getSkill(Corsair.BATTLE_SHIP)) + ((getLevel() - 120) * 200);
}
public void resetEnteredScript() {
if (entered.containsKey(map.getId())) {
entered.remove(map.getId());

View File

@@ -64,6 +64,7 @@ import client.inventory.MapleInventoryType;
import constants.GameConstants;
import constants.ServerConstants;
import scripting.AbstractPlayerInteraction;
import scripting.event.EventInstanceManager;
import scripting.event.EventManager;
import scripting.npc.NPCConversationManager;
import scripting.npc.NPCScriptManager;
@@ -798,8 +799,9 @@ public class MapleClient {
player.closePlayerInteractions();
QuestScriptManager.getInstance().dispose(this);
if (player.getEventInstance() != null) {
player.getEventInstance().playerDisconnected(player);
EventInstanceManager eim = player.getEventInstance();
if (eim != null) {
eim.playerDisconnected(player);
}
if (player.getMap() != null) {
int mapId = player.getMapId();

View File

@@ -1285,8 +1285,10 @@ public class Commands {
byte b = toDrop.getFlag();
b |= ItemConstants.ACCOUNT_SHARING;
b |= ItemConstants.UNTRADEABLE;
b |= ItemConstants.SANDBOX;
toDrop.setFlag(b);
toDrop.setOwner("TRIAL-MODE");
}
c.getPlayer().getMap().spawnItemDrop(c.getPlayer(), c.getPlayer(), toDrop, c.getPlayer().getPosition(), true, true);

View File

@@ -77,7 +77,7 @@ public class MapleInventoryManipulator {
if (i.hasNext()) {
Item eItem = (Item) i.next();
short oldQ = eItem.getQuantity();
if (oldQ < slotMax && (eItem.getOwner().equals(owner) || owner == null)) {
if (oldQ < slotMax && ((eItem.getOwner().equals(owner) || owner == null) && eItem.getFlag() == flag)) {
short newQ = (short) Math.min(oldQ + quantity, slotMax);
quantity -= (newQ - oldQ);
eItem.setQuantity(newQ);
@@ -89,6 +89,7 @@ public class MapleInventoryManipulator {
}
}
}
boolean sandboxItem = (flag & ItemConstants.SANDBOX) == ItemConstants.SANDBOX;
while (quantity > 0 || ItemConstants.isRechargeable(itemId)) {
short newQ = (short) Math.min(quantity, slotMax);
if (newQ != 0) {
@@ -106,6 +107,7 @@ public class MapleInventoryManipulator {
nItem.setOwner(owner);
}
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, nItem))));
if(sandboxItem) c.getPlayer().setHasSandboxItem();
if ((ItemConstants.isRechargeable(itemId)) && quantity == 0) {
break;
}
@@ -125,6 +127,7 @@ public class MapleInventoryManipulator {
return false;
}
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, nItem))));
if(MapleInventoryManipulator.isSandboxItem(nItem)) c.getPlayer().setHasSandboxItem();
}
} else if (quantity == 1) {
Item nEquip = ii.getEquipById(itemId);
@@ -140,6 +143,7 @@ public class MapleInventoryManipulator {
return false;
}
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, nEquip))));
if(MapleInventoryManipulator.isSandboxItem(nEquip)) c.getPlayer().setHasSandboxItem();
} else {
throw new RuntimeException("Trying to create equip with non-one quantity");
}
@@ -173,7 +177,7 @@ public class MapleInventoryManipulator {
if (i.hasNext()) {
Item eItem = (Item) i.next();
short oldQ = eItem.getQuantity();
if (oldQ < slotMax && item.getOwner().equals(eItem.getOwner())) {
if (oldQ < slotMax && item.getFlag() == eItem.getFlag() && item.getOwner().equals(eItem.getOwner())) {
short newQ = (short) Math.min(oldQ + quantity, slotMax);
quantity -= (newQ - oldQ);
eItem.setQuantity(newQ);
@@ -199,10 +203,12 @@ public class MapleInventoryManipulator {
return false;
}
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, nItem))));
if(MapleInventoryManipulator.isSandboxItem(nItem)) c.getPlayer().setHasSandboxItem();
}
} else {
Item nItem = new Item(item.getItemId(), (short) 0, quantity, petId);
nItem.setExpiration(item.getExpiration());
nItem.setFlag(item.getFlag());
short newSlot = c.getPlayer().getInventory(type).addItem(nItem);
if (newSlot == -1) {
@@ -211,6 +217,7 @@ public class MapleInventoryManipulator {
return false;
}
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, nItem))));
if(MapleInventoryManipulator.isSandboxItem(nItem)) c.getPlayer().setHasSandboxItem();
c.announce(MaplePacketCreator.enableActions());
}
} else if (quantity == 1) {
@@ -221,6 +228,7 @@ public class MapleInventoryManipulator {
return false;
}
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, item))));
if(MapleInventoryManipulator.isSandboxItem(item)) c.getPlayer().setHasSandboxItem();
} else {
FilePrinter.printError(FilePrinter.ITEM, "Tried to pickup Equip id " + item.getItemId() + " containing more than 1 quantity --> " + quantity);
c.announce(MaplePacketCreator.getInventoryFull());
@@ -625,4 +633,8 @@ public class MapleInventoryManipulator {
private static boolean isDroppedItemRestricted(Item it) {
return ServerConstants.USE_ERASE_UNTRADEABLE_DROP && ((it.getFlag() & ItemConstants.UNTRADEABLE) == ItemConstants.UNTRADEABLE);
}
public static boolean isSandboxItem(Item it) {
return (it.getFlag() & ItemConstants.SANDBOX) == ItemConstants.SANDBOX;
}
}

View File

@@ -42,6 +42,7 @@ public final class ItemConstants {
public final static int UNTRADEABLE = 0x08;
public final static int KARMA_EQP = 0x10;
public final static int KARMA_UNTRADEABLE = 0x20; // let 0x20 until it's proven something uses this
public final static int SANDBOX = 0x40; // let 0x40 until it's proven something uses this
public final static int PET_COME = 0x80;
public final static int ACCOUNT_SHARING = 0x100;

View File

@@ -57,9 +57,11 @@ public final class MobDamageMobFriendlyHandler extends AbstractMaplePacketHandle
} else if(monster.getId() == 9300093) { //tylus
monster.getMap().broadcastMessage(MaplePacketCreator.serverNotice(6, "Tylus has fallen by the overwhelming forces of the ambush."));
} else if(monster.getId() == 9300137) { //juliet
monster.getMap().broadcastMessage(MaplePacketCreator.serverNotice(6, "Juliet has fainted on the middle of the combat."));
monster.getMap().broadcastMessage(MaplePacketCreator.serverNotice(6, "Juliet has fainted in the middle of the combat."));
} else if(monster.getId() == 9300138) { //romeo
monster.getMap().broadcastMessage(MaplePacketCreator.serverNotice(6, "Romeo has fainted on the middle of the combat."));
monster.getMap().broadcastMessage(MaplePacketCreator.serverNotice(6, "Romeo has fainted in the middle of the combat."));
} else if(monster.getId() == 9400322 || monster.getId() == 9400327 || monster.getId() == 9400332) {
monster.getMap().broadcastMessage(MaplePacketCreator.serverNotice(6, "The Snowman has melted on the heat of the battle."));
}
c.getPlayer().getMap().killFriendlies(monster);

View File

@@ -263,11 +263,14 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
if (!c.hasVotedAlready()){
player.announce(MaplePacketCreator.earnTitleMessage("You can vote now! Vote and earn a vote point!"));
}
*/
*/
if (player.isGM()){
Server.getInstance().broadcastGMMessage(c.getWorld(), MaplePacketCreator.earnTitleMessage((player.gmLevel() < 6 ? "GM " : "Admin ") + player.getName() + " has logged in"));
}
} else {
if(player.isRidingBattleship()) {
player.announceBattleshipHp();
}
}
showDueyNotification(c, player);

View File

@@ -409,8 +409,12 @@ public final class RingActionHandler extends AbstractMaplePacketHandler {
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);
if(guestChr != null && guestChr.isLoggedinWorld()) {
guestChr.dropMessage(6, "[WEDDING] You've been invited to " + groom + " and " + bride + "'s Wedding! Receive your invitation from Duey!");
} 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);

View File

@@ -237,10 +237,8 @@ public final class TakeDamageHandler extends AbstractMaplePacketHandler {
}
chr.addMPHP(-damage, -mpattack);
} else {
if (chr.getBuffedValue(MapleBuffStat.MONSTER_RIDING) != null) {
if (chr.getBuffedValue(MapleBuffStat.MONSTER_RIDING).intValue() == Corsair.BATTLE_SHIP) {
chr.decreaseBattleshipHp(damage);
}
if (chr.isRidingBattleship()) {
chr.decreaseBattleshipHp(damage);
}
chr.addMPHP(-damage, -mpattack);
}

View File

@@ -31,6 +31,7 @@ import constants.ServerConstants;
import net.AbstractMaplePacketHandler;
import client.inventory.manipulator.MapleInventoryManipulator;
import server.MapleItemInformationProvider;
import server.MapleStatEffect;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
@@ -94,8 +95,16 @@ public final class UseItemHandler extends AbstractMaplePacketHandler {
remove(c, slot);
ii.getItemEffect(toUse.getItemId()).applyTo(chr);
chr.checkBerserk(chr.isHidden());
if(toUse.getItemId() != 2022153) {
ii.getItemEffect(toUse.getItemId()).applyTo(chr);
chr.checkBerserk(chr.isHidden());
} else {
MapleStatEffect mse = ii.getItemEffect(toUse.getItemId());
for(MapleCharacter player : chr.getMap().getCharacters()) {
mse.applyTo(player);
player.checkBerserk(player.isHidden());
}
}
}
}

View File

@@ -234,8 +234,7 @@ public class EventInstanceManager {
wL.lock();
try {
chars.put(chr.getId(), chr);
}
finally {
} finally {
wL.unlock();
}
@@ -420,8 +419,7 @@ public class EventInstanceManager {
rL.lock();
try {
return chars.size();
}
finally {
} finally {
rL.unlock();
}
}
@@ -430,8 +428,7 @@ public class EventInstanceManager {
rL.lock();
try {
return chars.get(id);
}
finally {
} finally {
rL.unlock();
}
}
@@ -440,8 +437,7 @@ public class EventInstanceManager {
rL.lock();
try {
return new ArrayList<>(chars.values());
}
finally {
} finally {
rL.unlock();
}
}

View File

@@ -121,6 +121,8 @@ public class MapleItemInformationProvider {
protected Map<Integer, Pair<String, Integer>> statUpgradeMakerCache = new HashMap<>();
protected Map<Integer, MakerItemFactory.MakerItemCreateEntry> makerItemCache = new HashMap<>();
protected Map<Integer, Integer> makerCatalystCache = new HashMap<>();
protected Map<Integer, Map<String, Integer>> skillUpgradeCache = new HashMap<>();
protected Map<Integer, MapleData> skillUpgradeInfoCache = new HashMap<>();
private MapleItemInformationProvider() {
loadCardIdData();
@@ -525,8 +527,9 @@ public class MapleItemInformationProvider {
return replaceOnExpireCache.get(itemId);
}
int itemReplacement = MapleDataTool.getInt("info/replace/itemid", getItemData(itemId), 0);
String msg = MapleDataTool.getString("info/replace/msg", getItemData(itemId));
MapleData data = getItemData(itemId);
int itemReplacement = MapleDataTool.getInt("info/replace/itemid", data, 0);
String msg = MapleDataTool.getString("info/replace/msg", data, "");
Pair<Integer, String> ret = new Pair<>(itemReplacement, msg);
replaceOnExpireCache.put(itemId, ret);
@@ -1239,25 +1242,42 @@ public class MapleItemInformationProvider {
return bRestricted;
}
public Map<String, Integer> getSkillStats(int itemId, double playerJob) {
Map<String, Integer> ret = new LinkedHashMap<>();
private Pair<Map<String, Integer>, MapleData> getSkillStatsInternal(int itemId) {
Map<String, Integer> ret = skillUpgradeCache.get(itemId);
MapleData retSkill = skillUpgradeInfoCache.get(itemId);
if(ret != null) return new Pair<>(ret, retSkill);
retSkill = null;
ret = new LinkedHashMap<>();
MapleData item = getItemData(itemId);
if (item == null) {
return null;
}
MapleData info = item.getChildByPath("info");
if (info == null) {
return null;
}
for (MapleData data : info.getChildren()) {
if (data.getName().startsWith("inc")) {
ret.put(data.getName().substring(3), MapleDataTool.getIntConvert(data));
if (item != null) {
MapleData info = item.getChildByPath("info");
if (info != null) {
for (MapleData data : info.getChildren()) {
if (data.getName().startsWith("inc")) {
ret.put(data.getName().substring(3), MapleDataTool.getIntConvert(data));
}
}
ret.put("masterLevel", MapleDataTool.getInt("masterLevel", info, 0));
ret.put("reqSkillLevel", MapleDataTool.getInt("reqSkillLevel", info, 0));
ret.put("success", MapleDataTool.getInt("success", info, 0));
retSkill = info.getChildByPath("skill");
}
}
ret.put("masterLevel", MapleDataTool.getInt("masterLevel", info, 0));
ret.put("reqSkillLevel", MapleDataTool.getInt("reqSkillLevel", info, 0));
ret.put("success", MapleDataTool.getInt("success", info, 0));
MapleData skill = info.getChildByPath("skill");
skillUpgradeCache.put(itemId, ret);
skillUpgradeInfoCache.put(itemId, retSkill);
return new Pair<>(ret, retSkill);
}
public Map<String, Integer> getSkillStats(int itemId, double playerJob) {
Pair<Map<String, Integer>, MapleData> retData = getSkillStatsInternal(itemId);
if(retData.getLeft().isEmpty()) return null;
Map<String, Integer> ret = new LinkedHashMap<>(retData.getLeft());
MapleData skill = retData.getRight();
int curskill;
for (int i = 0; i < skill.getChildren().size(); i++) {
curskill = MapleDataTool.getInt(Integer.toString(i), skill, 0);
@@ -1933,7 +1953,7 @@ public class MapleItemInformationProvider {
}
private boolean canUseSkillBook(MapleCharacter player, Integer skillBookId) {
Map<String, Integer> skilldata = MapleItemInformationProvider.getInstance().getSkillStats(skillBookId, player.getJob().getId());
Map<String, Integer> skilldata = getSkillStats(skillBookId, player.getJob().getId());
if(skilldata == null || skilldata.get("skillid") == 0) return false;
Skill skill2 = SkillFactory.getSkill(skilldata.get("skillid"));
@@ -1942,7 +1962,7 @@ public class MapleItemInformationProvider {
public List<Integer> usableMasteryBooks(MapleCharacter player) {
List<Integer> masterybook = new LinkedList<>();
for(Integer i = 2290000; i <= 2290125; i++) {
for(Integer i = 2290000; i <= 2290139; i++) {
if(canUseSkillBook(player, i)) {
masterybook.add(i);
}
@@ -1953,7 +1973,7 @@ public class MapleItemInformationProvider {
public List<Integer> usableSkillBooks(MapleCharacter player) {
List<Integer> skillbook = new LinkedList<>();
for(Integer i = 2280000; i <= 2280012; i++) {
for(Integer i = 2280000; i <= 2280019; i++) {
if(canUseSkillBook(player, i)) {
skillbook.add(i);
}

View File

@@ -1001,7 +1001,7 @@ public class MapleStatEffect {
}
}
if (sourceid == Corsair.BATTLE_SHIP) {
chr.announce(MaplePacketCreator.skillCooldown(5221999, chr.getBattleshipHp()));
chr.announceBattleshipHp();
}
}
@@ -1107,14 +1107,17 @@ public class MapleStatEffect {
} else if (isCombo()) {
mbuff = MaplePacketCreator.giveForeignBuff(applyto.getId(), statups);
} else if (isMonsterRiding()) {
if (sourceid == Corsair.BATTLE_SHIP) {//hp
if (applyto.getBattleshipHp() <= 0) {
applyto.resetBattleshipHp();
}
localstatups = statups;
}
buff = MaplePacketCreator.giveBuff(localsourceid, localDuration, localstatups);
mbuff = MaplePacketCreator.showMonsterRiding(applyto.getId(), givemount);
localDuration = duration;
if (sourceid == Corsair.BATTLE_SHIP) {//hp
if (applyto.getBattleshipHp() == 0) {
applyto.resetBattleshipHp();
}
}
} else if (isShadowPartner()) {
List<Pair<MapleBuffStat, Integer>> stat = Collections.singletonList(new Pair<>(MapleBuffStat.SHADOWPARTNER, 0));
mbuff = MaplePacketCreator.giveForeignBuff(applyto.getId(), stat);
@@ -1130,9 +1133,8 @@ public class MapleStatEffect {
if (buff != null) {
if (!hasNoIcon()) { //Thanks flav for such a simple release! :)
applyto.getClient().announce(buff);
}
else {
applyto.announce(buff);
} else {
System.out.println("<Error> NO buff icon for id " + sourceid);
}
}
@@ -1146,7 +1148,7 @@ public class MapleStatEffect {
applyto.getMap().broadcastMessage(applyto, mbuff, false);
}
if (sourceid == Corsair.BATTLE_SHIP) {
applyto.announce(MaplePacketCreator.skillCooldown(5221999, applyto.getBattleshipHp() / 10));
applyto.announceBattleshipHp();
}
}
}

View File

@@ -275,6 +275,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
*
* @param from the player that dealt the damage
* @param damage
* @param stayAlive
*/
public synchronized void damage(MapleCharacter from, int damage, boolean stayAlive) {
Integer trueDamage = applyAndGetHpDamage(damage, stayAlive);
@@ -293,7 +294,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
broadcastMobHpBar(from);
}
public void heal(int hp, int mp) {
Integer hpHealed = applyAndGetHpDamage(-hp, false);
if(hpHealed == null) return;
@@ -305,7 +306,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
}
setMp(mp2Heal);
if(hp > 0) getMap().broadcastMessage(MaplePacketCreator.healMonster(getObjectId(), hp));
if(hp > 0) getMap().broadcastMessage(MaplePacketCreator.healMonster(getObjectId(), hp, getHp(), getMaxHp()));
maxHpPlusHeal.addAndGet(hpHealed);
dispatchMonsterHealed(hpHealed);
@@ -695,7 +696,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
public boolean hasBossHPBar() {
return isBoss() && getTagColor() > 0;
}
@Override
public void sendSpawnData(MapleClient c) {
if (!isAlive()) {

View File

@@ -470,26 +470,26 @@ public class MapleMap {
public void generateMapDropRangeCache() {
bndLock.lock();
try {
Integer mapId = Integer.valueOf(mapid);
Pair<Integer, Integer> bounds = dropBoundsCache.get(mapId);
Pair<Integer, Integer> bounds = dropBoundsCache.get(mapid);
if(bounds != null) {
xLimits = bounds;
} else {
// assuming MINIMAP always have an equal-greater picture representation of the map area (players won't walk beyond the area known by the minimap).
Point lp = new Point(mapArea.x, mapArea.y), rp = new Point(mapArea.x + mapArea.width, mapArea.y), fallback = new Point(mapArea.x + (mapArea.width / 2), mapArea.y);
lp = bsearchDropPos(lp, fallback);
rp = bsearchDropPos(rp, fallback);
lp = bsearchDropPos(lp, fallback); // approximated leftmost fh node position
rp = bsearchDropPos(rp, fallback); // approximated rightmost fh node position
xLimits = new Pair<>(lp.x, rp.x);
dropBoundsCache.put(mapId, xLimits);
xLimits = new Pair<>(lp.x + 14, rp.x - 14);
dropBoundsCache.put(mapid, xLimits);
}
} finally {
bndLock.unlock();
}
}
public Point bsearchDropPos(Point initial, Point fallback) {
private Point bsearchDropPos(Point initial, Point fallback) {
Point res, dropPos = null;
int awayx = fallback.x;
@@ -1304,13 +1304,6 @@ public class MapleMap {
}
}
public void monsterCloakingDevice() {
for (MapleMapObject monstermo : getMapObjectsInRange(new Point(0, 0), Double.POSITIVE_INFINITY, Arrays.asList(MapleMapObjectType.MONSTER))) {
MapleMonster monster = (MapleMonster) monstermo;
broadcastMessage(MaplePacketCreator.makeMonsterInvisible(monster));
}
}
public void softKillAllMonsters() {
closeMapSpawnPoints();
@@ -1809,6 +1802,8 @@ public class MapleMap {
monsterItemDrop(monster, monster.getDropPeriodTime() / 3);
} else if (monster.getId() == 9300093) {
monsterItemDrop(monster, monster.getDropPeriodTime());
} else if (monster.getId() == 9400326 || monster.getId() == 9400331 || monster.getId() == 9400336) {
monsterItemDrop(monster, monster.getDropPeriodTime());
} else {
FilePrinter.printError(FilePrinter.UNHANDLED_EVENT, "UNCODED TIMED MOB DETECTED: " + monster.getId() + "\r\n");
}
@@ -2373,6 +2368,9 @@ public class MapleMap {
break;
}
}
chr.removeSandboxItems();
if (chr.isHidden()) {
broadcastGMMessage(chr, MaplePacketCreator.spawnEnterPlayerMapObject(chr), false);
chr.announce(MaplePacketCreator.getGMEffect(0x10, (byte) 1));
@@ -2842,11 +2840,11 @@ public class MapleMap {
}
public void setMapPointBoundings(int px, int py, int h, int w) {
mapArea.setBounds(px + 80, py, w - 160, h);
mapArea.setBounds(px, py, w, h);
}
public void setMapLineBoundings(int vrTop, int vrBottom, int vrLeft, int vrRight) {
mapArea.setBounds(vrLeft + 7, vrTop, vrRight - vrLeft - 14, vrBottom - vrTop);
mapArea.setBounds(vrLeft, vrTop, vrRight - vrLeft, vrBottom - vrTop);
}
/**

View File

@@ -58,6 +58,7 @@ public class MapleMiniDungeon {
}
dispose();
timeoutTask = null;
} finally {
lock.unlock();
}

View File

@@ -241,8 +241,6 @@ public class MaplePlayerShop extends AbstractMapleMapObject {
if (c.getPlayer().getMeso() >= price) {
if (canBuy(c, newItem)) {
c.getPlayer().gainMeso(-price, false);
if(ServerConstants.USE_ANNOUNCE_SHOPITEMSOLD) announceItemSold(newItem, price); // idea thanks to vcoc
owner.gainMeso(price, true);
SoldItem soldItem = new SoldItem(c.getPlayer().getName(), pItem.getItem().getItemId(), quantity, price);
@@ -275,12 +273,6 @@ public class MaplePlayerShop extends AbstractMapleMapObject {
}
}
private void announceItemSold(Item item, int mesos) {
String qtyStr = (item.getQuantity() > 1) ? " (qty. " + item.getQuantity() + ")" : "";
owner.dropMessage(6, "[PLAYER SHOP] Item '" + MapleItemInformationProvider.getInstance().getName(item.getItemId()) + "'" + qtyStr + " has been sold for " + mesos + " mesos.");
}
public void broadcastToVisitors(final byte[] packet) {
visitorLock.lock();
try {

View File

@@ -1883,13 +1883,25 @@ public class MaplePacketCreator {
mplew.writeInt(CHAR_MAGIC_SPAWN);
mplew.writeShort(0);
mplew.write(0);
final Item mount = chr.getInventory(MapleInventoryType.EQUIPPED).getItem((short) -18);
if (chr.getBuffedValue(MapleBuffStat.MONSTER_RIDING) != null && mount != null) {
mplew.writeInt(mount.getItemId());
mplew.writeInt(1004);
Integer bv = chr.getBuffedValue(MapleBuffStat.MONSTER_RIDING);
if (bv != null) {
if(bv.equals(Corsair.BATTLE_SHIP)) {
mplew.writeInt(1932000);
mplew.writeInt(Corsair.BATTLE_SHIP);
} else {
final Item mount = chr.getInventory(MapleInventoryType.EQUIPPED).getItem((short) -18);
if(mount != null) {
mplew.writeInt(mount.getItemId());
mplew.writeInt(1004);
} else {
mplew.writeLong(0);
}
}
} else {
mplew.writeLong(0);
}
mplew.writeInt(CHAR_MAGIC_SPAWN);
mplew.skip(9);
mplew.writeInt(CHAR_MAGIC_SPAWN);
@@ -2107,7 +2119,7 @@ public class MaplePacketCreator {
if (ring != null) {
mplew.writeInt(chr.getId());
mplew.writeInt(ring.getPartnerChrId());
mplew.writeInt(ring.getRingId());
mplew.writeInt(ring.getItemId());
}
}
@@ -3967,21 +3979,24 @@ public class MaplePacketCreator {
}
public static byte[] damageMonster(int oid, int damage) {
return damageMonster(oid, damage, 0, 0);
}
public static byte[] healMonster(int oid, int heal, int curhp, int maxhp) {
return damageMonster(oid, -heal, curhp, maxhp);
}
private static byte[] damageMonster(int oid, int damage, int curhp, int maxhp) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.DAMAGE_MONSTER.getValue());
mplew.writeInt(oid);
mplew.write(0);
mplew.writeInt(damage);
mplew.write(0);
mplew.write(0);
mplew.write(0);
mplew.writeInt(curhp);
mplew.writeInt(maxhp);
return mplew.getPacket();
}
public static byte[] healMonster(int oid, int heal) {
return damageMonster(oid, -heal);
}
public static byte[] updateBuddylist(Collection<BuddylistEntry> buddylist) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.BUDDYLIST.getValue());

View File

@@ -11223,6 +11223,7 @@
</canvas>
<int name="price" value="1"/>
<int name="notSale" value="1"/>
<int name="pquest" value="1"/>
</imgdir>
</imgdir>
<imgdir name="04032095">
@@ -11237,6 +11238,7 @@
</canvas>
<int name="price" value="1"/>
<int name="notSale" value="1"/>
<int name="pquest" value="1"/>
</imgdir>
</imgdir>
<imgdir name="04032096">

File diff suppressed because it is too large Load Diff

View File

@@ -1112,29 +1112,21 @@
</imgdir>
</imgdir>
<imgdir name="reactor">
<imgdir name="0">
<string name="id" value="8892000"/>
<imgdir name="1">
<string name="id" value="9400301"/>
<int name="x" value="-178"/>
<int name="y" value="32"/>
<int name="reactorTime" value="-1"/>
<int name="f" value="0"/>
<string name="name" value="snow00"/>
<string name="name" value="snowmanFakeCollector"/>
</imgdir>
<imgdir name="1">
<string name="id" value="8892001"/>
<int name="x" value="-188"/>
<int name="y" value="146"/>
<imgdir name="0">
<string name="id" value="9400300"/>
<int name="x" value="-178"/>
<int name="y" value="32"/>
<int name="reactorTime" value="-1"/>
<int name="f" value="0"/>
<string name="name" value="mob00"/>
</imgdir>
<imgdir name="2">
<string name="id" value="8898000"/>
<int name="x" value="-182"/>
<int name="y" value="31"/>
<int name="reactorTime" value="0"/>
<int name="f" value="1"/>
<string name="name" value="snow01"/>
<string name="name" value="snowmanCollector"/>
</imgdir>
</imgdir>
<imgdir name="snowMan">

View File

@@ -1281,28 +1281,20 @@
</imgdir>
<imgdir name="reactor">
<imgdir name="0">
<string name="id" value="8892002"/>
<string name="id" value="9400300"/>
<int name="x" value="-178"/>
<int name="y" value="32"/>
<int name="reactorTime" value="-1"/>
<int name="f" value="0"/>
<string name="name" value="snow10"/>
<string name="name" value="snowmanCollector"/>
</imgdir>
<imgdir name="1">
<string name="id" value="8892003"/>
<int name="x" value="-188"/>
<int name="y" value="146"/>
<string name="id" value="9400301"/>
<int name="x" value="-178"/>
<int name="y" value="32"/>
<int name="reactorTime" value="-1"/>
<int name="f" value="0"/>
<string name="name" value="mob10"/>
</imgdir>
<imgdir name="2">
<string name="id" value="8898000"/>
<int name="x" value="-182"/>
<int name="y" value="31"/>
<int name="reactorTime" value="0"/>
<int name="f" value="1"/>
<string name="name" value="snow01"/>
<string name="name" value="snowmanFakeCollector"/>
</imgdir>
</imgdir>
<imgdir name="snowMan">

View File

@@ -1225,28 +1225,20 @@
</imgdir>
<imgdir name="reactor">
<imgdir name="0">
<string name="id" value="8892004"/>
<string name="id" value="9400300"/>
<int name="x" value="-178"/>
<int name="y" value="32"/>
<int name="reactorTime" value="-1"/>
<int name="f" value="0"/>
<string name="name" value="snow20"/>
<string name="name" value="snowmanCollector"/>
</imgdir>
<imgdir name="1">
<string name="id" value="8892005"/>
<int name="x" value="-188"/>
<int name="y" value="146"/>
<string name="id" value="9400301"/>
<int name="x" value="-178"/>
<int name="y" value="32"/>
<int name="reactorTime" value="-1"/>
<int name="f" value="0"/>
<string name="name" value="mob20"/>
</imgdir>
<imgdir name="2">
<string name="id" value="8898000"/>
<int name="x" value="-182"/>
<int name="y" value="31"/>
<int name="reactorTime" value="0"/>
<int name="f" value="1"/>
<string name="name" value="snow01"/>
<string name="name" value="snowmanFakeCollector"/>
</imgdir>
</imgdir>
<imgdir name="snowMan">

View File

@@ -27173,7 +27173,6 @@
<imgdir name="28002">
<imgdir name="0">
<int name="npc" value="9105003"/>
<string name="end" value="2008123100"/>
</imgdir>
<imgdir name="1">
<int name="npc" value="2020005"/>
@@ -27188,7 +27187,6 @@
<imgdir name="28003">
<imgdir name="0">
<int name="npc" value="2020005"/>
<string name="end" value="2008123100"/>
<imgdir name="quest">
<imgdir name="0">
<int name="id" value="28002"/>
@@ -27212,7 +27210,6 @@
</imgdir>
<imgdir name="0">
<int name="npc" value="9105003"/>
<string name="end" value="2008123100"/>
<string name="startscript" value="q28004s"/>
<imgdir name="quest">
<imgdir name="0">
@@ -27220,7 +27217,8 @@
<int name="state" value="2"/>
</imgdir>
</imgdir>
<int name="interval" value="0"/>
<int name="lvmin" value="21"/>
<int name="lvmax" value="51"/>
</imgdir>
</imgdir>
<imgdir name="28102">

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<imgdir name="9400300.img">
<imgdir name="info">
<string name="info" value="로미오와줄리엣:비이커"/>
</imgdir>
<imgdir name="0">
<canvas name="0" width="1" height="1">
<vector name="origin" x="33" y="80"/>
<int name="z" value="0"/>
</canvas>
<imgdir name="event">
<imgdir name="0">
<int name="type" value="100"/>
<int name="state" value="0"/>
<int name="0" value="4032094"/>
<int name="1" value="1"/>
<vector name="lt" x="-220" y="-24"/>
<vector name="rb" x="220" y="9"/>
</imgdir>
</imgdir>
<imgdir name="hit">
<uol name="0" value="../0"/>
</imgdir>
</imgdir>
<imgdir name="1">
<canvas name="0" width="67" height="81">
<vector name="origin" x="33" y="80"/>
<int name="z" value="0"/>
<int name="delay" value="150"/>
</canvas>
<imgdir name="event">
<imgdir name="0">
<int name="type" value="101"/>
<int name="state" value="1"/>
</imgdir>
</imgdir>
</imgdir>
</imgdir>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<imgdir name="9400301.img">
<imgdir name="info">
<string name="info" value="로미오와줄리엣:비이커"/>
</imgdir>
<imgdir name="0">
<canvas name="0" width="1" height="1">
<vector name="origin" x="33" y="80"/>
<int name="z" value="0"/>
</canvas>
<imgdir name="event">
<imgdir name="0">
<int name="type" value="100"/>
<int name="state" value="0"/>
<int name="0" value="4032095"/>
<int name="1" value="1"/>
<vector name="lt" x="-220" y="-24"/>
<vector name="rb" x="220" y="9"/>
</imgdir>
</imgdir>
<imgdir name="hit">
<uol name="0" value="../0"/>
</imgdir>
</imgdir>
<imgdir name="1">
<canvas name="0" width="67" height="81">
<vector name="origin" x="33" y="80"/>
<int name="z" value="0"/>
<int name="delay" value="150"/>
</canvas>
<imgdir name="event">
<imgdir name="0">
<int name="type" value="101"/>
<int name="state" value="1"/>
</imgdir>
</imgdir>
</imgdir>
</imgdir>

View File

@@ -8614,33 +8614,43 @@
</imgdir>
<imgdir name="889100000">
<string name="mapName" value="Entrance - Snow Man&apos;s Land"/>
<string name="streetName" value="Snow Man&apos;s Land"/>
</imgdir>
<imgdir name="889100001">
<string name="mapName" value="Snow Man&apos;s Land"/>
<string name="streetName" value="Snow Man&apos;s Land"/>
</imgdir>
<imgdir name="889100002">
<string name="mapName" value="Exit - Snow Man&apos;s Land"/>
<string name="streetName" value="Snow Man&apos;s Land"/>
</imgdir>
<imgdir name="889100010">
<string name="mapName" value="Entrance - Snow Man&apos;s Land"/>
<string name="streetName" value="Snow Man&apos;s Land"/>
</imgdir>
<imgdir name="889100011">
<string name="mapName" value="Snow Man&apos;s Land"/>
<string name="streetName" value="Snow Man&apos;s Land"/>
</imgdir>
<imgdir name="889100012">
<string name="mapName" value="Exit - Snow Man&apos;s Land"/>
<string name="streetName" value="Snow Man&apos;s Land"/>
</imgdir>
<imgdir name="889100020">
<string name="mapName" value="Entrance - Snow Man&apos;s Land"/>
<string name="streetName" value="Snow Man&apos;s Land"/>
</imgdir>
<imgdir name="889100021">
<string name="mapName" value="Snow Man&apos;s Land"/>
<string name="streetName" value="Snow Man&apos;s Land"/>
</imgdir>
<imgdir name="889100022">
<string name="mapName" value="Exit - Snow Man&apos;s Land"/>
<string name="streetName" value="Snow Man&apos;s Land"/>
</imgdir>
<imgdir name="889100100">
<string name="mapName" value="Path to Snow Man&apos;s Land"/>
<string name="streetName" value="Snow Man&apos;s Land"/>
</imgdir>
<imgdir name="680100000">
<string name="streetName" value="Maple 7th Day Market"/>