Source for my MapleSolaxiaV2 (v83 MapleStory).
This commit is contained in:
ronancpl
2015-11-02 23:17:21 -02:00
parent 324982e94f
commit 972517e7b2
1675 changed files with 261831 additions and 0 deletions
@@ -0,0 +1,106 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <[email protected]>
Matthias Butz <[email protected]>
Jan Christian Meyer <[email protected]>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License 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.life;
import server.maps.AbstractAnimatedMapleMapObject;
public abstract class AbstractLoadedMapleLife extends AbstractAnimatedMapleMapObject {
private final int id;
private int f;
private boolean hide;
private int fh;
private int start_fh;
private int cy;
private int rx0;
private int rx1;
public AbstractLoadedMapleLife(int id) {
this.id = id;
}
public AbstractLoadedMapleLife(AbstractLoadedMapleLife life) {
this(life.getId());
this.f = life.f;
this.hide = life.hide;
this.fh = life.fh;
this.start_fh = life.fh;
this.cy = life.cy;
this.rx0 = life.rx0;
this.rx1 = life.rx1;
}
public int getF() {
return f;
}
public void setF(int f) {
this.f = f;
}
public boolean isHidden() {
return hide;
}
public void setHide(boolean hide) {
this.hide = hide;
}
public int getFh() {
return fh;
}
public void setFh(int fh) {
this.fh = fh;
}
public int getStartFh() {
return start_fh;
}
public int getCy() {
return cy;
}
public void setCy(int cy) {
this.cy = cy;
}
public int getRx0() {
return rx0;
}
public void setRx0(int rx0) {
this.rx0 = rx0;
}
public int getRx1() {
return rx1;
}
public void setRx1(int rx1) {
this.rx1 = rx1;
}
public int getId() {
return id;
}
}
+46
View File
@@ -0,0 +1,46 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <[email protected]>
Matthias Butz <[email protected]>
Jan Christian Meyer <[email protected]>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License 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.life;
public enum Element {
NEUTRAL, FIRE, ICE, LIGHTING, POISON, HOLY, DARK;
public static Element getFromChar(char c) {
switch (Character.toUpperCase(c)) {
case 'F':
return FIRE;
case 'I':
return ICE;
case 'L':
return LIGHTING;
case 'S':
return POISON;
case 'H':
return HOLY;
case 'D':
return DARK;
case 'P':
return NEUTRAL;
}
throw new IllegalArgumentException("unknown elemnt char " + c);
}
}
@@ -0,0 +1,41 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <[email protected]>
Matthias Butz <[email protected]>
Jan Christian Meyer <[email protected]>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License 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.life;
public enum ElementalEffectiveness {
NORMAL, IMMUNE, STRONG, WEAK, NEUTRAL;
public static ElementalEffectiveness getByNumber(int num) {
switch (num) {
case 1:
return IMMUNE;
case 2:
return STRONG;
case 3:
return WEAK;
case 4:
return NEUTRAL;
default:
throw new IllegalArgumentException("Unkown effectiveness: " + num);
}
}
}
+236
View File
@@ -0,0 +1,236 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <[email protected]>
Matthias Butz <[email protected]>
Jan Christian Meyer <[email protected]>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License 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.life;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import provider.wz.MapleDataType;
import tools.Pair;
import tools.StringUtil;
public class MapleLifeFactory {
private static MapleDataProvider data = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Mob.wz"));
private final static MapleDataProvider stringDataWZ = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/String.wz"));
private static MapleData mobStringData = stringDataWZ.getData("Mob.img");
private static MapleData npcStringData = stringDataWZ.getData("Npc.img");
private static Map<Integer, MapleMonsterStats> monsterStats = new HashMap<>();
public static AbstractLoadedMapleLife getLife(int id, String type) {
if (type.equalsIgnoreCase("n")) {
return getNPC(id);
} else if (type.equalsIgnoreCase("m")) {
return getMonster(id);
} else {
System.out.println("Unknown Life type: " + type);
return null;
}
}
public static MapleMonster getMonster(int mid) {
MapleMonsterStats stats = monsterStats.get(Integer.valueOf(mid));
if (stats == null) {
MapleData monsterData = data.getData(StringUtil.getLeftPaddedStr(Integer.toString(mid) + ".img", '0', 11));
if (monsterData == null) {
return null;
}
MapleData monsterInfoData = monsterData.getChildByPath("info");
stats = new MapleMonsterStats();
stats.setHp(MapleDataTool.getIntConvert("maxHP", monsterInfoData));
stats.setFriendly(MapleDataTool.getIntConvert("damagedByMob", monsterInfoData, 0) == 1);
stats.setPADamage(MapleDataTool.getIntConvert("PADamage", monsterInfoData));
stats.setPDDamage(MapleDataTool.getIntConvert("PDDamage", monsterInfoData));
stats.setMADamage(MapleDataTool.getIntConvert("MADamage", monsterInfoData));
stats.setMDDamage(MapleDataTool.getIntConvert("MDDamage", monsterInfoData));
stats.setMp(MapleDataTool.getIntConvert("maxMP", monsterInfoData, 0));
stats.setExp(MapleDataTool.getIntConvert("exp", monsterInfoData, 0));
stats.setLevel(MapleDataTool.getIntConvert("level", monsterInfoData));
stats.setRemoveAfter(MapleDataTool.getIntConvert("removeAfter", monsterInfoData, 0));
stats.setBoss(MapleDataTool.getIntConvert("boss", monsterInfoData, 0) > 0);
stats.setExplosiveReward(MapleDataTool.getIntConvert("explosiveReward", monsterInfoData, 0) > 0);
stats.setFfaLoot(MapleDataTool.getIntConvert("publicReward", monsterInfoData, 0) > 0);
stats.setUndead(MapleDataTool.getIntConvert("undead", monsterInfoData, 0) > 0);
stats.setName(MapleDataTool.getString(mid + "/name", mobStringData, "MISSINGNO"));
stats.setBuffToGive(MapleDataTool.getIntConvert("buff", monsterInfoData, -1));
stats.setCP(MapleDataTool.getIntConvert("getCP", monsterInfoData, 0));
stats.setRemoveOnMiss(MapleDataTool.getIntConvert("removeOnMiss", monsterInfoData, 0) > 0);
MapleData special = monsterInfoData.getChildByPath("coolDamage");
if (special != null) {
int coolDmg = MapleDataTool.getIntConvert("coolDamage", monsterInfoData);
int coolProb = MapleDataTool.getIntConvert("coolDamageProb", monsterInfoData, 0);
stats.setCool(new Pair<>(coolDmg, coolProb));
}
special = monsterInfoData.getChildByPath("loseItem");
if (special != null) {
for (MapleData liData : special.getChildren()) {
stats.addLoseItem(new loseItem(MapleDataTool.getInt(liData.getChildByPath("id")), (byte) MapleDataTool.getInt(liData.getChildByPath("prop")), (byte) MapleDataTool.getInt(liData.getChildByPath("x"))));
}
}
special = monsterInfoData.getChildByPath("selfDestruction");
if (special != null) {
stats.setSelfDestruction(new selfDestruction((byte) MapleDataTool.getInt(special.getChildByPath("action")), MapleDataTool.getIntConvert("removeAfter", special, -1), MapleDataTool.getIntConvert("hp", special, -1)));
}
MapleData firstAttackData = monsterInfoData.getChildByPath("firstAttack");
int firstAttack = 0;
if (firstAttackData != null) {
if (firstAttackData.getType() == MapleDataType.FLOAT) {
firstAttack = Math.round(MapleDataTool.getFloat(firstAttackData));
} else {
firstAttack = MapleDataTool.getInt(firstAttackData);
}
}
stats.setFirstAttack(firstAttack > 0);
stats.setDropPeriod(MapleDataTool.getIntConvert("dropItemPeriod", monsterInfoData, 0) * 10000);
stats.setTagColor(MapleDataTool.getIntConvert("hpTagColor", monsterInfoData, 0));
stats.setTagBgColor(MapleDataTool.getIntConvert("hpTagBgcolor", monsterInfoData, 0));
for (MapleData idata : monsterData) {
if (!idata.getName().equals("info")) {
int delay = 0;
for (MapleData pic : idata.getChildren()) {
delay += MapleDataTool.getIntConvert("delay", pic, 0);
}
stats.setAnimationTime(idata.getName(), delay);
}
}
MapleData reviveInfo = monsterInfoData.getChildByPath("revive");
if (reviveInfo != null) {
List<Integer> revives = new LinkedList<>();
for (MapleData data_ : reviveInfo) {
revives.add(MapleDataTool.getInt(data_));
}
stats.setRevives(revives);
}
decodeElementalString(stats, MapleDataTool.getString("elemAttr", monsterInfoData, ""));
MapleData monsterSkillData = monsterInfoData.getChildByPath("skill");
if (monsterSkillData != null) {
int i = 0;
List<Pair<Integer, Integer>> skills = new ArrayList<>();
while (monsterSkillData.getChildByPath(Integer.toString(i)) != null) {
skills.add(new Pair<>(Integer.valueOf(MapleDataTool.getInt(i + "/skill", monsterSkillData, 0)), Integer.valueOf(MapleDataTool.getInt(i + "/level", monsterSkillData, 0))));
i++;
}
stats.setSkills(skills);
}
MapleData banishData = monsterInfoData.getChildByPath("ban");
if (banishData != null) {
stats.setBanishInfo(new BanishInfo(MapleDataTool.getString("banMsg", banishData), MapleDataTool.getInt("banMap/0/field", banishData, -1), MapleDataTool.getString("banMap/0/portal", banishData, "sp")));
}
monsterStats.put(Integer.valueOf(mid), stats);
}
MapleMonster ret = new MapleMonster(mid, stats);
return ret;
}
private static void decodeElementalString(MapleMonsterStats stats, String elemAttr) {
for (int i = 0; i < elemAttr.length(); i += 2) {
stats.setEffectiveness(Element.getFromChar(elemAttr.charAt(i)), ElementalEffectiveness.getByNumber(Integer.valueOf(String.valueOf(elemAttr.charAt(i + 1)))));
}
}
public static MapleNPC getNPC(int nid) {
return new MapleNPC(nid, new MapleNPCStats(MapleDataTool.getString(nid + "/name", npcStringData, "MISSINGNO")));
}
public static class BanishInfo {
private int map;
private String portal, msg;
public BanishInfo(String msg, int map, String portal) {
this.msg = msg;
this.map = map;
this.portal = portal;
}
public int getMap() {
return map;
}
public String getPortal() {
return portal;
}
public String getMsg() {
return msg;
}
}
public static class loseItem {
private int id;
private byte chance, x;
private loseItem(int id, byte chance, byte x) {
this.id = id;
this.chance = chance;
this.x = x;
}
public int getId() {
return id;
}
public byte getChance() {
return chance;
}
public byte getX() {
return x;
}
}
public static class selfDestruction {
private byte action;
private int removeAfter;
private int hp;
private selfDestruction(byte action, int removeAfter, int hp) {
this.action = action;
this.removeAfter = removeAfter;
this.hp = hp;
}
public int getHp() {
return hp;
}
public byte getAction() {
return action;
}
public int removeAfter() {
return removeAfter;
}
}
}
+954
View File
@@ -0,0 +1,954 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <[email protected]>
Matthias Butz <[email protected]>
Jan Christian Meyer <[email protected]>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License 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.life;
import client.MapleBuffStat;
import client.MapleCharacter;
import client.MapleClient;
import client.MapleJob;
import client.Skill;
import client.SkillFactory;
import client.status.MonsterStatus;
import client.status.MonsterStatusEffect;
import constants.ServerConstants;
import constants.skills.FPMage;
import constants.skills.Hermit;
import constants.skills.ILMage;
import constants.skills.NightLord;
import constants.skills.NightWalker;
import constants.skills.Shadower;
import constants.skills.SuperGM;
import java.awt.Point;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import net.server.world.MapleParty;
import net.server.world.MaplePartyCharacter;
import scripting.event.EventInstanceManager;
import server.TimerManager;
import server.life.MapleLifeFactory.BanishInfo;
import server.maps.MapleMap;
import server.maps.MapleMapObject;
import server.maps.MapleMapObjectType;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.Randomizer;
public class MapleMonster extends AbstractLoadedMapleLife {
private MapleMonsterStats stats;
private int hp, mp;
private WeakReference<MapleCharacter> controller = new WeakReference<>(null);
private boolean controllerHasAggro, controllerKnowsAboutAggro;
private EventInstanceManager eventInstance = null;
private Collection<MonsterListener> listeners = new LinkedList<>();
private EnumMap<MonsterStatus, MonsterStatusEffect> stati = new EnumMap<>(MonsterStatus.class);
private ArrayList<MonsterStatus> alreadyBuffed = new ArrayList<MonsterStatus>();
private MapleMap map;
private int VenomMultiplier = 0;
private boolean fake = false;
private boolean dropsDisabled = false;
private List<Pair<Integer, Integer>> usedSkills = new ArrayList<>();
private Map<Pair<Integer, Integer>, Integer> skillsUsed = new HashMap<>();
private List<Integer> stolenItems = new ArrayList<>();
private int team;
private final HashMap<Integer, AtomicInteger> takenDamage = new HashMap<>();
public ReentrantLock monsterLock = new ReentrantLock();
public MapleMonster(int id, MapleMonsterStats stats) {
super(id);
initWithStats(stats);
}
public MapleMonster(MapleMonster monster) {
super(monster);
initWithStats(monster.stats);
}
private void initWithStats(MapleMonsterStats stats) {
setStance(5);
this.stats = stats;
hp = stats.getHp();
mp = stats.getMp();
}
public void disableDrops() {
this.dropsDisabled = true;
}
public boolean dropsDisabled() {
return dropsDisabled;
}
public void setMap(MapleMap map) {
this.map = map;
}
public int getHp() {
return hp;
}
public void setHp(int hp) {
this.hp = hp;
}
public int getMaxHp() {
return stats.getHp();
}
public int getMp() {
return mp;
}
public void setMp(int mp) {
if (mp < 0) {
mp = 0;
}
this.mp = mp;
}
public int getMaxMp() {
return stats.getMp();
}
public int getExp() {
return stats.getExp();
}
int getLevel() {
return stats.getLevel();
}
public int getCP() {
return stats.getCP();
}
public int getTeam() {
return team;
}
public void setTeam(int team) {
this.team = team;
}
public int getVenomMulti() {
return this.VenomMultiplier;
}
public void setVenomMulti(int multiplier) {
this.VenomMultiplier = multiplier;
}
public MapleMonsterStats getStats() {
return stats;
}
public boolean isBoss() {
return stats.isBoss() || isHT();
}
public int getAnimationTime(String name) {
return stats.getAnimationTime(name);
}
private List<Integer> getRevives() {
return stats.getRevives();
}
private byte getTagColor() {
return stats.getTagColor();
}
private byte getTagBgColor() {
return stats.getTagBgColor();
}
/**
*
* @param from the player that dealt the damage
* @param damage
*/
public synchronized void damage(MapleCharacter from, int damage) { // may be pointless synchronization
if (!isAlive()) {
return;
}
int trueDamage = Math.min(hp, damage); // since magic happens otherwise B^)
if(ServerConstants.USE_DEBUG == true && from != null) from.dropMessage(5, "Hitted MOB " + this.getId());
hp -= damage;
if (takenDamage.containsKey(from.getId())) {
takenDamage.get(from.getId()).addAndGet(trueDamage);
} else {
takenDamage.put(from.getId(), new AtomicInteger(trueDamage));
}
if (hasBossHPBar()) {
from.getMap().broadcastMessage(makeBossHPBarPacket(), getPosition());
} else if (!isBoss()) {
int remainingHP = (int) Math.max(1, hp * 100f / getMaxHp());
byte[] packet = MaplePacketCreator.showMonsterHP(getObjectId(), remainingHP);
if (from.getParty() != null) {
for (MaplePartyCharacter mpc : from.getParty().getMembers()) {
MapleCharacter member = from.getMap().getCharacterById(mpc.getId()); // god bless
if (member != null) {
member.announce(packet.clone()); // clone it just in case of crypto
}
}
} else {
from.announce(packet);
}
}
}
public void heal(int hp, int mp) {
int hp2Heal = getHp() + hp;
int mp2Heal = getMp() + mp;
if (hp2Heal >= getMaxHp()) {
hp2Heal = getMaxHp();
}
if (mp2Heal >= getMaxMp()) {
mp2Heal = getMaxMp();
}
setHp(hp2Heal);
setMp(mp2Heal);
getMap().broadcastMessage(MaplePacketCreator.healMonster(getObjectId(), hp));
}
public boolean isAttackedBy(MapleCharacter chr) {
return takenDamage.containsKey(chr.getId());
}
private void distributeExperienceToParty(int pid, int exp, int killer, Map<Integer, Integer> expDist) {
LinkedList<MapleCharacter> members = new LinkedList<>();
map.getCharacterReadLock().lock();
Collection<MapleCharacter> chrs = map.getCharacters();
try {
for (MapleCharacter mc : chrs) {
if (mc.getPartyId() == pid) {
members.add(mc);
}
}
} finally {
map.getCharacterReadLock().unlock();
}
final int minLevel = getLevel() - 5;
int partyLevel = 0;
int leechMinLevel = 0;
for (MapleCharacter mc : members) {
if (mc.getLevel() >= minLevel) {
leechMinLevel = Math.min(mc.getLevel() - 5, minLevel);
}
}
int leechCount = 0;
for (MapleCharacter mc : members) {
if (mc.getLevel() >= leechMinLevel) {
partyLevel += mc.getLevel();
leechCount++;
}
}
final int mostDamageCid = getHighestDamagerId();
for (MapleCharacter mc : members) {
int id = mc.getId();
int level = mc.getLevel();
if (expDist.containsKey(id)
|| level >= leechMinLevel) {
boolean isKiller = killer == id;
boolean mostDamage = mostDamageCid == id;
int xp = (int) (exp * 0.80f * level / partyLevel);
if (mostDamage) {
xp += (exp * 0.20f);
}
giveExpToCharacter(mc, xp, isKiller, leechCount);
}
}
}
public void distributeExperience(int killerId) {
if (isAlive()) {
return;
}
int exp = getExp();
int totalHealth = getMaxHp();
Map<Integer, Integer> expDist = new HashMap<>();
Map<Integer, Integer> partyExp = new HashMap<>();
// 80% of pool is split amongst all the damagers
for (Entry<Integer, AtomicInteger> damage : takenDamage.entrySet()) {
expDist.put(damage.getKey(), (int) (0.80f * exp * damage.getValue().get() / totalHealth));
}
map.getCharacterReadLock().lock(); // avoid concurrent mod
Collection<MapleCharacter> chrs = map.getCharacters();
try {
for (MapleCharacter mc : chrs) {
if (expDist.containsKey(mc.getId())) {
boolean isKiller = mc.getId() == killerId;
int xp = expDist.get(mc.getId());
if (isKiller) {
xp += exp / 5;
}
MapleParty p = mc.getParty();
if (p != null) {
int pID = p.getId();
int pXP = xp + (partyExp.containsKey(pID) ? partyExp.get(pID) : 0);
partyExp.put(pID, pXP);
} else {
giveExpToCharacter(mc, xp, isKiller, 1);
}
}
}
} finally {
map.getCharacterReadLock().unlock();
}
for (Entry<Integer, Integer> party : partyExp.entrySet()) {
distributeExperienceToParty(party.getKey(), party.getValue(), killerId, expDist);
}
}
public void giveExpToCharacter(MapleCharacter attacker, int exp, boolean isKiller, int numExpSharers) {
if (isKiller) {
if (eventInstance != null) {
eventInstance.monsterKilled(attacker, this);
}
}
final int partyModifier = numExpSharers > 1 ? (110 + (5 * (numExpSharers - 2))) : 0;
int partyExp = 0;
if (attacker.getHp() > 0) {
int personalExp = exp * attacker.getExpRate();
if (exp > 0) {
if (partyModifier > 0) {
partyExp = (int) (personalExp * ServerConstants.PARTY_EXPERIENCE_MOD * partyModifier / 1000f);
}
Integer holySymbol = attacker.getBuffedValue(MapleBuffStat.HOLY_SYMBOL);
boolean GMHolySymbol = attacker.getBuffSource(MapleBuffStat.HOLY_SYMBOL) == SuperGM.HOLY_SYMBOL;
if (holySymbol != null) {
if (numExpSharers == 1 && !GMHolySymbol) {
personalExp *= 1.0 + (holySymbol.doubleValue() / 500.0);
} else {
personalExp *= 1.0 + (holySymbol.doubleValue() / 100.0);
}
}
if (stati.containsKey(MonsterStatus.SHOWDOWN)) {
personalExp *= (stati.get(MonsterStatus.SHOWDOWN).getStati().get(MonsterStatus.SHOWDOWN).doubleValue() / 100.0 + 1.0);
}
}
if (exp < 0) {//O.O ><
personalExp = Integer.MAX_VALUE;
}
attacker.gainExp(personalExp, partyExp, true, false, isKiller);
attacker.mobKilled(getId());
attacker.increaseEquipExp(personalExp);//better place
}
}
public MapleCharacter killBy(MapleCharacter killer) {
distributeExperience(killer != null ? killer.getId() : 0);
if (getController() != null) { // this can/should only happen when a hidden gm attacks the monster
getController().getClient().announce(MaplePacketCreator.stopControllingMonster(this.getObjectId()));
getController().stopControllingMonster(this);
}
final List<Integer> toSpawn = this.getRevives(); // this doesn't work (?)
if (toSpawn != null) {
final MapleMap reviveMap = killer.getMap();
if (toSpawn.contains(9300216) && reviveMap.getId() > 925000000 && reviveMap.getId() < 926000000) {
reviveMap.broadcastMessage(MaplePacketCreator.playSound("Dojang/clear"));
reviveMap.broadcastMessage(MaplePacketCreator.showEffect("dojang/end/clear"));
}
Pair<Integer, String> timeMob = reviveMap.getTimeMob();
if (timeMob != null) {
if (toSpawn.contains(timeMob.getLeft())) {
reviveMap.broadcastMessage(MaplePacketCreator.serverNotice(6, timeMob.getRight()));
}
if (timeMob.getLeft() == 9300338 && (reviveMap.getId() >= 922240100 && reviveMap.getId() <= 922240119)) {
if (!reviveMap.containsNPC(9001108)) {
MapleNPC npc = MapleLifeFactory.getNPC(9001108);
npc.setPosition(new Point(172, 9));
npc.setCy(9);
npc.setRx0(172 + 50);
npc.setRx1(172 - 50);
npc.setFh(27);
reviveMap.addMapObject(npc);
reviveMap.broadcastMessage(MaplePacketCreator.spawnNPC(npc));
} else {
reviveMap.toggleHiddenNPC(9001108);
}
}
}
TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
for (Integer mid : toSpawn) {
final MapleMonster mob = MapleLifeFactory.getMonster(mid);
mob.setPosition(getPosition());
if (dropsDisabled()) {
mob.disableDrops();
}
reviveMap.spawnMonster(mob);
}
}
}, getAnimationTime("die1"));
}
if (eventInstance != null) {
if (!this.getStats().isFriendly()) {
eventInstance.monsterKilled(this);
}
}
// idk V just a troll
for (MonsterListener listener : listeners.toArray(new MonsterListener[listeners.size()])) {
listener.monsterKilled(getAnimationTime("die1"));
}
MapleCharacter looter = map.getCharacterById(getHighestDamagerId());
return looter != null ? looter : killer;
}
// should only really be used to determine drop owner
private int getHighestDamagerId() {
int curId = 0;
int curDmg = 0;
for (Entry<Integer, AtomicInteger> damage : takenDamage.entrySet()) {
curId = damage.getValue().get() >= curDmg ? damage.getKey() : curId;
curDmg = damage.getKey() == curId ? damage.getValue().get() : curDmg;
}
return curId;
}
public boolean isAlive() {
return this.hp > 0;
}
public MapleCharacter getController() {
return controller.get();
}
public void setController(MapleCharacter controller) {
this.controller = new WeakReference<>(controller);
}
public void switchController(MapleCharacter newController, boolean immediateAggro) {
MapleCharacter controllers = getController();
if (controllers == newController) {
return;
}
if (controllers != null) {
controllers.stopControllingMonster(this);
controllers.getClient().announce(MaplePacketCreator.stopControllingMonster(getObjectId()));
}
newController.controlMonster(this, immediateAggro);
setController(newController);
if (immediateAggro) {
setControllerHasAggro(true);
}
setControllerKnowsAboutAggro(false);
}
public void addListener(MonsterListener listener) {
listeners.add(listener);
}
public boolean isControllerHasAggro() {
return fake ? false : controllerHasAggro;
}
public void setControllerHasAggro(boolean controllerHasAggro) {
if (fake) {
return;
}
this.controllerHasAggro = controllerHasAggro;
}
public boolean isControllerKnowsAboutAggro() {
return fake ? false : controllerKnowsAboutAggro;
}
public void setControllerKnowsAboutAggro(boolean controllerKnowsAboutAggro) {
if (fake) {
return;
}
this.controllerKnowsAboutAggro = controllerKnowsAboutAggro;
}
public byte[] makeBossHPBarPacket() {
return MaplePacketCreator.showBossHP(getId(), getHp(), getMaxHp(), getTagColor(), getTagBgColor());
}
public boolean hasBossHPBar() {
return (isBoss() && getTagColor() > 0) || isHT();
}
private boolean isHT() {
return getId() == 8810018;
}
@Override
public void sendSpawnData(MapleClient c) {
if (!isAlive()) {
return;
}
if (isFake()) {
c.announce(MaplePacketCreator.spawnFakeMonster(this, 0));
} else {
c.announce(MaplePacketCreator.spawnMonster(this, false));
}
if (stati.size() > 0) {
for (final MonsterStatusEffect mse : this.stati.values()) {
c.announce(MaplePacketCreator.applyMonsterStatus(getObjectId(), mse, null));
}
}
if (hasBossHPBar()) {
if (this.getMap().countMonster(8810026) > 0 && this.getMap().getId() == 240060200) {
this.getMap().killAllMonsters();
return;
}
c.announce(makeBossHPBarPacket());
}
}
@Override
public void sendDestroyData(MapleClient client) {
client.announce(MaplePacketCreator.killMonster(getObjectId(), false));
}
@Override
public MapleMapObjectType getType() {
return MapleMapObjectType.MONSTER;
}
public void setEventInstance(EventInstanceManager eventInstance) {
this.eventInstance = eventInstance;
}
public EventInstanceManager getEventInstance() {
return eventInstance;
}
public boolean isMobile() {
return stats.isMobile();
}
public ElementalEffectiveness getEffectiveness(Element e) {
if (stati.size() > 0 && stati.get(MonsterStatus.DOOM) != null) {
return ElementalEffectiveness.NORMAL; // like blue snails
}
return stats.getEffectiveness(e);
}
public boolean applyStatus(MapleCharacter from, final MonsterStatusEffect status, boolean poison, long duration) {
return applyStatus(from, status, poison, duration, false);
}
public boolean applyStatus(MapleCharacter from, final MonsterStatusEffect status, boolean poison, long duration, boolean venom) {
switch (stats.getEffectiveness(status.getSkill().getElement())) {
case IMMUNE:
case STRONG:
case NEUTRAL:
return false;
case NORMAL:
case WEAK:
break;
default: {
System.out.println("Unknown elemental effectiveness: " + stats.getEffectiveness(status.getSkill().getElement()));
return false;
}
}
if (status.getSkill().getId() == FPMage.ELEMENT_COMPOSITION) { // fp compo
ElementalEffectiveness effectiveness = stats.getEffectiveness(Element.POISON);
if (effectiveness == ElementalEffectiveness.IMMUNE || effectiveness == ElementalEffectiveness.STRONG) {
return false;
}
} else if (status.getSkill().getId() == ILMage.ELEMENT_COMPOSITION) { // il compo
ElementalEffectiveness effectiveness = stats.getEffectiveness(Element.ICE);
if (effectiveness == ElementalEffectiveness.IMMUNE || effectiveness == ElementalEffectiveness.STRONG) {
return false;
}
} else if (status.getSkill().getId() == NightLord.VENOMOUS_STAR || status.getSkill().getId() == Shadower.VENOMOUS_STAB || status.getSkill().getId() == NightWalker.VENOM) {// venom
if (stats.getEffectiveness(Element.POISON) == ElementalEffectiveness.WEAK) {
return false;
}
}
if (poison && getHp() <= 1) {
return false;
}
final Map<MonsterStatus, Integer> statis = status.getStati();
if (stats.isBoss()) {
if (!(statis.containsKey(MonsterStatus.SPEED)
&& statis.containsKey(MonsterStatus.NINJA_AMBUSH)
&& statis.containsKey(MonsterStatus.WATK))) {
return false;
}
}
for (MonsterStatus stat : statis.keySet()) {
final MonsterStatusEffect oldEffect = stati.get(stat);
if (oldEffect != null) {
oldEffect.removeActiveStatus(stat);
if (oldEffect.getStati().isEmpty()) {
oldEffect.cancelTask();
oldEffect.cancelDamageSchedule();
}
}
}
TimerManager timerManager = TimerManager.getInstance();
final Runnable cancelTask = new Runnable() {
@Override
public void run() {
if (isAlive()) {
byte[] packet = MaplePacketCreator.cancelMonsterStatus(getObjectId(), status.getStati());
map.broadcastMessage(packet, getPosition());
if (getController() != null && !getController().isMapObjectVisible(MapleMonster.this)) {
getController().getClient().announce(packet);
}
}
for (MonsterStatus stat : status.getStati().keySet()) {
stati.remove(stat);
}
setVenomMulti(0);
status.cancelDamageSchedule();
}
};
if (poison) {
int poisonLevel = from.getSkillLevel(status.getSkill());
int poisonDamage = Math.min(Short.MAX_VALUE, (int) (getMaxHp() / (70.0 - poisonLevel) + 0.999));
status.setValue(MonsterStatus.POISON, Integer.valueOf(poisonDamage));
status.setDamageSchedule(timerManager.register(new DamageTask(poisonDamage, from, status, cancelTask, 0), 1000, 1000));
} else if (venom) {
if (from.getJob() == MapleJob.NIGHTLORD || from.getJob() == MapleJob.SHADOWER || from.getJob().isA(MapleJob.NIGHTWALKER3)) {
int poisonLevel, matk, id = from.getJob().getId();
int skill = (id == 412 ? NightLord.VENOMOUS_STAR : (id == 422 ? Shadower.VENOMOUS_STAB : NightWalker.VENOM));
poisonLevel = from.getSkillLevel(SkillFactory.getSkill(skill));
if (poisonLevel <= 0) {
return false;
}
matk = SkillFactory.getSkill(skill).getEffect(poisonLevel).getMatk();
int luk = from.getLuk();
int maxDmg = (int) Math.ceil(Math.min(Short.MAX_VALUE, 0.2 * luk * matk));
int minDmg = (int) Math.ceil(Math.min(Short.MAX_VALUE, 0.1 * luk * matk));
int gap = maxDmg - minDmg;
if (gap == 0) {
gap = 1;
}
int poisonDamage = 0;
for (int i = 0; i < getVenomMulti(); i++) {
poisonDamage += (Randomizer.nextInt(gap) + minDmg);
}
poisonDamage = Math.min(Short.MAX_VALUE, poisonDamage);
status.setValue(MonsterStatus.VENOMOUS_WEAPON, Integer.valueOf(poisonDamage));
status.setDamageSchedule(timerManager.register(new DamageTask(poisonDamage, from, status, cancelTask, 0), 1000, 1000));
} else {
return false;
}
} else if (status.getSkill().getId() == Hermit.SHADOW_WEB || status.getSkill().getId() == NightWalker.SHADOW_WEB) { //Shadow Web
status.setDamageSchedule(timerManager.schedule(new DamageTask((int) (getMaxHp() / 50.0 + 0.999), from, status, cancelTask, 1), 3500));
} else if (status.getSkill().getId() == 4121004 || status.getSkill().getId() == 4221004) { // Ninja Ambush
final Skill skill = SkillFactory.getSkill(status.getSkill().getId());
final byte level = from.getSkillLevel(skill);
final int damage = (int) ((from.getStr() + from.getLuk()) * (1.5 + (level * 0.05)) * skill.getEffect(level).getDamage());
/*if (getHp() - damage <= 1) { make hp 1 betch
damage = getHp() - (getHp() - 1);
}*/
status.setValue(MonsterStatus.NINJA_AMBUSH, Integer.valueOf(damage));
status.setDamageSchedule(timerManager.register(new DamageTask(damage, from, status, cancelTask, 2), 1000, 1000));
}
for (MonsterStatus stat : status.getStati().keySet()) {
stati.put(stat, status);
alreadyBuffed.add(stat);
}
int animationTime = status.getSkill().getAnimationTime();
byte[] packet = MaplePacketCreator.applyMonsterStatus(getObjectId(), status, null);
map.broadcastMessage(packet, getPosition());
if (getController() != null && !getController().isMapObjectVisible(this)) {
getController().getClient().announce(packet);
}
status.setCancelTask(timerManager.schedule(cancelTask, duration + animationTime));
return true;
}
public void applyMonsterBuff(final Map<MonsterStatus, Integer> stats, final int x, int skillId, long duration, MobSkill skill, final List<Integer> reflection) {
TimerManager timerManager = TimerManager.getInstance();
final Runnable cancelTask = new Runnable() {
@Override
public void run() {
if (isAlive()) {
byte[] packet = MaplePacketCreator.cancelMonsterStatus(getObjectId(), stats);
map.broadcastMessage(packet, getPosition());
if (getController() != null && !getController().isMapObjectVisible(MapleMonster.this)) {
getController().getClient().announce(packet);
}
for (final MonsterStatus stat : stats.keySet()) {
stati.remove(stat);
}
}
}
};
final MonsterStatusEffect effect = new MonsterStatusEffect(stats, null, skill, true);
byte[] packet = MaplePacketCreator.applyMonsterStatus(getObjectId(), effect, reflection);
map.broadcastMessage(packet, getPosition());
for (MonsterStatus stat : stats.keySet()) {
stati.put(stat, effect);
alreadyBuffed.add(stat);
}
if (getController() != null && !getController().isMapObjectVisible(this)) {
getController().getClient().announce(packet);
}
effect.setCancelTask(timerManager.schedule(cancelTask, duration));
}
public void debuffMob(int skillid) {
//skillid is not going to be used for now until I get warrior debuff working
MonsterStatus[] stats = {MonsterStatus.WEAPON_ATTACK_UP, MonsterStatus.WEAPON_DEFENSE_UP, MonsterStatus.MAGIC_ATTACK_UP, MonsterStatus.MAGIC_DEFENSE_UP};
for (int i = 0; i < stats.length; i++) {
if (isBuffed(stats[i])) {
final MonsterStatusEffect oldEffect = stati.get(stats[i]);
byte[] packet = MaplePacketCreator.cancelMonsterStatus(getObjectId(), oldEffect.getStati());
map.broadcastMessage(packet, getPosition());
if (getController() != null && !getController().isMapObjectVisible(MapleMonster.this)) {
getController().getClient().announce(packet);
}
stati.remove(stats);
}
}
}
public boolean isBuffed(MonsterStatus status) {
return stati.containsKey(status);
}
public void setFake(boolean fake) {
this.fake = fake;
}
public boolean isFake() {
return fake;
}
public MapleMap getMap() {
return map;
}
public List<Pair<Integer, Integer>> getSkills() {
return stats.getSkills();
}
public boolean hasSkill(int skillId, int level) {
return stats.hasSkill(skillId, level);
}
public boolean canUseSkill(MobSkill toUse) {
if (toUse == null) {
return false;
}
for (Pair<Integer, Integer> skill : usedSkills) {
if (skill.getLeft() == toUse.getSkillId() && skill.getRight() == toUse.getSkillLevel()) {
return false;
}
}
if (toUse.getLimit() > 0) {
if (this.skillsUsed.containsKey(new Pair<>(toUse.getSkillId(), toUse.getSkillLevel()))) {
int times = this.skillsUsed.get(new Pair<>(toUse.getSkillId(), toUse.getSkillLevel()));
if (times >= toUse.getLimit()) {
return false;
}
}
}
if (toUse.getSkillId() == 200) {
Collection<MapleMapObject> mmo = getMap().getMapObjects();
int i = 0;
for (MapleMapObject mo : mmo) {
if (mo.getType() == MapleMapObjectType.MONSTER) {
i++;
}
}
if (i > 100) {
return false;
}
}
return true;
}
public void usedSkill(final int skillId, final int level, long cooltime) {
this.usedSkills.add(new Pair<>(skillId, level));
if (this.skillsUsed.containsKey(new Pair<>(skillId, level))) {
int times = this.skillsUsed.get(new Pair<>(skillId, level)) + 1;
this.skillsUsed.remove(new Pair<>(skillId, level));
this.skillsUsed.put(new Pair<>(skillId, level), times);
} else {
this.skillsUsed.put(new Pair<>(skillId, level), 1);
}
final MapleMonster mons = this;
TimerManager tMan = TimerManager.getInstance();
tMan.schedule(
new Runnable() {
@Override
public void run() {
mons.clearSkill(skillId, level);
}
}, cooltime);
}
public void clearSkill(int skillId, int level) {
int index = -1;
for (Pair<Integer, Integer> skill : usedSkills) {
if (skill.getLeft() == skillId && skill.getRight() == level) {
index = usedSkills.indexOf(skill);
break;
}
}
if (index != -1) {
usedSkills.remove(index);
}
}
public int getNoSkills() {
return this.stats.getNoSkills();
}
public boolean isFirstAttack() {
return this.stats.isFirstAttack();
}
public int getBuffToGive() {
return this.stats.getBuffToGive();
}
private final class DamageTask implements Runnable {
private final int dealDamage;
private final MapleCharacter chr;
private final MonsterStatusEffect status;
private final Runnable cancelTask;
private final int type;
private final MapleMap map;
private DamageTask(int dealDamage, MapleCharacter chr, MonsterStatusEffect status, Runnable cancelTask, int type) {
this.dealDamage = dealDamage;
this.chr = chr;
this.status = status;
this.cancelTask = cancelTask;
this.type = type;
this.map = chr.getMap();
}
@Override
public void run() {
int damage = dealDamage;
if (damage >= hp) {
damage = hp - 1;
if (type == 1 || type == 2) {
map.broadcastMessage(MaplePacketCreator.damageMonster(getObjectId(), damage), getPosition());
cancelTask.run();
status.getCancelTask().cancel(false);
}
}
if (hp > 1 && damage > 0) {
damage(chr, damage);
if (type == 1) {
map.broadcastMessage(MaplePacketCreator.damageMonster(getObjectId(), damage), getPosition());
}
}
}
}
public String getName() {
return stats.getName();
}
public void addStolen(int itemId) {
stolenItems.add(itemId);
}
public List<Integer> getStolen() {
return stolenItems;
}
public void setTempEffectiveness(Element e, ElementalEffectiveness ee, long milli) {
final Element fE = e;
final ElementalEffectiveness fEE = stats.getEffectiveness(e);
if (!stats.getEffectiveness(e).equals(ElementalEffectiveness.WEAK)) {
stats.setEffectiveness(e, ee);
TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
stats.removeEffectiveness(fE);
stats.setEffectiveness(fE, fEE);
}
}, milli);
}
}
public Collection<MonsterStatus> alreadyBuffedStats() {
return Collections.unmodifiableCollection(alreadyBuffed);
}
public BanishInfo getBanish() {
return stats.getBanishInfo();
}
public void setBoss(boolean boss) {
this.stats.setBoss(boss);
}
public int getDropPeriodTime() {
return stats.getDropPeriod();
}
public int getPADamage() {
return stats.getPADamage();
}
public Map<MonsterStatus, MonsterStatusEffect> getStati() {
return stati;
}
}
@@ -0,0 +1,171 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 ~ 2010 Patrick Huy <[email protected]>
Matthias Butz <[email protected]>
Jan Christian Meyer <[email protected]>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License version 3
as published by the Free Software Foundation. You may not use, modify
or distribute this program under any other version of the
GNU Affero General Public License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package server.life;
import java.io.File;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import tools.DatabaseConnection;
import tools.Pair;
public class MapleMonsterInformationProvider {
// Author : LightPepsi
private static final MapleMonsterInformationProvider instance = new MapleMonsterInformationProvider();
private final Map<Integer, List<MonsterDropEntry>> drops = new HashMap<>();
private final List<MonsterGlobalDropEntry> globaldrops = new ArrayList<>();
protected MapleMonsterInformationProvider() {
retrieveGlobal();
}
public static MapleMonsterInformationProvider getInstance() {
return instance;
}
public final List<MonsterGlobalDropEntry> getGlobalDrop() {
return globaldrops;
}
private void retrieveGlobal() {
PreparedStatement ps = null;
ResultSet rs = null;
try {
final Connection con = DatabaseConnection.getConnection();
ps = con.prepareStatement("SELECT * FROM drop_data_global WHERE chance > 0");
rs = ps.executeQuery();
while (rs.next()) {
globaldrops.add(
new MonsterGlobalDropEntry(
rs.getInt("itemid"),
rs.getInt("chance"),
rs.getInt("continent"),
rs.getByte("dropType"),
rs.getInt("minimum_quantity"),
rs.getInt("maximum_quantity"),
rs.getShort("questid")));
}
rs.close();
ps.close();
} catch (SQLException e) {
System.err.println("Error retrieving drop" + e);
} finally {
try {
if (ps != null) {
ps.close();
}
if (rs != null) {
rs.close();
}
} catch (SQLException ignore) {
}
}
}
public final List<MonsterDropEntry> retrieveDrop(final int monsterId) {
if (drops.containsKey(monsterId)) {
return drops.get(monsterId);
}
final List<MonsterDropEntry> ret = new LinkedList<>();
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = DatabaseConnection.getConnection().prepareStatement("SELECT * FROM drop_data WHERE dropperid = ?");
ps.setInt(1, monsterId);
rs = ps.executeQuery();
while (rs.next()) {
ret.add(
new MonsterDropEntry(
rs.getInt("itemid"),
rs.getInt("chance"),
rs.getInt("minimum_quantity"),
rs.getInt("maximum_quantity"),
rs.getShort("questid")));
}
} catch (SQLException e) {
return ret;
} finally {
try {
if (ps != null) {
ps.close();
}
if (rs != null) {
rs.close();
}
} catch (SQLException ignore) {
return ret;
}
}
drops.put(monsterId, ret);
return ret;
}
public static ArrayList<Pair<Integer, String>> getMobsIDsFromName(String search)
{
MapleDataProvider dataProvider = MapleDataProviderFactory.getDataProvider(new File("wz/String.wz"));
ArrayList<Pair<Integer, String>> retMobs = new ArrayList<Pair<Integer, String>>();
MapleData data = dataProvider.getData("Mob.img");
List<Pair<Integer, String>> mobPairList = new LinkedList<Pair<Integer, String>>();
for (MapleData mobIdData : data.getChildren()) {
int mobIdFromData = Integer.parseInt(mobIdData.getName());
String mobNameFromData = MapleDataTool.getString(mobIdData.getChildByPath("name"), "NO-NAME");
mobPairList.add(new Pair<Integer, String>(mobIdFromData, mobNameFromData));
}
for (Pair<Integer, String> mobPair : mobPairList) {
if (mobPair.getRight().toLowerCase().contains(search.toLowerCase())) {
retMobs.add(mobPair);
}
}
return retMobs;
}
public static String getMobNameFromID(int id)
{
try
{
return MapleLifeFactory.getMonster(id).getName();
} catch (Exception e)
{
return null; //nonexistant mob
}
}
public final void clearDrops() {
drops.clear();
globaldrops.clear();
retrieveGlobal();
}
}
+327
View File
@@ -0,0 +1,327 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <[email protected]>
Matthias Butz <[email protected]>
Jan Christian Meyer <[email protected]>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License 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.life;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import server.life.MapleLifeFactory.BanishInfo;
import server.life.MapleLifeFactory.loseItem;
import server.life.MapleLifeFactory.selfDestruction;
import tools.Pair;
/**
* @author Frz
*/
public class MapleMonsterStats {
private int exp, hp, mp, level, PADamage, PDDamage, MADamage, MDDamage, dropPeriod, cp, buffToGive, removeAfter;
private boolean boss, undead, ffaLoot, isExplosiveReward, firstAttack, removeOnMiss;
private String name;
private Map<String, Integer> animationTimes = new HashMap<String, Integer>();
private Map<Element, ElementalEffectiveness> resistance = new HashMap<Element, ElementalEffectiveness>();
private List<Integer> revives = Collections.emptyList();
private byte tagColor, tagBgColor;
private List<Pair<Integer, Integer>> skills = new ArrayList<Pair<Integer, Integer>>();
private Pair<Integer, Integer> cool = null;
private BanishInfo banish = null;
private List<loseItem> loseItem = null;
private selfDestruction selfDestruction = null;
private boolean friendly;
public int getExp() {
return exp;
}
public void setExp(int exp) {
this.exp = exp;
}
public int getHp() {
return hp;
}
public void setHp(int hp) {
this.hp = hp;
}
public int getMp() {
return mp;
}
public void setMp(int mp) {
this.mp = mp;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public int removeAfter() {
return removeAfter;
}
public void setRemoveAfter(int removeAfter) {
this.removeAfter = removeAfter;
}
public int getDropPeriod() {
return dropPeriod;
}
public void setDropPeriod(int dropPeriod) {
this.dropPeriod = dropPeriod;
}
public void setBoss(boolean boss) {
this.boss = boss;
}
public boolean isBoss() {
return boss;
}
public void setFfaLoot(boolean ffaLoot) {
this.ffaLoot = ffaLoot;
}
public boolean isFfaLoot() {
return ffaLoot;
}
public void setAnimationTime(String name, int delay) {
animationTimes.put(name, delay);
}
public int getAnimationTime(String name) {
Integer ret = animationTimes.get(name);
if (ret == null) {
return 500;
}
return ret.intValue();
}
public boolean isMobile() {
return animationTimes.containsKey("move") || animationTimes.containsKey("fly");
}
public List<Integer> getRevives() {
return revives;
}
public void setRevives(List<Integer> revives) {
this.revives = revives;
}
public void setUndead(boolean undead) {
this.undead = undead;
}
public boolean getUndead() {
return undead;
}
public void setEffectiveness(Element e, ElementalEffectiveness ee) {
resistance.put(e, ee);
}
public ElementalEffectiveness getEffectiveness(Element e) {
ElementalEffectiveness elementalEffectiveness = resistance.get(e);
if (elementalEffectiveness == null) {
return ElementalEffectiveness.NORMAL;
} else {
return elementalEffectiveness;
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public byte getTagColor() {
return tagColor;
}
public void setTagColor(int tagColor) {
this.tagColor = (byte) tagColor;
}
public byte getTagBgColor() {
return tagBgColor;
}
public void setTagBgColor(int tagBgColor) {
this.tagBgColor = (byte) tagBgColor;
}
public void setSkills(List<Pair<Integer, Integer>> skills) {
for (Pair<Integer, Integer> skill : skills) {
this.skills.add(skill);
}
}
public List<Pair<Integer, Integer>> getSkills() {
return Collections.unmodifiableList(this.skills);
}
public int getNoSkills() {
return this.skills.size();
}
public boolean hasSkill(int skillId, int level) {
for (Pair<Integer, Integer> skill : skills) {
if (skill.getLeft() == skillId && skill.getRight() == level) {
return true;
}
}
return false;
}
public void setFirstAttack(boolean firstAttack) {
this.firstAttack = firstAttack;
}
public boolean isFirstAttack() {
return firstAttack;
}
public void setBuffToGive(int buff) {
this.buffToGive = buff;
}
public int getBuffToGive() {
return buffToGive;
}
void removeEffectiveness(Element e) {
resistance.remove(e);
}
public BanishInfo getBanishInfo() {
return banish;
}
public void setBanishInfo(BanishInfo banish) {
this.banish = banish;
}
public int getPADamage() {
return PADamage;
}
public void setPADamage(int PADamage) {
this.PADamage = PADamage;
}
public int getCP() {
return cp;
}
public void setCP(int cp) {
this.cp = cp;
}
public List<loseItem> loseItem() {
return loseItem;
}
public void addLoseItem(loseItem li) {
if (loseItem == null) {
loseItem = new LinkedList<loseItem>();
}
loseItem.add(li);
}
public selfDestruction selfDestruction() {
return selfDestruction;
}
public void setSelfDestruction(selfDestruction sd) {
this.selfDestruction = sd;
}
public void setExplosiveReward(boolean isExplosiveReward) {
this.isExplosiveReward = isExplosiveReward;
}
public boolean isExplosiveReward() {
return isExplosiveReward;
}
public void setRemoveOnMiss(boolean removeOnMiss) {
this.removeOnMiss = removeOnMiss;
}
public boolean removeOnMiss() {
return removeOnMiss;
}
public void setCool(Pair<Integer, Integer> cool) {
this.cool = cool;
}
public Pair<Integer, Integer> getCool() {
return cool;
}
public int getPDDamage() {
return PDDamage;
}
public int getMADamage() {
return MADamage;
}
public int getMDDamage() {
return MDDamage;
}
public boolean isFriendly() {
return friendly;
}
public void setFriendly(boolean value) {
this.friendly = value;
}
public void setPDDamage(int PDDamage) {
this.PDDamage = PDDamage;
}
public void setMADamage(int MADamage) {
this.MADamage = MADamage;
}
public void setMDDamage(int MDDamage) {
this.MDDamage = MDDamage;
}
}
+68
View File
@@ -0,0 +1,68 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <[email protected]>
Matthias Butz <[email protected]>
Jan Christian Meyer <[email protected]>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License 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.life;
import client.MapleClient;
import server.MapleShopFactory;
import server.maps.MapleMapObjectType;
import tools.MaplePacketCreator;
public class MapleNPC extends AbstractLoadedMapleLife {
private MapleNPCStats stats;
public MapleNPC(int id, MapleNPCStats stats) {
super(id);
this.stats = stats;
}
public boolean hasShop() {
return MapleShopFactory.getInstance().getShopForNPC(getId()) != null;
}
public void sendShop(MapleClient c) {
MapleShopFactory.getInstance().getShopForNPC(getId()).sendShop(c);
}
@Override
public void sendSpawnData(MapleClient client) {
if (this.getId() > 9010010 && this.getId() < 9010014) {
client.announce(MaplePacketCreator.spawnNPCRequestController(this, false));
} else {
client.announce(MaplePacketCreator.spawnNPC(this));
client.announce(MaplePacketCreator.spawnNPCRequestController(this, true));
}
}
@Override
public void sendDestroyData(MapleClient client) {
client.announce(MaplePacketCreator.removeNPC(getObjectId()));
}
@Override
public MapleMapObjectType getType() {
return MapleMapObjectType.NPC;
}
public String getName() {
return stats.getName();
}
}
+42
View File
@@ -0,0 +1,42 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <[email protected]>
Matthias Butz <[email protected]>
Jan Christian Meyer <[email protected]>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License 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.life;
/**
*
* @author Matze
*/
public class MapleNPCStats {
private String name;
public MapleNPCStats(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
+77
View File
@@ -0,0 +1,77 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <[email protected]>
Matthias Butz <[email protected]>
Jan Christian Meyer <[email protected]>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License 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.life;
/**
*
* @author Danny (Leifde)
*/
public class MobAttackInfo {
private boolean isDeadlyAttack;
private int mpBurn;
private int diseaseSkill;
private int diseaseLevel;
private int mpCon;
public MobAttackInfo(int mobId, int attackId) {
}
public void setDeadlyAttack(boolean isDeadlyAttack) {
this.isDeadlyAttack = isDeadlyAttack;
}
public boolean isDeadlyAttack() {
return isDeadlyAttack;
}
public void setMpBurn(int mpBurn) {
this.mpBurn = mpBurn;
}
public int getMpBurn() {
return mpBurn;
}
public void setDiseaseSkill(int diseaseSkill) {
this.diseaseSkill = diseaseSkill;
}
public int getDiseaseSkill() {
return diseaseSkill;
}
public void setDiseaseLevel(int diseaseLevel) {
this.diseaseLevel = diseaseLevel;
}
public int getDiseaseLevel() {
return diseaseLevel;
}
public void setMpCon(int mpCon) {
this.mpCon = mpCon;
}
public int getMpCon() {
return mpCon;
}
}
+79
View File
@@ -0,0 +1,79 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <[email protected]>
Matthias Butz <[email protected]>
Jan Christian Meyer <[email protected]>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License 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.life;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import tools.StringUtil;
/**
*
* @author Danny (Leifde)
*/
public class MobAttackInfoFactory {
private static Map<String, MobAttackInfo> mobAttacks = new HashMap<String, MobAttackInfo>();
private static MapleDataProvider dataSource = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Mob.wz"));
public static MobAttackInfo getMobAttackInfo(MapleMonster mob, int attack) {
MobAttackInfo ret = mobAttacks.get(mob.getId() + "" + attack);
if (ret != null) {
return ret;
}
synchronized (mobAttacks) {
ret = mobAttacks.get(mob.getId() + "" + attack);
if (ret == null) {
MapleData mobData = dataSource.getData(StringUtil.getLeftPaddedStr(Integer.toString(mob.getId()) + ".img", '0', 11));
if (mobData != null) {
// MapleData infoData = mobData.getChildByPath("info");
String linkedmob = MapleDataTool.getString("link", mobData, "");
if (!linkedmob.equals("")) {
mobData = dataSource.getData(StringUtil.getLeftPaddedStr(linkedmob + ".img", '0', 11));
}
MapleData attackData = mobData.getChildByPath("attack" + (attack + 1) + "/info");
if (attackData == null) {
return null;
}
MapleData deadlyAttack = attackData.getChildByPath("deadlyAttack");
int mpBurn = MapleDataTool.getInt("mpBurn", attackData, 0);
int disease = MapleDataTool.getInt("disease", attackData, 0);
int level = MapleDataTool.getInt("level", attackData, 0);
int mpCon = MapleDataTool.getInt("conMP", attackData, 0);
ret = new MobAttackInfo(mob.getId(), attack);
ret.setDeadlyAttack(deadlyAttack != null);
ret.setMpBurn(mpBurn);
ret.setDiseaseSkill(disease);
ret.setDiseaseLevel(level);
ret.setMpCon(mpCon);
}
mobAttacks.put(mob.getId() + "" + attack, ret);
}
return ret;
}
}
}
+386
View File
@@ -0,0 +1,386 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <[email protected]>
Matthias Butz <[email protected]>
Jan Christian Meyer <[email protected]>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License 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.life;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import client.MapleCharacter;
import client.MapleDisease;
import client.status.MonsterStatus;
import java.util.LinkedList;
import java.util.Map;
import tools.Randomizer;
import server.maps.MapleMapObject;
import server.maps.MapleMapObjectType;
import server.maps.MapleMist;
import tools.ArrayMap;
/**
*
* @author Danny (Leifde)
*/
public class MobSkill {
private int skillId, skillLevel, mpCon;
private List<Integer> toSummon = new ArrayList<Integer>();
private int spawnEffect, hp, x, y;
private long duration, cooltime;
private float prop;
private Point lt, rb;
private int limit;
public MobSkill(int skillId, int level) {
this.skillId = skillId;
this.skillLevel = level;
}
public void setMpCon(int mpCon) {
this.mpCon = mpCon;
}
public void addSummons(List<Integer> toSummon) {
for (Integer summon : toSummon) {
this.toSummon.add(summon);
}
}
public void setSpawnEffect(int spawnEffect) {
this.spawnEffect = spawnEffect;
}
public void setHp(int hp) {
this.hp = hp;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setDuration(long duration) {
this.duration = duration;
}
public void setCoolTime(long cooltime) {
this.cooltime = cooltime;
}
public void setProp(float prop) {
this.prop = prop;
}
public void setLtRb(Point lt, Point rb) {
this.lt = lt;
this.rb = rb;
}
public void setLimit(int limit) {
this.limit = limit;
}
public void applyEffect(MapleCharacter player, MapleMonster monster, boolean skill) {
MapleDisease disease = null;
Map<MonsterStatus, Integer> stats = new ArrayMap<MonsterStatus, Integer>();
List<Integer> reflection = new LinkedList<Integer>();
switch (skillId) {
case 100:
case 110:
case 150:
stats.put(MonsterStatus.WEAPON_ATTACK_UP, Integer.valueOf(x));
break;
case 101:
case 111:
case 151:
stats.put(MonsterStatus.MAGIC_ATTACK_UP, Integer.valueOf(x));
break;
case 102:
case 112:
case 152:
stats.put(MonsterStatus.WEAPON_DEFENSE_UP, Integer.valueOf(x));
break;
case 103:
case 113:
case 153:
stats.put(MonsterStatus.MAGIC_DEFENSE_UP, Integer.valueOf(x));
break;
case 114:
if (lt != null && rb != null && skill) {
List<MapleMapObject> objects = getObjectsInRange(monster, MapleMapObjectType.MONSTER);
final int hps = (getX() / 1000) * (int) (950 + 1050 * Math.random());
for (MapleMapObject mons : objects) {
((MapleMonster) mons).heal(hps, getY());
}
} else {
monster.heal(getX(), getY());
}
break;
case 120:
disease = MapleDisease.SEAL;
break;
case 121:
disease = MapleDisease.DARKNESS;
break;
case 122:
disease = MapleDisease.WEAKEN;
break;
case 123:
disease = MapleDisease.STUN;
break;
case 124:
disease = MapleDisease.CURSE;
break;
case 125:
disease = MapleDisease.POISON;
break;
case 126: // Slow
disease = MapleDisease.SLOW;
break;
case 127:
if (lt != null && rb != null && skill) {
for (MapleCharacter character : getPlayersInRange(monster, player)) {
character.dispel();
}
} else {
player.dispel();
}
break;
case 128: // Seduce
disease = MapleDisease.SEDUCE;
break;
case 129: // Banish
if (lt != null && rb != null && skill) {
for (MapleCharacter chr : getPlayersInRange(monster, player)) {
chr.changeMapBanish(monster.getBanish().getMap(), monster.getBanish().getPortal(), monster.getBanish().getMsg());
}
} else {
player.changeMapBanish(monster.getBanish().getMap(), monster.getBanish().getPortal(), monster.getBanish().getMsg());
}
break;
case 131: // Mist
monster.getMap().spawnMist(new MapleMist(calculateBoundingBox(monster.getPosition(), true), monster, this), x * 10, false, false, false);
break;
case 132:
disease = MapleDisease.CONFUSE;
break;
case 133: // zombify
break;
case 140:
if (makeChanceResult() && !monster.isBuffed(MonsterStatus.MAGIC_IMMUNITY)) {
stats.put(MonsterStatus.WEAPON_IMMUNITY, Integer.valueOf(x));
}
break;
case 141:
if (makeChanceResult() && !monster.isBuffed(MonsterStatus.WEAPON_IMMUNITY)) {
stats.put(MonsterStatus.MAGIC_IMMUNITY, Integer.valueOf(x));
}
break;
case 143: // Weapon Reflect
stats.put(MonsterStatus.WEAPON_REFLECT, Integer.valueOf(x));
stats.put(MonsterStatus.WEAPON_IMMUNITY, Integer.valueOf(x));
reflection.add(x);
break;
case 144: // Magic Reflect
stats.put(MonsterStatus.MAGIC_REFLECT, Integer.valueOf(x));
stats.put(MonsterStatus.MAGIC_IMMUNITY, Integer.valueOf(x));
reflection.add(x);
break;
case 145: // Weapon / Magic reflect
stats.put(MonsterStatus.WEAPON_REFLECT, Integer.valueOf(x));
stats.put(MonsterStatus.WEAPON_IMMUNITY, Integer.valueOf(x));
stats.put(MonsterStatus.MAGIC_REFLECT, Integer.valueOf(x));
stats.put(MonsterStatus.MAGIC_IMMUNITY, Integer.valueOf(x));
reflection.add(x);
break;
case 154: // accuracy up
case 155: // avoid up
case 156: // speed up
break;
case 200:
if (monster.getMap().getSpawnedMonstersOnMap() < 80) {
for (Integer mobId : getSummons()) {
MapleMonster toSpawn = MapleLifeFactory.getMonster(mobId);
toSpawn.setPosition(monster.getPosition());
int ypos, xpos;
xpos = (int) monster.getPosition().getX();
ypos = (int) monster.getPosition().getY();
switch (mobId) {
case 8500003: // Pap bomb high
toSpawn.setFh((int) Math.ceil(Math.random() * 19.0));
ypos = -590;
break;
case 8500004: // Pap bomb
xpos = (int) (monster.getPosition().getX() + Randomizer.nextInt(1000) - 500);
if (ypos != -590) {
ypos = (int) monster.getPosition().getY();
}
break;
case 8510100: //Pianus bomb
if (Math.ceil(Math.random() * 5) == 1) {
ypos = 78;
xpos = (int) Randomizer.nextInt(5) + (Randomizer.nextInt(2) == 1 ? 180 : 0);
} else {
xpos = (int) (monster.getPosition().getX() + Randomizer.nextInt(1000) - 500);
}
break;
}
switch (monster.getMap().getId()) {
case 220080001: //Pap map
if (xpos < -890) {
xpos = (int) (Math.ceil(Math.random() * 150) - 890);
} else if (xpos > 230) {
xpos = (int) (230 - Math.ceil(Math.random() * 150));
}
break;
case 230040420: // Pianus map
if (xpos < -239) {
xpos = (int) (Math.ceil(Math.random() * 150) - 239);
} else if (xpos > 371) {
xpos = (int) (371 - Math.ceil(Math.random() * 150));
}
break;
}
toSpawn.setPosition(new Point(xpos, ypos));
if (toSpawn.getId() == 8500004) {
monster.getMap().spawnFakeMonster(toSpawn);
} else {
monster.getMap().spawnMonsterWithEffect(toSpawn, getSpawnEffect(), toSpawn.getPosition());
}
}
}
break;
default:
System.out.println("Unhandeled Mob skill: " + skillId);
break;
}
if (stats.size() > 0) {
if (lt != null && rb != null && skill) {
for (MapleMapObject mons : getObjectsInRange(monster, MapleMapObjectType.MONSTER)) {
((MapleMonster) mons).applyMonsterBuff(stats, getX(), getSkillId(), getDuration(), this, reflection);
}
} else {
monster.applyMonsterBuff(stats, getX(), getSkillId(), getDuration(), this, reflection);
}
}
if (disease != null) {
if (lt != null && rb != null && skill) {
int i = 0;
for (MapleCharacter character : getPlayersInRange(monster, player)) {
if (!character.isActiveBuffedValue(2321005)) {
if (disease.equals(MapleDisease.SEDUCE)) {
if (i < 10) {
character.giveDebuff(MapleDisease.SEDUCE, this);
i++;
}
} else {
character.giveDebuff(disease, this);
}
}
}
} else {
player.giveDebuff(disease, this);
}
}
monster.usedSkill(skillId, skillLevel, cooltime);
monster.setMp(monster.getMp() - getMpCon());
}
private List<MapleCharacter> getPlayersInRange(MapleMonster monster, MapleCharacter player) {
List<MapleCharacter> players = new ArrayList<MapleCharacter>();
players.add(player);
return monster.getMap().getPlayersInRange(calculateBoundingBox(monster.getPosition(), monster.isFacingLeft()), players);
}
public int getSkillId() {
return skillId;
}
public int getSkillLevel() {
return skillLevel;
}
public int getMpCon() {
return mpCon;
}
public List<Integer> getSummons() {
return Collections.unmodifiableList(toSummon);
}
public int getSpawnEffect() {
return spawnEffect;
}
public int getHP() {
return hp;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public long getDuration() {
return duration;
}
public long getCoolTime() {
return cooltime;
}
public Point getLt() {
return lt;
}
public Point getRb() {
return rb;
}
public int getLimit() {
return limit;
}
public boolean makeChanceResult() {
return prop == 1.0 || Math.random() < prop;
}
private Rectangle calculateBoundingBox(Point posFrom, boolean facingLeft) {
int multiplier = facingLeft ? 1 : -1;
Point mylt = new Point(lt.x * multiplier + posFrom.x, lt.y + posFrom.y);
Point myrb = new Point(rb.x * multiplier + posFrom.x, rb.y + posFrom.y);
return new Rectangle(mylt.x, mylt.y, myrb.x - mylt.x, myrb.y - mylt.y);
}
private List<MapleMapObject> getObjectsInRange(MapleMonster monster, MapleMapObjectType objectType) {
List<MapleMapObjectType> objectTypes = new ArrayList<MapleMapObjectType>();
objectTypes.add(objectType);
return monster.getMap().getMapObjectsInBox(calculateBoundingBox(monster.getPosition(), monster.isFacingLeft()), objectTypes);
}
}
+109
View File
@@ -0,0 +1,109 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <[email protected]>
Matthias Butz <[email protected]>
Jan Christian Meyer <[email protected]>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License 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.life;
import java.awt.Point;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import provider.MapleData;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
/**
*
* @author Danny (Leifde)
*/
public class MobSkillFactory {
private static Map<String, MobSkill> mobSkills = new HashMap<String, MobSkill>();
private final static MapleDataProvider dataSource = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Skill.wz"));
private static MapleData skillRoot = dataSource.getData("MobSkill.img");
private static ReentrantReadWriteLock dataLock = new ReentrantReadWriteLock();
public static MobSkill getMobSkill(final int skillId, final int level) {
final String key = skillId + "" + level;
dataLock.readLock().lock();
try {
MobSkill ret = mobSkills.get(key);
if (ret != null) {
return ret;
}
} finally {
dataLock.readLock().unlock();
}
dataLock.writeLock().lock();
try {
MobSkill ret;
ret = mobSkills.get(key);
if (ret == null) {
MapleData skillData = skillRoot.getChildByPath(skillId + "/level/" + level);
if (skillData != null) {
int mpCon = MapleDataTool.getInt(skillData.getChildByPath("mpCon"), 0);
List<Integer> toSummon = new ArrayList<Integer>();
for (int i = 0; i > -1; i++) {
if (skillData.getChildByPath(String.valueOf(i)) == null) {
break;
}
toSummon.add(Integer.valueOf(MapleDataTool.getInt(skillData.getChildByPath(String.valueOf(i)), 0)));
}
int effect = MapleDataTool.getInt("summonEffect", skillData, 0);
int hp = MapleDataTool.getInt("hp", skillData, 100);
int x = MapleDataTool.getInt("x", skillData, 1);
int y = MapleDataTool.getInt("y", skillData, 1);
long duration = MapleDataTool.getInt("time", skillData, 0) * 1000;
long cooltime = MapleDataTool.getInt("interval", skillData, 0) * 1000;
int iprop = MapleDataTool.getInt("prop", skillData, 100);
float prop = iprop / 100;
int limit = MapleDataTool.getInt("limit", skillData, 0);
MapleData ltd = skillData.getChildByPath("lt");
Point lt = null;
Point rb = null;
if (ltd != null) {
lt = (Point) ltd.getData();
rb = (Point) skillData.getChildByPath("rb").getData();
}
ret = new MobSkill(skillId, level);
ret.addSummons(toSummon);
ret.setCoolTime(cooltime);
ret.setDuration(duration);
ret.setHp(hp);
ret.setMpCon(mpCon);
ret.setSpawnEffect(effect);
ret.setX(x);
ret.setY(y);
ret.setProp(prop);
ret.setLimit(limit);
ret.setLtRb(lt, rb);
}
mobSkills.put(skillId + "" + level, ret);
}
return ret;
} finally {
dataLock.writeLock().unlock();
}
}
}
+38
View File
@@ -0,0 +1,38 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 ~ 2010 Patrick Huy <[email protected]>
Matthias Butz <[email protected]>
Jan Christian Meyer <[email protected]>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License version 3
as published by the Free Software Foundation. You may not use, modify
or distribute this program under any other version of the
GNU Affero General Public License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package server.life;
/**
*
* @author LightPepsi
*/
public class MonsterDropEntry {
public MonsterDropEntry(int itemId, int chance, int Minimum, int Maximum, short questid) {
this.itemId = itemId;
this.chance = chance;
this.questid = questid;
this.Minimum = Minimum;
this.Maximum = Maximum;
}
public short questid;
public int itemId, chance, Minimum, Maximum;
}
@@ -0,0 +1,39 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 ~ 2010 Patrick Huy <[email protected]>
Matthias Butz <[email protected]>
Jan Christian Meyer <[email protected]>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License version 3
as published by the Free Software Foundation. You may not use, modify
or distribute this program under any other version of the
GNU Affero General Public License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package server.life;
/**
*
* @author LightPepsi
*/
public class MonsterGlobalDropEntry {
public MonsterGlobalDropEntry(int itemId, int chance, int continent, byte dropType, int Minimum, int Maximum, short questid) {
this.itemId = itemId;
this.chance = chance;
this.dropType = dropType;
this.questid = questid;
this.Minimum = Minimum;
this.Maximum = Maximum;
}
public byte dropType;
public int itemId, chance, Minimum, Maximum;
public short questid;
}
+6
View File
@@ -0,0 +1,6 @@
package server.life;
public interface MonsterListener {
public void monsterKilled(int aniTime);
}
+101
View File
@@ -0,0 +1,101 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <[email protected]>
Matthias Butz <[email protected]>
Jan Christian Meyer <[email protected]>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License 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.life;
import client.MapleCharacter;
import java.awt.Point;
import java.util.concurrent.atomic.AtomicInteger;
public class SpawnPoint {
private int monster, mobTime, team, fh, f;
private Point pos;
private long nextPossibleSpawn;
private int mobInterval = 5000;
private AtomicInteger spawnedMonsters = new AtomicInteger(0);
private boolean immobile;
public SpawnPoint(final MapleMonster monster, Point pos, boolean immobile, int mobTime, int mobInterval, int team) {
this.monster = monster.getId();
this.pos = new Point(pos);
this.mobTime = mobTime;
this.team = team;
this.fh = monster.getFh();
this.f = monster.getF();
this.immobile = immobile;
this.mobInterval = mobInterval;
this.nextPossibleSpawn = System.currentTimeMillis();
}
public boolean shouldSpawn() {
if (mobTime < 0 || ((mobTime != 0 || immobile) && spawnedMonsters.get() > 0) || spawnedMonsters.get() > 2) {//lol
return false;
}
return nextPossibleSpawn <= System.currentTimeMillis();
}
public boolean shouldForceSpawn() {
if (mobTime < 0 || ((mobTime != 0 || immobile) && spawnedMonsters.get() > 0) || spawnedMonsters.get() > 2) {//lol
return false;
}
return true;
}
public MapleMonster getMonster() {
MapleMonster mob = new MapleMonster(MapleLifeFactory.getMonster(monster));
mob.setPosition(new Point(pos));
mob.setTeam(team);
mob.setFh(fh);
mob.setF(f);
spawnedMonsters.incrementAndGet();
mob.addListener(new MonsterListener() {
@Override
public void monsterKilled(int aniTime) {
nextPossibleSpawn = System.currentTimeMillis();
if (mobTime > 0) {
nextPossibleSpawn += mobTime * 1000;
} else {
nextPossibleSpawn += aniTime;
}
spawnedMonsters.decrementAndGet();
}
});
if (mobTime == 0) {
nextPossibleSpawn = System.currentTimeMillis() + mobInterval;
}
return mob;
}
public Point getPosition() {
return pos;
}
public final int getF() {
return f;
}
public final int getFh() {
return fh;
}
}