Rename and clean up MapleLifeFactory
This commit is contained in:
369
src/main/java/server/life/LifeFactory.java
Normal file
369
src/main/java/server/life/LifeFactory.java
Normal file
@@ -0,0 +1,369 @@
|
||||
/*
|
||||
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.life;
|
||||
|
||||
import provider.Data;
|
||||
import provider.DataProvider;
|
||||
import provider.DataProviderFactory;
|
||||
import provider.DataTool;
|
||||
import provider.wz.DataType;
|
||||
import provider.wz.WZFiles;
|
||||
import tools.Pair;
|
||||
import tools.StringUtil;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
public class LifeFactory {
|
||||
private static final DataProvider data = DataProviderFactory.getDataProvider(WZFiles.MOB);
|
||||
private final static DataProvider stringDataWZ = DataProviderFactory.getDataProvider(WZFiles.STRING);
|
||||
private static final Data mobStringData = stringDataWZ.getData("Mob.img");
|
||||
private static final Data npcStringData = stringDataWZ.getData("Npc.img");
|
||||
private static final Map<Integer, MapleMonsterStats> monsterStats = new HashMap<>();
|
||||
private static final Set<Integer> hpbarBosses = getHpBarBosses();
|
||||
|
||||
private static Set<Integer> getHpBarBosses() {
|
||||
Set<Integer> ret = new HashSet<>();
|
||||
|
||||
DataProvider uiDataWZ = DataProviderFactory.getDataProvider(WZFiles.UI);
|
||||
for (Data bossData : uiDataWZ.getData("UIWindow.img").getChildByPath("MobGage/Mob").getChildren()) {
|
||||
ret.add(Integer.valueOf(bossData.getName()));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static AbstractLoadedLife 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;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MobAttackInfoHolder {
|
||||
protected int attackPos;
|
||||
protected int mpCon;
|
||||
protected int coolTime;
|
||||
protected int animationTime;
|
||||
|
||||
protected MobAttackInfoHolder(int attackPos, int mpCon, int coolTime, int animationTime) {
|
||||
this.attackPos = attackPos;
|
||||
this.mpCon = mpCon;
|
||||
this.coolTime = coolTime;
|
||||
this.animationTime = animationTime;
|
||||
}
|
||||
}
|
||||
|
||||
private static void setMonsterAttackInfo(int mid, List<MobAttackInfoHolder> attackInfos) {
|
||||
if (!attackInfos.isEmpty()) {
|
||||
MapleMonsterInformationProvider mi = MapleMonsterInformationProvider.getInstance();
|
||||
|
||||
for (MobAttackInfoHolder attackInfo : attackInfos) {
|
||||
mi.setMobAttackInfo(mid, attackInfo.attackPos, attackInfo.mpCon, attackInfo.coolTime);
|
||||
mi.setMobAttackAnimationTime(mid, attackInfo.attackPos, attackInfo.animationTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Pair<MapleMonsterStats, List<MobAttackInfoHolder>> getMonsterStats(int mid) {
|
||||
Data monsterData = data.getData(StringUtil.getLeftPaddedStr(mid + ".img", '0', 11));
|
||||
if (monsterData == null) {
|
||||
return null;
|
||||
}
|
||||
Data monsterInfoData = monsterData.getChildByPath("info");
|
||||
|
||||
List<MobAttackInfoHolder> attackInfos = new LinkedList<>();
|
||||
MapleMonsterStats stats = new MapleMonsterStats();
|
||||
|
||||
int linkMid = DataTool.getIntConvert("link", monsterInfoData, 0);
|
||||
if (linkMid != 0) {
|
||||
Pair<MapleMonsterStats, List<MobAttackInfoHolder>> linkStats = getMonsterStats(linkMid);
|
||||
if (linkStats == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// thanks resinate for noticing non-propagable infos such as revives getting retrieved
|
||||
attackInfos.addAll(linkStats.getRight());
|
||||
}
|
||||
|
||||
stats.setHp(DataTool.getIntConvert("maxHP", monsterInfoData));
|
||||
stats.setFriendly(DataTool.getIntConvert("damagedByMob", monsterInfoData, stats.isFriendly() ? 1 : 0) == 1);
|
||||
stats.setPADamage(DataTool.getIntConvert("PADamage", monsterInfoData));
|
||||
stats.setPDDamage(DataTool.getIntConvert("PDDamage", monsterInfoData));
|
||||
stats.setMADamage(DataTool.getIntConvert("MADamage", monsterInfoData));
|
||||
stats.setMDDamage(DataTool.getIntConvert("MDDamage", monsterInfoData));
|
||||
stats.setMp(DataTool.getIntConvert("maxMP", monsterInfoData, stats.getMp()));
|
||||
stats.setExp(DataTool.getIntConvert("exp", monsterInfoData, stats.getExp()));
|
||||
stats.setLevel(DataTool.getIntConvert("level", monsterInfoData));
|
||||
stats.setRemoveAfter(DataTool.getIntConvert("removeAfter", monsterInfoData, stats.removeAfter()));
|
||||
stats.setBoss(DataTool.getIntConvert("boss", monsterInfoData, stats.isBoss() ? 1 : 0) > 0);
|
||||
stats.setExplosiveReward(DataTool.getIntConvert("explosiveReward", monsterInfoData, stats.isExplosiveReward() ? 1 : 0) > 0);
|
||||
stats.setFfaLoot(DataTool.getIntConvert("publicReward", monsterInfoData, stats.isFfaLoot() ? 1 : 0) > 0);
|
||||
stats.setUndead(DataTool.getIntConvert("undead", monsterInfoData, stats.isUndead() ? 1 : 0) > 0);
|
||||
stats.setName(DataTool.getString(mid + "/name", mobStringData, "MISSINGNO"));
|
||||
stats.setBuffToGive(DataTool.getIntConvert("buff", monsterInfoData, stats.getBuffToGive()));
|
||||
stats.setCP(DataTool.getIntConvert("getCP", monsterInfoData, stats.getCP()));
|
||||
stats.setRemoveOnMiss(DataTool.getIntConvert("removeOnMiss", monsterInfoData, stats.removeOnMiss() ? 1 : 0) > 0);
|
||||
|
||||
Data special = monsterInfoData.getChildByPath("coolDamage");
|
||||
if (special != null) {
|
||||
int coolDmg = DataTool.getIntConvert("coolDamage", monsterInfoData);
|
||||
int coolProb = DataTool.getIntConvert("coolDamageProb", monsterInfoData, 0);
|
||||
stats.setCool(new Pair<>(coolDmg, coolProb));
|
||||
}
|
||||
special = monsterInfoData.getChildByPath("loseItem");
|
||||
if (special != null) {
|
||||
for (Data liData : special.getChildren()) {
|
||||
stats.addLoseItem(new loseItem(DataTool.getInt(liData.getChildByPath("id")), (byte) DataTool.getInt(liData.getChildByPath("prop")), (byte) DataTool.getInt(liData.getChildByPath("x"))));
|
||||
}
|
||||
}
|
||||
special = monsterInfoData.getChildByPath("selfDestruction");
|
||||
if (special != null) {
|
||||
stats.setSelfDestruction(new selfDestruction((byte) DataTool.getInt(special.getChildByPath("action")), DataTool.getIntConvert("removeAfter", special, -1), DataTool.getIntConvert("hp", special, -1)));
|
||||
}
|
||||
Data firstAttackData = monsterInfoData.getChildByPath("firstAttack");
|
||||
int firstAttack = 0;
|
||||
if (firstAttackData != null) {
|
||||
if (firstAttackData.getType() == DataType.FLOAT) {
|
||||
firstAttack = Math.round(DataTool.getFloat(firstAttackData));
|
||||
} else {
|
||||
firstAttack = DataTool.getInt(firstAttackData);
|
||||
}
|
||||
}
|
||||
stats.setFirstAttack(firstAttack > 0);
|
||||
stats.setDropPeriod(DataTool.getIntConvert("dropItemPeriod", monsterInfoData, stats.getDropPeriod() / 10000) * 10000);
|
||||
|
||||
// thanks yuxaij, Riizade, Z1peR, Anesthetic for noticing some bosses crashing players due to missing requirements
|
||||
boolean hpbarBoss = stats.isBoss() && hpbarBosses.contains(mid);
|
||||
stats.setTagColor(hpbarBoss ? DataTool.getIntConvert("hpTagColor", monsterInfoData, 0) : 0);
|
||||
stats.setTagBgColor(hpbarBoss ? DataTool.getIntConvert("hpTagBgcolor", monsterInfoData, 0) : 0);
|
||||
|
||||
for (Data idata : monsterData) {
|
||||
if (!idata.getName().equals("info")) {
|
||||
int delay = 0;
|
||||
for (Data pic : idata.getChildren()) {
|
||||
delay += DataTool.getIntConvert("delay", pic, 0);
|
||||
}
|
||||
stats.setAnimationTime(idata.getName(), delay);
|
||||
}
|
||||
}
|
||||
Data reviveInfo = monsterInfoData.getChildByPath("revive");
|
||||
if (reviveInfo != null) {
|
||||
List<Integer> revives = new LinkedList<>();
|
||||
for (Data data_ : reviveInfo) {
|
||||
revives.add(DataTool.getInt(data_));
|
||||
}
|
||||
stats.setRevives(revives);
|
||||
}
|
||||
decodeElementalString(stats, DataTool.getString("elemAttr", monsterInfoData, ""));
|
||||
|
||||
MapleMonsterInformationProvider mi = MapleMonsterInformationProvider.getInstance();
|
||||
Data monsterSkillInfoData = monsterInfoData.getChildByPath("skill");
|
||||
if (monsterSkillInfoData != null) {
|
||||
int i = 0;
|
||||
List<Pair<Integer, Integer>> skills = new ArrayList<>();
|
||||
while (monsterSkillInfoData.getChildByPath(Integer.toString(i)) != null) {
|
||||
int skillId = DataTool.getInt(i + "/skill", monsterSkillInfoData, 0);
|
||||
int skillLv = DataTool.getInt(i + "/level", monsterSkillInfoData, 0);
|
||||
skills.add(new Pair<>(skillId, skillLv));
|
||||
|
||||
Data monsterSkillData = monsterData.getChildByPath("skill" + (i + 1));
|
||||
if (monsterSkillData != null) {
|
||||
int animationTime = 0;
|
||||
for (Data effectEntry : monsterSkillData.getChildren()) {
|
||||
animationTime += DataTool.getIntConvert("delay", effectEntry, 0);
|
||||
}
|
||||
|
||||
MobSkill skill = MobSkillFactory.getMobSkill(skillId, skillLv);
|
||||
mi.setMobSkillAnimationTime(skill, animationTime);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
stats.setSkills(skills);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
Data monsterAttackData;
|
||||
while ((monsterAttackData = monsterData.getChildByPath("attack" + (i + 1))) != null) {
|
||||
int animationTime = 0;
|
||||
for (Data effectEntry : monsterAttackData.getChildren()) {
|
||||
animationTime += DataTool.getIntConvert("delay", effectEntry, 0);
|
||||
}
|
||||
|
||||
int mpCon = DataTool.getIntConvert("info/conMP", monsterAttackData, 0);
|
||||
int coolTime = DataTool.getIntConvert("info/attackAfter", monsterAttackData, 0);
|
||||
attackInfos.add(new MobAttackInfoHolder(i, mpCon, coolTime, animationTime));
|
||||
i++;
|
||||
}
|
||||
|
||||
Data banishData = monsterInfoData.getChildByPath("ban");
|
||||
if (banishData != null) {
|
||||
stats.setBanishInfo(new BanishInfo(DataTool.getString("banMsg", banishData), DataTool.getInt("banMap/0/field", banishData, -1), DataTool.getString("banMap/0/portal", banishData, "sp")));
|
||||
}
|
||||
|
||||
int noFlip = DataTool.getInt("noFlip", monsterInfoData, 0);
|
||||
if (noFlip > 0) {
|
||||
Point origin = DataTool.getPoint("stand/0/origin", monsterData, null);
|
||||
if (origin != null) {
|
||||
stats.setFixedStance(origin.getX() < 1 ? 5 : 4); // fixed left/right
|
||||
}
|
||||
}
|
||||
|
||||
return new Pair<>(stats, attackInfos);
|
||||
}
|
||||
|
||||
public static MapleMonster getMonster(int mid) {
|
||||
try {
|
||||
MapleMonsterStats stats = monsterStats.get(mid);
|
||||
if (stats == null) {
|
||||
Pair<MapleMonsterStats, List<MobAttackInfoHolder>> mobStats = getMonsterStats(mid);
|
||||
stats = mobStats.getLeft();
|
||||
setMonsterAttackInfo(mid, mobStats.getRight());
|
||||
|
||||
monsterStats.put(mid, stats);
|
||||
}
|
||||
MapleMonster ret = new MapleMonster(mid, stats);
|
||||
return ret;
|
||||
} catch (NullPointerException npe) {
|
||||
System.out.println("[SEVERE] MOB " + mid + " failed to load. Issue: " + npe.getMessage() + "\n\n");
|
||||
npe.printStackTrace();
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static int getMonsterLevel(int mid) {
|
||||
try {
|
||||
MapleMonsterStats stats = monsterStats.get(mid);
|
||||
if (stats == null) {
|
||||
Data monsterData = data.getData(StringUtil.getLeftPaddedStr(mid + ".img", '0', 11));
|
||||
if (monsterData == null) {
|
||||
return -1;
|
||||
}
|
||||
Data monsterInfoData = monsterData.getChildByPath("info");
|
||||
return DataTool.getIntConvert("level", monsterInfoData);
|
||||
} else {
|
||||
return stats.getLevel();
|
||||
}
|
||||
} catch (NullPointerException npe) {
|
||||
System.out.println("[SEVERE] MOB " + mid + " failed to load. Issue: " + npe.getMessage() + "\n\n");
|
||||
npe.printStackTrace();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
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.parseInt(String.valueOf(elemAttr.charAt(i + 1)))));
|
||||
}
|
||||
}
|
||||
|
||||
public static MapleNPC getNPC(int nid) {
|
||||
return new MapleNPC(nid, new MapleNPCStats(DataTool.getString(nid + "/name", npcStringData, "MISSINGNO")));
|
||||
}
|
||||
|
||||
public static String getNPCDefaultTalk(int nid) {
|
||||
return DataTool.getString(nid + "/d0", npcStringData, "(...)");
|
||||
}
|
||||
|
||||
public static class BanishInfo {
|
||||
|
||||
private final int map;
|
||||
private final String portal;
|
||||
private final String 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 final int id;
|
||||
private final byte chance;
|
||||
private final byte x;
|
||||
|
||||
public 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 final byte action;
|
||||
private final int removeAfter;
|
||||
private final int hp;
|
||||
|
||||
public 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user