Switch to Maven file structure

This commit is contained in:
P0nk
2021-03-30 21:07:35 +02:00
parent 4acc5675d6
commit 813643036b
817 changed files with 16 additions and 0 deletions

View File

@@ -0,0 +1,82 @@
/*
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 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.maps;
import java.util.Arrays;
import tools.data.input.ByteArrayByteStream;
import tools.data.input.GenericSeekableLittleEndianAccessor;
import tools.data.input.SeekableLittleEndianAccessor;
import tools.data.output.MaplePacketLittleEndianWriter;
public abstract class AbstractAnimatedMapleMapObject extends AbstractMapleMapObject implements AnimatedMapleMapObject {
static {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter((int) getIdleMovementDataLength());
mplew.write(1); //movement command count
mplew.write(0);
mplew.writeShort(-1); //x
mplew.writeShort(-1); //y
mplew.writeShort(0); //xwobble
mplew.writeShort(0); //ywobble
mplew.writeShort(0); //fh
mplew.write(-1); //stance
mplew.writeShort(0); //duration
idleMovementPacketData = mplew.getPacket();
}
private static final byte[] idleMovementPacketData;
private int stance;
@Override
public int getStance() {
return stance;
}
@Override
public void setStance(int stance) {
this.stance = stance;
}
@Override
public boolean isFacingLeft() {
return Math.abs(stance) % 2 == 1;
}
public SeekableLittleEndianAccessor getIdleMovement() {
byte[] movementData = Arrays.copyOf(idleMovementPacketData, idleMovementPacketData.length);
//seems wasteful to create a whole packet writer when only a few values are changed
int x = getPosition().x;
int y = getPosition().y;
movementData[2] = (byte) (x & 0xFF); //x
movementData[3] = (byte) (x >> 8 & 0xFF);
movementData[4] = (byte) (y & 0xFF); //y
movementData[5] = (byte) (y >> 8 & 0xFF);
movementData[12] = (byte) (getStance() & 0xFF);
return new GenericSeekableLittleEndianAccessor(new ByteArrayByteStream(movementData));
}
public static long getIdleMovementDataLength() {
return 15;
}
}

View File

@@ -0,0 +1,57 @@
/*
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 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.maps;
import java.awt.Point;
public abstract class AbstractMapleMapObject implements MapleMapObject {
private Point position = new Point();
private int objectId;
@Override
public abstract MapleMapObjectType getType();
@Override
public Point getPosition() {
return new Point(position);
}
@Override
public void setPosition(Point position) {
this.position.move(position.x, position.y);
}
@Override
public int getObjectId() {
return objectId;
}
@Override
public void setObjectId(int id) {
this.objectId = id;
}
@Override
public void nullifyPosition() {
this.position = null;
}
}

View File

@@ -0,0 +1,28 @@
/*
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 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.maps;
public interface AnimatedMapleMapObject extends MapleMapObject {
int getStance();
void setStance(int stance);
boolean isFacingLeft();
}

View File

@@ -0,0 +1,69 @@
/*
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/>.
*/
package server.maps;
/**
*
* @author AngelSL
*/
public enum FieldLimit {
JUMP(0x01),
MOVEMENTSKILLS(0x02),
SUMMON(0x04),
DOOR(0x08),
CANNOTMIGRATE(0x10), //change channel, town portal scroll, access cash shop, etc etc
//NO_NOTES(0x20),
CANNOTVIPROCK(0x40),
CANNOTMINIGAME(0x80),
//SPECIFIC_PORTAL_SCROLL_LIMIT(0x100), // APQ and a couple quest maps have this
CANNOTUSEMOUNTS(0x200),
//STAT_CHANGE_ITEM_CONSUME_LIMIT(0x400), // Monster carnival?
//PARTY_BOSS_CHANGE_LIMIT(0x800), // Monster carnival?
CANNOTUSEPOTION(0x1000),
//WEDDING_INVITATION_LIMIT(0x2000), // No notes
//CASH_WEATHER_CONSUME_LIMIT(0x4000),
//NO_PET(0x8000), // Ariant colosseum-related?
//ANTI_MACRO_LIMIT(0x10000), // No notes
CANNOTJUMPDOWN(0x20000),
//SUMMON_NPC_LIMIT(0x40000); // Seems to .. disable Rush if 0x2 is set
//......... EVEN MORE LIMITS ............
//SUMMON_NPC_LIMIT(0x40000),
NO_EXP_DECREASE(0x80000),
//NO_DAMAGE_ON_FALLING(0x100000),
//PARCEL_OPEN_LIMIT(0x200000),
DROP_LIMIT(0x400000);
//ROCKETBOOSTER_LIMIT(0x800000) //lol we don't even have mechanics <3
private long i;
private FieldLimit(long i) {
this.i = i;
}
public long getValue() {
return i;
}
public boolean check(int fieldlimit) {
return (fieldlimit & i) == i;
}
}

View File

@@ -0,0 +1,61 @@
/*
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 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.maps;
import java.util.concurrent.ScheduledFuture;
import server.TimerManager;
public class MapMonitor {
private ScheduledFuture<?> monitorSchedule;
private MapleMap map;
private MaplePortal portal;
public MapMonitor(final MapleMap map, String portal) {
this.map = map;
this.portal = map.getPortal(portal);
this.monitorSchedule = TimerManager.getInstance().register(new Runnable() {
@Override
public void run() {
if (map.getCharacters().size() < 1) {
cancelAction();
}
}
}, 5000);
}
private void cancelAction() {
if (monitorSchedule != null) { // thanks Thora for pointing a NPE occurring here
monitorSchedule.cancel(false);
monitorSchedule = null;
}
map.killAllMonsters();
map.clearDrops();
if (portal != null) {
portal.setPortalStatus(MaplePortal.OPEN);
}
map.resetReactors();
map = null;
portal = null;
}
}

View File

@@ -0,0 +1,197 @@
/*
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 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.maps;
import java.awt.Point;
import java.util.Collection;
import config.YamlConfig;
import tools.Pair;
import client.MapleCharacter;
import net.server.services.type.ChannelServices;
import net.server.services.task.channel.OverallService;
/**
*
* @author Matze
* @author Ronan
*/
public class MapleDoor {
private int ownerId;
private MapleMap town;
private MaplePortal townPortal;
private MapleMap target;
private Pair<String, Integer> posStatus = null;
private long deployTime;
private boolean active;
private MapleDoorObject townDoor;
private MapleDoorObject areaDoor;
public MapleDoor(MapleCharacter owner, Point targetPosition) {
this.ownerId = owner.getId();
this.target = owner.getMap();
if(target.canDeployDoor(targetPosition)) {
if(YamlConfig.config.server.USE_ENFORCE_MDOOR_POSITION) {
posStatus = target.getDoorPositionStatus(targetPosition);
}
if(posStatus == null) {
this.town = this.target.getReturnMap();
this.townPortal = getTownDoorPortal(owner.getDoorSlot());
this.deployTime = System.currentTimeMillis();
this.active = true;
if(townPortal != null) {
this.areaDoor = new MapleDoorObject(ownerId, town, target, townPortal.getId(), targetPosition, townPortal.getPosition());
this.townDoor = new MapleDoorObject(ownerId, target, town, -1, townPortal.getPosition(), targetPosition);
this.areaDoor.setPairOid(this.townDoor.getObjectId());
this.townDoor.setPairOid(this.areaDoor.getObjectId());
} else {
this.ownerId = -1;
}
} else {
this.ownerId = -3;
}
} else {
this.ownerId = -2;
}
}
public void updateDoorPortal(MapleCharacter owner) {
int slot = owner.fetchDoorSlot();
MaplePortal nextTownPortal = getTownDoorPortal(slot);
if(nextTownPortal != null) {
townPortal = nextTownPortal;
areaDoor.update(nextTownPortal.getId(), nextTownPortal.getPosition());
}
}
private void broadcastRemoveDoor(MapleCharacter owner) {
MapleDoorObject areaDoor = this.getAreaDoor();
MapleDoorObject townDoor = this.getTownDoor();
MapleMap target = this.getTarget();
MapleMap town = this.getTown();
Collection<MapleCharacter> targetChars = target.getCharacters();
Collection<MapleCharacter> townChars = town.getCharacters();
target.removeMapObject(areaDoor);
town.removeMapObject(townDoor);
for (MapleCharacter chr : targetChars) {
areaDoor.sendDestroyData(chr.getClient());
chr.removeVisibleMapObject(areaDoor);
}
for (MapleCharacter chr : townChars) {
townDoor.sendDestroyData(chr.getClient());
chr.removeVisibleMapObject(townDoor);
}
owner.removePartyDoor(false);
if (this.getTownPortal().getId() == 0x80) {
for (MapleCharacter chr : townChars) {
MapleDoor door = chr.getMainTownDoor();
if (door != null) {
townDoor.sendSpawnData(chr.getClient());
chr.addVisibleMapObject(townDoor);
}
}
}
}
public static void attemptRemoveDoor(final MapleCharacter owner) {
final MapleDoor destroyDoor = owner.getPlayerDoor();
if (destroyDoor != null && destroyDoor.dispose()) {
long effectTimeLeft = 3000 - destroyDoor.getElapsedDeployTime(); // portal deployment effect duration
if (effectTimeLeft > 0) {
MapleMap town = destroyDoor.getTown();
OverallService service = (OverallService) town.getChannelServer().getServiceAccess(ChannelServices.OVERALL);
service.registerOverallAction(town.getId(), new Runnable() {
@Override
public void run() {
destroyDoor.broadcastRemoveDoor(owner); // thanks BHB88 for noticing doors crashing players when instantly cancelling buff
}
}, effectTimeLeft);
} else {
destroyDoor.broadcastRemoveDoor(owner);
}
}
}
private MaplePortal getTownDoorPortal(int doorid) {
return town.getDoorPortal(doorid);
}
public int getOwnerId() {
return ownerId;
}
public MapleDoorObject getTownDoor() {
return townDoor;
}
public MapleDoorObject getAreaDoor() {
return areaDoor;
}
public MapleMap getTown() {
return town;
}
public MaplePortal getTownPortal() {
return townPortal;
}
public MapleMap getTarget() {
return target;
}
public Pair<String, Integer> getDoorStatus() {
return posStatus;
}
public long getElapsedDeployTime() {
return System.currentTimeMillis() - deployTime;
}
private boolean dispose() {
if (active) {
active = false;
return true;
} else {
return false;
}
}
public boolean isActive() {
return active;
}
}

View File

@@ -0,0 +1,188 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2019 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/>.
*/
package server.maps;
import java.awt.Point;
import client.MapleCharacter;
import client.MapleClient;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReadLock;
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
import net.server.audit.locks.MonitoredWriteLock;
import net.server.audit.locks.factory.MonitoredReadLockFactory;
import net.server.audit.locks.factory.MonitoredWriteLockFactory;
import net.server.world.MapleParty;
import tools.MaplePacketCreator;
/**
*
* @author Ronan
*/
public class MapleDoorObject extends AbstractMapleMapObject {
private final int ownerId;
private int pairOid;
private final MapleMap from;
private final MapleMap to;
private int linkedPortalId;
private Point linkedPos;
private final MonitoredReentrantReadWriteLock locks = new MonitoredReentrantReadWriteLock(MonitoredLockType.PLAYER_DOOR, true);
private MonitoredReadLock rlock = MonitoredReadLockFactory.createLock(locks);
private MonitoredWriteLock wlock = MonitoredWriteLockFactory.createLock(locks);
public MapleDoorObject(int owner, MapleMap destination, MapleMap origin, int townPortalId, Point targetPosition, Point toPosition) {
super();
setPosition(targetPosition);
ownerId = owner;
linkedPortalId = townPortalId;
from = origin;
to = destination;
linkedPos = toPosition;
}
public void update(int townPortalId, Point toPosition) {
wlock.lock();
try {
linkedPortalId = townPortalId;
linkedPos = toPosition;
} finally {
wlock.unlock();
}
}
private int getLinkedPortalId() {
rlock.lock();
try {
return linkedPortalId;
} finally {
rlock.unlock();
}
}
private Point getLinkedPortalPosition() {
rlock.lock();
try {
return linkedPos;
} finally {
rlock.unlock();
}
}
public void warp(final MapleCharacter chr) {
MapleParty party = chr.getParty();
if (chr.getId() == ownerId || (party != null && party.getMemberById(ownerId) != null)) {
chr.announce(MaplePacketCreator.playPortalSound());
if(!inTown() && party == null) {
chr.changeMap(to, getLinkedPortalId());
} else {
chr.changeMap(to, getLinkedPortalPosition());
}
} else {
chr.getClient().announce(MaplePacketCreator.blockedMessage(6));
chr.getClient().announce(MaplePacketCreator.enableActions());
}
}
@Override
public void sendSpawnData(MapleClient client) {
sendSpawnData(client, true);
}
public void sendSpawnData(MapleClient client, boolean launched) {
MapleCharacter chr = client.getPlayer();
if (this.getFrom().getId() == chr.getMapId()) {
if (chr.getParty() != null && (this.getOwnerId() == chr.getId() || chr.getParty().getMemberById(this.getOwnerId()) != null)) {
chr.announce(MaplePacketCreator.partyPortal(this.getFrom().getId(), this.getTo().getId(), this.toPosition()));
}
chr.announce(MaplePacketCreator.spawnPortal(this.getFrom().getId(), this.getTo().getId(), this.toPosition()));
if (!this.inTown()) {
chr.announce(MaplePacketCreator.spawnDoor(this.getOwnerId(), this.getPosition(), launched));
}
}
}
@Override
public void sendDestroyData(MapleClient client) {
MapleCharacter chr = client.getPlayer();
if (from.getId() == chr.getMapId()) {
MapleParty party = chr.getParty();
if (party != null && (ownerId == chr.getId() || party.getMemberById(ownerId) != null)) {
client.announce(MaplePacketCreator.partyPortal(999999999, 999999999, new Point(-1, -1)));
}
client.announce(MaplePacketCreator.removeDoor(ownerId, inTown()));
}
}
public void sendDestroyData(MapleClient client, boolean partyUpdate) {
if (client != null && from.getId() == client.getPlayer().getMapId()) {
client.announce(MaplePacketCreator.partyPortal(999999999, 999999999, new Point(-1, -1)));
client.announce(MaplePacketCreator.removeDoor(ownerId, inTown()));
}
}
public int getOwnerId() {
return ownerId;
}
public void setPairOid(int oid) {
this.pairOid = oid;
}
public int getPairOid() {
return pairOid;
}
public boolean inTown() {
return getLinkedPortalId() == -1;
}
public MapleMap getFrom() {
return from;
}
public MapleMap getTo() {
return to;
}
public MapleMap getTown() {
return inTown() ? from : to;
}
public MapleMap getArea() {
return !inTown() ? from : to;
}
public Point getAreaPosition() {
return !inTown() ? getPosition() : getLinkedPortalPosition();
}
public Point toPosition() {
return getLinkedPortalPosition();
}
@Override
public MapleMapObjectType getType() {
return MapleMapObjectType.DOOR;
}
}

View File

@@ -0,0 +1,65 @@
/*
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 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.maps;
import tools.MaplePacketCreator;
import client.MapleCharacter;
import client.MapleClient;
public class MapleDragon extends AbstractAnimatedMapleMapObject {
private MapleCharacter owner;
public MapleDragon(MapleCharacter chr) {
super();
this.owner = chr;
this.setPosition(chr.getPosition());
this.setStance(chr.getStance());
this.sendSpawnData(chr.getClient());
}
@Override
public MapleMapObjectType getType() {
return MapleMapObjectType.DRAGON;
}
@Override
public void sendSpawnData(MapleClient client) {
client.announce(MaplePacketCreator.spawnDragon(this));
}
@Override
public int getObjectId() {
return owner.getId();
}
@Override
public void sendDestroyData(MapleClient c) {
c.announce(MaplePacketCreator.removeDragon(owner.getId()));
}
public MapleCharacter getOwner() {
return owner;
}
}

View File

@@ -0,0 +1,103 @@
/*
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 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.maps;
import java.awt.Point;
/**
*
* @author Matze
*/
public class MapleFoothold implements Comparable<MapleFoothold> {
private Point p1;
private Point p2;
private int id;
private int next, prev;
public MapleFoothold(Point p1, Point p2, int id) {
this.p1 = p1;
this.p2 = p2;
this.id = id;
}
public boolean isWall() {
return p1.x == p2.x;
}
public int getX1() {
return p1.x;
}
public int getX2() {
return p2.x;
}
public int getY1() {
return p1.y;
}
public int getY2() {
return p2.y;
}
// XXX may need more precision
public int calculateFooting(int x) {
if (p1.y == p2.y) {
return p2.y; // y at both ends is the same
}
int slope = (p1.y - p2.y) / (p1.x - p2.x);
int intercept = p1.y - (slope * p1.x);
return (slope * x) + intercept;
}
@Override
public int compareTo(MapleFoothold o) {
MapleFoothold other = o;
if (p2.y < other.getY1()) {
return -1;
} else if (p1.y > other.getY2()) {
return 1;
} else {
return 0;
}
}
public int getId() {
return id;
}
public int getNext() {
return next;
}
public void setNext(int next) {
this.next = next;
}
public int getPrev() {
return prev;
}
public void setPrev(int prev) {
this.prev = prev;
}
}

View File

@@ -0,0 +1,222 @@
/*
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 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.maps;
import java.awt.Point;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
*
* @author Matze
*/
public class MapleFootholdTree {
private MapleFootholdTree nw = null;
private MapleFootholdTree ne = null;
private MapleFootholdTree sw = null;
private MapleFootholdTree se = null;
private List<MapleFoothold> footholds = new LinkedList<MapleFoothold>();
private Point p1;
private Point p2;
private Point center;
private int depth = 0;
private static int maxDepth = 8;
private int maxDropX;
private int minDropX;
public MapleFootholdTree(Point p1, Point p2) {
this.p1 = p1;
this.p2 = p2;
center = new Point((p2.x - p1.x) / 2, (p2.y - p1.y) / 2);
}
public MapleFootholdTree(Point p1, Point p2, int depth) {
this.p1 = p1;
this.p2 = p2;
this.depth = depth;
center = new Point((p2.x - p1.x) / 2, (p2.y - p1.y) / 2);
}
public void insert(MapleFoothold f) {
if (depth == 0) {
if (f.getX1() > maxDropX) {
maxDropX = f.getX1();
}
if (f.getX1() < minDropX) {
minDropX = f.getX1();
}
if (f.getX2() > maxDropX) {
maxDropX = f.getX2();
}
if (f.getX2() < minDropX) {
minDropX = f.getX2();
}
}
if (depth == maxDepth ||
(f.getX1() >= p1.x && f.getX2() <= p2.x &&
f.getY1() >= p1.y && f.getY2() <= p2.y)) {
footholds.add(f);
} else {
if (nw == null) {
nw = new MapleFootholdTree(p1, center, depth + 1);
ne = new MapleFootholdTree(new Point(center.x, p1.y), new Point(p2.x, center.y), depth + 1);
sw = new MapleFootholdTree(new Point(p1.x, center.y), new Point(center.x, p2.y), depth + 1);
se = new MapleFootholdTree(center, p2, depth + 1);
}
if (f.getX2() <= center.x && f.getY2() <= center.y) {
nw.insert(f);
} else if (f.getX1() > center.x && f.getY2() <= center.y) {
ne.insert(f);
} else if (f.getX2() <= center.x && f.getY1() > center.y) {
sw.insert(f);
} else {
se.insert(f);
}
}
}
private List<MapleFoothold> getRelevants(Point p) {
return getRelevants(p, new LinkedList<MapleFoothold>());
}
private List<MapleFoothold> getRelevants(Point p, List<MapleFoothold> list) {
list.addAll(footholds);
if (nw != null) {
if (p.x <= center.x && p.y <= center.y) {
nw.getRelevants(p, list);
} else if (p.x > center.x && p.y <= center.y) {
ne.getRelevants(p, list);
} else if (p.x <= center.x && p.y > center.y) {
sw.getRelevants(p, list);
} else {
se.getRelevants(p, list);
}
}
return list;
}
private MapleFoothold findWallR(Point p1, Point p2) {
MapleFoothold ret;
for (MapleFoothold f : footholds) {
if (f.isWall() && f.getX1() >= p1.x && f.getX1() <= p2.x &&
f.getY1() >= p1.y && f.getY2() <= p1.y) {
return f;
}
}
if (nw != null) {
if (p1.x <= center.x && p1.y <= center.y) {
ret = nw.findWallR(p1, p2);
if (ret != null) {
return ret;
}
}
if ((p1.x > center.x || p2.x > center.x) && p1.y <= center.y) {
ret = ne.findWallR(p1, p2);
if (ret != null) {
return ret;
}
}
if (p1.x <= center.x && p1.y > center.y) {
ret = sw.findWallR(p1, p2);
if (ret != null) {
return ret;
}
}
if ((p1.x > center.x || p2.x > center.x) && p1.y > center.y) {
ret = se.findWallR(p1, p2);
if (ret != null) {
return ret;
}
}
}
return null;
}
public MapleFoothold findWall(Point p1, Point p2) {
if (p1.y != p2.y) {
throw new IllegalArgumentException();
}
return findWallR(p1, p2);
}
public MapleFoothold findBelow(Point p) {
List<MapleFoothold> relevants = getRelevants(p);
List<MapleFoothold> xMatches = new LinkedList<MapleFoothold>();
for (MapleFoothold fh : relevants) {
if (fh.getX1() <= p.x && fh.getX2() >= p.x) {
xMatches.add(fh);
}
}
Collections.sort(xMatches);
for (MapleFoothold fh : xMatches) {
if (!fh.isWall()) {
if (fh.getY1() != fh.getY2()) {
int calcY;
double s1 = Math.abs(fh.getY2() - fh.getY1());
double s2 = Math.abs(fh.getX2() - fh.getX1());
double s4 = Math.abs(p.x - fh.getX1());
double alpha = Math.atan(s2 / s1);
double beta = Math.atan(s1 / s2);
double s5 = Math.cos(alpha) * (s4 / Math.cos(beta));
if (fh.getY2() < fh.getY1()) {
calcY = fh.getY1() - (int) s5;
} else {
calcY = fh.getY1() + (int) s5;
}
if (calcY >= p.y) {
return fh;
}
} else {
if (fh.getY1() >= p.y) {
return fh;
}
}
}
}
return null;
}
public int getX1() {
return p1.x;
}
public int getX2() {
return p2.x;
}
public int getY1() {
return p1.y;
}
public int getY2() {
return p2.y;
}
public int getMaxDropX() {
return maxDropX;
}
public int getMinDropX() {
return minDropX;
}
}

View File

@@ -0,0 +1,171 @@
/*
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 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.maps;
import client.MapleClient;
import client.MapleCharacter;
import constants.game.GameConstants;
import java.awt.Point;
import scripting.portal.PortalScriptManager;
import tools.MaplePacketCreator;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReentrantLock;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
public class MapleGenericPortal implements MaplePortal {
private String name;
private String target;
private Point position;
private int targetmap;
private int type;
private boolean status = true;
private int id;
private String scriptName;
private boolean portalState;
private MonitoredReentrantLock scriptLock = null;
public MapleGenericPortal(int type) {
this.type = type;
}
@Override
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public String getName() {
return name;
}
@Override
public Point getPosition() {
return position;
}
@Override
public String getTarget() {
return target;
}
@Override
public void setPortalStatus(boolean newStatus) {
this.status = newStatus;
}
@Override
public boolean getPortalStatus() {
return status;
}
@Override
public int getTargetMapId() {
return targetmap;
}
@Override
public int getType() {
return type;
}
@Override
public String getScriptName() {
return scriptName;
}
public void setName(String name) {
this.name = name;
}
public void setPosition(Point position) {
this.position = position;
}
public void setTarget(String target) {
this.target = target;
}
public void setTargetMapId(int targetmapid) {
this.targetmap = targetmapid;
}
@Override
public void setScriptName(String scriptName) {
this.scriptName = scriptName;
if(scriptName != null) {
if(scriptLock == null) {
scriptLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.PORTAL, true);
}
} else {
scriptLock = null;
}
}
@Override
public void enterPortal(MapleClient c) {
boolean changed = false;
if (getScriptName() != null) {
try {
scriptLock.lock();
try {
changed = PortalScriptManager.getInstance().executePortalScript(this, c);
} finally {
scriptLock.unlock();
}
} catch(NullPointerException npe) {
npe.printStackTrace();
}
} else if (getTargetMapId() != 999999999) {
MapleCharacter chr = c.getPlayer();
if (!(chr.getChalkboard() != null && GameConstants.isFreeMarketRoom(getTargetMapId()))) {
MapleMap to = chr.getEventInstance() == null ? c.getChannelServer().getMapFactory().getMap(getTargetMapId()) : chr.getEventInstance().getMapInstance(getTargetMapId());
MaplePortal pto = to.getPortal(getTarget());
if (pto == null) {// fallback for missing portals - no real life case anymore - interesting for not implemented areas
pto = to.getPortal(0);
}
chr.changeMap(to, pto); //late resolving makes this harder but prevents us from loading the whole world at once
changed = true;
} else {
chr.dropMessage(5, "You cannot enter this map with the chalkboard opened.");
}
}
if (!changed) {
c.announce(MaplePacketCreator.enableActions());
}
}
@Override
public void setPortalState(boolean state) {
this.portalState = state;
}
@Override
public boolean getPortalState() {
return portalState;
}
}

View File

@@ -0,0 +1,759 @@
/*
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 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.maps;
import client.MapleCharacter;
import client.MapleClient;
import client.inventory.Item;
import client.inventory.ItemFactory;
import client.inventory.MapleInventory;
import client.inventory.MapleInventoryType;
import client.inventory.manipulator.MapleInventoryManipulator;
import client.inventory.manipulator.MapleKarmaManipulator;
import client.processor.npc.FredrickProcessor;
import com.mysql.jdbc.Statement;
import config.YamlConfig;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import net.server.Server;
import server.MapleItemInformationProvider;
import tools.DatabaseConnection;
import tools.MaplePacketCreator;
import tools.Pair;
import net.server.audit.locks.MonitoredLockType;
import server.MapleTrade;
/**
*
* @author XoticStory
* @author Ronan - concurrency protection
*/
public class MapleHiredMerchant extends AbstractMapleMapObject {
private int ownerId, itemId, mesos = 0;
private int channel, world;
private long start;
private String ownerName = "";
private String description = "";
private MapleCharacter[] visitors = new MapleCharacter[3];
private final List<MaplePlayerShopItem> items = new LinkedList<>();
private List<Pair<String, Byte>> messages = new LinkedList<>();
private List<SoldItem> sold = new LinkedList<>();
private AtomicBoolean open = new AtomicBoolean();
private boolean published = false;
private MapleMap map;
private Lock visitorLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.VISITOR_MERCH, true);
public MapleHiredMerchant(final MapleCharacter owner, String desc, int itemId) {
this.setPosition(owner.getPosition());
this.start = System.currentTimeMillis();
this.ownerId = owner.getId();
this.channel = owner.getClient().getChannel();
this.world = owner.getWorld();
this.itemId = itemId;
this.ownerName = owner.getName();
this.description = desc;
this.map = owner.getMap();
}
public void broadcastToVisitorsThreadsafe(final byte[] packet) {
visitorLock.lock();
try {
broadcastToVisitors(packet);
} finally {
visitorLock.unlock();
}
}
private void broadcastToVisitors(final byte[] packet) {
for (MapleCharacter visitor : visitors) {
if (visitor != null) {
visitor.getClient().announce(packet);
}
}
}
public byte[] getShopRoomInfo() {
visitorLock.lock();
try {
byte count = 0;
if (this.isOpen()) {
for (MapleCharacter visitor : visitors) {
if (visitor != null) {
count++;
}
}
} else {
count = (byte) (visitors.length + 1);
}
return new byte[]{count, (byte) (visitors.length + 1)};
} finally {
visitorLock.unlock();
}
}
public boolean addVisitor(MapleCharacter visitor) {
visitorLock.lock();
try {
int i = this.getFreeSlot();
if (i > -1) {
visitors[i] = visitor;
broadcastToVisitors(MaplePacketCreator.hiredMerchantVisitorAdd(visitor, i + 1));
this.getMap().broadcastMessage(MaplePacketCreator.updateHiredMerchantBox(this));
return true;
}
return false;
} finally {
visitorLock.unlock();
}
}
public void removeVisitor(MapleCharacter visitor) {
visitorLock.lock();
try {
int slot = getVisitorSlot(visitor);
if (slot < 0) { //Not found
return;
}
if (visitors[slot] != null && visitors[slot].getId() == visitor.getId()) {
visitors[slot] = null;
broadcastToVisitors(MaplePacketCreator.hiredMerchantVisitorLeave(slot + 1));
this.getMap().broadcastMessage(MaplePacketCreator.updateHiredMerchantBox(this));
}
} finally {
visitorLock.unlock();
}
}
public int getVisitorSlotThreadsafe(MapleCharacter visitor) {
visitorLock.lock();
try {
return getVisitorSlot(visitor);
} finally {
visitorLock.unlock();
}
}
private int getVisitorSlot(MapleCharacter visitor) {
for (int i = 0; i < 3; i++) {
if (visitors[i] != null && visitors[i].getId() == visitor.getId()){
return i;
}
}
return -1; //Actually 0 because of the +1's.
}
private void removeAllVisitors() {
visitorLock.lock();
try {
for (int i = 0; i < 3; i++) {
MapleCharacter visitor = visitors[i];
if (visitor != null) {
visitor.setHiredMerchant(null);
visitor.getClient().announce(MaplePacketCreator.leaveHiredMerchant(i + 1, 0x11));
visitor.getClient().announce(MaplePacketCreator.hiredMerchantMaintenanceMessage());
visitors[i] = null;
}
}
this.getMap().broadcastMessage(MaplePacketCreator.updateHiredMerchantBox(this));
} finally {
visitorLock.unlock();
}
}
private void removeOwner(MapleCharacter owner) {
if (owner.getHiredMerchant() == this) {
owner.announce(MaplePacketCreator.hiredMerchantOwnerLeave());
owner.announce(MaplePacketCreator.leaveHiredMerchant(0x00, 0x03));
owner.setHiredMerchant(null);
}
}
public void withdrawMesos(MapleCharacter chr) {
if (isOwner(chr)) {
synchronized (items) {
chr.withdrawMerchantMesos();
}
}
}
public void takeItemBack(int slot, MapleCharacter chr) {
synchronized (items) {
MaplePlayerShopItem shopItem = items.get(slot);
if(shopItem.isExist()) {
if (shopItem.getBundles() > 0) {
Item iitem = shopItem.getItem().copy();
iitem.setQuantity((short) (shopItem.getItem().getQuantity() * shopItem.getBundles()));
if (!MapleInventory.checkSpot(chr, iitem)) {
chr.announce(MaplePacketCreator.serverNotice(1, "Have a slot available on your inventory to claim back the item."));
chr.announce(MaplePacketCreator.enableActions());
return;
}
MapleInventoryManipulator.addFromDrop(chr.getClient(), iitem, true);
}
removeFromSlot(slot);
chr.announce(MaplePacketCreator.updateHiredMerchant(this, chr));
}
if (YamlConfig.config.server.USE_ENFORCE_MERCHANT_SAVE) {
chr.saveCharToDB(false);
}
}
}
private static boolean canBuy(MapleClient c, Item newItem) { // thanks xiaokelvin (Conrad) for noticing a leaked test code here
return MapleInventoryManipulator.checkSpace(c, newItem.getItemId(), newItem.getQuantity(), newItem.getOwner()) && MapleInventoryManipulator.addFromDrop(c, newItem, false);
}
private int getQuantityLeft(int itemid) {
synchronized (items) {
int count = 0;
for (MaplePlayerShopItem mpsi : items) {
if (mpsi.getItem().getItemId() == itemid) {
count += (mpsi.getBundles() * mpsi.getItem().getQuantity());
}
}
return count;
}
}
public void buy(MapleClient c, int item, short quantity) {
synchronized (items) {
MaplePlayerShopItem pItem = items.get(item);
Item newItem = pItem.getItem().copy();
newItem.setQuantity((short) ((pItem.getItem().getQuantity() * quantity)));
if (quantity < 1 || !pItem.isExist() || pItem.getBundles() < quantity) {
c.announce(MaplePacketCreator.enableActions());
return;
} else if (newItem.getInventoryType().equals(MapleInventoryType.EQUIP) && newItem.getQuantity() > 1) {
c.announce(MaplePacketCreator.enableActions());
return;
}
MapleKarmaManipulator.toggleKarmaFlagToUntradeable(newItem);
int price = (int) Math.min((float) pItem.getPrice() * quantity, Integer.MAX_VALUE);
if (c.getPlayer().getMeso() >= price) {
if (canBuy(c, newItem)) {
c.getPlayer().gainMeso(-price, false);
price -= MapleTrade.getFee(price); // thanks BHB for pointing out trade fees not applying here
synchronized (sold) {
sold.add(new SoldItem(c.getPlayer().getName(), pItem.getItem().getItemId(), newItem.getQuantity(), price));
}
pItem.setBundles((short) (pItem.getBundles() - quantity));
if (pItem.getBundles() < 1) {
pItem.setDoesExist(false);
}
if(YamlConfig.config.server.USE_ANNOUNCE_SHOPITEMSOLD) { // idea thanks to Vcoc
announceItemSold(newItem, price, getQuantityLeft(pItem.getItem().getItemId()));
}
MapleCharacter owner = Server.getInstance().getWorld(world).getPlayerStorage().getCharacterByName(ownerName);
if (owner != null) {
owner.addMerchantMesos(price);
} else {
try {
Connection con = DatabaseConnection.getConnection();
long merchantMesos = 0;
try (PreparedStatement ps = con.prepareStatement("SELECT MerchantMesos FROM characters WHERE id = ?")) {
ps.setInt(1, ownerId);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
merchantMesos = rs.getInt(1);
}
}
}
merchantMesos += price;
try (PreparedStatement ps = con.prepareStatement("UPDATE characters SET MerchantMesos = ? WHERE id = ?", Statement.RETURN_GENERATED_KEYS)) {
ps.setInt(1, (int) Math.min(merchantMesos, Integer.MAX_VALUE));
ps.setInt(2, ownerId);
ps.executeUpdate();
}
con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
} else {
c.getPlayer().dropMessage(1, "Your inventory is full. Please clear a slot before buying this item.");
c.announce(MaplePacketCreator.enableActions());
return;
}
} else {
c.getPlayer().dropMessage(1, "You don't have enough mesos to purchase this item.");
c.announce(MaplePacketCreator.enableActions());
return;
}
try {
this.saveItems(false);
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void announceItemSold(Item item, int mesos, int inStore) {
String qtyStr = (item.getQuantity() > 1) ? " x " + item.getQuantity() : "";
MapleCharacter player = Server.getInstance().getWorld(world).getPlayerStorage().getCharacterById(ownerId);
if(player != null && player.isLoggedinWorld()) {
player.dropMessage(6, "[Hired Merchant] Item '" + MapleItemInformationProvider.getInstance().getName(item.getItemId()) + "'" + qtyStr + " has been sold for " + mesos + " mesos. (" + inStore + " left)");
}
}
public void forceClose() {
//Server.getInstance().getChannel(world, channel).removeHiredMerchant(ownerId);
map.broadcastMessage(MaplePacketCreator.removeHiredMerchantBox(getOwnerId()));
map.removeMapObject(this);
MapleCharacter owner = Server.getInstance().getWorld(world).getPlayerStorage().getCharacterById(ownerId);
visitorLock.lock();
try {
setOpen(false);
removeAllVisitors();
if(owner != null && owner.isLoggedinWorld() && this == owner.getHiredMerchant()) {
closeOwnerMerchant(owner);
}
} finally {
visitorLock.unlock();
}
Server.getInstance().getWorld(world).unregisterHiredMerchant(this);
try {
saveItems(true);
synchronized (items) {
items.clear();
}
} catch (SQLException ex) {
ex.printStackTrace();
}
MapleCharacter player = Server.getInstance().getWorld(world).getPlayerStorage().getCharacterById(ownerId);
if(player != null) {
player.setHasMerchant(false);
} else {
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("UPDATE characters SET HasMerchant = 0 WHERE id = ?", Statement.RETURN_GENERATED_KEYS);
ps.setInt(1, ownerId);
ps.executeUpdate();
ps.close();
con.close();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
map = null;
}
public void closeOwnerMerchant(MapleCharacter chr) {
if(this.isOwner(chr)) {
this.closeShop(chr.getClient(), false);
chr.setHasMerchant(false);
}
}
private void closeShop(MapleClient c, boolean timeout) {
map.removeMapObject(this);
map.broadcastMessage(MaplePacketCreator.removeHiredMerchantBox(ownerId));
c.getChannelServer().removeHiredMerchant(ownerId);
this.removeAllVisitors();
this.removeOwner(c.getPlayer());
try {
List<MaplePlayerShopItem> copyItems = getItems();
if (check(c.getPlayer(), copyItems) && !timeout) {
for (MaplePlayerShopItem mpsi : copyItems) {
if(mpsi.isExist()) {
if (mpsi.getItem().getInventoryType().equals(MapleInventoryType.EQUIP)) {
MapleInventoryManipulator.addFromDrop(c, mpsi.getItem(), false);
} else {
MapleInventoryManipulator.addById(c, mpsi.getItem().getItemId(), (short) (mpsi.getBundles() * mpsi.getItem().getQuantity()), mpsi.getItem().getOwner(), -1, mpsi.getItem().getFlag(), mpsi.getItem().getExpiration());
}
}
}
synchronized (items) {
items.clear();
}
}
try {
this.saveItems(timeout);
} catch (Exception e) {
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);
}
synchronized (items) {
items.clear();
}
} catch (Exception e) {
e.printStackTrace();
}
Server.getInstance().getWorld(world).unregisterHiredMerchant(this);
}
public synchronized void visitShop(MapleCharacter chr) {
visitorLock.lock();
try {
if (this.isOwner(chr)) {
this.setOpen(false);
this.removeAllVisitors();
chr.announce(MaplePacketCreator.getHiredMerchant(chr, this, false));
} else if (!this.isOpen()) {
chr.announce(MaplePacketCreator.getMiniRoomError(18));
return;
} else if (!this.addVisitor(chr)) {
chr.announce(MaplePacketCreator.getMiniRoomError(2));
return;
} else {
chr.announce(MaplePacketCreator.getHiredMerchant(chr, this, false));
}
chr.setHiredMerchant(this);
} finally {
visitorLock.unlock();
}
}
public String getOwner() {
return ownerName;
}
public void clearItems() {
synchronized (items) {
items.clear();
}
}
public int getOwnerId() {
return ownerId;
}
public String getDescription() {
return description;
}
public MapleCharacter[] getVisitors() {
visitorLock.lock();
try {
MapleCharacter[] copy = new MapleCharacter[3];
for(int i = 0; i < visitors.length; i++) copy[i] = visitors[i];
return copy;
} finally {
visitorLock.unlock();
}
}
public List<MaplePlayerShopItem> getItems() {
synchronized (items) {
return Collections.unmodifiableList(items);
}
}
public boolean hasItem(int itemid) {
for(MaplePlayerShopItem mpsi : getItems()) {
if(mpsi.getItem().getItemId() == itemid && mpsi.isExist() && mpsi.getBundles() > 0) {
return true;
}
}
return false;
}
public boolean addItem(MaplePlayerShopItem item) {
synchronized (items) {
if (items.size() >= 16) return false;
items.add(item);
return true;
}
}
public void clearInexistentItems() {
synchronized(items) {
for (int i = items.size() - 1; i >= 0; i--) {
if (!items.get(i).isExist()) {
items.remove(i);
}
}
try {
this.saveItems(false);
} catch (SQLException ex) {
ex.printStackTrace();
}
}
}
private void removeFromSlot(int slot) {
items.remove(slot);
try {
this.saveItems(false);
} catch (SQLException ex) {
ex.printStackTrace();
}
}
private int getFreeSlot() {
for (int i = 0; i < 3; i++) {
if (visitors[i] == null) {
return i;
}
}
return -1;
}
public void setDescription(String description) {
this.description = description;
}
public boolean isPublished() {
return published;
}
public boolean isOpen() {
return open.get();
}
public void setOpen(boolean set) {
open.getAndSet(set);
published = true;
}
public int getItemId() {
return itemId;
}
public boolean isOwner(MapleCharacter chr) {
return chr.getId() == ownerId;
}
public void sendMessage(MapleCharacter chr, String msg) {
String message = chr.getName() + " : " + msg;
byte slot = (byte) (getVisitorSlot(chr) + 1);
synchronized (messages) {
messages.add(new Pair<>(message, slot));
}
broadcastToVisitorsThreadsafe(MaplePacketCreator.hiredMerchantChat(message, slot));
}
public List<MaplePlayerShopItem> sendAvailableBundles(int itemid) {
List<MaplePlayerShopItem> list = new LinkedList<>();
List<MaplePlayerShopItem> all = new ArrayList<>();
if(!open.get()) return list;
synchronized (items) {
for(MaplePlayerShopItem mpsi : items) all.add(mpsi);
}
for(MaplePlayerShopItem mpsi : all) {
if(mpsi.getItem().getItemId() == itemid && mpsi.getBundles() > 0 && mpsi.isExist()) {
list.add(mpsi);
}
}
return list;
}
public void saveItems(boolean shutdown) throws SQLException {
List<Pair<Item, MapleInventoryType>> itemsWithType = new ArrayList<>();
List<Short> bundles = new ArrayList<>();
for (MaplePlayerShopItem pItems : getItems()) {
Item newItem = pItems.getItem();
short newBundle = pItems.getBundles();
if (shutdown) { //is "shutdown" really necessary?
newItem.setQuantity((short) (pItems.getItem().getQuantity()));
} else {
newItem.setQuantity((short) (pItems.getItem().getQuantity()));
}
if (newBundle > 0) {
itemsWithType.add(new Pair<>(newItem, newItem.getInventoryType()));
bundles.add(newBundle);
}
}
Connection con = DatabaseConnection.getConnection();
ItemFactory.MERCHANT.saveItems(itemsWithType, bundles, this.ownerId, con);
con.close();
FredrickProcessor.insertFredrickLog(this.ownerId);
}
private static boolean check(MapleCharacter chr, List<MaplePlayerShopItem> items) {
List<Pair<Item, MapleInventoryType>> li = new ArrayList<>();
for (MaplePlayerShopItem item : items) {
Item it = item.getItem().copy();
it.setQuantity((short)(it.getQuantity() * item.getBundles()));
li.add(new Pair<>(it, it.getInventoryType()));
}
return MapleInventory.checkSpotsAndOwnership(chr, li);
}
public int getChannel() {
return channel;
}
public int getTimeOpen() {
double openTime = (System.currentTimeMillis() - start) / 60000;
openTime /= 1440; // heuristics since engineered method to count time here is unknown
openTime *= 1318;
return (int) Math.ceil(openTime);
}
public void clearMessages() {
synchronized (messages) {
messages.clear();
}
}
public List<Pair<String, Byte>> getMessages() {
synchronized (messages) {
List<Pair<String, Byte>> msgList = new LinkedList<>();
for(Pair<String, Byte> m : messages) {
msgList.add(m);
}
return msgList;
}
}
public int getMapId() {
return map.getId();
}
public MapleMap getMap() {
return map;
}
public List<SoldItem> getSold() {
synchronized (sold) {
return Collections.unmodifiableList(sold);
}
}
public int getMesos() {
return mesos;
}
@Override
public MapleMapObjectType getType() {
return MapleMapObjectType.HIRED_MERCHANT;
}
@Override
public void sendDestroyData(MapleClient client) {}
@Override
public void sendSpawnData(MapleClient client) {
client.announce(MaplePacketCreator.spawnHiredMerchantBox(this));
}
public class SoldItem {
int itemid, mesos;
short quantity;
String buyer;
public SoldItem(String buyer, int itemid, short quantity, int mesos) {
this.buyer = buyer;
this.itemid = itemid;
this.quantity = quantity;
this.mesos = mesos;
}
public String getBuyer() {
return buyer;
}
public int getItemId() {
return itemid;
}
public short getQuantity() {
return quantity;
}
public int getMesos() {
return mesos;
}
}
}

View File

@@ -0,0 +1,60 @@
package server.maps;
import java.awt.Point;
import client.MapleCharacter;
import client.MapleClient;
import tools.MaplePacketCreator;
public class MapleKite extends AbstractMapleMapObject {
private Point pos;
private MapleCharacter owner;
private String text;
private int ft;
private int itemid;
public MapleKite(MapleCharacter owner, String text, int itemid) {
this.owner = owner;
this.pos = owner.getPosition();
this.ft = owner.getFh();
this.text = text;
this.itemid = itemid;
}
@Override
public MapleMapObjectType getType() {
return MapleMapObjectType.KITE;
}
@Override
public Point getPosition() {
return pos.getLocation();
}
public MapleCharacter getOwner() {
return owner;
}
@Override
public void setPosition(Point position) {
throw new UnsupportedOperationException();
}
@Override
public void sendDestroyData(MapleClient client) {
client.announce(makeDestroyData());
}
@Override
public void sendSpawnData(MapleClient client) {
client.announce(makeSpawnData());
}
public final byte[] makeSpawnData() {
return MaplePacketCreator.spawnKite(getObjectId(), itemid, owner.getName(), text, pos, ft);
}
public final byte[] makeDestroyData() {
return MaplePacketCreator.removeKite(getObjectId(), 0);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,48 @@
/*
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 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.maps;
import client.MapleClient;
import tools.MaplePacketCreator;
public class MapleMapEffect {
private String msg;
private int itemId;
private boolean active = true;
public MapleMapEffect(String msg, int itemId) {
this.msg = msg;
this.itemId = itemId;
}
public final byte[] makeDestroyData() {
return MaplePacketCreator.removeMapEffect();
}
public final byte[] makeStartData() {
return MaplePacketCreator.startMapEffect(msg, itemId, active);
}
public void sendStartData(MapleClient client) {
client.announce(makeStartData());
}
}

View File

@@ -0,0 +1,444 @@
/*
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 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.maps;
import java.awt.Point;
import java.awt.Rectangle;
import java.io.File;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import server.life.AbstractLoadedMapleLife;
import server.life.MapleLifeFactory;
import server.life.MapleMonster;
import server.life.MaplePlayerNPC;
import server.life.MaplePlayerNPCFactory;
import scripting.event.EventInstanceManager;
import server.partyquest.GuardianSpawnPoint;
import tools.DatabaseConnection;
import tools.StringUtil;
public class MapleMapFactory {
private static MapleData nameData;
private static MapleDataProvider mapSource;
static {
nameData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/String.wz")).getData("Map.img");
mapSource = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Map.wz"));
}
private static void loadLifeFromWz(MapleMap map, MapleData mapData) {
for (MapleData life : mapData.getChildByPath("life")) {
life.getName();
String id = MapleDataTool.getString(life.getChildByPath("id"));
String type = MapleDataTool.getString(life.getChildByPath("type"));
int team = MapleDataTool.getInt("team", life, -1);
if (map.isCPQMap2() && type.equals("m")) {
if ((Integer.parseInt(life.getName()) % 2) == 0) {
team = 0;
} else {
team = 1;
}
}
int cy = MapleDataTool.getInt(life.getChildByPath("cy"));
MapleData dF = life.getChildByPath("f");
int f = (dF != null) ? MapleDataTool.getInt(dF) : 0;
int fh = MapleDataTool.getInt(life.getChildByPath("fh"));
int rx0 = MapleDataTool.getInt(life.getChildByPath("rx0"));
int rx1 = MapleDataTool.getInt(life.getChildByPath("rx1"));
int x = MapleDataTool.getInt(life.getChildByPath("x"));
int y = MapleDataTool.getInt(life.getChildByPath("y"));
int hide = MapleDataTool.getInt("hide", life, 0);
int mobTime = MapleDataTool.getInt("mobTime", life, 0);
loadLifeRaw(map, Integer.parseInt(id), type, cy, f, fh, rx0, rx1, x, y, hide, mobTime, team);
}
}
private static void loadLifeFromDb(MapleMap map) {
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM plife WHERE map = ? and world = ?");
ps.setInt(1, map.getId());
ps.setInt(2, map.getWorld());
ResultSet rs = ps.executeQuery();
while (rs.next()) {
int id = rs.getInt("life");
String type = rs.getString("type");
int cy = rs.getInt("cy");
int f = rs.getInt("f");
int fh = rs.getInt("fh");
int rx0 = rs.getInt("rx0");
int rx1 = rs.getInt("rx1");
int x = rs.getInt("x");
int y = rs.getInt("y");
int hide = rs.getInt("hide");
int mobTime = rs.getInt("mobtime");
int team = rs.getInt("team");
loadLifeRaw(map, id, type, cy, f, fh, rx0, rx1, x, y, hide, mobTime, team);
}
rs.close();
ps.close();
con.close();
} catch (SQLException sqle) {
sqle.printStackTrace();
}
}
private static void loadLifeRaw(MapleMap map, int id, String type, int cy, int f, int fh, int rx0, int rx1, int x, int y, int hide, int mobTime, int team) {
AbstractLoadedMapleLife myLife = loadLife(id, type, cy, f, fh, rx0, rx1, x, y, hide);
if (myLife instanceof MapleMonster) {
MapleMonster monster = (MapleMonster) myLife;
if (mobTime == -1) { //does not respawn, force spawn once
map.spawnMonster(monster);
} else {
map.addMonsterSpawn(monster, mobTime, team);
}
//should the map be reseted, use allMonsterSpawn list of monsters to spawn them again
map.addAllMonsterSpawn(monster, mobTime, team);
} else {
map.addMapObject(myLife);
}
}
public static MapleMap loadMapFromWz(int mapid, int world, int channel, EventInstanceManager event) {
MapleMap map;
String mapName = getMapName(mapid);
MapleData mapData = mapSource.getData(mapName); // source.getData issue with giving nulls in rare ocasions found thanks to MedicOP
MapleData infoData = mapData.getChildByPath("info");
String link = MapleDataTool.getString(infoData.getChildByPath("link"), "");
if (!link.equals("")) { //nexon made hundreds of dojo maps so to reduce the size they added links.
mapName = getMapName(Integer.parseInt(link));
mapData = mapSource.getData(mapName);
}
float monsterRate = 0;
MapleData mobRate = infoData.getChildByPath("mobRate");
if (mobRate != null) {
monsterRate = ((Float) mobRate.getData()).floatValue();
}
map = new MapleMap(mapid, world, channel, MapleDataTool.getInt("returnMap", infoData), monsterRate);
map.setEventInstance(event);
String onFirstEnter = MapleDataTool.getString(infoData.getChildByPath("onFirstUserEnter"), String.valueOf(mapid));
map.setOnFirstUserEnter(onFirstEnter.equals("") ? String.valueOf(mapid) : onFirstEnter);
String onEnter = MapleDataTool.getString(infoData.getChildByPath("onUserEnter"), String.valueOf(mapid));
map.setOnUserEnter(onEnter.equals("") ? String.valueOf(mapid) : onEnter);
map.setFieldLimit(MapleDataTool.getInt(infoData.getChildByPath("fieldLimit"), 0));
map.setMobInterval((short) MapleDataTool.getInt(infoData.getChildByPath("createMobInterval"), 5000));
MaplePortalFactory portalFactory = new MaplePortalFactory();
for (MapleData portal : mapData.getChildByPath("portal")) {
map.addPortal(portalFactory.makePortal(MapleDataTool.getInt(portal.getChildByPath("pt")), portal));
}
MapleData timeMob = infoData.getChildByPath("timeMob");
if (timeMob != null) {
map.setTimeMob(MapleDataTool.getInt(timeMob.getChildByPath("id")), MapleDataTool.getString(timeMob.getChildByPath("message")));
}
int bounds[] = new int[4];
bounds[0] = MapleDataTool.getInt(infoData.getChildByPath("VRTop"));
bounds[1] = MapleDataTool.getInt(infoData.getChildByPath("VRBottom"));
if (bounds[0] == bounds[1]) { // old-style baked map
MapleData minimapData = mapData.getChildByPath("miniMap");
if (minimapData != null) {
bounds[0] = MapleDataTool.getInt(minimapData.getChildByPath("centerX")) * -1;
bounds[1] = MapleDataTool.getInt(minimapData.getChildByPath("centerY")) * -1;
bounds[2] = MapleDataTool.getInt(minimapData.getChildByPath("height"));
bounds[3] = MapleDataTool.getInt(minimapData.getChildByPath("width"));
map.setMapPointBoundings(bounds[0], bounds[1], bounds[2], bounds[3]);
} else {
int dist = (1 << 18);
map.setMapPointBoundings(-dist / 2, -dist / 2, dist, dist);
}
} else {
bounds[2] = MapleDataTool.getInt(infoData.getChildByPath("VRLeft"));
bounds[3] = MapleDataTool.getInt(infoData.getChildByPath("VRRight"));
map.setMapLineBoundings(bounds[0], bounds[1], bounds[2], bounds[3]);
}
List<MapleFoothold> allFootholds = new LinkedList<>();
Point lBound = new Point();
Point uBound = new Point();
for (MapleData footRoot : mapData.getChildByPath("foothold")) {
for (MapleData footCat : footRoot) {
for (MapleData footHold : footCat) {
int x1 = MapleDataTool.getInt(footHold.getChildByPath("x1"));
int y1 = MapleDataTool.getInt(footHold.getChildByPath("y1"));
int x2 = MapleDataTool.getInt(footHold.getChildByPath("x2"));
int y2 = MapleDataTool.getInt(footHold.getChildByPath("y2"));
MapleFoothold fh = new MapleFoothold(new Point(x1, y1), new Point(x2, y2), Integer.parseInt(footHold.getName()));
fh.setPrev(MapleDataTool.getInt(footHold.getChildByPath("prev")));
fh.setNext(MapleDataTool.getInt(footHold.getChildByPath("next")));
if (fh.getX1() < lBound.x) {
lBound.x = fh.getX1();
}
if (fh.getX2() > uBound.x) {
uBound.x = fh.getX2();
}
if (fh.getY1() < lBound.y) {
lBound.y = fh.getY1();
}
if (fh.getY2() > uBound.y) {
uBound.y = fh.getY2();
}
allFootholds.add(fh);
}
}
}
MapleFootholdTree fTree = new MapleFootholdTree(lBound, uBound);
for (MapleFoothold fh : allFootholds) {
fTree.insert(fh);
}
map.setFootholds(fTree);
if (mapData.getChildByPath("area") != null) {
for (MapleData area : mapData.getChildByPath("area")) {
int x1 = MapleDataTool.getInt(area.getChildByPath("x1"));
int y1 = MapleDataTool.getInt(area.getChildByPath("y1"));
int x2 = MapleDataTool.getInt(area.getChildByPath("x2"));
int y2 = MapleDataTool.getInt(area.getChildByPath("y2"));
map.addMapleArea(new Rectangle(x1, y1, (x2 - x1), (y2 - y1)));
}
}
if (mapData.getChildByPath("seat") != null) {
int seats = mapData.getChildByPath("seat").getChildren().size();
map.setSeats(seats);
}
if (event == null) {
try {
Connection con = DatabaseConnection.getConnection();
try (PreparedStatement ps = con.prepareStatement("SELECT * FROM playernpcs WHERE map = ? AND world = ?")) {
ps.setInt(1, mapid);
ps.setInt(2, world);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
map.addPlayerNPCMapObject(new MaplePlayerNPC(rs));
}
}
}
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
List<MaplePlayerNPC> dnpcs = MaplePlayerNPCFactory.getDeveloperNpcsFromMapid(mapid);
if (dnpcs != null) {
for (MaplePlayerNPC dnpc : dnpcs) {
map.addPlayerNPCMapObject(dnpc);
}
}
}
loadLifeFromWz(map, mapData);
loadLifeFromDb(map);
if (map.isCPQMap()) {
MapleData mcData = mapData.getChildByPath("monsterCarnival");
if (mcData != null) {
map.setDeathCP(MapleDataTool.getIntConvert("deathCP", mcData, 0));
map.setMaxMobs(MapleDataTool.getIntConvert("mobGenMax", mcData, 20)); // thanks Atoot for noticing CPQ1 bf. 3 and 4 not accepting spawns due to undefined limits, Lame for noticing a need to cap mob spawns even on such undefined limits
map.setTimeDefault(MapleDataTool.getIntConvert("timeDefault", mcData, 0));
map.setTimeExpand(MapleDataTool.getIntConvert("timeExpand", mcData, 0));
map.setMaxReactors(MapleDataTool.getIntConvert("guardianGenMax", mcData, 16));
MapleData guardianGenData = mcData.getChildByPath("guardianGenPos");
for (MapleData node : guardianGenData.getChildren()) {
GuardianSpawnPoint pt = new GuardianSpawnPoint(new Point(MapleDataTool.getIntConvert("x", node), MapleDataTool.getIntConvert("y", node)));
pt.setTeam(MapleDataTool.getIntConvert("team", node, -1));
pt.setTaken(false);
map.addGuardianSpawnPoint(pt);
}
if (mcData.getChildByPath("skill") != null) {
for (MapleData area : mcData.getChildByPath("skill")) {
map.addSkillId(MapleDataTool.getInt(area));
}
}
if (mcData.getChildByPath("mob") != null) {
for (MapleData area : mcData.getChildByPath("mob")) {
map.addMobSpawn(MapleDataTool.getInt(area.getChildByPath("id")), MapleDataTool.getInt(area.getChildByPath("spendCP")));
}
}
}
}
if (mapData.getChildByPath("reactor") != null) {
for (MapleData reactor : mapData.getChildByPath("reactor")) {
String id = MapleDataTool.getString(reactor.getChildByPath("id"));
if (id != null) {
MapleReactor newReactor = loadReactor(reactor, id, (byte) MapleDataTool.getInt(reactor.getChildByPath("f"), 0));
map.spawnReactor(newReactor);
}
}
}
map.setMapName(loadPlaceName(mapid));
map.setStreetName(loadStreetName(mapid));
map.setClock(mapData.getChildByPath("clock") != null);
map.setEverlast(MapleDataTool.getIntConvert("everlast", infoData, 0) != 0); // thanks davidlafriniere for noticing value 0 accounting as true
map.setTown(MapleDataTool.getIntConvert("town", infoData, 0) != 0);
map.setHPDec(MapleDataTool.getIntConvert("decHP", infoData, 0));
map.setHPDecProtect(MapleDataTool.getIntConvert("protectItem", infoData, 0));
map.setForcedReturnMap(MapleDataTool.getInt(infoData.getChildByPath("forcedReturn"), 999999999));
map.setBoat(mapData.getChildByPath("shipObj") != null);
map.setTimeLimit(MapleDataTool.getIntConvert("timeLimit", infoData, -1));
map.setFieldType(MapleDataTool.getIntConvert("fieldType", infoData, 0));
map.setMobCapacity(MapleDataTool.getIntConvert("fixedMobCapacity", infoData, 500));//Is there a map that contains more than 500 mobs?
MapleData recData = infoData.getChildByPath("recovery");
if (recData != null) {
map.setRecovery(MapleDataTool.getFloat(recData));
}
HashMap<Integer, Integer> backTypes = new HashMap<>();
try {
for (MapleData layer : mapData.getChildByPath("back")) { // yolo
int layerNum = Integer.parseInt(layer.getName());
int btype = MapleDataTool.getInt(layer.getChildByPath("type"), 0);
backTypes.put(layerNum, btype);
}
} catch (Exception e) {
e.printStackTrace();
// swallow cause I'm cool
}
map.setBackgroundTypes(backTypes);
map.generateMapDropRangeCache();
return map;
}
private static AbstractLoadedMapleLife loadLife(int id, String type, int cy, int f, int fh, int rx0, int rx1, int x, int y, int hide) {
AbstractLoadedMapleLife myLife = MapleLifeFactory.getLife(id, type);
myLife.setCy(cy);
myLife.setF(f);
myLife.setFh(fh);
myLife.setRx0(rx0);
myLife.setRx1(rx1);
myLife.setPosition(new Point(x, y));
if (hide == 1) {
myLife.setHide(true);
}
return myLife;
}
private static MapleReactor loadReactor(MapleData reactor, String id, final byte FacingDirection) {
MapleReactor myReactor = new MapleReactor(MapleReactorFactory.getReactor(Integer.parseInt(id)), Integer.parseInt(id));
int x = MapleDataTool.getInt(reactor.getChildByPath("x"));
int y = MapleDataTool.getInt(reactor.getChildByPath("y"));
myReactor.setFacingDirection(FacingDirection);
myReactor.setPosition(new Point(x, y));
myReactor.setDelay(MapleDataTool.getInt(reactor.getChildByPath("reactorTime")) * 1000);
myReactor.setName(MapleDataTool.getString(reactor.getChildByPath("name"), ""));
myReactor.resetReactorActions(0);
return myReactor;
}
private static String getMapName(int mapid) {
String mapName = StringUtil.getLeftPaddedStr(Integer.toString(mapid), '0', 9);
StringBuilder builder = new StringBuilder("Map/Map");
int area = mapid / 100000000;
builder.append(area);
builder.append("/");
builder.append(mapName);
builder.append(".img");
mapName = builder.toString();
return mapName;
}
private static String getMapStringName(int mapid) {
StringBuilder builder = new StringBuilder();
if (mapid < 100000000) {
builder.append("maple");
} else if (mapid >= 100000000 && mapid < 200000000) {
builder.append("victoria");
} else if (mapid >= 200000000 && mapid < 300000000) {
builder.append("ossyria");
} else if (mapid >= 300000000 && mapid < 400000000) {
builder.append("elin");
} else if (mapid >= 540000000 && mapid < 560000000) {
builder.append("singapore");
} else if (mapid >= 600000000 && mapid < 620000000) {
builder.append("MasteriaGL");
} else if (mapid >= 677000000 && mapid < 677100000) {
builder.append("Episode1GL");
} else if (mapid >= 670000000 && mapid < 682000000) {
if ((mapid >= 674030000 && mapid < 674040000) || (mapid >= 680100000 && mapid < 680200000)) {
builder.append("etc");
} else {
builder.append("weddingGL");
}
} else if (mapid >= 682000000 && mapid < 683000000) {
builder.append("HalloweenGL");
} else if (mapid >= 683000000 && mapid < 684000000) {
builder.append("event");
} else if (mapid >= 800000000 && mapid < 900000000) {
if ((mapid >= 889100000 && mapid < 889200000)) {
builder.append("etc");
} else {
builder.append("jp");
}
} else {
builder.append("etc");
}
builder.append("/").append(mapid);
return builder.toString();
}
public static String loadPlaceName(int mapid) {
try {
return MapleDataTool.getString("mapName", nameData.getChildByPath(getMapStringName(mapid)), "");
} catch (Exception e) {
return "";
}
}
public static String loadStreetName(int mapid) {
try {
return MapleDataTool.getString("streetName", nameData.getChildByPath(getMapStringName(mapid)), "");
} catch (Exception e) {
return "";
}
}
}

View File

@@ -0,0 +1,216 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 ~ 2010 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/>.
*/
package server.maps;
import client.MapleCharacter;
import client.MapleClient;
import client.inventory.Item;
import java.awt.Point;
import java.util.concurrent.locks.Lock;
import tools.MaplePacketCreator;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
public class MapleMapItem extends AbstractMapleMapObject {
protected MapleClient ownerClient;
protected Item item;
protected MapleMapObject dropper;
protected int character_ownerid, party_ownerid, meso, questid = -1;
protected byte type;
protected boolean pickedUp = false, playerDrop, partyDrop;
protected long dropTime;
private Lock itemLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MAP_ITEM);
public MapleMapItem(Item item, Point position, MapleMapObject dropper, MapleCharacter owner, MapleClient ownerClient, byte type, boolean playerDrop) {
setPosition(position);
this.item = item;
this.dropper = dropper;
this.character_ownerid = owner.getId();
this.party_ownerid = owner.getPartyId();
this.partyDrop = this.party_ownerid != -1;
this.ownerClient = owner.getClient();
this.meso = 0;
this.type = type;
this.playerDrop = playerDrop;
}
public MapleMapItem(Item item, Point position, MapleMapObject dropper, MapleCharacter owner, MapleClient ownerClient, byte type, boolean playerDrop, int questid) {
setPosition(position);
this.item = item;
this.dropper = dropper;
this.character_ownerid = owner.getId();
this.party_ownerid = owner.getPartyId();
this.partyDrop = this.party_ownerid != -1;
this.ownerClient = owner.getClient();
this.meso = 0;
this.type = type;
this.playerDrop = playerDrop;
this.questid = questid;
}
public MapleMapItem(int meso, Point position, MapleMapObject dropper, MapleCharacter owner, MapleClient ownerClient, byte type, boolean playerDrop) {
setPosition(position);
this.item = null;
this.dropper = dropper;
this.character_ownerid = owner.getId();
this.party_ownerid = owner.getPartyId();
this.partyDrop = this.party_ownerid != -1;
this.ownerClient = owner.getClient();
this.meso = meso;
this.type = type;
this.playerDrop = playerDrop;
}
public final Item getItem() {
return item;
}
public final int getQuest() {
return questid;
}
public final int getItemId() {
if (meso > 0) return meso;
return item.getItemId();
}
public final MapleMapObject getDropper() {
return dropper;
}
public final int getOwnerId() {
return character_ownerid;
}
public final int getPartyOwnerId() {
return party_ownerid;
}
public final void setPartyOwnerId(int partyid) {
party_ownerid = partyid;
}
public final int getClientsideOwnerId() { // thanks nozphex (RedHat) for noting an issue with collecting party items
if (this.party_ownerid == -1) {
return this.character_ownerid;
} else {
return this.party_ownerid;
}
}
public final boolean hasClientsideOwnership(MapleCharacter player) {
return this.character_ownerid == player.getId() || this.party_ownerid == player.getPartyId() || hasExpiredOwnershipTime();
}
public final boolean isFFADrop() {
return type == 2 || type == 3 || hasExpiredOwnershipTime();
}
public final boolean hasExpiredOwnershipTime() {
return System.currentTimeMillis() - dropTime >= 15 * 1000;
}
public final boolean canBePickedBy(MapleCharacter chr) {
if (character_ownerid <= 0 || isFFADrop()) return true;
if (party_ownerid == -1) {
if (chr.getId() == character_ownerid) {
return true;
} else if (chr.isPartyMember(character_ownerid)) {
party_ownerid = chr.getPartyId();
return true;
}
} else {
if (chr.getPartyId() == party_ownerid) {
return true;
} else if (chr.getId() == character_ownerid) {
party_ownerid = chr.getPartyId();
return true;
}
}
return hasExpiredOwnershipTime();
}
public final MapleClient getOwnerClient() {
return (ownerClient.isLoggedIn() && !ownerClient.getPlayer().isAwayFromWorld()) ? ownerClient : null;
}
public final int getMeso() {
return meso;
}
public final boolean isPlayerDrop() {
return playerDrop;
}
public final boolean isPickedUp() {
return pickedUp;
}
public void setPickedUp(final boolean pickedUp) {
this.pickedUp = pickedUp;
}
public long getDropTime() {
return dropTime;
}
public void setDropTime(long time) {
this.dropTime = time;
}
public byte getDropType() {
return type;
}
public void lockItem() {
itemLock.lock();
}
public void unlockItem() {
itemLock.unlock();
}
@Override
public final MapleMapObjectType getType() {
return MapleMapObjectType.ITEM;
}
@Override
public void sendSpawnData(final MapleClient client) {
MapleCharacter chr = client.getPlayer();
if (chr.needQuestItem(questid, getItemId())) {
this.lockItem();
try {
client.announce(MaplePacketCreator.dropItemFromMapObject(chr, this, null, getPosition(), (byte) 2));
} finally {
this.unlockItem();
}
}
}
@Override
public void sendDestroyData(final MapleClient client) {
client.announce(MaplePacketCreator.removeItemFromMap(getObjectId(), 1, 0));
}
}

View File

@@ -0,0 +1,143 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2019 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/>.
*/
package server.maps;
import java.util.HashMap;
import java.util.Map;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReadLock;
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
import net.server.audit.locks.MonitoredWriteLock;
import net.server.audit.locks.factory.MonitoredReadLockFactory;
import net.server.audit.locks.factory.MonitoredWriteLockFactory;
import scripting.event.EventInstanceManager;
public class MapleMapManager {
private int channel, world;
private EventInstanceManager event;
private Map<Integer, MapleMap> maps = new HashMap<>();
private MonitoredReadLock mapsRLock;
private MonitoredWriteLock mapsWLock;
public MapleMapManager(EventInstanceManager eim, int world, int channel) {
this.world = world;
this.channel = channel;
this.event = eim;
MonitoredReentrantReadWriteLock rrwl = new MonitoredReentrantReadWriteLock(MonitoredLockType.MAP_MANAGER);
this.mapsRLock = MonitoredReadLockFactory.createLock(rrwl);
this.mapsWLock = MonitoredWriteLockFactory.createLock(rrwl);
}
public MapleMap resetMap(int mapid) {
mapsWLock.lock();
try {
maps.remove(mapid);
} finally {
mapsWLock.unlock();
}
return getMap(mapid);
}
private synchronized MapleMap loadMapFromWz(int mapid, boolean cache) {
MapleMap map;
if (cache) {
mapsRLock.lock();
try {
map = maps.get(mapid);
} finally {
mapsRLock.unlock();
}
if (map != null) {
return map;
}
}
map = MapleMapFactory.loadMapFromWz(mapid, world, channel, event);
if (cache) {
mapsWLock.lock();
try {
maps.put(mapid, map);
} finally {
mapsWLock.unlock();
}
}
return map;
}
public MapleMap getMap(int mapid) {
MapleMap map;
mapsRLock.lock();
try {
map = maps.get(mapid);
} finally {
mapsRLock.unlock();
}
return (map != null) ? map : loadMapFromWz(mapid, true);
}
public MapleMap getDisposableMap(int mapid) {
return loadMapFromWz(mapid, false);
}
public boolean isMapLoaded(int mapId) {
mapsRLock.lock();
try {
return maps.containsKey(mapId);
} finally {
mapsRLock.unlock();
}
}
public Map<Integer, MapleMap> getMaps() {
mapsRLock.lock();
try {
return new HashMap<>(maps);
} finally {
mapsRLock.unlock();
}
}
public void updateMaps() {
for (MapleMap map : getMaps().values()) {
map.respawn();
map.mobMpRecovery();
}
}
public void dispose() {
for (MapleMap map : getMaps().values()) {
map.dispose();
}
this.event = null;
}
}

View File

@@ -0,0 +1,36 @@
/*
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 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.maps;
import java.awt.Point;
import client.MapleClient;
public interface MapleMapObject {
public int getObjectId();
public void setObjectId(int id);
public MapleMapObjectType getType();
public Point getPosition();
public void setPosition(Point position);
public void sendSpawnData(MapleClient client);
public void sendDestroyData(MapleClient client);
public void nullifyPosition();
}

View File

@@ -0,0 +1,26 @@
/*
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 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.maps;
public enum MapleMapObjectType {
NPC, MONSTER, ITEM, PLAYER, DOOR, SUMMON, SHOP, MINI_GAME, MIST, REACTOR, HIRED_MERCHANT, PLAYER_NPC, DRAGON, KITE;
}

View File

@@ -0,0 +1,28 @@
/*
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 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.maps;
public class MapleMapPortal extends MapleGenericPortal {
public MapleMapPortal() {
super(MaplePortal.MAP_PORTAL);
}
}

View File

@@ -0,0 +1,126 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2019 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/>.
*/
package server.maps;
import server.TimerManager;
import client.MapleCharacter;
import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.Lock;
import tools.MaplePacketCreator;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
/**
*
* @author Ronan
*/
public class MapleMiniDungeon {
List<MapleCharacter> players = new ArrayList<>();
ScheduledFuture<?> timeoutTask = null;
Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MINIDUNGEON, true);
int baseMap;
long expireTime;
public MapleMiniDungeon(int base, long timeLimit) {
baseMap = base;
expireTime = timeLimit * 1000;
timeoutTask = TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
close();
}
}, expireTime);
expireTime += System.currentTimeMillis();
}
public boolean registerPlayer(MapleCharacter chr) {
int time = (int)((expireTime - System.currentTimeMillis()) / 1000);
if(time > 0) chr.getClient().announce(MaplePacketCreator.getClock(time));
lock.lock();
try {
if(timeoutTask == null) return false;
players.add(chr);
} finally {
lock.unlock();
}
return true;
}
public boolean unregisterPlayer(MapleCharacter chr) {
chr.getClient().announce(MaplePacketCreator.removeClock());
lock.lock();
try {
players.remove(chr);
if(players.isEmpty()) {
dispose();
return false;
}
} finally {
lock.unlock();
}
if (chr.isPartyLeader()) { // thanks Conrad for noticing party is not sent out of the MD as soon as leader leaves it
close();
}
return true;
}
public void close() {
lock.lock();
try {
List<MapleCharacter> lchr = new ArrayList<>(players);
for(MapleCharacter chr : lchr) {
chr.changeMap(baseMap);
}
dispose();
timeoutTask = null;
} finally {
lock.unlock();
}
}
public void dispose() {
lock.lock();
try {
players.clear();
if(timeoutTask != null) {
timeoutTask.cancel(false);
timeoutTask = null;
}
} finally {
lock.unlock();
}
}
}

View File

@@ -0,0 +1,85 @@
/*
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 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.maps;
/**
*
* @author Alan (SharpAceX)
*/
public enum MapleMiniDungeonInfo {
//http://bbb.hidden-street.net/search_finder/mini%20dungeon
CAVE_OF_MUSHROOMS(105050100, 105050101, 30),
GOLEM_CASTLE_RUINS(105040304, 105040320, 34),
HILL_OF_SANDSTORMS(260020600, 260020630, 30),
HENESYS_PIG_FARM(100020000, 100020100, 30),
DRAKES_BLUE_CAVE(105090311, 105090320, 30),
DRUMMER_BUNNYS_LAIR(221023400, 221023401, 30),
THE_ROUND_TABLE_OF_KENTARUS(240020500, 240020512, 30),
THE_RESTORING_MEMORY(240040511, 240040800, 19),
NEWT_SECURED_ZONE(240040520, 240040900, 19),
PILLAGE_OF_TREASURE_ISLAND(251010402, 251010410, 30),
CRITICAL_ERROR(261020300, 261020301, 30),
LONGEST_RIDE_ON_BYEBYE_STATION(551030000, 551030001, 19);
private int baseId;
private int dungeonId;
private int dungeons;
private MapleMiniDungeonInfo(int baseId, int dungeonId, int dungeons) {
this.baseId = baseId;
this.dungeonId = dungeonId;
this.dungeons = dungeons;
}
public int getBase() {
return baseId;
}
public int getDungeonId() {
return dungeonId;
}
public int getDungeons() {
return dungeons;
}
public static boolean isDungeonMap(int map){
for (MapleMiniDungeonInfo dungeon : MapleMiniDungeonInfo.values()){
if (map >= dungeon.getDungeonId() && map <= dungeon.getDungeonId() + dungeon.getDungeons()){
return true;
}
}
return false;
}
public static MapleMiniDungeonInfo getDungeon(int map){
for (MapleMiniDungeonInfo dungeon : MapleMiniDungeonInfo.values()){
if (map >= dungeon.getDungeonId() && map <= dungeon.getDungeonId() + dungeon.getDungeons()){
return dungeon;
}
}
return null;
}
}

View File

@@ -0,0 +1,520 @@
/*
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 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.maps;
import client.MapleCharacter;
import client.MapleClient;
import net.server.Server;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import tools.MaplePacketCreator;
/**
*
* @author Matze
* @author Ronan (HeavenMS)
*/
public class MapleMiniGame extends AbstractMapleMapObject {
private MapleCharacter owner;
private MapleCharacter visitor;
private String password;
private MiniGameType GameType = MiniGameType.UNDEFINED;
private int piecetype;
private int inprogress = 0;
private int[] piece = new int[250];
private List<Integer> list4x3 = new ArrayList<>();
private List<Integer> list5x4 = new ArrayList<>();
private List<Integer> list6x5 = new ArrayList<>();
private String description;
private int loser = 1;
private int firstslot = 0;
private int visitorpoints = 0, visitorscore = 0, visitorforfeits = 0, lastvisitor = -1;
private int ownerpoints = 0, ownerscore = 0, ownerforfeits = 0;
private boolean visitorquit, ownerquit;
private long nextavailabletie = 0;
private int matchestowin = 0;
public static enum MiniGameType {
UNDEFINED(0), OMOK(1), MATCH_CARD(2);
private int value = 0;
private MiniGameType(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
public static enum MiniGameResult {
WIN, LOSS, TIE;
}
public MapleMiniGame(MapleCharacter owner, String description, String password) {
this.owner = owner;
this.description = description;
this.password = password;
}
public String getPassword() {
return this.password;
}
public boolean checkPassword(String sentPw) {
return this.password.length() == 0 || sentPw.toLowerCase().contentEquals(this.password.toLowerCase());
}
public boolean hasFreeSlot() {
return visitor == null;
}
public boolean isOwner(MapleCharacter chr) {
return owner.equals(chr);
}
public void addVisitor(MapleCharacter challenger) {
visitor = challenger;
if (lastvisitor != challenger.getId()) {
ownerscore = 0;
ownerforfeits = 0;
visitorscore = 0;
visitorforfeits = 0;
lastvisitor = challenger.getId();
}
MapleCharacter owner = this.getOwner();
if (GameType == MiniGameType.OMOK) {
owner.announce(MaplePacketCreator.getMiniGameNewVisitor(this, challenger, 1));
owner.getMap().broadcastMessage(MaplePacketCreator.addOmokBox(owner, 2, 0));
} else if (GameType == MiniGameType.MATCH_CARD) {
owner.announce(MaplePacketCreator.getMatchCardNewVisitor(this, challenger, 1));
owner.getMap().broadcastMessage(MaplePacketCreator.addMatchCardBox(owner, 2, 0));
}
}
public void closeRoom(boolean forceClose) {
owner.getMap().broadcastMessage(MaplePacketCreator.removeMinigameBox(owner));
if (forceClose) {
this.broadcastToOwner(MaplePacketCreator.getMiniGameClose(false, 4));
}
this.broadcastToVisitor(MaplePacketCreator.getMiniGameClose(true, 3));
if (visitor != null) {
visitor.setMiniGame(null);
visitor = null;
}
owner.setMiniGame(null);
owner = null;
}
public void removeVisitor(boolean forceClose, MapleCharacter challenger) {
if (visitor == challenger) {
if (forceClose) {
visitor.announce(MaplePacketCreator.getMiniGameClose(true, 4));
}
challenger.setMiniGame(null);
visitor = null;
this.getOwner().getClient().announce(MaplePacketCreator.getMiniGameRemoveVisitor());
if (GameType == MiniGameType.OMOK) {
this.getOwner().getMap().broadcastMessage(MaplePacketCreator.addOmokBox(owner, 1, 0));
} else if (GameType == MiniGameType.MATCH_CARD) {
this.getOwner().getMap().broadcastMessage(MaplePacketCreator.addMatchCardBox(owner, 1, 0));
}
}
}
public boolean isVisitor(MapleCharacter challenger) {
return visitor == challenger;
}
public void broadcastToOwner(final byte[] packet) {
MapleClient c = owner.getClient();
if (c != null && c.getSession() != null) {
c.announce(packet);
}
}
public void broadcastToVisitor(final byte[] packet) {
if (visitor != null) {
visitor.getClient().announce(packet);
}
}
public void setFirstSlot(int type) {
firstslot = type;
}
public int getFirstSlot() {
return firstslot;
}
private void updateMiniGameBox() {
this.getOwner().getMap().broadcastMessage(MaplePacketCreator.addOmokBox(owner, visitor != null ? 2 : 1, inprogress));
}
private synchronized boolean minigameMatchFinish() {
if (isMatchInProgress()) {
inprogress = 0;
return true;
} else {
return false;
}
}
private void minigameMatchFinished() {
updateMiniGameBox();
if (ownerquit) {
owner.closeMiniGame(true);
} else if (visitorquit) {
visitor.closeMiniGame(true);
}
}
public void minigameMatchStarted() {
inprogress = 1;
ownerquit = false;
visitorquit = false;
}
public void setQuitAfterGame(MapleCharacter player, boolean quit) {
if (isOwner(player)) {
ownerquit = quit;
} else {
visitorquit = quit;
}
}
public boolean isMatchInProgress() {
return inprogress != 0;
}
public void denyTie(MapleCharacter chr) {
if (this.isOwner(chr)) {
inprogress |= (1 << 1);
} else {
inprogress |= (1 << 2);
}
}
public boolean isTieDenied(MapleCharacter chr) {
if (this.isOwner(chr)) {
return ((inprogress >> 2) % 2) == 1;
} else {
return ((inprogress >> 1) % 2) == 1;
}
}
public void minigameMatchOwnerWins(boolean forfeit) {
if (!minigameMatchFinish()) return;
owner.setMiniGamePoints(visitor, 1, this.isOmok());
if (visitorforfeits < 4 || !forfeit) ownerscore += 50;
visitorscore += (15 * (forfeit ? -1 : 1));
if (forfeit) visitorforfeits++;
this.broadcast(MaplePacketCreator.getMiniGameOwnerWin(this, forfeit));
minigameMatchFinished();
}
public void minigameMatchVisitorWins(boolean forfeit) {
if (!minigameMatchFinish()) return;
owner.setMiniGamePoints(visitor, 2, this.isOmok());
if (ownerforfeits < 4 || !forfeit) visitorscore += 50;
ownerscore += (15 * (forfeit ? -1 : 1));
if (forfeit) ownerforfeits++;
this.broadcast(MaplePacketCreator.getMiniGameVisitorWin(this, forfeit));
minigameMatchFinished();
}
public void minigameMatchDraw() {
if (!minigameMatchFinish()) return;
owner.setMiniGamePoints(visitor, 3, this.isOmok());
long timeNow = Server.getInstance().getCurrentTime();
if (nextavailabletie <= timeNow) {
visitorscore += 10;
ownerscore += 10;
nextavailabletie = timeNow + 5 * 60 * 1000;
}
this.broadcast(MaplePacketCreator.getMiniGameTie(this));
minigameMatchFinished();
}
public void setOwnerPoints() {
ownerpoints++;
if (ownerpoints + visitorpoints == matchestowin) {
if (ownerpoints == visitorpoints) {
minigameMatchDraw();
} else if (ownerpoints > visitorpoints) {
minigameMatchOwnerWins(false);
} else {
minigameMatchVisitorWins(false);
}
ownerpoints = 0;
visitorpoints = 0;
}
}
public void setVisitorPoints() {
visitorpoints++;
if (ownerpoints + visitorpoints == matchestowin) {
if (ownerpoints > visitorpoints) {
minigameMatchOwnerWins(false);
} else if (visitorpoints > ownerpoints) {
minigameMatchVisitorWins(false);
} else {
minigameMatchDraw();
}
ownerpoints = 0;
visitorpoints = 0;
}
}
public void setMatchesToWin(int type) {
matchestowin = type;
}
public void setPieceType(int type) {
piecetype = type;
}
public int getPieceType() {
return piecetype;
}
public void setGameType(MiniGameType game) {
GameType = game;
if (GameType == MiniGameType.MATCH_CARD) {
if (matchestowin == 6) {
for (int i = 0; i < 6; i++) {
list4x3.add(i);
list4x3.add(i);
}
} else if (matchestowin == 10) {
for (int i = 0; i < 10; i++) {
list5x4.add(i);
list5x4.add(i);
}
} else {
for (int i = 0; i < 15; i++) {
list6x5.add(i);
list6x5.add(i);
}
}
}
}
public MiniGameType getGameType() {
return GameType;
}
public boolean isOmok() {
return GameType.equals(MiniGameType.OMOK);
}
public void shuffleList() {
if (matchestowin == 6) {
Collections.shuffle(list4x3);
} else if (matchestowin == 10) {
Collections.shuffle(list5x4);
} else {
Collections.shuffle(list6x5);
}
}
public int getCardId(int slot) {
int cardid;
if (matchestowin == 6) {
cardid = list4x3.get(slot);
} else if (matchestowin == 10) {
cardid = list5x4.get(slot);
} else {
cardid = list6x5.get(slot);
}
return cardid;
}
public int getMatchesToWin() {
return matchestowin;
}
public void setLoser(int type) {
loser = type;
}
public int getLoser() {
return loser;
}
public void broadcast(final byte[] packet) {
broadcastToOwner(packet);
broadcastToVisitor(packet);
}
public void chat(MapleClient c, String chat) {
broadcast(MaplePacketCreator.getPlayerShopChat(c.getPlayer(), chat, isOwner(c.getPlayer())));
}
public void sendOmok(MapleClient c, int type) {
c.announce(MaplePacketCreator.getMiniGame(c, this, isOwner(c.getPlayer()), type));
}
public void sendMatchCard(MapleClient c, int type) {
c.announce(MaplePacketCreator.getMatchCard(c, this, isOwner(c.getPlayer()), type));
}
public MapleCharacter getOwner() {
return owner;
}
public MapleCharacter getVisitor() {
return visitor;
}
public void setPiece(int move1, int move2, int type, MapleCharacter chr) {
int slot = move2 * 15 + move1 + 1;
if (piece[slot] == 0) {
piece[slot] = type;
this.broadcast(MaplePacketCreator.getMiniGameMoveOmok(this, move1, move2, type));
for (int y = 0; y < 15; y++) {
for (int x = 0; x < 11; x++) {
if (searchCombo(x, y, type)) {
if (this.isOwner(chr)) {
this.minigameMatchOwnerWins(false);
this.setLoser(0);
} else {
this.minigameMatchVisitorWins(false);
this.setLoser(1);
}
for (int y2 = 0; y2 < 15; y2++) {
for (int x2 = 0; x2 < 15; x2++) {
int slot2 = (y2 * 15 + x2 + 1);
piece[slot2] = 0;
}
}
}
}
}
for (int y = 0; y < 15; y++) {
for (int x = 4; x < 15; x++) {
if (searchCombo2(x, y, type)) {
if (this.isOwner(chr)) {
this.minigameMatchOwnerWins(false);
this.setLoser(0);
} else {
this.minigameMatchVisitorWins(false);
this.setLoser(1);
}
for (int y2 = 0; y2 < 15; y2++) {
for (int x2 = 0; x2 < 15; x2++) {
int slot2 = (y2 * 15 + x2 + 1);
piece[slot2] = 0;
}
}
}
}
}
}
}
private boolean searchCombo(int x, int y, int type) {
int slot = y * 15 + x + 1;
for (int i = 0; i < 5; i++) {
if (piece[slot + i] == type) {
if (i == 4) {
return true;
}
} else {
break;
}
}
for (int j = 15; j < 17; j++) {
for (int i = 0; i < 5; i++) {
if (piece[slot + i * j] == type) {
if (i == 4) {
return true;
}
} else {
break;
}
}
}
return false;
}
private boolean searchCombo2(int x, int y, int type) {
int slot = y * 15 + x + 1;
for (int j = 14; j < 15; j++) {
for (int i = 0; i < 5; i++) {
if (piece[slot + i * j] == type) {
if (i == 4) {
return true;
}
} else {
break;
}
}
}
return false;
}
public String getDescription() {
return description;
}
public int getOwnerScore() {
return ownerscore;
}
public int getVisitorScore() {
return visitorscore;
}
@Override
public void sendDestroyData(MapleClient client) {}
@Override
public void sendSpawnData(MapleClient client) {}
@Override
public MapleMapObjectType getType() {
return MapleMapObjectType.MINI_GAME;
}
}

View File

@@ -0,0 +1,168 @@
/*
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 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.maps;
import client.MapleCharacter;
import client.MapleClient;
import client.Skill;
import client.SkillFactory;
import java.awt.Point;
import java.awt.Rectangle;
import constants.skills.BlazeWizard;
import constants.skills.Evan;
import constants.skills.FPMage;
import constants.skills.NightWalker;
import constants.skills.Shadower;
import server.MapleStatEffect;
import server.life.MapleMonster;
import server.life.MobSkill;
import tools.MaplePacketCreator;
/**
*
* @author LaiLaiNoob
*/
public class MapleMist extends AbstractMapleMapObject {
private Rectangle mistPosition;
private MapleCharacter owner = null;
private MapleMonster mob = null;
private MapleStatEffect source;
private MobSkill skill;
private boolean isMobMist, isPoisonMist, isRecoveryMist;
private int skillDelay;
public MapleMist(Rectangle mistPosition, MapleMonster mob, MobSkill skill) {
this.mistPosition = mistPosition;
this.mob = mob;
this.skill = skill;
isMobMist = true;
isPoisonMist = true;
isRecoveryMist = false;
skillDelay = 0;
}
public MapleMist(Rectangle mistPosition, MapleCharacter owner, MapleStatEffect source) {
this.mistPosition = mistPosition;
this.owner = owner;
this.source = source;
this.skillDelay = 8;
this.isMobMist = false;
this.isRecoveryMist = false;
this.isPoisonMist = false;
switch (source.getSourceId()) {
case Evan.RECOVERY_AURA:
isRecoveryMist = true;
break;
case Shadower.SMOKE_SCREEN: // Smoke Screen
isPoisonMist = false;
break;
case FPMage.POISON_MIST: // FP mist
case BlazeWizard.FLAME_GEAR: // Flame Gear
case NightWalker.POISON_BOMB: // Poison Bomb
isPoisonMist = true;
break;
}
}
@Override
public MapleMapObjectType getType() {
return MapleMapObjectType.MIST;
}
@Override
public Point getPosition() {
return mistPosition.getLocation();
}
public Skill getSourceSkill() {
return SkillFactory.getSkill(source.getSourceId());
}
public boolean isMobMist() {
return isMobMist;
}
public boolean isPoisonMist() {
return isPoisonMist;
}
public boolean isRecoveryMist() {
return isRecoveryMist;
}
public int getSkillDelay() {
return skillDelay;
}
public MapleMonster getMobOwner() {
return mob;
}
public MapleCharacter getOwner() {
return owner;
}
public Rectangle getBox() {
return mistPosition;
}
@Override
public void setPosition(Point position) {
throw new UnsupportedOperationException();
}
public final byte[] makeDestroyData() {
return MaplePacketCreator.removeMist(getObjectId());
}
public final byte[] makeSpawnData() {
if (owner != null) {
return MaplePacketCreator.spawnMist(getObjectId(), owner.getId(), getSourceSkill().getId(), owner.getSkillLevel(SkillFactory.getSkill(source.getSourceId())), this);
}
return MaplePacketCreator.spawnMist(getObjectId(), mob.getId(), skill.getSkillId(), skill.getSkillLevel(), this);
}
public final byte[] makeFakeSpawnData(int level) {
if (owner != null) {
return MaplePacketCreator.spawnMist(getObjectId(), owner.getId(), getSourceSkill().getId(), level, this);
}
return MaplePacketCreator.spawnMist(getObjectId(), mob.getId(), skill.getSkillId(), skill.getSkillLevel(), this);
}
@Override
public void sendSpawnData(MapleClient client) {
client.announce(makeSpawnData());
}
@Override
public void sendDestroyData(MapleClient client) {
client.announce(makeDestroyData());
}
public boolean makeChanceResult() {
return source.makeChanceResult();
}
}

View File

@@ -0,0 +1,620 @@
/*
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 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.maps;
import client.MapleCharacter;
import client.MapleClient;
import client.inventory.Item;
import client.inventory.MapleInventory;
import client.inventory.MapleInventoryType;
import client.inventory.manipulator.MapleInventoryManipulator;
import client.inventory.manipulator.MapleKarmaManipulator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import net.opcodes.SendOpcode;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.data.output.MaplePacketLittleEndianWriter;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import server.MapleTrade;
/**
*
* @author Matze
* @author Ronan - concurrency protection
*/
public class MaplePlayerShop extends AbstractMapleMapObject {
private AtomicBoolean open = new AtomicBoolean(false);
private MapleCharacter owner;
private int itemid;
private MapleCharacter[] visitors = new MapleCharacter[3];
private List<MaplePlayerShopItem> items = new ArrayList<>();
private List<SoldItem> sold = new LinkedList<>();
private String description;
private int boughtnumber = 0;
private List<String> bannedList = new ArrayList<>();
private List<Pair<MapleCharacter, String>> chatLog = new LinkedList<>();
private Map<Integer, Byte> chatSlot = new LinkedHashMap<>();
private Lock visitorLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.VISITOR_PSHOP, true);
public MaplePlayerShop(MapleCharacter owner, String description, int itemid) {
this.setPosition(owner.getPosition());
this.owner = owner;
this.description = description;
this.itemid = itemid;
}
public int getChannel() {
return owner.getClient().getChannel();
}
public int getMapId() {
return owner.getMapId();
}
public int getItemId() {
return itemid;
}
public boolean isOpen() {
return open.get();
}
public void setOpen(boolean openShop) {
open.set(openShop);
}
public boolean hasFreeSlot() {
visitorLock.lock();
try {
return visitors[0] == null || visitors[1] == null || visitors[2] == null;
} finally {
visitorLock.unlock();
}
}
public byte[] getShopRoomInfo() {
visitorLock.lock();
try {
byte count = 0;
//if (this.isOpen()) {
for (MapleCharacter visitor : visitors) {
if (visitor != null) {
count++;
}
}
//} else { shouldn't happen since there isn't a "closed" state for player shops.
// count = (byte) (visitors.length + 1);
//}
return new byte[]{count, (byte) visitors.length};
} finally {
visitorLock.unlock();
}
}
public boolean isOwner(MapleCharacter chr) {
return owner.equals(chr);
}
private void addVisitor(MapleCharacter visitor) {
for (int i = 0; i < 3; i++) {
if (visitors[i] == null) {
visitors[i] = visitor;
visitor.setSlot(i);
this.broadcast(MaplePacketCreator.getPlayerShopNewVisitor(visitor, i + 1));
owner.getMap().broadcastMessage(MaplePacketCreator.updatePlayerShopBox(this));
break;
}
}
}
public void forceRemoveVisitor(MapleCharacter visitor) {
if (visitor == owner) {
owner.getMap().removeMapObject(this);
owner.setPlayerShop(null);
}
visitorLock.lock();
try {
for (int i = 0; i < 3; i++) {
if (visitors[i] != null && visitors[i].getId() == visitor.getId()) {
visitors[i].setPlayerShop(null);
visitors[i] = null;
visitor.setSlot(-1);
this.broadcast(MaplePacketCreator.getPlayerShopRemoveVisitor(i + 1));
owner.getMap().broadcastMessage(MaplePacketCreator.updatePlayerShopBox(this));
return;
}
}
} finally {
visitorLock.unlock();
}
}
public void removeVisitor(MapleCharacter visitor) {
if (visitor == owner) {
owner.getMap().removeMapObject(this);
owner.setPlayerShop(null);
} else {
visitorLock.lock();
try {
for (int i = 0; i < 3; i++) {
if (visitors[i] != null && visitors[i].getId() == visitor.getId()) {
visitor.setSlot(-1); //absolutely cant remove player slot for late players without dc'ing them... heh
for(int j = i; j < 2; j++) {
if(visitors[j] != null) owner.announce(MaplePacketCreator.getPlayerShopRemoveVisitor(j + 1));
visitors[j] = visitors[j + 1];
if(visitors[j] != null) visitors[j].setSlot(j);
}
visitors[2] = null;
for(int j = i; j < 2; j++) {
if(visitors[j] != null) owner.announce(MaplePacketCreator.getPlayerShopNewVisitor(visitors[j], j + 1));
}
this.broadcastRestoreToVisitors();
owner.getMap().broadcastMessage(MaplePacketCreator.updatePlayerShopBox(this));
return;
}
}
} finally {
visitorLock.unlock();
}
owner.getMap().broadcastMessage(MaplePacketCreator.updatePlayerShopBox(this));
}
}
public boolean isVisitor(MapleCharacter visitor) {
visitorLock.lock();
try {
return visitors[0] == visitor || visitors[1] == visitor || visitors[2] == visitor;
} finally {
visitorLock.unlock();
}
}
public boolean addItem(MaplePlayerShopItem item) {
synchronized (items) {
if (items.size() >= 16) return false;
items.add(item);
return true;
}
}
private void removeFromSlot(int slot) {
items.remove(slot);
}
private static boolean canBuy(MapleClient c, Item newItem) {
return MapleInventoryManipulator.checkSpace(c, newItem.getItemId(), newItem.getQuantity(), newItem.getOwner()) && MapleInventoryManipulator.addFromDrop(c, newItem, false);
}
public void takeItemBack(int slot, MapleCharacter chr) {
synchronized (items) {
MaplePlayerShopItem shopItem = items.get(slot);
if(shopItem.isExist()) {
if (shopItem.getBundles() > 0) {
Item iitem = shopItem.getItem().copy();
iitem.setQuantity((short) (shopItem.getItem().getQuantity() * shopItem.getBundles()));
if (!MapleInventory.checkSpot(chr, iitem)) {
chr.announce(MaplePacketCreator.serverNotice(1, "Have a slot available on your inventory to claim back the item."));
chr.announce(MaplePacketCreator.enableActions());
return;
}
MapleInventoryManipulator.addFromDrop(chr.getClient(), iitem, true);
}
removeFromSlot(slot);
chr.announce(MaplePacketCreator.getPlayerShopItemUpdate(this));
}
}
}
/**
* no warnings for now o.o
* @param c
* @param item
* @param quantity
*/
public boolean buy(MapleClient c, int item, short quantity) {
synchronized (items) {
if (isVisitor(c.getPlayer())) {
MaplePlayerShopItem pItem = items.get(item);
Item newItem = pItem.getItem().copy();
newItem.setQuantity((short) ((pItem.getItem().getQuantity() * quantity)));
if (quantity < 1 || !pItem.isExist() || pItem.getBundles() < quantity) {
c.announce(MaplePacketCreator.enableActions());
return false;
} else if (newItem.getInventoryType().equals(MapleInventoryType.EQUIP) && newItem.getQuantity() > 1) {
c.announce(MaplePacketCreator.enableActions());
return false;
}
MapleKarmaManipulator.toggleKarmaFlagToUntradeable(newItem);
visitorLock.lock();
try {
int price = (int) Math.min((float)pItem.getPrice() * quantity, Integer.MAX_VALUE);
if (c.getPlayer().getMeso() >= price) {
if (!owner.canHoldMeso(price)) { // thanks Rohenn for noticing owner hold check misplaced
c.getPlayer().dropMessage(1, "Transaction failed since the shop owner can't hold any more mesos.");
c.announce(MaplePacketCreator.enableActions());
return false;
}
if (canBuy(c, newItem)) {
c.getPlayer().gainMeso(-price, false);
price -= MapleTrade.getFee(price); // thanks BHB for pointing out trade fees not applying here
owner.gainMeso(price, true);
SoldItem soldItem = new SoldItem(c.getPlayer().getName(), pItem.getItem().getItemId(), quantity, price);
owner.announce(MaplePacketCreator.getPlayerShopOwnerUpdate(soldItem, item));
synchronized (sold) {
sold.add(soldItem);
}
pItem.setBundles((short) (pItem.getBundles() - quantity));
if (pItem.getBundles() < 1) {
pItem.setDoesExist(false);
if (++boughtnumber == items.size()) {
owner.setPlayerShop(null);
this.setOpen(false);
this.closeShop();
owner.dropMessage(1, "Your items are sold out, and therefore your shop is closed.");
}
}
} else {
c.getPlayer().dropMessage(1, "Your inventory is full. Please clear a slot before buying this item.");
c.announce(MaplePacketCreator.enableActions());
return false;
}
} else {
c.getPlayer().dropMessage(1, "You don't have enough mesos to purchase this item.");
c.announce(MaplePacketCreator.enableActions());
return false;
}
return true;
} finally {
visitorLock.unlock();
}
} else {
return false;
}
}
}
public void broadcastToVisitors(final byte[] packet) {
visitorLock.lock();
try {
for (int i = 0; i < 3; i++) {
if (visitors[i] != null) {
visitors[i].getClient().announce(packet);
}
}
} finally {
visitorLock.unlock();
}
}
public void broadcastRestoreToVisitors() {
visitorLock.lock();
try {
for (int i = 0; i < 3; i++) {
if (visitors[i] != null) {
visitors[i].getClient().announce(MaplePacketCreator.getPlayerShopRemoveVisitor(i + 1));
}
}
for (int i = 0; i < 3; i++) {
if (visitors[i] != null) {
visitors[i].getClient().announce(MaplePacketCreator.getPlayerShop(this, false));
}
}
recoverChatLog();
} finally {
visitorLock.unlock();
}
}
public void removeVisitors() {
List<MapleCharacter> visitorList = new ArrayList<>(3);
visitorLock.lock();
try {
try {
for (int i = 0; i < 3; i++) {
if (visitors[i] != null) {
visitors[i].getClient().announce(MaplePacketCreator.shopErrorMessage(10, 1));
visitorList.add(visitors[i]);
}
}
} catch (Exception e) {
e.printStackTrace();
}
} finally {
visitorLock.unlock();
}
for(MapleCharacter mc : visitorList) forceRemoveVisitor(mc);
if (owner != null) {
forceRemoveVisitor(owner);
}
}
public static byte[] shopErrorMessage(int error, int type) {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue());
mplew.write(0x0A);
mplew.write(type);
mplew.write(error);
return mplew.getPacket();
}
public void broadcast(final byte[] packet) {
if (owner.getClient() != null && owner.getClient().getSession() != null) {
owner.getClient().announce(packet);
}
broadcastToVisitors(packet);
}
private byte getVisitorSlot(MapleCharacter chr) {
byte s = 0;
for (MapleCharacter mc : getVisitors()) {
s++;
if (mc != null) {
if (mc.getName().equalsIgnoreCase(chr.getName())) {
break;
}
} else if (s == 3) {
s = 0;
}
}
return s;
}
public void chat(MapleClient c, String chat) {
byte s = getVisitorSlot(c.getPlayer());
synchronized(chatLog) {
chatLog.add(new Pair<>(c.getPlayer(), chat));
if(chatLog.size() > 25) chatLog.remove(0);
chatSlot.put(c.getPlayer().getId(), s);
}
broadcast(MaplePacketCreator.getPlayerShopChat(c.getPlayer(), chat, s));
}
private void recoverChatLog() {
synchronized(chatLog) {
for(Pair<MapleCharacter, String> it : chatLog) {
MapleCharacter chr = it.getLeft();
Byte pos = chatSlot.get(chr.getId());
broadcastToVisitors(MaplePacketCreator.getPlayerShopChat(chr, it.getRight(), pos));
}
}
}
private void clearChatLog() {
synchronized(chatLog) {
chatLog.clear();
}
}
public void closeShop() {
clearChatLog();
removeVisitors();
owner.getMap().broadcastMessage(MaplePacketCreator.removePlayerShopBox(this));
}
public void sendShop(MapleClient c) {
visitorLock.lock();
try {
c.announce(MaplePacketCreator.getPlayerShop(this, isOwner(c.getPlayer())));
} finally {
visitorLock.unlock();
}
}
public MapleCharacter getOwner() {
return owner;
}
public MapleCharacter[] getVisitors() {
visitorLock.lock();
try {
MapleCharacter[] copy = new MapleCharacter[3];
for(int i = 0; i < visitors.length; i++) copy[i] = visitors[i];
return copy;
} finally {
visitorLock.unlock();
}
}
public List<MaplePlayerShopItem> getItems() {
synchronized (items) {
return Collections.unmodifiableList(items);
}
}
public boolean hasItem(int itemid) {
for(MaplePlayerShopItem mpsi : getItems()) {
if(mpsi.getItem().getItemId() == itemid && mpsi.isExist() && mpsi.getBundles() > 0) {
return true;
}
}
return false;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public void banPlayer(String name) {
if (!bannedList.contains(name)) {
bannedList.add(name);
}
MapleCharacter target = null;
visitorLock.lock();
try {
for (int i = 0; i < 3; i++) {
if (visitors[i] != null && visitors[i].getName().equals(name)) {
target = visitors[i];
break;
}
}
} finally {
visitorLock.unlock();
}
if(target != null) {
target.getClient().announce(MaplePacketCreator.shopErrorMessage(5, 1));
removeVisitor(target);
}
}
public boolean isBanned(String name) {
return bannedList.contains(name);
}
public synchronized boolean visitShop(MapleCharacter chr) {
if (this.isBanned(chr.getName())) {
chr.dropMessage(1, "You have been banned from this store.");
return false;
}
visitorLock.lock();
try {
if(!open.get()) {
chr.dropMessage(1, "This store is not yet open.");
return false;
}
if (this.hasFreeSlot() && !this.isVisitor(chr)) {
this.addVisitor(chr);
chr.setPlayerShop(this);
this.sendShop(chr.getClient());
return true;
}
return false;
} finally {
visitorLock.unlock();
}
}
public List<MaplePlayerShopItem> sendAvailableBundles(int itemid) {
List<MaplePlayerShopItem> list = new LinkedList<>();
List<MaplePlayerShopItem> all = new ArrayList<>();
synchronized (items) {
for(MaplePlayerShopItem mpsi : items) all.add(mpsi);
}
for(MaplePlayerShopItem mpsi : all) {
if(mpsi.getItem().getItemId() == itemid && mpsi.getBundles() > 0 && mpsi.isExist()) {
list.add(mpsi);
}
}
return list;
}
public List<SoldItem> getSold() {
synchronized (sold) {
return Collections.unmodifiableList(sold);
}
}
@Override
public void sendDestroyData(MapleClient client) {
client.announce(MaplePacketCreator.removePlayerShopBox(this));
}
@Override
public void sendSpawnData(MapleClient client) {
client.announce(MaplePacketCreator.updatePlayerShopBox(this));
}
@Override
public MapleMapObjectType getType() {
return MapleMapObjectType.SHOP;
}
public class SoldItem {
int itemid, mesos;
short quantity;
String buyer;
public SoldItem(String buyer, int itemid, short quantity, int mesos) {
this.buyer = buyer;
this.itemid = itemid;
this.quantity = quantity;
this.mesos = mesos;
}
public String getBuyer() {
return buyer;
}
public int getItemId() {
return itemid;
}
public short getQuantity() {
return quantity;
}
public int getMesos() {
return mesos;
}
}
}

View File

@@ -0,0 +1,66 @@
/*
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 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.maps;
import client.inventory.Item;
/**
*
* @author Matze
*/
public class MaplePlayerShopItem {
private Item item;
private short bundles;
private int price;
private boolean doesExist;
public MaplePlayerShopItem(Item item, short bundles, int price) {
this.item = item;
this.bundles = bundles;
this.price = price;
this.doesExist = true;
}
public void setDoesExist(boolean tf) {
this.doesExist = tf;
}
public boolean isExist() {
return doesExist;
}
public Item getItem() {
return item;
}
public short getBundles() {
return bundles;
}
public int getPrice() {
return price;
}
public void setBundles(short bundles) {
this.bundles = bundles;
}
}

View File

@@ -0,0 +1,46 @@
/*
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 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.maps;
import java.awt.Point;
import client.MapleClient;
public interface MaplePortal {
public final int TELEPORT_PORTAL = 1;
public final int MAP_PORTAL = 2;
public final int DOOR_PORTAL = 6;
public static boolean OPEN = true;
public static boolean CLOSED = false;
int getType();
int getId();
Point getPosition();
String getName();
String getTarget();
String getScriptName();
void setScriptName(String newName);
void setPortalStatus(boolean newStatus);
boolean getPortalStatus();
int getTargetMapId();
void enterPortal(MapleClient c);
void setPortalState(boolean state);
boolean getPortalState();
}

View File

@@ -0,0 +1,68 @@
/*
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 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.maps;
import java.awt.Point;
import provider.MapleData;
import provider.MapleDataTool;
import server.maps.MapleGenericPortal;
import server.maps.MapleMapPortal;
public class MaplePortalFactory {
private int nextDoorPortal;
public MaplePortalFactory() {
nextDoorPortal = 0x80;
}
public MaplePortal makePortal(int type, MapleData portal) {
MapleGenericPortal ret = null;
if (type == MaplePortal.MAP_PORTAL) {
ret = new MapleMapPortal();
} else {
ret = new MapleGenericPortal(type);
}
loadPortal(ret, portal);
return ret;
}
private void loadPortal(MapleGenericPortal myPortal, MapleData portal) {
myPortal.setName(MapleDataTool.getString(portal.getChildByPath("pn")));
myPortal.setTarget(MapleDataTool.getString(portal.getChildByPath("tn")));
myPortal.setTargetMapId(MapleDataTool.getInt(portal.getChildByPath("tm")));
int x = MapleDataTool.getInt(portal.getChildByPath("x"));
int y = MapleDataTool.getInt(portal.getChildByPath("y"));
myPortal.setPosition(new Point(x, y));
String script = MapleDataTool.getString("script", portal, null);
if (script != null && script.equals("")) {
script = null;
}
myPortal.setScriptName(script);
if (myPortal.getType() == MaplePortal.DOOR_PORTAL) {
myPortal.setId(nextDoorPortal);
nextDoorPortal++;
} else {
myPortal.setId(Integer.parseInt(portal.getName()));
}
}
}

View File

@@ -0,0 +1,431 @@
/*
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 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.maps;
import client.MapleClient;
import config.YamlConfig;
import java.awt.Rectangle;
import java.util.List;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.Lock;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import scripting.reactor.ReactorScriptManager;
import server.TimerManager;
import tools.MaplePacketCreator;
import tools.Pair;
import net.server.audit.locks.MonitoredLockType;
import net.server.services.type.ChannelServices;
import net.server.services.task.channel.OverallService;
import server.partyquest.GuardianSpawnPoint;
/**
*
* @author Lerk
* @author Ronan
*/
public class MapleReactor extends AbstractMapleMapObject {
private int rid;
private MapleReactorStats stats;
private byte state;
private byte evstate;
private int delay;
private MapleMap map;
private String name;
private boolean alive;
private boolean shouldCollect;
private boolean attackHit;
private ScheduledFuture<?> timeoutTask = null;
private Runnable delayedRespawnRun = null;
private GuardianSpawnPoint guardian = null;
private byte facingDirection = 0;
private Lock reactorLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.REACTOR, true);
private Lock hitLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.REACTOR_HIT, true);
public MapleReactor(MapleReactorStats stats, int rid) {
this.evstate = (byte) 0;
this.stats = stats;
this.rid = rid;
this.alive = true;
}
public void setShouldCollect(boolean collect) {
this.shouldCollect = collect;
}
public boolean getShouldCollect() {
return shouldCollect;
}
public void lockReactor() {
reactorLock.lock();
}
public void unlockReactor() {
reactorLock.unlock();
}
public void hitLockReactor() {
hitLock.lock();
reactorLock.lock();
}
public void hitUnlockReactor() {
reactorLock.unlock();
hitLock.unlock();
}
public void setState(byte state) {
this.state = state;
}
public byte getState() {
return state;
}
public void setEventState(byte substate) {
this.evstate = substate;
}
public byte getEventState() {
return evstate;
}
public MapleReactorStats getStats() {
return stats;
}
public int getId() {
return rid;
}
public void setDelay(int delay) {
this.delay = delay;
}
public int getDelay() {
return delay;
}
@Override
public MapleMapObjectType getType() {
return MapleMapObjectType.REACTOR;
}
public int getReactorType() {
return stats.getType(state);
}
public boolean isRecentHitFromAttack() {
return attackHit;
}
public void setMap(MapleMap map) {
this.map = map;
}
public MapleMap getMap() {
return map;
}
public Pair<Integer, Integer> getReactItem(byte index) {
return stats.getReactItem(state, index);
}
public boolean isAlive() {
return alive;
}
public boolean isActive() {
return alive && stats.getType(state) != -1;
}
public void setAlive(boolean alive) {
this.alive = alive;
}
@Override
public void sendDestroyData(MapleClient client) {
client.announce(makeDestroyData());
}
public final byte[] makeDestroyData() {
return MaplePacketCreator.destroyReactor(this);
}
@Override
public void sendSpawnData(MapleClient client) {
if (this.isAlive()) {
client.announce(makeSpawnData());
}
}
public final byte[] makeSpawnData() {
return MaplePacketCreator.spawnReactor(this);
}
public void resetReactorActions(int newState) {
setState((byte) newState);
cancelReactorTimeout();
setShouldCollect(true);
refreshReactorTimeout();
if (map != null) {
map.searchItemReactors(this);
}
}
public void forceHitReactor(final byte newState) {
this.lockReactor();
try {
this.resetReactorActions(newState);
map.broadcastMessage(MaplePacketCreator.triggerReactor(this, (short) 0));
} finally {
this.unlockReactor();
}
}
private void tryForceHitReactor(final byte newState) { // weak hit state signal, if already changed reactor state before timeout then drop this
if (!reactorLock.tryLock()) {
return;
}
try {
this.resetReactorActions(newState);
map.broadcastMessage(MaplePacketCreator.triggerReactor(this, (short) 0));
} finally {
reactorLock.unlock();
}
}
public void cancelReactorTimeout() {
if (timeoutTask != null) {
timeoutTask.cancel(false);
timeoutTask = null;
}
}
private void refreshReactorTimeout() {
int timeOut = stats.getTimeout(state);
if (timeOut > -1) {
final byte nextState = stats.getTimeoutState(state);
timeoutTask = TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
timeoutTask = null;
tryForceHitReactor(nextState);
}
}, timeOut);
}
}
public void delayedHitReactor(final MapleClient c, long delay) {
TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
hitReactor(c);
}
}, delay);
}
public void hitReactor(MapleClient c) {
hitReactor(false, 0, (short) 0, 0, c);
}
public void hitReactor(boolean wHit, int charPos, short stance, int skillid, MapleClient c) {
try {
if (!this.isActive()) {
return;
}
if (hitLock.tryLock()) {
this.lockReactor();
try {
cancelReactorTimeout();
attackHit = wHit;
if (YamlConfig.config.server.USE_DEBUG == true) {
c.getPlayer().dropMessage(5, "Hitted REACTOR " + this.getId() + " with POS " + charPos + " , STANCE " + stance + " , SkillID " + skillid + " , STATE " + stats.getType(state) + " STATESIZE " + stats.getStateSize(state));
}
ReactorScriptManager.getInstance().onHit(c, this);
int reactorType = stats.getType(state);
if (reactorType < 999 && reactorType != -1) {//type 2 = only hit from right (kerning swamp plants), 00 is air left 02 is ground left
if (!(reactorType == 2 && (stance == 0 || stance == 2))) { //get next state
for (byte b = 0; b < stats.getStateSize(state); b++) {//YAY?
List<Integer> activeSkills = stats.getActiveSkills(state, b);
if (activeSkills != null) {
if (!activeSkills.contains(skillid)) {
continue;
}
}
state = stats.getNextState(state, b);
if (stats.getNextState(state, b) == -1) {//end of reactor
if (reactorType < 100) {//reactor broken
if (delay > 0) {
map.destroyReactor(getObjectId());
} else {//trigger as normal
map.broadcastMessage(MaplePacketCreator.triggerReactor(this, stance));
}
} else {//item-triggered on final step
map.broadcastMessage(MaplePacketCreator.triggerReactor(this, stance));
}
ReactorScriptManager.getInstance().act(c, this);
} else { //reactor not broken yet
map.broadcastMessage(MaplePacketCreator.triggerReactor(this, stance));
if (state == stats.getNextState(state, b)) {//current state = next state, looping reactor
ReactorScriptManager.getInstance().act(c, this);
}
setShouldCollect(true); // refresh collectability on item drop-based reactors
refreshReactorTimeout();
if (stats.getType(state) == 100) {
map.searchItemReactors(this);
}
}
break;
}
}
} else {
state++;
map.broadcastMessage(MaplePacketCreator.triggerReactor(this, stance));
if (this.getId() != 9980000 && this.getId() != 9980001) {
ReactorScriptManager.getInstance().act(c, this);
}
setShouldCollect(true);
refreshReactorTimeout();
if (stats.getType(state) == 100) {
map.searchItemReactors(this);
}
}
} finally {
this.unlockReactor();
hitLock.unlock(); // non-encapsulated unlock found thanks to MiLin
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public boolean destroy() {
if (reactorLock.tryLock()) {
try {
boolean alive = this.isAlive();
if (alive) {
this.setAlive(false);
this.cancelReactorTimeout();
if (this.getDelay() > 0) {
this.delayedRespawn();
}
} else if (this.inDelayedRespawn()) {
return false;
} else {
return true; // reactor neither alive nor in delayed respawn, remove map object allowed
}
} finally {
reactorLock.unlock();
}
}
map.broadcastMessage(MaplePacketCreator.destroyReactor(this));
return false;
}
private void respawn() {
this.lockReactor();
try {
this.resetReactorActions(0);
this.setAlive(true);
} finally {
this.unlockReactor();
}
map.broadcastMessage(this.makeSpawnData());
}
public void delayedRespawn() {
Runnable r = new Runnable() {
@Override
public void run() {
delayedRespawnRun = null;
respawn();
}
};
delayedRespawnRun = r;
OverallService service = (OverallService) map.getChannelServer().getServiceAccess(ChannelServices.OVERALL);
service.registerOverallAction(map.getId(), r, this.getDelay());
}
public boolean forceDelayedRespawn() {
Runnable r = delayedRespawnRun;
if (r != null) {
OverallService service = (OverallService) map.getChannelServer().getServiceAccess(ChannelServices.OVERALL);
service.forceRunOverallAction(map.getId(), r);
return true;
} else {
return false;
}
}
public boolean inDelayedRespawn() {
return delayedRespawnRun != null;
}
public Rectangle getArea() {
return new Rectangle(getPosition().x + stats.getTL().x, getPosition().y + stats.getTL().y, stats.getBR().x - stats.getTL().x, stats.getBR().y - stats.getTL().y);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public GuardianSpawnPoint getGuardian() {
return guardian;
}
public void setGuardian(GuardianSpawnPoint guardian) {
this.guardian = guardian;
}
public final void setFacingDirection(final byte facingDirection) {
this.facingDirection = facingDirection;
}
public final byte getFacingDirection() {
return facingDirection;
}
}

View File

@@ -0,0 +1,172 @@
/*
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 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.maps;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import server.maps.MapleReactorStats.StateData;
import tools.Pair;
import tools.StringUtil;
public class MapleReactorFactory {
private static MapleDataProvider data = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Reactor.wz"));
private static Map<Integer, MapleReactorStats> reactorStats = new HashMap<Integer, MapleReactorStats>();
public static final MapleReactorStats getReactorS(int rid) {
MapleReactorStats stats = reactorStats.get(Integer.valueOf(rid));
if (stats == null) {
int infoId = rid;
MapleData reactorData = data.getData(StringUtil.getLeftPaddedStr(Integer.toString(infoId) + ".img", '0', 11));
MapleData link = reactorData.getChildByPath("info/link");
if (link != null) {
infoId = MapleDataTool.getIntConvert("info/link", reactorData);
stats = reactorStats.get(Integer.valueOf(infoId));
}
if (stats == null) {
stats = new MapleReactorStats();
reactorData = data.getData(StringUtil.getLeftPaddedStr(Integer.toString(infoId) + ".img", '0', 11));
if (reactorData == null) {
return stats;
}
boolean canTouch = MapleDataTool.getInt("info/activateByTouch", reactorData, 0) > 0;
boolean areaSet = false;
boolean foundState = false;
for (byte i = 0; true; i++) {
MapleData reactorD = reactorData.getChildByPath(String.valueOf(i));
if (reactorD == null) {
break;
}
MapleData reactorInfoData_ = reactorD.getChildByPath("event");
if (reactorInfoData_ != null && reactorInfoData_.getChildByPath("0") != null) {
MapleData reactorInfoData = reactorInfoData_.getChildByPath("0");
Pair<Integer, Integer> reactItem = null;
int type = MapleDataTool.getIntConvert("type", reactorInfoData);
if (type == 100) { //reactor waits for item
reactItem = new Pair<Integer, Integer>(MapleDataTool.getIntConvert("0", reactorInfoData), MapleDataTool.getIntConvert("1", reactorInfoData, 1));
if (!areaSet) { //only set area of effect for item-triggered reactors once
stats.setTL(MapleDataTool.getPoint("lt", reactorInfoData));
stats.setBR(MapleDataTool.getPoint("rb", reactorInfoData));
areaSet = true;
}
}
foundState = true;
stats.addState(i, type, reactItem, (byte) MapleDataTool.getIntConvert("state", reactorInfoData), MapleDataTool.getIntConvert("timeOut", reactorInfoData_, -1), (byte) (canTouch ? 2 : (MapleDataTool.getIntConvert("2", reactorInfoData, 0) > 0 || reactorInfoData.getChildByPath("clickArea") != null || type == 9 ? 1 : 0)));
} else {
stats.addState(i, 999, null, (byte) (foundState ? -1 : (i + 1)), 0, (byte) 0);
}
}
reactorStats.put(Integer.valueOf(infoId), stats);
if (rid != infoId) {
reactorStats.put(Integer.valueOf(rid), stats);
}
} else { // stats exist at infoId but not rid; add to map
reactorStats.put(Integer.valueOf(rid), stats);
}
}
return stats;
}
public static MapleReactorStats getReactor(int rid) {
MapleReactorStats stats = reactorStats.get(Integer.valueOf(rid));
if (stats == null) {
int infoId = rid;
MapleData reactorData = data.getData(StringUtil.getLeftPaddedStr(Integer.toString(infoId) + ".img", '0', 11));
MapleData link = reactorData.getChildByPath("info/link");
if (link != null) {
infoId = MapleDataTool.getIntConvert("info/link", reactorData);
stats = reactorStats.get(Integer.valueOf(infoId));
}
MapleData activateOnTouch = reactorData.getChildByPath("info/activateByTouch");
boolean loadArea = false;
if (activateOnTouch != null) {
loadArea = MapleDataTool.getInt("info/activateByTouch", reactorData, 0) != 0;
}
if (stats == null) {
reactorData = data.getData(StringUtil.getLeftPaddedStr(Integer.toString(infoId) + ".img", '0', 11));
MapleData reactorInfoData = reactorData.getChildByPath("0");
stats = new MapleReactorStats();
List<StateData> statedatas = new ArrayList<>();
if (reactorInfoData != null) {
boolean areaSet = false;
byte i = 0;
while (reactorInfoData != null) {
MapleData eventData = reactorInfoData.getChildByPath("event");
if (eventData != null) {
int timeOut = -1;
for (MapleData fknexon : eventData.getChildren()) {
if (fknexon.getName().equalsIgnoreCase("timeOut")) {
timeOut = MapleDataTool.getInt(fknexon);
} else {
Pair<Integer, Integer> reactItem = null;
int type = MapleDataTool.getIntConvert("type", fknexon);
if (type == 100) { //reactor waits for item
reactItem = new Pair<Integer, Integer>(MapleDataTool.getIntConvert("0", fknexon), MapleDataTool.getIntConvert("1", fknexon));
if (!areaSet || loadArea) { //only set area of effect for item-triggered reactors once
stats.setTL(MapleDataTool.getPoint("lt", fknexon));
stats.setBR(MapleDataTool.getPoint("rb", fknexon));
areaSet = true;
}
}
MapleData activeSkillID = fknexon.getChildByPath("activeSkillID");
List<Integer> skillids = null;
if (activeSkillID != null) {
skillids = new ArrayList<Integer>();
for (MapleData skill : activeSkillID.getChildren()) {
skillids.add(MapleDataTool.getInt(skill));
}
}
byte nextState = (byte) MapleDataTool.getIntConvert("state", fknexon);
statedatas.add(new StateData(type, reactItem, skillids, nextState));
}
}
stats.addState(i, statedatas, timeOut);
}
i++;
reactorInfoData = reactorData.getChildByPath(Byte.toString(i));
statedatas = new ArrayList<>();
}
} else //sit there and look pretty; likely a reactor such as Zakum/Papulatus doors that shows if player can enter
{
statedatas.add(new StateData(999, null, null, (byte) 0));
stats.addState((byte) 0, statedatas, -1);
}
reactorStats.put(Integer.valueOf(infoId), stats);
if (rid != infoId) {
reactorStats.put(Integer.valueOf(rid), stats);
}
} else // stats exist at infoId but not rid; add to map
{
reactorStats.put(Integer.valueOf(rid), stats);
}
}
return stats;
}
}

View File

@@ -0,0 +1,148 @@
/*
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 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.maps;
import java.awt.Point;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import tools.Pair;
/**
* @author Lerk
* @author Ronan
*/
public class MapleReactorStats {
private Point tl;
private Point br;
private Map<Byte, List<StateData>> stateInfo = new HashMap<>();
private Map<Byte, Integer> timeoutInfo = new HashMap<>();
public void setTL(Point tl) {
this.tl = tl;
}
public void setBR(Point br) {
this.br = br;
}
public Point getTL() {
return tl;
}
public Point getBR() {
return br;
}
public void addState(byte state, List<StateData> data, int timeOut) {
stateInfo.put(state, data);
if(timeOut > -1) timeoutInfo.put(state, timeOut);
}
public void addState(byte state, int type, Pair<Integer, Integer> reactItem, byte nextState, int timeOut, byte canTouch) {
List<StateData> data = new ArrayList<>();
data.add(new StateData(type, reactItem, null, nextState));
stateInfo.put(state, data);
}
public int getTimeout(byte state) {
Integer i = timeoutInfo.get(state);
return (i == null) ? -1 : i;
}
public byte getTimeoutState(byte state) {
return stateInfo.get(state).get(stateInfo.get(state).size() - 1).getNextState();
}
public byte getStateSize(byte state) {
return (byte) stateInfo.get(state).size();
}
public byte getNextState(byte state, byte index) {
if (stateInfo.get(state) == null || stateInfo.get(state).size() < (index + 1)) return -1;
StateData nextState = stateInfo.get(state).get(index);
if (nextState != null) {
return nextState.getNextState();
} else {
return -1;
}
}
public List<Integer> getActiveSkills(byte state, byte index) {
StateData nextState = stateInfo.get(state).get(index);
if (nextState != null) {
return nextState.getActiveSkills();
} else {
return null;
}
}
public int getType(byte state) {
List<StateData> list = stateInfo.get(state);
if (list != null) {
return list.get(0).getType();
} else {
return -1;
}
}
public Pair<Integer, Integer> getReactItem(byte state, byte index) {
StateData nextState = stateInfo.get(state).get(index);
if (nextState != null) {
return nextState.getReactItem();
} else {
return null;
}
}
public static class StateData {
private int type;
private Pair<Integer, Integer> reactItem;
private List<Integer> activeSkills;
private byte nextState;
public StateData(int type, Pair<Integer, Integer> reactItem, List<Integer> activeSkills, byte nextState) {
this.type = type;
this.reactItem = reactItem;
this.activeSkills = activeSkills;
this.nextState = nextState;
}
private int getType() {
return type;
}
private byte getNextState() {
return nextState;
}
private Pair<Integer, Integer> getReactItem() {
return reactItem;
}
private List<Integer> getActiveSkills() {
return activeSkills;
}
}
}

View File

@@ -0,0 +1,102 @@
/*
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 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.maps;
import java.awt.Point;
import client.MapleCharacter;
import client.MapleClient;
import client.SkillFactory;
import tools.MaplePacketCreator;
/**
*
* @author Jan
*/
public class MapleSummon extends AbstractAnimatedMapleMapObject {
private MapleCharacter owner;
private byte skillLevel;
private int skill, hp;
private SummonMovementType movementType;
public MapleSummon(MapleCharacter owner, int skill, Point pos, SummonMovementType movementType) {
this.owner = owner;
this.skill = skill;
this.skillLevel = owner.getSkillLevel(SkillFactory.getSkill(skill));
if (skillLevel == 0) throw new RuntimeException();
this.movementType = movementType;
setPosition(pos);
}
@Override
public void sendSpawnData(MapleClient client) {
client.announce(MaplePacketCreator.spawnSummon(this, false));
}
@Override
public void sendDestroyData(MapleClient client) {
client.announce(MaplePacketCreator.removeSummon(this, true));
}
public MapleCharacter getOwner() {
return owner;
}
public int getSkill() {
return skill;
}
public int getHP() {
return hp;
}
public void addHP(int delta) {
this.hp += delta;
}
public SummonMovementType getMovementType() {
return movementType;
}
public boolean isStationary() {
return (skill == 3111002 || skill == 3211002 || skill == 5211001 || skill == 13111004);
}
public byte getSkillLevel() {
return skillLevel;
}
@Override
public MapleMapObjectType getType() {
return MapleMapObjectType.SUMMON;
}
public final boolean isPuppet() {
switch (skill) {
case 3111002:
case 3211002:
case 13111004:
return true;
}
return false;
}
}

View File

@@ -0,0 +1,71 @@
/*
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 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.maps;
import client.MapleCharacter;
import java.util.List;
import net.server.Server;
import server.TimerManager;
import tools.MaplePacketCreator;
/*
* MapleTVEffect
* @author MrXotic (XoticStory)
* @author Ronan - made MapleTV mechanics synchronous
*/
public class MapleTVEffect {
private final static boolean ACTIVE[] = new boolean[Server.getInstance().getWorldsSize()];
public static synchronized boolean broadcastMapleTVIfNotActive(MapleCharacter player, MapleCharacter victim, List<String> messages, int tvType){
int w = player.getWorld();
if(!ACTIVE[w]) {
broadcastTV(true, w, messages, player, tvType, victim);
return true;
}
return false;
}
private static synchronized void broadcastTV(boolean activity, final int userWorld, List<String> message, MapleCharacter user, int type, MapleCharacter partner) {
Server server = Server.getInstance();
ACTIVE[userWorld] = activity;
if (activity) {
server.broadcastMessage(userWorld, MaplePacketCreator.enableTV());
server.broadcastMessage(userWorld, MaplePacketCreator.sendTV(user, message, type <= 2 ? type : type - 3, partner));
int delay = 15000;
if (type == 4) {
delay = 30000;
} else if (type == 5) {
delay = 60000;
}
TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
broadcastTV(false, userWorld, null, null, -1, null);
}
}, delay);
} else {
server.broadcastMessage(userWorld, MaplePacketCreator.removeTV());
}
}
}

View File

@@ -0,0 +1,32 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 ~ 2010 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/>.
*/
package server.maps;
public class ReactorDropEntry {
public ReactorDropEntry(int itemId, int chance, int questId) {
this.itemId = itemId;
this.chance = chance;
this.questid = questId;
}
public int itemId, chance, questid;
public int assignedRangeStart, assignedRangeLength;
}

View File

@@ -0,0 +1,39 @@
/*
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 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.maps;
public class SavedLocation {
private int mapid = 102000000, portal;
public SavedLocation(int mapid, int portal) {
this.mapid = mapid;
this.portal = portal;
}
public int getMapId() {
return mapid;
}
public int getPortal() {
return portal;
}
}

View File

@@ -0,0 +1,40 @@
/*
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 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.maps;
public enum SavedLocationType {
FREE_MARKET,
WORLDTOUR,
FLORINA,
INTRO,
SUNDAY_MARKET,
MIRROR,
EVENT,
BOSSPQ,
HAPPYVILLE,
MONSTER_CARNIVAL,
DEVELOPER;
public static SavedLocationType fromString(String Str) {
return valueOf(Str);
}
}

View File

@@ -0,0 +1,35 @@
/*
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 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.maps;
public enum SummonMovementType {
STATIONARY(0), FOLLOW(1), CIRCLE_FOLLOW(3);
private final int val;
private SummonMovementType(int val) {
this.val = val;
}
public int getValue() {
return val;
}
}