Implemented Kites, PlayerNPCs and C. Shop Surprise & Tweaked login
Added code support for Kites. Reviewed concurrent access issues with pet autopot. Addressed PlayerStorage issue where characters would not be properly deregistered from channel PlayerStorage in certain situations. Implemented harp quest (questid 3314) because of reasons. Added SFX to signalize KC/NLC subway departing/approaching. Changed traveling time values to work similarly to GMS. Properly developed the PlayerNPC feature in the source. Added autodeployable PlayerNPC system and Hall of Fame. Solved a glitch with NLC mayor's quiz questline that would allow a player to restart the quiz as many times one would see fit. Added a custom server flag that allows overwriting the ToT 999 mobs to a new value (technically it doesn't overwrite, rather sets the player at quest start with 999 - n credited mobs). Fixed permanent pets expiring after a while. Added code support for Cash Shop Surprise item. Reviewed login handler system as a whole, protecting many exposed flaws. Solved a bug with ULTRA_THREE_SNAILS sometimes taking wrong etc shell from inventory.
This commit is contained in:
@@ -1,33 +0,0 @@
|
||||
// @Author: Resinate
|
||||
|
||||
var towns = new Array(800020120, 251010102, 260010201, 107000300, 200010300, 100040105, 100040106, 261030000, 110040000, 250010504, 240040401, 104000400, 222010310, 230040420, 230040420, 230020100, 105090310, 101030404, 250010304, 220050100, 220050000, 220050200, 221040301);
|
||||
var spawns = new Array(6090002, 5220004, 3220001, 6220000, 8220000, 5220002, 5220002, 8220002, 5220001, 7220002, 8220003, 2220000, 7220001, 8510000, 8520000, 4220001, 8220008, 3220000, 7220000, 5220003, 5220003, 5220003, 6220001);
|
||||
var x = new Array(560, 560, 645, 90, 208, 456, 474, -300, 200, 400, 0, 400, 0, 527, 138, 0, -626, 800, -300, -300, 0, 0, -4224);
|
||||
var y = new Array(50, 50, 275, 119, 83, 278, 278, 180, 140, 540, 1125, 455, 33, -437, 138, 520, -604, 1280, 390, 1030, 1030, 1030, 776);
|
||||
var mapObj;
|
||||
var mobObj;
|
||||
|
||||
function init() {
|
||||
scheduleNew();
|
||||
}
|
||||
|
||||
function scheduleNew() {
|
||||
setupTask = em.schedule("start", 0);
|
||||
}
|
||||
|
||||
function cancelSchedule() {
|
||||
if (setupTask != null)
|
||||
setupTask.cancel(true);
|
||||
}
|
||||
|
||||
function start() {
|
||||
var time = (Math.floor(Math.random() * 10) + 10) * (60 * 1000);
|
||||
for(var i = 0; i < towns.length; i++) {
|
||||
mapObj = em.getChannelServer().getMapFactory().getMap(towns[i]);
|
||||
mobObj = Packages.server.life.MapleLifeFactory.getMonster(spawns[i]);
|
||||
if(mapObj.getMonsterById(spawns[i]) == null) {
|
||||
mapObj.spawnMonsterOnGroundBelow(mobObj, new Packages.java.awt.Point(x[i],y[i]));
|
||||
}
|
||||
}
|
||||
em.schedule("start", time);
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 505 KiB |
@@ -1,3 +0,0 @@
|
||||
Change reason: New NPC files uploaded in with misleading header.
|
||||
|
||||
Committed 2017-09-01 (Reworked Autoassigner & Hero's Will & Trade + Visual NX + New commands)
|
||||
@@ -1,5 +0,0 @@
|
||||
Change reason: New NPC files uploaded in with misleading/missing header.
|
||||
|
||||
9000017.js: Introduced before repository started, around 2015-08-05 (extracted from mychanges_ptbr.txt @ "source" commit)
|
||||
9000036.js: Introduced before repository started, around 2015-07-25 (extracted from mychanges_ptbr.txt @ "source" commit)
|
||||
9000041.js: Committed 2016-07-18 (Instant sell NPC + new features)
|
||||
@@ -1,281 +0,0 @@
|
||||
/*
|
||||
* This file is part of the OdinMS Maple Story Server
|
||||
Copyright (C) 2008 Patrick Huy <[email protected]>
|
||||
Matthias Butz <[email protected]>
|
||||
Jan Christian Meyer <[email protected]>
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License 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: Sharenian Guild PQ
|
||||
*/
|
||||
|
||||
var isPq = true;
|
||||
var minPlayers = 1, maxPlayers = 30;
|
||||
var minLevel = 1, maxLevel = 200;
|
||||
var entryMap = 990000000;
|
||||
var exitMap = 990001100;
|
||||
var recruitMap = 101030104;
|
||||
var clearMap = 990001101;
|
||||
|
||||
var minMapId = 990000000;
|
||||
var maxMapId = 990001101;
|
||||
|
||||
var waitTime = 3;
|
||||
var eventTime = 90; // 90 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 = [1032033, 4001024, 4001025, 4001026, 4001027, 4001028, 4001029, 4001030, 4001031, 4001032, 4001033, 4001034, 4001035, 4001037];
|
||||
eim.setExclusiveItems(itemSet);
|
||||
}
|
||||
|
||||
function setEventRewards(eim) {
|
||||
var itemSet, itemQty, evLevel, expStages;
|
||||
|
||||
evLevel = 1; //Rewards at clear PQ
|
||||
itemSet = [];
|
||||
itemQty = [];
|
||||
eim.setEventRewards(evLevel, itemSet, itemQty);
|
||||
|
||||
expStages = []; //bonus exp given on CLEAR stage signal
|
||||
eim.setEventClearStageExp(expStages);
|
||||
}
|
||||
|
||||
function getEligibleParty(party) { //selects, from the given party, the team that is allowed to attempt this event
|
||||
var eligible = [];
|
||||
var hasLeader = false;
|
||||
|
||||
var guildId = 0;
|
||||
|
||||
if(party.size() > 0) {
|
||||
var partyList = party.toArray();
|
||||
|
||||
for(var i = 0; i < party.size(); i++) {
|
||||
var ch = partyList[i];
|
||||
if(ch.isLeader()) {
|
||||
guildId = ch.getGuildId();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for(var i = 0; i < party.size(); i++) {
|
||||
var ch = partyList[i];
|
||||
|
||||
if(ch.getMapId() == recruitMap && ch.getLevel() >= minLevel && ch.getLevel() <= maxLevel && ch.getGuildId() == guildId) {
|
||||
if(ch.isLeader()) hasLeader = true;
|
||||
eligible.push(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!(hasLeader)) eligible = [];
|
||||
return eligible;
|
||||
}
|
||||
|
||||
function setup(level, lobbyid) {
|
||||
var eim = em.newInstance("Guild" + lobbyid);
|
||||
eim.setProperty("level", level);
|
||||
|
||||
eim.setProperty("guild", 0);
|
||||
eim.setProperty("canJoin", 1);
|
||||
eim.setProperty("statusStg1", -1);
|
||||
|
||||
eim.getInstanceMap(990000000).resetPQ(level);
|
||||
eim.getInstanceMap(990000100).resetPQ(level);
|
||||
eim.getInstanceMap(990000200).resetPQ(level);
|
||||
eim.getInstanceMap(990000300).resetPQ(level);
|
||||
eim.getInstanceMap(990000301).resetPQ(level);
|
||||
eim.getInstanceMap(990000400).resetPQ(level);
|
||||
eim.getInstanceMap(990000401).resetPQ(level);
|
||||
eim.getInstanceMap(990000410).resetPQ(level);
|
||||
eim.getInstanceMap(990000420).resetPQ(level);
|
||||
eim.getInstanceMap(990000430).resetPQ(level);
|
||||
eim.getInstanceMap(990000431).resetPQ(level);
|
||||
eim.getInstanceMap(990000440).resetPQ(level);
|
||||
eim.getInstanceMap(990000500).resetPQ(level);
|
||||
eim.getInstanceMap(990000501).resetPQ(level);
|
||||
eim.getInstanceMap(990000502).resetPQ(level);
|
||||
eim.getInstanceMap(990000600).resetPQ(level);
|
||||
eim.getInstanceMap(990000610).resetPQ(level);
|
||||
eim.getInstanceMap(990000611).resetPQ(level);
|
||||
eim.getInstanceMap(990000620).resetPQ(level);
|
||||
eim.getInstanceMap(990000630).resetPQ(level);
|
||||
eim.getInstanceMap(990000631).resetPQ(level);
|
||||
eim.getInstanceMap(990000640).resetPQ(level);
|
||||
eim.getInstanceMap(990000641).resetPQ(level);
|
||||
eim.getInstanceMap(990000700).resetPQ(level);
|
||||
eim.getInstanceMap(990000800).resetPQ(level);
|
||||
eim.getInstanceMap(990000900).resetPQ(level);
|
||||
eim.getInstanceMap(990001000).resetPQ(level);
|
||||
eim.getInstanceMap(990001100).resetPQ(level);
|
||||
eim.getInstanceMap(990001101).resetPQ(level);
|
||||
|
||||
respawnStages(eim);
|
||||
|
||||
var ts = Date.now();
|
||||
ts += (60000 * waitTime);
|
||||
eim.setProperty("entryTimestamp", "" + ts);
|
||||
|
||||
eim.startEventTimer(waitTime * 60000);
|
||||
|
||||
setEventRewards(eim);
|
||||
setEventExclusives(eim);
|
||||
|
||||
return eim;
|
||||
}
|
||||
|
||||
/*
|
||||
function isTeamAllJobs(eim) {
|
||||
var eventJobs = eim.getEventPlayersJobs();
|
||||
var rangeJobs = parseInt('111110', 2);
|
||||
|
||||
return ((eventJobs & rangeJobs) == rangeJobs);
|
||||
}
|
||||
*/
|
||||
|
||||
function afterSetup(eim) {
|
||||
eim.setProperty("guild", "" + eim.getLeader().getGuildId());
|
||||
}
|
||||
|
||||
function respawnStages(eim) {}
|
||||
|
||||
function playerEntry(eim, player) {
|
||||
var map = eim.getMapInstance(entryMap);
|
||||
player.changeMap(map, map.getPortal(0));
|
||||
|
||||
var texttt = "So, here is the brief. You guys should be warned that, once out on the fortress outskirts, anyone that would not be equipping the #b#t1032033##k will die instantly due to the deteriorated state of the air around there. That being said, once your team move out to the next stage, make sure to #bhit the glowing rocks#k in that region and #bequip the dropped item#k before advancing stages. That will protect you thoroughly from the air sickness. Good luck!";
|
||||
player.getClient().getSession().write(Packages.tools.MaplePacketCreator.getNPCTalk(9040000, /*(byte)*/ 0, texttt, "00 00", /*(byte)*/ 0));
|
||||
}
|
||||
|
||||
function scheduledTimeout(eim) {
|
||||
if(eim.getIntProperty("canJoin") == 1) {
|
||||
eim.setProperty("canJoin", 0);
|
||||
|
||||
if(eim.checkEventTeamLacking(true, minPlayers)) {
|
||||
end(eim);
|
||||
} else {
|
||||
eim.startEventTimer(eventTime * 60000);
|
||||
}
|
||||
} else {
|
||||
end(eim);
|
||||
}
|
||||
}
|
||||
|
||||
function playerUnregistered(eim, player) {}
|
||||
|
||||
function playerExit(eim, player) {
|
||||
eim.unregisterPlayer(player);
|
||||
player.changeMap(exitMap, 0);
|
||||
}
|
||||
|
||||
function changedMap(eim, player, mapid) {
|
||||
if (mapid < minMapId || mapid > maxMapId) {
|
||||
if (eim.isEventTeamLackingNow(true, minPlayers, player) && eim.getIntProperty("canJoin") == 0) {
|
||||
eim.unregisterPlayer(player);
|
||||
end(eim);
|
||||
}
|
||||
else
|
||||
eim.unregisterPlayer(player);
|
||||
}
|
||||
}
|
||||
|
||||
function changedLeader(eim, leader) {}
|
||||
|
||||
function playerDead(eim, player) {}
|
||||
|
||||
function playerRevive(eim, player) { // player presses ok on the death pop up.
|
||||
if (eim.isEventTeamLackingNow(true, minPlayers, player) && eim.getIntProperty("canJoin") == 0) {
|
||||
eim.unregisterPlayer(player);
|
||||
end(eim);
|
||||
}
|
||||
else
|
||||
eim.unregisterPlayer(player);
|
||||
}
|
||||
|
||||
function playerDisconnected(eim, player) {
|
||||
if (eim.isEventTeamLackingNow(true, minPlayers, player) && eim.getIntProperty("canJoin") == 0) {
|
||||
eim.unregisterPlayer(player);
|
||||
end(eim);
|
||||
}
|
||||
else
|
||||
eim.unregisterPlayer(player);
|
||||
}
|
||||
|
||||
function leftParty(eim, player) {}
|
||||
|
||||
function disbandParty(eim) {
|
||||
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 monsterKilled(mob, eim) {}
|
||||
|
||||
function allMonstersDead(eim) {}
|
||||
|
||||
function cancelSchedule() {}
|
||||
|
||||
function dispose(eim) {
|
||||
em.schedule("reopenGuildQuest", em.getLobbyDelay() * 1.5 * 1000);
|
||||
}
|
||||
|
||||
function reopenGuildQuest() {
|
||||
em.attemptStartGuildInstance();
|
||||
}
|
||||
@@ -1,208 +0,0 @@
|
||||
/*
|
||||
* This file is part of the OdinMS Maple Story Server
|
||||
Copyright (C) 2008 Patrick Huy <[email protected]>
|
||||
Matthias Butz <[email protected]>
|
||||
Jan Christian Meyer <[email protected]>
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License 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 Lerk
|
||||
*
|
||||
* Guild Quest
|
||||
*/
|
||||
|
||||
var exitMap;
|
||||
var waitingListCurrent = 0;
|
||||
|
||||
importPackage(Packages.world);
|
||||
importPackage(Packages.client);
|
||||
importPackage(Packages.server.maps);
|
||||
importPackage(java.lang);
|
||||
|
||||
function init() {
|
||||
em.setProperty("shuffleReactors","false");
|
||||
em.setProperty("canEnter", "true");
|
||||
em.setProperty("gpqOpen", "true");
|
||||
}
|
||||
|
||||
function monsterValue(eim, mobId) { //should only trigger on ergoth
|
||||
if (mobId == 9300028) { //but, just to be safe...
|
||||
var rubian = new Packages.client.inventory.Item(4001024, 0, 1);
|
||||
var map = eim.getMapInstance(990000900);
|
||||
var reactor = map.getReactorByName("boss");
|
||||
map.spawnItemDrop(reactor, eim.getPlayers().get(0), rubian, reactor.getPosition(), true, false);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function setup(eim) {
|
||||
exitMap = em.getChannelServer().getMapFactory().getMap(990001100); //returning path
|
||||
|
||||
//shuffle reactors in two maps for stage 3
|
||||
eim.getMapInstance(990000501).shuffleReactors();
|
||||
eim.getMapInstance(990000502).shuffleReactors();
|
||||
|
||||
//force no-respawn on certain map reactors
|
||||
eim.getMapInstance(990000611).getReactorByName("").setDelay(-1);
|
||||
eim.getMapInstance(990000620).getReactorByName("").setDelay(-1);
|
||||
eim.getMapInstance(990000631).getReactorByName("").setDelay(-1);
|
||||
eim.getMapInstance(990000641).getReactorByName("").setDelay(-1);
|
||||
|
||||
//activate three minutes after start
|
||||
eim.setProperty("entryTimestamp", Packages.java.lang.System.currentTimeMillis());
|
||||
eim.setProperty("canEnter","true");
|
||||
eim.schedule("begin", 60000);
|
||||
eim.startEventTimer(60000);
|
||||
}
|
||||
|
||||
function begin(eim) {
|
||||
eim.setProperty("canEnter","false");
|
||||
var party = eim.getPlayers();
|
||||
//if (party.size() < 6) { //not enough to start
|
||||
// end(eim,"There are no longer enough players to continue, and those remaining shall be warped out.");
|
||||
//} else {
|
||||
var iter = party.iterator();
|
||||
while (iter.hasNext()) {
|
||||
iter.next().dropMessage(6,"The quest has begun.");
|
||||
}
|
||||
|
||||
eim.startEventTimer(1000 * 60 * 90);
|
||||
eim.schedule("timeOut", 1000 * 60 * 90);
|
||||
//}
|
||||
}
|
||||
|
||||
function timeOut(eim) {
|
||||
end(eim, "Your allotted time to finish the quest has passed.");
|
||||
}
|
||||
|
||||
function playerEntry(eim, player) {
|
||||
var map = eim.getMapInstance(990000000);
|
||||
player.changeMap(map, map.getPortal(0));
|
||||
}
|
||||
|
||||
function playerRevive(eim, player) {
|
||||
var returnMap = 990000200;
|
||||
if (eim.getProperty("canEnter").equals("true")) {
|
||||
returnMap = 990000000;
|
||||
}
|
||||
player.setHp(50);
|
||||
player.setStance(0);
|
||||
player.changeMap(eim.getMapInstance(returnMap), eim.getMapInstance(returnMap).getPortal(0));
|
||||
return false;
|
||||
}
|
||||
|
||||
function playerDead(eim, player) {
|
||||
}
|
||||
|
||||
function playerDisconnected(eim, player) {
|
||||
var party = eim.getPlayers();
|
||||
if (player.getName().equals(eim.getProperty("leader"))) { //check for party leader
|
||||
//boot all players and end
|
||||
var iter = party.iterator();
|
||||
while (iter.hasNext()) {
|
||||
var pl = iter.next();
|
||||
pl.dropMessage(6,"The leader of the instance has disconnected, and the remaining players shall be warped out.");
|
||||
if (pl.equals(player)) {
|
||||
removePlayer(eim, pl);
|
||||
}
|
||||
else {
|
||||
eim.unregisterPlayer(pl);
|
||||
pl.changeMap(exitMap, exitMap.getPortal(0));
|
||||
}
|
||||
}
|
||||
eim.dispose();
|
||||
}
|
||||
else { //boot d/ced player and check if enough players left
|
||||
removePlayer(eim, player);
|
||||
if (party.size() < 6) { //five after player booted
|
||||
end(eim,"There are no longer enough players to continue, and those remaining shall be warped out.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function leftParty(eim, player) { //ignore for GQ
|
||||
}
|
||||
|
||||
function disbandParty(eim) { //ignore for GQ
|
||||
}
|
||||
|
||||
function playerExit(eim, player) {
|
||||
eim.unregisterPlayer(player);
|
||||
player.changeMap(exitMap, exitMap.getPortal(0));
|
||||
var party = eim.getPlayers();
|
||||
if (party.size() < 6) { //five after player booted
|
||||
end(eim,"There are no longer enough players to continue, and those remaining shall be warped out.");
|
||||
}
|
||||
}
|
||||
|
||||
function end(eim, msg) {
|
||||
var iter = eim.getPlayers().iterator();
|
||||
while (iter.hasNext()) {
|
||||
var player = iter.next();
|
||||
player.dropMessage(6,msg);
|
||||
eim.unregisterPlayer(player);
|
||||
player.changeMap(exitMap, exitMap.getPortal(0));
|
||||
}
|
||||
eim.dispose();
|
||||
}
|
||||
|
||||
//for offline players
|
||||
function removePlayer(eim, player) {
|
||||
eim.unregisterPlayer(player);
|
||||
player.getMap().removePlayer(player);
|
||||
player.setMap(exitMap);
|
||||
}
|
||||
|
||||
function clearPQ(eim) {
|
||||
var iter = eim.getPlayers().iterator();
|
||||
var bonusMap = eim.getMapInstance(990001000);
|
||||
eim.startEventTimer(40000);
|
||||
while (iter.hasNext()) {
|
||||
var player = iter.next();
|
||||
player.changeMap(bonusMap, bonusMap.getPortal(0));
|
||||
}
|
||||
eim.schedule("finish", 40000)
|
||||
}
|
||||
|
||||
function finish(eim) {
|
||||
var iter = eim.getPlayers().iterator();
|
||||
while (iter.hasNext()) {
|
||||
var player = iter.next();
|
||||
eim.unregisterPlayer(player);
|
||||
player.changeMap(exitMap, exitMap.getPortal(0));
|
||||
}
|
||||
eim.dispose();
|
||||
}
|
||||
|
||||
function allMonstersDead(eim) {
|
||||
//do nothing; GQ has nothing to do with monster killing
|
||||
}
|
||||
|
||||
function cancelSchedule() {
|
||||
}
|
||||
|
||||
function dispose(eim) {
|
||||
em.schedule("openGPQ", 5000);
|
||||
}
|
||||
|
||||
function openGPQ() {
|
||||
em.setProperty("gpqOpen", "true");
|
||||
}
|
||||
|
||||
|
||||
function timeOut() {
|
||||
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
Change reason: Rewrote script file as a whole, now using methods suitable for the server's improved EventManager (queue system and such).
|
||||
|
||||
Committed 2017-06-13 (GuildPQ Queue system + revamped Warp mechanic)
|
||||
@@ -1,14 +0,0 @@
|
||||
[COMMIT 198]
|
||||
|
||||
Licenses for the following files are being changed due to misleading authorship:
|
||||
- CafePQ NPCs: those NPC script files were not around on OdinMS server, neither before HeavenMS.
|
||||
- GuildQuest.js: that event script file was completely rewritten to suit well the underlying EIM of HeavenMS.
|
||||
- Custom NPCs 9000017, 9000036, 9000041: those NPC script files were not around on OdinMS server, neither on Solaxia.
|
||||
|
||||
Original dates are summarized on the subfolders, for steadfast conference.
|
||||
|
||||
Licenses for the following files are being added:
|
||||
- Several PQs and boss battles: those event scripts are authored by myself, all following similar pattern and using
|
||||
common functions introduced on this server (such as getEligibleParty, startEventTimer or isEventTeamLackingNow).
|
||||
|
||||
I am expecting no infringements from this action, should any issues arise contact me asap.
|
||||
+17
-4
@@ -25,23 +25,26 @@ PQs:
|
||||
* HPQ/KPQ/LPQ/LMPQ/OPQ/EllinPQ/PiratePQ/MagatiaPQ/HorntailPQ/AmoriaPQ/TreasurePQ.
|
||||
* CWKPQ as Expedition-based event.
|
||||
* Expeditions: Scarga/Horntail/Showa/Balrog/Zakum/Pinkbean.
|
||||
* GuildPQ 100% + Guild queue with multi-lobby systems available.
|
||||
* GuildPQ + Guild queue with multi-lobby systems available.
|
||||
* Brand-new PQs: BossRushPQ, CafePQ.
|
||||
* Mu Lung Dojo.
|
||||
* Capt. Latanica remade as an event (parties can now fight the boss).
|
||||
|
||||
Skills:
|
||||
|
||||
* Some skills behaving oddly have been patched, such as Venomous Star/Stab and Mystic Doors.
|
||||
* Maker skill features properly developed.
|
||||
* Server is using heurisitics to calculate fee costs for the Maker (errors sums up to 8k mesos, reagent errors stacks up comformant with it's level).
|
||||
* 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.
|
||||
|
||||
Quests:
|
||||
|
||||
* Doll house quest functional.
|
||||
* Quests can now reward properly items when matching a reward item with the player's job.
|
||||
* Loads of quests have been patched.
|
||||
* Quest rewards according to jobs works properly.
|
||||
* Reward selection and randomed reward works properly.
|
||||
* Loads of quests have been patched.
|
||||
* Lots of job questlines (rewarding skills) have been patched/implemented.
|
||||
* Enchanced rewarding system: checks for stacking opportunities on the inventory before checking for new slots.
|
||||
* Complete overhaul on the 3rd job quiz (explorers), with all 40-question pool now made available.
|
||||
|
||||
@@ -52,6 +55,8 @@ Player Social Network:
|
||||
* Enhanced synchronization on Player Shops and Hired Merchants. Transactions made are instantly informed to the owner.
|
||||
* Game minirooms such as match cards and omok now has semi-functional password system.
|
||||
* Item pickup cooldown on non-owned/non-partyowned items functional.
|
||||
* Further improved the server's ranking system, to now display properly daily player ranking movement.
|
||||
* Automated support for Player NPCs and Hall of Fame.
|
||||
|
||||
Cash & Items:
|
||||
|
||||
@@ -65,6 +70,8 @@ Cash & Items:
|
||||
* Owl of Minerva.
|
||||
* Pet item ignore.
|
||||
* New Year's card (New Year effect sometimes d/c's a player).
|
||||
* Kite.
|
||||
* Cash Shop Surprise.
|
||||
|
||||
Monsters, Maps & Reactors:
|
||||
|
||||
@@ -76,6 +83,7 @@ Monsters, Maps & Reactors:
|
||||
* Added Boss HP Bar for dozens of bosses (needs provided custom wz).
|
||||
* If multiple bosses are on the same area, client will prioritize Boss HP bar of the target of the player.
|
||||
* Boats, elevator and other travelling mechanics fully working.
|
||||
* Crimson Balrog boat approaching visual effect made functional.
|
||||
* PQs, Taxis and other event-driven situations warps players at random spawnpoints, GMS-like.
|
||||
* Some reactors (PQ bonus boxes) now sprays items on the map, instead of dropping everything at once.
|
||||
* Updated Crimsonwood, World Tour, Nihal Desert and Neo City, enabling quest completion and game progression in these areas.
|
||||
@@ -102,6 +110,7 @@ Server potentials:
|
||||
* Enhanced auto-pot system: pet uses as many potions as necessary to reach the desired threshold.
|
||||
* Enhanced buff system: smartly checks for the best available buff effects to be active on the player.
|
||||
* Enhanced AP auto-assigner: exactly matches AP with the needed for the player's current level, surplus assigned to the primary attribute.
|
||||
* Pet item pickup now gives preference to player attacks rather than forcing attack disables when automatically picking up.
|
||||
* Channel capacity bar functional and world servers with max capacity checks.
|
||||
* Disease status are now visible for other players, even when changing maps.
|
||||
* Poison damage value are now visible for other players.
|
||||
@@ -110,6 +119,7 @@ Server potentials:
|
||||
* Delete Character (requires ENABLE_PIC activated).
|
||||
* Autosaver (periodically saves on DB current state of every player in-game).
|
||||
* Both fixed and randomized versions of HP/MP growth rate available, regarding player job (enable one at ServerConstants). Placeholder for HP/MP washing feature.
|
||||
* Reallocated mapobjectids utilization throughout the source, preventing issues such as "NPC disappearing mysteriously after some server time" from happening.
|
||||
* 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.
|
||||
|
||||
@@ -131,9 +141,9 @@ External tools:
|
||||
* MapleMesoFetcher - Creates meso drop data for mobs with more than 4 items (thus overworld mobs), calculations based on mob level and whether it's a boss or not.
|
||||
* MapleMobBookIndexer - Generates a SQL table with all relations of cardid and mobid present in the mob book.
|
||||
* MapleMobBookUpdate - Generates a wz.xml that is a copy of the original MonsterBook.wz.xml, except it updates the drop data info in the book with those currently on DB.
|
||||
* MapleQuestlineFetcher - Searches the quest WZ files and reports in all questids that currently doesn't have script files.
|
||||
* MapleQuestItemCountFetcher - Searches the quest WZ files and reports in all relevant data regarding missing "count" labels on item acts at "complete quest".
|
||||
* MapleQuestItemFetcher - Searches the SQL tables and project files and reports in all relevant data regarding missing/erroneous quest items.
|
||||
* MapleQuestlineFetcher - Searches the quest WZ files and reports in all questids that currently doesn't have script files.
|
||||
* MapleQuestMesoFetcher - Searches the quest WZ files and reports in all relevant data regarding missing/erroneous quest fee checks.
|
||||
* MapleReactorDropFetcher - Searches the DB for reactors with drop data and reports in reactorids that are not yet coded.
|
||||
* MapleSkillMakerFetcher - Updates the DB Maker-related tables with the current info present on the WZs.
|
||||
@@ -145,7 +155,9 @@ Project:
|
||||
* Highly updated drop data.
|
||||
* Highly configurable server (see all server flags at ServerConstants).
|
||||
* 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.
|
||||
* 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.
|
||||
* ThreadTracker: embedded auditing tool for run-time deadlock scanning throughout the server source (relies heavily on memory usage, designed only for debugging purposes).
|
||||
|
||||
@@ -154,6 +166,7 @@ Exploits patched:
|
||||
* Player being given free access to any character of any account once they have authenticated their account on login phase.
|
||||
* Player being given permission to delete any character of any account once they have authenticated their account on login phase.
|
||||
* Player being able to start/complete any quest freely.
|
||||
* Several assynchronous-oriented explots patched, highlights on those involving Fredrick & Duey.
|
||||
|
||||
Localhost:
|
||||
|
||||
|
||||
@@ -1,315 +0,0 @@
|
||||
var DISABLED = false;
|
||||
|
||||
var SavedLocationType = Packages.server.maps.SavedLocationType;
|
||||
|
||||
var MonsterCarnival = Packages.server.partyquest.mcpq.MonsterCarnival;
|
||||
var MCTracker = Packages.server.partyquest.mcpq.MCTracker;
|
||||
var MCParty = Packages.server.partyquest.mcpq.MCParty;
|
||||
var MCField = Packages.server.partyquest.mcpq.MCField;
|
||||
|
||||
var status = -1;
|
||||
var store = false;
|
||||
var ctx = -1;
|
||||
var storeInfo;
|
||||
var purchaseId;
|
||||
var purchaseCost;
|
||||
|
||||
var coinId = 4001129;
|
||||
var coinIcon = "#i" + coinId + "#";
|
||||
var infoMaps = [220000000, 200000000, 103000000, 540000000]; // ludi, orbis, kerning, singapore
|
||||
var gradeS = 600
|
||||
var gradeA = 500
|
||||
var gradeB = 400
|
||||
var gradeC = 300
|
||||
var gradeD = 200
|
||||
var gradeE = 100
|
||||
|
||||
var expRewards = [[150000, 100000], // S Winner/Loser
|
||||
[100000, 70000], // A Winner/Loser
|
||||
[75000, 43250], // B Winner/Loser
|
||||
[50000, 25000], // C Winner/Loser
|
||||
[25000, 12500], // D Winner/Loser
|
||||
[12500, 6250], // E Winner/Loser
|
||||
[5000, 2500] // F Winner/Loser
|
||||
];
|
||||
|
||||
// Exchange stores
|
||||
var warrior = [[1302004, 7], [1402006, 7], [1302009, 10], [1402007, 10],
|
||||
[1302010, 20], [1402003, 20], [1312006, 7], [1412004, 7],
|
||||
[1312007, 10], [1412005, 10], [1312018, 20], [1412003, 20],
|
||||
[1322015, 7], [1422008, 7], [1322016, 10], [1422007, 10],
|
||||
[1322017, 20], [1422005, 20], [1432003, 7], [1442003, 7],
|
||||
[1432005, 10], [1442009, 10], [1442005, 20], [1432004, 20]];
|
||||
|
||||
var magician = [[1372001, 7], [1382018, 7], [1372012, 10], [1382019, 10],
|
||||
[1382001, 20], [1372007, 20]];
|
||||
|
||||
var archer = [[1452006, 7], [1452007, 10], [1452008, 20], [1462005, 7],
|
||||
[1462006, 10], [1462007, 20]];
|
||||
|
||||
var thief = [[1472013, 7], [1472017, 10], [1472021, 20], [1332014, 7],
|
||||
[1332011, 10], [1332031, 10], [1332016, 20], [1332034, 20]];
|
||||
|
||||
var pirate = [[1482005, 7], [1482006, 10], [1482007, 20], [1492005, 7],
|
||||
[1492006, 10], [1492007, 20]];
|
||||
|
||||
var necklace = [[1122007, 50], [2041211, 40]];
|
||||
|
||||
var infoText = "You wish to know about the Monster Carnival? Very well. The Monster Carnival is a place of trilling battles and exciting competiton against people just as strong and motivated as yourself. You must summon monsters and defeat the monsters summoned by the opposing party. That's the essence of the Monster Carnival. Once you enter the Carnival Field, the task is to earn CP by hunter monsters from the opposing party and use those CP's to distract the opposing party from hunting monsters. There are three ways to distract the other party; Summon a Monster, Skill or Protector. Please remember this though, it's never a good idea to save up CP just for the sake of it. The CP's you've used will also help determine the winner and the loser of the carnival.";
|
||||
var no = "You do not have enough Maple Coins for this item. Come back to me when you acquire more!";
|
||||
|
||||
function getGrade(cp) {
|
||||
if (cp >= gradeS) {
|
||||
return 0;
|
||||
} else if (cp >= gradeA) {
|
||||
return 1;
|
||||
} else if (cp >= gradeB) {
|
||||
return 2;
|
||||
} else if (cp >= gradeC) {
|
||||
return 3;
|
||||
} else if (cp >= gradeD) {
|
||||
return 4;
|
||||
} else if (cp >= gradeE) {
|
||||
return 5;
|
||||
} else {
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
|
||||
function isTownMap(map) {
|
||||
for (var i = 0; i < infoMaps.length; i++) {
|
||||
if (infoMaps[i] == map) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isExitMap(map) {
|
||||
return map == 980000010;
|
||||
}
|
||||
|
||||
function isWinnerMap(map) {
|
||||
return (map >= 980000000 && map <= 980000700 && map % 10 == 3);
|
||||
}
|
||||
|
||||
function isLoserMap(map) {
|
||||
return (map >= 980000000 && map <= 980000700 && map % 10 == 4);
|
||||
}
|
||||
|
||||
var CONTEXT_NONE = -1;
|
||||
var CONTEXT_TOWN = 0;
|
||||
var CONTEXT_EXIT = 1;
|
||||
var CONTEXT_WIN = 2;
|
||||
var CONTEXT_LOSE = 3;
|
||||
|
||||
function start() {
|
||||
if (DISABLED) {
|
||||
cm.sendOk("CPQ is temporarily unavailable.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
m = cm.getMapId();
|
||||
if (isTownMap(m)) {
|
||||
ctx = CONTEXT_TOWN;
|
||||
} else if (isExitMap(m)) {
|
||||
ctx = CONTEXT_EXIT;
|
||||
} else if (isWinnerMap(m)) {
|
||||
ctx = CONTEXT_WIN;
|
||||
} else if (isLoserMap(m)) {
|
||||
ctx = CONTEXT_LOSE;
|
||||
} else {
|
||||
ctx = CONTEXT_NONE;
|
||||
}
|
||||
|
||||
action(1, 0, 0);
|
||||
}
|
||||
|
||||
function doLoserMap(mode, type, selection) {
|
||||
if (cm.getPlayer().getMCPQParty() == null) {
|
||||
cm.warp(MonsterCarnival.MAP_LOBBY);
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode == -1) {
|
||||
cm.dispose();
|
||||
} else {
|
||||
if (mode == 1) status++;
|
||||
else status--;
|
||||
|
||||
if (status == 0) {
|
||||
cm.sendNext("Unfortunately, you did not manage to win this round. Better luck next time!");
|
||||
} else if (status == 1) {
|
||||
var points = cm.getPlayer().getMCPQParty().getTotalCP();
|
||||
var grade = getGrade(points);
|
||||
var letterGrade = "ABCDF"[grade];
|
||||
var expReward = expRewards[grade][1];
|
||||
|
||||
cm.sendNext("Your grade is: #b" + letterGrade + "\r\n\r\n#kEXP Reward: " + expReward);
|
||||
cm.gainExp(expReward);
|
||||
} else if (status == 2) {
|
||||
cm.warp(MonsterCarnival.MAP_LOBBY);
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function doWinnerMap(mode, type, selection) {
|
||||
if (cm.getPlayer().getMCPQParty() == null) {
|
||||
cm.warp(MonsterCarnival.MAP_LOBBY);
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode == -1) {
|
||||
cm.dispose();
|
||||
} else {
|
||||
if (mode == 1) status++;
|
||||
else status--;
|
||||
|
||||
if (status == 0) {
|
||||
cm.sendNext("Congratulations! You managed to defeat the enemy team!");
|
||||
} else if (status == 1) {
|
||||
var points = cm.getPlayer().getMCPQParty().getTotalCP();
|
||||
var grade = getGrade(points);
|
||||
var letterGrade = "ABCDF"[grade];
|
||||
var expReward = expRewards[grade][0];
|
||||
|
||||
cm.sendNext("Your grade is: #b" + letterGrade + "\r\n\r\n#kEXP Reward: " + expReward);
|
||||
cm.gainExp(expReward);
|
||||
} else if (status == 2) {
|
||||
cm.warp(MonsterCarnival.MAP_LOBBY);
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function doTown(mode, type, selection) {
|
||||
if (mode == -1) {
|
||||
cm.sendOk("Be sure to vote for the server every 24 hours!");
|
||||
cm.dispose();
|
||||
} else {
|
||||
if (mode == 1) status++;
|
||||
else status--;
|
||||
|
||||
if (status == 0) {
|
||||
cm.sendSimple("What would you like to do? If you have never participated in the Monster Carnival, you'll need to know a thing or two about it before joining.\r\n\r\n#b#L0#Go to the Monster Carnival Field#l\r\n#L1#Learn about the Monster Carnival#l\r\n#L2#Trade Maple Coin#l");
|
||||
} else if (status == 1) {
|
||||
if (selection == 0) {
|
||||
if (cm.getChar().getLevel() < MonsterCarnival.MIN_LEVEL || cm.getChar().getLevel() > MonsterCarnival.MAX_LEVEL) {
|
||||
cm.sendOk("You must be between level " + MonsterCarnival.MIN_LEVEL + " and level " + MonsterCarnival.MAX_LEVEL + " to enter.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
cm.getChar().saveLocation(SavedLocationType.MONSTER_CARNIVAL);
|
||||
cm.warp(MonsterCarnival.MAP_LOBBY, 4);
|
||||
cm.dispose();
|
||||
return;
|
||||
} else if (selection == 1) {
|
||||
cm.sendPrev(infoText);
|
||||
cm.dispose();
|
||||
return;
|
||||
} else if (selection == 2) {
|
||||
store = true;
|
||||
cm.sendSimple("Select a category:\r\n" +
|
||||
"#L101##bTrade Maple Coins for Warrior Weapons\r\n" +
|
||||
"#L102#Trade Maple Coins for Magician Weapons\r\n" +
|
||||
"#L103#Trade Maple Coins for Bowman Weapons\r\n" +
|
||||
"#L104#Trade Maple Coins for Thief Weapons\r\n" +
|
||||
"#L105#Trade Maple Coins for Pirate Weapons\r\n" +
|
||||
"#L106#Trade Maple Coins for a Necklace");
|
||||
}
|
||||
} else if (status == 2) {
|
||||
if (store) {
|
||||
switch (selection) {
|
||||
case 101:
|
||||
storeInfo = warrior;
|
||||
break;
|
||||
case 102:
|
||||
storeInfo = magician;
|
||||
break;
|
||||
case 103:
|
||||
storeInfo = archer;
|
||||
break;
|
||||
case 104:
|
||||
storeInfo = thief;
|
||||
break;
|
||||
case 105:
|
||||
storeInfo = pirate;
|
||||
break;
|
||||
case 106:
|
||||
storeInfo = necklace;
|
||||
break;
|
||||
default:
|
||||
storeInfo = [];
|
||||
}
|
||||
if (storeInfo.length == 0) {
|
||||
cm.sendOk("That store doesn't exist.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
var storeText = "";
|
||||
for (var i = 0; i < storeInfo.length; ++i) {
|
||||
var wepId = storeInfo[i][0];
|
||||
var cost = storeInfo[i][1];
|
||||
storeText += "#L" + i + "##v" + wepId + "# - #z" + wepId + "# - " + cost + " " + coinIcon + "#l\r\n";
|
||||
}
|
||||
cm.sendSimple(storeText);
|
||||
} else {
|
||||
MCTracker.log("[MCPQ_Info] CONTEXT_TOWN: Invalid status 2");
|
||||
}
|
||||
} else if (status == 3) {
|
||||
if (store) {
|
||||
purchaseId = storeInfo[selection][0];
|
||||
purchaseCost = storeInfo[selection][1];
|
||||
|
||||
if (cm.haveItem(coinId, purchaseCost)) {
|
||||
cm.sendYesNo("Are you sure you want to purchase #i" + purchaseId + "#? You will have #r#e" + (cm.itemQuantity(coinId) - purchaseCost) + " " + coinIcon + "##k#n remaining.");
|
||||
} else {
|
||||
cm.sendOk("You don't have enough " + coinIcon + ".");
|
||||
cm.dispose();
|
||||
}
|
||||
} else {
|
||||
MCTracker.log("[MCPQ_Info] CONTEXT_TOWN: Invalid status 3");
|
||||
}
|
||||
} else if (status == 4) {
|
||||
if (store) {
|
||||
if (cm.haveItem(coinId, purchaseCost)) {
|
||||
cm.gainItem(coinId, -purchaseCost);
|
||||
cm.gainItem(purchaseId);
|
||||
cm.sendOk("Congratulations! Enjoy your new item.");
|
||||
cm.dispose();
|
||||
}
|
||||
} else {
|
||||
MCTracker.log("[MCPQ_Info] CONTEXT_TOWN: Invalid status 4");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function doExit() {
|
||||
cm.warp(MonsterCarnival.MAP_LOBBY);
|
||||
cm.sendOk("Hope you had fun in the Carnival PQ!");
|
||||
cm.dispose();
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
switch (ctx) {
|
||||
case CONTEXT_TOWN:
|
||||
doTown(mode, type, selection);
|
||||
break;
|
||||
case CONTEXT_EXIT:
|
||||
doExit();
|
||||
break;
|
||||
case CONTEXT_LOSE:
|
||||
doLoserMap(mode, type, selection);
|
||||
break;
|
||||
case CONTEXT_WIN:
|
||||
doWinnerMap(mode, type, selection);
|
||||
break;
|
||||
default:
|
||||
MCTracker.log("[MCPQ_INFO] Invalid context (value: " + ctx + ")");
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,464 +0,0 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
package server.partyquest.mcpq;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import packet.creators.CarnivalPackets;
|
||||
import packet.creators.PacketCreator;
|
||||
import client.player.Player;
|
||||
import server.life.MapleLifeFactory;
|
||||
import server.life.MapleMonster;
|
||||
import server.life.MobSkill;
|
||||
import server.life.MobSkillFactory;
|
||||
import server.life.SpawnPoint;
|
||||
import server.life.Spawns;
|
||||
import server.maps.Field;
|
||||
import server.maps.FieldObject;
|
||||
import server.maps.FieldObjectType;
|
||||
import server.maps.reactors.MapleReactor;
|
||||
import server.maps.reactors.MapleReactorFactory;
|
||||
import server.partyquest.mcpq.MCField.MCTeam;
|
||||
import static server.partyquest.mcpq.MCField.MCTeam.RED;
|
||||
import server.partyquest.mcpq.MCWZData.MCGuardianGenPos;
|
||||
import server.partyquest.mcpq.MCWZData.MCMobGenPos;
|
||||
|
||||
|
||||
/**
|
||||
* Keeps track of guardians and spawns in MCPQ.
|
||||
*
|
||||
* @author s4nta
|
||||
*/
|
||||
public class MCBattlefield {
|
||||
|
||||
private Field map;
|
||||
private MCWZData wzData;
|
||||
private int numGuardiansSpawned = 0;
|
||||
private int numMonstersSpawned = 0;
|
||||
// These map Guardian IDs (aka codes for status) to the guardian position.
|
||||
private Map<Integer, MCWZData.MCGuardianGenPos> redGuardianIdToPos = new HashMap<>();
|
||||
private Map<Integer, MCWZData.MCGuardianGenPos> blueGuardianIdToPos = new HashMap<>();
|
||||
// These map Reactor Object IDs to guardian objects.
|
||||
private Map<Integer, MCGuardian> redReactors = new HashMap<>();
|
||||
private Map<Integer, MCGuardian> blueReactors = new HashMap<>();
|
||||
// used for divided maps
|
||||
// we use an arraylist here for easier random element lookup.
|
||||
private List<MCWZData.MCGuardianGenPos> originalRedGuardianSpawns = new ArrayList<>();
|
||||
private List<MCWZData.MCGuardianGenPos> originalBlueGuardianSpawns = new ArrayList<>();
|
||||
// used for undivided map
|
||||
private List<MCWZData.MCGuardianGenPos> originalGuardianSpawns = new ArrayList<>();
|
||||
|
||||
// used for divided maps
|
||||
// we use an arraylist here for easier random element lookup.
|
||||
private List<MCWZData.MCMobGenPos> originalRedSpawns = new ArrayList<>();
|
||||
private List<MCWZData.MCMobGenPos> originalBlueSpawns = new ArrayList<>();
|
||||
// use for undivided map
|
||||
private List<MCWZData.MCMobGenPos> originalUnifiedSpawns = new ArrayList<>();
|
||||
|
||||
private List<SpawnPoint> originalSpawns = new ArrayList<>();
|
||||
private List<SpawnPoint> addedSpawns = new ArrayList<>();
|
||||
|
||||
public MCBattlefield(Field battleInstance) {
|
||||
this.map = battleInstance;
|
||||
fetchCarnivalData();
|
||||
getOriginalSpawnPoints();
|
||||
populateGuardianSpawns();
|
||||
populateMobSpawns();
|
||||
}
|
||||
|
||||
private void fetchCarnivalData() {
|
||||
wzData = this.map.getMCPQData();
|
||||
if (wzData == null) {
|
||||
MCTracker.log("[MCPQ] Fetching carnival failed for map " + map.getId());
|
||||
}
|
||||
}
|
||||
|
||||
private void getOriginalSpawnPoints() {
|
||||
for (Spawns sp : this.map.getSpawnPoints()) {
|
||||
originalSpawns.add((SpawnPoint) sp);
|
||||
}
|
||||
}
|
||||
|
||||
private void populateGuardianSpawns() {
|
||||
for (MCWZData.MCGuardianGenPos gpos : wzData.guardianGenPosList) {
|
||||
switch (gpos.team) {
|
||||
case 0:
|
||||
originalRedGuardianSpawns.add(gpos);
|
||||
break;
|
||||
case 1:
|
||||
originalBlueGuardianSpawns.add(gpos);
|
||||
break;
|
||||
default:
|
||||
originalGuardianSpawns.add(gpos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void populateMobSpawns() {
|
||||
for (MCWZData.MCMobGenPos mpos : wzData.mobGenPosList) {
|
||||
switch (mpos.team) {
|
||||
case 0:
|
||||
originalRedSpawns.add(mpos);
|
||||
break;
|
||||
case 1:
|
||||
originalBlueSpawns.add(mpos);
|
||||
break;
|
||||
default:
|
||||
originalUnifiedSpawns.add(mpos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addSpawn(Player chr, int num) {
|
||||
if (numMonstersSpawned > wzData.mobGenMax) {
|
||||
chr.getClient().announce(CarnivalPackets.CarnivalMessage(3));
|
||||
return;
|
||||
}
|
||||
|
||||
MCWZData.MCSummonMob mobToSummon = wzData.summons.get(num);
|
||||
MCWZData.MCMobGenPos spawnPos = getRandomSpawnPos(chr.getMCPQTeam());
|
||||
|
||||
MCField.MCTeam team = chr.getMCPQTeam();
|
||||
if (spawnPos == null) {
|
||||
chr.getClient().announce(CarnivalPackets.CarnivalMessage(2));
|
||||
return;
|
||||
}
|
||||
|
||||
int spendCp = mobToSummon.spendCP;
|
||||
if (spendCp > chr.getAvailableCP()) {
|
||||
readdSpawn(spawnPos, team);
|
||||
chr.getClient().announce(CarnivalPackets.CarnivalMessage(1));
|
||||
return;
|
||||
}
|
||||
|
||||
chr.getMCPQField().loseCP(chr, spendCp);
|
||||
this.map.broadcastMessage(CarnivalPackets.PlayerSummoned(MonsterCarnival.TAB_SPAWNS, num, chr.getName()));
|
||||
numMonstersSpawned++; // TODO: AtomicInteger this
|
||||
|
||||
MapleMonster monster = MapleLifeFactory.getMonster(mobToSummon.id);
|
||||
Point pos = new Point(spawnPos.x, spawnPos.y);
|
||||
SpawnPoint sp = new SpawnPoint(monster, pos, mobToSummon.mobTime, chr.getTeam());
|
||||
|
||||
addedSpawns.add(sp);
|
||||
updateMonsterBuffs();
|
||||
}
|
||||
|
||||
public void useSkill(Player chr, int num) {
|
||||
if (!wzData.skills.containsKey(num)) {
|
||||
MCTracker.log("Attempting to use a null skill.");
|
||||
return;
|
||||
}
|
||||
int realSkill = wzData.skills.get(num);
|
||||
MCSkill skill = MCSkillFactory.getMCSkill(realSkill);
|
||||
// TODO: add skill cooldowns
|
||||
|
||||
int spendCp = skill.getSpendCP();
|
||||
if (spendCp > chr.getAvailableCP()) {
|
||||
chr.getClient().announce(CarnivalPackets.CarnivalMessage(1));
|
||||
return;
|
||||
}
|
||||
|
||||
MCParty teamToApply = chr.getMCPQParty().getEnemy();
|
||||
boolean success = teamToApply.applyMCSkill(skill);
|
||||
|
||||
if (success) {
|
||||
chr.getMCPQField().loseCP(chr, spendCp);
|
||||
map.broadcastMessage(CarnivalPackets.PlayerSummoned(MonsterCarnival.TAB_DEBUFF, num, chr.getName()));
|
||||
} else {
|
||||
chr.getClient().getSession().write(CarnivalPackets.CarnivalMessage(5));
|
||||
}
|
||||
}
|
||||
|
||||
public void readdSpawn(MCWZData.MCMobGenPos pos, MCField.MCTeam team) {
|
||||
List<MCWZData.MCMobGenPos> lst = null;
|
||||
if (this.wzData.mapDivided) {
|
||||
if (null == team) {
|
||||
return;
|
||||
} else switch (team) {
|
||||
case RED:
|
||||
lst = originalRedSpawns;
|
||||
break;
|
||||
case BLUE:
|
||||
lst = originalBlueSpawns;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
lst = originalUnifiedSpawns;
|
||||
}
|
||||
|
||||
if (lst == null) {
|
||||
return;
|
||||
}
|
||||
lst.add(pos);
|
||||
}
|
||||
|
||||
public void spawnGuardian(Player chr, int num) {
|
||||
if (numGuardiansSpawned > wzData.guardianGenMax) {
|
||||
chr.getClient().announce(CarnivalPackets.CarnivalMessage(3));
|
||||
return;
|
||||
}
|
||||
|
||||
int guardianId = wzData.guardians.get(num);
|
||||
MCGuardian guardian = MCSkillFactory.getMCGuardian(guardianId);
|
||||
if (guardian == null) {
|
||||
MCTracker.log("Attempting to spawn invalid guardian.");
|
||||
return;
|
||||
}
|
||||
|
||||
MCField.MCTeam team = chr.getMCPQTeam();
|
||||
if (team == MCField.MCTeam.RED) {
|
||||
if (redGuardianIdToPos.containsKey(guardianId)) {
|
||||
chr.getClient().announce(CarnivalPackets.CarnivalMessage(4));
|
||||
return;
|
||||
}
|
||||
} else if (team == MCField.MCTeam.BLUE) {
|
||||
if (blueGuardianIdToPos.containsKey(guardianId)) {
|
||||
chr.getClient().announce(CarnivalPackets.CarnivalMessage(4));
|
||||
return;
|
||||
}
|
||||
}
|
||||
int spendCp = guardian.getSpendCP();
|
||||
if (spendCp > chr.getAvailableCP()) {
|
||||
chr.getClient().announce(CarnivalPackets.CarnivalMessage(1));
|
||||
return;
|
||||
}
|
||||
|
||||
chr.getMCPQField().loseCP(chr, spendCp);
|
||||
this.map.broadcastMessage(CarnivalPackets.PlayerSummoned(MonsterCarnival.TAB_GUARDIAN, num, chr.getName()));
|
||||
numGuardiansSpawned++; // TODO: AtomicInteger this
|
||||
MCWZData.MCGuardianGenPos genPos = getRandomGuardianPos(team);
|
||||
Point spawnPos = new Point(genPos.x, genPos.y);
|
||||
|
||||
MapleReactor reactor;
|
||||
if (null == team) {
|
||||
return;
|
||||
} else switch (team) {
|
||||
case RED:
|
||||
reactor = new MapleReactor(MapleReactorFactory.getReactor(MonsterCarnival.GUARDIAN_RED), MonsterCarnival.GUARDIAN_RED);
|
||||
reactor.setPosition(spawnPos);
|
||||
redGuardianIdToPos.put(num, genPos);
|
||||
break;
|
||||
case BLUE:
|
||||
reactor = new MapleReactor(MapleReactorFactory.getReactor(MonsterCarnival.GUARDIAN_BLUE), MonsterCarnival.GUARDIAN_BLUE);
|
||||
reactor.setPosition(spawnPos);
|
||||
blueGuardianIdToPos.put(num, genPos);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
reactor.setDelay(-1);
|
||||
map.spawnReactor(reactor);
|
||||
|
||||
if (team == MCField.MCTeam.RED) {
|
||||
redReactors.put(reactor.getObjectId(), MCSkillFactory.getMCGuardian(num));
|
||||
} else {
|
||||
blueReactors.put(reactor.getObjectId(), MCSkillFactory.getMCGuardian(num));
|
||||
}
|
||||
|
||||
map.setReactorState(reactor, (byte) 1);
|
||||
updateMonsterBuffs();
|
||||
}
|
||||
|
||||
public void onGuardianHit(Player chr, MapleReactor reactor) {
|
||||
if (MonsterCarnival.DEBUG) {
|
||||
System.out.println("STATE: " + reactor.getState());
|
||||
}
|
||||
MCField.MCTeam team = chr.getMCPQTeam();
|
||||
if (team == MCField.MCTeam.RED && reactor.getId() == MonsterCarnival.GUARDIAN_RED) {
|
||||
return;
|
||||
}
|
||||
if (team == MCField.MCTeam.BLUE && reactor.getId() == MonsterCarnival.GUARDIAN_BLUE) {
|
||||
return;
|
||||
}
|
||||
reactor.setState((byte) (reactor.getState() + 1));
|
||||
map.broadcastMessage(PacketCreator.TriggerReactor(reactor, reactor.getState()));
|
||||
|
||||
if (reactor.getState() > 3) {
|
||||
int reactorObjId = reactor.getObjectId();
|
||||
map.destroyReactor(reactorObjId);
|
||||
|
||||
MCGuardian guard;
|
||||
MCWZData.MCGuardianGenPos guardianGenPos;
|
||||
if (team == MCField.MCTeam.RED) {
|
||||
guard = blueReactors.remove(reactorObjId);
|
||||
guardianGenPos = blueGuardianIdToPos.remove(guard.getType());
|
||||
} else {
|
||||
guard = redReactors.remove(reactorObjId);
|
||||
guardianGenPos = redGuardianIdToPos.remove(guard.getType());
|
||||
}
|
||||
numGuardiansSpawned--;
|
||||
|
||||
if (MonsterCarnival.DEBUG) {
|
||||
System.out.println("Removing reactor with x = " + guardianGenPos.x);
|
||||
}
|
||||
if (wzData.mapDivided) {
|
||||
if (team == MCField.MCTeam.RED) {
|
||||
originalBlueGuardianSpawns.add(guardianGenPos);
|
||||
} else {
|
||||
originalRedGuardianSpawns.add(guardianGenPos);
|
||||
}
|
||||
} else {
|
||||
originalGuardianSpawns.add(guardianGenPos);
|
||||
}
|
||||
|
||||
if (MonsterCarnival.DEBUG) {
|
||||
System.out.println("Attempting to remove buff " + guard.getName());
|
||||
}
|
||||
updateMonsterBuffs();
|
||||
}
|
||||
}
|
||||
|
||||
private MCGuardianGenPos getRandomGuardianPos(MCTeam team) {
|
||||
if (this.wzData.mapDivided) {
|
||||
if (null == team) {
|
||||
return null;
|
||||
} else switch (team) {
|
||||
case RED: {
|
||||
int randIndex = (int) Math.floor(Math.random() * this.originalRedGuardianSpawns.size());
|
||||
return originalRedGuardianSpawns.remove(randIndex);
|
||||
}
|
||||
case BLUE: {
|
||||
int randIndex = (int) Math.floor(Math.random() * this.originalBlueGuardianSpawns.size());
|
||||
return originalBlueGuardianSpawns.remove(randIndex);
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
int randIndex = (int) Math.floor(Math.random() * this.originalGuardianSpawns.size());
|
||||
return originalGuardianSpawns.remove(randIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private MCMobGenPos getRandomSpawnPos(MCTeam team) {
|
||||
List<MCMobGenPos> lst = null;
|
||||
if (this.wzData.mapDivided) {
|
||||
if (null == team) {
|
||||
return null;
|
||||
} else switch (team) {
|
||||
case RED:
|
||||
lst = originalRedSpawns;
|
||||
break;
|
||||
case BLUE:
|
||||
lst = originalBlueSpawns;
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
lst = originalUnifiedSpawns;
|
||||
}
|
||||
|
||||
if (lst == null) {
|
||||
return null;
|
||||
}
|
||||
if (lst.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
int randIndex = (int) Math.floor(Math.random() * lst.size());
|
||||
return lst.remove(randIndex);
|
||||
}
|
||||
|
||||
private void updateMonsterBuffs() {
|
||||
List<MCGuardian> redGuardians = new ArrayList<>();
|
||||
List<MCGuardian> blueGuardians = new ArrayList<>();
|
||||
|
||||
for (MCGuardian g : this.redReactors.values()) {
|
||||
redGuardians.add(g);
|
||||
if (MonsterCarnival.DEBUG) {
|
||||
System.out.println("update buff red " + g.getMobSkillID());
|
||||
}
|
||||
}
|
||||
for (MCGuardian g : this.blueReactors.values()) {
|
||||
blueGuardians.add(g);
|
||||
if (MonsterCarnival.DEBUG) {
|
||||
System.out.println("update buff blue " + g.getMobSkillID());
|
||||
}
|
||||
}
|
||||
|
||||
for (FieldObject mmo : map.getAllMonsters()) {
|
||||
if (mmo.getType() == FieldObjectType.MONSTER) {
|
||||
MapleMonster mob = ((MapleMonster) mmo);
|
||||
mob.dispel();
|
||||
|
||||
if (mob.getTeam() == MCField.MCTeam.RED.code) {
|
||||
applyGuardians(mob, redGuardians);
|
||||
} else if (mob.getTeam() == MCField.MCTeam.BLUE.code) {
|
||||
applyGuardians(mob, blueGuardians);
|
||||
} else {
|
||||
MCTracker.log("[MCPQ] Attempting to give guardians to mob without team.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void giveMonsterBuffs(MapleMonster mob) {
|
||||
List<MCGuardian> redGuardians = new ArrayList<>();
|
||||
List<MCGuardian> blueGuardians = new ArrayList<>();
|
||||
|
||||
for (MCGuardian g : this.redReactors.values()) {
|
||||
redGuardians.add(g);
|
||||
if (MonsterCarnival.DEBUG) {
|
||||
System.out.println("update buff red " + g.getMobSkillID());
|
||||
}
|
||||
}
|
||||
for (MCGuardian g : this.blueReactors.values()) {
|
||||
blueGuardians.add(g);
|
||||
if (MonsterCarnival.DEBUG) {
|
||||
System.out.println("update buff blue " + g.getMobSkillID());
|
||||
}
|
||||
}
|
||||
|
||||
if (mob.getTeam() == MCField.MCTeam.RED.code) {
|
||||
applyGuardians(mob, redGuardians);
|
||||
} else if (mob.getTeam() == MCField.MCTeam.BLUE.code) {
|
||||
applyGuardians(mob, blueGuardians);
|
||||
} else {
|
||||
MCTracker.log("[MCPQ] Attempting to give guardians to mob without team.");
|
||||
}
|
||||
}
|
||||
|
||||
private void applyGuardians(MapleMonster mob, List<MCGuardian> guardians) {
|
||||
for (MCGuardian g : guardians) {
|
||||
MobSkill sk = MobSkillFactory.getMobSkill(g.getMobSkillID(), g.getLevel());
|
||||
sk.applyEffect(null, mob, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void spawningTask() {
|
||||
for (SpawnPoint sp : originalSpawns) {
|
||||
if (sp.shouldSpawn()) {
|
||||
MapleMonster mob = sp.spawnMonster(this.map);
|
||||
giveMonsterBuffs(mob);
|
||||
}
|
||||
}
|
||||
for (SpawnPoint sp : addedSpawns) {
|
||||
if (sp.shouldSpawn()) {
|
||||
MapleMonster mob = sp.spawnMonster(this.map);
|
||||
giveMonsterBuffs(mob);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,861 +0,0 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
package server.partyquest.mcpq;
|
||||
|
||||
import handling.channel.ChannelServer;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import packet.creators.CarnivalPackets;
|
||||
import packet.creators.EffectPackets;
|
||||
import packet.creators.PacketCreator;
|
||||
import packet.transfer.write.OutPacket;
|
||||
import client.player.Player;
|
||||
import server.MapleStatEffect;
|
||||
import server.itens.MapleItemInformationProvider;
|
||||
import server.maps.Field;
|
||||
import server.maps.FieldItem;
|
||||
import server.maps.reactors.MapleReactor;
|
||||
import server.maps.portal.Portal;
|
||||
import tools.TimerTools.EventTimer;
|
||||
|
||||
/**
|
||||
* Keeps track of a specific field (1-6). Handles all packet broadcasting, etc.
|
||||
* @author s4nta
|
||||
*/
|
||||
public class MCField {
|
||||
|
||||
/**
|
||||
* Different teams for MCPQ.
|
||||
*/
|
||||
public enum MCTeam {
|
||||
RED(0),
|
||||
BLUE(1),
|
||||
NONE(-1);
|
||||
public final int code;
|
||||
|
||||
MCTeam(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public final int getEnemyTeamCode() {
|
||||
return Math.abs(this.code - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the current state of the field.
|
||||
*/
|
||||
public enum MCState {
|
||||
LOBBY,
|
||||
BATTLE,
|
||||
END;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keys to access different map instances relating to this field.
|
||||
*/
|
||||
public enum MCMaps {
|
||||
LOBBY(0),
|
||||
BATTLEFIELD(1),
|
||||
RESURRECT(2),
|
||||
VICTORY(3),
|
||||
DEFEAT(4),
|
||||
NONE(-1);
|
||||
private final int code;
|
||||
|
||||
MCMaps(int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public static MCMaps getByCode(int code) {
|
||||
for (MCMaps m : values()) {
|
||||
if (m.code == code) {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
return NONE;
|
||||
}
|
||||
}
|
||||
|
||||
private int arena;
|
||||
private ChannelServer cserv;
|
||||
private MCParty red, blue;
|
||||
private List<MCParty> requests = new ArrayList<>();
|
||||
private MCState state;
|
||||
private Map<MCMaps, Field> mapInstances = new HashMap<>();
|
||||
private long startTime;
|
||||
private MCBattlefield battlefield;
|
||||
|
||||
// Timer Tasks
|
||||
private ScheduledFuture<?> acceptRequestsTask, validateRoomTask, startBattleTask,
|
||||
validateBattleTask, runBattleTask, endBattleTask,
|
||||
spawnMonstersTask;
|
||||
|
||||
public MCField(int arena, ChannelServer cserv, MCParty red, MCParty blue) {
|
||||
this.arena = arena;
|
||||
this.cserv = cserv;
|
||||
this.red = red;
|
||||
this.blue = blue;
|
||||
this.state = MCState.LOBBY;
|
||||
}
|
||||
|
||||
public boolean isFull() {
|
||||
if (MonsterCarnival.DEBUG) {
|
||||
return false;
|
||||
}
|
||||
return this.red != null && this.blue != null;
|
||||
}
|
||||
|
||||
public boolean needsRequest() {
|
||||
return this.red != null && this.blue == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the state of the field, warping out players and resetting tasks.
|
||||
* [MENTION=2000183830]para[/MENTION]m warpPlayers Warp out players or not
|
||||
* @param warpPlayers
|
||||
*/
|
||||
public void deregister(boolean warpPlayers) {
|
||||
if (warpPlayers) {
|
||||
if (this.red != null) {
|
||||
this.red.deregisterPlayers();
|
||||
}
|
||||
if (this.blue != null) {
|
||||
this.blue.deregisterPlayers();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.red = null;
|
||||
this.blue = null;
|
||||
this.requests.clear();
|
||||
this.state = MCState.LOBBY;
|
||||
|
||||
if (this.acceptRequestsTask != null) {
|
||||
this.acceptRequestsTask.cancel(true);
|
||||
this.acceptRequestsTask = null;
|
||||
}
|
||||
|
||||
if (this.validateRoomTask != null) {
|
||||
this.validateRoomTask.cancel(true);
|
||||
this.validateRoomTask = null;
|
||||
}
|
||||
|
||||
if (this.startBattleTask != null) {
|
||||
this.startBattleTask.cancel(true);
|
||||
this.startBattleTask = null;
|
||||
}
|
||||
|
||||
if (this.endBattleTask != null) {
|
||||
this.endBattleTask.cancel(true);
|
||||
this.endBattleTask = null;
|
||||
}
|
||||
|
||||
if (this.runBattleTask != null) {
|
||||
this.runBattleTask.cancel(true);
|
||||
this.runBattleTask = null;
|
||||
}
|
||||
|
||||
if (this.validateBattleTask != null) {
|
||||
this.validateBattleTask.cancel(true);
|
||||
this.validateBattleTask = null;
|
||||
}
|
||||
|
||||
if (this.spawnMonstersTask != null) {
|
||||
this.spawnMonstersTask.cancel(true);
|
||||
this.spawnMonstersTask = null;
|
||||
}
|
||||
|
||||
for (MCMaps mapType : MCMaps.values()) {
|
||||
this.mapInstances.remove(mapType);
|
||||
}
|
||||
}
|
||||
|
||||
public MCParty getRed() {
|
||||
return red;
|
||||
}
|
||||
|
||||
public MCParty getBlue() {
|
||||
return blue;
|
||||
}
|
||||
|
||||
public void announce(OutPacket pkt) {
|
||||
if (this.red != null) {
|
||||
red.broadcast(pkt);
|
||||
} else {
|
||||
MCTracker.log("[MCPQ] Trying to announce packet to red when it is null.");
|
||||
}
|
||||
|
||||
if (this.blue != null) {
|
||||
blue.broadcast(pkt);
|
||||
} else {
|
||||
MCTracker.log("[MCPQ] Trying to announce packet to blue when it is null.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string representing the status of this room for the Spiegelmann NPC.
|
||||
* [MENTION=850422]return[/MENTION] String representing the room's status.
|
||||
* @return
|
||||
*/
|
||||
public String getStatus() {
|
||||
if (isFull()) {
|
||||
return "";
|
||||
}
|
||||
if (this.state != MCState.LOBBY) {
|
||||
return "";
|
||||
}
|
||||
String waitingParty = "";
|
||||
if (this.red != null) {
|
||||
String fmt = " <Party Size: %d, Average Level: %d>";
|
||||
waitingParty = String.format(fmt, this.red.getSize(), this.red.getAverageLevel());
|
||||
}
|
||||
String fmt = "#L%d#Carnival Field %d%s#l\r\n";
|
||||
return String.format(fmt, this.arena, this.arena, waitingParty);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to register a party in this field. If success, then all players in the party
|
||||
* will be warped to the waiting lobby. All players in the party will also have relevant
|
||||
* CPQ information assigned to them.
|
||||
*
|
||||
* [MENTION=2000183830]para[/MENTION]m party The party to register.
|
||||
*/
|
||||
public void register(MCParty party, MCTeam team) {
|
||||
if (this.red == null && team == MCTeam.RED) {
|
||||
party.setTeam(team);
|
||||
this.red = party;
|
||||
} else if (this.blue == null && team == MCTeam.BLUE) {
|
||||
party.setTeam(team);
|
||||
this.blue = party;
|
||||
} else {
|
||||
MCTracker.log("Attempting to register party when team is already set.");
|
||||
return;
|
||||
}
|
||||
party.setField(this);
|
||||
party.updatePlayers();
|
||||
party.warp(MCMaps.LOBBY);
|
||||
onPartyRegistered(party, team);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a challenge to the party in this field.
|
||||
*
|
||||
* [MENTION=2000183830]para[/MENTION]m party The party requesting a challenge.
|
||||
*/
|
||||
public void request(MCParty party) {
|
||||
if (this.red == null) {
|
||||
MCTracker.log("Attempting to request when waiting team is null.");
|
||||
this.deregister(true);
|
||||
return;
|
||||
}
|
||||
requests.add(party);
|
||||
this.red.notice("[MCPQ] A challenge has arrived! Click on the Assistant in your room to view it.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts a challenge from a team.
|
||||
* [MENTION=2000183830]para[/MENTION]m index Index of team in requests.
|
||||
* [MENTION=850422]return[/MENTION] 1 if the challenge was accepted successfully, 0 otherwise.
|
||||
*/
|
||||
public int acceptRequest(int index) {
|
||||
MCParty toAccept = this.requests.get(index);
|
||||
register(toAccept, MCTeam.BLUE);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for pending requests.
|
||||
*
|
||||
* [MENTION=850422]return[/MENTION] True if there are pending requests.
|
||||
*/
|
||||
public boolean hasPendingRequests() {
|
||||
return requests.size() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a pending list of requests, formatted for use in a NPC. Also cleans up the requests,
|
||||
* getting rid of MCParties that no longer exist.
|
||||
*
|
||||
* [MENTION=850422]return[/MENTION] Formatted list of requests for NPC.
|
||||
* @return
|
||||
*/
|
||||
public String getNPCRequestString() {
|
||||
StringBuilder sb = new StringBuilder("Here is the list of pending requests:\r\n\r\n#b");
|
||||
for (MCParty pty : requests) {
|
||||
if (!pty.exists()) {
|
||||
continue;
|
||||
}
|
||||
sb.append("#L").append(requests.indexOf(pty)).append("#");
|
||||
String fmt = "<Party Size: %d, Average Level: %d>";
|
||||
fmt = String.format(fmt, pty.getSize(), pty.getAverageLevel());
|
||||
sb.append(fmt);
|
||||
sb.append("#l\r\n");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a R3 minute waiting timer in the lobby for teams to accept requests.
|
||||
* If the timer completes the countdown, teams are deregistered and sent back to the lobby.
|
||||
*/
|
||||
private void startLobbyTask(MCParty host) {
|
||||
host.clock(MonsterCarnival.TIME_LOBBYWAIT);
|
||||
this.acceptRequestsTask = EventTimer.getInstance().schedule(new AcceptingRequestsTask(this, host), 1000 * MonsterCarnival.TIME_LOBBYWAIT); // 3 minutes
|
||||
this.validateRoomTask = EventTimer.getInstance().register(new ValidateLobbyTask(this), 1000, 1000); // repeat every second
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handling for when a team registers.
|
||||
*
|
||||
* [MENTION=2000183830]para[/MENTION]m party Party that registers.
|
||||
* [MENTION=2000183830]para[/MENTION]m team Team of party.
|
||||
*/
|
||||
private void onPartyRegistered(MCParty party, MCTeam team) {
|
||||
if (team == MCTeam.RED) {
|
||||
startLobbyTask(party);
|
||||
}
|
||||
if (team == MCTeam.BLUE) { // both teams are in
|
||||
this.validateRoomTask.cancel(true);
|
||||
this.acceptRequestsTask.cancel(true);
|
||||
blue.clock(10);
|
||||
red.clock(10);
|
||||
|
||||
this.startBattleTask = EventTimer.getInstance().schedule(new GoBattlefieldTask(this), 1000 * 10); // 10 seconds
|
||||
|
||||
red.notice("[MCPQ] The Carnival PQ will start in 10 seconds!");
|
||||
blue.notice("[MCPQ] The Carnival PQ will start in 10 seconds!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Warps both parties in the field to the battlefield map.
|
||||
*/
|
||||
private void goBattle() {
|
||||
Field map = getMap(MCMaps.BATTLEFIELD);
|
||||
if (MonsterCarnival.DEBUG)
|
||||
System.out.println("warping to battle " + map + " " + map.getId());
|
||||
if (red != null) {
|
||||
red.warp(map, "red00");
|
||||
} else {
|
||||
MCTracker.log("[MCPQ] Trying to warp red party when it is null.");
|
||||
}
|
||||
|
||||
if (blue != null) {
|
||||
blue.warp(map, "blue00");
|
||||
} else {
|
||||
MCTracker.log("[MCPQ] Trying to warp blue party when it is null.");
|
||||
}
|
||||
|
||||
red.clock(MonsterCarnival.TIME_PREBATTLE);
|
||||
blue.clock(MonsterCarnival.TIME_PREBATTLE);
|
||||
|
||||
red.notice("[MCPQ] The battle will start in 10 seconds!");
|
||||
blue.notice("[MCPQ] The battle will start in 10 seconds!");
|
||||
|
||||
startBattleTask = EventTimer.getInstance().schedule(new BeginCarnivalTask(this), 1000 * MonsterCarnival.TIME_PREBATTLE); // 10 seconds
|
||||
|
||||
validateBattleTask = EventTimer.getInstance().register(new ValidateBattlefieldTask(this), 1000, 500); // check every second
|
||||
|
||||
battlefield = new MCBattlefield(getMap(MCMaps.BATTLEFIELD));
|
||||
|
||||
red.setEnemy(blue);
|
||||
blue.setEnemy(red);
|
||||
}
|
||||
|
||||
private void beginCarnival() {
|
||||
red.clock(MonsterCarnival.TIME_BATTLE);
|
||||
blue.clock(MonsterCarnival.TIME_BATTLE);
|
||||
startTime = System.currentTimeMillis();
|
||||
|
||||
getMap(MCMaps.BATTLEFIELD).broadcastMessage(PacketCreator.ServerNotice(6, "[MCPQ] You have 10 minutes to kill monsters!"));
|
||||
|
||||
endBattleTask = EventTimer.getInstance().schedule(new EndBattleTask(this), 1000 * MonsterCarnival.TIME_BATTLE);
|
||||
spawnMonstersTask = EventTimer.getInstance().register(new SpawnTask(this.battlefield), 1000 * 5);
|
||||
|
||||
}
|
||||
|
||||
public void endBattle(MCParty winner, MCParty loser) {
|
||||
endBattle(winner, loser, false);
|
||||
}
|
||||
|
||||
public void endBattle(MCParty winner, MCParty loser, boolean abnormal) {
|
||||
// TODO: Abnormal win codes to prevent exploits
|
||||
|
||||
validateBattleTask.cancel(true);
|
||||
spawnMonstersTask.cancel(true);
|
||||
|
||||
MCWZData cpqData = this.getMap(MCMaps.BATTLEFIELD).getMCPQData();
|
||||
String effectWin = cpqData.effectWin;
|
||||
String effectLose = cpqData.effectLose;
|
||||
String soundWin = cpqData.soundWin;
|
||||
String soundLose = cpqData.soundLose;
|
||||
|
||||
winner.broadcast(EffectPackets.ShowEffect(effectWin));
|
||||
winner.broadcast(EffectPackets.PlaySound(soundWin));
|
||||
loser.broadcast(EffectPackets.ShowEffect(effectLose));
|
||||
loser.broadcast(EffectPackets.PlaySound(soundLose));
|
||||
|
||||
this.getMap(MCMaps.BATTLEFIELD).killAllMonsters(false);
|
||||
this.getMap(MCMaps.BATTLEFIELD).clearDrops();
|
||||
this.deregister(false);
|
||||
|
||||
EventTimer.getInstance().schedule(new WarpEndBattleTask(this, winner, loser), 1000 * 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles CP gain and packet updates when a monster is killed.
|
||||
* [MENTION=2000183830]para[/MENTION]m chr Character that kills the monster.
|
||||
* [MENTION=2000183830]para[/MENTION]m cp CP gained.
|
||||
*/
|
||||
public void monsterKilled(Player chr, int cp) {
|
||||
if (MonsterCarnival.DEBUG) {
|
||||
// System.out.println(chr.getName() + " killed for +" + cp + " CP");
|
||||
}
|
||||
// TODO: Personal stats for CP gain
|
||||
this.gainCP(chr, cp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles game logic and packet broadcasting for CP gain.
|
||||
* Broadcasts personal CP update to chr, and broadcasts party CP update
|
||||
* to the entire field.
|
||||
*
|
||||
* [MENTION=2000183830]para[/MENTION]m chr Character that gains CP.
|
||||
* [MENTION=2000183830]para[/MENTION]m cp CP gained.
|
||||
*/
|
||||
public void gainCP(Player chr, int cp) {
|
||||
if (cp < 0) {
|
||||
MCTracker.log("[MCPQ] Adding negative CP.");
|
||||
if (MonsterCarnival.DEBUG) {
|
||||
System.out.println("Adding negative CP: stacktrace");
|
||||
new Exception().printStackTrace();
|
||||
}
|
||||
}
|
||||
MCParty pty = chr.getMCPQParty();
|
||||
chr.gainCP(cp);
|
||||
pty.gainCP(cp);
|
||||
chr.getClient().announce(CarnivalPackets.UpdatePersonalCP(chr));
|
||||
this.announce(CarnivalPackets.UpdatePartyCP(pty));
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtracts from available CP while leaving total CP untouched.
|
||||
*
|
||||
* [MENTION=2000183830]para[/MENTION]m chr Character that loses CP.
|
||||
* [MENTION=2000183830]para[/MENTION]m cp CP lost (should be positive number).
|
||||
*/
|
||||
public void loseCP(Player chr, int cp) {
|
||||
if (cp < 0) {
|
||||
MCTracker.log("[MCPQ] Losing negative CP.");
|
||||
if (MonsterCarnival.DEBUG) {
|
||||
System.out.println("Adding negative CP: stacktrace");
|
||||
new Exception().printStackTrace();
|
||||
}
|
||||
}
|
||||
MCParty pty = chr.getMCPQParty();
|
||||
chr.loseCP(cp);
|
||||
pty.loseCP(cp);
|
||||
chr.getClient().announce(CarnivalPackets.UpdatePersonalCP(chr));
|
||||
this.announce(CarnivalPackets.UpdatePartyCP(pty));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a player looting an item.
|
||||
* [MENTION=2000183830]para[/MENTION]m player Player that picked up the object.
|
||||
* [MENTION=2000183830]para[/MENTION]m mapitem Object picked up.
|
||||
*
|
||||
* [MENTION=850422]return[/MENTION] True if pickup was successful, false otherwise.
|
||||
*/
|
||||
public boolean onItemPickup(Player player, FieldItem mapitem) {
|
||||
if (mapitem == null) {
|
||||
MCTracker.log("[MCPQ] Attempting to loot null object.");
|
||||
return false;
|
||||
}
|
||||
int itemid = mapitem.getItem().getItemId();
|
||||
if (!MonsterCarnival.isCPQConsumeItem(itemid)) {
|
||||
return false;
|
||||
}
|
||||
MCParty pty = player.getMCPQParty();
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
MapleStatEffect itemEffect = ii.getItemEffect(itemid);
|
||||
if (!itemEffect.isConsumeOnPickup()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (itemEffect.isParty()) {
|
||||
for (Player chr : pty.getMembers()) {
|
||||
if (chr.getStat().getCurrentHp() > 0) {
|
||||
itemEffect.applyTo(chr);
|
||||
}
|
||||
}
|
||||
} else { // Single Target Item
|
||||
itemEffect.applyTo(player);
|
||||
}
|
||||
// Status items
|
||||
if (itemEffect.getNuffSkill() != -1) {
|
||||
MCSkill debuff = MCSkillFactory.getMCSkill(itemEffect.getNuffSkill());
|
||||
if (debuff == null) {
|
||||
MCTracker.log("[MCPQ] debuff skill is null " + itemEffect.getNuffSkill());
|
||||
return false;
|
||||
}
|
||||
|
||||
pty.getEnemy().applyMCSkill(debuff);
|
||||
}
|
||||
|
||||
if (itemEffect.getCP() > 0) {
|
||||
this.gainCP(player, itemEffect.getCP());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void onPlayerRespawn(Player player) {
|
||||
int cpLoss = Math.min(player.getAvailableCP(), MonsterCarnival.CP_LOSS_ON_DEATH);
|
||||
this.announce(CarnivalPackets.PlayerDiedMessage(player, cpLoss));
|
||||
this.loseCP(player, cpLoss);
|
||||
player.getStat().addMPHP(30000, 30000);
|
||||
player.changeMap(this.getMap(MCMaps.RESURRECT), this.getMap(MCMaps.RESURRECT).getPortal(0));
|
||||
player.getClient().getSession().write(PacketCreator.GetClock(getTimeRemaining()));
|
||||
player.getClient().getSession().write(CarnivalPackets.StartMonsterCarnival(player));
|
||||
}
|
||||
|
||||
public void onPlayerDisconnected(Player player) {
|
||||
MCParty pty = player.getMCPQParty();
|
||||
if (pty != null) {
|
||||
pty.removePlayer(player);
|
||||
} else {
|
||||
MCTracker.log("[MCPQ] Attempting to run player disconnect event when party is null for character " + player.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public void onAddSpawn(Player chr, int num) {
|
||||
if (this.battlefield != null) {
|
||||
battlefield.addSpawn(chr, num);
|
||||
} else {
|
||||
MCTracker.log("[MCPQ] Summoning guardian with null battlefield.");
|
||||
}
|
||||
}
|
||||
|
||||
public void onUseSkill(Player chr, int num) {
|
||||
if (this.battlefield != null) {
|
||||
battlefield.useSkill(chr, num);
|
||||
} else {
|
||||
MCTracker.log("[MCPQ] Summoning guardian with null battlefield.");
|
||||
}
|
||||
}
|
||||
|
||||
public void onGuardianSummon(Player chr, int num) {
|
||||
if (this.battlefield != null) {
|
||||
battlefield.spawnGuardian(chr, num);
|
||||
} else {
|
||||
MCTracker.log("[MCPQ] Summoning guardian with null battlefield.");
|
||||
}
|
||||
}
|
||||
|
||||
public void onGuardianHit(Player chr, MapleReactor reactor) {
|
||||
if (this.battlefield != null) {
|
||||
battlefield.onGuardianHit(chr, reactor);
|
||||
} else {
|
||||
MCTracker.log("[MCPQ] Hitting reactor with null battlefield.");
|
||||
}
|
||||
}
|
||||
|
||||
public void onRevive(Player player) {
|
||||
MCTeam team = player.getMCPQTeam();
|
||||
Portal portal;
|
||||
if (team == MCTeam.RED) {
|
||||
portal = getMap(MCMaps.BATTLEFIELD).getPortal("red_revive");
|
||||
} else {
|
||||
portal = getMap(MCMaps.BATTLEFIELD).getPortal("blue_revive");
|
||||
}
|
||||
|
||||
player.changeMap(getMap(MCMaps.BATTLEFIELD), portal);
|
||||
player.getClient().getSession().write(PacketCreator.GetClock(getTimeRemaining()));
|
||||
player.getClient().getSession().write(CarnivalPackets.StartMonsterCarnival(player));
|
||||
}
|
||||
|
||||
public int getTimeRemaining() {
|
||||
// TODO: add support for setting an explicit endTime instead of using the hack with MonsterCarnival variables
|
||||
return (int) ((startTime + 1000 * MonsterCarnival.TIME_BATTLE) - System.currentTimeMillis()) / 1000;
|
||||
}
|
||||
|
||||
|
||||
// Map Instances
|
||||
|
||||
/**
|
||||
* Returns the map instance for a requested map. Creates a new map instance if unavailable.
|
||||
* [MENTION=2000183830]para[/MENTION]m type Map instance to return.
|
||||
* [MENTION=850422]return[/MENTION] The instanced map.
|
||||
*/
|
||||
public Field getMap(MCMaps type) {
|
||||
if (this.mapInstances.containsKey(type)) {
|
||||
return this.mapInstances.get(type);
|
||||
}
|
||||
return createInstanceMap(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to create an instanced map, based on the type passed in. Also creates a mapping in
|
||||
* this.mapInstances.
|
||||
*
|
||||
* [MENTION=2000183830]para[/MENTION]m type Type of map to generate.
|
||||
* [MENTION=850422]return[/MENTION] MapleMap for the instanced map if type is supported, otherwise null.
|
||||
*/
|
||||
private Field createInstanceMap(MCMaps type) {
|
||||
int mapid = -1;
|
||||
switch (type) {
|
||||
case LOBBY:
|
||||
mapid = MonsterCarnival.getLobbyMap(this.arena);
|
||||
break;
|
||||
case BATTLEFIELD:
|
||||
mapid = MonsterCarnival.getBattleFieldMap(this.arena);
|
||||
break;
|
||||
case RESURRECT:
|
||||
mapid = MonsterCarnival.getResurrectionMap(this.arena);
|
||||
break;
|
||||
case VICTORY:
|
||||
mapid = MonsterCarnival.getVictoriousMap(this.arena);
|
||||
break;
|
||||
case DEFEAT:
|
||||
mapid = MonsterCarnival.getDefeatedMap(this.arena);
|
||||
break;
|
||||
}
|
||||
if (mapid == -1) return null;
|
||||
Field mapInstance = this.cserv.getMapFactory().instanceMap(mapid, true, true);
|
||||
this.mapInstances.put(type, mapInstance);
|
||||
return mapInstance;
|
||||
}
|
||||
|
||||
// Timer Tasks
|
||||
|
||||
public class ValidateLobbyTask implements Runnable {
|
||||
|
||||
private final MCField field;
|
||||
|
||||
/**
|
||||
* Timer task to ensure all players are on the right field.
|
||||
* If anything is wrong with the parties, the field is deregistered.
|
||||
* [MENTION=2000183830]para[/MENTION]m field Field to run the validation task on.
|
||||
* @param field
|
||||
*/
|
||||
public ValidateLobbyTask(MCField field) {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (this.field.red == null) {
|
||||
this.field.deregister(true);
|
||||
return;
|
||||
}
|
||||
for (Player c : field.red.getMembers()) {
|
||||
if (c.getMap() != field.getMap(MCMaps.LOBBY)) {
|
||||
this.field.deregister(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (this.field.blue != null) {
|
||||
for (Player c : field.blue.getMembers()) {
|
||||
if (c.getMap() != field.getMap(MCMaps.LOBBY)) {
|
||||
this.field.deregister(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ValidateBattlefieldTask implements Runnable {
|
||||
|
||||
private final MCField field;
|
||||
|
||||
/**
|
||||
* Timer task to ensure all players are on the right field.
|
||||
* If anything is wrong with the parties, the field is deregistered.
|
||||
* [MENTION=2000183830]para[/MENTION]m field Field to run the validation task on.
|
||||
*/
|
||||
|
||||
public ValidateBattlefieldTask(MCField field) {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (this.field.red == null || field.red.getSize() == 0) {
|
||||
MCTracker.log("[MCPQ] Red team null when validating battlefield");
|
||||
field.endBattle(blue, red);
|
||||
return;
|
||||
}
|
||||
Collection<Player> members = Collections.unmodifiableCollection(field.red.getMembers());
|
||||
for (Player c : members) {
|
||||
if (c.getMap() != field.getMap(MCMaps.BATTLEFIELD) && c.getMap() != field.getMap(MCMaps.RESURRECT)) {
|
||||
this.field.announce(CarnivalPackets.CarnivalLeave(MCTeam.RED.code, c.getName()));
|
||||
red.removePlayer(c); // TODO: fix concurrent modification
|
||||
}
|
||||
if (c.getMap() == field.getMap(MCMaps.BATTLEFIELD) && !c.isAlive()) {
|
||||
this.field.onPlayerRespawn(c);
|
||||
}
|
||||
}
|
||||
if (this.field.blue == null || field.blue.getSize() == 0) {
|
||||
MCTracker.log("[MCPQ] Blue team null when validating battlefield");
|
||||
field.endBattle(red, blue);
|
||||
return;
|
||||
}
|
||||
members = Collections.unmodifiableCollection(field.blue.getMembers());
|
||||
for (Player c : members) {
|
||||
if (c.getMap() != field.getMap(MCMaps.BATTLEFIELD) && c.getMap() != field.getMap(MCMaps.RESURRECT)) {
|
||||
this.field.announce(CarnivalPackets.CarnivalLeave(MCTeam.BLUE.code, c.getName()));
|
||||
blue.removePlayer(c);
|
||||
}
|
||||
if (c.getMap() == field.getMap(MCMaps.BATTLEFIELD) && !c.isAlive()) {
|
||||
this.field.onPlayerRespawn(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class AcceptingRequestsTask implements Runnable {
|
||||
|
||||
private final MCField field;
|
||||
private final MCParty host;
|
||||
|
||||
/**
|
||||
* Runs a task that counts down for 3 minutes, then warps the hosting party out.
|
||||
*
|
||||
* [MENTION=2000183830]para[/MENTION]m field Field to accept requests on.
|
||||
* [MENTION=2000183830]para[/MENTION]m host Hosting party that will be warped out if they do not accept a request
|
||||
* within 3 minutes.
|
||||
*/
|
||||
public AcceptingRequestsTask(MCField field, MCParty host) {
|
||||
this.field = field;
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Collection<Player> chrs = this.host.getMembers();
|
||||
for (Player c : chrs) {
|
||||
c.changeMap(MonsterCarnival.MAP_LOBBY);
|
||||
}
|
||||
this.field.deregister(true);
|
||||
}
|
||||
}
|
||||
|
||||
public class GoBattlefieldTask implements Runnable {
|
||||
|
||||
private final MCField field;
|
||||
|
||||
public GoBattlefieldTask(MCField field) {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
field.goBattle();
|
||||
|
||||
field.red.startBattle();
|
||||
field.blue.startBattle();
|
||||
|
||||
field.state = MCState.BATTLE;
|
||||
}
|
||||
}
|
||||
|
||||
public class BeginCarnivalTask implements Runnable {
|
||||
|
||||
private final MCField field;
|
||||
|
||||
public BeginCarnivalTask(MCField field) {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Field map = field.getMap(MCMaps.BATTLEFIELD);
|
||||
map.beginSpawning();
|
||||
field.beginCarnival();
|
||||
}
|
||||
}
|
||||
|
||||
/* I have no idea why this doesn't work normally :/ */
|
||||
public class SpawnTask implements Runnable {
|
||||
|
||||
private final MCBattlefield battleMap;
|
||||
|
||||
public SpawnTask(MCBattlefield field) {
|
||||
this.battleMap = field;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// TODO: adjust spawn rates based on cp
|
||||
battleMap.spawningTask();
|
||||
}
|
||||
}
|
||||
|
||||
public class EndBattleTask implements Runnable {
|
||||
private final MCField field;
|
||||
|
||||
public EndBattleTask(MCField field) {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
MCParty winner, loser;
|
||||
if (field.red.getTotalCP() > field.blue.getTotalCP()) {
|
||||
winner = field.red;
|
||||
loser = field.blue;
|
||||
} else if (field.red.getTotalCP() < field.blue.getTotalCP()) {
|
||||
winner = field.blue;
|
||||
loser = field.red;
|
||||
} else {
|
||||
// if tied: random chance
|
||||
// TODO: proper extension of time
|
||||
if (Math.random() < .5) {
|
||||
winner = field.red;
|
||||
loser = field.blue;
|
||||
} else {
|
||||
winner = field.blue;
|
||||
loser = field.red;
|
||||
}
|
||||
}
|
||||
|
||||
field.state = MCState.END;
|
||||
field.endBattle(winner, loser);
|
||||
}
|
||||
}
|
||||
|
||||
public class WarpEndBattleTask implements Runnable {
|
||||
|
||||
private final MCField field;
|
||||
private final MCParty winner, loser;
|
||||
|
||||
public WarpEndBattleTask(MCField field, MCParty winner, MCParty loser) {
|
||||
this.field = field;
|
||||
this.winner = winner;
|
||||
this.loser = loser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
winner.warp(field.getMap(MCMaps.VICTORY));
|
||||
loser.warp(field.getMap(MCMaps.DEFEAT));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
package server.partyquest.mcpq;
|
||||
|
||||
/**
|
||||
* Object representing MCGuardian in Skill.wz/MCGuardian.img.
|
||||
*
|
||||
* @author s4nta
|
||||
*/
|
||||
|
||||
public class MCGuardian {
|
||||
private final int type, spendCP, mobSkillID, level;
|
||||
private String name, desc;
|
||||
|
||||
public MCGuardian(int type, int spendCP, int mobSkillID, int level) {
|
||||
this.type = type;
|
||||
this.mobSkillID = mobSkillID;
|
||||
this.level = level;
|
||||
this.spendCP = spendCP;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public int getSpendCP() {
|
||||
return spendCP;
|
||||
}
|
||||
|
||||
public int getMobSkillID() {
|
||||
return mobSkillID;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
public void setDesc(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
}
|
||||
@@ -1,326 +0,0 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
package server.partyquest.mcpq;
|
||||
|
||||
import community.MapleParty;
|
||||
import community.MaplePartyCharacter;
|
||||
import handling.channel.ChannelServer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import packet.creators.CarnivalPackets;
|
||||
import packet.creators.PacketCreator;
|
||||
import packet.transfer.write.OutPacket;
|
||||
import client.player.Player;
|
||||
import client.player.buffs.Disease;
|
||||
import server.life.MobSkill;
|
||||
import server.life.MobSkillFactory;
|
||||
import server.maps.Field;
|
||||
import server.partyquest.mcpq.MCField.MCTeam;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sammy Guergachi <sguergachi at gmail.com>
|
||||
*/
|
||||
/**
|
||||
* Provides an interface for Monster Carnival-specific party methods and variables.
|
||||
*
|
||||
* @author s4nta
|
||||
*/
|
||||
public class MCParty {
|
||||
|
||||
private MapleParty party;
|
||||
private List<Player> characters = new ArrayList<>();
|
||||
private int availCP = 0;
|
||||
private int totalCP = 0;
|
||||
private MCField.MCTeam team = MCField.MCTeam.NONE;
|
||||
private MCField field;
|
||||
private MCParty enemy;
|
||||
|
||||
public MCParty(MapleParty party) {
|
||||
this.party = party;
|
||||
for (MaplePartyCharacter chr : party.getMembers()) {
|
||||
if (!chr.isOnline()) continue;
|
||||
Player c = ChannelServer.getInstance(chr.getChannel()).getPlayerStorage().getCharacterById(chr.getId());
|
||||
|
||||
characters.add(c);
|
||||
}
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return this.characters.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the underlying MapleParty still exists in the same way it did when it was created.
|
||||
* That is, if there were no players who left the party.
|
||||
*
|
||||
* [MENTION=850422]return[/MENTION] True if the underlying MapleParty still exists in its original format.
|
||||
*/
|
||||
public boolean exists() {
|
||||
Collection<Player> members = getMembers();
|
||||
for (Player chr : members) {
|
||||
if (chr.getParty() == null || chr.getParty() != this.party) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getAverageLevel() {
|
||||
int sum = 0, num = 0;
|
||||
for (Player chr : getMembers()) {
|
||||
sum += chr.getLevel();
|
||||
num += 1;
|
||||
}
|
||||
return sum / num;
|
||||
}
|
||||
|
||||
public boolean checkLevels() {
|
||||
if (MonsterCarnival.DEBUG) {
|
||||
return true;
|
||||
}
|
||||
for (Player chr : getMembers()) {
|
||||
int lv = chr.getLevel();
|
||||
if (lv < MonsterCarnival.MIN_LEVEL || lv > MonsterCarnival.MAX_LEVEL) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean checkChannels() {
|
||||
if (MonsterCarnival.DEBUG) {
|
||||
return true;
|
||||
}
|
||||
for (Player chr : getMembers()) {
|
||||
if (chr.getClient().getChannel() != party.getLeader().getChannel()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean checkMaps() {
|
||||
if (MonsterCarnival.DEBUG) {
|
||||
return true;
|
||||
}
|
||||
for (Player chr : getMembers()) {
|
||||
if (chr.getMapId() != MonsterCarnival.MAP_LOBBY) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void warp(int map) {
|
||||
for (Player chr : getMembers()) {
|
||||
chr.changeMap(map);
|
||||
}
|
||||
}
|
||||
|
||||
public void warp(Field map) {
|
||||
for (Player chr : getMembers()) {
|
||||
chr.changeMap(map, map.getPortal(0));
|
||||
}
|
||||
}
|
||||
|
||||
public void warp(Field map, String portal) {
|
||||
for (Player chr : getMembers()) {
|
||||
chr.changeMap(map, map.getPortal(portal));
|
||||
}
|
||||
}
|
||||
|
||||
public void warp(MCField.MCMaps type) {
|
||||
Field m = this.field.getMap(type);
|
||||
for (Player chr : getMembers()) {
|
||||
chr.changeMap(m, m.getPortal(0));
|
||||
}
|
||||
}
|
||||
|
||||
public void clock(int secs) {
|
||||
for (Player chr : getMembers()) {
|
||||
chr.getClient().announce(PacketCreator.GetClock(secs));
|
||||
}
|
||||
}
|
||||
|
||||
public void notice(String msg) {
|
||||
broadcast(PacketCreator.ServerNotice(6, msg));
|
||||
}
|
||||
|
||||
public void broadcast(OutPacket pkt) {
|
||||
for (Player chr : getMembers()) {
|
||||
chr.getClient().announce(pkt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets MCPQTeam, MCPQParty, and MCPQField for a given character.
|
||||
* [MENTION=2000183830]para[/MENTION]m chr Character to update.
|
||||
*/
|
||||
public void updatePlayer(Player chr) {
|
||||
chr.setMCPQTeam(this.team);
|
||||
chr.setMCPQParty(this);
|
||||
chr.setMCPQField(this.field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets MCPQTeam, MCPQParty, and MCPQ field for all characters in the party.
|
||||
* Unlike deregisterPlayers, this method does NOT warp players to the lobby map.
|
||||
*/
|
||||
public void updatePlayers() {
|
||||
for (Player chr : getMembers()) {
|
||||
this.updatePlayer(chr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets MCPQ variables for a given character.
|
||||
* [MENTION=2000183830]para[/MENTION]m chr Character to reset.
|
||||
*/
|
||||
public static void deregisterPlayer(Player chr) {
|
||||
chr.setMCPQTeam(MCTeam.NONE);
|
||||
chr.setMCPQParty(null);
|
||||
chr.setMCPQField(null);
|
||||
|
||||
chr.setAvailableCP(0);
|
||||
chr.setTotalCP(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets MCPQ variables for all characters in the party.
|
||||
* Unlike updatePlayers, this method DOES warp players to the lobby map.
|
||||
*/
|
||||
public void deregisterPlayers() {
|
||||
for (Player chr : getMembers()) {
|
||||
MCParty.deregisterPlayer(chr);
|
||||
chr.changeMap(MonsterCarnival.MAP_EXIT);
|
||||
}
|
||||
}
|
||||
|
||||
public void removePlayer(Player chr) {
|
||||
characters.remove(chr);
|
||||
deregisterPlayer(chr);
|
||||
}
|
||||
|
||||
public void startBattle() {
|
||||
for (Player chr : characters) {
|
||||
chr.getClient().getSession().write(CarnivalPackets.StartMonsterCarnival(chr));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses some amount of available CP.
|
||||
* [MENTION=2000183830]para[/MENTION]m use A positive integer to be subtracted from available CP.
|
||||
*/
|
||||
public void loseCP(int use) {
|
||||
// TODO: locks?
|
||||
if (use < 0) {
|
||||
System.err.println("Attempting to use negative CP.");
|
||||
}
|
||||
this.availCP -= use;
|
||||
}
|
||||
|
||||
public void gainCP(int gain) {
|
||||
// TODO: locks?
|
||||
this.availCP += gain;
|
||||
this.totalCP += gain;
|
||||
}
|
||||
|
||||
public MCParty getEnemy() {
|
||||
return enemy;
|
||||
}
|
||||
|
||||
public void setEnemy(MCParty enemy) {
|
||||
this.enemy = enemy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies a MCSkill to the entire team. This is used on the team's own players
|
||||
* because it is called when the enemy team uses a debuff/cube of darkness.
|
||||
* [MENTION=2000183830]para[/MENTION]m skill Skill to apply.
|
||||
* [MENTION=850422]return[/MENTION] True if skill was applied, false otherwise.
|
||||
*/
|
||||
public boolean applyMCSkill(MCSkill skill) {
|
||||
MobSkill s = MobSkillFactory.getMobSkill(skill.getMobSkillID(), skill.getLevel());
|
||||
Disease disease = Disease.getType(skill.getMobSkillID());
|
||||
if (disease == null) {
|
||||
disease = Disease.DARKNESS;
|
||||
s = MobSkillFactory.getMobSkill(121, 6); // HACK: darkness
|
||||
} else if (disease == Disease.POISON) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We only target players on the battlefield map.
|
||||
if (skill.getTarget() == 2) {
|
||||
for (Player chr : getMembers()) {
|
||||
if (MonsterCarnival.isBattlefieldMap(chr.getMapId())) {
|
||||
chr.giveDebuff(disease, s);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
if (getRandomMember() != null) {
|
||||
getRandomMember().giveDebuff(disease, 1, 30000L, disease.getDisease(), 1);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setField(MCField field) {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
public void setTeam(MCTeam newTeam) {
|
||||
this.team = newTeam;
|
||||
}
|
||||
|
||||
public MCTeam getTeam() {
|
||||
return team;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a collection of online members in the party.
|
||||
* [MENTION=850422]return[/MENTION] Online MCParty members.
|
||||
*/
|
||||
public Collection<Player> getMembers() {
|
||||
return this.characters;
|
||||
}
|
||||
|
||||
public Player getRandomMember() {
|
||||
List<Player> chrsOnMap = new ArrayList<>();
|
||||
for (Player chr : this.characters) {
|
||||
if (MonsterCarnival.isBattlefieldMap(chr.getMapId())) {
|
||||
chrsOnMap.add(chr);
|
||||
}
|
||||
}
|
||||
if (chrsOnMap.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return chrsOnMap.get(new Random().nextInt(chrsOnMap.size()));
|
||||
}
|
||||
|
||||
public int getAvailableCP() {
|
||||
return availCP;
|
||||
}
|
||||
|
||||
public int getTotalCP() {
|
||||
return totalCP;
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package server.partyquest.mcpq;
|
||||
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Object representing MCSkill in Skill.wz/MCSkill.img.
|
||||
*
|
||||
* @author s4nta
|
||||
*/
|
||||
public class MCSkill {
|
||||
private final int id, target, mobSkillID, level, spendCP;
|
||||
private String name, desc;
|
||||
|
||||
public MCSkill(int id, int target, int mobSkillID, int level, int spendCP) {
|
||||
this.id = id;
|
||||
this.target = target;
|
||||
this.mobSkillID = mobSkillID;
|
||||
this.level = level;
|
||||
this.spendCP = spendCP;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
public int getMobSkillID() {
|
||||
return mobSkillID;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public int getSpendCP() {
|
||||
return spendCP;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
public void setDesc(String desc) {
|
||||
this.desc = desc;
|
||||
}
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
package server.partyquest.mcpq;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sammy Guergachi <sguergachi at gmail.com>
|
||||
*/
|
||||
public class MCSkillFactory {
|
||||
|
||||
private static Map<Integer, MCSkill> mcSkills = new HashMap<>();
|
||||
private static Map<Integer, MCGuardian> mcGuardians = new HashMap<>();
|
||||
private static MapleDataProvider dataSource = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Skill.wz"));
|
||||
private static MapleData mcSkillRoot = dataSource.getData("MCSkill.img");
|
||||
private static MapleData mcGuardianRoot = dataSource.getData("MCGuardian.img");
|
||||
private static ReentrantReadWriteLock skillLock = new ReentrantReadWriteLock();
|
||||
private static ReentrantReadWriteLock guardianLock = new ReentrantReadWriteLock();
|
||||
|
||||
public static MCSkill getMCSkill(int skillId) {
|
||||
skillLock.readLock().lock();
|
||||
try {
|
||||
MCSkill ret = mcSkills.get(skillId);
|
||||
if (ret != null) {
|
||||
return ret;
|
||||
}
|
||||
} finally {
|
||||
skillLock.readLock().unlock();
|
||||
}
|
||||
skillLock.writeLock().lock();
|
||||
try {
|
||||
MCSkill ret;
|
||||
ret = mcSkills.get(skillId);
|
||||
if (ret == null) {
|
||||
MapleData skillData = mcSkillRoot.getChildByPath(String.valueOf(skillId));
|
||||
if (skillData != null) {
|
||||
int target = MapleDataTool.getInt("target", skillData, 0);
|
||||
int spendCP = MapleDataTool.getInt("spendCP", skillData, 0);
|
||||
int mobSkillID = MapleDataTool.getInt("mobSkillID", skillData, 0);
|
||||
int level = MapleDataTool.getInt("level", skillData, 0);
|
||||
ret = new MCSkill(skillId, target, mobSkillID, level, spendCP);
|
||||
|
||||
if (MonsterCarnival.DEBUG) {
|
||||
String name = MapleDataTool.getString("name", skillData, "");
|
||||
String desc = MapleDataTool.getString("desc", skillData, "");
|
||||
ret.setName(name);
|
||||
ret.setDesc(desc);
|
||||
}
|
||||
mcSkills.put(skillId, ret);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
} finally {
|
||||
skillLock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public static MCGuardian getMCGuardian(int id) {
|
||||
guardianLock.readLock().lock();
|
||||
try {
|
||||
MCGuardian ret = mcGuardians.get(id);
|
||||
if (ret != null) {
|
||||
return ret;
|
||||
}
|
||||
} finally {
|
||||
guardianLock.readLock().unlock();
|
||||
}
|
||||
guardianLock.writeLock().lock();
|
||||
try {
|
||||
MCGuardian ret;
|
||||
ret = mcGuardians.get(id);
|
||||
if (ret == null) {
|
||||
MapleData skillData = mcGuardianRoot.getChildByPath(String.valueOf(id));
|
||||
if (skillData != null) {
|
||||
int type = MapleDataTool.getInt("type", skillData, 0);
|
||||
int spendCP = MapleDataTool.getInt("spendCP", skillData, 0);
|
||||
int mobSkillID = MapleDataTool.getInt("mobSkillID", skillData, 0);
|
||||
int level = MapleDataTool.getInt("level", skillData, 0);
|
||||
ret = new MCGuardian(type, spendCP, mobSkillID, level);
|
||||
|
||||
if (MonsterCarnival.DEBUG) {
|
||||
String name = MapleDataTool.getString("name", skillData, "");
|
||||
String desc = MapleDataTool.getString("desc", skillData, "");
|
||||
ret.setName(name);
|
||||
ret.setDesc(desc);
|
||||
}
|
||||
mcGuardians.put(type, ret);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
} finally {
|
||||
guardianLock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
package server.partyquest.mcpq;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import provider.MapleData;
|
||||
import provider.MapleDataTool;
|
||||
|
||||
/**
|
||||
* Representation of the data tree inside of the Map.wz file.
|
||||
*
|
||||
* @author s4nta
|
||||
*/
|
||||
public class MCWZData {
|
||||
|
||||
public List<MCMobGenPos> mobGenPosList = new ArrayList<>();
|
||||
public List<MCGuardianGenPos> guardianGenPosList = new ArrayList<>();
|
||||
public Map<Integer, MCSummonMob> summons = new HashMap<>();
|
||||
public Map<Integer, Integer> skills = new HashMap<>();
|
||||
public Map<Integer, Integer> guardians = new HashMap<>();
|
||||
|
||||
public String effectWin, effectLose, soundWin, soundLose;
|
||||
public int rewardMapWin, rewardMapLose;
|
||||
|
||||
public int mobGenMax, guardianGenMax;
|
||||
public boolean mapDivided;
|
||||
public int deathCP;
|
||||
public int reactorRed, reactorBlue;
|
||||
|
||||
public MCWZData(MapleData src) {
|
||||
parse(src);
|
||||
}
|
||||
|
||||
public void parse(MapleData src) {
|
||||
populateMobGenPos(src.getChildByPath("mobGenPos"));
|
||||
populateSummonMobs(src.getChildByPath("mob"));
|
||||
effectWin = MapleDataTool.getString("effectWin", src);
|
||||
effectLose = MapleDataTool.getString("effectLose", src);
|
||||
soundWin = MapleDataTool.getString("soundWin", src);
|
||||
soundLose = MapleDataTool.getString("soundLose", src);
|
||||
|
||||
rewardMapWin = MapleDataTool.getInt("rewardMapWin", src);
|
||||
rewardMapLose = MapleDataTool.getInt("rewardMapLose", src);
|
||||
|
||||
populateSkills(src.getChildByPath("skill"));
|
||||
populateGuardianGenPos(src.getChildByPath("guardianGenPos"));
|
||||
populateGuardians(src.getChildByPath("guardian"));
|
||||
|
||||
mobGenMax = MapleDataTool.getInt("mobGenMax", src, 20); // HACK: 20 default
|
||||
guardianGenMax = MapleDataTool.getInt("guardianGenMax", src, 20); // HACK: 20 default
|
||||
|
||||
mapDivided = MapleDataTool.getInt("mapDivided", src) > 0;
|
||||
|
||||
deathCP = MapleDataTool.getInt("deathCP", src);
|
||||
reactorRed = MapleDataTool.getInt("reactorRed", src);
|
||||
reactorBlue = MapleDataTool.getInt("reactorBlue", src);
|
||||
}
|
||||
|
||||
private void populateMobGenPos(MapleData src) {
|
||||
for (MapleData n : src) {
|
||||
MCMobGenPos nn = new MCMobGenPos(MapleDataTool.getInt("x", n, 0),
|
||||
MapleDataTool.getInt("y", n, 0),
|
||||
MapleDataTool.getInt("fh", n, 0),
|
||||
MapleDataTool.getInt("cy", n, 0),
|
||||
MapleDataTool.getInt("team", n, -1));
|
||||
mobGenPosList.add(nn);
|
||||
}
|
||||
}
|
||||
|
||||
private void populateSummonMobs(MapleData src) {
|
||||
for (MapleData n : src) {
|
||||
int id = Integer.parseInt(n.getName());
|
||||
MCSummonMob mcs = new MCSummonMob(
|
||||
MapleDataTool.getInt("id", n, 0),
|
||||
MapleDataTool.getInt("spendCP", n, 0),
|
||||
MapleDataTool.getInt("mobTime", n, 0)
|
||||
);
|
||||
|
||||
this.summons.put(id, mcs);
|
||||
}
|
||||
}
|
||||
|
||||
private void populateSkills(MapleData src) {
|
||||
for (MapleData n : src) {
|
||||
int key = Integer.parseInt(n.getName());
|
||||
int val = MapleDataTool.getInt(n);
|
||||
|
||||
skills.put(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
private void populateGuardianGenPos(MapleData src) {
|
||||
for (MapleData n : src) {
|
||||
MCGuardianGenPos nn = new MCGuardianGenPos(MapleDataTool.getInt("x", n, 0),
|
||||
MapleDataTool.getInt("y", n, 0),
|
||||
MapleDataTool.getInt("f", n, 0),
|
||||
MapleDataTool.getInt("team", n, -1));
|
||||
guardianGenPosList.add(nn);
|
||||
}
|
||||
}
|
||||
|
||||
private void populateGuardians(MapleData src) {
|
||||
for (MapleData n : src) {
|
||||
int key = Integer.parseInt(n.getName());
|
||||
int val = MapleDataTool.getInt(n);
|
||||
|
||||
guardians.put(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
public class MCMobGenPos {
|
||||
|
||||
public final int x, y, fh, cy, team;
|
||||
|
||||
private MCMobGenPos(int x, int y, int fh, int cy, int team) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.fh = fh;
|
||||
this.cy = cy;
|
||||
this.team = team;
|
||||
}
|
||||
}
|
||||
|
||||
public class MCGuardianGenPos {
|
||||
|
||||
public final int x, y, f, team;
|
||||
|
||||
private MCGuardianGenPos(int x, int y, int f, int team) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.f = f;
|
||||
this.team = team;
|
||||
}
|
||||
}
|
||||
|
||||
public class MCSummonMob {
|
||||
|
||||
public final int id, spendCP, mobTime;
|
||||
|
||||
private MCSummonMob(int id, int spendCP, int mobTime) {
|
||||
this.id = id;
|
||||
this.spendCP = spendCP;
|
||||
this.mobTime = mobTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,371 +0,0 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
package server.partyquest.mcpq;
|
||||
|
||||
import community.MapleParty;
|
||||
import handling.channel.ChannelServer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import client.player.Player;
|
||||
import client.player.buffs.Disease;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sammy Guergachi <sguergachi at gmail.com>
|
||||
*/
|
||||
/**
|
||||
* Processes game logic for Monster Carnival PQ.
|
||||
*
|
||||
* TODO: Display IGNs/Jobs/Level in Pending Requests
|
||||
* TODO: fix reactor handling and make it less hacky
|
||||
* TODO: fix cube of darkness and make it less hacky
|
||||
*
|
||||
* @author s4nta
|
||||
*/
|
||||
public class MonsterCarnival {
|
||||
|
||||
// Logger
|
||||
static org.slf4j.Logger log = LoggerFactory.getLogger(MonsterCarnival.class);
|
||||
|
||||
// Map of channel to a MonsterCarnival instance.
|
||||
private static final HashMap<Integer, MonsterCarnival> instances = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Returns the MonsterCarnival instance for a channel. Creates a new one and maps it
|
||||
* if it does not exist.
|
||||
* [MENTION=2000183830]para[/MENTION]m channel Channel to check for.
|
||||
* [MENTION=850422]return[/MENTION] MonsterCarnival instance for a channel.
|
||||
* @param channel
|
||||
* @return
|
||||
*/
|
||||
public static MonsterCarnival getMonsterCarnival(int channel) {
|
||||
// TODO: synchronization?
|
||||
if (channel < 1 || channel > 20) {
|
||||
log.warn("Attempting to get a Monster Carnival instance for invalid channel.");
|
||||
return null;
|
||||
}
|
||||
if (instances.containsKey(channel)) {
|
||||
return instances.get(channel);
|
||||
}
|
||||
ChannelServer cserv = ChannelServer.getInstance(channel);
|
||||
if (cserv == null) {
|
||||
log.error("ChannelServer instance for channel " + channel + " is null.");
|
||||
return null;
|
||||
}
|
||||
MonsterCarnival inst = new MonsterCarnival(cserv);
|
||||
instances.put(channel, inst);
|
||||
return inst;
|
||||
}
|
||||
|
||||
// Instance variables
|
||||
private ChannelServer cserv;
|
||||
private Map<Integer, MCField> fields = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Constructor for a MonsterCarnival instance.
|
||||
* [MENTION=2000183830]para[/MENTION]m cserv Channel server for this instance.
|
||||
*/
|
||||
public MonsterCarnival(ChannelServer cserv) {
|
||||
this.cserv = cserv;
|
||||
this.initFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes empty fields for the instance.
|
||||
*/
|
||||
private void initFields() {
|
||||
for (int i = 1; i <= NUM_FIELDS; i++) {
|
||||
fields.put(i, new MCField(i, this.cserv, null, null));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the field with a specified ID.
|
||||
* [MENTION=2000183830]para[/MENTION]m id ID of field to retrieve.
|
||||
* [MENTION=850422]return[/MENTION]
|
||||
*/
|
||||
public MCField getField(int id) {
|
||||
if (id >= 1 && id <= NUM_FIELDS) {
|
||||
return fields.get(id);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a party can join a field or not.
|
||||
*
|
||||
* [MENTION=2000183830]para[/MENTION]m pty Party to register.
|
||||
* [MENTION=2000183830]para[/MENTION]m room Room to join.
|
||||
* [MENTION=850422]return[/MENTION] Different code based on status. OK if successful.
|
||||
*/
|
||||
public int registerStatus(MapleParty pty, int room) {
|
||||
if (!isValidField(room)) {
|
||||
return STATUS_FIELD_INVALID;
|
||||
}
|
||||
MCField field = this.getField(room);
|
||||
if (field.isFull()) {
|
||||
return STATUS_FIELD_FULL;
|
||||
}
|
||||
MCParty party = new MCParty(pty);
|
||||
if (!sizeCheck(party.getSize(), room)) {
|
||||
return STATUS_PARTY_SIZE;
|
||||
}
|
||||
boolean levelCheck = party.checkLevels();
|
||||
if (!levelCheck) {
|
||||
return STATUS_PARTY_LEVEL;
|
||||
}
|
||||
boolean chanCheck = party.checkChannels();
|
||||
if (!chanCheck) {
|
||||
return STATUS_PARTY_MISSING;
|
||||
}
|
||||
boolean mapCheck = party.checkMaps();
|
||||
if (!mapCheck) {
|
||||
return STATUS_PARTY_MISSING;
|
||||
}
|
||||
if (field.needsRequest()) {
|
||||
return STATUS_REQUEST;
|
||||
}
|
||||
return STATUS_PROCEED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new MCParty based on a regular MapleParty object.
|
||||
* [MENTION=2000183830]para[/MENTION]m pty Party to base off of.
|
||||
* [MENTION=850422]return[/MENTION] Newly created MCParty.
|
||||
*/
|
||||
public MCParty createParty(MapleParty pty) {
|
||||
return new MCParty(pty);
|
||||
}
|
||||
|
||||
public void resetPlayer(Player chr) {
|
||||
MCParty.deregisterPlayer(chr);
|
||||
chr.changeMap(MAP_LOBBY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String containing information about lobby waiting rooms.
|
||||
*
|
||||
* [MENTION=850422]return[/MENTION] String containing lobby information formatted for NPC.
|
||||
*/
|
||||
public String getNPCAvailableFields() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Welcome to the #bCarnival PQ#k! Rooms 1-4 can hold 2-4 people, and rooms 5-6 can hold 3-6.\r\n#b");
|
||||
for (int i = 1; i <= NUM_FIELDS; i++) {
|
||||
MCField field = this.fields.get(i);
|
||||
sb.append(field.getStatus());
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
// Reference Information
|
||||
|
||||
// Game Constants
|
||||
public static final int CP_LOSS_ON_DEATH = 10;
|
||||
public static final int TIME_PREBATTLE = 10;
|
||||
public static final int TIME_BATTLE = 600;
|
||||
public static final int TIME_LOBBYWAIT = 180;
|
||||
|
||||
public static final int TAB_SPAWNS = 0;
|
||||
public static final int TAB_DEBUFF = 1;
|
||||
public static final int TAB_GUARDIAN = 2;
|
||||
|
||||
/**
|
||||
* Gets a random debuff for (Mini) Cube of Darkness.
|
||||
* [MENTION=850422]return[/MENTION] Random MapleDisease.
|
||||
*/
|
||||
public static Disease getRandomDebuff() {
|
||||
return DEBUFFS[new Random().nextInt(DEBUFFS.length)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks party size. Information from hidden-street MCPQ page.
|
||||
* [MENTION=2000183830]para[/MENTION]m size Size of the party.
|
||||
* [MENTION=2000183830]para[/MENTION]m field Field to check for.
|
||||
* [MENTION=850422]return[/MENTION] True if party size is okay, False otherwise.
|
||||
*/
|
||||
public static final boolean sizeCheck(int size, int field) {
|
||||
if (DEBUG) {
|
||||
return true;
|
||||
}
|
||||
switch (field) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
// return size >= 2 && size <= 4;
|
||||
return size >= 1 && size <= 4;
|
||||
case 5:
|
||||
case 6:
|
||||
// return size >= 3 && size <= 6;
|
||||
return size >= 1 && size <= 6;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static final boolean isValidField(int field) {
|
||||
return field >= 1 && field <= 6;
|
||||
}
|
||||
|
||||
public static final int getLobbyMap(int field) {
|
||||
if (field < 1 || field > NUM_FIELDS) {
|
||||
log.warn("Attempting to get lobby map for invalid field.");
|
||||
return MAP_EXIT;
|
||||
}
|
||||
return 980000000 + field * 100;
|
||||
}
|
||||
|
||||
public static final boolean isLobbyMap(int mapid) {
|
||||
switch (mapid) {
|
||||
case 980000100:
|
||||
case 980000200:
|
||||
case 980000300:
|
||||
case 980000400:
|
||||
case 980000500:
|
||||
case 980000600:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static final int getBattleFieldMap(int field) {
|
||||
if (field < 1 || field > NUM_FIELDS) {
|
||||
log.warn("Attempting to get battlefield map for invalid field.");
|
||||
return MAP_EXIT;
|
||||
}
|
||||
return 980000000 + field * 100 + 1;
|
||||
}
|
||||
|
||||
public static final boolean isBattlefieldMap(int mapid) {
|
||||
switch (mapid) {
|
||||
case 980000101:
|
||||
case 980000201:
|
||||
case 980000301:
|
||||
case 980000401:
|
||||
case 980000501:
|
||||
case 980000601:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static final int getResurrectionMap(int field) {
|
||||
if (field < 1 || field > NUM_FIELDS) {
|
||||
log.warn("Attempting to get resurrection map for invalid field.");
|
||||
return MAP_EXIT;
|
||||
}
|
||||
return 980000000 + field * 100 + 2;
|
||||
}
|
||||
|
||||
public static final int getVictoriousMap(int field) {
|
||||
if (field < 1 || field > NUM_FIELDS) {
|
||||
log.warn("Attempting to get victory map for invalid field.");
|
||||
return MAP_EXIT;
|
||||
}
|
||||
return 980000000 + field * 100 + 3;
|
||||
}
|
||||
|
||||
public static final int getDefeatedMap(int field) {
|
||||
if (field < 1 || field > NUM_FIELDS) {
|
||||
log.warn("Attempting to get defeat map for invalid field.");
|
||||
return MAP_EXIT;
|
||||
}
|
||||
return 980000000 + field * 100 + 4;
|
||||
}
|
||||
|
||||
public static final boolean isCPQConsumeItem(int itemid) {
|
||||
switch (itemid) {
|
||||
case ITEM_CP_1:
|
||||
case ITEM_CP_2:
|
||||
case ITEM_CP_3:
|
||||
case ITEM_PTY_ELIX:
|
||||
case ITEM_PTY_PELIX:
|
||||
case ITEM_PTY_ALLC:
|
||||
case ITEM_MINICUBE:
|
||||
case ITEM_DARKCUBE:
|
||||
case ITEM_STUNNER:
|
||||
case ITEM_IND_WHITE:
|
||||
case ITEM_IND_MANA:
|
||||
case ITEM_IND_ELIX:
|
||||
case ITEM_IND_PELIX:
|
||||
case ITEM_IND_ALLC:
|
||||
case ITEM_PTY_MANA:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Error Codes
|
||||
// Note: These would be in an enum, but since these will be used in a NPC, they are not.
|
||||
public static final int STATUS_FIELD_FULL = 0;
|
||||
public static final int STATUS_PARTY_SIZE = 1;
|
||||
public static final int STATUS_PARTY_LEVEL = 2;
|
||||
public static final int STATUS_PARTY_MISSING = 3;
|
||||
public static final int STATUS_FIELD_INVALID = 4;
|
||||
public static final int STATUS_REQUEST = 98;
|
||||
public static final int STATUS_PROCEED = 99;
|
||||
|
||||
// Maps
|
||||
public static final int MAP_LOBBY = 980000000;
|
||||
public static final int MAP_EXIT = 980000010;
|
||||
|
||||
// NPCs
|
||||
public static final int NPC_LOBBY = 2042000;
|
||||
public static final int NPC_ENTER = 2042001; // Warp in from outside
|
||||
public static final int NPC_INFO = 2042002;
|
||||
public static final int NPC_ASST_RED = 2042003;
|
||||
public static final int NPC_ASST_BLUE = 2042004;
|
||||
|
||||
// Items
|
||||
public static final int ITEM_CP_1 = 2022157;
|
||||
public static final int ITEM_CP_2 = 2022158;
|
||||
public static final int ITEM_CP_3 = 2022159;
|
||||
public static final int ITEM_PTY_MANA = 2022160;
|
||||
public static final int ITEM_PTY_ELIX = 2022161;
|
||||
public static final int ITEM_PTY_PELIX = 2022162;
|
||||
public static final int ITEM_PTY_ALLC = 2022163;
|
||||
public static final int ITEM_MINICUBE = 2022164;
|
||||
public static final int ITEM_DARKCUBE = 2022165;
|
||||
public static final int ITEM_STUNNER = 2022166;
|
||||
public static final int ITEM_IND_WHITE = 2022174;
|
||||
public static final int ITEM_IND_ELIX = 2022175;
|
||||
public static final int ITEM_IND_PELIX = 2022176;
|
||||
public static final int ITEM_IND_MANA = 2022177;
|
||||
public static final int ITEM_IND_ALLC = 2022178;
|
||||
|
||||
// Guardians
|
||||
public static final int GUARDIAN_RED = 9980000;
|
||||
public static final int GUARDIAN_BLUE = 9980001;
|
||||
|
||||
// Debuffs
|
||||
public static final Disease[] DEBUFFS = {Disease.STUN, Disease.DARKNESS, Disease.WEAKEN}; // intentionally leave out a few
|
||||
|
||||
// Miscellaneous
|
||||
public static final int MIN_LEVEL = 30;
|
||||
public static final int MAX_LEVEL = 50;
|
||||
public static final int NUM_FIELDS = 6;
|
||||
|
||||
// Debug
|
||||
public static final boolean DEBUG = false;
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
Blob contains extra classes for Monster Carnival. Can possibly be used for implementing MCPQ.
|
||||
|
||||
Scripts and Src contains the code changed on OdinMS to have CPQ working.
|
||||
@@ -1,82 +0,0 @@
|
||||
/** * [MENTION=19862]id[/MENTION] 2042000
|
||||
* [MENTION=806871]NPC[/MENTION] Spiegelmann
|
||||
* [MENTION=836108]Function[/MENTION] Monster Carnival Lobby NPC
|
||||
* @author s4nta
|
||||
*/
|
||||
|
||||
// Relevant Monster Carnival classes
|
||||
var MonsterCarnival = net.sf.odinms.server.partyquest.mcpq.MonsterCarnival;
|
||||
var MCTracker = net.sf.odinms.server.partyquest.mcpq.MCTracker;
|
||||
var MCParty = net.sf.odinms.server.partyquest.mcpq.MCParty;
|
||||
var MCField = net.sf.odinms.server.partyquest.mcpq.MCField;
|
||||
var MCTeam = net.sf.odinms.server.partyquest.mcpq.MCField.MCTeam;
|
||||
|
||||
// NPC variables
|
||||
var status = -1;
|
||||
var carnival, field;
|
||||
var room = -1;
|
||||
|
||||
function start() {
|
||||
if (cm.getMapId() != 980000000) {
|
||||
MCTracker.log("Spiegelmann called on invalid map " + cm.getMapId() + " by player " + cm.getName());
|
||||
cm.sendOk("You are not authorized to do this.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
action(1, 0, 0);
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
if (mode == -1) {
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
if (mode == 1) status++;
|
||||
else status--;
|
||||
|
||||
if (status == 0) {
|
||||
if (cm.getParty() == null) {
|
||||
cm.sendOk("You are not in a party.");
|
||||
cm.dispose();
|
||||
return;
|
||||
} else if (!cm.isLeader()) {
|
||||
cm.sendOk("If you want to try Carnival PQ, please tell the #bleader of your party#k to talk to me.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
carnival = MonsterCarnival.getMonsterCarnival(cm.getChannel());
|
||||
cm.sendSimple(carnival.getNPCAvailableFields());
|
||||
} else if (status == 1) {
|
||||
room = selection;
|
||||
if (room < 1 || room > 6) {
|
||||
cm.sendOk("That is not a valid room.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
var code = carnival.registerStatus(cm.getParty(), selection);
|
||||
if (code == MonsterCarnival.STATUS_FIELD_FULL) {
|
||||
cm.sendOk("This room is currently full.")
|
||||
} else if (code == MonsterCarnival.STATUS_PARTY_SIZE) {
|
||||
cm.sendOk("Your party is not the right size for this field.");
|
||||
} else if (code == MonsterCarnival.STATUS_PARTY_LEVEL) {
|
||||
cm.sendOk("Please check to see that the members in your party are between level 30 and 50.");
|
||||
} else if (code == MonsterCarnival.STATUS_PARTY_MISSING) {
|
||||
cm.sendOk("Please make sure everyone in your party is in this lobby.");
|
||||
} else if (code == MonsterCarnival.STATUS_FIELD_INVALID) {
|
||||
cm.sendOk("Unauthorized request.");
|
||||
}
|
||||
|
||||
if (code == MonsterCarnival.STATUS_PROCEED) {
|
||||
field = carnival.getField(room);
|
||||
party = carnival.createParty(cm.getParty());
|
||||
field.register(party, MCTeam.RED);
|
||||
cm.sendOk("You will have 3 minutes to accept challenges from other parties.");
|
||||
} else if (code == MonsterCarnival.STATUS_REQUEST) {
|
||||
cm.sendOk("Sending request to room " + room + ". You will be automatically warped in if they accept your challenge.");
|
||||
field = carnival.getField(room);
|
||||
party = carnival.createParty(cm.getParty());
|
||||
field.request(party);
|
||||
}
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,326 +0,0 @@
|
||||
/** * [MENTION=19862]id[/MENTION] 2042002
|
||||
* [MENTION=806871]NPC[/MENTION] Spiegelmann
|
||||
* [MENTION=836108]Function[/MENTION] Monster Carnival NPC
|
||||
* @author s4nta
|
||||
* [MENTION=497496]cred[/MENTION]its xirengfx (for store code, CPQ description)
|
||||
*/
|
||||
var DISABLED = false;
|
||||
|
||||
var SavedLocationType = Packages.net.sf.odinms.server.maps.SavedLocationType;
|
||||
|
||||
// Relevant Monster Carnival classes
|
||||
var MonsterCarnival = Packages.net.sf.odinms.server.partyquest.mcpq.MonsterCarnival;
|
||||
var MCTracker = Packages.net.sf.odinms.server.partyquest.mcpq.MCTracker;
|
||||
var MCParty = Packages.net.sf.odinms.server.partyquest.mcpq.MCParty;
|
||||
var MCField = Packages.net.sf.odinms.server.partyquest.mcpq.MCField;
|
||||
|
||||
// NPC variables
|
||||
var status = -1;
|
||||
var store = false;
|
||||
var ctx = -1; //context
|
||||
var storeInfo;
|
||||
var purchaseId;
|
||||
var purchaseCost;
|
||||
|
||||
// Reference
|
||||
var coinId = 4001129;
|
||||
var coinIcon = "#i" + coinId + "#";
|
||||
var infoMaps = [220000000, 200000000, 103000000, 540000000]; // ludi, orbis, kerning, singapore
|
||||
var gradeS = 600
|
||||
var gradeA = 500
|
||||
var gradeB = 400
|
||||
var gradeC = 300
|
||||
var gradeD = 200
|
||||
var gradeE = 100
|
||||
|
||||
var expRewards = [[150000, 100000], // S Winner/Loser
|
||||
[100000, 70000], // A Winner/Loser
|
||||
[75000, 43250], // B Winner/Loser
|
||||
[50000, 25000], // C Winner/Loser
|
||||
[25000, 12500], // D Winner/Loser
|
||||
[12500, 6250], // E Winner/Loser
|
||||
[5000, 2500] // F Winner/Loser
|
||||
];
|
||||
|
||||
// Exchange stores
|
||||
var warrior = [[1302004, 7], [1402006, 7], [1302009, 10], [1402007, 10],
|
||||
[1302010, 20], [1402003, 20], [1312006, 7], [1412004, 7],
|
||||
[1312007, 10], [1412005, 10], [1312018, 20], [1412003, 20],
|
||||
[1322015, 7], [1422008, 7], [1322016, 10], [1422007, 10],
|
||||
[1322017, 20], [1422005, 20], [1432003, 7], [1442003, 7],
|
||||
[1432005, 10], [1442009, 10], [1442005, 20], [1432004, 20]];
|
||||
|
||||
var magician = [[1372001, 7], [1382018, 7], [1372012, 10], [1382019, 10],
|
||||
[1382001, 20], [1372007, 20]];
|
||||
|
||||
var archer = [[1452006, 7], [1452007, 10], [1452008, 20], [1462005, 7],
|
||||
[1462006, 10], [1462007, 20]];
|
||||
|
||||
var thief = [[1472013, 7], [1472017, 10], [1472021, 20], [1332014, 7],
|
||||
[1332011, 10], [1332031, 10], [1332016, 20], [1332034, 20]];
|
||||
|
||||
var pirate = [[1482005, 7], [1482006, 10], [1482007, 20], [1492005, 7],
|
||||
[1492006, 10], [1492007, 20]];
|
||||
|
||||
var necklace = [[1122007, 50], [2041211, 40]];
|
||||
|
||||
// Long Text Descriptions
|
||||
var infoText = "You wish to know about the Monster Carnival? Very well. The Monster Carnival is a place of trilling battles and exciting competiton against people just as strong and motivated as yourself. You must summon monsters and defeat the monsters summoned by the opposing party. That's the essence of the Monster Carnival. Once you enter the Carnival Field, the task is to earn CP by hunter monsters from the opposing party and use those CP's to distract the opposing party from hunting monsters. There are three ways to distract the other party; Summon a Monster, Skill or Protector. Please remember this though, it's never a good idea to save up CP just for the sake of it. The CP's you've used will also help determine the winner and the loser of the carnival.";
|
||||
var no = "You do not have enough Maple Coins for this item. Come back to me when you acquire more!";
|
||||
|
||||
function getGrade(cp) {
|
||||
// Returns index of corresponding expRewards pair.
|
||||
if (cp >= gradeS) {
|
||||
return 0;
|
||||
} else if (cp >= gradeA) {
|
||||
return 1;
|
||||
} else if (cp >= gradeB) {
|
||||
return 2;
|
||||
} else if (cp >= gradeC) {
|
||||
return 3;
|
||||
} else if (cp >= gradeD) {
|
||||
return 4;
|
||||
} else if (cp >= gradeE) {
|
||||
return 5;
|
||||
} else {
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
|
||||
function isTownMap(map) {
|
||||
for (var i = 0; i < infoMaps.length; i++) {
|
||||
if (infoMaps[i] == map) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isExitMap(map) {
|
||||
return map == 980000010;
|
||||
}
|
||||
|
||||
function isWinnerMap(map) {
|
||||
return (map >= 980000000 && map <= 980000700 && map % 10 == 3);
|
||||
}
|
||||
|
||||
function isLoserMap(map) {
|
||||
return (map >= 980000000 && map <= 980000700 && map % 10 == 4);
|
||||
}
|
||||
|
||||
var CONTEXT_NONE = -1;
|
||||
var CONTEXT_TOWN = 0;
|
||||
var CONTEXT_EXIT = 1;
|
||||
var CONTEXT_WIN = 2;
|
||||
var CONTEXT_LOSE = 3;
|
||||
|
||||
function start() {
|
||||
if (DISABLED) {
|
||||
cm.sendOk("CPQ is temporarily unavailable.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
m = cm.getMapId();
|
||||
if (isTownMap(m)) {
|
||||
ctx = CONTEXT_TOWN;
|
||||
} else if (isExitMap(m)) {
|
||||
ctx = CONTEXT_EXIT;
|
||||
} else if (isWinnerMap(m)) {
|
||||
ctx = CONTEXT_WIN;
|
||||
} else if (isLoserMap(m)) {
|
||||
ctx = CONTEXT_LOSE;
|
||||
} else {
|
||||
ctx = CONTEXT_NONE;
|
||||
}
|
||||
|
||||
action(1, 0, 0);
|
||||
}
|
||||
|
||||
function doLoserMap(mode, type, selection) {
|
||||
if (cm.getPlayer().getMCPQParty() == null) {
|
||||
cm.warp(MonsterCarnival.MAP_LOBBY);
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode == -1) {
|
||||
cm.dispose();
|
||||
} else {
|
||||
if (mode == 1) status++;
|
||||
else status--;
|
||||
|
||||
if (status == 0) {
|
||||
cm.sendNext("Unfortunately, you did not manage to win this round. Better luck next time!");
|
||||
} else if (status == 1) {
|
||||
var points = cm.getPlayer().getMCPQParty().getTotalCP();
|
||||
var grade = getGrade(points);
|
||||
var letterGrade = "ABCDF"[grade];
|
||||
var expReward = expRewards[grade][1];
|
||||
|
||||
cm.sendNext("Your grade is: #b" + letterGrade + "\r\n\r\n#kEXP Reward: " + expReward);
|
||||
cm.gainExp(expReward);
|
||||
} else if (status == 2) {
|
||||
cm.warp(MonsterCarnival.MAP_LOBBY);
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function doWinnerMap(mode, type, selection) {
|
||||
if (cm.getPlayer().getMCPQParty() == null) {
|
||||
cm.warp(MonsterCarnival.MAP_LOBBY);
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode == -1) {
|
||||
cm.dispose();
|
||||
} else {
|
||||
if (mode == 1) status++;
|
||||
else status--;
|
||||
|
||||
if (status == 0) {
|
||||
cm.sendNext("Congratulations! You managed to defeat the enemy team!");
|
||||
} else if (status == 1) {
|
||||
var points = cm.getPlayer().getMCPQParty().getTotalCP();
|
||||
var grade = getGrade(points);
|
||||
var letterGrade = "ABCDF"[grade];
|
||||
var expReward = expRewards[grade][0];
|
||||
|
||||
cm.sendNext("Your grade is: #b" + letterGrade + "\r\n\r\n#kEXP Reward: " + expReward);
|
||||
cm.gainExp(expReward);
|
||||
} else if (status == 2) {
|
||||
cm.warp(MonsterCarnival.MAP_LOBBY);
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function doTown(mode, type, selection) {
|
||||
if (mode == -1) {
|
||||
cm.sendOk("Be sure to vote for the server every 24 hours!");
|
||||
cm.dispose();
|
||||
} else {
|
||||
if (mode == 1) status++;
|
||||
else status--;
|
||||
|
||||
if (status == 0) {
|
||||
cm.sendSimple("What would you like to do? If you have never participated in the Monster Carnival, you'll need to know a thing or two about it before joining.\r\n\r\n#b#L0#Go to the Monster Carnival Field#l\r\n#L1#Learn about the Monster Carnival#l\r\n#L2#Trade Maple Coin#l");
|
||||
} else if (status == 1) {
|
||||
if (selection == 0) {
|
||||
if (cm.getChar().getLevel() < MonsterCarnival.MIN_LEVEL || cm.getChar().getLevel() > MonsterCarnival.MAX_LEVEL) {
|
||||
cm.sendOk("You must be between level " + MonsterCarnival.MIN_LEVEL + " and level " + MonsterCarnival.MAX_LEVEL + " to enter.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
cm.getChar().saveLocation(SavedLocationType.MONSTER_CARNIVAL);
|
||||
cm.warp(MonsterCarnival.MAP_LOBBY, 4);
|
||||
cm.dispose();
|
||||
return;
|
||||
} else if (selection == 1) {
|
||||
cm.sendPrev(infoText);
|
||||
cm.dispose();
|
||||
return;
|
||||
} else if (selection == 2) {
|
||||
store = true;
|
||||
cm.sendSimple("Select a category:\r\n" +
|
||||
"#L101##bTrade Maple Coins for Warrior Weapons\r\n" +
|
||||
"#L102#Trade Maple Coins for Magician Weapons\r\n" +
|
||||
"#L103#Trade Maple Coins for Bowman Weapons\r\n" +
|
||||
"#L104#Trade Maple Coins for Thief Weapons\r\n" +
|
||||
"#L105#Trade Maple Coins for Pirate Weapons\r\n" +
|
||||
"#L106#Trade Maple Coins for a Necklace");
|
||||
}
|
||||
} else if (status == 2) {
|
||||
if (store) {
|
||||
switch (selection) {
|
||||
case 101:
|
||||
storeInfo = warrior;
|
||||
break;
|
||||
case 102:
|
||||
storeInfo = magician;
|
||||
break;
|
||||
case 103:
|
||||
storeInfo = archer;
|
||||
break;
|
||||
case 104:
|
||||
storeInfo = thief;
|
||||
break;
|
||||
case 105:
|
||||
storeInfo = pirate;
|
||||
break;
|
||||
case 106:
|
||||
storeInfo = necklace;
|
||||
break;
|
||||
default:
|
||||
storeInfo = [];
|
||||
}
|
||||
if (storeInfo.length == 0) {
|
||||
cm.sendOk("That store doesn't exist.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
var storeText = "";
|
||||
for (var i = 0; i < storeInfo.length; ++i) {
|
||||
var wepId = storeInfo[i][0];
|
||||
var cost = storeInfo[i][1];
|
||||
storeText += "#L" + i + "##v" + wepId + "# - #z" + wepId + "# - " + cost + " " + coinIcon + "#l\r\n";
|
||||
}
|
||||
cm.sendSimple(storeText);
|
||||
} else {
|
||||
MCTracker.log("[MCPQ_Info] CONTEXT_TOWN: Invalid status 2");
|
||||
}
|
||||
} else if (status == 3) {
|
||||
if (store) {
|
||||
purchaseId = storeInfo[selection][0];
|
||||
purchaseCost = storeInfo[selection][1];
|
||||
|
||||
if (cm.haveItem(coinId, purchaseCost)) {
|
||||
cm.sendYesNo("Are you sure you want to purchase #i" + purchaseId + "#? You will have #r#e" + (cm.itemQuantity(coinId) - purchaseCost) + " " + coinIcon + "##k#n remaining.");
|
||||
} else {
|
||||
cm.sendOk("You don't have enough " + coinIcon + ".");
|
||||
cm.dispose();
|
||||
}
|
||||
} else {
|
||||
MCTracker.log("[MCPQ_Info] CONTEXT_TOWN: Invalid status 3");
|
||||
}
|
||||
} else if (status == 4) {
|
||||
if (store) {
|
||||
if (cm.haveItem(coinId, purchaseCost)) {
|
||||
cm.gainItem(coinId, -purchaseCost);
|
||||
cm.gainItem(purchaseId);
|
||||
cm.sendOk("Congratulations! Enjoy your new item.");
|
||||
cm.dispose();
|
||||
}
|
||||
} else {
|
||||
MCTracker.log("[MCPQ_Info] CONTEXT_TOWN: Invalid status 4");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function doExit() {
|
||||
cm.warp(MonsterCarnival.MAP_LOBBY);
|
||||
cm.sendOk("Hope you had fun in the Carnival PQ!");
|
||||
cm.dispose();
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
switch (ctx) {
|
||||
case CONTEXT_TOWN:
|
||||
doTown(mode, type, selection);
|
||||
break;
|
||||
case CONTEXT_EXIT:
|
||||
doExit();
|
||||
break;
|
||||
case CONTEXT_LOSE:
|
||||
doLoserMap(mode, type, selection);
|
||||
break;
|
||||
case CONTEXT_WIN:
|
||||
doWinnerMap(mode, type, selection);
|
||||
break;
|
||||
default:
|
||||
MCTracker.log("[MCPQ_INFO] Invalid context (value: " + ctx + ")");
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/** * [MENTION=19862]id[/MENTION] 2042003
|
||||
* [MENTION=806871]NPC[/MENTION] Assistant Red
|
||||
* [MENTION=836108]Function[/MENTION] Monster Carnival Waiting Room NPC
|
||||
* @author s4nta
|
||||
*/
|
||||
|
||||
// Relevant Monster Carnival classes
|
||||
var MonsterCarnival = net.sf.odinms.server.partyquest.mcpq.MonsterCarnival;
|
||||
var MCTracker = net.sf.odinms.server.partyquest.mcpq.MCTracker;
|
||||
var MCParty = net.sf.odinms.server.partyquest.mcpq.MCParty;
|
||||
var MCField = net.sf.odinms.server.partyquest.mcpq.MCField;
|
||||
var MCTeam = net.sf.odinms.server.partyquest.mcpq.MCField.MCTeam;
|
||||
|
||||
// NPC variables
|
||||
var status = -1;
|
||||
var carnival, field;
|
||||
var room = -1;
|
||||
|
||||
function start() {
|
||||
if (!MonsterCarnival.isLobbyMap(cm.getMapId())) {
|
||||
MCTracker.log("Assistant called on invalid map " + cm.getMapId() + " by player " + cm.getName());
|
||||
cm.sendOk("You are not authorized to do this.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
action(1, 0, 0);
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
if (mode == -1) {
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
if (mode == 1) status++;
|
||||
else status--;
|
||||
|
||||
if (status == 0) {
|
||||
if (cm.getParty() == null) {
|
||||
cm.warp(MonsterCarnival.MAP_LOBBY);
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
options = ["#L1#Leave the room #r#e(WARNING: Abusing this will block you from future Carnival PQs)#b#n.#l",
|
||||
"#L2#Close NPC#l"];
|
||||
if (cm.isLeader()) {
|
||||
options.unshift("#L0#View pending challenges#l");
|
||||
}
|
||||
|
||||
text = "Welcome to Carnival PQ. I am #rAssistant Red#k. What can I do for you?#b\r\n";
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
text += options[i];
|
||||
text += "\r\n";
|
||||
}
|
||||
cm.sendSimple(text);
|
||||
} else if (status == 1) {
|
||||
field = cm.getChar().getMCPQField();
|
||||
if (selection == 0) {
|
||||
if (!cm.isLeader()) {
|
||||
cm.sendOk("You are not authorized to do this.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
if (!field.hasPendingRequests()) {
|
||||
cm.sendOk("There are no pending requests at this time.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
cm.sendSimple(field.getNPCRequestString());
|
||||
} else if (selection == 1) {
|
||||
if (field != null) {
|
||||
field.deregister(true);
|
||||
} else {
|
||||
cm.warp(MonsterCarnival.MAP_EXIT);
|
||||
}
|
||||
cm.dispose();
|
||||
} else {
|
||||
cm.dispose();
|
||||
}
|
||||
} else if (status == 2) {
|
||||
var code = field.acceptRequest(selection);
|
||||
if (code == 1) {
|
||||
cm.sendOk("The challenge was accepted.");
|
||||
} else {
|
||||
cm.sendOk("An unknown error occurred.");
|
||||
}
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/** * [MENTION=19862]id[/MENTION] 2042004
|
||||
* [MENTION=806871]NPC[/MENTION] Assistant Blue
|
||||
* [MENTION=836108]Function[/MENTION] Monster Carnival Waiting Room NPC
|
||||
* @author s4nta
|
||||
*/
|
||||
|
||||
// Relevant Monster Carnival classes
|
||||
var MonsterCarnival = net.sf.odinms.server.partyquest.mcpq.MonsterCarnival;
|
||||
var MCTracker = net.sf.odinms.server.partyquest.mcpq.MCTracker;
|
||||
var MCParty = net.sf.odinms.server.partyquest.mcpq.MCParty;
|
||||
var MCField = net.sf.odinms.server.partyquest.mcpq.MCField;
|
||||
var MCTeam = net.sf.odinms.server.partyquest.mcpq.MCField.MCTeam;
|
||||
|
||||
// NPC variables
|
||||
var status = -1;
|
||||
var carnival, field;
|
||||
var room = -1;
|
||||
|
||||
function start() {
|
||||
if (!MonsterCarnival.isLobbyMap(cm.getMapId())) {
|
||||
MCTracker.log("Assistant called on invalid map " + cm.getMapId() + " by player " + cm.getName());
|
||||
cm.sendOk("You are not authorized to do this.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
action(1, 0, 0);
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
if (mode == -1) {
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
if (mode == 1) status++;
|
||||
else status--;
|
||||
|
||||
if (status == 0) {
|
||||
if (cm.getParty() == null) {
|
||||
cm.warp(MonsterCarnival.MAP_LOBBY);
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
options = ["#L1#Leave the room #r#e(WARNING: Abusing this will block you from future Carnival PQs)#b#n.#l",
|
||||
"#L2#Close NPC#l"];
|
||||
if (cm.isLeader()) {
|
||||
options.unshift("#L0#View pending challenges#l");
|
||||
}
|
||||
|
||||
text = "Welcome to Carnival PQ. I am #bAssistant Blue#k. What can I do for you?#b\r\n";
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
text += options[i];
|
||||
text += "\r\n";
|
||||
}
|
||||
cm.sendSimple(text);
|
||||
} else if (status == 1) {
|
||||
field = cm.getChar().getMCPQField();
|
||||
if (selection == 0) {
|
||||
if (!cm.isLeader()) {
|
||||
cm.sendOk("You are not authorized to do this.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
if (!field.hasPendingRequests()) {
|
||||
cm.sendOk("There are no pending requests at this time.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
cm.sendSimple(field.getNPCRequestString());
|
||||
} else if (selection == 1) {
|
||||
if (field != null) {
|
||||
field.deregister(true);
|
||||
} else {
|
||||
cm.warp(MonsterCarnival.MAP_EXIT);
|
||||
}
|
||||
cm.dispose();
|
||||
} else {
|
||||
cm.dispose();
|
||||
}
|
||||
} else if (status == 2) {
|
||||
var code = field.acceptRequest(selection);
|
||||
if (code == 1) {
|
||||
cm.sendOk("The challenge was accepted.");
|
||||
} else {
|
||||
cm.sendOk("An unknown error occurred.");
|
||||
}
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
function enter(pi) { player = pi.getPlayer();
|
||||
if (player.getMCPQField() != null) {
|
||||
player.getMCPQField().onRevive(player);
|
||||
} else {
|
||||
pi.warp(980000000);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
function enter(pi) { player = pi.getPlayer();
|
||||
if (player.getMCPQField() != null) {
|
||||
player.getMCPQField().onRevive(player);
|
||||
} else {
|
||||
pi.warp(980000000);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
function enter(pi) { player = pi.getPlayer();
|
||||
if (player.getMCPQField() != null) {
|
||||
player.getMCPQField().onRevive(player);
|
||||
} else {
|
||||
pi.warp(980000000);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
function enter(pi) { player = pi.getPlayer();
|
||||
if (player.getMCPQField() != null) {
|
||||
player.getMCPQField().onRevive(player);
|
||||
} else {
|
||||
pi.warp(980000000);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
function enter(pi) { player = pi.getPlayer();
|
||||
if (player.getMCPQField() != null) {
|
||||
player.getMCPQField().onRevive(player);
|
||||
} else {
|
||||
pi.warp(980000000);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
function enter(pi) { player = pi.getPlayer();
|
||||
if (player.getMCPQField() != null) {
|
||||
player.getMCPQField().onRevive(player);
|
||||
} else {
|
||||
pi.warp(980000000);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
This file is part of the OdinMS Maple Story Server
|
||||
Copyright (C) 2008 Patrick Huy <[email protected]>
|
||||
Matthias Butz <[email protected]>
|
||||
Jan Christian Meyer <[email protected]>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License 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/>.
|
||||
*/
|
||||
|
||||
importPackage(Packages.net.sf.odinms.server.maps);
|
||||
|
||||
/*
|
||||
Return from MCPQ map.
|
||||
*/
|
||||
|
||||
function enter(pi) {
|
||||
var returnMap = pi.getPlayer().getSavedLocation(SavedLocationType.MONSTER_CARNIVAL);
|
||||
if (returnMap < 0) {
|
||||
returnMap = 100000000; // to fix people who entered the fm trough an unconventional way
|
||||
}
|
||||
var target = pi.getPlayer().getClient().getChannelServer().getMapFactory().getMap(returnMap);
|
||||
var targetPortal;
|
||||
|
||||
if (returnMap == 230000000) {
|
||||
targetPortal = target.getPortal("market01");
|
||||
} else {
|
||||
targetPortal = target.getPortal("market00");
|
||||
}
|
||||
|
||||
if (targetPortal == null)
|
||||
targetPortal = target.getPortal(0);
|
||||
|
||||
if (pi.getPlayer().getMapId() != target) {
|
||||
pi.getPlayer().clearSavedLocation(SavedLocationType.MONSTER_CARNIVAL);
|
||||
pi.getPlayer().changeMap(target, targetPortal);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
Under the respawn map change code (!c.getPlayer().isAlive()), add before executeStandardPath is done:
|
||||
PHP Code:
|
||||
if (player.getMCPQField() != null) { player.getMCPQField().onPlayerRespawn(player);
|
||||
return;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
Add before items are added to inventory, or after mesos are handled:
|
||||
PHP Code:
|
||||
|
||||
else if (c.getPlayer().getMCPQField() != null) { // CPQ Handling boolean consumed = c.getPlayer().getMCPQField().onItemPickup(c.getPlayer(), mapitem);
|
||||
if (consumed) {
|
||||
c.getPlayer().getMap().broadcastMessage(MaplePacketCreator.removeItemFromMap(mapitem.getObjectId(), 2, c.getPlayer().getId()),
|
||||
mapitem.getPosition());
|
||||
c.getPlayer().getCheatTracker().pickupComplete();
|
||||
c.getPlayer().getMap().removeMapObject(ob);
|
||||
} else {
|
||||
if (MapleInventoryManipulator.addFromDrop(c, mapitem.getItem(), true)) {
|
||||
c.getPlayer().getMap().broadcastMessage(
|
||||
MaplePacketCreator.removeItemFromMap(mapitem.getObjectId(), 2, c.getPlayer().getId()),
|
||||
mapitem.getPosition());
|
||||
c.getPlayer().getCheatTracker().pickupComplete();
|
||||
c.getPlayer().getMap().removeMapObject(ob);
|
||||
} else {
|
||||
c.getPlayer().getCheatTracker().pickupComplete();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
client/MapleCharacter.java
|
||||
Add fields:
|
||||
PHP Code:
|
||||
private MCField.MCTeam MCPQTeam;private MCParty MCPQParty;
|
||||
private MCField MCPQField;
|
||||
private int availableCP = 0;
|
||||
private int totalCP = 0;
|
||||
under playerDead() method:
|
||||
PHP Code:
|
||||
if (player.getMap().isTown()) { XPdummy *= 0.01;
|
||||
} else if (MonsterCarnival.isBattlefieldMap(player.getMapId())) {
|
||||
XPdummy = 0;
|
||||
}
|
||||
under the giveDebuff() method, add a field that force adds the disease if some variable cpq is set, regardless of buffs.
|
||||
method signature:
|
||||
PHP Code:
|
||||
public void giveDebuff(MapleDisease disease, MobSkill skill, boolean cpq)
|
||||
Add these methods:
|
||||
PHP Code:
|
||||
public int getTeam() { if (this.MCPQTeam == null) {
|
||||
return -1;
|
||||
}
|
||||
return this.MCPQTeam.code;
|
||||
}
|
||||
|
||||
public MCField.MCTeam getMCPQTeam() {
|
||||
return MCPQTeam;
|
||||
}
|
||||
|
||||
public void setMCPQTeam(MCField.MCTeam MCPQTeam) {
|
||||
this.MCPQTeam = MCPQTeam;
|
||||
}
|
||||
|
||||
public MCParty getMCPQParty() {
|
||||
return MCPQParty;
|
||||
}
|
||||
|
||||
public void setMCPQParty(MCParty MCPQParty) {
|
||||
this.MCPQParty = MCPQParty;
|
||||
}
|
||||
|
||||
public MCField getMCPQField() {
|
||||
return MCPQField;
|
||||
}
|
||||
|
||||
public void setMCPQField(MCField MCPQField) {
|
||||
this.MCPQField = MCPQField;
|
||||
}
|
||||
|
||||
public int getAvailableCP() {
|
||||
return availableCP;
|
||||
}
|
||||
|
||||
public void setAvailableCP(int availableCP) {
|
||||
this.availableCP = availableCP;
|
||||
}
|
||||
|
||||
public int getTotalCP() {
|
||||
return totalCP;
|
||||
}
|
||||
|
||||
public void setTotalCP(int totalCP) {
|
||||
this.totalCP = totalCP;
|
||||
}
|
||||
|
||||
public void gainCP(int cp) {
|
||||
this.availableCP += cp;
|
||||
this.totalCP += cp;
|
||||
}
|
||||
|
||||
public void loseCP(int cp) {
|
||||
this.availableCP -= cp;
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
Add this under disconnect() (right after event instance calls onPlayerDisconnect, preferably):
|
||||
PHP Code:
|
||||
if (chr.getMCPQField() != null) {
|
||||
chr.getMCPQField().onPlayerDisconnected(player);
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
Add to getMonster(int monsterId):
|
||||
PHP Code:
|
||||
stats.setCp(MapleDataTool.getIntConvert("getCP", monsterInfoData, 0));
|
||||
@@ -1,80 +0,0 @@
|
||||
Add fields:
|
||||
PHP Code:
|
||||
private boolean respawning = true;
|
||||
private MCWZData mcpqData;
|
||||
Add methods:
|
||||
PHP Code:
|
||||
public final List<MapleMonster> getAllMonsters() { return getAllMapObjects(MapleMapObjectType.MONSTER);
|
||||
}
|
||||
|
||||
public void addMonsterSpawn(MapleMonster monster, int mobTime, int team) {
|
||||
Point newpos = calcPointBelow(monster.getPosition());
|
||||
newpos.y -= 1;
|
||||
SpawnPoint sp = new SpawnPoint(monster, newpos, mobTime, team);
|
||||
monsterSpawn.add(sp);
|
||||
if (!respawning) return;
|
||||
|
||||
if (sp.shouldSpawn() || mobTime == -1) {
|
||||
sp.spawnMonster(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void setReactorState(MapleReactor reactor, byte state) {
|
||||
synchronized (this.mapobjects) {
|
||||
reactor.setState(state);
|
||||
broadcastMessage(MaplePacketCreator.triggerReactor(reactor, state));
|
||||
}
|
||||
}
|
||||
|
||||
public <E extends MapleMapObject> List<E> getAllMapObjects(MapleMapObjectType type) {
|
||||
List<E> ret = new ArrayList<>();
|
||||
synchronized (mapobjects) {
|
||||
for (MapleMapObject l : mapobjects.values()) {
|
||||
if (l.getType() == type) {
|
||||
ret.add((E) l);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void clearDrops() {
|
||||
List<MapleMapItem> items = getAllMapObjects(MapleMapObjectType.ITEM);
|
||||
for (MapleMapItem itemmo : items) {
|
||||
removeMapObject(itemmo);
|
||||
broadcastMessage(MaplePacketCreator.removeItemFromMap(itemmo.getObjectId(), 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<SpawnPoint> getSpawnPoints() {
|
||||
return monsterSpawn;
|
||||
}
|
||||
|
||||
public void respawn() {
|
||||
for (SpawnPoint sp : this.monsterSpawn) {
|
||||
if (sp.shouldSpawn()) {
|
||||
sp.spawnMonster(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void beginSpawning() {
|
||||
this.respawning = true;
|
||||
this.respawn();
|
||||
}
|
||||
|
||||
public boolean isRespawning() {
|
||||
return respawning;
|
||||
}
|
||||
|
||||
public void setRespawning(boolean respawning) {
|
||||
this.respawning = respawning;
|
||||
}
|
||||
|
||||
public MCWZData getMCPQData() {
|
||||
return this.mcpqData;
|
||||
}
|
||||
|
||||
public void setMCPQData(MCWZData data) {
|
||||
this.mcpqData = data;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
Add to getMap() method, under where it parses PQ areas:
|
||||
PHP Code:
|
||||
MapleData mcData = mapData.getChildByPath("monsterCarnival");if (mcData != null) {
|
||||
MCWZData mcpqInfo = new MCWZData(mcData);
|
||||
map.setMCPQData(mcpqInfo);
|
||||
map.setRespawning(false);
|
||||
}
|
||||
Add to getMap() method, under where it parses mobTime:
|
||||
PHP Code:
|
||||
int team = MapleDataTool.getInt("team", life, -1);
|
||||
Change addMonsterSpawn() method call to the following, using the new SpawnPoint construction we defined in MapleMap:
|
||||
PHP Code:
|
||||
map.addMonsterSpawn(monster, mobTime, team);
|
||||
Add the method:
|
||||
PHP Code:
|
||||
public MapleMap instanceMap(int mapid, boolean respawns, boolean npcs) { return instanceMap(mapid, respawns, npcs, true);
|
||||
}
|
||||
and
|
||||
|
||||
PHP Code:
|
||||
public MapleMap instanceMap(int mapid, boolean respawns, boolean npcs, boolean reactors)
|
||||
, where the code is the same as getMap but does not try to load from cache and does not store into cache.
|
||||
@@ -1,31 +0,0 @@
|
||||
Add fields:
|
||||
PHP Code:
|
||||
private int team = -1;
|
||||
|
||||
Add methods:
|
||||
PHP Code:
|
||||
public int getCP() { return stats.getCp();
|
||||
}
|
||||
|
||||
public int getTeam() {
|
||||
return team;
|
||||
}
|
||||
|
||||
public void setTeam(int team) {
|
||||
this.team = team;
|
||||
}
|
||||
|
||||
public void dispel() {
|
||||
if (!isAlive()) return;
|
||||
|
||||
for (MonsterStatus i : MonsterStatus.values()) {
|
||||
if (monsterBuffs.contains(i)) {
|
||||
removeMonsterBuff(i);
|
||||
MaplePacket packet = MaplePacketCreator.cancelMonsterStatus(getObjectId(), Collections.singletonMap(i, Integer.valueOf(1)));
|
||||
map.broadcastMessage(packet, getPosition());
|
||||
if (getController() != null && !getController().isMapObjectVisible(MapleMonster.this)) {
|
||||
getController().getClient().getSession().write(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
Add field:
|
||||
PHP Code:
|
||||
private int cp;
|
||||
|
||||
Add methods:
|
||||
PHP Code:
|
||||
public int getCp() {
|
||||
return cp;
|
||||
}
|
||||
|
||||
public void setCp(int cp) {
|
||||
this.cp = cp;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
Under hitReactor(), add before standard handling:
|
||||
PHP Code:
|
||||
if (c.getPlayer().getMCPQField() != null) {
|
||||
c.getPlayer().getMCPQField().onGuardianHit(c.getPlayer(), this);
|
||||
return;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
Add fields:
|
||||
PHP Code:
|
||||
private boolean consumeOnPickup, party;
|
||||
private int cp, nuffSkill;
|
||||
|
||||
Add to stat parsing (loadFromData()):
|
||||
PHP Code:
|
||||
|
||||
ret.cp = MapleDataTool.getInt("cp", source, 0);ret.party = MapleDataTool.getInt("party", source, 0) > 0;
|
||||
ret.consumeOnPickup = MapleDataTool.getInt("consumeOnPickup", source, 0) > 0;
|
||||
ret.nuffSkill = MapleDataTool.getInt("nuffSkill", source, -1);
|
||||
Add methods:
|
||||
PHP Code:
|
||||
public int getCP() { return cp;
|
||||
}
|
||||
|
||||
public boolean isParty() {
|
||||
return party;
|
||||
}
|
||||
|
||||
public boolean isConsumeOnPickup() {
|
||||
return consumeOnPickup;
|
||||
}
|
||||
|
||||
public int getNuffSkill() {
|
||||
return nuffSkill;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
Add to applyEffect():
|
||||
|
||||
PHP Code:
|
||||
case 150: monStat = MonsterStatus.WEAPON_ATTACK_UP;
|
||||
break;
|
||||
case 151:
|
||||
monStat = MonsterStatus.WEAPON_DEFENSE_UP;
|
||||
break;
|
||||
case 152:
|
||||
monStat = MonsterStatus.MAGIC_ATTACK_UP;
|
||||
break;
|
||||
case 153:
|
||||
monStat = MonsterStatus.MAGIC_DEFENSE_UP;
|
||||
break;
|
||||
case 154:
|
||||
monStat = MonsterStatus.ACC;
|
||||
break;
|
||||
case 155:
|
||||
monStat = MonsterStatus.AVOID;
|
||||
break;
|
||||
case 156:
|
||||
monStat = MonsterStatus.SPEED;
|
||||
break;
|
||||
@@ -1,41 +0,0 @@
|
||||
package net.sf.odinms.net.channel.handler;
|
||||
import net.sf.odinms.client.MapleCharacter;
|
||||
import net.sf.odinms.client.MapleClient;
|
||||
import net.sf.odinms.net.AbstractMaplePacketHandler;
|
||||
import net.sf.odinms.server.partyquest.mcpq.MCField;
|
||||
import net.sf.odinms.server.partyquest.mcpq.MCTracker;
|
||||
import net.sf.odinms.server.partyquest.mcpq.MonsterCarnival;
|
||||
import net.sf.odinms.tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
/**
|
||||
* Packet handler for Monster Carnival.
|
||||
* @author s4nta
|
||||
*/
|
||||
public class MonsterCarnivalHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
@Override
|
||||
public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
int tab = slea.readByte();
|
||||
int num = slea.readByte();
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
|
||||
if (MonsterCarnival.DEBUG) {
|
||||
MCTracker.log("[MCHandler] " + chr.getName() + " used tab " + tab + " num " + num);
|
||||
System.out.println("[MCHandler] " + chr.getName() + " used tab " + tab + " num " + num);
|
||||
}
|
||||
|
||||
if (chr.getMCPQField() == null || chr.getMCPQParty() == null) {
|
||||
MCTracker.log("[MCHandler] " + chr.getName() + " attempting to use Monster Carnival handler without being in Monster Carnival");
|
||||
return;
|
||||
}
|
||||
|
||||
MCField field = chr.getMCPQField();
|
||||
if (tab == 0) {
|
||||
field.onAddSpawn(c.getPlayer(), num);
|
||||
} else if (tab == 1) {
|
||||
field.onUseSkill(c.getPlayer(), num);
|
||||
} else if (tab == 2) { // status
|
||||
field.onGuardianSummon(c.getPlayer(), num);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
else if (c.getPlayer().getMCPQField() != null) { // CPQ Handling
|
||||
boolean consumed = c.getPlayer().getMCPQField().onItemPickup(c.getPlayer(), mapitem);
|
||||
if (consumed) {
|
||||
c.getPlayer().getMap().broadcastMessage(MaplePacketCreator.removeItemFromMap(mapitem.getObjectId(), 2, c.getPlayer().getId()),
|
||||
mapitem.getPosition());
|
||||
c.getPlayer().getCheatTracker().pickupComplete();
|
||||
c.getPlayer().getMap().removeMapObject(ob);
|
||||
} else {
|
||||
if (MapleInventoryManipulator.addFromDrop(c, mapitem.getItem(), true)) {
|
||||
c.getPlayer().getMap().broadcastMessage(
|
||||
MaplePacketCreator.removeItemFromMap(mapitem.getObjectId(), 2, c.getPlayer().getId()),
|
||||
mapitem.getPosition());
|
||||
c.getPlayer().getCheatTracker().pickupComplete();
|
||||
c.getPlayer().getMap().removeMapObject(ob);
|
||||
} else {
|
||||
c.getPlayer().getCheatTracker().pickupComplete();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
Add field:
|
||||
PHP Code:
|
||||
private int team = -1;
|
||||
Add constructor:
|
||||
PHP Code:
|
||||
public SpawnPoint(MapleMonster monster, Point pos, int mobTime, int team) { super();
|
||||
this.monster = monster;
|
||||
this.pos = new Point(pos);
|
||||
this.mobTime = mobTime;
|
||||
this.immobile = !monster.isMobile();
|
||||
this.nextPossibleSpawn = System.currentTimeMillis();
|
||||
this.team = team;
|
||||
}
|
||||
Add monster death listener under spawnMonster():
|
||||
PHP Code:
|
||||
if (team > -1) { final int cp = mob.getCP();
|
||||
mob.addListener(new MonsterListener() {
|
||||
|
||||
@Override
|
||||
public void monsterKilled(MapleMonster monster, MapleCharacter highestDamageChar) {
|
||||
if (highestDamageChar == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (highestDamageChar.getMCPQParty() == null) {
|
||||
MCTracker.log("Attempted to give CP to character without assigned MCPQ Party.");
|
||||
return;
|
||||
}
|
||||
|
||||
highestDamageChar.getMCPQField().monsterKilled(highestDamageChar, cp);
|
||||
|
||||
}
|
||||
});
|
||||
mob.setTeam(team);
|
||||
}
|
||||
+19
-1
@@ -938,4 +938,22 @@ Corrigido Body Pressure não mostrando valor de dano para outros jogadores.
|
||||
Adicionado flag que permite town scrolls atuarem como se fossem "player banishes", permitindo ativação do antibanish scroll.
|
||||
|
||||
09 Maio 2018,
|
||||
Corrigido autopot handler consumindo mais pots que o necessário para pots com ganhos percentuais.
|
||||
Corrigido autopot handler consumindo mais pots que o necessário para pots com ganhos percentuais.
|
||||
|
||||
10 - 11 Maio 2018,
|
||||
Adicionado suporte para Kite's.
|
||||
Revisto acesso concorrente com módulos de pet autopot.
|
||||
Tomado medida paliativa pra tentar resolver o problema com players retidos em certos channels do jogo (que leva ao mesmo não conseguir logar num channel).
|
||||
Implementado quest da harpa (questid 3314), além de implementado som na harpa podendo ser ouvido por todos os jogadores no mapa.
|
||||
Adicionado efeito sonoro para destacar ação de metro (sinaliza chegando/saindo similarmente ao usado pelos outros objetos de viagem).
|
||||
Adicionado suporte para player NPCs e hall da fama.
|
||||
|
||||
14 - 19 Maio 2018,
|
||||
Resolvido bug com NPC prefeito de NLC, permitindo jogador repetir quiz o quanto quiser.
|
||||
Calibrado sistema de automatização de novas posições para player NPCs.
|
||||
Adicionado sistema de ranqueamento para player NPCs.
|
||||
Adicionado flag que define novo requerimento de mobs para questline do Temple of Time.
|
||||
Corrigido pets permanentes expirando com o tempo.
|
||||
Adicionado suporte para Cash Shop Surprise.
|
||||
Revisado sistema de handlers que gerencia login no server.
|
||||
Resolvido um problema com flag ultra three snails.
|
||||
@@ -1,206 +0,0 @@
|
||||
/**
|
||||
---------------------------------------------------------------------------------------------------
|
||||
* NPC Name: High Priest John - ID: 9201002.js
|
||||
* @author Vcoc
|
||||
* @author Ronan
|
||||
---------------------------------------------------------------------------------------------------
|
||||
**/
|
||||
importPackage(Packages.client);
|
||||
importPackage(Packages.server);
|
||||
importPackage(Packages.tools);
|
||||
|
||||
var status;
|
||||
var minLevel = 10;
|
||||
var maxLevel = 200;
|
||||
var mySelection = -1;
|
||||
var rings = Array(1112001, 1112002, 1112003, 1112005, 1112006);
|
||||
|
||||
function start() {
|
||||
status = -1;
|
||||
action(1, 0, 0);
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
if (mode == 1) {
|
||||
status++;
|
||||
} else {
|
||||
if (type == 1 && mode == 0)
|
||||
cm.sendOk("...");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if (cm.getPlayer().getMapId() == 680000000) {
|
||||
if (status == 0) {
|
||||
cm.sendSimple("Olá #h #,\r\n#bO que você gostaria de fazer hoje?#b\r\n#L0#Eu quero me casar!#l\r\n#L1#Eu quero ver meus amigos no casamento!#l\r\n#L2#Quero trocar um Bilhete Premium de casamento por 5 convites!#l\r\n#L3#Eu quero comprar um Bilhete de Casamento!#l\r\n#L4#Gostaria de obter uma licença de casamento.#l");
|
||||
} else if (status == 1) {
|
||||
if (selection == 0) {
|
||||
if (cm.getParty() == null) { // no party
|
||||
cm.sendOk("Para se casar e necessario estar em grupo com a parceira(o)!");
|
||||
cm.dispose();
|
||||
} else if (!cm.isLeader()) { // not party leader
|
||||
cm.sendOk("Por favor, fale ao seu parceiro(a) para falar comigo.");
|
||||
cm.dispose();
|
||||
} else {
|
||||
var party = cm.getParty().getMembers();
|
||||
var mapId = cm.getPlayer().getMapId();
|
||||
var levelValid = 0;
|
||||
var genderRight = 0;
|
||||
var alreadyMarried = 0;
|
||||
for (var i = 0; i < party.size(); i++) {
|
||||
var pPlayer = party.get(i);
|
||||
if (pPlayer.getLevel() >= minLevel && pPlayer.getLevel() <= maxLevel)
|
||||
levelValid += 1;
|
||||
if (pPlayer.getGender() == 0) {
|
||||
genderRight += 1;
|
||||
} else if (pPlayer.getGender() == 1) {
|
||||
genderRight += 2;
|
||||
}
|
||||
if (pPlayer.isMarried() == 1) {
|
||||
alreadyMarried += 1;
|
||||
}
|
||||
}
|
||||
if (party.size() == 2) {
|
||||
if (party.get(0).getGender() == 0) { // leader.
|
||||
if (levelValid == 2 || cm.partyMembersInMap() == 2) {
|
||||
if (genderRight == 3) {
|
||||
if (alreadyMarried == 0) {
|
||||
if (cm.haveItem(4031374, 1) && cm.haveItem(5251003, 1)) {
|
||||
// Kick it into action. Slate says nothing here, just warps you in.
|
||||
var em = cm.getEventManager("CathedralWedding");
|
||||
if (em == null) {
|
||||
cm.sendOk("Indisponivel!");
|
||||
} else {
|
||||
// Begin the Wedding o.O
|
||||
em.startInstance(cm.getParty(), cm.getPlayer().getMap());
|
||||
party = cm.getPlayer().getEventInstance().getPlayers();
|
||||
var hname = party.get(0).getName();
|
||||
var wname = party.get(1).getName();
|
||||
var StringLine = "[" + hname + " & " + wname + "] Irão se casar na Catedral, no canal (" + cm.getC().getChannel() + ").";
|
||||
cm.worldMessage(5, StringLine);
|
||||
var eim = cm.getChar().getEventInstance();
|
||||
eim.setProperty("husband", party.get(0).getName());
|
||||
eim.setProperty("wife", party.get(1).getName());
|
||||
}
|
||||
} else
|
||||
cm.sendOk("Você não tem os itens necessários, desculpe!");
|
||||
} else
|
||||
cm.sendOk("Você já está casado.");
|
||||
} else
|
||||
cm.sendOk("O nosso servidor não suporta este tipo de casamento!");
|
||||
} else
|
||||
cm.sendOk("Vocês precisam estar no mesmo mapa e ser pelo menos no nível ["+minLevel+" ~ "+maxLevel+"]");
|
||||
} else
|
||||
cm.sendOk("Por favor faça o teu marido lider do grupo.");
|
||||
} else
|
||||
cm.sendOk("Somentos marido e mulher no grupo!");
|
||||
cm.dispose();
|
||||
}
|
||||
} else if (selection == 1) {
|
||||
if (cm.haveItem(5251100, 1)) {
|
||||
cm.sendGetText("Por favor, insira o nome de um dos membros do casamento .");
|
||||
} else {
|
||||
cm.sendOk("Parece que o casal que você quer assistir não te deu um convite ainda.");
|
||||
cm.dispose();
|
||||
}
|
||||
} else if (selection == 2) {
|
||||
if (cm.haveItem(5251003, 1)) {
|
||||
cm.gainItem(5251003, -1);
|
||||
cm.gainItem(5251100,5);
|
||||
} else {
|
||||
cm.sendOk("Você não tem o Bilhete de Casamento Premium.");
|
||||
}
|
||||
cm.dispose();
|
||||
} else if (selection == 3) {
|
||||
cm.sendOk("Você pode comprar um Bilhete de Casamento com a Ria! Localizada no Mercado Livre.");
|
||||
|
||||
cm.dispose();
|
||||
} else if (selection == 4) {
|
||||
if (cm.getPlayer().getMarriageQuestLevel() == 50) {
|
||||
cm.sendNext("Por favor, vá visitar a Mom eo Dad em sua casa. Eles vivem em algum lugar Henesys Hunting Ground II.");
|
||||
cm.getPlayer().addMarriageQuestLevel();
|
||||
} else if (cm.getPlayer().getMarriageQuestLevel() == 53) {
|
||||
if (cm.haveItem(4031373, 1)) {
|
||||
cm.sendNext("Pronto, aqui sua permissão!");
|
||||
cm.removeAll(4031373);
|
||||
cm.gainItem(4031374, 1);
|
||||
cm.getPlayer().setMarriageQuestLevel(100);
|
||||
} else {
|
||||
cm.sendNext("Você não tem a benção de Mom e Dad!");
|
||||
}
|
||||
} else {
|
||||
cm.sendNext("Eu não sei o que você está falando.");
|
||||
}
|
||||
cm.dispose();
|
||||
}
|
||||
} else if (status == 2) {
|
||||
var chr = cm.getCharByName(cm.getText());
|
||||
if (chr != null) {
|
||||
if (chr.getMapId() == 680000200) {
|
||||
var eim = chrr.getEventInstance();
|
||||
eim.registerPlayer(cm.getPlayer());
|
||||
} else {
|
||||
cm.sendOk("O casamento que você gostaria de participar não foi iniciado.");
|
||||
}
|
||||
} else
|
||||
cm.sendOk("Jogador não encontrado.");
|
||||
cm.dispose();
|
||||
}
|
||||
} else if (cm.getPlayer().getMapId() == 680000210) {
|
||||
var eim = cm.getPlayer().getEventInstance();
|
||||
|
||||
var husbandName = eim.getProperty("husband");
|
||||
var wifeName = eim.getProperty("wife");
|
||||
|
||||
var husband = cm.getCharByName(husbandName);
|
||||
var wife = cm.getCharByName(wifeName);
|
||||
|
||||
var id = cm.getPlayer().getId();
|
||||
|
||||
var hclicked = eim.getProperty("hclicked");
|
||||
var wclicked = eim.getProperty("hclicked");
|
||||
|
||||
var otherChar = husband == cm.getPlayer() ? wife : husband;
|
||||
|
||||
if (husband != null && wife != null) {
|
||||
if (status == 0) {
|
||||
if (id != husband.getId() && id != husband.getId()) {
|
||||
cm.sendOk("Você não está se casando!");
|
||||
cm.dispose();
|
||||
} else if (cm.getPlayer().isMarried() > 0) {
|
||||
cm.sendOk("Você já foi casado.");
|
||||
cm.dispose();
|
||||
} else if (hclicked == 1 && husbandName.equals(cm.getPlayer().getName())) {
|
||||
cm.sendOk("Você já aceitou casar-se com sua esposa, pergunte à sua esposa a aceitar agora");
|
||||
cm.dispose();
|
||||
} else if (wclicked == 1 && wifeName.equals(cm.getPlayer().getName())) {
|
||||
cm.sendOk("Você já aceitou casar-se com o seu marido , pergunte ao seu marido a aceitar agora");
|
||||
cm.dispose();
|
||||
} else {
|
||||
cm.sendYesNo("Você deseja se casar com seu parceiro?\r\n\r\nIsso será uma decisão final.");
|
||||
}
|
||||
} else if (status == 1) {
|
||||
if (husband == cm.getPlayer())
|
||||
eim.setProperty("hclicked", 1);
|
||||
else if (wife == cm.getPlayer())
|
||||
eim.setProperty("wclicked", 1);
|
||||
else {
|
||||
cm.sendOk("Como assim?");
|
||||
cm.dispose();
|
||||
}
|
||||
|
||||
if (eim.getProperty("hclicked") == 1 && eim.getProperty("hclicked") == 1) {
|
||||
if (!cm.createMarriage(otherChar.getName())) {
|
||||
cm.sendOk("O sistema não pode encontrar o seu parceiro.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
cm.worldMessage(5, "Parabéns a "+husbandName+" e "+wifeName+". São recém-casados, você pode enchê-los de spam agora!");
|
||||
cm.removeAll(4031374);
|
||||
MapleInventoryManipulator.removeById(otherChar.getClient(), MapleInventoryType.USE, 4031374, otherChar.getItemQuantity(4031374, false), false, false);
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
var status = 0;
|
||||
|
||||
function start() {
|
||||
status = -1;
|
||||
action(1, 0, 0);
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
if (mode == -1) {
|
||||
cm.dispose();
|
||||
}else if (mode == 0){
|
||||
cm.dispose();
|
||||
return;
|
||||
} else {
|
||||
if (mode == 1)
|
||||
status++;
|
||||
else
|
||||
status--;
|
||||
if (status == 0) {
|
||||
cm.sendOk("Wow Vickii! You look so beautiful today. Are you ready to move on to the next part of this surprise?\r\n I will be giving you a tour for our chapel.");
|
||||
cm.getPlayer().startWedding();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4334,6 +4334,7 @@ etc
|
||||
926023400 - Hidden Street - Nett's Pyramid
|
||||
926023500 - Hidden Street - Nett's Pyramid
|
||||
930010000 - Hidden Street - Dangerous Library
|
||||
977777777 - Hidden Street - Developers' Headquarters
|
||||
|
||||
singapore
|
||||
|
||||
|
||||
@@ -2,6 +2,18 @@
|
||||
<project-private xmlns="http://www.netbeans.org/ns/project-private/1">
|
||||
<editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/2" lastBookmarkId="2"/>
|
||||
<open-files xmlns="http://www.netbeans.org/ns/projectui-open-files/2">
|
||||
<group/>
|
||||
<group>
|
||||
<file>file:/C:/Nexon/MapleSolaxia/HeavenMS/scripts/reactor/2119006.js</file>
|
||||
<file>file:/C:/Nexon/MapleSolaxia/HeavenMS/scripts/reactor/2119002.js</file>
|
||||
<file>file:/C:/Nexon/MapleSolaxia/HeavenMS/scripts/reactor/2119003.js</file>
|
||||
<file>file:/C:/Nexon/MapleSolaxia/HeavenMS/scripts/reactor/2229009.js</file>
|
||||
<file>file:/C:/Nexon/MapleSolaxia/HeavenMS/src/server/maps/MapleMap.java</file>
|
||||
<file>file:/C:/Nexon/MapleSolaxia/HeavenMS/scripts/reactor/2119001.js</file>
|
||||
<file>file:/C:/Nexon/MapleSolaxia/HeavenMS/scripts/reactor/2119004.js</file>
|
||||
<file>file:/C:/Nexon/MapleSolaxia/HeavenMS/scripts/reactor/2119000.js</file>
|
||||
<file>file:/C:/Nexon/MapleSolaxia/HeavenMS/scripts/reactor/2119005.js</file>
|
||||
<file>file:/C:/Nexon/MapleSolaxia/HeavenMS/src/scripting/reactor/ReactorScriptManager.java</file>
|
||||
<file>file:/C:/Nexon/MapleSolaxia/HeavenMS/src/scripting/reactor/ReactorActionManager.java</file>
|
||||
</group>
|
||||
</open-files>
|
||||
</project-private>
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
importPackage(Packages.tools);
|
||||
|
||||
//Time Setting is in millisecond
|
||||
var closeTime = 24 * 1000; //The time to close the gate
|
||||
var beginTime = 30 * 1000; //The time to begin the ride
|
||||
var rideTime = 15 * 1000; //The time that require move to destination
|
||||
var KC_bfd;
|
||||
var Plane_to_CBD;
|
||||
var CBD_docked;
|
||||
@@ -11,6 +7,11 @@ var CBD_bfd;
|
||||
var Plane_to_KC;
|
||||
var KC_docked;
|
||||
|
||||
//Time Setting is in millisecond
|
||||
var closeTime = 4 * 60 * 1000; //The time to close the gate
|
||||
var beginTime = 5 * 60 * 1000; //The time to begin the ride
|
||||
var rideTime = 1 * 60 * 1000; //The time that require move to destination
|
||||
|
||||
function init() {
|
||||
KC_bfd = em.getChannelServer().getMapFactory().getMap(540010100);
|
||||
CBD_bfd = em.getChannelServer().getMapFactory().getMap(540010001);
|
||||
|
||||
@@ -2,13 +2,6 @@ importPackage(Packages.client);
|
||||
importPackage(Packages.tools);
|
||||
importPackage(Packages.server.life);
|
||||
|
||||
//Time Setting is in millisecond
|
||||
var closeTime = 50 * 1000; //The time to close the gate
|
||||
var beginTime = 60 * 1000; //The time to begin the ride
|
||||
var rideTime = 120 * 1000; //The time that require move to destination
|
||||
var invasionStartTime = 30 * 1000; //The time to balrog ship approach
|
||||
var invasionDelayTime = 15 * 1000; //The time to balrog ship approach
|
||||
var invasionDelay = 5 * 1000; //The time that spawn balrog
|
||||
var Orbis_btf;
|
||||
var Boat_to_Orbis;
|
||||
var Orbis_Boat_Cabin;
|
||||
@@ -17,6 +10,14 @@ var Ellinia_btf;
|
||||
var Ellinia_Boat_Cabin;
|
||||
var Ellinia_docked;
|
||||
|
||||
//Time Setting is in millisecond
|
||||
var closeTime = 4 * 60 * 1000; //The time to close the gate
|
||||
var beginTime = 5 * 60 * 1000; //The time to begin the ride
|
||||
var rideTime = 10 * 60 * 1000; //The time that require move to destination
|
||||
var invasionStartTime = 3 * 60 * 1000; //The time to balrog ship approach
|
||||
var invasionDelayTime = 1 * 60 * 1000; //The time to balrog ship approach
|
||||
var invasionDelay = 5 * 1000; //The time that spawn balrog
|
||||
|
||||
function init() {
|
||||
Orbis_btf = em.getChannelServer().getMapFactory().getMap(200000112);
|
||||
Ellinia_btf = em.getChannelServer().getMapFactory().getMap(101000301);
|
||||
|
||||
@@ -41,10 +41,6 @@
|
||||
|
||||
importPackage(Packages.tools);
|
||||
|
||||
//Time Setting is in millisecond
|
||||
var closeTime = 50 * 1000; //The time to close the gate
|
||||
var beginTime = 60 * 1000; //The time to begin the ride
|
||||
var rideTime = 60 * 1000; //The time that require move to destination
|
||||
var Orbis_btf;
|
||||
var Leafre_btf;
|
||||
var Cabin_to_Orbis;
|
||||
@@ -52,6 +48,11 @@ var Cabin_to_Leafre;
|
||||
var Orbis_docked;
|
||||
var Leafre_docked;
|
||||
|
||||
//Time Setting is in millisecond
|
||||
var closeTime = 4 * 60 * 1000; //The time to close the gate
|
||||
var beginTime = 5 * 60 * 1000; //The time to begin the ride
|
||||
var rideTime = 5 * 60 * 1000; //The time that require move to destination
|
||||
|
||||
function init() {
|
||||
Orbis_btf = em.getChannelServer().getMapFactory().getMap(200000132);
|
||||
Leafre_btf = em.getChannelServer().getMapFactory().getMap(240000111);
|
||||
|
||||
@@ -21,10 +21,6 @@
|
||||
*/
|
||||
importPackage(Packages.tools);
|
||||
|
||||
//Time Setting is in millisecond
|
||||
var closeTime = 50 * 1000; //The time to close the gate
|
||||
var beginTime = 60 * 1000; //The time to begin the ride
|
||||
var rideTime = 60 * 1000; //The time that require move to destination
|
||||
var Orbis_btf;
|
||||
var Genie_to_Orbis;
|
||||
var Orbis_docked;
|
||||
@@ -32,6 +28,11 @@ var Ariant_btf;
|
||||
var Genie_to_Ariant;
|
||||
var Ariant_docked;
|
||||
|
||||
//Time Setting is in millisecond
|
||||
var closeTime = 4 * 60 * 1000; //The time to close the gate
|
||||
var beginTime = 5 * 60 * 1000; //The time to begin the ride
|
||||
var rideTime = 5 * 60 * 1000; //The time that require move to destination
|
||||
|
||||
function init() {
|
||||
Orbis_btf = em.getChannelServer().getMapFactory().getMap(200000152);
|
||||
Ariant_btf = em.getChannelServer().getMapFactory().getMap(260000110);
|
||||
|
||||
@@ -7,9 +7,11 @@ var myRide;
|
||||
var returnMap;
|
||||
var exitMap;
|
||||
var map;
|
||||
var timeOnRide = 60; //Seconds
|
||||
var onRide;
|
||||
|
||||
//Time Setting is in millisecond
|
||||
var rideTime = 60 * 1000;
|
||||
|
||||
function init() {}
|
||||
|
||||
function setup() {
|
||||
@@ -29,8 +31,9 @@ function playerEntry(eim, player) {
|
||||
returnMap = eim.getMapFactory().getMap(returnTo[myRide]);
|
||||
onRide = eim.getMapFactory().getMap(birdRide[myRide]);
|
||||
player.changeMap(onRide, onRide.getPortal(0));
|
||||
player.getClient().getSession().write(MaplePacketCreator.getClock(timeOnRide));
|
||||
eim.schedule("timeOut", timeOnRide * 1000);
|
||||
|
||||
player.getClient().getSession().write(MaplePacketCreator.getClock(rideTime / 1000));
|
||||
eim.schedule("timeOut", rideTime);
|
||||
}
|
||||
|
||||
function timeOut(eim) {
|
||||
|
||||
@@ -7,9 +7,11 @@ var myRide;
|
||||
var returnMap;
|
||||
var exitMap;
|
||||
var map;
|
||||
var timeOnRide = 10; //Seconds
|
||||
var onRide;
|
||||
|
||||
//Time Setting is in millisecond
|
||||
var rideTime = 10 * 1000;
|
||||
|
||||
function init() {}
|
||||
|
||||
function setup() {
|
||||
@@ -30,8 +32,10 @@ function playerEntry(eim, player) {
|
||||
returnMap = eim.getMapFactory().getMap(returnTo[myRide]);
|
||||
onRide = eim.getMapFactory().getMap(trainRide[myRide]);
|
||||
player.changeMap(onRide, onRide.getPortal(0));
|
||||
player.getClient().getSession().write(MaplePacketCreator.getClock(timeOnRide));
|
||||
eim.schedule("timeOut", timeOnRide * 1000);
|
||||
|
||||
player.getClient().getSession().write(MaplePacketCreator.getClock(rideTime / 1000));
|
||||
player.getClient().getSession().write(MaplePacketCreator.earnTitleMessage("The next stop is at Kerning " + (myRide == 0 ? "Square" : "Subway") + " Station. The exit is to your left."));
|
||||
eim.schedule("timeOut", rideTime);
|
||||
}
|
||||
|
||||
function timeOut(eim) {
|
||||
|
||||
+13
-4
@@ -1,7 +1,5 @@
|
||||
//Time Setting is in millisecond
|
||||
var closeTime = 24 * 1000; //[24 seconds] The time to close the gate
|
||||
var beginTime = 30 * 1000; //[30 seconds] The time to begin the ride
|
||||
var rideTime = 30 * 1000; //[30 seconds] The time that require move to destination
|
||||
importPackage(Packages.tools);
|
||||
|
||||
var KC_Waiting;
|
||||
var Subway_to_KC;
|
||||
var KC_docked;
|
||||
@@ -9,6 +7,11 @@ var NLC_Waiting;
|
||||
var Subway_to_NLC;
|
||||
var NLC_docked;
|
||||
|
||||
//Time Setting is in millisecond
|
||||
var closeTime = 50 * 1000; //The time to close the gate
|
||||
var beginTime = 1 * 60 * 1000; //The time to begin the ride
|
||||
var rideTime = 4 * 60 * 1000; //The time that require move to destination
|
||||
|
||||
function init() {
|
||||
KC_Waiting = em.getChannelServer().getMapFactory().getMap(600010004);
|
||||
NLC_Waiting = em.getChannelServer().getMapFactory().getMap(600010002);
|
||||
@@ -31,6 +34,9 @@ function stopEntry() {
|
||||
}
|
||||
|
||||
function takeoff() {
|
||||
KC_docked.broadcastMessage(MaplePacketCreator.playSound("subway/whistle"));
|
||||
NLC_docked.broadcastMessage(MaplePacketCreator.playSound("subway/whistle"));
|
||||
|
||||
em.setProperty("docked","false");
|
||||
KC_Waiting.warpEveryone(Subway_to_NLC.getId());
|
||||
NLC_Waiting.warpEveryone(Subway_to_KC.getId());
|
||||
@@ -41,6 +47,9 @@ function arrived() {
|
||||
Subway_to_KC.warpEveryone(KC_docked.getId(), 0);
|
||||
Subway_to_NLC.warpEveryone(NLC_docked.getId(), 0);
|
||||
scheduleNew();
|
||||
|
||||
KC_docked.broadcastMessage(MaplePacketCreator.playSound("subway/whistle"));
|
||||
NLC_docked.broadcastMessage(MaplePacketCreator.playSound("subway/whistle"));
|
||||
}
|
||||
|
||||
function cancelSchedule() {
|
||||
|
||||
@@ -10,9 +10,9 @@ var Orbis_Station;
|
||||
var Ludibrium_Station;
|
||||
|
||||
//Time Setting is in millisecond
|
||||
var closeTime = 50 * 1000; //The time to close the gate
|
||||
var beginTime = 60 * 1000; //The time to begin the ride
|
||||
var rideTime = 60 * 1000; //The time that require move to destination
|
||||
var closeTime = 4 * 60 * 1000; //The time to close the gate
|
||||
var beginTime = 5 * 60 * 1000; //The time to begin the ride
|
||||
var rideTime = 5 * 60 * 1000; //The time that require move to destination
|
||||
|
||||
function init() {
|
||||
Orbis_btf = em.getChannelServer().getMapFactory().getMap(200000122);
|
||||
|
||||
@@ -19,6 +19,11 @@ 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/>.
|
||||
*/
|
||||
|
||||
//Time Setting is in millisecond
|
||||
var beginTime = 60 * 1000; //The time to begin the ride
|
||||
var rideTime = 60 * 1000; //The time that require move to destination
|
||||
|
||||
function init() {
|
||||
em.getChannelServer().getMapFactory().getMap(222020100).resetReactors();
|
||||
em.getChannelServer().getMapFactory().getMap(222020200).resetReactors();
|
||||
@@ -32,21 +37,21 @@ function scheduleNew() {
|
||||
|
||||
em.getChannelServer().getMapFactory().getMap(222020100).resetReactors();
|
||||
em.getChannelServer().getMapFactory().getMap(222020200).setReactorState();
|
||||
em.schedule("goingUpNow", 50000);
|
||||
em.schedule("goingUpNow", beginTime);
|
||||
}
|
||||
|
||||
function goUp() {
|
||||
em.schedule("goingUpNow", 50000); // might be 60
|
||||
em.schedule("goingUpNow", beginTime);
|
||||
}
|
||||
|
||||
function goDown() {
|
||||
em.schedule("goingDownNow", 50000); // might be 60
|
||||
em.schedule("goingDownNow", beginTime);
|
||||
}
|
||||
|
||||
function goingUpNow() {
|
||||
em.getChannelServer().getMapFactory().getMap(222020110).warpEveryone(222020111);
|
||||
em.setProperty("goingUp", "true");
|
||||
em.schedule("isUpNow", 55000);
|
||||
em.schedule("isUpNow", rideTime);
|
||||
|
||||
em.getChannelServer().getMapFactory().getMap(222020100).setReactorState();
|
||||
}
|
||||
@@ -54,7 +59,7 @@ function goingUpNow() {
|
||||
function goingDownNow() {
|
||||
em.getChannelServer().getMapFactory().getMap(222020210).warpEveryone(222020211);
|
||||
em.setProperty("goingDown", "true");
|
||||
em.schedule("isDownNow", 55000);
|
||||
em.schedule("isDownNow", rideTime);
|
||||
|
||||
em.getChannelServer().getMapFactory().getMap(222020200).setReactorState();
|
||||
}
|
||||
|
||||
+73
-138
@@ -29,36 +29,55 @@ status = -1;
|
||||
actionx = {"1stJob" : false, "2ndjob" : false, "3thJobI" : false, "3thJobC" : false};
|
||||
job = 310;
|
||||
|
||||
spawnPnpc = false;
|
||||
spawnPnpcFee = 7000000;
|
||||
jobType = 3;
|
||||
|
||||
function numberWithCommas(x) {
|
||||
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
}
|
||||
|
||||
function start() {
|
||||
if (cm.getJobId() == 0) {
|
||||
actionx["1stJob"] = true;
|
||||
if (cm.getLevel() >= 10)
|
||||
cm.sendNext("So you decided to become a #rBowman#k?");
|
||||
else {
|
||||
cm.sendOk("Train a bit more and I can show you the way of the #rBowman#k.");
|
||||
if (parseInt(cm.getJobId() / 100) == jobType && cm.canSpawnPlayerNpc(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()))) {
|
||||
spawnPnpc = true;
|
||||
|
||||
var sendStr = "You have walked a long way to reach the power, wisdom and courage you hold today, didn't you? What do you say about having right now #ra NPC on the Hall of Fame holding the current image of your character#k? Do you like it?";
|
||||
if(spawnPnpcFee > 0) {
|
||||
sendStr += " I can do it for you, for the fee of #b " + numberWithCommas(spawnPnpcFee) + " mesos.#k";
|
||||
}
|
||||
|
||||
cm.sendYesNo(sendStr);
|
||||
} else {
|
||||
if (cm.getJobId() == 0) {
|
||||
actionx["1stJob"] = true;
|
||||
if (cm.getLevel() >= 10)
|
||||
cm.sendNext("So you decided to become a #rBowman#k?");
|
||||
else {
|
||||
cm.sendOk("Train a bit more and I can show you the way of the #rBowman#k.");
|
||||
cm.dispose();
|
||||
}
|
||||
} else if (cm.getLevel() >= 30 && cm.getJobId() == 300) {
|
||||
actionx["2ndJob"] = true;
|
||||
if (cm.haveItem(4031012))
|
||||
cm.sendNext("Haha...I knew you'd breeze through that test. I'll admit, you are a great bowman. I'll make you much stronger than you're right now. before that, however... you;ll need to choose one of two paths given to you. It'll be a difficult decision for you to make, but... if there's any question to ask, please do so.");
|
||||
else if (cm.haveItem(4031011)){
|
||||
cm.sendOk("Go and see the #b#p1072002##k.");
|
||||
cm.dispose();
|
||||
} else
|
||||
cm.sendYesNo("Hmmm... you have grown a lot since I last saw you. I don't see the weakling I saw before, and instead, look much more like a bowman now. Well, what do you think? Don't you want to get even more powerful than that? Pass a simple test and I'll do just that for you. Do you want to do it?");
|
||||
} else if (actionx["3thJobI"] || (cm.getPlayer().gotPartyQuestItem("JB3") && cm.getLevel() >= 70 && cm.getJobId() % 10 == 0 && parseInt(cm.getJobId() / 100) == 3 && !cm.getPlayer().gotPartyQuestItem("JBP"))){
|
||||
actionx["3thJobI"] = true;
|
||||
cm.sendNext("There you are. A few days ago, #b#p2020010##k of Ossyria talked to me about you. I see that you are interested in making the leap to the amazing world of the third job advancement for archers. To achieve that goal, I will have to test your strength in order to see whether you are worthy of the advancement. There is an opening in the middle of a deep forest in Victoria Island, where it'll lead you to a secret passage. Once inside, you'll face a clone of myself. Your task is to defeat her and bring #b#t4031059##k back with you.");
|
||||
} else if (cm.getPlayer().gotPartyQuestItem("JBP") && !cm.haveItem(4031059)){
|
||||
cm.sendNext("Please, bring me the #b#t4031059##k.");
|
||||
cm.dispose();
|
||||
} else if (cm.haveItem(4031059) && cm.getPlayer().gotPartyQuestItem("JBP")){
|
||||
actionx["3thJobC"] = true;
|
||||
cm.sendNext("Nice work. You have defeated my clone and brought #b#t4031059##k back safely. You have now proven yourself worthy of the 3rd job advancement from the physical standpoint. Now you should give this necklace to #b#p2020011##k in Ossyria to take on the second part of the test. Good luck. You'll need it.");
|
||||
} else {
|
||||
cm.sendOk("You have chosen wisely.");
|
||||
cm.dispose();
|
||||
}
|
||||
} else if (cm.getLevel() >= 30 && cm.getJobId() == 300) {
|
||||
actionx["2ndJob"] = true;
|
||||
if (cm.haveItem(4031012))
|
||||
cm.sendNext("Haha...I knew you'd breeze through that test. I'll admit, you are a great bowman. I'll make you much stronger than you're right now. before that, however... you;ll need to choose one of two paths given to you. It'll be a difficult decision for you to make, but... if there's any question to ask, please do so.");
|
||||
else if (cm.haveItem(4031011)){
|
||||
cm.sendOk("Go and see the #b#p1072002##k.");
|
||||
cm.dispose();
|
||||
} else
|
||||
cm.sendYesNo("Hmmm... you have grown a lot since I last saw you. I don't see the weakling I saw before, and instead, look much more like a bowman now. Well, what do you think? Don't you want to get even more powerful than that? Pass a simple test and I'll do just that for you. Do you want to do it?");
|
||||
} else if (actionx["3thJobI"] || (cm.getPlayer().gotPartyQuestItem("JB3") && cm.getLevel() >= 70 && cm.getJobId() % 10 == 0 && parseInt(cm.getJobId() / 100) == 3 && !cm.getPlayer().gotPartyQuestItem("JBP"))){
|
||||
actionx["3thJobI"] = true;
|
||||
cm.sendNext("There you are. A few days ago, #b#p2020010##k of Ossyria talked to me about you. I see that you are interested in making the leap to the amazing world of the third job advancement for archers. To achieve that goal, I will have to test your strength in order to see whether you are worthy of the advancement. There is an opening in the middle of a deep forest in Victoria Island, where it'll lead you to a secret passage. Once inside, you'll face a clone of myself. Your task is to defeat her and bring #b#t4031059##k back with you.");
|
||||
} else if (cm.getPlayer().gotPartyQuestItem("JBP") && !cm.haveItem(4031059)){
|
||||
cm.sendNext("Please, bring me the #b#t4031059##k.");
|
||||
cm.dispose();
|
||||
} else if (cm.haveItem(4031059) && cm.getPlayer().gotPartyQuestItem("JBP")){
|
||||
actionx["3thJobC"] = true;
|
||||
cm.sendNext("Nice work. You have defeated my clone and brought #b#t4031059##k back safely. You have now proven yourself worthy of the 3rd job advancement from the physical standpoint. Now you should give this necklace to #b#p2020011##k in Ossyria to take on the second part of the test. Good luck. You'll need it.");
|
||||
} else {
|
||||
cm.sendOk("You have chosen wisely.");
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,14 +88,37 @@ function action(mode, type, selection) {
|
||||
if (status == -1){
|
||||
start();
|
||||
return;
|
||||
} else if (mode != 1 || status == 7 && type != 1 || (actionx["1stJob"] && status == 4) || (cm.haveItem(4031008) && status == 2) || (actionx["3thJobI"] && status == 1)){
|
||||
if (mode == 0 && status == 2 && type == 1)
|
||||
cm.sendOk("You know there is no other choice...");
|
||||
if (!(mode == 0 && type != 1)){
|
||||
} else {
|
||||
if(spawnPnpc) {
|
||||
if(mode > 0) {
|
||||
if(cm.getMeso() < spawnPnpcFee) {
|
||||
cm.sendOk("Sorry, you don't have enough mesos to purchase your place on the Hall of Fame.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if(Packages.server.life.MaplePlayerNPC.spawnPlayerNPC(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()), cm.getPlayer())) {
|
||||
cm.sendOk("There you go! Hope you will like it.");
|
||||
cm.gainMeso(-spawnPnpcFee);
|
||||
} else {
|
||||
cm.sendOk("Sorry, the Hall of Fame is currently full...");
|
||||
}
|
||||
}
|
||||
|
||||
cm.dispose();
|
||||
return;
|
||||
} else {
|
||||
if (mode != 1 || status == 7 && type != 1 || (actionx["1stJob"] && status == 4) || (cm.haveItem(4031008) && status == 2) || (actionx["3thJobI"] && status == 1)){
|
||||
if (mode == 0 && status == 2 && type == 1)
|
||||
cm.sendOk("You know there is no other choice...");
|
||||
if (!(mode == 0 && type != 1)){
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (actionx["1stJob"]){
|
||||
if (status == 0)
|
||||
cm.sendNextPrev("It is an important and final choice. You will not be able to turn back.");
|
||||
@@ -161,110 +203,3 @@ function action(mode, type, selection) {
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
function start() {
|
||||
if (cm.getJobId() == 0) {
|
||||
actionx["1stJob"] = true;
|
||||
if (cm.getLevel() >= 10)
|
||||
|
||||
else {
|
||||
|
||||
cm.dispose();
|
||||
}
|
||||
} else {
|
||||
if (cm.getLevel() >= 30 && cm.getJobId() == 300) {
|
||||
actionx["2ndJob"] = true;
|
||||
if (cm.haveItem(4031012))
|
||||
|
||||
else if (cm.haveItem(4031010)){
|
||||
cm.sendOk("Go and see the #b#p1072002##k.");
|
||||
cm.dispose();
|
||||
} else
|
||||
|
||||
} else if (cm.isQuestStarted(100100)) {
|
||||
if (cm.isQuestCompleted(100101))
|
||||
cm.sendOk("Alright, now take this to #bRene#k.");
|
||||
else {
|
||||
|
||||
cm.startQuest(100101);
|
||||
}
|
||||
cm.dispose();
|
||||
} else {
|
||||
cm.sendOk("You have chosen wisely.");
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
status++;
|
||||
if (mode == 0 && type != 1)
|
||||
status -= 2;
|
||||
if (status == -1){
|
||||
start();
|
||||
return;
|
||||
} else if (mode != 1 || status == 7 && type != 1 || (cm.haveItem(4031010) && status == 2)){
|
||||
if (mode == 0 && status == 2)
|
||||
cm.sendOk("Make up your mind and visit me again.");
|
||||
if (!(mode == 0 && type != 1)){
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (actionx["1stJob"]){
|
||||
if (status == 0)
|
||||
|
||||
else if (status == 1)
|
||||
|
||||
else if (status == 2) {
|
||||
|
||||
cm.sendOk("So be it! Now go, and go with pride.");
|
||||
} else {
|
||||
cm.sendNext("Make some room in your inventory and talk to me again.");
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
} else if(actionx["2ndJob"]){
|
||||
if (status == 0){
|
||||
if (cm.haveItem(4031012))
|
||||
cm.sendSimple("Alright, when you have made your decision, click on [I'll choose my occupation] at the bottom.#b\r\n#L0#Please explain to me what being the Hunter is all about.\r\n#L1#Please explain to me what being the Crossbowman is all about.\r\n#L2#I'll choose my occupation!");
|
||||
else {
|
||||
cm.sendNext("Good decision. You look strong, but I need to see if you really are strong enough to pass the test, it's not a difficult test, so you'll do just fine. Here, take my letter first... make sure you don't lose it!");
|
||||
if(!cm.isQuestStarted(100000)) cm.startQuest(100000);
|
||||
}
|
||||
} else if (status == 1){
|
||||
if (!cm.haveItem(4031012)){
|
||||
if (cm.canHold(4031010)){
|
||||
if (!cm.haveItem(4031010))
|
||||
cm.gainItem(4031010, 1);
|
||||
cm.sendNextPrev("Please get this letter to #b#p1072002##k who's around #b#m106010000##k near Henesys. She is taking care of the job of an instructor in place of me. Give her the letter and she'll test you in place of me. Best of luck to you.");
|
||||
} else {
|
||||
cm.sendNext("Please, make some space in your inventory.");
|
||||
cm.dispose();
|
||||
}
|
||||
} else {
|
||||
if (selection < 2){
|
||||
cm.sendNext("Not done.");
|
||||
status -= 2;
|
||||
} else
|
||||
cm.sendSimple("Now... have you made up your mind? Please choose the job you'd like to select for your 2nd job advancement. #b\r\n#L0#Hunter\r\n#L1#Crossbowman");
|
||||
}
|
||||
} else if (status == 2){
|
||||
job += selection * 10;
|
||||
cm.sendYesNo("So you want to make the second job advancement as the " + (job == 310 ? "#bHunter#k" : "#bCrossbowman#k") + "? You know you won't be able to choose a different job for the 2nd job advancement once you make your desicion here, right?");
|
||||
} else if (status == 3){
|
||||
if (cm.haveItem(4031012))
|
||||
cm.gainItem(4031012, -1);
|
||||
cm.completeQuest(100002);
|
||||
cm.sendNext("Alright, you're the " + (job == 310 ? "#bHunter#k" : "#bCrossbowman#k") + " from here on out. Hunters are the intelligent bunch with incredible vision, able to pierce the arrow through the heart of the monsters with ease... please train yourself each and everyday. I'll help you become even stronger than you already are.");
|
||||
if (cm.getJobId() != job)
|
||||
cm.changeJobById(job);
|
||||
} else if (status == 4)
|
||||
cm.sendNextPrev("I have just given you a book that gives you the list of skills you can acquire as a " + (job == 310 ? "hunter" : "crossbowman") + ". Also your etc inventory has expanded by adding another row to it. Your max HP and MP have increased, too. Go check and see for it yourself.");
|
||||
else if (status == 5)
|
||||
cm.sendNextPrev("I have also given you a little bit of #bSP#k. Open the #bSkill Menu#k located at the bottomleft corner. you'll be able to boost up the newer acquired 2nd level skills. A word of warning, though. You can't boost them up all at once. Some of the skills are only available after you have learned other skills. Make sure yo remember that.");
|
||||
else if (status == 6)
|
||||
cm.sendNextPrev((job == 310 ? "Hunter" : "Crossbowman") + " need to be strong. But remember that you can't abuse that power and use it on a weakling. Please use your enormous power the right way, because... for you to use that the right way, that is much harden than just getting stronger. Please find me after you have advanced much further. I'll be waiting for you.");
|
||||
}
|
||||
}*/
|
||||
+73
-31
@@ -30,36 +30,55 @@ status = -1;
|
||||
actionx = {"1stJob" : false, "2ndjob" : false, "3thJobI" : false, "3thJobC" : false};
|
||||
job = 110;
|
||||
|
||||
spawnPnpc = false;
|
||||
spawnPnpcFee = 7000000;
|
||||
jobType = 1;
|
||||
|
||||
function numberWithCommas(x) {
|
||||
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
}
|
||||
|
||||
function start() {
|
||||
if (cm.getJobId() == 0) {
|
||||
actionx["1stJob"] = true;
|
||||
if (cm.getLevel() >= 10)
|
||||
cm.sendNext("Do you want to become a Warrior? You need to meet some criteria in order to do so.#b You should be at least in level 10, with at least 35 in STR#k. Let's see...");
|
||||
else {
|
||||
cm.sendOk("Train a bit more and I can show you the way of the #rWarrior#k.");
|
||||
if (parseInt(cm.getJobId() / 100) == jobType && cm.canSpawnPlayerNpc(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()))) {
|
||||
spawnPnpc = true;
|
||||
|
||||
var sendStr = "You have walked a long way to reach the power, wisdom and courage you hold today, didn't you? What do you say about having right now #ra NPC on the Hall of Fame holding the current image of your character#k? Do you like it?";
|
||||
if(spawnPnpcFee > 0) {
|
||||
sendStr += " I can do it for you, for the fee of #b " + numberWithCommas(spawnPnpcFee) + " mesos.#k";
|
||||
}
|
||||
|
||||
cm.sendYesNo(sendStr);
|
||||
} else {
|
||||
if (cm.getJobId() == 0) {
|
||||
actionx["1stJob"] = true;
|
||||
if (cm.getLevel() >= 10)
|
||||
cm.sendNext("Do you want to become a Warrior? You need to meet some criteria in order to do so.#b You should be at least in level 10, with at least 35 in STR#k. Let's see...");
|
||||
else {
|
||||
cm.sendOk("Train a bit more and I can show you the way of the #rWarrior#k.");
|
||||
cm.dispose();
|
||||
}
|
||||
} else if (cm.getLevel() >= 30 && cm.getJobId() == 100) {
|
||||
actionx["2ndJob"] = true;
|
||||
if (cm.haveItem(4031012))
|
||||
cm.sendNext("Oh... you came back safe! I knew you'd breeze through. I'll admit, you are a strong, formidable Warrior! Alright, I'll make you an even stronger Warrior than you already are. But before that, you need to choose one of the three paths that you'll be given. It isn't going to be easy, so if you have and questions, feel free to ask.");
|
||||
else if (cm.haveItem(4031008)){
|
||||
cm.sendOk("Go and see the #b#p1072000##k.");
|
||||
cm.dispose();
|
||||
} else
|
||||
cm.sendNext("The progress you have made is astonishing.");
|
||||
} else if (actionx["3thJobI"] || (cm.getPlayer().gotPartyQuestItem("JB3") && cm.getLevel() >= 70 && (cm.getJobId() % 10 == 0 && parseInt(cm.getJobId() / 100) == 1 && !cm.getPlayer().gotPartyQuestItem("JBP")))) {
|
||||
actionx["3thJobI"] = true;
|
||||
cm.sendNext("I was waiting for you. Few days ago, I heard about you from #b#p2020008##k in Ossyria. Well... I'd like to test your strength. There is a secret passage near the ant tunnel. Nobody but you can go into that passage. If you go into the passage, you will meat my the other self. Beat him and bring #b#t4031059##k to me.");
|
||||
} else if (cm.getPlayer().gotPartyQuestItem("JBP") && !cm.haveItem(4031059)){
|
||||
cm.sendNext("Please, bring me the #b#t4031059##k.");
|
||||
cm.dispose();
|
||||
} else if (cm.haveItem(4031059) && cm.getPlayer().gotPartyQuestItem("JBP")){
|
||||
actionx["3thJobC"] = true;
|
||||
cm.sendNext("Wow... You beat my the other self and brought #b#t4031059##k to me. Good! this surely proves your strength. In terms of strength, you are ready to advance to 3th job. As I promised, I will give #b#t4031057##k to you. Give this necklace to #b#p2020008##k in Ossyria and you will be able to take second test of 3rd job advancement. Good Luck~");
|
||||
} else {
|
||||
cm.sendOk("You have chosen wisely.");
|
||||
cm.dispose();
|
||||
}
|
||||
} else if (cm.getLevel() >= 30 && cm.getJobId() == 100) {
|
||||
actionx["2ndJob"] = true;
|
||||
if (cm.haveItem(4031012))
|
||||
cm.sendNext("Oh... you came back safe! I knew you'd breeze through. I'll admit, you are a strong, formidable Warrior! Alright, I'll make you an even stronger Warrior than you already are. But before that, you need to choose one of the three paths that you'll be given. It isn't going to be easy, so if you have and questions, feel free to ask.");
|
||||
else if (cm.haveItem(4031008)){
|
||||
cm.sendOk("Go and see the #b#p1072000##k.");
|
||||
cm.dispose();
|
||||
} else
|
||||
cm.sendNext("The progress you have made is astonishing.");
|
||||
} else if (actionx["3thJobI"] || (cm.getPlayer().gotPartyQuestItem("JB3") && cm.getLevel() >= 70 && (cm.getJobId() % 10 == 0 && parseInt(cm.getJobId() / 100) == 1 && !cm.getPlayer().gotPartyQuestItem("JBP")))) {
|
||||
actionx["3thJobI"] = true;
|
||||
cm.sendNext("I was waiting for you. Few days ago, I heard about you from #b#p2020008##k in Ossyria. Well... I'd like to test your strength. There is a secret passage near the ant tunnel. Nobody but you can go into that passage. If you go into the passage, you will meat my the other self. Beat him and bring #b#t4031059##k to me.");
|
||||
} else if (cm.getPlayer().gotPartyQuestItem("JBP") && !cm.haveItem(4031059)){
|
||||
cm.sendNext("Please, bring me the #b#t4031059##k.");
|
||||
cm.dispose();
|
||||
} else if (cm.haveItem(4031059) && cm.getPlayer().gotPartyQuestItem("JBP")){
|
||||
actionx["3thJobC"] = true;
|
||||
cm.sendNext("Wow... You beat my the other self and brought #b#t4031059##k to me. Good! this surely proves your strength. In terms of strength, you are ready to advance to 3th job. As I promised, I will give #b#t4031057##k to you. Give this necklace to #b#p2020008##k in Ossyria and you will be able to take second test of 3rd job advancement. Good Luck~");
|
||||
} else {
|
||||
cm.sendOk("You have chosen wisely.");
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,14 +89,37 @@ function action(mode, type, selection) {
|
||||
if (status == -1){
|
||||
start();
|
||||
return;
|
||||
} else if (mode != 1 || status == 7 && type != 1 || (actionx["1stJob"] && status == 4) || (cm.haveItem(4031008) && status == 2) || (actionx["3thJob"] && status == 1)){
|
||||
if (mode == 0 && status == 2 && type == 1)
|
||||
cm.sendOk("Make up your mind and visit me again.");
|
||||
if (!(mode == 0 && type != 1)){
|
||||
} else {
|
||||
if(spawnPnpc) {
|
||||
if(mode > 0) {
|
||||
if(cm.getMeso() < spawnPnpcFee) {
|
||||
cm.sendOk("Sorry, you don't have enough mesos to purchase your place on the Hall of Fame.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if(Packages.server.life.MaplePlayerNPC.spawnPlayerNPC(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()), cm.getPlayer())) {
|
||||
cm.sendOk("There you go! Hope you will like it.");
|
||||
cm.gainMeso(-spawnPnpcFee);
|
||||
} else {
|
||||
cm.sendOk("Sorry, the Hall of Fame is currently full...");
|
||||
}
|
||||
}
|
||||
|
||||
cm.dispose();
|
||||
return;
|
||||
} else {
|
||||
if (mode != 1 || status == 7 && type != 1 || (actionx["1stJob"] && status == 4) || (cm.haveItem(4031008) && status == 2) || (actionx["3thJob"] && status == 1)){
|
||||
if (mode == 0 && status == 2 && type == 1)
|
||||
cm.sendOk("Make up your mind and visit me again.");
|
||||
if (!(mode == 0 && type != 1)){
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (actionx["1stJob"]){
|
||||
if (status == 0)
|
||||
cm.sendNextPrev("It is an important and final choice. You will not be able to turn back.");
|
||||
|
||||
+73
-31
@@ -30,36 +30,55 @@ status = -1;
|
||||
actionx = {"1stJob" : false, "2ndjob" : false, "3thJobI" : false, "3thJobC" : false};
|
||||
job = 210;
|
||||
|
||||
spawnPnpc = false;
|
||||
spawnPnpcFee = 7000000;
|
||||
jobType = 2;
|
||||
|
||||
function numberWithCommas(x) {
|
||||
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
}
|
||||
|
||||
function start() {
|
||||
if (cm.getJobId() == 0) {
|
||||
actionx["1stJob"] = true;
|
||||
if (cm.getLevel() >= 8)
|
||||
cm.sendNext("Want to be a magician? There are some standards to meet. because we can't just accept EVERYONE in... #bYour level should be at least 8#k, with getting INT as your top priority. Let's see.");
|
||||
else {
|
||||
cm.sendOk("Train a bit more and I can show you the way of the #rMagician#k.");
|
||||
if (parseInt(cm.getJobId() / 100) == jobType && cm.canSpawnPlayerNpc(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()))) {
|
||||
spawnPnpc = true;
|
||||
|
||||
var sendStr = "You have walked a long way to reach the power, wisdom and courage you hold today, didn't you? What do you say about having right now #ra NPC on the Hall of Fame holding the current image of your character#k? Do you like it?";
|
||||
if(spawnPnpcFee > 0) {
|
||||
sendStr += " I can do it for you, for the fee of #b " + numberWithCommas(spawnPnpcFee) + " mesos.#k";
|
||||
}
|
||||
|
||||
cm.sendYesNo(sendStr);
|
||||
} else {
|
||||
if (cm.getJobId() == 0) {
|
||||
actionx["1stJob"] = true;
|
||||
if (cm.getLevel() >= 8)
|
||||
cm.sendNext("Want to be a magician? There are some standards to meet. because we can't just accept EVERYONE in... #bYour level should be at least 8#k, with getting INT as your top priority. Let's see.");
|
||||
else {
|
||||
cm.sendOk("Train a bit more and I can show you the way of the #rMagician#k.");
|
||||
cm.dispose();
|
||||
}
|
||||
} else if (cm.getLevel() >= 30 && cm.getJobId() == 200) {
|
||||
actionx["2ndJob"] = true;
|
||||
if (cm.haveItem(4031012))
|
||||
cm.sendNext("I see you have done well. I will allow you to take the next step on your long road.");
|
||||
else if (cm.haveItem(4031009)){
|
||||
cm.sendOk("Go and see the #b#p1072001##k.");
|
||||
cm.dispose();
|
||||
} else
|
||||
cm.sendNext("The progress you have made is astonishing.");
|
||||
} else if (actionx["3thJobI"] || (cm.getPlayer().gotPartyQuestItem("JB3") && cm.getLevel() >= 70 && cm.getJobId() % 10 == 0 && parseInt(cm.getJobId() / 100) == 2 && !cm.getPlayer().gotPartyQuestItem("JBP"))){
|
||||
actionx["3thJobI"] = true;
|
||||
cm.sendNext("There you are. A few days ago, #b#p2020009##k of Ossyria talked to me about you. I see that you are interested in making the leap to the enlightened of the third job advancement for magicians. To archieve that goal, I will have to test your strength in order to see whether you are worthy of the advancement. There is an opening in the middle of a deep forest of evil in Victoria Island, where it'll lead you to a secret passage. Once inside, you'll face a clone of myself. Your task is to defeat him and bring #b#t4031059##k back with you.");
|
||||
} else if (cm.getPlayer().gotPartyQuestItem("JBP") && !cm.haveItem(4031059)){
|
||||
cm.sendNext("Please, bring me the #b#t4031059##k from my clone. You can find him inside a hole in space which is deep in a forest of evil.");
|
||||
cm.dispose();
|
||||
} else if (cm.haveItem(4031059) && cm.getPlayer().gotPartyQuestItem("JBP")){
|
||||
actionx["3thJobC"] = true;
|
||||
cm.sendNext("Nice work. You have defeated my clone and brought #b#t4031059##k back safely. You have now proven yourself worthy of the 3rd job advancement from the physical standpoint. Now you should give this necklace to #b#p2020011##k in Ossyria to take on the second part of the test. Good luck. You'll need it.");
|
||||
} else {
|
||||
cm.sendOk("You have chosen wisely.");
|
||||
cm.dispose();
|
||||
}
|
||||
} else if (cm.getLevel() >= 30 && cm.getJobId() == 200) {
|
||||
actionx["2ndJob"] = true;
|
||||
if (cm.haveItem(4031012))
|
||||
cm.sendNext("I see you have done well. I will allow you to take the next step on your long road.");
|
||||
else if (cm.haveItem(4031009)){
|
||||
cm.sendOk("Go and see the #b#p1072001##k.");
|
||||
cm.dispose();
|
||||
} else
|
||||
cm.sendNext("The progress you have made is astonishing.");
|
||||
} else if (actionx["3thJobI"] || (cm.getPlayer().gotPartyQuestItem("JB3") && cm.getLevel() >= 70 && cm.getJobId() % 10 == 0 && parseInt(cm.getJobId() / 100) == 2 && !cm.getPlayer().gotPartyQuestItem("JBP"))){
|
||||
actionx["3thJobI"] = true;
|
||||
cm.sendNext("There you are. A few days ago, #b#p2020009##k of Ossyria talked to me about you. I see that you are interested in making the leap to the enlightened of the third job advancement for magicians. To archieve that goal, I will have to test your strength in order to see whether you are worthy of the advancement. There is an opening in the middle of a deep forest of evil in Victoria Island, where it'll lead you to a secret passage. Once inside, you'll face a clone of myself. Your task is to defeat him and bring #b#t4031059##k back with you.");
|
||||
} else if (cm.getPlayer().gotPartyQuestItem("JBP") && !cm.haveItem(4031059)){
|
||||
cm.sendNext("Please, bring me the #b#t4031059##k from my clone. You can find him inside a hole in space which is deep in a forest of evil.");
|
||||
cm.dispose();
|
||||
} else if (cm.haveItem(4031059) && cm.getPlayer().gotPartyQuestItem("JBP")){
|
||||
actionx["3thJobC"] = true;
|
||||
cm.sendNext("Nice work. You have defeated my clone and brought #b#t4031059##k back safely. You have now proven yourself worthy of the 3rd job advancement from the physical standpoint. Now you should give this necklace to #b#p2020011##k in Ossyria to take on the second part of the test. Good luck. You'll need it.");
|
||||
} else {
|
||||
cm.sendOk("You have chosen wisely.");
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,14 +89,37 @@ function action(mode, type, selection) {
|
||||
if (status == -1){
|
||||
start();
|
||||
return;
|
||||
} else if (mode != 1 || status == 7 || (actionx["1stJob"] && status == 4) || (cm.haveItem(4031008) && status == 2) || (actionx["3thJobI"] && status == 1)){
|
||||
if (mode == 0 && status == 2 && type == 1)
|
||||
cm.sendOk("You know there is no other choice...");
|
||||
if (!(mode == 0 && type == 0)){
|
||||
} else {
|
||||
if(spawnPnpc) {
|
||||
if(mode > 0) {
|
||||
if(cm.getMeso() < spawnPnpcFee) {
|
||||
cm.sendOk("Sorry, you don't have enough mesos to purchase your place on the Hall of Fame.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if(Packages.server.life.MaplePlayerNPC.spawnPlayerNPC(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()), cm.getPlayer())) {
|
||||
cm.sendOk("There you go! Hope you will like it.");
|
||||
cm.gainMeso(-spawnPnpcFee);
|
||||
} else {
|
||||
cm.sendOk("Sorry, the Hall of Fame is currently full...");
|
||||
}
|
||||
}
|
||||
|
||||
cm.dispose();
|
||||
return;
|
||||
} else {
|
||||
if (mode != 1 || status == 7 || (actionx["1stJob"] && status == 4) || (cm.haveItem(4031008) && status == 2) || (actionx["3thJobI"] && status == 1)){
|
||||
if (mode == 0 && status == 2 && type == 1)
|
||||
cm.sendOk("You know there is no other choice...");
|
||||
if (!(mode == 0 && type == 0)){
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (actionx["1stJob"]){
|
||||
if (status == 0)
|
||||
cm.sendYesNo("Oh...! You look like someone that can definitely be a part of us... all you need is a little sinister mind, and... yeah... so, what do you think? Wanna be the Magician?");
|
||||
|
||||
+75
-33
@@ -29,38 +29,57 @@ status = -1;
|
||||
actionx = {"1stJob" : false, "2ndjob" : false, "3thJobI" : false, "3thJobC" : false};
|
||||
job = 410;
|
||||
|
||||
spawnPnpc = false;
|
||||
spawnPnpcFee = 7000000;
|
||||
jobType = 4;
|
||||
|
||||
function numberWithCommas(x) {
|
||||
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
}
|
||||
|
||||
function start() {
|
||||
if (cm.getJobId() == 0) {
|
||||
actionx["1stJob"] = true;
|
||||
if (cm.getLevel() >= 10)
|
||||
cm.sendNext("Want to be a thief? There are some standards to meet. because we can't just accept EVERYONE in... #bYour level should be at least 10, with your DEX over 25#k. Let's see.");
|
||||
else {
|
||||
cm.sendOk("Train a bit more and I can show you the way of the #rThief#k.");
|
||||
if (parseInt(cm.getJobId() / 100) == jobType && cm.canSpawnPlayerNpc(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()))) {
|
||||
spawnPnpc = true;
|
||||
|
||||
var sendStr = "You have walked a long way to reach the power, wisdom and courage you hold today, didn't you? What do you say about having right now #ra NPC on the Hall of Fame holding the current image of your character#k? Do you like it?";
|
||||
if(spawnPnpcFee > 0) {
|
||||
sendStr += " I can do it for you, for the fee of #b " + numberWithCommas(spawnPnpcFee) + " mesos.#k";
|
||||
}
|
||||
|
||||
cm.sendYesNo(sendStr);
|
||||
} else {
|
||||
if (cm.getJobId() == 0) {
|
||||
actionx["1stJob"] = true;
|
||||
if (cm.getLevel() >= 10)
|
||||
cm.sendNext("Want to be a thief? There are some standards to meet. because we can't just accept EVERYONE in... #bYour level should be at least 10, with your DEX over 25#k. Let's see.");
|
||||
else {
|
||||
cm.sendOk("Train a bit more and I can show you the way of the #rThief#k.");
|
||||
cm.dispose();
|
||||
}
|
||||
} else if (cm.getLevel() >= 30 && cm.getJobId() == 400) {
|
||||
actionx["2ndJob"] = true;
|
||||
if (cm.haveItem(4031012))
|
||||
cm.sendNext("I see you have done well. I will allow you to take the next step on your long road.");
|
||||
else if (cm.haveItem(4031011)){
|
||||
cm.sendOk("Go and see the #b#p1072003##k.");
|
||||
cm.dispose();
|
||||
} else
|
||||
cm.sendNext("The progress you have made is astonishing.");
|
||||
} else if (actionx["3thJobI"] || (cm.getPlayer().gotPartyQuestItem("JB3") && cm.getLevel() >= 70 && cm.getJobId() % 10 == 0 && parseInt(cm.getJobId() / 100) == 4 && !cm.getPlayer().gotPartyQuestItem("JBP"))){
|
||||
actionx["3thJobI"] = true;
|
||||
cm.sendNext("There you are. A few days ago, #b#p2020011##k of Ossyria talked to me about you. I see that you are interested in making the leap to the dark world of the third job advancement for thieves. To archieve that goal, I will have to test your strength in orden to see whether you are worthy of the advancement. There is an opening in the middle of a deep swamp in Victoria Island, where it'll lead you to a secret passage. Once inside, you'll face a clone of myself. Your task is to defeat him and bring #b#t4031059##k back with you.");
|
||||
} else if (cm.getPlayer().gotPartyQuestItem("JBP") && !cm.haveItem(4031059)){
|
||||
cm.sendNext("Please, bring me the #b#t4031059##k.");
|
||||
cm.dispose();
|
||||
} else if (cm.haveItem(4031059) && cm.getPlayer().gotPartyQuestItem("JBP")){
|
||||
actionx["3thJobC"] = true;
|
||||
cm.sendNext("Nice work. You have defeated my clone and brought #b#t4031059##k back safely. You have now proven yourself worthy of the 3rd job advancement from the physical standpoint. Now you should give this necklace to #b#p2020011##k in Ossyria to take on the second part of the test. Good luck. You'll need it.");
|
||||
} else if (cm.isQuestStarted(6141)) {
|
||||
cm.warp(910300000, 3);
|
||||
} else {
|
||||
cm.sendOk("You have chosen wisely.");
|
||||
cm.dispose();
|
||||
}
|
||||
} else if (cm.getLevel() >= 30 && cm.getJobId() == 400) {
|
||||
actionx["2ndJob"] = true;
|
||||
if (cm.haveItem(4031012))
|
||||
cm.sendNext("I see you have done well. I will allow you to take the next step on your long road.");
|
||||
else if (cm.haveItem(4031011)){
|
||||
cm.sendOk("Go and see the #b#p1072003##k.");
|
||||
cm.dispose();
|
||||
} else
|
||||
cm.sendNext("The progress you have made is astonishing.");
|
||||
} else if (actionx["3thJobI"] || (cm.getPlayer().gotPartyQuestItem("JB3") && cm.getLevel() >= 70 && cm.getJobId() % 10 == 0 && parseInt(cm.getJobId() / 100) == 4 && !cm.getPlayer().gotPartyQuestItem("JBP"))){
|
||||
actionx["3thJobI"] = true;
|
||||
cm.sendNext("There you are. A few days ago, #b#p2020011##k of Ossyria talked to me about you. I see that you are interested in making the leap to the dark world of the third job advancement for thieves. To archieve that goal, I will have to test your strength in orden to see whether you are worthy of the advancement. There is an opening in the middle of a deep swamp in Victoria Island, where it'll lead you to a secret passage. Once inside, you'll face a clone of myself. Your task is to defeat him and bring #b#t4031059##k back with you.");
|
||||
} else if (cm.getPlayer().gotPartyQuestItem("JBP") && !cm.haveItem(4031059)){
|
||||
cm.sendNext("Please, bring me the #b#t4031059##k.");
|
||||
cm.dispose();
|
||||
} else if (cm.haveItem(4031059) && cm.getPlayer().gotPartyQuestItem("JBP")){
|
||||
actionx["3thJobC"] = true;
|
||||
cm.sendNext("Nice work. You have defeated my clone and brought #b#t4031059##k back safely. You have now proven yourself worthy of the 3rd job advancement from the physical standpoint. Now you should give this necklace to #b#p2020011##k in Ossyria to take on the second part of the test. Good luck. You'll need it.");
|
||||
} else if (cm.isQuestStarted(6141)) {
|
||||
cm.warp(910300000, 3);
|
||||
} else {
|
||||
cm.sendOk("You have chosen wisely.");
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,14 +90,37 @@ function action(mode, type, selection) {
|
||||
if (status == -1){
|
||||
start();
|
||||
return;
|
||||
} else if (mode != 1 || status == 7 && type != 1 || (actionx["1stJob"] && status == 4) || (cm.haveItem(4031008) && status == 2) || (actionx["3thJobI"] && status == 1)){
|
||||
if (mode == 0 && status == 2 && type == 1)
|
||||
cm.sendOk("You know there is no other choice...");
|
||||
if (!(mode == 0 && type != 1)){
|
||||
} else {
|
||||
if(spawnPnpc) {
|
||||
if(mode > 0) {
|
||||
if(cm.getMeso() < spawnPnpcFee) {
|
||||
cm.sendOk("Sorry, you don't have enough mesos to purchase your place on the Hall of Fame.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if(Packages.server.life.MaplePlayerNPC.spawnPlayerNPC(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()), cm.getPlayer())) {
|
||||
cm.sendOk("There you go! Hope you will like it.");
|
||||
cm.gainMeso(-spawnPnpcFee);
|
||||
} else {
|
||||
cm.sendOk("Sorry, the Hall of Fame is currently full...");
|
||||
}
|
||||
}
|
||||
|
||||
cm.dispose();
|
||||
return;
|
||||
} else {
|
||||
if (mode != 1 || status == 7 && type != 1 || (actionx["1stJob"] && status == 4) || (cm.haveItem(4031008) && status == 2) || (actionx["3thJobI"] && status == 1)){
|
||||
if (mode == 0 && status == 2 && type == 1)
|
||||
cm.sendOk("You know there is no other choice...");
|
||||
if (!(mode == 0 && type != 1)){
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (actionx["1stJob"]){
|
||||
if (status == 0)
|
||||
cm.sendYesNo("Oh...! You look like someone that can definitely be a part of us... all you need is a little sinister mind, and... yeah... so, what do you think? Wanna be the Rogue?");
|
||||
|
||||
+70
-28
@@ -29,33 +29,52 @@ status = -1;
|
||||
actionx = {"1stJob" : false, "2ndjob" : false, "2ndjobT" : false, "3thJobI" : false, "3thJobC" : false};
|
||||
job = 510;
|
||||
|
||||
spawnPnpc = false;
|
||||
spawnPnpcFee = 7000000;
|
||||
jobType = 5;
|
||||
|
||||
function numberWithCommas(x) {
|
||||
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
}
|
||||
|
||||
function start() {
|
||||
if (cm.getJobId() == 0) {
|
||||
actionx["1stJob"] = true;
|
||||
if (cm.getLevel() >= 10)
|
||||
cm.sendNext("Want to be a pirate? There are some standards to meet. because we can't just accept EVERYONE in... #bYour level should be at least 10#k. Let's see.");
|
||||
else {
|
||||
cm.sendOk("Train a bit more and I can show you the way of the #rPirate#k.");
|
||||
if (parseInt(cm.getJobId() / 100) == jobType && cm.canSpawnPlayerNpc(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()))) {
|
||||
spawnPnpc = true;
|
||||
|
||||
var sendStr = "You have walked a long way to reach the power, wisdom and courage you hold today, didn't you? What do you say about having right now #ra NPC on the Hall of Fame holding the current image of your character#k? Do you like it?";
|
||||
if(spawnPnpcFee > 0) {
|
||||
sendStr += " I can do it for you, for the fee of #b " + numberWithCommas(spawnPnpcFee) + " mesos.#k";
|
||||
}
|
||||
|
||||
cm.sendYesNo(sendStr);
|
||||
} else {
|
||||
if (cm.getJobId() == 0) {
|
||||
actionx["1stJob"] = true;
|
||||
if (cm.getLevel() >= 10)
|
||||
cm.sendNext("Want to be a pirate? There are some standards to meet. because we can't just accept EVERYONE in... #bYour level should be at least 10#k. Let's see.");
|
||||
else {
|
||||
cm.sendOk("Train a bit more and I can show you the way of the #rPirate#k.");
|
||||
cm.dispose();
|
||||
}
|
||||
} else if (cm.getLevel() >= 30 && cm.getJobId() == 500) {
|
||||
actionx["2ndJob"] = true;
|
||||
if (cm.isQuestCompleted(2191) || cm.isQuestCompleted(2192))
|
||||
cm.sendNext("I see you have done well. I will allow you to take the next step on your long road.");
|
||||
else
|
||||
cm.sendNext("The progress you have made is astonishing.");
|
||||
} else if (actionx["3thJobI"] || (cm.getPlayer().gotPartyQuestItem("JB3") && cm.getLevel() >= 70 && cm.getJobId() % 10 == 0 && parseInt(cm.getJobId() / 100) == 5 && !cm.getPlayer().gotPartyQuestItem("JBP"))){
|
||||
actionx["3thJobI"] = true;
|
||||
cm.sendNext("There you are. A few days ago, #b#p2020013##k of Ossyria talked to me about you. I see that you are interested in making the leap to the world of the third job advancement for pirates. To archieve that goal, I will have to test your strength in order to see whether you are worthy of the advancement. There is an opening in the middle of a cave on Victoria Island, where it'll lead you to a secret passage. Once inside, you'll face a clone of myself. Your task is to defeat him and bring #b#t4031059##k back with you.");
|
||||
} else if (cm.getPlayer().gotPartyQuestItem("JBP") && !cm.haveItem(4031059)){
|
||||
cm.sendNext("Please, bring me the #b#t4031059##k.");
|
||||
cm.dispose();
|
||||
} else if (cm.haveItem(4031059) && cm.getPlayer().gotPartyQuestItem("JBP")){
|
||||
actionx["3thJobC"] = true;
|
||||
cm.sendNext("Nice work. You have defeated my clone and brought #b#t4031059##k back safely. You have now proven yourself worthy of the 3rd job advancement from the physical standpoint. Now you should give this necklace to #b#p2020013##k in Ossyria to take on the second part of the test. Good luck. You'll need it.");
|
||||
} else {
|
||||
cm.sendOk("You have chosen wisely.");
|
||||
cm.dispose();
|
||||
}
|
||||
} else if (cm.getLevel() >= 30 && cm.getJobId() == 500) {
|
||||
actionx["2ndJob"] = true;
|
||||
if (cm.isQuestCompleted(2191) || cm.isQuestCompleted(2192))
|
||||
cm.sendNext("I see you have done well. I will allow you to take the next step on your long road.");
|
||||
else
|
||||
cm.sendNext("The progress you have made is astonishing.");
|
||||
} else if (actionx["3thJobI"] || (cm.getPlayer().gotPartyQuestItem("JB3") && cm.getLevel() >= 70 && cm.getJobId() % 10 == 0 && parseInt(cm.getJobId() / 100) == 5 && !cm.getPlayer().gotPartyQuestItem("JBP"))){
|
||||
actionx["3thJobI"] = true;
|
||||
cm.sendNext("There you are. A few days ago, #b#p2020013##k of Ossyria talked to me about you. I see that you are interested in making the leap to the world of the third job advancement for pirates. To archieve that goal, I will have to test your strength in order to see whether you are worthy of the advancement. There is an opening in the middle of a cave on Victoria Island, where it'll lead you to a secret passage. Once inside, you'll face a clone of myself. Your task is to defeat him and bring #b#t4031059##k back with you.");
|
||||
} else if (cm.getPlayer().gotPartyQuestItem("JBP") && !cm.haveItem(4031059)){
|
||||
cm.sendNext("Please, bring me the #b#t4031059##k.");
|
||||
cm.dispose();
|
||||
} else if (cm.haveItem(4031059) && cm.getPlayer().gotPartyQuestItem("JBP")){
|
||||
actionx["3thJobC"] = true;
|
||||
cm.sendNext("Nice work. You have defeated my clone and brought #b#t4031059##k back safely. You have now proven yourself worthy of the 3rd job advancement from the physical standpoint. Now you should give this necklace to #b#p2020013##k in Ossyria to take on the second part of the test. Good luck. You'll need it.");
|
||||
} else {
|
||||
cm.sendOk("You have chosen wisely.");
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,14 +85,37 @@ function action(mode, type, selection) {
|
||||
if (status == -1){
|
||||
start();
|
||||
return;
|
||||
} else if (mode != 1 || status == 7 && type != 1 || (actionx["1stJob"] && status == 4) || (cm.haveItem(4031008) && status == 2) || (actionx["3thJobI"] && status == 1)){
|
||||
if (mode == 0 && status == 2 && type == 1)
|
||||
cm.sendOk("You know there is no other choice...");
|
||||
if (!(mode == 0 && type != 1)){
|
||||
} else {
|
||||
if(spawnPnpc) {
|
||||
if(mode > 0) {
|
||||
if(cm.getMeso() < spawnPnpcFee) {
|
||||
cm.sendOk("Sorry, you don't have enough mesos to purchase your place on the Hall of Fame.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if(Packages.server.life.MaplePlayerNPC.spawnPlayerNPC(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()), cm.getPlayer())) {
|
||||
cm.sendOk("There you go! Hope you will like it.");
|
||||
cm.gainMeso(-spawnPnpcFee);
|
||||
} else {
|
||||
cm.sendOk("Sorry, the Hall of Fame is currently full...");
|
||||
}
|
||||
}
|
||||
|
||||
cm.dispose();
|
||||
return;
|
||||
} else {
|
||||
if (mode != 1 || status == 7 && type != 1 || (actionx["1stJob"] && status == 4) || (cm.haveItem(4031008) && status == 2) || (actionx["3thJobI"] && status == 1)){
|
||||
if (mode == 0 && status == 2 && type == 1)
|
||||
cm.sendOk("You know there is no other choice...");
|
||||
if (!(mode == 0 && type != 1)){
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (actionx["1stJob"]){
|
||||
if (status == 0)
|
||||
cm.sendYesNo("Oh...! You look like someone that can definitely be a part of us... all you need is a little slang, and... yeah... so, what do you think? Wanna be the Pirate?");
|
||||
|
||||
+58
-3
@@ -20,9 +20,64 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/*
|
||||
* @author BubblesDev, Rich for text
|
||||
* @author BubblesDev, Rich for text, Ronan for PNPCs
|
||||
*/
|
||||
|
||||
var status = 0;
|
||||
var spawnPnpc = false;
|
||||
var spawnPnpcFee = 7000000;
|
||||
var minJobType = 11;
|
||||
var maxJobType = 15;
|
||||
|
||||
function numberWithCommas(x) {
|
||||
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
}
|
||||
|
||||
function start() {
|
||||
cm.sendOk("Welcome to the Knights Chamber.");
|
||||
cm.dispose();
|
||||
var jobType = parseInt(cm.getJobId() / 100);
|
||||
if (jobType >= minJobType && jobType <= maxJobType && cm.canSpawnPlayerNpc(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()))) {
|
||||
spawnPnpc = true;
|
||||
|
||||
var sendStr = "You have walked a long way to reach the power, wisdom and courage you hold today, didn't you? What do you say about having right now #ra NPC on the Hall of Fame holding the current image of your character#k? Do you like it?";
|
||||
if(spawnPnpcFee > 0) {
|
||||
sendStr += " I can do it for you, for the fee of #b " + numberWithCommas(spawnPnpcFee) + " mesos.#k";
|
||||
}
|
||||
|
||||
cm.sendYesNo(sendStr);
|
||||
} else {
|
||||
cm.sendOk("Welcome to the Knights Chamber.");
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
status++;
|
||||
if (mode == 0 && type != 1)
|
||||
status -= 2;
|
||||
if (status == -1){
|
||||
start();
|
||||
return;
|
||||
} else {
|
||||
if(spawnPnpc) {
|
||||
if(mode > 0) {
|
||||
if(cm.getMeso() < spawnPnpcFee) {
|
||||
cm.sendOk("Sorry, you don't have enough mesos to purchase your place on the Hall of Fame.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if(Packages.server.life.MaplePlayerNPC.spawnPlayerNPC(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()), cm.getPlayer())) {
|
||||
cm.sendOk("There you go! Hope you will like it.");
|
||||
cm.gainMeso(-spawnPnpcFee);
|
||||
} else {
|
||||
cm.sendOk("Sorry, the Hall of Fame is currently full...");
|
||||
}
|
||||
}
|
||||
|
||||
cm.dispose();
|
||||
return;
|
||||
} else {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
+50
-4
@@ -1,11 +1,57 @@
|
||||
var status = 0;
|
||||
|
||||
var spawnPnpc = false;
|
||||
var spawnPnpcFee = 7000000;
|
||||
var jobType = 21;
|
||||
|
||||
function numberWithCommas(x) {
|
||||
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
}
|
||||
|
||||
function start() {
|
||||
status = -1;
|
||||
action(1, 0, 0);
|
||||
if (parseInt(cm.getJobId() / 100) == jobType && cm.canSpawnPlayerNpc(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()))) {
|
||||
spawnPnpc = true;
|
||||
|
||||
var sendStr = "You have walked a long way to reach the power, wisdom and courage you hold today, didn't you? What do you say about having right now #ra NPC on the Hall of Fame holding the current image of your character#k? Do you like it?";
|
||||
if(spawnPnpcFee > 0) {
|
||||
sendStr += " I can do it for you, for the fee of #b " + numberWithCommas(spawnPnpcFee) + " mesos.#k";
|
||||
}
|
||||
|
||||
cm.sendYesNo(sendStr);
|
||||
} else {
|
||||
cm.sendOk("Behold, the distinguished brave heroes of Rien! Those stout hearts are who have been protecting our people since long ago, our valiant comrades.");
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
cm.sendOk("Behold, the distinguished brave heroes of Rien! Those stout hearts are who have been protecting our people since long ago, our valiant comrades.");
|
||||
cm.dispose();
|
||||
status++;
|
||||
if (mode == 0 && type != 1)
|
||||
status -= 2;
|
||||
if (status == -1){
|
||||
start();
|
||||
return;
|
||||
} else {
|
||||
if(spawnPnpc) {
|
||||
if(mode > 0) {
|
||||
if(cm.getMeso() < spawnPnpcFee) {
|
||||
cm.sendOk("Sorry, you don't have enough mesos to purchase your place on the Hall of Fame.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if(Packages.server.life.MaplePlayerNPC.spawnPlayerNPC(Packages.constants.GameConstants.getHallOfFameMapid(cm.getJob()), cm.getPlayer())) {
|
||||
cm.sendOk("There you go! Hope you will like it.");
|
||||
cm.gainMeso(-spawnPnpcFee);
|
||||
} else {
|
||||
cm.sendOk("Sorry, the Hall of Fame is currently full...");
|
||||
}
|
||||
}
|
||||
|
||||
cm.dispose();
|
||||
return;
|
||||
} else {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
/* Harp String C
|
||||
Hidden Street - Eliza’s Garden (200010303)
|
||||
*/
|
||||
|
||||
importPackage(Packages.tools);
|
||||
|
||||
var status;
|
||||
var harpNote = 'C';
|
||||
var harpSounds = ["do", "la", "mi", "pa", "re", "si", "sol"];
|
||||
var harpSong = "CCGGAAGFFEEDDC|GGFFEED|GGFFEED|CCGGAAGFFEEDDC|";
|
||||
|
||||
function start() {
|
||||
status = -1;
|
||||
action(1, 0, 0);
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
if (mode == -1) {
|
||||
cm.dispose();
|
||||
} else {
|
||||
if (mode == 0 && type > 0) {
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
if (mode == 1)
|
||||
status++;
|
||||
else
|
||||
status--;
|
||||
|
||||
if(status == 0) {
|
||||
cm.getMap().broadcastMessage(MaplePacketCreator.playSound("orbis/" + harpSounds[cm.getNpc() - 2012027]));
|
||||
|
||||
if(cm.isQuestStarted(3114)) {
|
||||
var idx = cm.getQuestProgress(3114, 7777);
|
||||
|
||||
if(idx != -1) {
|
||||
var nextNote = harpSong[idx];
|
||||
|
||||
if(harpNote != nextNote) {
|
||||
cm.setQuestProgress(3114, 7777, 0);
|
||||
|
||||
cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/wrong_kor"));
|
||||
cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Failed"));
|
||||
|
||||
cm.message("You've missed the note... Start over again.");
|
||||
} else {
|
||||
nextNote = harpSong[idx + 1];
|
||||
|
||||
if(nextNote == '|') {
|
||||
idx++;
|
||||
|
||||
if(idx == 45) { // finished lullaby
|
||||
cm.message("Twinkle, twinkle, little star, how I wonder what you are.");
|
||||
cm.setQuestProgress(3114, 7777, -1);
|
||||
|
||||
cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/clear"));
|
||||
cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Clear"));
|
||||
|
||||
cm.dispose();
|
||||
return;
|
||||
} else {
|
||||
if(idx == 14) {
|
||||
cm.message("Twinkle, twinkle, little star, how I wonder what you are!");
|
||||
} else if(idx == 22) {
|
||||
cm.message("Up above the world so high,");
|
||||
} else if(idx == 30) {
|
||||
cm.message("like a diamond in the sky.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cm.setQuestProgress(3114, 7777, idx + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
/* Harp String D
|
||||
Hidden Street - Eliza’s Garden (200010303)
|
||||
*/
|
||||
|
||||
importPackage(Packages.tools);
|
||||
|
||||
var status;
|
||||
var harpNote = 'D';
|
||||
var harpSounds = ["do", "la", "mi", "pa", "re", "si", "sol"];
|
||||
var harpSong = "CCGGAAGFFEEDDC|GGFFEED|GGFFEED|CCGGAAGFFEEDDC|";
|
||||
|
||||
function start() {
|
||||
status = -1;
|
||||
action(1, 0, 0);
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
if (mode == -1) {
|
||||
cm.dispose();
|
||||
} else {
|
||||
if (mode == 0 && type > 0) {
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
if (mode == 1)
|
||||
status++;
|
||||
else
|
||||
status--;
|
||||
|
||||
if(status == 0) {
|
||||
cm.getMap().broadcastMessage(MaplePacketCreator.playSound("orbis/" + harpSounds[cm.getNpc() - 2012027]));
|
||||
|
||||
if(cm.isQuestStarted(3114)) {
|
||||
var idx = cm.getQuestProgress(3114, 7777);
|
||||
|
||||
if(idx != -1) {
|
||||
var nextNote = harpSong[idx];
|
||||
|
||||
if(harpNote != nextNote) {
|
||||
cm.setQuestProgress(3114, 7777, 0);
|
||||
|
||||
cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/wrong_kor"));
|
||||
cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Failed"));
|
||||
|
||||
cm.message("You've missed the note... Start over again.");
|
||||
} else {
|
||||
nextNote = harpSong[idx + 1];
|
||||
|
||||
if(nextNote == '|') {
|
||||
idx++;
|
||||
|
||||
if(idx == 45) { // finished lullaby
|
||||
cm.message("Twinkle, twinkle, little star, how I wonder what you are.");
|
||||
cm.setQuestProgress(3114, 7777, -1);
|
||||
|
||||
cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/clear"));
|
||||
cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Clear"));
|
||||
|
||||
cm.dispose();
|
||||
return;
|
||||
} else {
|
||||
if(idx == 14) {
|
||||
cm.message("Twinkle, twinkle, little star, how I wonder what you are!");
|
||||
} else if(idx == 22) {
|
||||
cm.message("Up above the world so high,");
|
||||
} else if(idx == 30) {
|
||||
cm.message("like a diamond in the sky.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cm.setQuestProgress(3114, 7777, idx + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
/* Harp String E
|
||||
Hidden Street - Eliza’s Garden (200010303)
|
||||
*/
|
||||
|
||||
importPackage(Packages.tools);
|
||||
|
||||
var status;
|
||||
var harpNote = 'E';
|
||||
var harpSounds = ["do", "la", "mi", "pa", "re", "si", "sol"];
|
||||
var harpSong = "CCGGAAGFFEEDDC|GGFFEED|GGFFEED|CCGGAAGFFEEDDC|";
|
||||
|
||||
function start() {
|
||||
status = -1;
|
||||
action(1, 0, 0);
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
if (mode == -1) {
|
||||
cm.dispose();
|
||||
} else {
|
||||
if (mode == 0 && type > 0) {
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
if (mode == 1)
|
||||
status++;
|
||||
else
|
||||
status--;
|
||||
|
||||
if(status == 0) {
|
||||
cm.getMap().broadcastMessage(MaplePacketCreator.playSound("orbis/" + harpSounds[cm.getNpc() - 2012027]));
|
||||
|
||||
if(cm.isQuestStarted(3114)) {
|
||||
var idx = cm.getQuestProgress(3114, 7777);
|
||||
|
||||
if(idx != -1) {
|
||||
var nextNote = harpSong[idx];
|
||||
|
||||
if(harpNote != nextNote) {
|
||||
cm.setQuestProgress(3114, 7777, 0);
|
||||
|
||||
cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/wrong_kor"));
|
||||
cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Failed"));
|
||||
|
||||
cm.message("You've missed the note... Start over again.");
|
||||
} else {
|
||||
nextNote = harpSong[idx + 1];
|
||||
|
||||
if(nextNote == '|') {
|
||||
idx++;
|
||||
|
||||
if(idx == 45) { // finished lullaby
|
||||
cm.message("Twinkle, twinkle, little star, how I wonder what you are.");
|
||||
cm.setQuestProgress(3114, 7777, -1);
|
||||
|
||||
cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/clear"));
|
||||
cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Clear"));
|
||||
|
||||
cm.dispose();
|
||||
return;
|
||||
} else {
|
||||
if(idx == 14) {
|
||||
cm.message("Twinkle, twinkle, little star, how I wonder what you are!");
|
||||
} else if(idx == 22) {
|
||||
cm.message("Up above the world so high,");
|
||||
} else if(idx == 30) {
|
||||
cm.message("like a diamond in the sky.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cm.setQuestProgress(3114, 7777, idx + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
/* Harp String F
|
||||
Hidden Street - Eliza’s Garden (200010303)
|
||||
*/
|
||||
|
||||
importPackage(Packages.tools);
|
||||
|
||||
var status;
|
||||
var harpNote = 'F';
|
||||
var harpSounds = ["do", "la", "mi", "pa", "re", "si", "sol"];
|
||||
var harpSong = "CCGGAAGFFEEDDC|GGFFEED|GGFFEED|CCGGAAGFFEEDDC|";
|
||||
|
||||
function start() {
|
||||
status = -1;
|
||||
action(1, 0, 0);
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
if (mode == -1) {
|
||||
cm.dispose();
|
||||
} else {
|
||||
if (mode == 0 && type > 0) {
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
if (mode == 1)
|
||||
status++;
|
||||
else
|
||||
status--;
|
||||
|
||||
if(status == 0) {
|
||||
cm.getMap().broadcastMessage(MaplePacketCreator.playSound("orbis/" + harpSounds[cm.getNpc() - 2012027]));
|
||||
|
||||
if(cm.isQuestStarted(3114)) {
|
||||
var idx = cm.getQuestProgress(3114, 7777);
|
||||
|
||||
if(idx != -1) {
|
||||
var nextNote = harpSong[idx];
|
||||
|
||||
if(harpNote != nextNote) {
|
||||
cm.setQuestProgress(3114, 7777, 0);
|
||||
|
||||
cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/wrong_kor"));
|
||||
cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Failed"));
|
||||
|
||||
cm.message("You've missed the note... Start over again.");
|
||||
} else {
|
||||
nextNote = harpSong[idx + 1];
|
||||
|
||||
if(nextNote == '|') {
|
||||
idx++;
|
||||
|
||||
if(idx == 45) { // finished lullaby
|
||||
cm.message("Twinkle, twinkle, little star, how I wonder what you are.");
|
||||
cm.setQuestProgress(3114, 7777, -1);
|
||||
|
||||
cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/clear"));
|
||||
cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Clear"));
|
||||
|
||||
cm.dispose();
|
||||
return;
|
||||
} else {
|
||||
if(idx == 14) {
|
||||
cm.message("Twinkle, twinkle, little star, how I wonder what you are!");
|
||||
} else if(idx == 22) {
|
||||
cm.message("Up above the world so high,");
|
||||
} else if(idx == 30) {
|
||||
cm.message("like a diamond in the sky.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cm.setQuestProgress(3114, 7777, idx + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
/* Harp String G
|
||||
Hidden Street - Eliza’s Garden (200010303)
|
||||
*/
|
||||
|
||||
importPackage(Packages.tools);
|
||||
|
||||
var status;
|
||||
var harpNote = 'G';
|
||||
var harpSounds = ["do", "la", "mi", "pa", "re", "si", "sol"];
|
||||
var harpSong = "CCGGAAGFFEEDDC|GGFFEED|GGFFEED|CCGGAAGFFEEDDC|";
|
||||
|
||||
function start() {
|
||||
status = -1;
|
||||
action(1, 0, 0);
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
if (mode == -1) {
|
||||
cm.dispose();
|
||||
} else {
|
||||
if (mode == 0 && type > 0) {
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
if (mode == 1)
|
||||
status++;
|
||||
else
|
||||
status--;
|
||||
|
||||
if(status == 0) {
|
||||
cm.getMap().broadcastMessage(MaplePacketCreator.playSound("orbis/" + harpSounds[cm.getNpc() - 2012027]));
|
||||
|
||||
if(cm.isQuestStarted(3114)) {
|
||||
var idx = cm.getQuestProgress(3114, 7777);
|
||||
|
||||
if(idx != -1) {
|
||||
var nextNote = harpSong[idx];
|
||||
|
||||
if(harpNote != nextNote) {
|
||||
cm.setQuestProgress(3114, 7777, 0);
|
||||
|
||||
cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/wrong_kor"));
|
||||
cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Failed"));
|
||||
|
||||
cm.message("You've missed the note... Start over again.");
|
||||
} else {
|
||||
nextNote = harpSong[idx + 1];
|
||||
|
||||
if(nextNote == '|') {
|
||||
idx++;
|
||||
|
||||
if(idx == 45) { // finished lullaby
|
||||
cm.message("Twinkle, twinkle, little star, how I wonder what you are.");
|
||||
cm.setQuestProgress(3114, 7777, -1);
|
||||
|
||||
cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/clear"));
|
||||
cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Clear"));
|
||||
|
||||
cm.dispose();
|
||||
return;
|
||||
} else {
|
||||
if(idx == 14) {
|
||||
cm.message("Twinkle, twinkle, little star, how I wonder what you are!");
|
||||
} else if(idx == 22) {
|
||||
cm.message("Up above the world so high,");
|
||||
} else if(idx == 30) {
|
||||
cm.message("like a diamond in the sky.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cm.setQuestProgress(3114, 7777, idx + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
/* Harp String A
|
||||
Hidden Street - Eliza’s Garden (200010303)
|
||||
*/
|
||||
|
||||
importPackage(Packages.tools);
|
||||
|
||||
var status;
|
||||
var harpNote = 'A';
|
||||
var harpSounds = ["do", "la", "mi", "pa", "re", "si", "sol"];
|
||||
var harpSong = "CCGGAAGFFEEDDC|GGFFEED|GGFFEED|CCGGAAGFFEEDDC|";
|
||||
|
||||
function start() {
|
||||
status = -1;
|
||||
action(1, 0, 0);
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
if (mode == -1) {
|
||||
cm.dispose();
|
||||
} else {
|
||||
if (mode == 0 && type > 0) {
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
if (mode == 1)
|
||||
status++;
|
||||
else
|
||||
status--;
|
||||
|
||||
if(status == 0) {
|
||||
cm.getMap().broadcastMessage(MaplePacketCreator.playSound("orbis/" + harpSounds[cm.getNpc() - 2012027]));
|
||||
|
||||
if(cm.isQuestStarted(3114)) {
|
||||
var idx = cm.getQuestProgress(3114, 7777);
|
||||
|
||||
if(idx != -1) {
|
||||
var nextNote = harpSong[idx];
|
||||
|
||||
if(harpNote != nextNote) {
|
||||
cm.setQuestProgress(3114, 7777, 0);
|
||||
|
||||
cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/wrong_kor"));
|
||||
cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Failed"));
|
||||
|
||||
cm.message("You've missed the note... Start over again.");
|
||||
} else {
|
||||
nextNote = harpSong[idx + 1];
|
||||
|
||||
if(nextNote == '|') {
|
||||
idx++;
|
||||
|
||||
if(idx == 45) { // finished lullaby
|
||||
cm.message("Twinkle, twinkle, little star, how I wonder what you are.");
|
||||
cm.setQuestProgress(3114, 7777, -1);
|
||||
|
||||
cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/clear"));
|
||||
cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Clear"));
|
||||
|
||||
cm.dispose();
|
||||
return;
|
||||
} else {
|
||||
if(idx == 14) {
|
||||
cm.message("Twinkle, twinkle, little star, how I wonder what you are!");
|
||||
} else if(idx == 22) {
|
||||
cm.message("Up above the world so high,");
|
||||
} else if(idx == 30) {
|
||||
cm.message("like a diamond in the sky.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cm.setQuestProgress(3114, 7777, idx + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
/* Harp String B
|
||||
Hidden Street - Eliza’s Garden (200010303)
|
||||
*/
|
||||
|
||||
importPackage(Packages.tools);
|
||||
|
||||
var status;
|
||||
var harpNote = 'B';
|
||||
var harpSounds = ["do", "la", "mi", "pa", "re", "si", "sol"];
|
||||
var harpSong = "CCGGAAGFFEEDDC|GGFFEED|GGFFEED|CCGGAAGFFEEDDC|";
|
||||
|
||||
function start() {
|
||||
status = -1;
|
||||
action(1, 0, 0);
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
if (mode == -1) {
|
||||
cm.dispose();
|
||||
} else {
|
||||
if (mode == 0 && type > 0) {
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
if (mode == 1)
|
||||
status++;
|
||||
else
|
||||
status--;
|
||||
|
||||
if(status == 0) {
|
||||
cm.getMap().broadcastMessage(MaplePacketCreator.playSound("orbis/" + harpSounds[cm.getNpc() - 2012027]));
|
||||
|
||||
if(cm.isQuestStarted(3114)) {
|
||||
var idx = cm.getQuestProgress(3114, 7777);
|
||||
|
||||
if(idx != -1) {
|
||||
var nextNote = harpSong[idx];
|
||||
|
||||
if(harpNote != nextNote) {
|
||||
cm.setQuestProgress(3114, 7777, 0);
|
||||
|
||||
cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/wrong_kor"));
|
||||
cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Failed"));
|
||||
|
||||
cm.message("You've missed the note... Start over again.");
|
||||
} else {
|
||||
nextNote = harpSong[idx + 1];
|
||||
|
||||
if(nextNote == '|') {
|
||||
idx++;
|
||||
|
||||
if(idx == 45) { // finished lullaby
|
||||
cm.message("Twinkle, twinkle, little star, how I wonder what you are.");
|
||||
cm.setQuestProgress(3114, 7777, -1);
|
||||
|
||||
cm.getPlayer().announce(MaplePacketCreator.showEffect("quest/party/clear"));
|
||||
cm.getPlayer().announce(MaplePacketCreator.playSound("Party1/Clear"));
|
||||
|
||||
cm.dispose();
|
||||
return;
|
||||
} else {
|
||||
if(idx == 14) {
|
||||
cm.message("Twinkle, twinkle, little star, how I wonder what you are!");
|
||||
} else if(idx == 22) {
|
||||
cm.message("Up above the world so high,");
|
||||
} else if(idx == 30) {
|
||||
cm.message("like a diamond in the sky.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cm.setQuestProgress(3114, 7777, idx + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
-14
@@ -40,24 +40,20 @@ function action(mode, type, selection) {
|
||||
else
|
||||
status--;
|
||||
if (status == 0 && mode == 1) {
|
||||
if (!cm.isQuestStarted(4900)){
|
||||
if (cm.isQuestCompleted(4911)) {
|
||||
cm.sendNext("Good job! You've solved all of my questions about NLC. Enjoy your trip!");
|
||||
cm.dispose();
|
||||
} else if (cm.isQuestCompleted(4900) || cm.isQuestStarted(4900)) { // thanks imbee for pointing out the quiz leak
|
||||
cm.sendNext("Hey, pay attention, I'm trying to quiz you on another question, fam!");
|
||||
cm.dispose();
|
||||
} else {
|
||||
var selStr = "What up! Name's Icebyrd Slimm, mayor of New Leaf City! Happy to see you accepted my invite. So, what can I do for you?#b"
|
||||
var info = new Array("What is this place?","Who is Professor Foxwit?","What's a Foxwit Door?","Where are the MesoGears?","What is the Krakian Jungle?","What's a Gear Portal?","What do the street signs mean?","What's the deal with Jack Masque?","Lita Lawless looks like a tough cookie, what's her story?","When will new boroughs open up in the city?","I want to take the quiz!");
|
||||
for (var i = 0; i < info.length; i++)
|
||||
selStr += "\r\n#L" + i + "# " + info[i] + "#l";
|
||||
cm.sendSimple(selStr);
|
||||
}
|
||||
else if (!cm.isQuestStarted(4911)){
|
||||
cm.sendNext("Good job! You've solved all of my questions about NLC. Enjoy of your trip!");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
cm.sendNext("Hey, pay attention, I'm trying to quiz you on another question!");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
} else if(status == 1 && !cm.isQuestStarted(4900)) {
|
||||
} else if(status == 1) {
|
||||
switch (selection) {
|
||||
case 0:
|
||||
cm.sendNext("I've always dreamed of building a city. Not just any city, but one where everyone was welcome. I used to live in Kerning City, so I decided to see if I could create a city. As I went along in finding the means to do so, I encountered many people, some of whom I've come to regard as friends. Like Professor Foxwit-he's our resident genius; saved him from a group of man-eating plants. Jack Masque is an old hunting buddy from Amoria-almost too smooth of a talker for his own good. Lita and I are old friends from Kerning City-she's saved me a few times with that weapon of hers; so I figured she was a perfect choice for Town Sheriff. It took a bit of persuasion, but she came to believe her destiny lies here. About our resident explorer, Barricade came searching for something; he agreed to bring whatever he found to the museum. I'd heard stories about him and his brother when I was still in Kerning City. And Elpam...well, let's just say he's not from around here. At all. We've spoken before, and he seems to mean well, so I've allowed him to stay. I just realized that I've rambled quite a bit! What else would you like to know?");
|
||||
@@ -103,9 +99,10 @@ function action(mode, type, selection) {
|
||||
if (cm.getLevel() >= minlevel) {
|
||||
cm.sendNext("No problem. I'll give you something nice if you answer them correctly!");
|
||||
cm.startQuest(4900);
|
||||
}
|
||||
else
|
||||
} else {
|
||||
cm.sendNext("Eager, are we? How about you explore a bit more before I let you take the quiz?");
|
||||
}
|
||||
|
||||
cm.dispose();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
/* Ronan
|
||||
Hidden Street - Developers' Headquarters (777777777)
|
||||
HeavenMS developer info.
|
||||
*/
|
||||
|
||||
importPackage(Packages.tools);
|
||||
|
||||
var status;
|
||||
|
||||
var anthemSong = "Field/anthem/brazil"; // sound src: https://c7.rbxcdn.com/f91060652a6e9fbfbf92cb1418435448
|
||||
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", "Project"];
|
||||
|
||||
function addFeature(feature) {
|
||||
feature_cursor.push(feature);
|
||||
}
|
||||
|
||||
function writeFeatureTab_PQs() {
|
||||
addFeature("HPQ/KPQ/LPQ/LMPQ/OPQ/EllinPQ/PiratePQ.");
|
||||
addFeature("MagatiaPQ/HorntailPQ/AmoriaPQ/TreasurePQ.");
|
||||
addFeature("CWKPQ as Expedition-based event.");
|
||||
addFeature("Scarga/Horntail/Showa/Balrog/Zakum/Pinkbean.");
|
||||
addFeature("GuildPQ & queue with multi-lobby systems available.");
|
||||
addFeature("Brand-new PQs: BossRushPQ, CafePQ.");
|
||||
addFeature("Mu Lung Dojo.");
|
||||
addFeature("Capt. Latanica with party fighting the boss.");
|
||||
}
|
||||
|
||||
function writeFeatureTab_Skills() {
|
||||
addFeature("Reviewed many skills, such as V. Star and M. Door.");
|
||||
addFeature("Maker skill features properly developed.");
|
||||
addFeature("Chair Mastery - map chair boosts HP/MP rec.");
|
||||
}
|
||||
|
||||
function writeFeatureTab_Quests() {
|
||||
addFeature("Doll house quest functional.");
|
||||
addFeature("Quests are now rewarding items properly.");
|
||||
addFeature("Selection of rewards works properly.");
|
||||
addFeature("Loads of quests have been patched.");
|
||||
addFeature("Lots of job questlines have been reviewed.");
|
||||
addFeature("Rewarding system now looks up for item stacking.");
|
||||
addFeature("3rd job quiz with all 40-question pool available.");
|
||||
}
|
||||
|
||||
function writeFeatureTab_PlayerSocialNetwork() {
|
||||
addFeature("Guild and Alliance system fully functional.");
|
||||
addFeature("Party for novices-only (may still be disabled locally).");
|
||||
addFeature("Thoroughly reviewed P. Shops and H. Merchants.");
|
||||
addFeature("Transactions on Merchs instantly announced to owner.");
|
||||
addFeature("Game minirooms with semi-functional pw system.");
|
||||
addFeature("Proper item pickup cooldown on non-owned items.");
|
||||
addFeature("Improved ranking system, with daily movement.");
|
||||
addFeature("Automated support for Player NPCs and Hall of Fame.");
|
||||
}
|
||||
|
||||
function writeFeatureTab_CashItems() {
|
||||
addFeature("EXP/DROP/Cosmetic Coupons.");
|
||||
addFeature("EXP/DROP Coupon as buff effect during active time.");
|
||||
addFeature("Great deal of cash items functional.");
|
||||
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("Vega's spell.");
|
||||
addFeature("Owl of Minerva.");
|
||||
addFeature("Pet item ignore.");
|
||||
addFeature("New Year's card.");
|
||||
addFeature("Kite.");
|
||||
addFeature("Cash Shop surprise.");
|
||||
}
|
||||
|
||||
function writeFeatureTab_MonstersMapsReactors() {
|
||||
addFeature("Every monsterbook card is now droppable.");
|
||||
addFeature("Added meso drop data for many missing mobs.");
|
||||
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("Added Boss HP Bar for dozens of bosses.");
|
||||
addFeature("Game will favor showing the targeted boss HPbar.");
|
||||
addFeature("Boats, elevator and other travel mechanics functional.");
|
||||
addFeature("C. Balrog's boat approaching visual effect functional.");
|
||||
addFeature("PQs, Taxis and events warps players to random SPs.");
|
||||
addFeature("PQ boxes sprays items when opened, GMS-like.");
|
||||
addFeature("Reviewed Masteria, W. Tour, N. Desert and Neo City.");
|
||||
addFeature("Giant Cake boss drops s. bags and Maple items.");
|
||||
}
|
||||
|
||||
function writeFeatureTab_PQpotentials() {
|
||||
addFeature("Lobby system: Same channel, multiple PQ instances.");
|
||||
addFeature("Exped system: Many parties can join a same instance.");
|
||||
addFeature("Guild queue: guild registration for the GPQ.");
|
||||
addFeature("EIM Pool system: optimized instance loadouts.");
|
||||
}
|
||||
|
||||
function writeFeatureTab_Playerpotentials() {
|
||||
addFeature("Adventurer Mount quests functional.");
|
||||
addFeature("All Equipment levels up.");
|
||||
addFeature("Player level rates.");
|
||||
addFeature("Gain fame by quests.");
|
||||
}
|
||||
|
||||
function writeFeatureTab_Serverpotentials() {
|
||||
addFeature("Multi-worlds.");
|
||||
addFeature("Inventory auto-gather and auto-sorting feature.");
|
||||
addFeature("Enhanced auto-pot system: smart pet potion handle.");
|
||||
addFeature("Enhanced buff system: best buffs effects takes place.");
|
||||
addFeature("Enhanced AP auto-assigner: focus on eqp demands.");
|
||||
addFeature("Pet pickup gives preference to player attacks.");
|
||||
addFeature("Channel capacity bar and worlds with capacity check.");
|
||||
addFeature("Diseases visible for others, even after changing maps.");
|
||||
addFeature("Poison damage value visible for other players.");
|
||||
addFeature("M. book announcer displays info based on demand.");
|
||||
addFeature("Custom jail system.");
|
||||
addFeature("Delete Character.");
|
||||
addFeature("Autosaver (periodically saves player's data on DB).");
|
||||
addFeature("Fixed and randomized HP/MP growth rate available.");
|
||||
addFeature("Prevented 'NPC gone after some uptime' issue.");
|
||||
addFeature("Automatic account registration - thanks shavit!");
|
||||
}
|
||||
|
||||
function writeFeatureTab_AdminGMcommands() {
|
||||
addFeature("Server commands layered by GM levels.");
|
||||
addFeature("Spawn Zakum/Horntail/Pinkbean.");
|
||||
addFeature("Several new commands.");
|
||||
}
|
||||
|
||||
function writeFeatureTab_Project() {
|
||||
addFeature("Organized project code.");
|
||||
addFeature("Highly updated drop data.");
|
||||
addFeature("Highly configurable server.");
|
||||
addFeature("Fixed/added many missing packet opcodes.");
|
||||
addFeature("Uncovered many opcodes throughout the source.");
|
||||
addFeature("Reviewed many Java aspects that needed attention.");
|
||||
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.");
|
||||
}
|
||||
|
||||
function writeAllFeatures() {
|
||||
var re = /[ ,&\/]+/g;
|
||||
|
||||
for(var i = 0; i < tabs.length; i++) {
|
||||
feature_cursor = [];
|
||||
|
||||
var tabName = (tabs[i]).replace(re, "");
|
||||
eval("writeFeatureTab_" + tabName)();
|
||||
|
||||
feature_tree.push(feature_cursor);
|
||||
}
|
||||
}
|
||||
|
||||
function start() {
|
||||
cm.getPlayer().announce(MaplePacketCreator.musicChange(anthemSong));
|
||||
status = -1;
|
||||
writeAllFeatures();
|
||||
action(1, 0, 0);
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
if (mode == -1) {
|
||||
cm.getPlayer().announce(MaplePacketCreator.musicChange(ambientSong));
|
||||
cm.dispose();
|
||||
} else {
|
||||
if (mode == 0 && type > 0) {
|
||||
cm.getPlayer().announce(MaplePacketCreator.musicChange(ambientSong));
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
if (mode == 1)
|
||||
status++;
|
||||
else
|
||||
status--;
|
||||
|
||||
if (status == 0) {
|
||||
var sendStr = "HeavenMS was developed on the timespan of 3 years, based on where Solaxia left. On the meantime many nice features emerged, development aimed to get back the old GMS experience. Now many of these so-long missing features are gracefully presented to you in the shape of this server. Long live MapleStory!!\r\n\r\nThese are the features of #bHeavenMS#k:\r\n\r\n";
|
||||
for(var i = 0; i < tabs.length; i++) {
|
||||
sendStr += "#L" + i + "##b" + tabs[i] + "#k#l\r\n";
|
||||
}
|
||||
|
||||
cm.sendSimple(sendStr);
|
||||
} else if(status == 1) {
|
||||
var tabName;
|
||||
|
||||
for(var i = 0; i < tabs.length; i++) {
|
||||
if(selection == i) {
|
||||
tabName = feature_tree[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var sendStr = "#b" + tabs[selection] + "#k:\r\n\r\n";
|
||||
for(var i = 0; i < tabName.length; i++) {
|
||||
sendStr += " #L" + i + "# " + tabName[i];
|
||||
sendStr += "#l\r\n";
|
||||
}
|
||||
|
||||
cm.sendPrev(sendStr);
|
||||
} else {
|
||||
cm.getPlayer().announce(MaplePacketCreator.musicChange(ambientSong));
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function generateSelectionMenu(array) {
|
||||
var menu = "";
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
menu += "#L" + i + "#" + array[i] + "#l\r\n";
|
||||
}
|
||||
return menu;
|
||||
}
|
||||
+16
-16
@@ -41,7 +41,7 @@ function addCommand(comm, desc) {
|
||||
desc_cursor.push(desc);
|
||||
}
|
||||
|
||||
function writeSolaxiaCommandsLv6() { //Admin
|
||||
function writeHeavenMSCommandsLv6() { //Admin
|
||||
comm_cursor = comm_lv6;
|
||||
desc_cursor = desc_lv6;
|
||||
|
||||
@@ -57,7 +57,7 @@ function writeSolaxiaCommandsLv6() { //Admin
|
||||
addCommand("clearquest", "");
|
||||
}
|
||||
|
||||
function writeSolaxiaCommandsLv5() { //Developer
|
||||
function writeHeavenMSCommandsLv5() { //Developer
|
||||
comm_cursor = comm_lv5;
|
||||
desc_cursor = desc_lv5;
|
||||
|
||||
@@ -77,7 +77,7 @@ function writeSolaxiaCommandsLv5() { //Developer
|
||||
addCommand("set", "");
|
||||
}
|
||||
|
||||
function writeSolaxiaCommandsLv4() { //SuperGM
|
||||
function writeHeavenMSCommandsLv4() { //SuperGM
|
||||
comm_cursor = comm_lv4;
|
||||
desc_cursor = desc_lv4;
|
||||
|
||||
@@ -99,7 +99,7 @@ function writeSolaxiaCommandsLv4() { //SuperGM
|
||||
//addCommand("playernpc", "");
|
||||
}
|
||||
|
||||
function writeSolaxiaCommandsLv3() { //GM
|
||||
function writeHeavenMSCommandsLv3() { //GM
|
||||
comm_cursor = comm_lv3;
|
||||
desc_cursor = desc_lv3;
|
||||
|
||||
@@ -154,7 +154,7 @@ function writeSolaxiaCommandsLv3() { //GM
|
||||
addCommand("hair", "");
|
||||
}
|
||||
|
||||
function writeSolaxiaCommandsLv2() { //JrGM
|
||||
function writeHeavenMSCommandsLv2() { //JrGM
|
||||
comm_cursor = comm_lv2;
|
||||
desc_cursor = desc_lv2;
|
||||
|
||||
@@ -191,7 +191,7 @@ function writeSolaxiaCommandsLv2() { //JrGM
|
||||
addCommand("unbug", "");
|
||||
}
|
||||
|
||||
function writeSolaxiaCommandsLv1() { //Donator
|
||||
function writeHeavenMSCommandsLv1() { //Donator
|
||||
comm_cursor = comm_lv1;
|
||||
desc_cursor = desc_lv1;
|
||||
|
||||
@@ -204,7 +204,7 @@ function writeSolaxiaCommandsLv1() { //Donator
|
||||
addCommand("recharge", "");
|
||||
}
|
||||
|
||||
function writeSolaxiaCommandsLv0() { //Common
|
||||
function writeHeavenMSCommandsLv0() { //Common
|
||||
comm_cursor = comm_lv0;
|
||||
desc_cursor = desc_lv0;
|
||||
|
||||
@@ -231,19 +231,19 @@ function writeSolaxiaCommandsLv0() { //Common
|
||||
addCommand("luk", "");
|
||||
}
|
||||
|
||||
function writeSolaxiaCommands() {
|
||||
writeSolaxiaCommandsLv0(); //Common
|
||||
writeSolaxiaCommandsLv1(); //Donator
|
||||
writeSolaxiaCommandsLv2(); //JrGM
|
||||
writeSolaxiaCommandsLv3(); //GM
|
||||
writeSolaxiaCommandsLv4(); //Developer
|
||||
writeSolaxiaCommandsLv5(); //SuperGM
|
||||
writeSolaxiaCommandsLv6(); //Admin
|
||||
function writeHeavenMSCommands() {
|
||||
writeHeavenMSCommandsLv0(); //Common
|
||||
writeHeavenMSCommandsLv1(); //Donator
|
||||
writeHeavenMSCommandsLv2(); //JrGM
|
||||
writeHeavenMSCommandsLv3(); //GM
|
||||
writeHeavenMSCommandsLv4(); //Developer
|
||||
writeHeavenMSCommandsLv5(); //SuperGM
|
||||
writeHeavenMSCommandsLv6(); //Admin
|
||||
}
|
||||
|
||||
function start() {
|
||||
status = -1;
|
||||
writeSolaxiaCommands();
|
||||
writeHeavenMSCommands();
|
||||
action(1, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
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
|
||||
* Player NPC Ranking System */
|
||||
|
||||
importPackage(Packages.constants);
|
||||
|
||||
var status;
|
||||
|
||||
function start() {
|
||||
status = -1;
|
||||
action(1, 0, 0);
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
if (mode == -1) {
|
||||
cm.dispose();
|
||||
} else {
|
||||
if (mode == 0 && type > 0) {
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
if (mode == 1)
|
||||
status++;
|
||||
else
|
||||
status--;
|
||||
|
||||
if(status == 0) {
|
||||
var pnpc = cm.getPlayerNPCByScriptid(cm.getNpc());
|
||||
|
||||
if(pnpc != null) {
|
||||
var branchJobName = GameConstants.getJobName(pnpc.getJob());
|
||||
|
||||
var rankStr = "Hi, I am #b" + pnpc.getName() + "#k, #r" + GameConstants.ordinal(pnpc.getWorldJobRank()) + "#k in the #r" + branchJobName + "#k class to reach the max level and obtain a statue on " + GameConstants.WORLD_NAMES[cm.getPlayer().getWorld()] + ".\r\n";
|
||||
rankStr += "\r\n World rank: #e#b" + GameConstants.ordinal(pnpc.getWorldRank()) + "#k#n";
|
||||
rankStr += "\r\n Overall " + branchJobName + " rank: #e#b" + GameConstants.ordinal(pnpc.getOverallJobRank()) + "#k#n";
|
||||
rankStr += "\r\n Overall rank: #e#b" + GameConstants.ordinal(pnpc.getOverallRank()) + "#k#n";
|
||||
|
||||
cm.sendOk(rankStr);
|
||||
} else {
|
||||
cm.sendOk("Hi, how're you doing?");
|
||||
}
|
||||
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
function enter(pi) {
|
||||
try {
|
||||
var elevator = pi.getEventManager("elevator");
|
||||
var elevator = pi.getEventManager("Elevator");
|
||||
if (elevator == null) {
|
||||
pi.getPlayer().dropMessage(5, "The elevator is under maintenance.");
|
||||
} else if (elevator.getProperty(pi.getMapId() == 222020100 ? ("goingUp") : ("goingDown")).equals("false")) {
|
||||
|
||||
@@ -20,6 +20,6 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
function enter(pi){
|
||||
pi.playPortalSound(); pi.warp(920020000, 0);
|
||||
pi.playPortalSound(); pi.warp(920020000, 2);
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
function enter(pi) {
|
||||
if (pi.getPlayer().getMapId() != 777777777) {
|
||||
if(!Packages.constants.GameConstants.canEnterDeveloperRoom()) {
|
||||
pi.message("The next room is currently unavailable.");
|
||||
return false;
|
||||
}
|
||||
|
||||
pi.getPlayer().saveLocation("DEVELOPER");
|
||||
pi.playPortalSound(); pi.warp(777777777, "out00");
|
||||
} else {
|
||||
try {
|
||||
var toMap = pi.getPlayer().getSavedLocation("DEVELOPER");
|
||||
pi.playPortalSound(); pi.warp(toMap, "in00");
|
||||
} catch(err) {
|
||||
pi.playPortalSound(); pi.warp(100000000, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -21,7 +21,7 @@ function enter(pi) {
|
||||
pi.warp(100000204, 1); //or 100000205
|
||||
break;
|
||||
default:
|
||||
pi.warp(pi.getMapId() + 1, 0); //or + 2
|
||||
pi.warp(pi.getMapId() + 1, 1); //or + 2
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
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 end(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.getQuestProgress(3114, 7777) != -1) {
|
||||
if(!qm.haveItem(4161036, 1)) {
|
||||
if(qm.canHold(4161036, 1)) {
|
||||
qm.gainItem(4161036, 1);
|
||||
qm.sendNext("Seems you lost a book with the notes to Little Star. Here is another one. Please play it for me.", 9);
|
||||
} else {
|
||||
qm.sendNext("Seems you lost a book with the notes to Little Star, but you don't have an ETC available. Please free some room.", 9);
|
||||
}
|
||||
} else {
|
||||
qm.sendNext(".....", 9);
|
||||
}
|
||||
|
||||
qm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
qm.sendNext("(Eliza seems to be in deep sleep.)", 3);
|
||||
} else if (status == 1) {
|
||||
qm.gainFame(20);
|
||||
|
||||
qm.forceCompleteQuest();
|
||||
qm.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
-7
@@ -16477,27 +16477,40 @@ CREATE TABLE IF NOT EXISTS `playernpcs` (
|
||||
`hair` int(11) NOT NULL,
|
||||
`face` int(11) NOT NULL,
|
||||
`skin` int(11) NOT NULL,
|
||||
`gender` int(11) NOT NULL DEFAULT '0',
|
||||
`x` int(11) NOT NULL,
|
||||
`cy` int(11) NOT NULL DEFAULT '0',
|
||||
`map` int(11) NOT NULL,
|
||||
`gender` int(11) NOT NULL DEFAULT '0',
|
||||
`world` int(11) NOT NULL DEFAULT '0',
|
||||
`map` int(11) NOT NULL DEFAULT '0',
|
||||
`dir` int(11) NOT NULL DEFAULT '0',
|
||||
`ScriptId` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`Foothold` int(11) NOT NULL DEFAULT '0',
|
||||
`scriptid` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`fh` int(11) NOT NULL DEFAULT '0',
|
||||
`rx0` int(11) NOT NULL DEFAULT '0',
|
||||
`rx1` int(11) NOT NULL DEFAULT '0',
|
||||
`worldrank` int(11) NOT NULL DEFAULT '0',
|
||||
`overallrank` int(11) NOT NULL DEFAULT '0',
|
||||
`worldjobrank` int(11) NOT NULL DEFAULT '0',
|
||||
`job` int(11) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=2147000000 ;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `playernpcs_equip` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`NpcId` int(11) NOT NULL DEFAULT '0',
|
||||
`npcid` int(11) NOT NULL DEFAULT '0',
|
||||
`equipid` int(11) NOT NULL,
|
||||
`type` int(11) NOT NULL DEFAULT '0',
|
||||
`equippos` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `playernpcs_field` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`world` int(11) NOT NULL,
|
||||
`map` int(11) NOT NULL,
|
||||
`step` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `questactions` (
|
||||
`questactionid` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`questid` int(11) NOT NULL DEFAULT '0',
|
||||
@@ -17401,7 +17414,7 @@ CREATE TABLE IF NOT EXISTS `rings` (
|
||||
CREATE TABLE IF NOT EXISTS `savedlocations` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`characterid` int(11) NOT NULL,
|
||||
`locationtype` enum('FREE_MARKET','WORLDTOUR','FLORINA','INTRO','MIRROR','EVENT','BOSSPQ','HAPPYVILLE') NOT NULL,
|
||||
`locationtype` enum('FREE_MARKET','WORLDTOUR','FLORINA','INTRO','SUNDAY_MARKET','MIRROR','EVENT','BOSSPQ','HAPPYVILLE','DEVELOPER') NOT NULL,
|
||||
`map` int(11) NOT NULL,
|
||||
`portal` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
|
||||
@@ -94,7 +94,7 @@ import server.maps.MapleMiniGame;
|
||||
import server.maps.MaplePlayerShop;
|
||||
import server.maps.MaplePlayerShopItem;
|
||||
import server.maps.MapleSummon;
|
||||
import server.maps.PlayerNPCs;
|
||||
import server.life.MaplePlayerNPC;
|
||||
import server.maps.SavedLocation;
|
||||
import server.maps.SavedLocationType;
|
||||
import server.partyquest.MonsterCarnival;
|
||||
@@ -313,7 +313,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
private int banishMap = -1;
|
||||
private int banishSp = -1;
|
||||
private long banishTime = 0;
|
||||
|
||||
|
||||
private MapleCharacter() {
|
||||
useCS = false;
|
||||
|
||||
@@ -5111,6 +5111,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
maxmp = Math.min(30000, maxmp);
|
||||
if (level == 200) {
|
||||
exp.set(0);
|
||||
if(ServerConstants.PLAYERNPC_AUTODEPLOY) MaplePlayerNPC.spawnPlayerNPC(GameConstants.getHallOfFameMapid(job), this);
|
||||
}
|
||||
recalcLocalStats();
|
||||
hp = localmaxhp;
|
||||
@@ -5226,11 +5227,11 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
} else if (level == 80) {
|
||||
yellowMessage("You think you are powerful enough? Try facing horntail!");
|
||||
} else if (level == 85) {
|
||||
yellowMessage("Did you know? The majority of people who hit level 85 in Solaxia don't live to be 85 years old?");
|
||||
yellowMessage("Did you know? The majority of people who hit level 85 in HeavenMS don't live to be 85 years old?");
|
||||
} else if (level == 90) {
|
||||
yellowMessage("Hey do you like the amusement park? I heard Spooky World is the best theme park around. I heard they sell cute teddy-bears.");
|
||||
} else if (level == 95) {
|
||||
yellowMessage("100% of people who hit level 95 in Solaxia don't live to be 95 years old.");
|
||||
yellowMessage("100% of people who hit level 95 in HeavenMS don't live to be 95 years old.");
|
||||
} else if (level == 100) {
|
||||
yellowMessage("Mid-journey so far... You just reached level 100! Now THAT's such a feat, however to manage the 200 you will need even more passion and determination than ever! Good hunting!");
|
||||
} else if (level == 105) {
|
||||
@@ -5923,65 +5924,6 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
maplemount = new MapleMount(this, id, skillid);
|
||||
}
|
||||
|
||||
public void playerNPC(MapleCharacter v, int scriptId) {
|
||||
int npcId;
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT id FROM playernpcs WHERE ScriptId = ?");
|
||||
ps.setInt(1, scriptId);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
if (!rs.next()) {
|
||||
rs.close();
|
||||
ps = con.prepareStatement("INSERT INTO playernpcs (name, hair, face, skin, x, cy, map, ScriptId, Foothold, rx0, rx1) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
|
||||
ps.setString(1, v.getName());
|
||||
ps.setInt(2, v.getHair());
|
||||
ps.setInt(3, v.getFace());
|
||||
ps.setInt(4, v.getSkinColor().getId());
|
||||
ps.setInt(5, getPosition().x);
|
||||
ps.setInt(6, getPosition().y);
|
||||
ps.setInt(7, getMapId());
|
||||
ps.setInt(8, scriptId);
|
||||
ps.setInt(9, getMap().getFootholds().findBelow(getPosition()).getId());
|
||||
ps.setInt(10, getPosition().x + 50);
|
||||
ps.setInt(11, getPosition().x - 50);
|
||||
ps.executeUpdate();
|
||||
rs = ps.getGeneratedKeys();
|
||||
rs.next();
|
||||
npcId = rs.getInt(1);
|
||||
ps.close();
|
||||
ps = con.prepareStatement("INSERT INTO playernpcs_equip (NpcId, equipid, equippos) VALUES (?, ?, ?)");
|
||||
ps.setInt(1, npcId);
|
||||
for (Item equip : getInventory(MapleInventoryType.EQUIPPED)) {
|
||||
int position = Math.abs(equip.getPosition());
|
||||
if ((position < 12 && position > 0) || (position > 100 && position < 112)) {
|
||||
ps.setInt(2, equip.getItemId());
|
||||
ps.setInt(3, equip.getPosition());
|
||||
ps.addBatch();
|
||||
}
|
||||
}
|
||||
ps.executeBatch();
|
||||
ps.close();
|
||||
rs.close();
|
||||
ps = con.prepareStatement("SELECT * FROM playernpcs WHERE ScriptId = ?");
|
||||
ps.setInt(1, scriptId);
|
||||
rs = ps.executeQuery();
|
||||
rs.next();
|
||||
PlayerNPCs pn = new PlayerNPCs(rs);
|
||||
for (Channel channel : Server.getInstance().getChannelsFromWorld(world)) {
|
||||
MapleMap m = channel.getMapFactory().getMap(getMapId());
|
||||
m.broadcastMessage(MaplePacketCreator.spawnPlayerNPC(pn));
|
||||
m.broadcastMessage(MaplePacketCreator.getPlayerNPC(pn));
|
||||
m.addMapObject(pn);
|
||||
}
|
||||
}
|
||||
ps.close();
|
||||
rs.close();
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void playerDead() {
|
||||
cancelAllBuffs(false);
|
||||
dispelDebuffs();
|
||||
@@ -6840,7 +6782,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
}
|
||||
|
||||
public void sendPolice(int greason, String reason, int duration) {
|
||||
announce(MaplePacketCreator.sendPolice(String.format("You have been blocked by the#b %s Police for %s.#k", "Solaxia", reason)));
|
||||
announce(MaplePacketCreator.sendPolice(String.format("You have been blocked by the#b %s Police for %s.#k", "HeavenMS", reason)));
|
||||
this.isbanned = true;
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
@Override
|
||||
@@ -7966,7 +7908,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
|
||||
public void autoban(String reason) {
|
||||
this.ban(reason);
|
||||
announce(MaplePacketCreator.sendPolice(String.format("You have been blocked by the#b %s Police for HACK reason.#k", "Solaxia")));
|
||||
announce(MaplePacketCreator.sendPolice(String.format("You have been blocked by the#b %s Police for HACK reason.#k", "HeavenMS")));
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
@@ -112,7 +112,7 @@ public class MapleClient {
|
||||
private long lastNpcClick;
|
||||
private long sessionId;
|
||||
|
||||
public MapleClient(MapleAESOFB send, MapleAESOFB receive, IoSession session) {
|
||||
public MapleClient(MapleAESOFB send, MapleAESOFB receive, IoSession session) {
|
||||
this.send = send;
|
||||
this.receive = receive;
|
||||
this.session = session;
|
||||
@@ -914,10 +914,9 @@ public class MapleClient {
|
||||
} catch (final Exception e) {
|
||||
FilePrinter.printError(FilePrinter.ACCOUNT_STUCK, e);
|
||||
} finally {
|
||||
getChannelServer().removePlayer(player);
|
||||
|
||||
if (!this.serverTransition) {
|
||||
worlda.removePlayer(player);
|
||||
//getChannelServer().removePlayer(player); already being done
|
||||
|
||||
player.saveCooldowns();
|
||||
player.saveToDB(true);
|
||||
@@ -925,10 +924,11 @@ public class MapleClient {
|
||||
player.empty(false);
|
||||
}
|
||||
player.logOff();
|
||||
}
|
||||
else {
|
||||
player.saveCooldowns();
|
||||
player.saveToDB();
|
||||
} else {
|
||||
getChannelServer().removePlayer(player);
|
||||
|
||||
player.saveCooldowns();
|
||||
player.saveToDB();
|
||||
}
|
||||
player = null;
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ public enum MapleJob {
|
||||
|
||||
final int jobid;
|
||||
final static int maxId = 22; // maxId = (EVAN / 100);
|
||||
|
||||
|
||||
private MapleJob(int id) {
|
||||
jobid = id;
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ public class MapleQuestStatus {
|
||||
}
|
||||
|
||||
public void setProgress(int id, String pr) {
|
||||
progress.put(id, pr);
|
||||
progress.put(id, pr);
|
||||
}
|
||||
|
||||
public boolean madeProgress() {
|
||||
|
||||
@@ -68,6 +68,7 @@ import server.life.MapleLifeFactory;
|
||||
import server.life.MapleMonster;
|
||||
import server.life.MapleMonsterInformationProvider;
|
||||
import server.life.MapleNPC;
|
||||
import server.life.MaplePlayerNPC;
|
||||
import server.life.MobSkill;
|
||||
import server.life.MobSkillFactory;
|
||||
import server.life.MonsterDropEntry;
|
||||
@@ -350,6 +351,22 @@ public class Commands {
|
||||
MapleCharacter player = c.getPlayer();
|
||||
|
||||
switch(sub[0]) {
|
||||
case "wow":
|
||||
MaplePlayerNPC.multicastSpawnPlayerNPC(player.getMapId(), player.getWorld());
|
||||
break;
|
||||
|
||||
case "out":
|
||||
MaplePlayerNPC.removeAllPlayerNPC();
|
||||
break;
|
||||
|
||||
case "er":
|
||||
MaplePlayerNPC.spawnPlayerNPC(player.getMapId(), player);
|
||||
break;
|
||||
|
||||
case "re":
|
||||
MaplePlayerNPC.removePlayerNPC(player);
|
||||
break;
|
||||
|
||||
case "help":
|
||||
case "commands":
|
||||
case "playercommands":
|
||||
@@ -369,7 +386,7 @@ public class Commands {
|
||||
case "time":
|
||||
DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
|
||||
dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE));
|
||||
player.yellowMessage("Solaxia Server Time: " + dateFormat.format(new Date()));
|
||||
player.yellowMessage("HeavenMS Server Time: " + dateFormat.format(new Date()));
|
||||
break;
|
||||
|
||||
case "credits":
|
||||
@@ -2069,10 +2086,6 @@ public class Commands {
|
||||
}
|
||||
break;
|
||||
|
||||
case "energy":
|
||||
System.out.println(c.getPlayer().getDojoEnergy());
|
||||
break;
|
||||
|
||||
case "maxenergy":
|
||||
c.getPlayer().setDojoEnergy(10000);
|
||||
c.announce(MaplePacketCreator.getEnergy("energy", 10000));
|
||||
@@ -2599,16 +2612,15 @@ public class Commands {
|
||||
|
||||
player.getMap().spawnMonsterOnGroundBelow(monster, player.getPosition());
|
||||
break;
|
||||
|
||||
/*
|
||||
|
||||
case "playernpc":
|
||||
if (sub.length < 3){
|
||||
player.yellowMessage("Syntax: !playernpc <playername> <npcid>");
|
||||
player.yellowMessage("Syntax: !playernpc <playername>");
|
||||
break;
|
||||
}
|
||||
player.playerNPC(c.getChannelServer().getPlayerStorage().getCharacterByName(sub[1]), Integer.parseInt(sub[2]));
|
||||
|
||||
MaplePlayerNPC.spawnPlayerNPC(player.getMapId(), player.getPosition(), c.getChannelServer().getPlayerStorage().getCharacterByName(sub[1]));
|
||||
break;
|
||||
*/
|
||||
|
||||
default:
|
||||
return false;
|
||||
@@ -2930,7 +2942,7 @@ public class Commands {
|
||||
else return true;
|
||||
}
|
||||
|
||||
public static boolean executeSolaxiaPlayerCommand(MapleClient c, String[] sub, char heading) {
|
||||
public static boolean executeHeavenMSPlayerCommand(MapleClient c, String[] sub, char heading) {
|
||||
Channel cserv = c.getChannelServer();
|
||||
Server srv = Server.getInstance();
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import server.MapleItemInformationProvider;
|
||||
|
||||
public class Item implements Comparable<Item> {
|
||||
|
||||
@@ -153,7 +154,7 @@ public class Item implements Comparable<Item> {
|
||||
}
|
||||
|
||||
public void setExpiration(long expire) {
|
||||
this.expiration = expire;
|
||||
this.expiration = !ItemConstants.isPermanentItem(id) ? expire : ItemConstants.isPet(id) ? Long.MAX_VALUE : -1;
|
||||
}
|
||||
|
||||
public int getSN() {
|
||||
|
||||
@@ -550,4 +550,12 @@ public class MapleInventory implements Iterable<Item> {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void lockInventory() {
|
||||
lock.lock();
|
||||
}
|
||||
|
||||
public void unlockInventory() {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package constants;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import client.MapleJob;
|
||||
import constants.skills.Aran;
|
||||
import server.maps.MapleMap;
|
||||
@@ -11,6 +13,9 @@ import server.quest.MapleQuest;
|
||||
* @author Ronan
|
||||
*/
|
||||
public class GameConstants {
|
||||
public static String[] WORLD_NAMES = {"Scania", "Bera", "Broa", "Windia", "Khaini", "Bellocan", "Mardia", "Kradia", "Yellonde", "Demethos", "Galicia", "El Nido", "Zenith", "Arcenia", "Kastia", "Judis", "Plana", "Kalluna", "Stius", "Croa", "Medere"};
|
||||
|
||||
final static Map<Integer, String> jobNames = new HashMap<>();
|
||||
public static final int[] OWL_DATA = new int[]{1082002, 2070005, 2070006, 1022047, 1102041, 2044705, 2340000, 2040017, 1092030, 2040804};
|
||||
|
||||
// Ronan's rates upgrade system
|
||||
@@ -18,6 +23,16 @@ public class GameConstants {
|
||||
private static final int[] MESO_RATE_GAIN = {1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78, 91, 105};
|
||||
private static final int[] EXP_RATE_GAIN = {1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610}; //fibonacci :3
|
||||
|
||||
public static boolean availableDeveloperRoom = false;
|
||||
|
||||
public static void setAvailableDeveloperRoom() {
|
||||
availableDeveloperRoom = true;
|
||||
}
|
||||
|
||||
public static boolean canEnterDeveloperRoom() {
|
||||
return availableDeveloperRoom;
|
||||
}
|
||||
|
||||
public static int getPlayerBonusDropRate(int slot) {
|
||||
return(DROP_RATE_GAIN[slot]);
|
||||
}
|
||||
@@ -63,6 +78,130 @@ public class GameConstants {
|
||||
330000, 340000, 350000, 360000, 370000, 380000, 390000, 400000, 410000, 420000, 430000, 440000, 450000, 460000, 470000, 480000, 490000, 500000, 510000, 520000,
|
||||
530000, 550000, 570000, 590000, 610000, 630000, 650000, 670000, 690000, 710000, 730000, 750000, 770000, 790000, 810000, 830000, 850000, 870000, 890000, 910000};
|
||||
|
||||
public static String getJobName(int jobid) {
|
||||
String name = jobNames.get(jobid);
|
||||
|
||||
if(name == null) {
|
||||
MapleJob job = MapleJob.getById(jobid);
|
||||
|
||||
if(job != null) {
|
||||
name = job.name().toLowerCase();
|
||||
name = name.replaceAll("[*0-9]", "");
|
||||
name = name.substring(0, 1).toUpperCase() + name.substring(1);
|
||||
} else {
|
||||
name = "";
|
||||
}
|
||||
|
||||
jobNames.put(jobid, name);
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
public static boolean isHallOfFameMap(int mapid) {
|
||||
switch(mapid) {
|
||||
case 102000004: // warrior
|
||||
case 101000004: // magician
|
||||
case 100000204: // bowman
|
||||
case 103000008: // thief
|
||||
case 120000105: // pirate
|
||||
case 130000100: // cygnus
|
||||
case 130000101: // other cygnus
|
||||
case 130000110: // cygnus 2nd floor
|
||||
case 130000120: // cygnus 3rd floor (beginners)
|
||||
case 140010110: // aran
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static byte getHallOfFameBranch(MapleJob job, int mapid) {
|
||||
if(!isHallOfFameMap(mapid)) {
|
||||
return (byte) (26 + 4 * (mapid / 100000000)); // custom, 400 pnpcs available per continent
|
||||
}
|
||||
|
||||
if(job.isA(MapleJob.WARRIOR)) {
|
||||
return 10;
|
||||
} else if(job.isA(MapleJob.MAGICIAN)) {
|
||||
return 11;
|
||||
} else if(job.isA(MapleJob.BOWMAN)) {
|
||||
return 12;
|
||||
} else if(job.isA(MapleJob.THIEF)) {
|
||||
return 13;
|
||||
} else if(job.isA(MapleJob.PIRATE)) {
|
||||
return 14;
|
||||
} else if(job.isA(MapleJob.DAWNWARRIOR1)) {
|
||||
return 15;
|
||||
} else if(job.isA(MapleJob.BLAZEWIZARD1)) {
|
||||
return 16;
|
||||
} else if(job.isA(MapleJob.WINDARCHER1)) {
|
||||
return 17;
|
||||
} else if(job.isA(MapleJob.NIGHTWALKER1)) {
|
||||
return 18;
|
||||
} else if(job.isA(MapleJob.THUNDERBREAKER1)) {
|
||||
return 19;
|
||||
} else if(job.isA(MapleJob.ARAN1)) {
|
||||
return 20;
|
||||
} else if(job.isA(MapleJob.EVAN1)) {
|
||||
return 21;
|
||||
} else if(job.isA(MapleJob.BEGINNER)) {
|
||||
return 22;
|
||||
} else if(job.isA(MapleJob.NOBLESSE)) {
|
||||
return 23;
|
||||
} else if(job.isA(MapleJob.LEGEND)) {
|
||||
return 24;
|
||||
} else {
|
||||
return 25;
|
||||
}
|
||||
}
|
||||
|
||||
public static int getOverallJobRankByScriptId(int scriptId) {
|
||||
int branch = (scriptId / 100) % 100;
|
||||
|
||||
if(branch < 26) {
|
||||
return (scriptId % 100) + 1;
|
||||
} else {
|
||||
return ((scriptId - 2600) % 400) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean canPnpcBranchUseScriptId(byte branch, int scriptId) {
|
||||
scriptId /= 100;
|
||||
scriptId %= 100;
|
||||
|
||||
if(branch < 26) {
|
||||
return branch == scriptId;
|
||||
} else {
|
||||
return scriptId >= branch && scriptId < branch + 4;
|
||||
}
|
||||
}
|
||||
|
||||
public static int getHallOfFameMapid(MapleJob job) {
|
||||
int jobid = job.getId();
|
||||
|
||||
if(isCygnus(jobid)) {
|
||||
return 130000100;
|
||||
} else if(isAran(jobid)) {
|
||||
return 140010110;
|
||||
} else {
|
||||
if(job.isA(MapleJob.WARRIOR)) {
|
||||
return 102000004;
|
||||
} else if(job.isA(MapleJob.MAGICIAN)) {
|
||||
return 101000004;
|
||||
} else if(job.isA(MapleJob.BOWMAN)) {
|
||||
return 100000204;
|
||||
} else if(job.isA(MapleJob.THIEF)) {
|
||||
return 103000008;
|
||||
} else if(job.isA(MapleJob.PIRATE)) {
|
||||
return 120000105;
|
||||
} else {
|
||||
return 130000110; // beginner explorers are allotted with the Cygnus, available map lul
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int getJobBranch(MapleJob job) {
|
||||
int jobid = job.getId();
|
||||
|
||||
@@ -125,6 +264,10 @@ public class GameConstants {
|
||||
return Aran.HIDDEN_FULL_DOUBLE == skill || Aran.HIDDEN_FULL_TRIPLE == skill || Aran.HIDDEN_OVER_DOUBLE == skill || Aran.HIDDEN_OVER_TRIPLE == skill;
|
||||
}
|
||||
|
||||
public static boolean isCygnus(final int job) {
|
||||
return job / 1000 == 1;
|
||||
}
|
||||
|
||||
public static boolean isAran(final int job) {
|
||||
return job == 2000 || (job >= 2100 && job <= 2112);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
package constants;
|
||||
|
||||
import client.inventory.MapleInventoryType;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -42,7 +44,15 @@ public final class ItemConstants {
|
||||
public final static int ACCOUNT_SHARING = 0x100;
|
||||
|
||||
public final static boolean EXPIRING_ITEMS = true;
|
||||
public final static Set<Integer> permanentItemids = new HashSet<>();
|
||||
|
||||
static {
|
||||
int[] pi = {5000060, 5000100, 5000101, 5000102}; // i ain't going to open one gigantic itemid cache just for 4 perma itemids, no way!
|
||||
for(int i : pi) {
|
||||
permanentItemids.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
public static int getFlagByInt(int type) {
|
||||
if (type == 128) {
|
||||
return PET_COME;
|
||||
@@ -97,6 +107,10 @@ public final class ItemConstants {
|
||||
return ServerConstants.USE_ERASE_PET_ON_EXPIRATION || itemId == 5000054;
|
||||
}
|
||||
|
||||
public static boolean isPermanentItem(int itemId) {
|
||||
return permanentItemids.contains(itemId);
|
||||
}
|
||||
|
||||
public static boolean isNewYearCardEtc(int itemId) {
|
||||
return itemId / 10000 == 430;
|
||||
}
|
||||
|
||||
@@ -13,9 +13,8 @@ public class ServerConstants {
|
||||
public static String DB_PASS = "";
|
||||
public static final boolean DB_CONNECTION_POOL = true; //Installs a connection pool to hub DB connections. Set false to default.
|
||||
|
||||
//World And Version
|
||||
//Server Version
|
||||
public static short VERSION = 83;
|
||||
public static String[] WORLD_NAMES = {"Scania", "Bera", "Broa", "Windia", "Khaini", "Bellocan", "Mardia", "Kradia", "Yellonde", "Demethos", "Galicia", "El Nido", "Zenith", "Arcenia", "Kastia", "Judis", "Plana", "Kalluna", "Stius", "Croa", "Medere"};
|
||||
|
||||
//Login Configuration
|
||||
public static final int CHANNEL_LOAD = 100; //Max players per channel (limit actually used to calculate the World server capacity).
|
||||
@@ -93,17 +92,19 @@ public class ServerConstants {
|
||||
public static final boolean BLOCK_GENERATE_CASH_ITEM = false; //Prevents creation of cash items with the item/drop command.
|
||||
|
||||
//Miscellaneous Configuration
|
||||
public static final byte MIN_UNDERLEVEL_TO_EXP_GAIN = 5; //Characters are unable to get EXP from a mob if their level are under this threshold, only if "USE_ENFORCE_MOB_LEVEL_RANGE" is enabled. For bosses, this attribute is doubled.
|
||||
public static final byte MAX_MONITORED_BUFFSTATS = 5; //Limits accounting for "dormant" buff effects, that should take place when stronger stat buffs expires.
|
||||
public static String TIMEZONE = "-GMT3";
|
||||
public static boolean USE_UNITPRICE_WITH_COMMA = true; //Set this accordingly with the layout of the unitPrices on Item.wz XML's, whether it's using commas or dots to represent fractions.
|
||||
public static final byte MIN_UNDERLEVEL_TO_EXP_GAIN = 5; //Characters are unable to get EXP from a mob if their level are under this threshold, only if "USE_ENFORCE_MOB_LEVEL_RANGE" is enabled. For bosses, this attribute is doubled.
|
||||
public static final byte MAX_MONITORED_BUFFSTATS = 5; //Limits accounting for "dormant" buff effects, that should take place when stronger stat buffs expires.
|
||||
public static final int MAX_AP = 32767; //Max AP allotted on the auto-assigner.
|
||||
public static final int MAX_EVENT_LEVELS = 8; //Event has different levels of rewarding system.
|
||||
public static final long BLOCK_NPC_RACE_CONDT = (long)(0.5 * 1000); //Time the player client must wait before reopening a conversation with an NPC.
|
||||
public static final long PET_LOOT_UPON_ATTACK = (long)(0.7 * 1000); //Time the pet must wait before trying to pick items up.
|
||||
public static boolean USE_UNITPRICE_WITH_COMMA = true; //Set this accordingly with the layout of the unitPrices on Item.wz XML's, whether it's using commas or dots to represent fractions.
|
||||
public static String TIMEZONE = "-GMT3";
|
||||
public static final int TOT_MOB_QUEST_REQUIREMENT = 0; //Overwrites old 999-mobs requirement for the ToT questline with new requirement value, set 0 for default.
|
||||
|
||||
//Dangling Items/Locks Configuration
|
||||
public static final int ITEM_EXPIRE_TIME = 3 * 60 * 1000; //Time before items start disappearing. Recommended to be set up to 3 minutes.
|
||||
public static final int KITE_EXPIRE_TIME = 60 * 60 * 1000; //Time before new year (cash item) kite disappears.
|
||||
public static final int ITEM_MONITOR_TIME = 5 * 60 * 1000; //Interval between item monitoring tasks on maps, which checks for dangling (null) item objects on the map item history.
|
||||
public static final int LOCK_MONITOR_TIME = 30 * 1000; //Waiting time for a lock to be released. If it reach timed out, a critical server deadlock has made present.
|
||||
public static final int ITEM_EXPIRE_CHECK = 10 * 1000; //Interval between item expiring tasks on maps, which checks and makes disappear expired items.
|
||||
@@ -145,6 +146,15 @@ public class ServerConstants {
|
||||
public static final byte CHAIR_EXTRA_HEAL_HP = 70; //Each chair extra heal proc increasing HP.
|
||||
public static final byte CHAIR_EXTRA_HEAL_MP = 42; //Each chair extra heal proc increasing MP.
|
||||
|
||||
//Player NPC Configuration
|
||||
public static final int PLAYERNPC_INITIAL_X = 262; //Map frame width for putting PlayerNPCs.
|
||||
public static final int PLAYERNPC_INITIAL_Y = 262; //Map frame height for putting PlayerNPCs.
|
||||
public static final int PLAYERNPC_AREA_X = 320; //Initial width gap between PlayerNPCs.
|
||||
public static final int PLAYERNPC_AREA_Y = 160; //Initial height gap between PlayerNPCs.
|
||||
public static final int PLAYERNPC_AREA_STEPS = 4; //Max number of times gap is shortened to comport PlayerNPCs
|
||||
public static final boolean PLAYERNPC_ORGANIZE_AREA = true; //Automatically rearranges PlayerNPCs on the map if there is no space set the new NPC. Current distance gap between NPCs is decreased to solve this issue.
|
||||
public static final boolean PLAYERNPC_AUTODEPLOY = true; //Makes PlayerNPC automatically deployed on the Hall of Fame at the instant one reaches max level. If false, eligible players must talk to 1st job instructor to deploy a NPC.
|
||||
|
||||
//Pet Auto-Pot Configuration
|
||||
public static final boolean USE_EQUIPS_ON_AUTOPOT = true; //Player MaxHP and MaxMP check values on autopot handler will be updated by the HP/MP bonuses on equipped items.
|
||||
public static final double PET_AUTOHP_RATIO = 0.99; //Will automatically consume potions until given ratio of the MaxHP/MaxMP is reached.
|
||||
|
||||
@@ -227,6 +227,7 @@ public final class PacketProcessor {
|
||||
registerHandler(RecvOpcode.USE_SOLOMON_ITEM, new UseSolomonHandler());
|
||||
registerHandler(RecvOpcode.USE_GACHA_EXP, new UseGachaExpHandler());
|
||||
registerHandler(RecvOpcode.NEW_YEAR_CARD_REQUEST, new NewYearCardHandler());
|
||||
registerHandler(RecvOpcode.CASHSHOP_SURPRISE, new CashShopSurpriseHandler());
|
||||
registerHandler(RecvOpcode.USE_ITEM_REWARD, new ItemRewardHandler());
|
||||
registerHandler(RecvOpcode.USE_REMOTE, new RemoteGachaponHandler());
|
||||
registerHandler(RecvOpcode.ACCEPT_FAMILY, new AcceptFamilyHandler());
|
||||
|
||||
@@ -147,6 +147,7 @@ public enum RecvOpcode {
|
||||
USE_SOLOMON_ITEM(0x9D),
|
||||
USE_GACHA_EXP(0x9E),
|
||||
NEW_YEAR_CARD_REQUEST(0x9F),
|
||||
CASHSHOP_SURPRISE(0xA1),
|
||||
CLICK_GUIDE(0xA2),
|
||||
ARAN_COMBO_COUNTER(0xA3),
|
||||
MOVE_PET(0xA7),
|
||||
|
||||
+12
-2
@@ -268,8 +268,9 @@ public enum SendOpcode {
|
||||
UPDATE_HIRED_MERCHANT(0x10B),
|
||||
DROP_ITEM_FROM_MAPOBJECT(0x10C),
|
||||
REMOVE_ITEM_FROM_MAP(0x10D),
|
||||
KITE_MESSAGE(0x10E),
|
||||
KITE(0x10F),
|
||||
CANNOT_SPAWN_KITE(0x10E),
|
||||
SPAWN_KITE(0x10F),
|
||||
REMOVE_KITE(0x110),
|
||||
SPAWN_MIST(0x111),
|
||||
REMOVE_MIST(0x112),
|
||||
SPAWN_DOOR(0x113),
|
||||
@@ -326,6 +327,15 @@ public enum SendOpcode {
|
||||
QUERY_CASH_RESULT(0x144),
|
||||
CASHSHOP_OPERATION(0x145),
|
||||
|
||||
//check whether the character's name is ok.(0x146),
|
||||
//this name can be used, press ok if you wish.(0x147),
|
||||
//rule for character name change (needed lots of bytes)(0x148),
|
||||
//rule for character transfer(0x14A),
|
||||
//cannot receive gacha stamps(0x14B),
|
||||
CASHSHOP_GACHAPON_STAMP_RESULT(0x14C),
|
||||
CASHSHOP_CASH_ITEM_GACHAPON_RESULT(0x14D),
|
||||
CASHSHOP_CASH_GACHAPON_OPEN_RESULT(0x14E),
|
||||
|
||||
KEYMAP(0x14F),
|
||||
AUTO_HP_POT(0x150),
|
||||
AUTO_MP_POT(0x151),
|
||||
|
||||
+79
-12
@@ -27,11 +27,13 @@ import net.server.worker.RankingWorker;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.Security;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.HashMap;
|
||||
@@ -43,7 +45,6 @@ import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import tools.locks.MonitoredReentrantLock;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import net.MapleServerHandler;
|
||||
import net.mina.MapleCodecFactory;
|
||||
import net.server.channel.Channel;
|
||||
@@ -60,23 +61,23 @@ import org.apache.mina.core.session.IdleStatus;
|
||||
import org.apache.mina.filter.codec.ProtocolCodecFilter;
|
||||
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
|
||||
|
||||
import server.CashShop.CashItemFactory;
|
||||
import server.TimerManager;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.FilePrinter;
|
||||
import tools.Pair;
|
||||
import client.MapleClient;
|
||||
import client.MapleCharacter;
|
||||
import client.SkillFactory;
|
||||
import client.newyear.NewYearCardRecord;
|
||||
import constants.ItemConstants;
|
||||
import constants.GameConstants;
|
||||
import constants.ServerConstants;
|
||||
import java.security.Security;
|
||||
import java.util.Calendar;
|
||||
import net.server.audit.ThreadTracker;
|
||||
import server.CashShop.CashItemFactory;
|
||||
import server.TimerManager;
|
||||
import server.life.MaplePlayerNPCFactory;
|
||||
import server.quest.MapleQuest;
|
||||
import tools.locks.MonitoredLockType;
|
||||
import tools.AutoJCE;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.FilePrinter;
|
||||
import tools.Pair;
|
||||
import tools.locks.MonitoredLockType;
|
||||
|
||||
public class Server {
|
||||
private static final Set<Integer> activeFly = new HashSet<>();
|
||||
@@ -89,6 +90,7 @@ public class Server {
|
||||
private final Properties subnetInfo = new Properties();
|
||||
private static Server instance = null;
|
||||
private final Map<Integer, Set<Integer>> accountChars = new HashMap<>();
|
||||
private final Map<Integer, Integer> worldChars = new HashMap<>();
|
||||
private final Map<String, Integer> transitioningChars = new HashMap<>();
|
||||
private List<Pair<Integer, String>> worldRecommendedList = new LinkedList<>();
|
||||
private final Map<Integer, MapleGuild> guilds = new HashMap<>(100);
|
||||
@@ -132,6 +134,25 @@ public class Server {
|
||||
return newyears.remove(cardid);
|
||||
}
|
||||
|
||||
private void loadPlayerNpcMapStepFromDb() {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT * FROM playernpcs_field");
|
||||
|
||||
ResultSet rs = ps.executeQuery();
|
||||
while(rs.next()) {
|
||||
int world = rs.getInt("world"), map = rs.getInt("map"), step = rs.getInt("step");
|
||||
worlds.get(world).setPlayerNpcMapStep(map, step, true);
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public void removeChannel(int worldid, int channel) { //lol don't!
|
||||
channels.remove(channel);
|
||||
@@ -365,7 +386,7 @@ public class Server {
|
||||
if(ServerConstants.USE_THREAD_TRACKER) ThreadTracker.getInstance().registerThreadTrackerTask();
|
||||
|
||||
try {
|
||||
Integer worldCount = Math.min(ServerConstants.WORLD_NAMES.length, Integer.parseInt(p.getProperty("worlds")));
|
||||
Integer worldCount = Math.min(GameConstants.WORLD_NAMES.length, Integer.parseInt(p.getProperty("worlds")));
|
||||
|
||||
for (int i = 0; i < worldCount; i++) {
|
||||
System.out.println("Starting world " + i);
|
||||
@@ -389,6 +410,9 @@ public class Server {
|
||||
world.setServerMessage(p.getProperty("servermessage" + i));
|
||||
System.out.println("Finished loading world " + i + "\r\n");
|
||||
}
|
||||
|
||||
MaplePlayerNPCFactory.loadFactoryMetadata();
|
||||
loadPlayerNpcMapStepFromDb();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();//For those who get errors
|
||||
System.out.println("Error in moople.ini, start CreateINI.bat to re-make the file.");
|
||||
@@ -579,7 +603,7 @@ public class Server {
|
||||
|
||||
if(mc != null) {
|
||||
mc.setMGC(g.getMGC(mc.getId()));
|
||||
if(g.getMGC(mc.getId()) == null) System.out.println("null for " + mc.getName() + " when loading " + id);
|
||||
if(g.getMGC(mc.getId()) == null) System.out.println("null for " + mc.getName() + " when loading guild " + id);
|
||||
g.getMGC(mc.getId()).setCharacter(mc);
|
||||
g.setOnline(mc.getId(), true, mc.getClient().getChannel());
|
||||
}
|
||||
@@ -824,7 +848,7 @@ public class Server {
|
||||
}
|
||||
}
|
||||
|
||||
public void createCharacterid(Integer accountid, Integer chrid) {
|
||||
public void createCharacterid(Integer accountid, Integer chrid, Integer world) {
|
||||
lgnLock.lock();
|
||||
try {
|
||||
Set<Integer> accChars = accountChars.get(accountid);
|
||||
@@ -834,6 +858,7 @@ public class Server {
|
||||
}
|
||||
|
||||
accChars.add(chrid);
|
||||
worldChars.put(chrid, world);
|
||||
} finally {
|
||||
lgnLock.unlock();
|
||||
}
|
||||
@@ -846,6 +871,48 @@ public class Server {
|
||||
if(accChars != null) {
|
||||
accChars.remove(chrid);
|
||||
}
|
||||
|
||||
worldChars.remove(chrid);
|
||||
} finally {
|
||||
lgnLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private static int getCharacterWorldFromDB(int chrid) {
|
||||
int world = -1;
|
||||
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
|
||||
try (PreparedStatement ps = con.prepareStatement("SELECT world FROM characters WHERE id = ?")) {
|
||||
ps.setInt(1, chrid);
|
||||
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
if(rs.next()) {
|
||||
world = rs.getInt("world");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
con.close();
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
}
|
||||
|
||||
return world;
|
||||
}
|
||||
|
||||
public int getCharacterWorld(Integer chrid) {
|
||||
lgnLock.lock();
|
||||
try {
|
||||
Integer worldid = worldChars.get(chrid);
|
||||
|
||||
if(worldid == null) {
|
||||
worldid = getCharacterWorldFromDB(chrid);
|
||||
worldChars.put(chrid, worldid);
|
||||
}
|
||||
|
||||
return worldid;
|
||||
} finally {
|
||||
lgnLock.unlock();
|
||||
}
|
||||
|
||||
@@ -191,8 +191,8 @@ public final class Channel {
|
||||
return players;
|
||||
}
|
||||
|
||||
public void removePlayer(MapleCharacter chr) {
|
||||
players.removePlayer(chr.getId());
|
||||
public boolean removePlayer(MapleCharacter chr) {
|
||||
return players.removePlayer(chr.getId()) != null;
|
||||
}
|
||||
|
||||
public int getChannelCapacity() {
|
||||
|
||||
@@ -445,29 +445,37 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
|
||||
AutobanFactory.FIX_DAMAGE.autoban(player, String.valueOf(totDamageToOneMonster) + " damage");
|
||||
}
|
||||
|
||||
if(ServerConstants.USE_ULTRA_THREE_SNAILS) {
|
||||
AbstractPlayerInteraction api = player.getClient().getAbstractPlayerInteraction();
|
||||
|
||||
int shellId;
|
||||
switch(totDamageToOneMonster) {
|
||||
case 10:
|
||||
shellId = 4000019;
|
||||
break;
|
||||
|
||||
case 25:
|
||||
shellId = 4000000;
|
||||
break;
|
||||
|
||||
default:
|
||||
shellId = 4000016;
|
||||
}
|
||||
|
||||
if(api.haveItem(shellId, 1)) {
|
||||
api.gainItem(shellId, (short)-1, false);
|
||||
totDamageToOneMonster *= player.getLevel();
|
||||
}
|
||||
else {
|
||||
player.dropMessage(5, "You ran out of shells to activate the hidden power of Three Snails.");
|
||||
int threeSnailsId = player.getJobType() * 10000000 + 1000;
|
||||
if(attack.skill == threeSnailsId) {
|
||||
if(ServerConstants.USE_ULTRA_THREE_SNAILS) {
|
||||
int skillLv = player.getSkillLevel(threeSnailsId);
|
||||
|
||||
if(skillLv > 0) {
|
||||
AbstractPlayerInteraction api = player.getClient().getAbstractPlayerInteraction();
|
||||
|
||||
int shellId;
|
||||
switch(skillLv) {
|
||||
case 1:
|
||||
shellId = 4000019;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
shellId = 4000000;
|
||||
break;
|
||||
|
||||
default:
|
||||
shellId = 4000016;
|
||||
}
|
||||
|
||||
if(api.haveItem(shellId, 1)) {
|
||||
api.gainItem(shellId, (short) -1, false);
|
||||
totDamageToOneMonster *= player.getLevel();
|
||||
} else {
|
||||
player.dropMessage(5, "You have ran out of shells to activate the hidden power of Three Snails.");
|
||||
}
|
||||
} else {
|
||||
totDamageToOneMonster = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
if (action == 0x03) { // Item
|
||||
Item item = cItem.toItem();
|
||||
cs.addToInventory(item);
|
||||
cs.addToInventory(item);
|
||||
c.announce(MaplePacketCreator.showBoughtCashItem(item, c.getAccID()));
|
||||
} else { // Package
|
||||
List<Item> cashPackage = CashItemFactory.getPackage(cItem.getItemId());
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user