/*
* This file is part of the OdinMS Maple Story Server Copyright (C) 2008 ~ 2010
* Patrick Huy <patrick.huy@frz.cc> Matthias Butz <matze@odinms.de> Jan
* Christian Meyer <vimes@odinms.de>
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by
* the Free Software Foundation. You may not use, modify or distribute this
* program under any other version of the GNU Affero General Public License.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package javastory.channel.life;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javastory.channel.ChannelCharacter;
import javastory.channel.client.Disease;
import javastory.channel.client.MonsterStatus;
import javastory.channel.maps.GameMapObject;
import javastory.channel.maps.GameMapObjectType;
import javastory.channel.maps.Mist;
import javastory.game.BanishInfo;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
public class MobSkill {
private final int skillId, skillLevel;
private int mpCon;
private int spawnEffect;
private int hp;
private int x;
private int y;
private long duration, cooltime;
private float prop;
// private short effect_delay;
private short limit;
private List<Integer> toSummon = Lists.newArrayList();
private Point lt, rb;
public MobSkill(final int skillId, final int level) {
this.skillId = skillId;
this.skillLevel = level;
}
public void setMpCon(final int mpCon) {
this.mpCon = mpCon;
}
public void addSummons(final List<Integer> toSummon) {
this.toSummon = toSummon;
}
// public void setEffectDelay(short effect_delay) {
// this.effect_delay =* effect_delay;
// }
public void setSpawnEffect(final int spawnEffect) {
this.spawnEffect = spawnEffect;
}
public void setHp(final int hp) {
this.hp = hp;
}
public void setX(final int x) {
this.x = x;
}
public void setY(final int y) {
this.y = y;
}
public void setDuration(final long duration) {
this.duration = duration;
}
public void setCoolTime(final long cooltime) {
this.cooltime = cooltime;
}
public void setProp(final float prop) {
this.prop = prop;
}
public void setLtRb(final Point lt, final Point rb) {
this.lt = lt;
this.rb = rb;
}
public void setLimit(final short limit) {
this.limit = limit;
}
public boolean checkCurrentBuff(final ChannelCharacter player, final Monster monster) {
boolean stop = false;
switch (this.skillId) {
case 100:
case 110:
stop = monster.isBuffed(MonsterStatus.WEAPON_ATTACK_UP);
break;
case 101:
case 111:
stop = monster.isBuffed(MonsterStatus.MAGIC_ATTACK_UP);
break;
case 102:
case 112:
stop = monster.isBuffed(MonsterStatus.WEAPON_DEFENSE_UP);
break;
case 103:
case 113:
stop = monster.isBuffed(MonsterStatus.MAGIC_DEFENSE_UP);
break;
case 140:
case 141:
case 143:
case 144:
case 145:
stop = monster.isBuffed(MonsterStatus.MAGIC_IMMUNITY) || monster.isBuffed(MonsterStatus.WEAPON_IMMUNITY);
break;
case 200:
// int count = 0;
// for (MapleMapObject ob_mob : player.getMap().getAllMonster()) {
// if (((MapleMonster) ob_mob).getId() == 0) {
// count++;
// }
// }
stop = player.getMap().getAllMonster().size() >= this.limit;
break;
}
return stop;
}
public void applyEffect(final ChannelCharacter player, final Monster monster, final boolean skill) {
Disease disease = null;
final Map<MonsterStatus, Integer> stats = Maps.newEnumMap(MonsterStatus.class);
final List<Integer> reflection = Lists.newLinkedList();
switch (this.skillId) {
case 100:
case 110:
stats.put(MonsterStatus.WEAPON_ATTACK_UP, Integer.valueOf(this.x));
break;
case 101:
case 111:
stats.put(MonsterStatus.MAGIC_ATTACK_UP, Integer.valueOf(this.x));
break;
case 102:
case 112:
stats.put(MonsterStatus.WEAPON_DEFENSE_UP, Integer.valueOf(this.x));
break;
case 103:
case 113:
stats.put(MonsterStatus.MAGIC_DEFENSE_UP, Integer.valueOf(this.x));
break;
case 114:
if (this.lt != null && this.rb != null && skill) {
final List<GameMapObject> objects = this.getObjectsInRange(monster, GameMapObjectType.MONSTER);
final int hp = this.getX() / 1000 * (int) (950 + 1050 * Math.random());
for (final GameMapObject mons : objects) {
((Monster) mons).heal(hp, this.getY(), true);
}
} else {
monster.heal(this.getX(), this.getY(), true);
}
break;
case 120:
disease = Disease.SEAL;
break;
case 121:
disease = Disease.DARKNESS;
break;
case 122:
disease = Disease.WEAKEN;
break;
case 123:
disease = Disease.STUN;
break;
case 124:
disease = Disease.CURSE;
break;
case 125:
disease = Disease.POISON;
break;
case 126: // Slow
disease = Disease.SLOW;
break;
case 127:
if (this.lt != null && this.rb != null && skill) {
for (final ChannelCharacter character : this.getPlayersInRange(monster, player)) {
character.dispel();
}
} else {
player.dispel();
}
break;
case 128: // Seduce
disease = Disease.SEDUCE;
break;
case 129: // Banish
final BanishInfo info = monster.getStats().getBanishInfo();
if (this.lt != null && this.rb != null && skill) {
for (final ChannelCharacter chr : this.getPlayersInRange(monster, player)) {
chr.changeMapBanish(info.Map, info.Portal, info.Message);
}
} else {
player.changeMapBanish(info.Map, info.Portal, info.Message);
}
break;
case 131: // Mist
monster.getMap().spawnMist(new Mist(this.calculateBoundingBox(monster.getPosition(), true), monster, this), this.x * 10, false, false);
break;
case 132:
disease = Disease.REVERSE_DIRECTION;
break;
case 133:
disease = Disease.ZOMBIFY;
break;
case 140:
if (this.makeChanceResult()) {
stats.put(MonsterStatus.WEAPON_IMMUNITY, Integer.valueOf(this.x));
}
break;
case 141:
if (this.makeChanceResult()) {
stats.put(MonsterStatus.MAGIC_IMMUNITY, Integer.valueOf(this.x));
}
break;
case 143: // Weapon Reflect
stats.put(MonsterStatus.WEAPON_DAMAGE_REFLECT, Integer.valueOf(this.x));
stats.put(MonsterStatus.WEAPON_IMMUNITY, Integer.valueOf(this.x));
reflection.add(this.x);
break;
case 144: // Magic Reflect
stats.put(MonsterStatus.MAGIC_DAMAGE_REFLECT, Integer.valueOf(this.x));
stats.put(MonsterStatus.MAGIC_IMMUNITY, Integer.valueOf(this.x));
reflection.add(this.x);
break;
case 145: // Weapon / Magic reflect
stats.put(MonsterStatus.WEAPON_DAMAGE_REFLECT, Integer.valueOf(this.x));
stats.put(MonsterStatus.WEAPON_IMMUNITY, Integer.valueOf(this.x));
stats.put(MonsterStatus.MAGIC_DAMAGE_REFLECT, Integer.valueOf(this.x));
stats.put(MonsterStatus.MAGIC_IMMUNITY, Integer.valueOf(this.x));
reflection.add(this.x);
reflection.add(this.x);
break;
case 200:
for (final Integer mobId : this.getSummons()) {
final Monster toSpawn = LifeFactory.getMonster(mobId);
toSpawn.setPosition(monster.getPosition());
int ypos = (int) monster.getPosition().getY(), xpos = (int) monster.getPosition().getX();
switch (mobId) {
case 8500003: // Pap bomb high
toSpawn.setFoothold((int) Math.ceil(Math.random() * 19.0));
ypos = -590;
break;
case 8500004: // Pap bomb
// Spawn between -500 and 500 from the monsters X position
xpos = (int) (monster.getPosition().getX() + Math.ceil(Math.random() * 1000.0) - 500);
ypos = (int) monster.getPosition().getY();
break;
case 8510100: // Pianus bomb
if (Math.ceil(Math.random() * 5) == 1) {
ypos = 78;
xpos = (int) (0 + Math.ceil(Math.random() * 5)) + (Math.ceil(Math.random() * 2) == 1 ? 180 : 0);
} else {
xpos = (int) (monster.getPosition().getX() + Math.ceil(Math.random() * 1000.0) - 500);
}
break;
}
// Get spawn coordinates (This fixes monster lock)
// TODO get map left and right wall.
switch (monster.getMap().getId()) {
case 220080001: // Pap map
if (xpos < -890) {
xpos = (int) (-890 + Math.ceil(Math.random() * 150));
} else if (xpos > 230) {
xpos = (int) (230 - Math.ceil(Math.random() * 150));
}
break;
case 230040420: // Pianus map
if (xpos < -239) {
xpos = (int) (-239 + Math.ceil(Math.random() * 150));
} else if (xpos > 371) {
xpos = (int) (371 - Math.ceil(Math.random() * 150));
}
break;
}
monster.getMap().spawnMonsterWithEffect(toSpawn, this.getSpawnEffect(), monster.getMap().calcPointBelow(new Point(xpos, ypos - 1)));
}
break;
}
if (stats.size() > 0) {
if (this.lt != null && this.rb != null && skill) {
for (final GameMapObject mons : this.getObjectsInRange(monster, GameMapObjectType.MONSTER)) {
((Monster) mons).applyMonsterBuff(stats, this.getX(), this.getSkillId(), this.getDuration(), this, reflection);
}
} else {
monster.applyMonsterBuff(stats, this.getX(), this.getSkillId(), this.getDuration(), this, reflection);
}
}
if (disease != null) {
if (this.lt != null && this.rb != null && skill) {
for (final ChannelCharacter chr : this.getPlayersInRange(monster, player)) {
chr.giveDebuff(disease, this);
}
} else {
player.giveDebuff(disease, this);
}
}
monster.setMp(monster.getMp() - this.getMpCon());
}
public int getSkillId() {
return this.skillId;
}
public int getSkillLevel() {
return this.skillLevel;
}
public int getMpCon() {
return this.mpCon;
}
public List<Integer> getSummons() {
return Collections.unmodifiableList(this.toSummon);
}
/*
* public short getEffectDelay() { return effect_delay; }
*/
public int getSpawnEffect() {
return this.spawnEffect;
}
public int getHP() {
return this.hp;
}
public int getX() {
return this.x;
}
public int getY() {
return this.y;
}
public long getDuration() {
return this.duration;
}
public long getCoolTime() {
return this.cooltime;
}
public Point getLt() {
return this.lt;
}
public Point getRb() {
return this.rb;
}
public int getLimit() {
return this.limit;
}
public boolean makeChanceResult() {
return this.prop == 1.0 || Math.random() < this.prop;
}
private Rectangle calculateBoundingBox(final Point posFrom, final boolean facingLeft) {
Point mylt, myrb;
if (facingLeft) {
mylt = new Point(this.lt.x + posFrom.x, this.lt.y + posFrom.y);
myrb = new Point(this.rb.x + posFrom.x, this.rb.y + posFrom.y);
} else {
myrb = new Point(this.lt.x * -1 + posFrom.x, this.rb.y + posFrom.y);
mylt = new Point(this.rb.x * -1 + posFrom.x, this.lt.y + posFrom.y);
}
final Rectangle bounds = new Rectangle(mylt.x, mylt.y, myrb.x - mylt.x, myrb.y - mylt.y);
return bounds;
}
private List<ChannelCharacter> getPlayersInRange(final Monster monster, final ChannelCharacter player) {
final Rectangle bounds = this.calculateBoundingBox(monster.getPosition(), monster.isFacingLeft());
final List<ChannelCharacter> players = Lists.newArrayList();
players.add(player);
return monster.getMap().getPlayersInRect(bounds, players);
}
private List<GameMapObject> getObjectsInRange(final Monster monster, final GameMapObjectType objectType) {
final Rectangle bounds = this.calculateBoundingBox(monster.getPosition(), monster.isFacingLeft());
final List<GameMapObjectType> objectTypes = Lists.newArrayList();
objectTypes.add(objectType);
return monster.getMap().getMapObjectsInRect(bounds, objectTypes);
}
}