package server.life; import client.MapleBuffStat; import client.MapleCharacter; import client.MapleClient; import client.MapleDisease; import client.Skill; import client.SkillFactory; import client.inventory.Equip; import client.inventory.Item; import client.inventory.MapleInventoryType; import client.status.MonsterStatus; import client.status.MonsterStatusEffect; import constants.GameConstants; import constants.ItemConstants; import handling.channel.ChannelServer; import handling.world.party.MapleParty; import handling.world.party.MaplePartyCharacter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.TimerTask; import java.util.concurrent.locks.ReentrantReadWriteLock; import scripting.event.EventInstanceManager; import server.MapleItemInformationProvider; import server.MapleStatEffect; import server.Randomizer; import server.Timer; import server.Timer.EtcTimer; import server.maps.MapleMap; import server.maps.MapleMapObject; import server.maps.MapleMapObjectType; import server.skill.冒险家.侠客; import server.skill.冒险家.冰雷巫师; import server.skill.冒险家.无影人; import server.skill.冒险家.火毒巫师; import tools.ConcurrentEnumMap; import tools.MaplePacketCreator; import tools.Pair; import tools.packet.MobPacket; import tools.packet.SkillPacket; public final class MapleMonster extends AbstractLoadedMapleLife { private MapleMonsterStats stats; private ChangeableStats ostats = null; private long hp; private long nextKill = 0L; private long lastDropTime = 0L; private int mp; private byte carnivalTeam = -1; private MapleMap map; private WeakReference<MapleMonster> sponge = new WeakReference(null); private int linkoid = 0; private int lastNode = -1; private int highestDamageChar = 0; private int linkCID = 0; private WeakReference<MapleCharacter> controller = new WeakReference(null); private boolean fake = false; private boolean dropsDisabled = false; private boolean controllerHasAggro = false; private final Collection<AttackerEntry> attackers = new LinkedList(); private EventInstanceManager eventInstance; private MonsterListener listener = null; private byte[] reflectpack = null; private byte[] nodepack = null; private final ConcurrentEnumMap<MonsterStatus, MonsterStatusEffect> stati = new ConcurrentEnumMap<>(MonsterStatus.class); private final LinkedList<MonsterStatusEffect> poisons = new LinkedList(); private final ReentrantReadWriteLock poisonsLock = new ReentrantReadWriteLock(); private Map<Integer, Long> usedSkills; private int stolen = -1; private boolean shouldDropItem = false; private boolean killed = false; private int triangulation = 0; private boolean mark = false; private int 怪物类型 = -1; private boolean special = false; public MapleMonster(int id, MapleMonsterStats stats) { super(id); initWithStats(stats); setpecial(id); } public MapleMonster(MapleMonster monster) { super(monster); initWithStats(monster.stats); setpecial(monster.getId()); } private void initWithStats(MapleMonsterStats stats) { setStance(5); this.stats = stats; this.hp = stats.getHp(); this.mp = stats.getMp(); if (stats.getNoSkills() > 0) { this.usedSkills = new HashMap(); } } public ArrayList<AttackerEntry> getAttackers() { if ((this.attackers == null) || (this.attackers.size() <= 0)) { return new ArrayList(); } ArrayList ret = new ArrayList(); for (AttackerEntry e : this.attackers) { if (e != null) { ret.add(e); } } return ret; } public MapleMonsterStats getStats() { return this.stats; } public void disableDrops() { this.dropsDisabled = true; } public boolean dropsDisabled() { return this.dropsDisabled; } public boolean isSpecial() { return this.special; } public void setpecial(int id) { switch (id) { case 8910000: case 8910100: special = true; break; } } public void set怪物类型(int 怪物类型) { this.怪物类型 = 怪物类型; } public void setSponge(MapleMonster mob) { this.sponge = new WeakReference(mob); if (this.linkoid <= 0) { this.linkoid = mob.getObjectId(); } } public void setMap(MapleMap map) { this.map = map; startDropItemSchedule(); } public int getMobLevel() { if (this.ostats != null) { return this.ostats.level; } return this.stats.getLevel(); } public long getHp() { return this.hp; } public void setHp(long hp) { this.hp = hp; } public ChangeableStats getChangedStats() { return this.ostats; } public long getMobMaxHp() { if (this.ostats != null) { return this.ostats.hp; } return this.stats.getHp(); } public int getMp() { return this.mp; } public void setMp(int mp) { if (mp < 0) { mp = 0; } this.mp = mp; } public int getMobMaxMp() { if (this.ostats != null) { return this.ostats.mp; } return this.stats.getMp(); } public int getMobExp() { if (this.ostats != null) { return this.ostats.exp; } return this.stats.getExp(); } public void setOverrideStats(OverrideMonsterStats ostats) { this.ostats = new ChangeableStats(this.stats, ostats); this.hp = ostats.getHp(); this.mp = ostats.getMp(); } public void changeLevel(int newLevel) { changeLevel(newLevel, true); } public void changeLevel(int newLevel, boolean pqMob) { if (!this.stats.isChangeable()) { return; } this.ostats = new ChangeableStats(this.stats, newLevel, pqMob); this.hp = this.ostats.getHp(); this.mp = this.ostats.getMp(); } public void changeLevelmod(int newLevel, int multipler) { if (!this.stats.isChangeable()) { return; } this.ostats = new ChangeableStats(this.stats, newLevel, multipler); this.hp = this.ostats.getHp(); this.mp = this.ostats.getMp(); } public MapleMonster getSponge() { return (MapleMonster) this.sponge.get(); } public void damage(MapleCharacter from, long damage, boolean updateAttackTime) { damage(from, damage, updateAttackTime, 0); } /** * 对怪物造成伤害 * @param from * @param damage * @param updateAttackTime * @param lastSkill */ public void damage(MapleCharacter from, long damage, boolean updateAttackTime, int lastSkill) { if ((from == null) || (damage <= 0L) || (!isAlive())) { return; } AttackerEntry attacker = null; if (from.getParty() != null) { attacker = new PartyAttackerEntry(from.getParty().getId()); } else { attacker = new SingleAttackerEntry(from); } boolean replaced = false; for (AttackerEntry aentry : getAttackers()) { if ((aentry != null) && (aentry.equals(attacker))) { attacker = aentry; replaced = true; break; } } if (!replaced) { this.attackers.add(attacker); } long rDamage = Math.max(0L, Math.min(damage, this.hp)); attacker.addDamage(from, rDamage, updateAttackTime); if (this.stats.getSelfD() != -1) { this.hp -= rDamage; if (this.hp > 0L) { if (this.hp < this.stats.getSelfDHp()) { this.map.killMonster(this, from, false, false, this.stats.getSelfD(), lastSkill); } else { for (AttackerEntry mattacker : getAttackers()) { for (AttackingMapleCharacter cattacker : mattacker.getAttackers()) { if ((cattacker.getAttacker().getMap() == from.getMap()) && (cattacker.getLastAttackTime() >= System.currentTimeMillis() - 4000L)) { cattacker.getAttacker().getClient().getSession().write(MobPacket.showMonsterHP(getObjectId(), getHPPercent())); } } } } } else { this.map.killMonster(this, from, true, false, (byte) 1, lastSkill); } } else { if (this.sponge.get() != null) { if (((MapleMonster) this.sponge.get()).hp > 0L) { ((MapleMonster) this.sponge.get()).hp -= rDamage; if (((MapleMonster) this.sponge.get()).hp <= 0L) { this.map.broadcastMessage(MobPacket.showBossHP((this.sponge.get()).getId(), -1L, ((MapleMonster) this.sponge.get()).getMobMaxHp())); this.map.killMonster(this.sponge.get(), from, true, false, (byte) 1, lastSkill); } else { this.map.broadcastMessage(MobPacket.showBossHP( this.sponge.get())); } } } if (this.hp > 0L) { this.hp -= rDamage; if (this.eventInstance != null) { this.eventInstance.monsterDamaged(from, this, (int) rDamage); } else { EventInstanceManager em = from.getEventInstance(); if (em != null) { em.monsterDamaged(from, this, (int) rDamage); } } if ((this.sponge.get() == null) && (this.hp > 0L)) { switch (this.stats.getHPDisplayType()) { case 0: this.map.broadcastMessage(MobPacket.showBossHP(this), getTruePosition()); break; case 1: this.map.broadcastMessage(from, MobPacket.damageFriendlyMob(this, damage, true), false); break; case 2: this.map.broadcastMessage(MobPacket.showMonsterHP(getObjectId(), getHPPercent())); from.mulung_EnergyModify(true); break; case 3: for (AttackerEntry mattacker : getAttackers()) { if (mattacker != null) { for (AttackingMapleCharacter cattacker : mattacker.getAttackers()) { if ((cattacker != null) && (cattacker.getAttacker().getMap() == from.getMap()) && (cattacker.getLastAttackTime() >= System.currentTimeMillis() - 4000L)) { cattacker.getAttacker().getClient().getSession().write(MobPacket.showMonsterHP(getObjectId(), getHPPercent())); } } } } } } if (this.hp <= 0L) { if (this.stats.getHPDisplayType() == 0) { this.map.broadcastMessage(MobPacket.showBossHP(getId(), -1L, getMobMaxHp()), getTruePosition()); } this.map.killMonster(this, from, true, false, (byte) 1, lastSkill); } } } startDropItemSchedule(); } public int getHPPercent() { return (int) Math.ceil(this.hp * 100.0D / getMobMaxHp()); } public void heal(int hp, int mp, boolean broadcast) { long TotalHP = getHp() + hp; int TotalMP = getMp() + mp; if (TotalHP >= getMobMaxHp()) { setHp(getMobMaxHp()); } else { setHp(TotalHP); } if (TotalMP >= getMp()) { setMp(getMp()); } else { setMp(TotalMP); } if (broadcast) { this.map.broadcastMessage(MobPacket.healMonster(getObjectId(), hp)); } else if (this.sponge.get() != null) { ((MapleMonster) this.sponge.get()).hp += hp; } } public void killed() { if (this.listener != null) { this.listener.monsterKilled(); } this.listener = null; } private void giveExpToCharacter(MapleCharacter attacker, int exp, boolean 最高伤害, int numExpSharers, byte pty, byte Class_Bonus_EXP_PERCENT, byte Premium_Bonus_EXP_PERCENT, int lastskillID) { if (最高伤害) { if (this.eventInstance != null) { this.eventInstance.monsterKilled(attacker, this); } else { EventInstanceManager em = attacker.getEventInstance(); if (em != null) { em.monsterKilled(attacker, this); } } this.highestDamageChar = attacker.getId(); } if (exp > 0) { if (attacker.hasDisease(MapleDisease.诅咒)) { exp /= 2; } int acExpRate = ChannelServer.getInstance(map.getChannel()).getExpRate(attacker.getWorld()); if ((attacker.getLevel() < 10) && (GameConstants.is新手职业(0))) { acExpRate = 1; } long gainexp = exp * acExpRate; exp = (int) Math.min(1000000000, gainexp); int Class_Bonus_EXP = 0; if (Class_Bonus_EXP_PERCENT > 0) { Class_Bonus_EXP = (int) (exp / 100.0D * Class_Bonus_EXP_PERCENT); } attacker.gainExpMonster(exp, true, 最高伤害, pty, Class_Bonus_EXP, this); } attacker.mobKilled(getId(), lastskillID); } public void killGainExp(int lastSkill) { int totalBaseExp = getMobExp(); AttackerEntry highest = null; long highdamage = 0L; List<AttackerEntry> list = getAttackers(); for (AttackerEntry attackEntry : list) { if ((attackEntry != null) && (attackEntry.getDamage() > highdamage)) { highest = attackEntry; highdamage = attackEntry.getDamage(); } } for (AttackerEntry attackEntry : list) { if (attackEntry != null) { int baseExp = (int) Math.ceil(totalBaseExp * ((double) attackEntry.getDamage() / getMobMaxHp())); attackEntry.killedMob(getMap(), baseExp, attackEntry == highest, lastSkill); } } } public int killBy(MapleCharacter killer, int lastSkill) { if (this.killed) { return -1; } this.killed = true; MapleCharacter controll = (MapleCharacter) this.controller.get(); if (controll != null) { controll.getClient().getSession().write(MobPacket.stopControllingMonster(getObjectId())); controll.stopControllingMonster(this); } int achievement = 0; switch (getId()) { case 9400121: achievement = 12; break; case 8500002: achievement = 13; break; case 8510000: case 8520000: achievement = 14; break; } spawnRevives(getMap()); if (this.eventInstance != null) { this.eventInstance.unregisterMonster(this); this.eventInstance = null; } this.hp = 0L; MapleMonster oldSponge = getSponge(); this.sponge = new WeakReference(null); if ((oldSponge != null) && (oldSponge.isAlive())) { boolean set = true; for (MapleMapObject mon : this.map.getAllMonstersThreadsafe()) { MapleMonster mons = (MapleMonster) mon; if ((mons.isAlive()) && (mons.getObjectId() != oldSponge.getObjectId()) && (mons.getStats().getLevel() > 1) && (mons.getObjectId() != getObjectId()) && ((mons.getSponge() == oldSponge) || (mons.getLinkOid() == oldSponge.getObjectId()))) { set = false; break; } } if (set) { this.map.killMonster(oldSponge, killer, true, false, (byte) 1); } } this.reflectpack = null; this.nodepack = null; if (this.stati.size() > 0) { List<MonsterStatus> statuses = new LinkedList(this.stati.keySet()); for (MonsterStatus ms : statuses) { cancelStatus(ms); } statuses.clear(); } if (this.poisons.size() > 0) { List<MonsterStatusEffect> ps = new LinkedList(); this.poisonsLock.readLock().lock(); try { ps.addAll(this.poisons); } finally { this.poisonsLock.readLock().unlock(); } for (MonsterStatusEffect p : ps) { cancelSingleStatus(p); } ps.clear(); } cancelDropItem(); int v1 = this.highestDamageChar; this.highestDamageChar = 0; return v1; } public void spawnRevives(MapleMap map) { List<Integer> toSpawn = stats.getRevives(); if ((toSpawn == null) || (getLinkCID() > 0)) { return; } for (final int i : toSpawn) { final MapleMonster mob = MapleLifeFactory.getMonster(i); if (eventInstance != null) { eventInstance.registerMonster(mob); } mob.setPosition(getTruePosition()); if (dropsDisabled()) { mob.disableDrops(); } int objId = this.getObjectId(); if (this.getId() == 5100001 || this.getId() == 5130106) { Timer.MapTimer.getInstance().schedule(new Runnable() { @Override public void run() { map.spawnRevives(mob, objId); } }, 2800L); } else { map.spawnRevives(mob, this.getObjectId()); } } } public boolean isAlive() { return this.hp > 0L; } public void setCarnivalTeam(byte team) { this.carnivalTeam = team; } public byte getCarnivalTeam() { return this.carnivalTeam; } public MapleCharacter getController() { return (MapleCharacter) this.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().getSession().write(MobPacket.stopControllingMonster(getObjectId())); sendStatus(controllers.getClient()); } newController.controlMonster(this, immediateAggro); setController(newController); if (immediateAggro) { setControllerHasAggro(true); } } public void addListener(MonsterListener listener) { this.listener = listener; } public boolean isControllerHasAggro() { return this.controllerHasAggro; } public void setControllerHasAggro(boolean controllerHasAggro) { this.controllerHasAggro = controllerHasAggro; } public void sendStatus(MapleClient client) { if (this.reflectpack != null) { client.getSession().write(this.reflectpack); } if (this.poisons.size() > 0) { this.poisonsLock.readLock().lock(); try { client.getSession().write(MobPacket.applyMonsterPoisonStatus(this, this.poisons,100)); } finally { this.poisonsLock.readLock().unlock(); } } } @Override public void sendSpawnData(MapleClient client) { if (!isAlive()) { return; } client.getSession().write(MobPacket.spawnMonster(this, (fake) && (this.linkCID <= 0) ? -4 : -1, 0));//进入地图发的召唤怪物 sendStatus(client); if (this.map != null && !this.stats.isEscort() && client.getPlayer() != null && (client.getPlayer().getTruePosition().distance(getTruePosition()) <= GameConstants.maxViewRangeSq())) { this.map.updateMonsterController(this); } } @Override public void sendDestroyData(MapleClient client) { if ((this.stats.isEscort()) && (getEventInstance() != null) && (this.lastNode >= 0)) { this.map.resetShammos(client); } else { client.getSession().write(MobPacket.killMonster(getObjectId(), 0)); if ((getController() != null) && (client.getPlayer() != null) && (client.getPlayer().getId() == getController().getId())) { client.getPlayer().stopControllingMonster(this); } } } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(this.stats.getName()); sb.append("("); sb.append(getId()); sb.append(") 等级:"); sb.append(this.stats.getLevel()); if (this.ostats != null) { sb.append("→"); sb.append(this.ostats.level); } sb.append(" 坐标(X:"); sb.append(getTruePosition().x); sb.append("/Y:"); sb.append(getTruePosition().y); sb.append(") 信息: "); sb.append(getHp()); sb.append("/"); sb.append(getMobMaxHp()); sb.append("Hp, "); sb.append(getMp()); sb.append("/"); sb.append(getMobMaxMp()); sb.append("Mp, oid: "); sb.append(getObjectId()); sb.append("||仇恨目标: "); MapleCharacter chr = (MapleCharacter) this.controller.get(); sb.append(chr != null ? chr.getName() : "无"); return sb.toString(); } @Override public MapleMapObjectType getType() { return MapleMapObjectType.MONSTER; } public EventInstanceManager getEventInstance() { return this.eventInstance; } public void setEventInstance(EventInstanceManager eventInstance) { this.eventInstance = eventInstance; } public int getStatusSourceID(MonsterStatus status) { if ((status == MonsterStatus.中毒) ) { this.poisonsLock.readLock().lock(); try { for (MonsterStatusEffect ps : this.poisons) { if (ps != null) { int i = ps.getSkill(); return i; } } } finally { this.poisonsLock.readLock().unlock(); } } MonsterStatusEffect effect = (MonsterStatusEffect) this.stati.get(status); if (effect != null) { return effect.getSkill(); } return -1; } public ElementalEffectiveness getEffectiveness(Element e) { // if ((this.stati.size() > 0) && (this.stati.containsKey(MonsterStatus.巫毒))) { if (this.stati.size() > 0) { return ElementalEffectiveness.NORMAL; } return this.stats.getEffectiveness(e); } /** * 给怪物加BUFF * @param from * @param status * @param poison * @param duration * @param checkboss * @param effect */ public void applyStatus(MapleCharacter from, MonsterStatusEffect status, boolean poison, long duration, boolean checkboss, MapleStatEffect effect) { if ((!isAlive()) || (getLinkCID() > 0)) { return; } Skill skilz = SkillFactory.getSkill(status.getSkill()); if (skilz != null) { switch (stats.getEffectiveness(skilz.getElement())) { case IMMUNE: case STRONG: return; case NORMAL: case WEAK: break; default: throw new RuntimeException("Unknown elemental effectiveness: " + stats.getEffectiveness(skilz.getElement())); } } int statusSkill = status.getSkill(); switch (statusSkill) { case 火毒巫师.火毒合击: switch (stats.getEffectiveness(Element.POISON)) { case IMMUNE: case STRONG: return; } break; case 冰雷巫师.冰雷合击: switch (stats.getEffectiveness(Element.ICE)) { case IMMUNE: case STRONG: return; } break; } MonsterStatus stat = status.getStati(); if ((this.stats.isNoDoom())) { return; } if (this.stats.isBoss()) { if ((stat == MonsterStatus.眩晕) || (stat == MonsterStatus.速度)) { return; } if ((checkboss) && (stat != MonsterStatus.物攻) && (stat != MonsterStatus.中毒) ) { return; } if ((getId() == 8850011)) { return; } } if (((this.stats.isFriendly()) || (isFake())) && ((stat == MonsterStatus.眩晕) || (stat == MonsterStatus.速度) || (stat == MonsterStatus.中毒) )) { return; } if (((stat == MonsterStatus.中毒)) && (effect == null)) { return; } if (this.stati.containsKey(stat)) { cancelStatus(stat); } if ((stat == MonsterStatus.中毒) ) { this.poisonsLock.readLock().lock(); try { for (MonsterStatusEffect mse : this.poisons) { if ((mse != null) && ((mse.getSkill() == effect.getSourceId()) )) { return; } } } finally { this.poisonsLock.readLock().unlock(); } } if ((poison) && (getHp() > 1L) && (effect != null)) { if (statusSkill == 火毒巫师.致命毒雾) { duration = effect.getDOTTime() * 1000; } else { duration = Math.max(duration, effect.getDOTTime() * 1000); } } duration += from.getStat().dotTime * 1000; if (duration >= 60000L) { duration = 10000L; } long aniTime = duration; status.setCancelTask(aniTime); if ((poison) && (getHp() > 1L)) { if (status.getchr() != null) { return; } status.setDotTime(duration); int poisonDot = from.getStat().dot; int damageIncrease = from.getStat().getDamageIncrease(effect.getSourceId()); if (damageIncrease > effect.getDOT()) { poisonDot += damageIncrease; } else { poisonDot += effect.getDOT(); } if (from.isShowPacket()) { from.dropSpouseMessage(18, "[持续伤害] 开始处理效果 - 技能ID:" + effect.getSourceId()); from.dropSpouseMessage(18, "[持续伤害] 加成 - 技能ID:" + effect.getDOT() + " 被动: " + from.getStat().dot + " 被动加成: " + damageIncrease + " 最终加成:" + poisonDot); } status.setValue(status.getStati(), (int) (poisonDot * from.getStat().getCurrentMaxBaseDamage() / 100.0D)); int poisonDamage = (int) (aniTime / 1000L * status.getX() / 2L); if (from.isShowPacket()) { from.dropSpouseMessage(18, "[持续伤害] 持续伤害: " + poisonDamage + " 持续时间:" + aniTime + " 持续掉血:" + status.getX()); } status.setPoisonSchedule(poisonDamage, from); if (poisonDamage > 0) { if (poisonDamage >= this.hp) { poisonDamage = (int) (this.hp - 1L); } damage(from, poisonDamage, false); } } else if ((statusSkill == 无影人.影网术)) { status.setValue(status.getStati(), (int) (getMobMaxHp() / 50.0D + 0.999D)); status.setPoisonSchedule(status.getX(), from); } MapleCharacter con = getController(); if ((stat == MonsterStatus.中毒) ) { this.poisonsLock.writeLock().lock(); try { this.poisons.add(status); status.scheduledoPoison(this); if (con != null) { this.map.broadcastMessage(con, MobPacket.applyMonsterPoisonStatus(this, this.poisons,(int)duration), getTruePosition()); con.getClient().getSession().write(MobPacket.applyMonsterPoisonStatus(this, this.poisons,(int)duration)); } else { this.map.broadcastMessage(MobPacket.applyMonsterPoisonStatus(this, this.poisons,(int)duration), getTruePosition()); } } finally { this.poisonsLock.writeLock().unlock(); } } else { this.stati.put(stat, status); if (con != null) { this.map.broadcastMessage(con, MobPacket.applyMonsterStatus(this, status,(int)duration), getTruePosition()); // MapleMonster mons, MonsterStatusEffect ms // int oid, Map<MonsterStatus, Integer> stati, List<Integer> reflection, MobSkill skil con.getClient().getSession().write(MobPacket.applyMonsterStatus(this, status,(int)duration)); } else { this.map.broadcastMessage(MobPacket.applyMonsterStatus(this, status,(int)duration), getTruePosition()); } } } public void applyStatus(MonsterStatusEffect status) { if (this.stati.containsKey(status.getStati())) { cancelStatus(status.getStati()); } this.stati.put(status.getStati(), status); this.map.broadcastMessage(MobPacket.applyMonsterStatus(this, status,status.getDotTime()), getTruePosition()); } public void dispelSkill(MobSkill skillId) { List<MonsterStatus> toCancel = new ArrayList(); for (Entry<MonsterStatus, MonsterStatusEffect> effects : this.stati.entrySet()) { MonsterStatusEffect mse = (MonsterStatusEffect) effects.getValue(); if ((mse.getMobSkill() != null) && (mse.getMobSkill().getSkillId() == skillId.getSkillId())) { toCancel.add(effects.getKey()); } } for (MonsterStatus stat : toCancel) { cancelStatus(stat); } } public void applyMonsterBuff(Map<MonsterStatus, Integer> effect, int skillId, long duration, MobSkill skill, List<Integer> reflection) { for (Map.Entry status : effect.entrySet()) { if (this.stati.containsKey((MonsterStatus) status.getKey())) { cancelStatus((MonsterStatus) status.getKey()); } MonsterStatusEffect effectz = new MonsterStatusEffect((MonsterStatus) status.getKey(), (Integer) status.getValue(), 0, skill, true, reflection.size() > 0); effectz.setCancelTask(duration); this.stati.put((MonsterStatus) status.getKey(), effectz); } MapleCharacter con = getController(); if (reflection.size() > 0) { this.reflectpack = MobPacket.applyMonsterStatus(getObjectId(), effect, reflection, skill,duration); if (con != null) { this.map.broadcastMessage(con, this.reflectpack, getTruePosition()); con.getClient().getSession().write(this.reflectpack); } else { this.map.broadcastMessage(this.reflectpack, getTruePosition()); } } else { for (Map.Entry status : effect.entrySet()) { if (con != null) { this.map.broadcastMessage(con, MobPacket.applyMonsterStatus(getObjectId(), (MonsterStatus) status.getKey(), ((Integer) status.getValue()), skill,duration), getTruePosition()); con.getClient().getSession().write(MobPacket.applyMonsterStatus(getObjectId(), (MonsterStatus) status.getKey(), ((Integer) status.getValue()), skill,duration)); } else { this.map.broadcastMessage(MobPacket.applyMonsterStatus(getObjectId(), (MonsterStatus) status.getKey(), ((Integer) status.getValue()), skill,duration), getTruePosition()); } } } } public void setTempEffectiveness(final Element e, long milli) { this.stats.setEffectiveness(e, ElementalEffectiveness.WEAK); EtcTimer.getInstance().schedule(new Runnable() { @Override public void run() { MapleMonster.this.stats.removeEffectiveness(e); } }, milli); } public boolean isBuffed(MonsterStatus status) { if ((status == MonsterStatus.中毒) ) { return (this.poisons.size() > 0) || (this.stati.containsKey(status)); } return this.stati.containsKey(status); } public MonsterStatusEffect getBuff(MonsterStatus status) { return (MonsterStatusEffect) this.stati.get(status); } public int getStatiSize() { return this.stati.size() + (this.poisons.size() > 0 ? 1 : 0); } public ArrayList<MonsterStatusEffect> getAllBuffs() { ArrayList ret = new ArrayList(); for (MonsterStatusEffect e : this.stati.values()) { ret.add(e); } this.poisonsLock.readLock().lock(); try { for (MonsterStatusEffect e : this.poisons) { ret.add(e); } } finally { this.poisonsLock.readLock().unlock(); } return ret; } public void setFake(boolean fake) { this.fake = fake; } public boolean isFake() { return this.fake; } public MapleMap getMap() { return this.map; } public List<Pair<Integer, Integer>> getSkills() { return this.stats.getSkills(); } public boolean hasSkill(int skillId, int level) { return this.stats.hasSkill(skillId, level); } public long getLastSkillUsed(int skillId) { if (this.usedSkills.containsKey(skillId)) { return (this.usedSkills.get(skillId)); } return 0L; } public void setLastSkillUsed(int skillId, long now, long cooltime) { switch (skillId) { case 140: this.usedSkills.put(skillId, now + cooltime * 2L); this.usedSkills.put(141, now); break; case 141: this.usedSkills.put(skillId, now + cooltime * 2L); this.usedSkills.put(140, now + cooltime); break; default: this.usedSkills.put(skillId, now + cooltime); } } public byte getNoSkills() { return this.stats.getNoSkills(); } public boolean isFirstAttack() { return this.stats.isFirstAttack(); } public int getBuffToGive() { return this.stats.getBuffToGive(); } public void doPoison(MonsterStatusEffect status, WeakReference<MapleCharacter> weakChr) { if (((status.getStati() == MonsterStatus.中毒)) && (this.poisons.size() <= 0)) { return; } if ((status.getStati() != MonsterStatus.中毒) && (!this.stati.containsKey(status.getStati()))) { return; } if (weakChr == null) { return; } long damage = status.getPoisonSchedule(); boolean shadowWeb = (status.getSkill() == 无影人.影网术); MapleCharacter chr = weakChr.get(); boolean cancel = (damage <= 0L) || (chr == null) || (chr.getMapId() != this.map.getId()); if (damage >= this.hp) { damage = this.hp - 1L; cancel = (!shadowWeb) || (cancel); } if (!cancel) { damage(chr, damage, true); if (shadowWeb) { this.map.broadcastMessage(MobPacket.damageMonster(getObjectId(), damage), getTruePosition()); } } } public int getLinkOid() { return this.linkoid; } public void setLinkOid(int lo) { this.linkoid = lo; } public ConcurrentEnumMap<MonsterStatus, MonsterStatusEffect> getStati() { return this.stati; } public void addEmpty() { for (MonsterStatus stat : MonsterStatus.values()) { if (stat.isEmpty()) { this.stati.put(stat, new MonsterStatusEffect(stat, 0, 0, null, false)); } } } public int getStolen() { return this.stolen; } public void setStolen(int s) { this.stolen = s; } /** * 怪物被偷东西 * @param chr */ public void handleSteal(MapleCharacter chr) { double showdown = 100.0D; Skill steal = SkillFactory.getSkill(侠客.神通术); int level = chr.getTotalSkillLevel(steal); int chServerrate = ChannelServer.getInstance(chr.getClient().getChannel()).getDropRate(chr.getWorld()); if ((level > 0) && (!getStats().isBoss()) && (this.stolen == -1) && (steal.getEffect(level).makeChanceResult())) { MapleMonsterInformationProvider mi = MapleMonsterInformationProvider.getInstance(); List de = mi.retrieveDrop(getId()); if (de == null) { this.stolen = 0; return; } List<MonsterDropEntry> dropEntry = new ArrayList(de); for (MonsterDropEntry d : dropEntry) { if ((d.itemId > 0) && (d.questid == 0) && (d.itemId / 10000 != 238) && (Randomizer.nextInt(GameConstants.DROP_ITEM_PER) < (int) (10 * d.chance * chServerrate * chr.getDropMod() * (chr.getStat().getDropBuff() / 100.0D) * (showdown / 100.0D)))) { Item idrop; if (ItemConstants.getInventoryType(d.itemId) == MapleInventoryType.EQUIP) { Equip eq = (Equip) MapleItemInformationProvider.getInstance().getEquipById(d.itemId); idrop = MapleItemInformationProvider.getInstance().randomizeStats(eq); } else { idrop = new Item(d.itemId, (byte) 0, (short) (d.Maximum != 1 ? Randomizer.nextInt(d.Maximum - d.Minimum) + d.Minimum : 1), (short) 0); } this.stolen = d.itemId; this.map.spawnMobDrop(idrop, this.map.calcDropPos(getPosition(), getTruePosition()), this, chr, (byte) 0, 0); break; } } } } public void setLastNode(int lastNode) { this.lastNode = lastNode; } public int getLastNode() { return this.lastNode; } public void cancelStatus(MonsterStatus stat) { MonsterStatusEffect mse = (MonsterStatusEffect) this.stati.get(stat); if ((mse == null) || (!isAlive())) { return; } if (mse.isReflect()) { this.reflectpack = null; } mse.cancelPoisonSchedule(this); MapleCharacter con = getController(); if (con != null) { this.map.broadcastMessage(con, MobPacket.cancelMonsterStatus(getObjectId(), stat), getTruePosition()); con.getClient().getSession().write(MobPacket.cancelMonsterStatus(getObjectId(), stat)); } else { this.map.broadcastMessage(MobPacket.cancelMonsterStatus(getObjectId(), stat), getTruePosition()); } this.stati.remove(stat); } public void cancelSingleStatus(MonsterStatusEffect stat) { if ((stat == null) || (!isAlive())) { return; } if ((stat.getStati() != MonsterStatus.中毒) ) { cancelStatus(stat.getStati()); return; } this.poisonsLock.writeLock().lock(); try { if (!this.poisons.contains(stat)) { return; } this.poisons.remove(stat); if (stat.isReflect()) { this.reflectpack = null; } stat.cancelPoisonSchedule(this); MapleCharacter con = getController(); if (con != null) { this.map.broadcastMessage(con, MobPacket.cancelMonsterPoisonStatus(getObjectId(), stat), getTruePosition()); con.getClient().getSession().write(MobPacket.cancelMonsterPoisonStatus(getObjectId(), stat)); } else { this.map.broadcastMessage(MobPacket.cancelMonsterPoisonStatus(getObjectId(), stat), getTruePosition()); } } finally { this.poisonsLock.writeLock().unlock(); } } public void cancelDropItem() { this.lastDropTime = 0L; } public void startDropItemSchedule() { cancelDropItem(); if ((this.stats.getDropItemPeriod() <= 0) || (!isAlive())) { return; } this.shouldDropItem = false; this.lastDropTime = System.currentTimeMillis(); } public boolean shouldDrop(long now) { return (this.lastDropTime > 0L) && (this.lastDropTime + this.stats.getDropItemPeriod() * 1000 < now); } public void doDropItem(long now) { int itemId; switch (getId()) { case 9300061: itemId = 4001101; break; default: cancelDropItem(); return; } if ((isAlive()) && (this.map != null)) { if (this.shouldDropItem) { this.map.spawnAutoDrop(itemId, getTruePosition()); } else { this.shouldDropItem = true; } } this.lastDropTime = now; } public byte[] getNodePacket() { return this.nodepack; } public void setNodePacket(byte[] np) { this.nodepack = np; } public void registerKill(long next) { this.nextKill = (System.currentTimeMillis() + next); } public boolean shouldKill(long now) { return (this.nextKill > 0L) && (now > this.nextKill); } public int getLinkCID() { return this.linkCID; } public void setLinkCID(int lc) { this.linkCID = lc; if (lc > 0) { // this.stati.put(MonsterStatus.心灵控制, new MonsterStatusEffect(MonsterStatus.心灵控制, 60000, 30001062, null, false)); } } private class PartyAttackerEntry implements MapleMonster.AttackerEntry { private long totDamage = 0L; private final Map<Integer, MapleMonster.OnePartyAttacker> attackers = new HashMap(6); private final int partyid; public PartyAttackerEntry(int partyid) { this.partyid = partyid; } @Override public List<MapleMonster.AttackingMapleCharacter> getAttackers() { List ret = new ArrayList(this.attackers.size()); for (Map.Entry entry : this.attackers.entrySet()) { MapleCharacter chr = MapleMonster.this.map.getCharacterById(((Integer) entry.getKey()).intValue()); if (chr != null) { ret.add(new MapleMonster.AttackingMapleCharacter(chr, ((MapleMonster.OnePartyAttacker) entry.getValue()).lastAttackTime)); } } return ret; } private Map<MapleCharacter, MapleMonster.OnePartyAttacker> resolveAttackers() { Map ret = new HashMap(this.attackers.size()); for (Map.Entry aentry : this.attackers.entrySet()) { MapleCharacter chr = MapleMonster.this.map.getCharacterById(((Integer) aentry.getKey()).intValue()); if (chr != null) { ret.put(chr, aentry.getValue()); } } return ret; } @Override public boolean contains(MapleCharacter chr) { return this.attackers.containsKey(chr.getId()); } @Override public long getDamage() { return this.totDamage; } @Override public void addDamage(MapleCharacter from, long damage, boolean updateAttackTime) { MapleMonster.OnePartyAttacker oldPartyAttacker = (MapleMonster.OnePartyAttacker) this.attackers.get(Integer.valueOf(from.getId())); if (oldPartyAttacker != null) { oldPartyAttacker.damage += damage; oldPartyAttacker.lastKnownParty = from.getParty(); if (updateAttackTime) { oldPartyAttacker.lastAttackTime = System.currentTimeMillis(); } } else { MapleMonster.OnePartyAttacker onePartyAttacker = new MapleMonster.OnePartyAttacker(from.getParty(), damage); this.attackers.put(from.getId(), onePartyAttacker); if (!updateAttackTime) { onePartyAttacker.lastAttackTime = 0L; } } this.totDamage += damage; } @Override public void killedMob(MapleMap map, int baseExp, boolean mostDamage, int lastSkill) { MapleCharacter highest = null; long highestDamage = 0L; int iexp = 0; double addedPartyLevel; //Map expMap = new HashMap(6); List<MapleCharacter> expApplicable; byte added_partyinc, 职业奖励经验, 召回戒指经验; double innerBaseExp; Map<MapleCharacter, ExpMap> expMap = new HashMap<>(6); for (final Entry<MapleCharacter, OnePartyAttacker> attacker : resolveAttackers().entrySet()) { MapleParty party = ((MapleMonster.OnePartyAttacker) attacker.getValue()).lastKnownParty; addedPartyLevel = 0.0D; added_partyinc = 0; 职业奖励经验 = 0; 召回戒指经验 = 0; expApplicable = new ArrayList(); for (MaplePartyCharacter partychar : party.getMembers()) { if ((((MapleCharacter) attacker.getKey()).getLevel() - partychar.getLevel() <= 5) || (MapleMonster.this.stats.getLevel() - partychar.getLevel() <= 5)) { MapleCharacter pchr = map.getCharacterById(partychar.getId()); if ((pchr != null) && (pchr.isAlive())) { expApplicable.add(pchr); addedPartyLevel += pchr.getLevel(); 职业奖励经验 = (byte) (职业奖励经验 + pchr.get精灵祝福()); if ((pchr.getStat().equippedWelcomeBackRing) && (召回戒指经验 == 0)) { 召回戒指经验 = 80; } if ((pchr.getStat().hasPartyBonus) && (added_partyinc < 4) && (map.getPartyBonusRate() <= 0)) { added_partyinc = (byte) (added_partyinc + 1); } } } } long iDamage = ((MapleMonster.OnePartyAttacker) attacker.getValue()).damage; if (iDamage > highestDamage) { highest = (MapleCharacter) attacker.getKey(); highestDamage = iDamage; } innerBaseExp = baseExp * ((double) iDamage / this.totDamage); if (expApplicable.size() <= 1) { 职业奖励经验 = 0; } for (MapleCharacter expReceiver : expApplicable) { iexp = expMap.get(expReceiver) == null ? 0 : ((MapleMonster.ExpMap) expMap.get(expReceiver)).exp; double levelMod = expReceiver.getLevel() / addedPartyLevel * 0.4D; iexp += (int) Math.round(((((MapleCharacter) attacker.getKey()).getId() == expReceiver.getId() ? 0.6D : 0.0D) + levelMod) * innerBaseExp); expMap.put(expReceiver, new MapleMonster.ExpMap(iexp, (byte) (expApplicable.size() + added_partyinc), 职业奖励经验, 召回戒指经验)); } } for (Entry<MapleCharacter, ExpMap> expReceiver : expMap.entrySet()) { MapleMonster.ExpMap expmap = (MapleMonster.ExpMap) expReceiver.getValue(); MapleMonster.this.giveExpToCharacter((MapleCharacter) expReceiver.getKey(), expmap.exp, expReceiver.getKey() == highest, expMap.size(), expmap.ptysize, expmap.职业奖励经验, expmap.召回戒指经验, lastSkill); } } @Override public int hashCode() { int prime = 31; int result = 1; result = prime * result + this.partyid; return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } PartyAttackerEntry other = (PartyAttackerEntry) obj; return this.partyid == other.partyid; } } private static final class OnePartyAttacker { public MapleParty lastKnownParty; public long damage; public long lastAttackTime; public OnePartyAttacker(MapleParty lastKnownParty, long damage) { this.lastKnownParty = lastKnownParty; this.damage = damage; this.lastAttackTime = System.currentTimeMillis(); } } private static final class ExpMap { public final int exp; public final byte ptysize; public final byte 职业奖励经验; public final byte 召回戒指经验; public ExpMap(int exp, byte ptysize, byte 职业奖励经验, byte 召回戒指经验) { this.exp = exp; this.ptysize = ptysize; this.职业奖励经验 = 职业奖励经验; this.召回戒指经验 = 召回戒指经验; } } private final class SingleAttackerEntry implements MapleMonster.AttackerEntry { private long damage = 0L; private final int chrid; private long lastAttackTime; public SingleAttackerEntry(MapleCharacter from) { this.chrid = from.getId(); } @Override public void addDamage(MapleCharacter from, long damage, boolean updateAttackTime) { if (this.chrid == from.getId()) { this.damage += damage; if (updateAttackTime) { this.lastAttackTime = System.currentTimeMillis(); } } } @Override public List<MapleMonster.AttackingMapleCharacter> getAttackers() { MapleCharacter chr = MapleMonster.this.map.getCharacterById(this.chrid); if (chr != null) { return Collections.singletonList(new MapleMonster.AttackingMapleCharacter(chr, this.lastAttackTime)); } return Collections.emptyList(); } @Override public boolean contains(MapleCharacter chr) { return this.chrid == chr.getId(); } @Override public long getDamage() { return this.damage; } @Override public void killedMob(MapleMap map, int baseExp, boolean mostDamage, int lastSkill) { MapleCharacter chr = map.getCharacterById(this.chrid); if ((chr != null) && (chr.isAlive())) { MapleMonster.this.giveExpToCharacter(chr, baseExp, mostDamage, 1, (byte) 0, (byte) 0, (byte) 0, lastSkill); } } @Override public int hashCode() { return this.chrid; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } SingleAttackerEntry other = (SingleAttackerEntry) obj; return this.chrid == other.chrid; } } private static abstract interface AttackerEntry { public abstract List<MapleMonster.AttackingMapleCharacter> getAttackers(); public abstract void addDamage(MapleCharacter paramMapleCharacter, long paramLong, boolean paramBoolean); public abstract long getDamage(); public abstract boolean contains(MapleCharacter paramMapleCharacter); public abstract void killedMob(MapleMap paramMapleMap, int paramInt1, boolean paramBoolean, int paramInt2); } private static class AttackingMapleCharacter { private final MapleCharacter attacker; private long lastAttackTime; public AttackingMapleCharacter(MapleCharacter attacker, long lastAttackTime) { this.attacker = attacker; this.lastAttackTime = lastAttackTime; } public long getLastAttackTime() { return this.lastAttackTime; } public void setLastAttackTime(long lastAttackTime) { this.lastAttackTime = lastAttackTime; } public MapleCharacter getAttacker() { return this.attacker; } } public void setTriangulation(int triangulation) { this.triangulation = triangulation; } public int getTriangulation() { return triangulation; } public void setmark(boolean x) { this.mark = x; } public boolean getmark() { return mark; } }