Updated Meso & Arrow drops + Aran change jobs fix + improved concurrency

Added meso drop data for many mobs that were missing mesos.
Enhanced arrow drop data, now dropping bundles instead of unitary items.
Fixed issues with several Aran change jobs crashing the player shortly after changing jobs.
Improved concurrency in MapleGuild, MapleAlliance and MaplePlayerShop.
New tools: MapleArrowFetcher and MapleMesoFetcher, that were used to compile the updated drop data info.
This commit is contained in:
ronancpl
2017-11-01 13:34:26 -02:00
parent 44949aea37
commit 64af2cfa00
192 changed files with 44793 additions and 271 deletions

View File

@@ -45,6 +45,18 @@ Cash & Items:
* New scroll: antibanish. For use only in cases where bosses send a player back to town.
* Inventory system properly checks for item slot free space and ownership.
* Storage with "Arrange Items" feature functional.
* Vega's spell.
* Owl of Minerva.
* Pet item ignore.
Monsters & Maps:
* Every monsterbook card is now droppable by overworld mobs.
* Added meso drop data for basically every missing overworld mob.
* Monsterbook displays drop data info conformant with the underlying DB (needs custom wz). See more on the MobBookUpdate feature.
* Every skill/mastery book is now droppable by mobs.
* 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.
PQ potentials:
* Lobby system - Multiple PQ instances on same channel.
@@ -52,36 +64,28 @@ PQ potentials:
* Guild queue system - Guilds can register themselves on a queue for the GPQ.
* EIM Pool system - After the first instance setup, next event instances are loaded beforehand and set on a pooling queue, optimizing future loadouts.
Server potentials:
* Multi-worlds 100%.
Player potentials:
* Adventurer Mount quests 100%.
* All Equipment levels up.
* Player level rates.
* Gain fame by quests.
* Every monsterbook card is now droppable by overworld mobs.
* Monsterbook displays drop data info conformant with the underlying DB (needs custom wz). See more on the MobBookUpdate feature.
* Mastery book announcer displays droppers of needed books of a player, by reading underlying DB.
* Every skill/mastery book is now droppable by mobs.
Server potentials:
* Multi-worlds 100%.
* Inventory auto-gather and auto-sorting feature.
* 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.
* 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.
* Mastery book announcer displays droppers of needed books of a player, by reading underlying DB.
* Custom jail system (needs provided custom wz).
* Delete Character 100% (requires ENABLE_PIC activated).
* Boats, elevator and other travelling mechanics fully working.
* Enabled Hired Merchant being able to be used anywhere but FM Entrance and other few places.
* Vega's spell.
* Owl of Minerva.
* Pet item ignore.
* Autosaver (periodically saves on DB current state of every player in-game).
* Fixed and randomized versions of HP/MP growth rate, regarding player job. Placeholder for HP/MP washing feature.
* Both fixed and randomized versions of HP/MP growth rate abailable, regarding player job (enable one at ServerConstants). Placeholder for HP/MP washing feature.
Admin/GM commands:
* Server commands layered by GM levels.
* New commands.
* Spawn Zakum/Horntail/Pinkbean 100%.
* New commands.
Project:
* Organized project code.

View File

@@ -1 +1,3 @@
Extra classes for Monster Carnival. Can possibly be used for implementing MCPQ.
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.

View File

@@ -0,0 +1,82 @@
/** * [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();
}
}

View File

@@ -0,0 +1,326 @@
/** * [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();
}
}

View File

@@ -0,0 +1,88 @@
/** * [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();
}
}

View File

@@ -0,0 +1,88 @@
/** * [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();
}
}

View File

@@ -0,0 +1,8 @@
function enter(pi) { player = pi.getPlayer();
if (player.getMCPQField() != null) {
player.getMCPQField().onRevive(player);
} else {
pi.warp(980000000);
}
return true;
}

View File

@@ -0,0 +1,8 @@
function enter(pi) { player = pi.getPlayer();
if (player.getMCPQField() != null) {
player.getMCPQField().onRevive(player);
} else {
pi.warp(980000000);
}
return true;
}

View File

@@ -0,0 +1,8 @@
function enter(pi) { player = pi.getPlayer();
if (player.getMCPQField() != null) {
player.getMCPQField().onRevive(player);
} else {
pi.warp(980000000);
}
return true;
}

View File

@@ -0,0 +1,8 @@
function enter(pi) { player = pi.getPlayer();
if (player.getMCPQField() != null) {
player.getMCPQField().onRevive(player);
} else {
pi.warp(980000000);
}
return true;
}

View File

@@ -0,0 +1,8 @@
function enter(pi) { player = pi.getPlayer();
if (player.getMCPQField() != null) {
player.getMCPQField().onRevive(player);
} else {
pi.warp(980000000);
}
return true;
}

View File

@@ -0,0 +1,8 @@
function enter(pi) { player = pi.getPlayer();
if (player.getMCPQField() != null) {
player.getMCPQField().onRevive(player);
} else {
pi.warp(980000000);
}
return true;
}

View File

@@ -0,0 +1,51 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
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;
}

View File

@@ -0,0 +1,5 @@
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;
}

View File

@@ -0,0 +1,22 @@
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;
}
}
}

View File

@@ -0,0 +1,73 @@
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;
}

View File

@@ -0,0 +1,5 @@
Add this under disconnect() (right after event instance calls onPlayerDisconnect, preferably):
PHP Code:
if (chr.getMCPQField() != null) {
chr.getMCPQField().onPlayerDisconnected(player);
}

View File

@@ -0,0 +1,3 @@
Add to getMonster(int monsterId):
PHP Code:
stats.setCp(MapleDataTool.getIntConvert("getCP", monsterInfoData, 0));

View File

@@ -0,0 +1,80 @@
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;
}

View File

@@ -0,0 +1,22 @@
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.

View File

@@ -0,0 +1,31 @@
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);
}
}
}
}

View File

@@ -0,0 +1,13 @@
Add field:
PHP Code:
private int cp;
Add methods:
PHP Code:
public int getCp() {
return cp;
}
public void setCp(int cp) {
this.cp = cp;
}

View File

@@ -0,0 +1,6 @@
Under hitReactor(), add before standard handling:
PHP Code:
if (c.getPlayer().getMCPQField() != null) {
c.getPlayer().getMCPQField().onGuardianHit(c.getPlayer(), this);
return;
}

View File

@@ -0,0 +1,27 @@
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;
}

View File

@@ -0,0 +1,23 @@
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;

View File

@@ -0,0 +1,41 @@
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);
}
}
}

View File

@@ -0,0 +1,20 @@
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;
}
}
}

View File

@@ -0,0 +1,35 @@
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);
}

View File

@@ -118,7 +118,7 @@ Inclus
01 - 02 Abril 2017,
Correção de bug que impedia carregamento do mapa Singapore: Spooky World.
Adição dos cards de mob restantes no drop_data do BD.
Adição dos cards de mob restantes no drop_data do DB.
03 Abril 2017,
Inclusão de scripts para interceptar condições de corrida em eventos de viagem.
@@ -623,4 +623,12 @@ Corrigido storage n
Corrigido sistema de buffs não computando buffs com valor zero não sendo apropriadamente ativados no jogador.
29 Outubro 2017,
Adicionado drop data para diversas versões de Fairy, Yetis e Pepes.
Adicionado drop data para diversas versões de Fairy, Yetis e Pepes.
Adicionado proteção contra acesso concorrente em MapleGuild e MapleAlliance.
30 Outubro 2017,
Adicionado drop data de mesos para o restante dos mobs que dropam 4 ou mais itens.
31 Outubro 2017,
Corrigido bug ao mudar classe de Aran desconectando o jogador.
Corrigido drop data de flechas agora dropando bundles ao invés de uma quantidade unitária.