Switch to Maven file structure
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
57
src/main/java/server/maps/AbstractMapleMapObject.java
Normal file
57
src/main/java/server/maps/AbstractMapleMapObject.java
Normal 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;
|
||||
}
|
||||
}
|
||||
28
src/main/java/server/maps/AnimatedMapleMapObject.java
Normal file
28
src/main/java/server/maps/AnimatedMapleMapObject.java
Normal 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();
|
||||
}
|
||||
69
src/main/java/server/maps/FieldLimit.java
Normal file
69
src/main/java/server/maps/FieldLimit.java
Normal 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;
|
||||
}
|
||||
}
|
||||
61
src/main/java/server/maps/MapMonitor.java
Normal file
61
src/main/java/server/maps/MapMonitor.java
Normal 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;
|
||||
}
|
||||
}
|
||||
197
src/main/java/server/maps/MapleDoor.java
Normal file
197
src/main/java/server/maps/MapleDoor.java
Normal 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;
|
||||
}
|
||||
}
|
||||
188
src/main/java/server/maps/MapleDoorObject.java
Normal file
188
src/main/java/server/maps/MapleDoorObject.java
Normal 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;
|
||||
}
|
||||
}
|
||||
65
src/main/java/server/maps/MapleDragon.java
Normal file
65
src/main/java/server/maps/MapleDragon.java
Normal 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;
|
||||
}
|
||||
}
|
||||
103
src/main/java/server/maps/MapleFoothold.java
Normal file
103
src/main/java/server/maps/MapleFoothold.java
Normal 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;
|
||||
}
|
||||
}
|
||||
222
src/main/java/server/maps/MapleFootholdTree.java
Normal file
222
src/main/java/server/maps/MapleFootholdTree.java
Normal 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;
|
||||
}
|
||||
}
|
||||
171
src/main/java/server/maps/MapleGenericPortal.java
Normal file
171
src/main/java/server/maps/MapleGenericPortal.java
Normal 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;
|
||||
}
|
||||
}
|
||||
759
src/main/java/server/maps/MapleHiredMerchant.java
Normal file
759
src/main/java/server/maps/MapleHiredMerchant.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
60
src/main/java/server/maps/MapleKite.java
Normal file
60
src/main/java/server/maps/MapleKite.java
Normal 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);
|
||||
}
|
||||
}
|
||||
4624
src/main/java/server/maps/MapleMap.java
Normal file
4624
src/main/java/server/maps/MapleMap.java
Normal file
File diff suppressed because it is too large
Load Diff
48
src/main/java/server/maps/MapleMapEffect.java
Normal file
48
src/main/java/server/maps/MapleMapEffect.java
Normal 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());
|
||||
}
|
||||
}
|
||||
444
src/main/java/server/maps/MapleMapFactory.java
Normal file
444
src/main/java/server/maps/MapleMapFactory.java
Normal 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 "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
216
src/main/java/server/maps/MapleMapItem.java
Normal file
216
src/main/java/server/maps/MapleMapItem.java
Normal 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));
|
||||
}
|
||||
}
|
||||
143
src/main/java/server/maps/MapleMapManager.java
Normal file
143
src/main/java/server/maps/MapleMapManager.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
36
src/main/java/server/maps/MapleMapObject.java
Normal file
36
src/main/java/server/maps/MapleMapObject.java
Normal 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();
|
||||
}
|
||||
26
src/main/java/server/maps/MapleMapObjectType.java
Normal file
26
src/main/java/server/maps/MapleMapObjectType.java
Normal 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;
|
||||
}
|
||||
28
src/main/java/server/maps/MapleMapPortal.java
Normal file
28
src/main/java/server/maps/MapleMapPortal.java
Normal 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);
|
||||
}
|
||||
}
|
||||
126
src/main/java/server/maps/MapleMiniDungeon.java
Normal file
126
src/main/java/server/maps/MapleMiniDungeon.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
85
src/main/java/server/maps/MapleMiniDungeonInfo.java
Normal file
85
src/main/java/server/maps/MapleMiniDungeonInfo.java
Normal 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;
|
||||
}
|
||||
}
|
||||
520
src/main/java/server/maps/MapleMiniGame.java
Normal file
520
src/main/java/server/maps/MapleMiniGame.java
Normal 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;
|
||||
}
|
||||
}
|
||||
168
src/main/java/server/maps/MapleMist.java
Normal file
168
src/main/java/server/maps/MapleMist.java
Normal 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();
|
||||
}
|
||||
}
|
||||
620
src/main/java/server/maps/MaplePlayerShop.java
Normal file
620
src/main/java/server/maps/MaplePlayerShop.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
66
src/main/java/server/maps/MaplePlayerShopItem.java
Normal file
66
src/main/java/server/maps/MaplePlayerShopItem.java
Normal 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;
|
||||
}
|
||||
}
|
||||
46
src/main/java/server/maps/MaplePortal.java
Normal file
46
src/main/java/server/maps/MaplePortal.java
Normal 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();
|
||||
}
|
||||
68
src/main/java/server/maps/MaplePortalFactory.java
Normal file
68
src/main/java/server/maps/MaplePortalFactory.java
Normal 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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
431
src/main/java/server/maps/MapleReactor.java
Normal file
431
src/main/java/server/maps/MapleReactor.java
Normal 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;
|
||||
}
|
||||
}
|
||||
172
src/main/java/server/maps/MapleReactorFactory.java
Normal file
172
src/main/java/server/maps/MapleReactorFactory.java
Normal 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;
|
||||
}
|
||||
}
|
||||
148
src/main/java/server/maps/MapleReactorStats.java
Normal file
148
src/main/java/server/maps/MapleReactorStats.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
102
src/main/java/server/maps/MapleSummon.java
Normal file
102
src/main/java/server/maps/MapleSummon.java
Normal 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;
|
||||
}
|
||||
}
|
||||
71
src/main/java/server/maps/MapleTVEffect.java
Normal file
71
src/main/java/server/maps/MapleTVEffect.java
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
32
src/main/java/server/maps/ReactorDropEntry.java
Normal file
32
src/main/java/server/maps/ReactorDropEntry.java
Normal 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;
|
||||
}
|
||||
39
src/main/java/server/maps/SavedLocation.java
Normal file
39
src/main/java/server/maps/SavedLocation.java
Normal 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;
|
||||
}
|
||||
}
|
||||
40
src/main/java/server/maps/SavedLocationType.java
Normal file
40
src/main/java/server/maps/SavedLocationType.java
Normal 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);
|
||||
}
|
||||
}
|
||||
35
src/main/java/server/maps/SummonMovementType.java
Normal file
35
src/main/java/server/maps/SummonMovementType.java
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user