Skillbook announcer update + Encoded temporary mob buffs + Slot gains patch

Encoded temporary mob buffs on mob control request/spawn packets.
Improved scroll generator NPC, now testing other bucket set possibilities.
Fixed slot gain method recently updated leading to a temporary visual glitch (inventory would show as normal after relogin).
Reviewed mobid check by name, no longer trying to generate a whole mob instance for info retrieval.
Added debuff purge when applying a buyback.
Reviewed skillbook announcer not informing properly storybooks, reactor or script loots.
Fixed autoaggro not working properly as soon as a player enters the field.
Fixed "fake" mobs disappearing as soon as its controller changes.
This commit is contained in:
ronancpl
2019-12-24 20:11:32 -03:00
parent 959d990ab8
commit 07fe495bfd
34 changed files with 211 additions and 143 deletions

View File

@@ -6,7 +6,7 @@ Besides myself for maintaining this repository, credits are to be given to Wizet
Regarding distributability and usage of the code presented here: like it was before, this MapleStory server is open-source. By that, it is meant that anyone is **free to install, use, modify and redistribute the contents**, as long as there is **no kind of commercial trading involved** and the **credits to the original creators are maintained** within the codes.
This is a NetBeans 8.2 Project, that should be built and run on Java 8 in order to run properly (used to be ran in Java 7, thanks @kolakcc for the Java 8 support!).
This is a NetBeans 8.2 Project, that should be built and run on Java 8 in order to run properly. -- Used to be ran in Java 7, thanks kolakcc (Familiar) for the Java 8 support!
Being a NetBeans 8.2 Project, this means that it's easier to install the project via opening the server project folder inside NetBeans' IDE. Once installed, build this project on your machine and run the server using the "launch.bat" application.
@@ -33,6 +33,8 @@ Java 8 SDK & NetBeans bundle: https://www.oracle.com/technetwork/pt/java/javase/
**Change log:**
* Fixed Monster Magnet crashing the caster when trying to pull fixed mobs. https://gofile.io/?c=BW7dVM
* Cleared need for administrator privileges (OS) to play the game, credits to Ubaware.
* Set a higher cap for AP assigning with AP Reset, credits to Ubaware.
@@ -75,7 +77,7 @@ HeavenClient Github: https://github.com/ryantpayton/HeavenClient
---
### Development information
Status: <span style="color:grey">__In development (4th round)__</span>.
Status: <span style="color:SkyBlue">__Released (4 rounds)__</span>.
#### Mission
@@ -112,15 +114,11 @@ Our Discord channel is still available on: https://discord.gg/Q7wKxHX
<hr id="donate" />
### Donation
If you REALLY liked what you have seen on the project, please feel free to donate a little something as a helping hand for my contributions towards Maple development. Also remember to **support Nexon**!
Paypal: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3K8KVTWRLFBQ4
[//]: <> (If you REALLY liked what you have seen on the project, please feel free to donate a little something as a helping hand for my contributions towards Maple development. Also remember to **support Nexon**!)
### Disclaimer
* HeavenMS development is decisively __ONLY accepting donations__ from the Paypal link aforementioned, in the __ronancpl/HeavenMS__ repository readme (no patreons or other revenue resources).
[//]: <> (* HeavenMS development is decisively __ONLY accepting donations__ from the Paypal link aforementioned, in the __ronancpl/HeavenMS__ repository readme (no patreons or other revenue resources).)
* HeavenMS staff has __no current intention__ to publicly open a server with this source, if that ever comes to happen this note will be lifted. __Don't be scammed!__

View File

@@ -335,6 +335,7 @@ server:
MOB_STATUS_MONITOR_LIFE: 84 #Idle proc count the mob disease monitor is allowed to be there before closing it due to inactivity.
MOB_STATUS_AGGRO_PERSISTENCE: 2 #Idle proc count on aggro update for a mob to keep following the current controller, given him/her is the leading damage dealer.
MOB_STATUS_AGGRO_INTERVAL: 5000 #Interval in milliseconds between aggro logistics update.
USE_AUTOAGGRO_NEARBY: false #Mobs start following the player when approached.
#Some Gameplay Enhancing Configurations
#Scroll Configuration

View File

@@ -2330,3 +2330,29 @@ Revisado Inventory Sort, agora ordenando projéteis por bônus de dano.
06 Dezembro 2019,
Implementado pacote para visão de buffs de efeito imbuído em armas para outros jogadores.
Corrigido casos de exceção devido a portais nulos na função que troca jogador de mapas interferindo com próximas trocas de mapa (jogador fica preso até relogar).
07 Dezembro 2019,
Corrigido caso de exceção ao tentar atribuir pontos de stats ao jogador, que passou a ocorrer após mudança recente no sistema de stats.
09 Dezembro 2019,
Implementado aplicação de buffs ao se realizar a chamada/requisição de controle de um mob.
11 Dezembro 2019,
Incrementado suporte para aplicação de buffs de mobs de dano refletido, completando-se a ronda.
14 Dezembro 2019,
Melhorado NPC de geração de scrolls, agora testando outras possibilidades caso nenhum scroll encontrado em conjunto de scrolls.
Corrigido método de ganho de slots atualizado recentemente levando a bug visual, onde somente os 4 primeiros itens do inventário estariam aparecendo na tela.
Revisado método de busca por nomes, não mais tentando gerar um objeto de mob somente para checar o nome.
Adicionado limpa de debuffs ao se utilizar buyback.
Revisado anunciador de skillbooks não informando devidamente sobre os campos de reatores e scripts.
16 Dezembro 2019,
Revisado possível caso de dupe com itens ao fechar Hired Merchant.
20 Dezembro 2019,
Corrigido autoaggro não atuando devidamente em mobs assim que jogador entra no mapa e adquire controladores sobre os mesmos.
Corrigido anunciador de skillbooks não reportando etc de quest para começar, quando lidando com questlines onde se obtém a skill diretamente.
21 Dezembro 2019,
Corrigido mobs em estado "fake" desaparecendo da tela assim que muda-se o controlador do mesmo.

View File

@@ -88,7 +88,7 @@ function setEventRewards(eim) {
function afterSetup(eim) {}
function setup(channel) {
var eim = em.newInstance("Horntail" + channel); // thanks Thora for reporting an issue with misleading event name here
var eim = em.newInstance("Horntail" + channel); // thanks Thora (Arufonsu) for reporting an issue with misleading event name here
eim.setProperty("canJoin", 1);
eim.setProperty("defeatedBoss", 0);
eim.setProperty("defeatedHead", 0);

View File

@@ -125,7 +125,7 @@ function action(mode, type, selection) {
sendStr += " #L" + i + "# " + mobList[i] + "#l\r\n";
}
sendStr += "\r\n";
sendStr += "\r\n\r\n";
}
} else {
sendStr = "\r\n\r\n";

View File

@@ -353,7 +353,7 @@ function calculateScrollTiers() {
return tiers;
}
function getRandomScroll(tiers) {
function getRandomScrollFromTiers(tiers) {
var typeTier = tiers[0], subtypeTier = tiers[1], successTier = tiers[2];
var scrollTypePool = getScrollTypePool(typeTier);
var scrollPool = getAvailableScrollsPool(scrollTypePool, subtypeTier, successTier);
@@ -365,6 +365,36 @@ function getRandomScroll(tiers) {
}
}
function getRandomScrollFromRightPermutations(tiers) {
for (var i = 2; i >= 0; i--) {
for (var j = i - 1; j >= 0; j--) {
if (tiers[i] >= 3) {
break;
} else if (tiers[j] > 1) {
tiers[i]++;
tiers[j]--;
var itemid = getRandomScrollFromTiers(tiers);
if (itemid != -1) {
return itemid;
}
}
}
}
return -1;
}
function getRandomScroll(tiers) {
var itemid = getRandomScrollFromTiers(tiers);
if (itemid == -1) {
// worst case shift-right permutations...
itemid = getRandomScrollFromRightPermutations(tiers);
}
return itemid;
}
function performExchange(sgItemid, sgCount) {
if (cm.getMeso() < sgAppliedMeso) {
return false;

View File

@@ -144,7 +144,6 @@ import client.processor.action.PetAutopotProcessor;
import constants.game.ExpTable;
import constants.game.GameConstants;
import constants.inventory.ItemConstants;
import constants.net.ServerConstants;
import constants.skills.Aran;
import constants.skills.Beginner;
import constants.skills.Bishop;
@@ -2819,7 +2818,14 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
dispelDebuff(MapleDisease.WEAKEN);
dispelDebuff(MapleDisease.SLOW); // thanks Conrad for noticing ZOMBIFY isn't dispellable
}
public void purgeDebuffs() {
dispelDebuff(MapleDisease.SEDUCE);
dispelDebuff(MapleDisease.ZOMBIFY);
dispelDebuff(MapleDisease.CONFUSE);
dispelDebuffs();
}
public void cancelAllDebuffs() {
chrLock.lock();
try {
@@ -9420,26 +9426,27 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
public boolean gainSlots(int type, int slots, boolean update) {
boolean ret = gainSlotsInternal(type, slots, update);
if (ret) {
int newLimit = gainSlotsInternal(type, slots);
if (newLimit != -1) {
this.saveCharToDB();
if (update) {
client.announce(MaplePacketCreator.updateInventorySlotLimit(type, slots));
client.announce(MaplePacketCreator.updateInventorySlotLimit(type, newLimit));
}
return true;
} else {
return false;
}
return ret;
}
private boolean gainSlotsInternal(int type, int slots, boolean update) {
private int gainSlotsInternal(int type, int slots) {
inventory[type].lockInventory();
try {
if (canGainSlots(type, slots)) {
slots += inventory[type].getSlotLimit();
inventory[type].setSlotLimit(slots);
return true;
int newLimit = inventory[type].getSlotLimit() + slots;
inventory[type].setSlotLimit(newLimit);
return newLimit;
} else {
return false;
return -1;
}
} finally {
inventory[type].unlockInventory();
@@ -10652,6 +10659,12 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
client = null; // clients still triggers handlers a few times after disconnecting
map = null;
setListener(null);
// thanks Shavit for noticing a memory leak with inventories holding owner object
for (int i = 0; i < inventory.length; i++) {
inventory[i].dispose();
}
inventory = null;
}
}, 5 * 60 * 1000);
}

View File

@@ -33,7 +33,7 @@ public class DisposeCommand extends Command {
{
setDescription("");
}
@Override
public void execute(MapleClient c, String[] params) {
NPCScriptManager.getInstance().dispose(c);
@@ -41,5 +41,5 @@ public class DisposeCommand extends Command {
c.announce(MaplePacketCreator.enableActions());
c.removeClickedNPC();
c.getPlayer().message("You've been disposed.");
}
}
}

View File

@@ -25,7 +25,6 @@ package client.command.commands.gm0;
import client.MapleClient;
import client.command.Command;
import constants.net.ServerConstants;
import java.text.DateFormat;
import java.text.SimpleDateFormat;

View File

@@ -29,7 +29,6 @@ import client.MapleClient;
import server.MapleItemInformationProvider;
import server.life.MapleMonsterInformationProvider;
import server.life.MonsterDropEntry;
import tools.MaplePacketCreator;
import tools.Pair;
import java.util.Iterator;

View File

@@ -25,14 +25,12 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
@@ -672,4 +670,8 @@ public class MapleInventory implements Iterable<Item> {
public void unlockInventory() {
lock.unlock();
}
public void dispose() {
owner = null;
}
}

View File

@@ -70,6 +70,7 @@ public class BuybackProcessor {
}
chr.healHpMp();
chr.purgeDebuffs();
chr.broadcastStance(chr.isFacingLeft() ? 5 : 4);
MapleMap map = chr.getMap();

View File

@@ -179,6 +179,7 @@ public class ServerConfig {
public int MOB_STATUS_MONITOR_LIFE;
public int MOB_STATUS_AGGRO_PERSISTENCE;
public int MOB_STATUS_AGGRO_INTERVAL;
public boolean USE_AUTOAGGRO_NEARBY;
//Some Gameplay Enhancing Configurations
//Scroll Configuration

View File

@@ -12,6 +12,7 @@ public class ServerConstants {
public static int DEBUG_VALUES[] = new int[10]; // Field designed for packet testing purposes
// https://github.com/openstreetmap/josm/blob/a3a6e8a6b657cf4c5b4c64ea14d6e87be6280d65/src/org/openstreetmap/josm/tools/Utils.java#L1566-L1585
// Added by kolakcc (Familiar)
/**
* Returns the Java version as an int value.
* @return the Java version as an int value (8, 9, etc.)

View File

@@ -25,7 +25,6 @@ import config.YamlConfig;
import net.server.guild.MapleGuildResponse;
import net.server.guild.MapleGuild;
import constants.game.GameConstants;
import constants.net.ServerConstants;
import client.MapleClient;
import net.AbstractMaplePacketHandler;
import tools.data.input.SeekableLittleEndianAccessor;

View File

@@ -31,7 +31,6 @@ import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
import client.MapleCharacter;
import client.MapleClient;
import constants.net.ServerConstants;
import net.server.coordinator.world.MapleInviteCoordinator;
import net.server.coordinator.world.MapleInviteCoordinator.InviteResult;
import net.server.coordinator.world.MapleInviteCoordinator.InviteType;

View File

@@ -58,13 +58,13 @@ public final class PlayerMapTransitionHandler extends AbstractMaplePacketHandler
if (m.getController() == chr) {
c.announce(MaplePacketCreator.stopControllingMonster(m.getObjectId()));
m.sendDestroyData(c);
m.aggroRedirectController();
m.aggroRemoveController();
} else {
m.sendDestroyData(c);
}
m.aggroSwitchController(chr, false);
m.sendSpawnData(c);
m.aggroSwitchController(chr, false);
}
}
}

View File

@@ -354,7 +354,7 @@ public class MapleMatchCheckerCoordinator {
if (mmce != null) {
synchronized (mmce) {
if (!mmce.isMatchActive()) { // thanks Alex (CanIGetaPR) for noticing that exploiters could stall on match checking
if (!mmce.isMatchActive()) { // thanks Alex (Alex-0000) for noticing that exploiters could stall on match checking
matchEntries.remove(cid);
mmce = null;
} else {

View File

@@ -27,7 +27,7 @@ import net.server.PlayerStorage;
/**
* @author Ronan
*/
public class CharacterAutosaverTask extends BaseTask implements Runnable { // thanks Alex (Alex09) for noticing these runnable classes are tasks, "workers" runs them
public class CharacterAutosaverTask extends BaseTask implements Runnable { // thanks Alex09 (Alex-0000) for noticing these runnable classes are tasks, "workers" runs them
@Override
public void run() {

View File

@@ -28,7 +28,6 @@ import java.sql.SQLException;
import client.MapleJob;
import config.YamlConfig;
import tools.DatabaseConnection;
import constants.net.ServerConstants;
import net.server.Server;
/**

View File

@@ -60,7 +60,6 @@ import client.inventory.MaplePet;
import constants.game.GameConstants;
import constants.inventory.ItemConstants;
import constants.string.LanguageConstants;
import net.server.PlayerStorage;
import net.server.channel.Channel;
import net.server.coordinator.matchchecker.MatchCheckerListenerFactory.MatchCheckerType;
import server.MapleMarriage;
@@ -603,6 +602,12 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
switch (sbe) {
case UNAVAILABLE:
return "";
case REACTOR:
return " Obtainable through #rexploring#k (loot boxes).";
case SCRIPT:
return " Obtainable through #rexploring#k (field interaction).";
case QUEST_BOOK:
return " Obtainable through #rquestline#k (collecting book).";
@@ -633,7 +638,7 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
for (int i = 0; i < 6; i++) {
if (fieldTaken(i)) {
if (fieldLobbied(i)) {
msg += "#b#L" + i + "#Carnival Field " + (i + 1) + " (Level: " // "Carnival field" GMS-like improvement thanks to Jayd
msg += "#b#L" + i + "#Carnival Field " + (i + 1) + " (Level: " // "Carnival field" GMS-like improvement thanks to Jayd (jaydenseah)
+ cpqCalcAvgLvl(980000100 + i * 100) + " / "
+ getPlayerCount(980000100 + i * 100) + "x"
+ getPlayerCount(980000100 + i * 100) + ") #l\r\n";

View File

@@ -49,7 +49,6 @@ import client.inventory.ItemFactory;
import client.inventory.MapleInventoryType;
import client.inventory.MaplePet;
import constants.inventory.ItemConstants;
import constants.net.ServerConstants;
import java.util.Collections;
import net.server.audit.locks.MonitoredLockType;

View File

@@ -60,7 +60,6 @@ import client.inventory.Item;
import client.inventory.MapleInventory;
import client.inventory.MapleInventoryType;
import client.inventory.MapleWeaponType;
import constants.net.ServerConstants;
import constants.inventory.EquipSlot;
import constants.inventory.ItemConstants;
import constants.skills.Assassin;
@@ -2117,7 +2116,7 @@ public class MapleItemInformationProvider {
ResultSet rs = ps.executeQuery();
while(rs.next()) {
String resultName = MapleMonsterInformationProvider.getInstance().getMobNameFromId(rs.getInt("dropperid"));
if (resultName != null) {
if (!resultName.isEmpty()) {
list.add(resultName);
}
}

View File

@@ -65,18 +65,11 @@ public class MapleSkillbookInformationProvider {
SCRIPT
}
private static String host = "jdbc:mysql://localhost:3306/heavenms";
private static String driver = "com.mysql.jdbc.Driver";
private static String username = "root";
private static String password = "";
private static String rootDirectory = ".";
private static int skillbookMinItemid = 2280000;
private static int skillbookMaxItemid = 2300000; // exclusively
private static Set<Integer> questSkills = new HashSet<>();
static {
loadSkillbooks();
}
@@ -156,7 +149,13 @@ public class MapleSkillbookInformationProvider {
int skillid = MapleDataTool.getInt("id", questSkillData, 0);
if (is4thJobSkill(skillid)) {
// negative itemids are skill rewards
foundSkillbooks.put(-skillid, SkillBookEntry.QUEST_REWARD);
int questbook = fetchQuestbook(checkData, questData.getName());
if (questbook < 0) {
foundSkillbooks.put(-skillid, SkillBookEntry.QUEST_REWARD);
} else {
foundSkillbooks.put(-skillid, SkillBookEntry.QUEST_BOOK);
}
}
}
}

View File

@@ -958,10 +958,7 @@ public class MapleStatEffect {
if (isDispel() && makeChanceResult()) {
applyto.dispelDebuffs();
} else if (isCureAllAbnormalStatus()) {
applyto.dispelDebuff(MapleDisease.SEDUCE);
applyto.dispelDebuff(MapleDisease.ZOMBIFY);
applyto.dispelDebuff(MapleDisease.CONFUSE);
applyto.dispelDebuffs();
applyto.purgeDebuffs();
} else if (isComboReset()) {
applyto.setCombo((short) 0);
}

View File

@@ -36,7 +36,6 @@ import client.inventory.MapleInventoryType;
import client.inventory.manipulator.MapleInventoryManipulator;
import client.inventory.manipulator.MapleKarmaManipulator;
import constants.game.GameConstants;
import constants.net.ServerConstants;
import net.server.coordinator.world.MapleInviteCoordinator;
import net.server.coordinator.world.MapleInviteCoordinator.InviteResult;
import net.server.coordinator.world.MapleInviteCoordinator.InviteType;

View File

@@ -1040,23 +1040,6 @@ public class MapleMonster extends AbstractLoadedMapleLife {
return isBoss() && getTagColor() > 0;
}
public void broadcastMonsterStatus() {
Collection<MonsterStatusEffect> mseList = this.getStati().values();
for (MapleCharacter chr : map.getAllPlayers()) {
announceMonsterStatusInternal(chr.getClient(), mseList);
}
}
public void announceMonsterStatus(MapleClient client) {
announceMonsterStatusInternal(client, this.getStati().values());
}
public void announceMonsterStatusInternal(MapleClient client, Collection<MonsterStatusEffect> mseList) {
for (MonsterStatusEffect mse : mseList) {
client.announce(MaplePacketCreator.applyMonsterStatus(getObjectId(), mse, null));
}
}
@Override
public void sendSpawnData(MapleClient client) {
if (hp.get() <= 0) { // mustn't monsterLock this function
@@ -1068,8 +1051,6 @@ public class MapleMonster extends AbstractLoadedMapleLife {
client.announce(MaplePacketCreator.spawnMonster(this, false));
}
announceMonsterStatus(client);
if (hasBossHPBar()) {
client.announceBossHpBar(this, this.hashCode(), makeBossHPBarPacket());
}
@@ -1934,7 +1915,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
* Removes controllability status from the current controller of this mob.
*
*/
private Pair<MapleCharacter, Boolean> aggroRemoveController() {
public Pair<MapleCharacter, Boolean> aggroRemoveController() {
MapleCharacter chrController;
boolean hadAggro;
@@ -1951,7 +1932,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
}
if (chrController != null) { // this can/should only happen when a hidden gm attacks the monster
chrController.announce(MaplePacketCreator.stopControllingMonster(this.getObjectId()));
if (!this.isFake()) chrController.announce(MaplePacketCreator.stopControllingMonster(this.getObjectId()));
chrController.stopControllingMonster(this);
}
@@ -2123,9 +2104,12 @@ public class MapleMonster extends AbstractLoadedMapleLife {
MapleCharacter chrController = this.getActiveController();
if (chrController == null) {
this.aggroSwitchController(player, true);
this.aggroSwitchController(player, true);
} else if (chrController.getId() == player.getId()) {
this.setControllerHasAggro(true);
if (!YamlConfig.config.server.USE_AUTOAGGRO_NEARBY) { // thanks Lichtmager for noticing autoaggro not updating the player properly
aggroMonsterControl(player.getClient(), this, true);
}
}
}
@@ -2164,9 +2148,6 @@ public class MapleMonster extends AbstractLoadedMapleLife {
private static void aggroMonsterControl(MapleClient c, MapleMonster mob, boolean immediateAggro) {
c.announce(MaplePacketCreator.controlMonster(mob, false, immediateAggro));
// thanks BHB for noticing puppets disrupting mobstatuses for bowmans
mob.announceMonsterStatus(c);
}
private void aggroRefreshPuppetVisibility(MapleCharacter chrController, MapleSummon puppet) {
@@ -2185,7 +2166,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
chrController.announce(MaplePacketCreator.removeSummon(puppet, false));
MapleClient c = chrController.getClient();
for (MapleMonster mob : puppetControlled) {
for (MapleMonster mob : puppetControlled) { // thanks BHB for noticing puppets disrupting mobstatuses for bowmans
aggroMonsterControl(c, mob, mob.isControllerKnowsAboutAggro());
}
chrController.announce(MaplePacketCreator.spawnSummon(puppet, false));

View File

@@ -316,16 +316,10 @@ public class MapleMonsterInformationProvider {
public String getMobNameFromId(int id) {
String mobName = mobNameCache.get(id);
if (mobName == null) {
try {
mobName = MapleLifeFactory.getMonster(id).getName();
} catch (NullPointerException npe) {
mobName = ""; //nonexistant mob
} catch (Exception e) {
e.printStackTrace();
System.err.println("Nonexistant mob id " + id);
mobName = ""; //nonexistant mob
}
MapleDataProvider dataProvider = MapleDataProviderFactory.getDataProvider(new File("wz/String.wz"));
MapleData mobData = dataProvider.getData("Mob.img");
mobName = MapleDataTool.getString(mobData.getChildByPath(id + "/name"), "");
mobNameCache.put(id, mobName);
}

View File

@@ -44,7 +44,6 @@ import client.MapleClient;
import client.inventory.Item;
import client.inventory.MapleInventoryType;
import constants.game.GameConstants;
import constants.net.ServerConstants;
import net.server.Server;
import net.server.channel.Channel;
import net.server.world.World;

View File

@@ -19,7 +19,6 @@
*/
package server.life;
import constants.net.ServerConstants;
import java.io.File;
import java.util.HashMap;
import java.util.Map;

View File

@@ -413,18 +413,6 @@ public class MapleHiredMerchant extends AbstractMapleMapObject {
this.removeOwner(c.getPlayer());
try {
MapleCharacter player = c.getWorldServer().getPlayerStorage().getCharacterById(ownerId);
if(player != null) {
player.setHasMerchant(false);
} else {
Connection con = DatabaseConnection.getConnection();
try (PreparedStatement ps = con.prepareStatement("UPDATE characters SET HasMerchant = 0 WHERE id = ?", Statement.RETURN_GENERATED_KEYS)) {
ps.setInt(1, ownerId);
ps.executeUpdate();
}
con.close();
}
List<MaplePlayerShopItem> copyItems = getItems();
if (check(c.getPlayer(), copyItems) && !timeout) {
for (MaplePlayerShopItem mpsi : copyItems) {
@@ -448,6 +436,19 @@ public class MapleHiredMerchant extends AbstractMapleMapObject {
e.printStackTrace();
}
// thanks Rohenn for noticing a possible dupe scenario on closing shop
MapleCharacter player = c.getWorldServer().getPlayerStorage().getCharacterById(ownerId);
if(player != null) {
player.setHasMerchant(false);
} else {
Connection con = DatabaseConnection.getConnection();
try (PreparedStatement ps = con.prepareStatement("UPDATE characters SET HasMerchant = 0 WHERE id = ?", Statement.RETURN_GENERATED_KEYS)) {
ps.setInt(1, ownerId);
ps.executeUpdate();
}
con.close();
}
if (YamlConfig.config.server.USE_ENFORCE_MERCHANT_SAVE) {
c.getPlayer().saveCharToDB(false);
}

View File

@@ -2017,7 +2017,7 @@ public class MapleMap {
if (getEventInstance() != null) {
getEventInstance().registerMonster(monster);
}
spawnAndAddRangedMapObject(monster, new DelayedPacketCreation() {
@Override
public void sendPackets(MapleClient c) {
@@ -2108,7 +2108,7 @@ public class MapleMap {
c.announce(MaplePacketCreator.spawnFakeMonster(monster, 0));
}
});
spawnedMonstersOnMap.incrementAndGet();
addSelfDestructive(monster);
}
@@ -2116,7 +2116,6 @@ public class MapleMap {
public void makeMonsterReal(final MapleMonster monster) {
monster.setFake(false);
broadcastMessage(MaplePacketCreator.makeMonsterReal(monster));
monster.broadcastMonsterStatus();
monster.aggroUpdateController();
updateBossSpawn(monster);
}
@@ -3081,8 +3080,6 @@ public class MapleMap {
for (MapleMapObject o : objects) {
if (isNonRangedType(o.getType())) {
o.sendSpawnData(c);
} else if (o.getType() == MapleMapObjectType.MONSTER) {
((MapleMonster) o).aggroUpdateController();
} else if (o.getType() == MapleMapObjectType.SUMMON) {
MapleSummon summon = (MapleSummon) o;
if (summon.getOwner() == chr) {
@@ -3110,6 +3107,10 @@ public class MapleMap {
} else {
o.sendSpawnData(chr.getClient());
chr.addVisibleMapObject(o);
if (o.getType() == MapleMapObjectType.MONSTER) {
((MapleMonster) o).aggroUpdateController();
}
}
}
}

View File

@@ -1486,6 +1486,43 @@ public class MaplePacketCreator {
mplew.write(newSpawn ? -2 : -1);
}
private static void encodeTemporary(MaplePacketLittleEndianWriter mplew, Map<MonsterStatus, MonsterStatusEffect> stati) {
int pCounter = -1, mCounter = -1;
writeLongEncodeTemporaryMask(mplew, stati.keySet()); // packet structure mapped thanks to Eric
for (Entry<MonsterStatus, MonsterStatusEffect> s : stati.entrySet()) {
MonsterStatusEffect mse = s.getValue();
mplew.writeShort(mse.getStati().get(s.getKey()));
MobSkill mobSkill = mse.getMobSkill();
if (mobSkill != null) {
mplew.writeShort(mobSkill.getSkillId());
mplew.writeShort(mobSkill.getSkillLevel());
switch(s.getKey()) {
case WEAPON_REFLECT:
pCounter = mobSkill.getX();
break;
case MAGIC_REFLECT:
mCounter = mobSkill.getY();
break;
}
} else {
Skill skill = mse.getSkill();
mplew.writeInt(skill != null ? skill.getId() : 0);
}
mplew.writeShort(-1); // duration
}
// reflect packet structure found thanks to Arnah (Vertisy)
if(pCounter != -1) mplew.writeInt(pCounter);// wPCounter_
if(mCounter != -1) mplew.writeInt(mCounter);// wMCounter_
if(pCounter != -1 || mCounter != -1) mplew.writeInt(100);// nCounterProb_
}
/**
* Internal function to handler monster spawning and controlling.
*
@@ -1513,9 +1550,13 @@ public class MaplePacketCreator {
mplew.writeInt(life.getObjectId());
mplew.write(life.getController() == null ? 5 : 1);
mplew.writeInt(life.getId());
mplew.skip(15);
mplew.write(0x88);
mplew.skip(6);
if (requestController) {
encodeTemporary(mplew, life.getStati()); // thanks shot for noticing encode temporary buffs missing
} else {
mplew.skip(16);
}
mplew.writePos(life.getPosition());
mplew.write(life.getStance());
mplew.writeShort(0); //Origin FH //life.getStartFh()
@@ -1561,9 +1602,7 @@ public class MaplePacketCreator {
mplew.writeInt(life.getObjectId());
mplew.write(5);
mplew.writeInt(life.getId());
mplew.skip(15);
mplew.write(0x88);
mplew.skip(6);
encodeTemporary(mplew, life.getStati());
mplew.writePos(life.getPosition());
mplew.write(life.getStance());
mplew.writeShort(0);//life.getStartFh()
@@ -1591,9 +1630,7 @@ public class MaplePacketCreator {
mplew.writeInt(life.getObjectId());
mplew.write(5);
mplew.writeInt(life.getId());
mplew.skip(15);
mplew.write(0x88);
mplew.skip(6);
encodeTemporary(mplew, life.getStati());
mplew.writePos(life.getPosition());
mplew.write(life.getStance());
mplew.writeShort(0);//life.getStartFh()
@@ -3151,6 +3188,21 @@ public class MaplePacketCreator {
mplew.writeLong(firstmask);
mplew.writeLong(secondmask);
}
private static void writeLongEncodeTemporaryMask(final MaplePacketLittleEndianWriter mplew, Collection<MonsterStatus> stati) {
int masks[] = new int[4];
for (MonsterStatus statup : stati) {
int pos = statup.isFirst() ? 0 : 2;
for (int i = 0; i < 2; i++) {
masks[pos + i] |= statup.getValue() >> 32 * i;
}
}
for (int i = 0; i < masks.length; i++) {
mplew.writeInt(masks[i]);
}
}
public static byte[] cancelDebuff(long mask) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(19);
@@ -4137,30 +4189,6 @@ public class MaplePacketCreator {
mplew.writeInt(secondmask);
}
public static byte[] applyMonsterStatus(int oid, Map<MonsterStatus, Integer> stats, int skill, boolean monsterSkill, int delay, MobSkill mobskill) {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.APPLY_MONSTER_STATUS.getValue());
mplew.writeInt(oid);
int mask = 0;
for (MonsterStatus stat : stats.keySet()) {
mask |= stat.getValue();
}
mplew.writeInt(mask);
for (Integer val : stats.values()) {
mplew.writeShort(val);
if (monsterSkill) {
mplew.writeShort(mobskill.getSkillId());
mplew.writeShort(mobskill.getSkillLevel());
} else {
mplew.writeInt(skill);
}
mplew.writeShort(0); // as this looks similar to giveBuff this
}
mplew.writeShort(delay); // delay in ms
mplew.write(1); // ?
return mplew.getPacket();
}
public static byte[] applyMonsterStatus(final int oid, final MonsterStatusEffect mse, final List<Integer> reflection) {
Map<MonsterStatus, Integer> stati = mse.getStati();
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();

View File

@@ -23,7 +23,6 @@ import client.MapleCharacter;
import config.YamlConfig;
import constants.game.GameConstants;
import constants.inventory.ItemConstants;
import constants.net.ServerConstants;
import server.MapleItemInformationProvider;
import tools.MaplePacketCreator;