Merge pull request #116 from P0nk/mob-skill-refactoring
Clean up and refactor mob skills
This commit is contained in:
@@ -28,11 +28,12 @@
|
|||||||
<log4j.version>2.18.0</log4j.version> <!-- Slf4j implementation -->
|
<log4j.version>2.18.0</log4j.version> <!-- Slf4j implementation -->
|
||||||
<graalvm.version>22.2.0</graalvm.version> <!-- ScriptEngine implementation -->
|
<graalvm.version>22.2.0</graalvm.version> <!-- ScriptEngine implementation -->
|
||||||
<netty.version>4.1.79.Final</netty.version> <!-- Networking -->
|
<netty.version>4.1.79.Final</netty.version> <!-- Networking -->
|
||||||
<junit.version>5.9.0</junit.version> <!-- Unit test -->
|
|
||||||
<yamlbeans.version>1.15</yamlbeans.version> <!-- Config file -->
|
<yamlbeans.version>1.15</yamlbeans.version> <!-- Config file -->
|
||||||
<jcip-annotations.version>1.0</jcip-annotations.version> <!-- Annotations for concurrency documentation -->
|
<jcip-annotations.version>1.0</jcip-annotations.version> <!-- Annotations for concurrency documentation -->
|
||||||
<HikariCP.version>5.0.1</HikariCP.version> <!-- Database connection pool -->
|
<HikariCP.version>5.0.1</HikariCP.version> <!-- Database connection pool -->
|
||||||
<mysql-connector-java.version>8.0.30</mysql-connector-java.version> <!-- MySQL JDBC driver -->
|
<mysql-connector-java.version>8.0.30</mysql-connector-java.version> <!-- MySQL JDBC driver -->
|
||||||
|
<junit.version>5.9.0</junit.version> <!-- Unit test -->
|
||||||
|
<mockito.version>4.7.0</mockito.version> <!-- Unit test -->
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@@ -120,16 +121,25 @@
|
|||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter-api</artifactId>
|
<artifactId>junit-jupiter-api</artifactId>
|
||||||
<version>${junit.version}</version>
|
<version>${junit.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
<version>${junit.version}</version>
|
<version>${junit.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter-params</artifactId>
|
<artifactId>junit-jupiter-params</artifactId>
|
||||||
<version>${junit.version}</version>
|
<version>${junit.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-core</artifactId>
|
||||||
|
<version>${mockito.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|||||||
@@ -64,10 +64,7 @@ import server.events.Events;
|
|||||||
import server.events.RescueGaga;
|
import server.events.RescueGaga;
|
||||||
import server.events.gm.Fitness;
|
import server.events.gm.Fitness;
|
||||||
import server.events.gm.Ola;
|
import server.events.gm.Ola;
|
||||||
import server.life.MobSkill;
|
import server.life.*;
|
||||||
import server.life.MobSkillFactory;
|
|
||||||
import server.life.Monster;
|
|
||||||
import server.life.PlayerNPC;
|
|
||||||
import server.maps.*;
|
import server.maps.*;
|
||||||
import server.maps.MiniGame.MiniGameResult;
|
import server.maps.MiniGame.MiniGameResult;
|
||||||
import server.minigame.RockPaperScissor;
|
import server.minigame.RockPaperScissor;
|
||||||
@@ -7329,13 +7326,11 @@ public class Character extends AbstractCharacterObject {
|
|||||||
final int skilllv = rs.getInt("mobskilllv");
|
final int skilllv = rs.getInt("mobskilllv");
|
||||||
final long length = rs.getInt("length");
|
final long length = rs.getInt("length");
|
||||||
|
|
||||||
MobSkill ms = MobSkillFactory.getMobSkill(skillid, skilllv);
|
MobSkill ms = MobSkillFactory.getMobSkill(MobSkillType.from(skillid), skilllv);
|
||||||
if (ms != null) {
|
|
||||||
loadedDiseases.put(disease, new Pair<>(length, ms));
|
loadedDiseases.put(disease, new Pair<>(length, ms));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Debuffs (delete)
|
// Debuffs (delete)
|
||||||
try (PreparedStatement ps = con.prepareStatement("DELETE FROM playerdiseases WHERE charid = ?")) {
|
try (PreparedStatement ps = con.prepareStatement("DELETE FROM playerdiseases WHERE charid = ?")) {
|
||||||
@@ -8111,8 +8106,9 @@ public class Character extends AbstractCharacterObject {
|
|||||||
ps.setInt(2, e.getKey().ordinal());
|
ps.setInt(2, e.getKey().ordinal());
|
||||||
|
|
||||||
MobSkill ms = e.getValue().getRight();
|
MobSkill ms = e.getValue().getRight();
|
||||||
ps.setInt(3, ms.getSkillId());
|
MobSkillId msId = ms.getId();
|
||||||
ps.setInt(4, ms.getSkillLevel());
|
ps.setInt(3, msId.type().getId());
|
||||||
|
ps.setInt(4, msId.level());
|
||||||
ps.setInt(5, e.getValue().getLeft().intValue());
|
ps.setInt(5, e.getValue().getLeft().intValue());
|
||||||
ps.addBatch();
|
ps.addBatch();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,37 +22,34 @@
|
|||||||
package client;
|
package client;
|
||||||
|
|
||||||
import constants.game.GameConstants;
|
import constants.game.GameConstants;
|
||||||
|
import server.life.MobSkillType;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
public enum Disease {
|
public enum Disease {
|
||||||
NULL(0x0),
|
NULL(0x0),
|
||||||
SLOW(0x1, 126),
|
SLOW(0x1, MobSkillType.SLOW),
|
||||||
SEDUCE(0x80, 128),
|
SEDUCE(0x80, MobSkillType.SEDUCE),
|
||||||
FISHABLE(0x100),
|
FISHABLE(0x100),
|
||||||
ZOMBIFY(0x4000),
|
ZOMBIFY(0x4000),
|
||||||
CONFUSE(0x80000, 132),
|
CONFUSE(0x80000, MobSkillType.REVERSE_INPUT),
|
||||||
STUN(0x2000000000000L, 123),
|
STUN(0x2000000000000L, MobSkillType.STUN),
|
||||||
POISON(0x4000000000000L, 125),
|
POISON(0x4000000000000L, MobSkillType.POISON),
|
||||||
SEAL(0x8000000000000L, 120),
|
SEAL(0x8000000000000L, MobSkillType.SEAL),
|
||||||
DARKNESS(0x10000000000000L, 121),
|
DARKNESS(0x10000000000000L, MobSkillType.DARKNESS),
|
||||||
WEAKEN(0x4000000000000000L, 122),
|
WEAKEN(0x4000000000000000L, MobSkillType.WEAKNESS),
|
||||||
CURSE(0x8000000000000000L, 124);
|
CURSE(0x8000000000000000L, MobSkillType.CURSE);
|
||||||
|
|
||||||
private final long i;
|
private final long i;
|
||||||
private final boolean first;
|
private final MobSkillType mobSkillType;
|
||||||
private final int mobskill;
|
|
||||||
|
|
||||||
Disease(long i) {
|
Disease(long i) {
|
||||||
this(i, false, 0);
|
this(i, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
Disease(long i, int skill) {
|
Disease(long i, MobSkillType skill) {
|
||||||
this(i, false, skill);
|
|
||||||
}
|
|
||||||
|
|
||||||
Disease(long i, boolean first, int skill) {
|
|
||||||
this.i = i;
|
this.i = i;
|
||||||
this.first = first;
|
this.mobSkillType = skill;
|
||||||
this.mobskill = skill;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getValue() {
|
public long getValue() {
|
||||||
@@ -60,11 +57,11 @@ public enum Disease {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFirst() {
|
public boolean isFirst() {
|
||||||
return first;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getDisease() {
|
public MobSkillType getMobSkillType() {
|
||||||
return mobskill;
|
return mobSkillType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Disease ordinal(int ord) {
|
public static Disease ordinal(int ord) {
|
||||||
@@ -80,13 +77,14 @@ public enum Disease {
|
|||||||
return diseases[(int) (Math.random() * diseases.length)];
|
return diseases[(int) (Math.random() * diseases.length)];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Disease getBySkill(final int skill) {
|
public static final Disease getBySkill(MobSkillType skill) {
|
||||||
for (Disease d : Disease.values()) {
|
if (skill == null) {
|
||||||
if (d.getDisease() == skill && d.getDisease() != 0) {
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
return Arrays.stream(Disease.values())
|
||||||
|
.filter(d -> d.mobSkillType == skill)
|
||||||
|
.findAny()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ import client.Client;
|
|||||||
import client.command.Command;
|
import client.command.Command;
|
||||||
import server.life.MobSkill;
|
import server.life.MobSkill;
|
||||||
import server.life.MobSkillFactory;
|
import server.life.MobSkillFactory;
|
||||||
|
import server.life.MobSkillType;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
@@ -21,7 +22,8 @@ public class MobSkillCommand extends Command {
|
|||||||
|
|
||||||
String skillId = params[0];
|
String skillId = params[0];
|
||||||
String skillLevel = params[1];
|
String skillLevel = params[1];
|
||||||
MobSkill mobSkill = MobSkillFactory.getMobSkill(Integer.parseInt(skillId), Integer.parseInt(skillLevel));
|
MobSkillType type = MobSkillType.from(Integer.parseInt(skillId));
|
||||||
|
MobSkill mobSkill = MobSkillFactory.getMobSkill(type, Integer.parseInt(skillLevel));
|
||||||
if (mobSkill == null) {
|
if (mobSkill == null) {
|
||||||
throw new IllegalArgumentException("Mob skill not found. Id: %s, level: %s".formatted(skillId, skillLevel));
|
throw new IllegalArgumentException("Mob skill not found. Id: %s, level: %s".formatted(skillId, skillLevel));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import client.Disease;
|
|||||||
import client.command.Command;
|
import client.command.Command;
|
||||||
import server.life.MobSkill;
|
import server.life.MobSkill;
|
||||||
import server.life.MobSkillFactory;
|
import server.life.MobSkillFactory;
|
||||||
|
import server.life.MobSkillType;
|
||||||
import server.maps.MapObject;
|
import server.maps.MapObject;
|
||||||
import server.maps.MapObjectType;
|
import server.maps.MapObjectType;
|
||||||
|
|
||||||
@@ -51,55 +52,46 @@ public class DebuffCommand extends Command {
|
|||||||
MobSkill skill = null;
|
MobSkill skill = null;
|
||||||
|
|
||||||
switch (params[0].toUpperCase()) {
|
switch (params[0].toUpperCase()) {
|
||||||
case "SLOW":
|
case "SLOW" -> {
|
||||||
disease = Disease.SLOW;
|
disease = Disease.SLOW;
|
||||||
skill = MobSkillFactory.getMobSkill(126, 7);
|
skill = MobSkillFactory.getMobSkill(MobSkillType.SLOW, 7);
|
||||||
break;
|
}
|
||||||
|
case "SEDUCE" -> {
|
||||||
case "SEDUCE":
|
|
||||||
disease = Disease.SEDUCE;
|
disease = Disease.SEDUCE;
|
||||||
skill = MobSkillFactory.getMobSkill(128, 7);
|
skill = MobSkillFactory.getMobSkill(MobSkillType.SEDUCE, 7);
|
||||||
break;
|
}
|
||||||
|
case "ZOMBIFY" -> {
|
||||||
case "ZOMBIFY":
|
|
||||||
disease = Disease.ZOMBIFY;
|
disease = Disease.ZOMBIFY;
|
||||||
skill = MobSkillFactory.getMobSkill(133, 1);
|
skill = MobSkillFactory.getMobSkill(MobSkillType.UNDEAD, 1);
|
||||||
break;
|
}
|
||||||
|
case "CONFUSE" -> {
|
||||||
case "CONFUSE":
|
|
||||||
disease = Disease.CONFUSE;
|
disease = Disease.CONFUSE;
|
||||||
skill = MobSkillFactory.getMobSkill(132, 2);
|
skill = MobSkillFactory.getMobSkill(MobSkillType.REVERSE_INPUT, 2);
|
||||||
break;
|
}
|
||||||
|
case "STUN" -> {
|
||||||
case "STUN":
|
|
||||||
disease = Disease.STUN;
|
disease = Disease.STUN;
|
||||||
skill = MobSkillFactory.getMobSkill(123, 7);
|
skill = MobSkillFactory.getMobSkill(MobSkillType.STUN, 7);
|
||||||
break;
|
}
|
||||||
|
case "POISON" -> {
|
||||||
case "POISON":
|
|
||||||
disease = Disease.POISON;
|
disease = Disease.POISON;
|
||||||
skill = MobSkillFactory.getMobSkill(125, 5);
|
skill = MobSkillFactory.getMobSkill(MobSkillType.POISON, 5);
|
||||||
break;
|
}
|
||||||
|
case "SEAL" -> {
|
||||||
case "SEAL":
|
|
||||||
disease = Disease.SEAL;
|
disease = Disease.SEAL;
|
||||||
skill = MobSkillFactory.getMobSkill(120, 1);
|
skill = MobSkillFactory.getMobSkill(MobSkillType.SEAL, 1);
|
||||||
break;
|
}
|
||||||
|
case "DARKNESS" -> {
|
||||||
case "DARKNESS":
|
|
||||||
disease = Disease.DARKNESS;
|
disease = Disease.DARKNESS;
|
||||||
skill = MobSkillFactory.getMobSkill(121, 1);
|
skill = MobSkillFactory.getMobSkill(MobSkillType.DARKNESS, 1);
|
||||||
break;
|
}
|
||||||
|
case "WEAKEN" -> {
|
||||||
case "WEAKEN":
|
|
||||||
disease = Disease.WEAKEN;
|
disease = Disease.WEAKEN;
|
||||||
skill = MobSkillFactory.getMobSkill(122, 1);
|
skill = MobSkillFactory.getMobSkill(MobSkillType.WEAKNESS, 1);
|
||||||
break;
|
}
|
||||||
|
case "CURSE" -> {
|
||||||
case "CURSE":
|
|
||||||
disease = Disease.CURSE;
|
disease = Disease.CURSE;
|
||||||
skill = MobSkillFactory.getMobSkill(124, 1);
|
skill = MobSkillFactory.getMobSkill(MobSkillType.CURSE, 1);
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (disease == null) {
|
if (disease == null) {
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ import server.maps.MapObject;
|
|||||||
import server.maps.MapObjectType;
|
import server.maps.MapObjectType;
|
||||||
import server.maps.MapleMap;
|
import server.maps.MapleMap;
|
||||||
import tools.PacketCreator;
|
import tools.PacketCreator;
|
||||||
import tools.Pair;
|
|
||||||
import tools.Randomizer;
|
import tools.Randomizer;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
@@ -491,22 +490,18 @@ public abstract class AbstractDealDamageHandler extends AbstractPacketHandler {
|
|||||||
map.damageMonster(player, monster, totDamageToOneMonster);
|
map.damageMonster(player, monster, totDamageToOneMonster);
|
||||||
}
|
}
|
||||||
if (monster.isBuffed(MonsterStatus.WEAPON_REFLECT) && !attack.magic) {
|
if (monster.isBuffed(MonsterStatus.WEAPON_REFLECT) && !attack.magic) {
|
||||||
List<Pair<Integer, Integer>> mobSkills = monster.getSkills();
|
for (MobSkillId msId : monster.getSkills()) {
|
||||||
|
if (msId.type() == MobSkillType.PHYSICAL_AND_MAGIC_COUNTER) {
|
||||||
for (Pair<Integer, Integer> ms : mobSkills) {
|
MobSkill toUse = MobSkillFactory.getMobSkill(MobSkillType.PHYSICAL_AND_MAGIC_COUNTER, msId.level());
|
||||||
if (ms.left == 145) {
|
|
||||||
MobSkill toUse = MobSkillFactory.getMobSkill(ms.left, ms.right);
|
|
||||||
player.addHP(-toUse.getX());
|
player.addHP(-toUse.getX());
|
||||||
map.broadcastMessage(player, PacketCreator.damagePlayer(0, monster.getId(), player.getId(), toUse.getX(), 0, 0, false, 0, true, monster.getObjectId(), 0, 0), true);
|
map.broadcastMessage(player, PacketCreator.damagePlayer(0, monster.getId(), player.getId(), toUse.getX(), 0, 0, false, 0, true, monster.getObjectId(), 0, 0), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (monster.isBuffed(MonsterStatus.MAGIC_REFLECT) && attack.magic) {
|
if (monster.isBuffed(MonsterStatus.MAGIC_REFLECT) && attack.magic) {
|
||||||
List<Pair<Integer, Integer>> mobSkills = monster.getSkills();
|
for (MobSkillId msId : monster.getSkills()) {
|
||||||
|
if (msId.type() == MobSkillType.PHYSICAL_AND_MAGIC_COUNTER) {
|
||||||
for (Pair<Integer, Integer> ms : mobSkills) {
|
MobSkill toUse = MobSkillFactory.getMobSkill(MobSkillType.PHYSICAL_AND_MAGIC_COUNTER, msId.level());
|
||||||
if (ms.left == 145) {
|
|
||||||
MobSkill toUse = MobSkillFactory.getMobSkill(ms.left, ms.right);
|
|
||||||
player.addHP(-toUse.getY());
|
player.addHP(-toUse.getY());
|
||||||
map.broadcastMessage(player, PacketCreator.damagePlayer(0, monster.getId(), player.getId(), toUse.getY(), 0, 0, false, 0, true, monster.getObjectId(), 0, 0), true);
|
map.broadcastMessage(player, PacketCreator.damagePlayer(0, monster.getId(), player.getId(), toUse.getY(), 0, 0, false, 0, true, monster.getObjectId(), 0, 0), true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import net.packet.InPacket;
|
|||||||
import net.server.world.Party;
|
import net.server.world.Party;
|
||||||
import net.server.world.PartyCharacter;
|
import net.server.world.PartyCharacter;
|
||||||
import server.life.LifeFactory;
|
import server.life.LifeFactory;
|
||||||
|
import server.life.MobSkillType;
|
||||||
import server.life.Monster;
|
import server.life.Monster;
|
||||||
import server.partyquest.CarnivalFactory;
|
import server.partyquest.CarnivalFactory;
|
||||||
import server.partyquest.CarnivalFactory.MCSkill;
|
import server.partyquest.CarnivalFactory.MCSkill;
|
||||||
@@ -94,18 +95,15 @@ public final class MonsterCarnivalHandler extends AbstractPacketHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final MCSkill skill = CarnivalFactory.getInstance().getSkill(skillid.get(num)); //ugh wtf
|
final MCSkill skill = CarnivalFactory.getInstance().getSkill(skillid.get(num)); //ugh wtf
|
||||||
if (skill == null || c.getPlayer().getCP() < skill.cpLoss) {
|
if (skill == null || c.getPlayer().getCP() < skill.cpLoss()) {
|
||||||
c.sendPacket(PacketCreator.CPQMessage((byte) 1));
|
c.sendPacket(PacketCreator.CPQMessage((byte) 1));
|
||||||
c.sendPacket(PacketCreator.enableActions());
|
c.sendPacket(PacketCreator.enableActions());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final Disease dis = skill.getDisease();
|
final Disease dis = skill.getDisease();
|
||||||
Party enemies = c.getPlayer().getParty().getEnemy();
|
Party enemies = c.getPlayer().getParty().getEnemy();
|
||||||
if (skill.targetsAll) {
|
if (skill.targetsAll()) {
|
||||||
int hitChance = 0;
|
int hitChance = rollHitChance(dis.getMobSkillType());
|
||||||
if (dis.getDisease() == 121 || dis.getDisease() == 122 || dis.getDisease() == 125 || dis.getDisease() == 126) {
|
|
||||||
hitChance = (int) (Math.random() * 100);
|
|
||||||
}
|
|
||||||
if (hitChance <= 80) {
|
if (hitChance <= 80) {
|
||||||
for (PartyCharacter mpc : enemies.getPartyMembers()) {
|
for (PartyCharacter mpc : enemies.getPartyMembers()) {
|
||||||
Character mc = mpc.getPlayer();
|
Character mc = mpc.getPlayer();
|
||||||
@@ -130,11 +128,11 @@ public final class MonsterCarnivalHandler extends AbstractPacketHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
neededCP = skill.cpLoss;
|
neededCP = skill.cpLoss();
|
||||||
c.sendPacket(PacketCreator.enableActions());
|
c.sendPacket(PacketCreator.enableActions());
|
||||||
} else if (tab == 2) { //protectors
|
} else if (tab == 2) { //protectors
|
||||||
final MCSkill skill = CarnivalFactory.getInstance().getGuardian(num);
|
final MCSkill skill = CarnivalFactory.getInstance().getGuardian(num);
|
||||||
if (skill == null || c.getPlayer().getCP() < skill.cpLoss) {
|
if (skill == null || c.getPlayer().getCP() < skill.cpLoss()) {
|
||||||
c.sendPacket(PacketCreator.CPQMessage((byte) 1));
|
c.sendPacket(PacketCreator.CPQMessage((byte) 1));
|
||||||
c.sendPacket(PacketCreator.enableActions());
|
c.sendPacket(PacketCreator.enableActions());
|
||||||
return;
|
return;
|
||||||
@@ -165,7 +163,7 @@ public final class MonsterCarnivalHandler extends AbstractPacketHandler {
|
|||||||
c.sendPacket(PacketCreator.enableActions());
|
c.sendPacket(PacketCreator.enableActions());
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
neededCP = skill.cpLoss;
|
neededCP = skill.cpLoss();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -179,4 +177,11 @@ public final class MonsterCarnivalHandler extends AbstractPacketHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int rollHitChance(MobSkillType type) {
|
||||||
|
return switch (type) {
|
||||||
|
case DARKNESS, WEAKNESS, POISON, SLOW -> (int) (Math.random() * 100);
|
||||||
|
default -> 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,16 +27,11 @@ import config.YamlConfig;
|
|||||||
import net.packet.InPacket;
|
import net.packet.InPacket;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import server.life.MobSkill;
|
import server.life.*;
|
||||||
import server.life.MobSkillFactory;
|
|
||||||
import server.life.Monster;
|
|
||||||
import server.life.MonsterInformationProvider;
|
|
||||||
import server.maps.MapObject;
|
import server.maps.MapObject;
|
||||||
import server.maps.MapObjectType;
|
import server.maps.MapObjectType;
|
||||||
import server.maps.MapleMap;
|
import server.maps.MapleMap;
|
||||||
import tools.PacketCreator;
|
import tools.PacketCreator;
|
||||||
import tools.Pair;
|
|
||||||
import tools.Randomizer;
|
|
||||||
import tools.exceptions.EmptyMovementException;
|
import tools.exceptions.EmptyMovementException;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
@@ -84,7 +79,6 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
|
|||||||
boolean isAttack = inRangeInclusive(rawActivity, 24, 41);
|
boolean isAttack = inRangeInclusive(rawActivity, 24, 41);
|
||||||
boolean isSkill = inRangeInclusive(rawActivity, 42, 59);
|
boolean isSkill = inRangeInclusive(rawActivity, 42, 59);
|
||||||
|
|
||||||
MobSkill toUse = null;
|
|
||||||
int useSkillId = 0, useSkillLevel = 0;
|
int useSkillId = 0, useSkillLevel = 0;
|
||||||
|
|
||||||
MobSkill nextUse = null;
|
MobSkill nextUse = null;
|
||||||
@@ -92,18 +86,16 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
|
|||||||
|
|
||||||
boolean nextMovementCouldBeSkill = !(isSkill || (pNibbles != 0));
|
boolean nextMovementCouldBeSkill = !(isSkill || (pNibbles != 0));
|
||||||
|
|
||||||
int castPos;
|
|
||||||
if (isSkill) {
|
if (isSkill) {
|
||||||
useSkillId = skillId;
|
useSkillId = skillId;
|
||||||
useSkillLevel = skillLv;
|
useSkillLevel = skillLv;
|
||||||
|
|
||||||
castPos = monster.getSkillPos(useSkillId, useSkillLevel);
|
if (monster.hasSkill(useSkillId, useSkillLevel)) {
|
||||||
if (castPos != -1) {
|
MobSkill toUse = MobSkillFactory.getMobSkill(MobSkillType.from(useSkillId), useSkillLevel);
|
||||||
toUse = MobSkillFactory.getMobSkill(useSkillId, useSkillLevel);
|
|
||||||
|
|
||||||
if (monster.canUseSkill(toUse, true)) {
|
if (monster.canUseSkill(toUse, true)) {
|
||||||
int animationTime = MonsterInformationProvider.getInstance().getMobSkillAnimationTime(toUse);
|
int animationTime = MonsterInformationProvider.getInstance().getMobSkillAnimationTime(toUse);
|
||||||
if (animationTime > 0 && toUse.getSkillId() != 129) {
|
if (animationTime > 0 && toUse.getType() != MobSkillType.BANISH) {
|
||||||
toUse.applyDelayedEffect(player, monster, true, animationTime);
|
toUse.applyDelayedEffect(player, monster, true, animationTime);
|
||||||
} else {
|
} else {
|
||||||
banishPlayers = new LinkedList<>();
|
banishPlayers = new LinkedList<>();
|
||||||
@@ -112,8 +104,7 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
castPos = (rawActivity - 24) / 2;
|
int castPos = (rawActivity - 24) / 2;
|
||||||
|
|
||||||
int atkStatus = monster.canUseAttack(castPos, isSkill);
|
int atkStatus = monster.canUseAttack(castPos, isSkill);
|
||||||
if (atkStatus < 1) {
|
if (atkStatus < 1) {
|
||||||
rawActivity = -1;
|
rawActivity = -1;
|
||||||
@@ -122,15 +113,11 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int mobMp = monster.getMp();
|
int mobMp = monster.getMp();
|
||||||
if (nextMovementCouldBeSkill) {
|
if (nextMovementCouldBeSkill && monster.hasAnySkill()) {
|
||||||
int noSkills = monster.getNoSkills();
|
MobSkillId skillToUse = monster.getRandomSkill();
|
||||||
if (noSkills > 0) {
|
nextSkillId = skillToUse.type().getId();
|
||||||
int rndSkill = Randomizer.nextInt(noSkills);
|
nextSkillLevel = skillToUse.level();
|
||||||
|
nextUse = MobSkillFactory.getMobSkill(skillToUse.type(), skillToUse.level());
|
||||||
Pair<Integer, Integer> skillToUse = monster.getSkills().get(rndSkill);
|
|
||||||
nextSkillId = skillToUse.getLeft();
|
|
||||||
nextSkillLevel = skillToUse.getRight();
|
|
||||||
nextUse = MobSkillFactory.getMobSkill(nextSkillId, nextSkillLevel);
|
|
||||||
|
|
||||||
if (!(nextUse != null && monster.canUseSkill(nextUse, false) && nextUse.getHP() >= (int) (((float) monster.getHp() / monster.getMaxHp()) * 100) && mobMp >= nextUse.getMpCon())) {
|
if (!(nextUse != null && monster.canUseSkill(nextUse, false) && nextUse.getHP() >= (int) (((float) monster.getHp() / monster.getMaxHp()) * 100) && mobMp >= nextUse.getMpCon())) {
|
||||||
// thanks OishiiKawaiiDesu for noticing mobs trying to cast skills they are not supposed to be able
|
// thanks OishiiKawaiiDesu for noticing mobs trying to cast skills they are not supposed to be able
|
||||||
@@ -140,7 +127,6 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
|
|||||||
nextUse = null;
|
nextUse = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
p.readByte();
|
p.readByte();
|
||||||
p.readInt(); // whatever
|
p.readInt(); // whatever
|
||||||
@@ -168,8 +154,8 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
|
|||||||
p.seek(movementDataStart);
|
p.seek(movementDataStart);
|
||||||
|
|
||||||
if (YamlConfig.config.server.USE_DEBUG_SHOW_RCVD_MVLIFE) {
|
if (YamlConfig.config.server.USE_DEBUG_SHOW_RCVD_MVLIFE) {
|
||||||
log.debug("{} castPos: {}, rawAct: {}, opt: {}, skillId: {}, skillLv: {}, allowSkill: {}, mobMp: {}",
|
log.debug("{} rawAct: {}, opt: {}, skillId: {}, skillLv: {}, allowSkill: {}, mobMp: {}",
|
||||||
isSkill ? "SKILL" : (isAttack ? "ATTCK" : ""), castPos, rawActivity, pOption, useSkillId,
|
isSkill ? "SKILL" : (isAttack ? "ATTCK" : ""), rawActivity, pOption, useSkillId,
|
||||||
useSkillLevel, nextMovementCouldBeSkill, mobMp);
|
useSkillLevel, nextMovementCouldBeSkill, mobMp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ public final class TakeDamageHandler extends AbstractPacketHandler {
|
|||||||
is_deadly = true;
|
is_deadly = true;
|
||||||
}
|
}
|
||||||
mpattack += attackInfo.getMpBurn();
|
mpattack += attackInfo.getMpBurn();
|
||||||
MobSkill mobSkill = MobSkillFactory.getMobSkill(attackInfo.getDiseaseSkill(), attackInfo.getDiseaseLevel());
|
MobSkill mobSkill = MobSkillFactory.getMobSkill(MobSkillType.from(attackInfo.getDiseaseSkill()), attackInfo.getDiseaseLevel());
|
||||||
if (mobSkill != null && damage > 0) {
|
if (mobSkill != null && damage > 0) {
|
||||||
mobSkill.applyEffect(chr, attacker, false, banishPlayers);
|
mobSkill.applyEffect(chr, attacker, false, banishPlayers);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -965,10 +965,6 @@ public class AbstractPlayerInteraction {
|
|||||||
return LifeFactory.getMonster(mid);
|
return LifeFactory.getMonster(mid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MobSkill getMobSkill(int skill, int level) {
|
|
||||||
return MobSkillFactory.getMobSkill(skill, level);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void spawnGuide() {
|
public void spawnGuide() {
|
||||||
c.sendPacket(PacketCreator.spawnGuide(true));
|
c.sendPacket(PacketCreator.spawnGuide(true));
|
||||||
}
|
}
|
||||||
@@ -1199,12 +1195,12 @@ public class AbstractPlayerInteraction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void applySealSkill(Monster monster) {
|
private void applySealSkill(Monster monster) {
|
||||||
MobSkill sealSkill = MobSkillFactory.getMobSkill(157, 1);
|
MobSkill sealSkill = MobSkillFactory.getMobSkill(MobSkillType.SEAL_SKILL, 1);
|
||||||
sealSkill.applyEffect(monster);
|
sealSkill.applyEffect(monster);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyReduceAvoid(Monster monster) {
|
private void applyReduceAvoid(Monster monster) {
|
||||||
MobSkill reduceAvoidSkill = MobSkillFactory.getMobSkill(155, 2);
|
MobSkill reduceAvoidSkill = MobSkillFactory.getMobSkill(MobSkillType.EVA, 2);
|
||||||
reduceAvoidSkill.applyEffect(monster);
|
reduceAvoidSkill.applyEffect(monster);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,11 +42,11 @@ import provider.Data;
|
|||||||
import provider.DataTool;
|
import provider.DataTool;
|
||||||
import server.life.MobSkill;
|
import server.life.MobSkill;
|
||||||
import server.life.MobSkillFactory;
|
import server.life.MobSkillFactory;
|
||||||
|
import server.life.MobSkillType;
|
||||||
import server.life.Monster;
|
import server.life.Monster;
|
||||||
import server.maps.*;
|
import server.maps.*;
|
||||||
import server.partyquest.CarnivalFactory;
|
import server.partyquest.CarnivalFactory;
|
||||||
import server.partyquest.CarnivalFactory.MCSkill;
|
import server.partyquest.CarnivalFactory.MCSkill;
|
||||||
import tools.ArrayMap;
|
|
||||||
import tools.PacketCreator;
|
import tools.PacketCreator;
|
||||||
import tools.Pair;
|
import tools.Pair;
|
||||||
|
|
||||||
@@ -422,7 +422,7 @@ public class StatEffect {
|
|||||||
ret.itemCon = DataTool.getInt("itemCon", source, 0);
|
ret.itemCon = DataTool.getInt("itemCon", source, 0);
|
||||||
ret.itemConNo = DataTool.getInt("itemConNo", source, 0);
|
ret.itemConNo = DataTool.getInt("itemConNo", source, 0);
|
||||||
ret.moveTo = DataTool.getInt("moveTo", source, -1);
|
ret.moveTo = DataTool.getInt("moveTo", source, -1);
|
||||||
Map<MonsterStatus, Integer> monsterStatus = new ArrayMap<>();
|
Map<MonsterStatus, Integer> monsterStatus = new EnumMap<>(MonsterStatus.class);
|
||||||
if (skill) {
|
if (skill) {
|
||||||
switch (sourceid) {
|
switch (sourceid) {
|
||||||
// BEGINNER
|
// BEGINNER
|
||||||
@@ -1039,14 +1039,15 @@ public class StatEffect {
|
|||||||
if (skill != null) {
|
if (skill != null) {
|
||||||
final Disease dis = skill.getDisease();
|
final Disease dis = skill.getDisease();
|
||||||
Party opposition = applyfrom.getParty().getEnemy();
|
Party opposition = applyfrom.getParty().getEnemy();
|
||||||
if (skill.targetsAll) {
|
if (skill.targetsAll()) {
|
||||||
for (PartyCharacter enemyChrs : opposition.getPartyMembers()) {
|
for (PartyCharacter enemyChrs : opposition.getPartyMembers()) {
|
||||||
Character chrApp = enemyChrs.getPlayer();
|
Character chrApp = enemyChrs.getPlayer();
|
||||||
if (chrApp != null && chrApp.getMap().isCPQMap()) {
|
if (chrApp != null && chrApp.getMap().isCPQMap()) {
|
||||||
if (dis == null) {
|
if (dis == null) {
|
||||||
chrApp.dispel();
|
chrApp.dispel();
|
||||||
} else {
|
} else {
|
||||||
chrApp.giveDebuff(dis, MCSkill.getMobSkill(dis.getDisease(), skill.level));
|
MobSkill mobSkill = MobSkillFactory.getMobSkill(dis.getMobSkillType(), skill.level());
|
||||||
|
chrApp.giveDebuff(dis, mobSkill);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1058,7 +1059,8 @@ public class StatEffect {
|
|||||||
if (dis == null) {
|
if (dis == null) {
|
||||||
chrApp.dispel();
|
chrApp.dispel();
|
||||||
} else {
|
} else {
|
||||||
chrApp.giveDebuff(dis, MCSkill.getMobSkill(dis.getDisease(), skill.level));
|
MobSkill mobSkill = MobSkillFactory.getMobSkill(dis.getMobSkillType(), skill.level());
|
||||||
|
chrApp.giveDebuff(dis, mobSkill);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1068,8 +1070,9 @@ public class StatEffect {
|
|||||||
applyfrom.dispelDebuff(debuff);
|
applyfrom.dispelDebuff(debuff);
|
||||||
}
|
}
|
||||||
} else if (mobSkill > 0 && mobSkillLevel > 0) {
|
} else if (mobSkill > 0 && mobSkillLevel > 0) {
|
||||||
MobSkill ms = MobSkillFactory.getMobSkill(mobSkill, mobSkillLevel);
|
var mobSkillType = MobSkillType.from(mobSkill);
|
||||||
Disease dis = Disease.getBySkill(mobSkill);
|
MobSkill ms = MobSkillFactory.getMobSkill(mobSkillType, mobSkillLevel);
|
||||||
|
Disease dis = Disease.getBySkill(mobSkillType);
|
||||||
|
|
||||||
if (target > 0) {
|
if (target > 0) {
|
||||||
for (Character chr : applyto.getMap().getAllPlayers()) {
|
for (Character chr : applyto.getMap().getAllPlayers()) {
|
||||||
|
|||||||
@@ -188,11 +188,12 @@ public class LifeFactory {
|
|||||||
Data monsterSkillInfoData = monsterInfoData.getChildByPath("skill");
|
Data monsterSkillInfoData = monsterInfoData.getChildByPath("skill");
|
||||||
if (monsterSkillInfoData != null) {
|
if (monsterSkillInfoData != null) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
List<Pair<Integer, Integer>> skills = new ArrayList<>();
|
Set<MobSkillId> skills = new HashSet<>();
|
||||||
while (monsterSkillInfoData.getChildByPath(Integer.toString(i)) != null) {
|
while (monsterSkillInfoData.getChildByPath(Integer.toString(i)) != null) {
|
||||||
int skillId = DataTool.getInt(i + "/skill", monsterSkillInfoData, 0);
|
int skillId = DataTool.getInt(i + "/skill", monsterSkillInfoData, 0);
|
||||||
int skillLv = DataTool.getInt(i + "/level", monsterSkillInfoData, 0);
|
int skillLv = DataTool.getInt(i + "/level", monsterSkillInfoData, 0);
|
||||||
skills.add(new Pair<>(skillId, skillLv));
|
MobSkillType type = MobSkillType.from(skillId);
|
||||||
|
skills.add(new MobSkillId(type, skillLv));
|
||||||
|
|
||||||
Data monsterSkillData = monsterData.getChildByPath("skill" + (i + 1));
|
Data monsterSkillData = monsterData.getChildByPath("skill" + (i + 1));
|
||||||
if (monsterSkillData != null) {
|
if (monsterSkillData != null) {
|
||||||
@@ -201,7 +202,7 @@ public class LifeFactory {
|
|||||||
animationTime += DataTool.getIntConvert("delay", effectEntry, 0);
|
animationTime += DataTool.getIntConvert("delay", effectEntry, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
MobSkill skill = MobSkillFactory.getMobSkill(skillId, skillLv);
|
MobSkill skill = MobSkillFactory.getMobSkill(type, skillLv);
|
||||||
mi.setMobSkillAnimationTime(skill, animationTime);
|
mi.setMobSkillAnimationTime(skill, animationTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import client.Disease;
|
|||||||
import client.status.MonsterStatus;
|
import client.status.MonsterStatus;
|
||||||
import constants.id.MapId;
|
import constants.id.MapId;
|
||||||
import constants.id.MobId;
|
import constants.id.MobId;
|
||||||
|
import constants.skills.Bishop;
|
||||||
import net.server.services.task.channel.OverallService;
|
import net.server.services.task.channel.OverallService;
|
||||||
import net.server.services.type.ChannelServices;
|
import net.server.services.type.ChannelServices;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -34,7 +35,6 @@ import server.maps.MapObject;
|
|||||||
import server.maps.MapObjectType;
|
import server.maps.MapObjectType;
|
||||||
import server.maps.MapleMap;
|
import server.maps.MapleMap;
|
||||||
import server.maps.Mist;
|
import server.maps.Mist;
|
||||||
import tools.ArrayMap;
|
|
||||||
import tools.Randomizer;
|
import tools.Randomizer;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
@@ -47,68 +47,130 @@ import java.util.*;
|
|||||||
public class MobSkill {
|
public class MobSkill {
|
||||||
private static final Logger log = LoggerFactory.getLogger(MobSkill.class);
|
private static final Logger log = LoggerFactory.getLogger(MobSkill.class);
|
||||||
|
|
||||||
private final int skillId;
|
private final MobSkillId id;
|
||||||
private final int skillLevel;
|
private final int mpCon;
|
||||||
private int mpCon;
|
private final int spawnEffect;
|
||||||
private final List<Integer> toSummon = new ArrayList<>();
|
private final int hp;
|
||||||
private int spawnEffect, hp, x, y, count;
|
private final int x;
|
||||||
private long duration, cooltime;
|
private final int y;
|
||||||
private float prop;
|
private final int count;
|
||||||
private Point lt, rb;
|
private final long duration;
|
||||||
private int limit;
|
private final long cooltime;
|
||||||
|
private final float prop;
|
||||||
|
private final Point lt;
|
||||||
|
private final Point rb;
|
||||||
|
private final int limit;
|
||||||
|
private final List<Integer> toSummon;
|
||||||
|
|
||||||
public MobSkill(int skillId, int level) {
|
private MobSkill(MobSkillType type, int level, int mpCon, int spawnEffect, int hp, int x, int y, int count,
|
||||||
this.skillId = skillId;
|
long duration, long cooltime, float prop, Point lt, Point rb, int limit, List<Integer> toSummon) {
|
||||||
this.skillLevel = level;
|
this.id = new MobSkillId(type, level);
|
||||||
}
|
|
||||||
|
|
||||||
public void setMpCon(int mpCon) {
|
|
||||||
this.mpCon = mpCon;
|
this.mpCon = mpCon;
|
||||||
}
|
|
||||||
|
|
||||||
public void addSummons(List<Integer> toSummon) {
|
|
||||||
this.toSummon.addAll(toSummon);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSpawnEffect(int spawnEffect) {
|
|
||||||
this.spawnEffect = spawnEffect;
|
this.spawnEffect = spawnEffect;
|
||||||
}
|
|
||||||
|
|
||||||
public void setHp(int hp) {
|
|
||||||
this.hp = hp;
|
this.hp = hp;
|
||||||
}
|
|
||||||
|
|
||||||
public void setX(int x) {
|
|
||||||
this.x = x;
|
this.x = x;
|
||||||
}
|
|
||||||
|
|
||||||
public void setY(int y) {
|
|
||||||
this.y = y;
|
this.y = y;
|
||||||
}
|
|
||||||
|
|
||||||
public void setCount(int count) {
|
|
||||||
this.count = count;
|
this.count = count;
|
||||||
}
|
|
||||||
|
|
||||||
public void setDuration(long duration) {
|
|
||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
}
|
|
||||||
|
|
||||||
public void setCoolTime(long cooltime) {
|
|
||||||
this.cooltime = cooltime;
|
this.cooltime = cooltime;
|
||||||
}
|
|
||||||
|
|
||||||
public void setProp(float prop) {
|
|
||||||
this.prop = prop;
|
this.prop = prop;
|
||||||
}
|
|
||||||
|
|
||||||
public void setLtRb(Point lt, Point rb) {
|
|
||||||
this.lt = lt;
|
this.lt = lt;
|
||||||
this.rb = rb;
|
this.rb = rb;
|
||||||
|
this.limit = limit;
|
||||||
|
this.toSummon = toSummon;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLimit(int limit) {
|
static class Builder {
|
||||||
|
private final MobSkillType type;
|
||||||
|
private final int level;
|
||||||
|
private int mpCon;
|
||||||
|
private int spawnEffect;
|
||||||
|
private int hp;
|
||||||
|
private int x;
|
||||||
|
private int y;
|
||||||
|
private int count;
|
||||||
|
private long duration;
|
||||||
|
private long cooltime;
|
||||||
|
private float prop;
|
||||||
|
private Point lt;
|
||||||
|
private Point rb;
|
||||||
|
private int limit;
|
||||||
|
private List<Integer> toSummon;
|
||||||
|
|
||||||
|
public Builder(MobSkillType type, int level) {
|
||||||
|
this.type = type;
|
||||||
|
this.level = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder mpCon(int mpCon) {
|
||||||
|
this.mpCon = mpCon;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder spawnEffect(int spawnEffect) {
|
||||||
|
this.spawnEffect = spawnEffect;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder hp(int hp) {
|
||||||
|
this.hp = hp;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder x(int x) {
|
||||||
|
this.x = x;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder y(int y) {
|
||||||
|
this.y = y;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder count(int count) {
|
||||||
|
this.count = count;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder duration(long duration) {
|
||||||
|
this.duration = duration;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder cooltime(long cooltime) {
|
||||||
|
this.cooltime = cooltime;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder prop(float prop) {
|
||||||
|
this.prop = prop;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder lt(Point lt) {
|
||||||
|
this.lt = lt;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder rb(Point rb) {
|
||||||
|
this.rb = rb;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder limit(int limit) {
|
||||||
this.limit = limit;
|
this.limit = limit;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder toSummon(List<Integer> toSummon) {
|
||||||
|
this.toSummon = Collections.unmodifiableList(toSummon);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MobSkill build() {
|
||||||
|
return new MobSkill(type, level, mpCon, spawnEffect, hp, x, y, count, duration, cooltime, prop, lt, rb,
|
||||||
|
limit, toSummon);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applyDelayedEffect(final Character player, final Monster monster, final boolean skill, int animationTime) {
|
public void applyDelayedEffect(final Character player, final Monster monster, final boolean skill, int animationTime) {
|
||||||
@@ -126,32 +188,72 @@ public class MobSkill {
|
|||||||
applyEffect(null, monster, false, Collections.emptyList());
|
applyEffect(null, monster, false, Collections.emptyList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applyEffect(Character player, Monster monster, boolean skill, List<Character> banishPlayers) {
|
// TODO: avoid output argument banishPlayersOutput
|
||||||
|
public void applyEffect(Character player, Monster monster, boolean skill, List<Character> banishPlayersOutput) {
|
||||||
Disease disease = null;
|
Disease disease = null;
|
||||||
Map<MonsterStatus, Integer> stats = new ArrayMap<>();
|
Map<MonsterStatus, Integer> stats = new EnumMap<>(MonsterStatus.class);
|
||||||
List<Integer> reflection = new LinkedList<>();
|
List<Integer> reflection = new ArrayList<>();
|
||||||
switch (skillId) {
|
switch (id.type()) {
|
||||||
case 100:
|
case ATTACK_UP, ATTACK_UP_M, PAD -> stats.put(MonsterStatus.WEAPON_ATTACK_UP, x);
|
||||||
case 110:
|
case MAGIC_ATTACK_UP, MAGIC_ATTACK_UP_M, MAD -> stats.put(MonsterStatus.MAGIC_ATTACK_UP, x);
|
||||||
case 150:
|
case DEFENSE_UP, DEFENSE_UP_M, PDR -> stats.put(MonsterStatus.WEAPON_DEFENSE_UP, x);
|
||||||
stats.put(MonsterStatus.WEAPON_ATTACK_UP, x);
|
case MAGIC_DEFENSE_UP, MAGIC_DEFENSE_UP_M, MDR -> stats.put(MonsterStatus.MAGIC_DEFENSE_UP, x);
|
||||||
break;
|
case HEAL_M -> applyHealEffect(skill, monster);
|
||||||
case 101:
|
case SEAL -> disease = Disease.SEAL;
|
||||||
case 111:
|
case DARKNESS -> disease = Disease.DARKNESS;
|
||||||
case 151:
|
case WEAKNESS -> disease = Disease.WEAKEN;
|
||||||
stats.put(MonsterStatus.MAGIC_ATTACK_UP, x);
|
case STUN -> disease = Disease.STUN;
|
||||||
break;
|
case CURSE -> disease = Disease.CURSE;
|
||||||
case 102:
|
case POISON -> disease = Disease.POISON;
|
||||||
case 112:
|
case SLOW -> disease = Disease.SLOW;
|
||||||
case 152:
|
case DISPEL -> applyDispelEffect(skill, monster, player);
|
||||||
stats.put(MonsterStatus.WEAPON_DEFENSE_UP, x);
|
case SEDUCE -> disease = Disease.SEDUCE;
|
||||||
break;
|
case BANISH -> applyBanishEffect(skill, monster, player, banishPlayersOutput);
|
||||||
case 103:
|
case AREA_POISON -> spawnMonsterMist(monster);
|
||||||
case 113:
|
case REVERSE_INPUT -> disease = Disease.CONFUSE;
|
||||||
case 153:
|
case UNDEAD -> disease = Disease.ZOMBIFY;
|
||||||
stats.put(MonsterStatus.MAGIC_DEFENSE_UP, x);
|
case PHYSICAL_IMMUNE -> {
|
||||||
break;
|
if (makeChanceResult() && !monster.isBuffed(MonsterStatus.MAGIC_IMMUNITY)) {
|
||||||
case 114:
|
stats.put(MonsterStatus.WEAPON_IMMUNITY, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case MAGIC_IMMUNE -> {
|
||||||
|
if (makeChanceResult() && !monster.isBuffed(MonsterStatus.WEAPON_IMMUNITY)) {
|
||||||
|
stats.put(MonsterStatus.MAGIC_IMMUNITY, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case PHYSICAL_COUNTER -> {
|
||||||
|
stats.put(MonsterStatus.WEAPON_REFLECT, 10);
|
||||||
|
stats.put(MonsterStatus.WEAPON_IMMUNITY, 10);
|
||||||
|
reflection.add(x);
|
||||||
|
}
|
||||||
|
case MAGIC_COUNTER -> {
|
||||||
|
stats.put(MonsterStatus.MAGIC_REFLECT, 10);
|
||||||
|
stats.put(MonsterStatus.MAGIC_IMMUNITY, 10);
|
||||||
|
reflection.add(x);
|
||||||
|
}
|
||||||
|
case PHYSICAL_AND_MAGIC_COUNTER -> {
|
||||||
|
stats.put(MonsterStatus.WEAPON_REFLECT, 10);
|
||||||
|
stats.put(MonsterStatus.WEAPON_IMMUNITY, 10);
|
||||||
|
stats.put(MonsterStatus.MAGIC_REFLECT, 10);
|
||||||
|
stats.put(MonsterStatus.MAGIC_IMMUNITY, 10);
|
||||||
|
reflection.add(x);
|
||||||
|
}
|
||||||
|
case ACC -> stats.put(MonsterStatus.ACC, x);
|
||||||
|
case EVA -> stats.put(MonsterStatus.AVOID, x);
|
||||||
|
case SPEED -> stats.put(MonsterStatus.SPEED, x);
|
||||||
|
case SEAL_SKILL -> stats.put(MonsterStatus.SEAL_SKILL, x);
|
||||||
|
case SUMMON -> summonMonsters(monster);
|
||||||
|
}
|
||||||
|
if (stats.size() > 0) {
|
||||||
|
applyMonsterBuffs(stats, skill, monster, reflection);
|
||||||
|
}
|
||||||
|
if (disease != null) {
|
||||||
|
applyDisease(disease, skill, monster, player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyHealEffect(boolean skill, Monster monster) {
|
||||||
if (lt != null && rb != null && skill) {
|
if (lt != null && rb != null && skill) {
|
||||||
List<MapObject> objects = getObjectsInRange(monster, MapObjectType.MONSTER);
|
List<MapObject> objects = getObjectsInRange(monster, MapObjectType.MONSTER);
|
||||||
final int hps = (getX() / 1000) * (int) (950 + 1050 * Math.random());
|
final int hps = (getX() / 1000) * (int) (950 + 1050 * Math.random());
|
||||||
@@ -161,96 +263,33 @@ public class MobSkill {
|
|||||||
} else {
|
} else {
|
||||||
monster.heal(getX(), getY());
|
monster.heal(getX(), getY());
|
||||||
}
|
}
|
||||||
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 (lt != null && rb != null && skill) {
|
|
||||||
for (Character character : getPlayersInRange(monster)) {
|
|
||||||
character.dispel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void applyDispelEffect(boolean skill, Monster monster, Character player) {
|
||||||
|
if (lt != null && rb != null && skill) {
|
||||||
|
getPlayersInRange(monster).forEach(Character::dispel);
|
||||||
} else {
|
} else {
|
||||||
player.dispel();
|
player.dispel();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case 128: // Seduce
|
|
||||||
disease = Disease.SEDUCE;
|
private void applyBanishEffect(boolean skill, Monster monster, Character player,
|
||||||
break;
|
List<Character> banishPlayersOutput) {
|
||||||
case 129: // Banish
|
|
||||||
if (lt != null && rb != null && skill) {
|
if (lt != null && rb != null && skill) {
|
||||||
banishPlayers.addAll(getPlayersInRange(monster));
|
banishPlayersOutput.addAll(getPlayersInRange(monster));
|
||||||
} else {
|
} else {
|
||||||
banishPlayers.add(player);
|
banishPlayersOutput.add(player);
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case 131: // Mist
|
|
||||||
monster.getMap().spawnMist(new Mist(calculateBoundingBox(monster.getPosition()), monster, this), x * 100, false, false, false);
|
|
||||||
break;
|
|
||||||
case 132:
|
|
||||||
disease = Disease.CONFUSE;
|
|
||||||
break;
|
|
||||||
case 133: // zombify
|
|
||||||
disease = Disease.ZOMBIFY;
|
|
||||||
break;
|
|
||||||
case 140:
|
|
||||||
if (makeChanceResult() && !monster.isBuffed(MonsterStatus.MAGIC_IMMUNITY)) {
|
|
||||||
stats.put(MonsterStatus.WEAPON_IMMUNITY, x);
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case 141:
|
private void spawnMonsterMist(Monster monster) {
|
||||||
if (makeChanceResult() && !monster.isBuffed(MonsterStatus.WEAPON_IMMUNITY)) {
|
Rectangle mistArea = calculateBoundingBox(monster.getPosition());
|
||||||
stats.put(MonsterStatus.MAGIC_IMMUNITY, x);
|
var mist = new Mist(mistArea, monster, this);
|
||||||
|
int mistDuration = x * 100;
|
||||||
|
monster.getMap().spawnMist(mist, mistDuration, false, false, false);
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case 143: // Weapon Reflect
|
private void summonMonsters(Monster monster) {
|
||||||
stats.put(MonsterStatus.WEAPON_REFLECT, 10);
|
|
||||||
stats.put(MonsterStatus.WEAPON_IMMUNITY, 10);
|
|
||||||
reflection.add(x);
|
|
||||||
break;
|
|
||||||
case 144: // Magic Reflect
|
|
||||||
stats.put(MonsterStatus.MAGIC_REFLECT, 10);
|
|
||||||
stats.put(MonsterStatus.MAGIC_IMMUNITY, 10);
|
|
||||||
reflection.add(x);
|
|
||||||
break;
|
|
||||||
case 145: // Weapon / Magic reflect
|
|
||||||
stats.put(MonsterStatus.WEAPON_REFLECT, 10);
|
|
||||||
stats.put(MonsterStatus.WEAPON_IMMUNITY, 10);
|
|
||||||
stats.put(MonsterStatus.MAGIC_REFLECT, 10);
|
|
||||||
stats.put(MonsterStatus.MAGIC_IMMUNITY, 10);
|
|
||||||
reflection.add(x);
|
|
||||||
break;
|
|
||||||
case 154:
|
|
||||||
stats.put(MonsterStatus.ACC, x);
|
|
||||||
break;
|
|
||||||
case 155:
|
|
||||||
stats.put(MonsterStatus.AVOID, x);
|
|
||||||
break;
|
|
||||||
case 156:
|
|
||||||
stats.put(MonsterStatus.SPEED, x);
|
|
||||||
break;
|
|
||||||
case 157:
|
|
||||||
stats.put(MonsterStatus.SEAL_SKILL, x);
|
|
||||||
break;
|
|
||||||
case 200: // summon
|
|
||||||
int skillLimit = this.getLimit();
|
int skillLimit = this.getLimit();
|
||||||
MapleMap map = monster.getMap();
|
MapleMap map = monster.getMap();
|
||||||
|
|
||||||
@@ -322,25 +361,23 @@ public class MobSkill {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
default:
|
|
||||||
log.warn("Unhandled Mob skill: {}", skillId);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if (stats.size() > 0) {
|
|
||||||
|
private void applyMonsterBuffs(Map<MonsterStatus, Integer> stats, boolean skill, Monster monster, List<Integer> reflection) {
|
||||||
if (lt != null && rb != null && skill) {
|
if (lt != null && rb != null && skill) {
|
||||||
for (MapObject mons : getObjectsInRange(monster, MapObjectType.MONSTER)) {
|
for (MapObject mons : getObjectsInRange(monster, MapObjectType.MONSTER)) {
|
||||||
((Monster) mons).applyMonsterBuff(stats, getX(), getSkillId(), getDuration(), this, reflection);
|
((Monster) mons).applyMonsterBuff(stats, getX(), getDuration(), this, reflection);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
monster.applyMonsterBuff(stats, getX(), getSkillId(), getDuration(), this, reflection);
|
monster.applyMonsterBuff(stats, getX(), getDuration(), this, reflection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (disease != null) {
|
|
||||||
|
private void applyDisease(Disease disease, boolean skill, Monster monster, Character player) {
|
||||||
if (lt != null && rb != null && skill) {
|
if (lt != null && rb != null && skill) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (Character character : getPlayersInRange(monster)) {
|
for (Character character : getPlayersInRange(monster)) {
|
||||||
if (!character.hasActiveBuff(2321005)) { // holy shield
|
if (!character.hasActiveBuff(Bishop.HOLY_SHIELD)) {
|
||||||
if (disease.equals(Disease.SEDUCE)) {
|
if (disease.equals(Disease.SEDUCE)) {
|
||||||
if (i < count) {
|
if (i < count) {
|
||||||
character.giveDebuff(Disease.SEDUCE, this);
|
character.giveDebuff(Disease.SEDUCE, this);
|
||||||
@@ -355,18 +392,17 @@ public class MobSkill {
|
|||||||
player.giveDebuff(disease, this);
|
player.giveDebuff(disease, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private List<Character> getPlayersInRange(Monster monster) {
|
private List<Character> getPlayersInRange(Monster monster) {
|
||||||
return monster.getMap().getPlayersInRange(calculateBoundingBox(monster.getPosition()));
|
return monster.getMap().getPlayersInRange(calculateBoundingBox(monster.getPosition()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSkillId() {
|
public MobSkillId getId() {
|
||||||
return skillId;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSkillLevel() {
|
public MobSkillType getType() {
|
||||||
return skillLevel;
|
return id.type();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getMpCon() {
|
public int getMpCon() {
|
||||||
@@ -374,7 +410,7 @@ public class MobSkill {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<Integer> getSummons() {
|
public List<Integer> getSummons() {
|
||||||
return new ArrayList<>(toSummon);
|
return toSummon;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSpawnEffect() {
|
public int getSpawnEffect() {
|
||||||
|
|||||||
@@ -28,10 +28,8 @@ import provider.DataTool;
|
|||||||
import provider.wz.WZFiles;
|
import provider.wz.WZFiles;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.*;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReadWriteLock;
|
import java.util.concurrent.locks.ReadWriteLock;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
@@ -42,33 +40,43 @@ import static java.util.concurrent.TimeUnit.SECONDS;
|
|||||||
* @author Danny (Leifde)
|
* @author Danny (Leifde)
|
||||||
*/
|
*/
|
||||||
public class MobSkillFactory {
|
public class MobSkillFactory {
|
||||||
|
|
||||||
private static final Map<String, MobSkill> mobSkills = new HashMap<>();
|
private static final Map<String, MobSkill> mobSkills = new HashMap<>();
|
||||||
private final static DataProvider dataSource = DataProviderFactory.getDataProvider(WZFiles.SKILL);
|
private static final DataProvider dataSource = DataProviderFactory.getDataProvider(WZFiles.SKILL);
|
||||||
private static final Data skillRoot = dataSource.getData("MobSkill.img");
|
private static final Data skillRoot = dataSource.getData("MobSkill.img");
|
||||||
private final static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
|
private static final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
|
||||||
private final static Lock readLock = readWriteLock.readLock();
|
private static final Lock readLock = readWriteLock.readLock();
|
||||||
private final static Lock writeLock = readWriteLock.writeLock();
|
private static final Lock writeLock = readWriteLock.writeLock();
|
||||||
|
|
||||||
public static MobSkill getMobSkill(final int skillId, final int level) {
|
public static MobSkill getMobSkill(final MobSkillType type, final int level) {
|
||||||
final String key = skillId + "" + level;
|
|
||||||
readLock.lock();
|
readLock.lock();
|
||||||
try {
|
try {
|
||||||
MobSkill ret = mobSkills.get(key);
|
MobSkill ms = mobSkills.get(createKey(type, level));
|
||||||
if (ret != null) {
|
if (ms != null) {
|
||||||
return ret;
|
return ms;
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
readLock.unlock();
|
readLock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return loadMobSkill(type, level).orElseThrow(
|
||||||
|
() -> new IllegalArgumentException("No MobSkill exists for type %s, level %d".formatted(type, level))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Optional<MobSkill> loadMobSkill(final MobSkillType type, final int level) {
|
||||||
writeLock.lock();
|
writeLock.lock();
|
||||||
try {
|
try {
|
||||||
MobSkill ret;
|
MobSkill existingMs = mobSkills.get(createKey(type, level));
|
||||||
ret = mobSkills.get(key);
|
if (existingMs != null) {
|
||||||
if (ret == null) {
|
return Optional.of(existingMs);
|
||||||
Data skillData = skillRoot.getChildByPath(skillId + "/level/" + level);
|
}
|
||||||
if (skillData != null) {
|
|
||||||
int mpCon = DataTool.getInt(skillData.getChildByPath("mpCon"), 0);
|
Data skillData = skillRoot.getChildByPath("%d/level/%d".formatted(type.getId(), level));
|
||||||
|
if (skillData == null) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
int mpCon = DataTool.getInt("mpCon", skillData, 0);
|
||||||
List<Integer> toSummon = new ArrayList<>();
|
List<Integer> toSummon = new ArrayList<>();
|
||||||
for (int i = 0; i > -1; i++) {
|
for (int i = 0; i > -1; i++) {
|
||||||
if (skillData.getChildByPath(String.valueOf(i)) == null) {
|
if (skillData.getChildByPath(String.valueOf(i)) == null) {
|
||||||
@@ -86,32 +94,39 @@ public class MobSkillFactory {
|
|||||||
int iprop = DataTool.getInt("prop", skillData, 100);
|
int iprop = DataTool.getInt("prop", skillData, 100);
|
||||||
float prop = iprop / 100;
|
float prop = iprop / 100;
|
||||||
int limit = DataTool.getInt("limit", skillData, 0);
|
int limit = DataTool.getInt("limit", skillData, 0);
|
||||||
Data ltd = skillData.getChildByPath("lt");
|
|
||||||
|
Data ltData = skillData.getChildByPath("lt");
|
||||||
|
Data rbData = skillData.getChildByPath("rb");
|
||||||
Point lt = null;
|
Point lt = null;
|
||||||
Point rb = null;
|
Point rb = null;
|
||||||
if (ltd != null) {
|
if (ltData != null && rbData != null) {
|
||||||
lt = (Point) ltd.getData();
|
lt = (Point) ltData.getData();
|
||||||
rb = (Point) skillData.getChildByPath("rb").getData();
|
rb = (Point) rbData.getData();
|
||||||
}
|
}
|
||||||
ret = new MobSkill(skillId, level);
|
|
||||||
ret.addSummons(toSummon);
|
MobSkill loadedMobSkill = new MobSkill.Builder(type, level)
|
||||||
ret.setCoolTime(cooltime);
|
.mpCon(mpCon)
|
||||||
ret.setDuration(duration);
|
.toSummon(toSummon)
|
||||||
ret.setHp(hp);
|
.cooltime(cooltime)
|
||||||
ret.setMpCon(mpCon);
|
.duration(duration)
|
||||||
ret.setSpawnEffect(effect);
|
.hp(hp)
|
||||||
ret.setX(x);
|
.x(x)
|
||||||
ret.setY(y);
|
.y(y)
|
||||||
ret.setCount(count);
|
.count(count)
|
||||||
ret.setProp(prop);
|
.prop(prop)
|
||||||
ret.setLimit(limit);
|
.limit(limit)
|
||||||
ret.setLtRb(lt, rb);
|
.lt(lt)
|
||||||
}
|
.rb(rb)
|
||||||
mobSkills.put(skillId + "" + level, ret);
|
.build();
|
||||||
}
|
|
||||||
return ret;
|
mobSkills.put(createKey(type, level), loadedMobSkill);
|
||||||
|
return Optional.of(loadedMobSkill);
|
||||||
} finally {
|
} finally {
|
||||||
writeLock.unlock();
|
writeLock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String createKey(MobSkillType type, int skillLevel) {
|
||||||
|
return type.getId() + "" + skillLevel;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
package server.life;
|
||||||
|
|
||||||
|
public record MobSkillId(MobSkillType type, int level) {}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package server.life;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public enum MobSkillType {
|
||||||
|
ATTACK_UP(100),
|
||||||
|
MAGIC_ATTACK_UP(101),
|
||||||
|
DEFENSE_UP(102),
|
||||||
|
MAGIC_DEFENSE_UP(103),
|
||||||
|
ATTACK_UP_M(110),
|
||||||
|
MAGIC_ATTACK_UP_M(111),
|
||||||
|
DEFENSE_UP_M(112),
|
||||||
|
MAGIC_DEFENSE_UP_M(113),
|
||||||
|
HEAL_M(114),
|
||||||
|
HASTE_M(115),
|
||||||
|
SEAL(120),
|
||||||
|
DARKNESS(121),
|
||||||
|
WEAKNESS(122),
|
||||||
|
STUN(123),
|
||||||
|
CURSE(124),
|
||||||
|
POISON(125),
|
||||||
|
SLOW(126),
|
||||||
|
DISPEL(127),
|
||||||
|
SEDUCE(128),
|
||||||
|
BANISH(129),
|
||||||
|
AREA_POISON(131),
|
||||||
|
REVERSE_INPUT(132),
|
||||||
|
UNDEAD(133),
|
||||||
|
STOP_POTION(134),
|
||||||
|
STOP_MOTION(135),
|
||||||
|
FEAR(136),
|
||||||
|
PHYSICAL_IMMUNE(140),
|
||||||
|
MAGIC_IMMUNE(141),
|
||||||
|
HARD_SKIN(142),
|
||||||
|
PHYSICAL_COUNTER(143),
|
||||||
|
MAGIC_COUNTER(144),
|
||||||
|
PHYSICAL_AND_MAGIC_COUNTER(145),
|
||||||
|
PAD(150),
|
||||||
|
MAD(151),
|
||||||
|
PDR(152),
|
||||||
|
MDR(153),
|
||||||
|
ACC(154),
|
||||||
|
EVA(155),
|
||||||
|
SPEED(156),
|
||||||
|
SEAL_SKILL(157),
|
||||||
|
SUMMON(200);
|
||||||
|
|
||||||
|
private final int id;
|
||||||
|
|
||||||
|
MobSkillType(int id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MobSkillType from(int id) {
|
||||||
|
return Arrays.stream(values())
|
||||||
|
.filter(type -> type.getId() == id)
|
||||||
|
.findFirst()
|
||||||
|
.orElseThrow(IllegalArgumentException::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -82,8 +82,7 @@ public class Monster extends AbstractLoadedLife {
|
|||||||
private int VenomMultiplier = 0;
|
private int VenomMultiplier = 0;
|
||||||
private boolean fake = false;
|
private boolean fake = false;
|
||||||
private boolean dropsDisabled = false;
|
private boolean dropsDisabled = false;
|
||||||
private final List<Pair<Integer, Integer>> usedSkills = new ArrayList<>();
|
private final Set<MobSkillId> usedSkills = new HashSet<>();
|
||||||
private final Map<Pair<Integer, Integer>, Integer> skillsUsed = new HashMap<>();
|
|
||||||
private final Set<Integer> usedAttacks = new HashSet<>();
|
private final Set<Integer> usedAttacks = new HashSet<>();
|
||||||
private Set<Integer> calledMobOids = null;
|
private Set<Integer> calledMobOids = null;
|
||||||
private WeakReference<Monster> callerMob = new WeakReference<>(null);
|
private WeakReference<Monster> callerMob = new WeakReference<>(null);
|
||||||
@@ -1276,11 +1275,11 @@ public class Monster extends AbstractLoadedLife {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void dispelSkill(final MobSkill skillId) {
|
public final void dispelSkill(final MobSkill skill) {
|
||||||
List<MonsterStatus> toCancel = new ArrayList<>();
|
List<MonsterStatus> toCancel = new ArrayList<>();
|
||||||
for (Entry<MonsterStatus, MonsterStatusEffect> effects : stati.entrySet()) {
|
for (Entry<MonsterStatus, MonsterStatusEffect> effects : stati.entrySet()) {
|
||||||
MonsterStatusEffect mse = effects.getValue();
|
MonsterStatusEffect mse = effects.getValue();
|
||||||
if (mse.getMobSkill() != null && mse.getMobSkill().getSkillId() == skillId.getSkillId()) { //not checking for level.
|
if (mse.getMobSkill() != null && mse.getMobSkill().getType() == skill.getType()) { //not checking for level.
|
||||||
toCancel.add(effects.getKey());
|
toCancel.add(effects.getKey());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1289,7 +1288,7 @@ public class Monster extends AbstractLoadedLife {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applyMonsterBuff(final Map<MonsterStatus, Integer> stats, final int x, int skillId, long duration, MobSkill skill, final List<Integer> reflection) {
|
public void applyMonsterBuff(final Map<MonsterStatus, Integer> stats, final int x, long duration, MobSkill skill, final List<Integer> reflection) {
|
||||||
final Runnable cancelTask = () -> {
|
final Runnable cancelTask = () -> {
|
||||||
if (isAlive()) {
|
if (isAlive()) {
|
||||||
Packet packet = PacketCreator.cancelMonsterStatus(getObjectId(), stats);
|
Packet packet = PacketCreator.cancelMonsterStatus(getObjectId(), stats);
|
||||||
@@ -1426,7 +1425,7 @@ public class Monster extends AbstractLoadedLife {
|
|||||||
return map.getAggroCoordinator();
|
return map.getAggroCoordinator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Pair<Integer, Integer>> getSkills() {
|
public Set<MobSkillId> getSkills() {
|
||||||
return stats.getSkills();
|
return stats.getSkills();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1434,26 +1433,12 @@ public class Monster extends AbstractLoadedLife {
|
|||||||
return stats.hasSkill(skillId, level);
|
return stats.hasSkill(skillId, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSkillPos(int skillId, int level) {
|
|
||||||
int pos = 0;
|
|
||||||
for (Pair<Integer, Integer> ms : this.getSkills()) {
|
|
||||||
if (ms.getLeft() == skillId && ms.getRight() == level) {
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean canUseSkill(MobSkill toUse, boolean apply) {
|
public boolean canUseSkill(MobSkill toUse, boolean apply) {
|
||||||
if (toUse == null || isBuffed(MonsterStatus.SEAL_SKILL)) {
|
if (toUse == null || isBuffed(MonsterStatus.SEAL_SKILL)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int useSkillid = toUse.getSkillId();
|
if (isReflectSkill(toUse)) {
|
||||||
if (useSkillid >= 143 && useSkillid <= 145) {
|
|
||||||
if (this.isBuffed(MonsterStatus.WEAPON_REFLECT) || this.isBuffed(MonsterStatus.MAGIC_REFLECT)) {
|
if (this.isBuffed(MonsterStatus.WEAPON_REFLECT) || this.isBuffed(MonsterStatus.MAGIC_REFLECT)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1461,11 +1446,9 @@ public class Monster extends AbstractLoadedLife {
|
|||||||
|
|
||||||
monsterLock.lock();
|
monsterLock.lock();
|
||||||
try {
|
try {
|
||||||
for (Pair<Integer, Integer> skill : usedSkills) { // thanks OishiiKawaiiDesu for noticing an issue with mobskill cooldown
|
if (usedSkills.contains(toUse.getId())) {
|
||||||
if (skill.getLeft() == useSkillid && skill.getRight() == toUse.getSkillLevel()) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
int mpCon = toUse.getMpCon();
|
int mpCon = toUse.getMpCon();
|
||||||
if (mp < mpCon) {
|
if (mp < mpCon) {
|
||||||
@@ -1488,48 +1471,36 @@ public class Monster extends AbstractLoadedLife {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void usedSkill(MobSkill skill) {
|
private boolean isReflectSkill(MobSkill mobSkill) {
|
||||||
final int skillId = skill.getSkillId(), level = skill.getSkillLevel();
|
return switch (mobSkill.getType()) {
|
||||||
long cooltime = skill.getCoolTime();
|
case PHYSICAL_COUNTER, MAGIC_COUNTER, PHYSICAL_AND_MAGIC_COUNTER -> true;
|
||||||
|
default -> false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void usedSkill(MobSkill skill) {
|
||||||
|
final MobSkillId msId = skill.getId();
|
||||||
monsterLock.lock();
|
monsterLock.lock();
|
||||||
try {
|
try {
|
||||||
mp -= skill.getMpCon();
|
mp -= skill.getMpCon();
|
||||||
|
|
||||||
Pair<Integer, Integer> skillKey = new Pair<>(skillId, level);
|
this.usedSkills.add(msId);
|
||||||
this.usedSkills.add(skillKey);
|
|
||||||
|
|
||||||
Integer useCount = this.skillsUsed.remove(skillKey);
|
|
||||||
if (useCount != null) {
|
|
||||||
this.skillsUsed.put(skillKey, useCount + 1);
|
|
||||||
} else {
|
|
||||||
this.skillsUsed.put(skillKey, 1);
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
monsterLock.unlock();
|
monsterLock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
final Monster mons = this;
|
final Monster mons = this;
|
||||||
MapleMap mmap = mons.getMap();
|
MapleMap mmap = mons.getMap();
|
||||||
Runnable r = () -> mons.clearSkill(skillId, level);
|
Runnable r = () -> mons.clearSkill(skill.getId());
|
||||||
|
|
||||||
MobClearSkillService service = (MobClearSkillService) map.getChannelServer().getServiceAccess(ChannelServices.MOB_CLEAR_SKILL);
|
MobClearSkillService service = (MobClearSkillService) map.getChannelServer().getServiceAccess(ChannelServices.MOB_CLEAR_SKILL);
|
||||||
service.registerMobClearSkillAction(mmap.getId(), r, cooltime);
|
service.registerMobClearSkillAction(mmap.getId(), r, skill.getCoolTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearSkill(int skillId, int level) {
|
private void clearSkill(MobSkillId msId) {
|
||||||
monsterLock.lock();
|
monsterLock.lock();
|
||||||
try {
|
try {
|
||||||
int index = -1;
|
usedSkills.remove(msId);
|
||||||
for (Pair<Integer, Integer> skill : usedSkills) {
|
|
||||||
if (skill.getLeft() == skillId && skill.getRight() == level) {
|
|
||||||
index = usedSkills.indexOf(skill);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (index != -1) {
|
|
||||||
usedSkills.remove(index);
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
monsterLock.unlock();
|
monsterLock.unlock();
|
||||||
}
|
}
|
||||||
@@ -1593,8 +1564,20 @@ public class Monster extends AbstractLoadedLife {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getNoSkills() {
|
public boolean hasAnySkill() {
|
||||||
return this.stats.getNoSkills();
|
return this.stats.getNoSkills() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MobSkillId getRandomSkill() {
|
||||||
|
Set<MobSkillId> skills = stats.getSkills();
|
||||||
|
if (skills.size() == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// There is no simple way of getting a random element from a Set. Have to make do with this.
|
||||||
|
return skills.stream()
|
||||||
|
.skip(Randomizer.nextInt(skills.size()))
|
||||||
|
.findAny()
|
||||||
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFirstAttack() {
|
public boolean isFirstAttack() {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public class MonsterStats {
|
|||||||
public Map<Element, ElementalEffectiveness> resistance = new HashMap<>();
|
public Map<Element, ElementalEffectiveness> resistance = new HashMap<>();
|
||||||
public List<Integer> revives = Collections.emptyList();
|
public List<Integer> revives = Collections.emptyList();
|
||||||
public byte tagColor, tagBgColor;
|
public byte tagColor, tagBgColor;
|
||||||
public List<Pair<Integer, Integer>> skills = new ArrayList<>();
|
public Set<MobSkillId> skills = new HashSet<>();
|
||||||
public Pair<Integer, Integer> cool = null;
|
public Pair<Integer, Integer> cool = null;
|
||||||
public BanishInfo banish = null;
|
public BanishInfo banish = null;
|
||||||
public List<loseItem> loseItem = null;
|
public List<loseItem> loseItem = null;
|
||||||
@@ -190,18 +190,12 @@ public class MonsterStats {
|
|||||||
this.tagBgColor = (byte) tagBgColor;
|
this.tagBgColor = (byte) tagBgColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSkills(List<Pair<Integer, Integer>> skills) {
|
public void setSkills(Set<MobSkillId> skills) {
|
||||||
for (int i = this.skills.size(); i < skills.size(); i++) {
|
this.skills = skills;
|
||||||
this.skills.add(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < skills.size(); i++) {
|
public Set<MobSkillId> getSkills() {
|
||||||
this.skills.set(i, skills.get(i));
|
return Collections.unmodifiableSet(this.skills);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Pair<Integer, Integer>> getSkills() {
|
|
||||||
return Collections.unmodifiableList(this.skills);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getNoSkills() {
|
public int getNoSkills() {
|
||||||
@@ -209,8 +203,8 @@ public class MonsterStats {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasSkill(int skillId, int level) {
|
public boolean hasSkill(int skillId, int level) {
|
||||||
for (Pair<Integer, Integer> skill : skills) {
|
for (MobSkillId skill : skills) {
|
||||||
if (skill.getLeft() == skillId && skill.getRight() == level) {
|
if (skill.type().getId() == skillId && skill.level() == level) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,14 +138,14 @@ public class Mist extends AbstractMapObject {
|
|||||||
if (owner != null) {
|
if (owner != null) {
|
||||||
return PacketCreator.spawnMist(getObjectId(), owner.getId(), getSourceSkill().getId(), owner.getSkillLevel(SkillFactory.getSkill(source.getSourceId())), this);
|
return PacketCreator.spawnMist(getObjectId(), owner.getId(), getSourceSkill().getId(), owner.getSkillLevel(SkillFactory.getSkill(source.getSourceId())), this);
|
||||||
}
|
}
|
||||||
return PacketCreator.spawnMist(getObjectId(), mob.getId(), skill.getSkillId(), skill.getSkillLevel(), this);
|
return PacketCreator.spawnMobMist(getObjectId(), mob.getId(), skill.getId(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final Packet makeFakeSpawnData(int level) {
|
public final Packet makeFakeSpawnData(int level) {
|
||||||
if (owner != null) {
|
if (owner != null) {
|
||||||
return PacketCreator.spawnMist(getObjectId(), owner.getId(), getSourceSkill().getId(), level, this);
|
return PacketCreator.spawnMist(getObjectId(), owner.getId(), getSourceSkill().getId(), level, this);
|
||||||
}
|
}
|
||||||
return PacketCreator.spawnMist(getObjectId(), mob.getId(), skill.getSkillId(), skill.getSkillLevel(), this);
|
return PacketCreator.spawnMobMist(getObjectId(), mob.getId(), skill.getId(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import provider.DataTool;
|
|||||||
import provider.wz.WZFiles;
|
import provider.wz.WZFiles;
|
||||||
import server.life.MobSkill;
|
import server.life.MobSkill;
|
||||||
import server.life.MobSkillFactory;
|
import server.life.MobSkillFactory;
|
||||||
|
import server.life.MobSkillType;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -42,7 +43,15 @@ public class CarnivalFactory {
|
|||||||
}
|
}
|
||||||
for (Data z : dataRoot.getData("MCSkill.img")) {
|
for (Data z : dataRoot.getData("MCSkill.img")) {
|
||||||
Integer id = Integer.parseInt(z.getName());
|
Integer id = Integer.parseInt(z.getName());
|
||||||
MCSkill ms = new MCSkill(DataTool.getInt("spendCP", z, 0), DataTool.getInt("mobSkillID", z, 0), DataTool.getInt("level", z, 0), DataTool.getInt("target", z, 1) > 1);
|
int spendCp = DataTool.getInt("spendCP", z, 0);
|
||||||
|
int mobSkillId = DataTool.getInt("mobSkillID", z, 0);
|
||||||
|
MobSkillType mobSkillType = null;
|
||||||
|
if (mobSkillId != 0) {
|
||||||
|
mobSkillType = MobSkillType.from(mobSkillId);
|
||||||
|
}
|
||||||
|
int level = DataTool.getInt("level", z, 0);
|
||||||
|
boolean isMultiTarget = DataTool.getInt("target", z, 1) > 1;
|
||||||
|
MCSkill ms = new MCSkill(spendCp, mobSkillType, level, isMultiTarget);
|
||||||
|
|
||||||
skills.put(id, ms);
|
skills.put(id, ms);
|
||||||
if (ms.targetsAll) {
|
if (ms.targetsAll) {
|
||||||
@@ -52,7 +61,11 @@ public class CarnivalFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (Data z : dataRoot.getData("MCGuardian.img")) {
|
for (Data z : dataRoot.getData("MCGuardian.img")) {
|
||||||
guardians.put(Integer.parseInt(z.getName()), new MCSkill(DataTool.getInt("spendCP", z, 0), DataTool.getInt("mobSkillID", z, 0), DataTool.getInt("level", z, 0), true));
|
int spendCp = DataTool.getInt("spendCP", z, 0);
|
||||||
|
int mobSkillId = DataTool.getInt("mobSkillID", z, 0);
|
||||||
|
MobSkillType mobSkillType = MobSkillType.from(mobSkillId);
|
||||||
|
int level = DataTool.getInt("level", z, 0);
|
||||||
|
guardians.put(Integer.parseInt(z.getName()), new MCSkill(spendCp, mobSkillType, level, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,7 +79,7 @@ public class CarnivalFactory {
|
|||||||
|
|
||||||
public MCSkill getSkill(final int id) {
|
public MCSkill getSkill(final int id) {
|
||||||
MCSkill skill = skills.get(id);
|
MCSkill skill = skills.get(id);
|
||||||
if (skill != null && skill.skillid <= 0) {
|
if (skill != null && skill.mobSkillType == null) {
|
||||||
return randomizeSkill(skill.targetsAll);
|
return randomizeSkill(skill.targetsAll);
|
||||||
} else {
|
} else {
|
||||||
return skill;
|
return skill;
|
||||||
@@ -77,28 +90,13 @@ public class CarnivalFactory {
|
|||||||
return guardians.get(id);
|
return guardians.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class MCSkill {
|
public record MCSkill(int cpLoss, MobSkillType mobSkillType, int level, boolean targetsAll) {
|
||||||
|
|
||||||
public int cpLoss, skillid, level;
|
|
||||||
public boolean targetsAll;
|
|
||||||
|
|
||||||
public MCSkill(int _cpLoss, int _skillid, int _level, boolean _targetsAll) {
|
|
||||||
cpLoss = _cpLoss;
|
|
||||||
skillid = _skillid;
|
|
||||||
level = _level;
|
|
||||||
targetsAll = _targetsAll;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MobSkill getSkill() {
|
public MobSkill getSkill() {
|
||||||
return getMobSkill(skillid, level);
|
return MobSkillFactory.getMobSkill(mobSkillType, level);
|
||||||
}
|
|
||||||
|
|
||||||
public static MobSkill getMobSkill(int skillid, int level) {
|
|
||||||
return MobSkillFactory.getMobSkill(skillid, level);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Disease getDisease() {
|
public Disease getDisease() {
|
||||||
return Disease.getBySkill(skillid);
|
return Disease.getBySkill(mobSkillType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,144 +0,0 @@
|
|||||||
/*
|
|
||||||
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 tools;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public class ArrayMap<K, V> extends AbstractMap<K, V> {
|
|
||||||
|
|
||||||
static class Entry<K, V> implements Map.Entry<K, V> {
|
|
||||||
protected K key;
|
|
||||||
protected V value;
|
|
||||||
|
|
||||||
public Entry(K key, V value) {
|
|
||||||
this.key = key;
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public K getKey() {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public V getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public V setValue(V newValue) {
|
|
||||||
V oldValue = value;
|
|
||||||
value = newValue;
|
|
||||||
return oldValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (o instanceof Map.Entry<?, ?> e) {
|
|
||||||
return (key == null ? e.getKey() == null : key.equals(e.getKey())) && (value == null ? e.getValue() == null : value.equals(e.getValue()));
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
int keyHash = (key == null ? 0 : key.hashCode());
|
|
||||||
int valueHash = (value == null ? 0 : value.hashCode());
|
|
||||||
return keyHash ^ valueHash;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return key + "=" + value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<? extends java.util.Map.Entry<K, V>> entries = null;
|
|
||||||
private final ArrayList<Entry<K, V>> list;
|
|
||||||
|
|
||||||
public ArrayMap() {
|
|
||||||
list = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArrayMap(Map<K, V> map) {
|
|
||||||
list = new ArrayList<>();
|
|
||||||
putAll(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArrayMap(int initialCapacity) {
|
|
||||||
list = new ArrayList<>(initialCapacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public Set<java.util.Map.Entry<K, V>> entrySet() {
|
|
||||||
if (entries == null) {
|
|
||||||
entries = new AbstractSet<Entry<K, V>>() {
|
|
||||||
@Override
|
|
||||||
public void clear() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterator<Entry<K, V>> iterator() {
|
|
||||||
return list.iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int size() {
|
|
||||||
return list.size();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return (Set<java.util.Map.Entry<K, V>>) entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public V put(K key, V value) {
|
|
||||||
int size = list.size();
|
|
||||||
Entry<K, V> entry = null;
|
|
||||||
int i;
|
|
||||||
if (key == null) {
|
|
||||||
for (i = 0; i < size; i++) {
|
|
||||||
entry = (list.get(i));
|
|
||||||
if (entry.getKey() == null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (i = 0; i < size; i++) {
|
|
||||||
entry = (list.get(i));
|
|
||||||
if (key.equals(entry.getKey())) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
V oldValue = null;
|
|
||||||
if (i < size) {
|
|
||||||
oldValue = entry.getValue();
|
|
||||||
entry.setValue(value);
|
|
||||||
} else {
|
|
||||||
list.add(new Entry<>(key, value));
|
|
||||||
}
|
|
||||||
return oldValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -64,10 +64,7 @@ import server.CashShop.CashItemFactory;
|
|||||||
import server.CashShop.SpecialCashItem;
|
import server.CashShop.SpecialCashItem;
|
||||||
import server.*;
|
import server.*;
|
||||||
import server.events.gm.Snowball;
|
import server.events.gm.Snowball;
|
||||||
import server.life.MobSkill;
|
import server.life.*;
|
||||||
import server.life.Monster;
|
|
||||||
import server.life.NPC;
|
|
||||||
import server.life.PlayerNPC;
|
|
||||||
import server.maps.*;
|
import server.maps.*;
|
||||||
import server.maps.MiniGame.MiniGameResult;
|
import server.maps.MiniGame.MiniGameResult;
|
||||||
import server.movement.LifeMovementFragment;
|
import server.movement.LifeMovementFragment;
|
||||||
@@ -106,6 +103,11 @@ public class PacketCreator {
|
|||||||
return utcTimestamp * 10000 + FT_UT_OFFSET;
|
return utcTimestamp * 10000 + FT_UT_OFFSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void writeMobSkillId(OutPacket packet, MobSkillId msId) {
|
||||||
|
packet.writeShort(msId.type().getId());
|
||||||
|
packet.writeShort(msId.level());
|
||||||
|
}
|
||||||
|
|
||||||
public static Packet showHpHealed(int cid, int amount) {
|
public static Packet showHpHealed(int cid, int amount) {
|
||||||
OutPacket p = OutPacket.create(SendOpcode.SHOW_FOREIGN_EFFECT);
|
OutPacket p = OutPacket.create(SendOpcode.SHOW_FOREIGN_EFFECT);
|
||||||
p.writeInt(cid);
|
p.writeInt(cid);
|
||||||
@@ -1410,8 +1412,7 @@ public class PacketCreator {
|
|||||||
|
|
||||||
MobSkill mobSkill = mse.getMobSkill();
|
MobSkill mobSkill = mse.getMobSkill();
|
||||||
if (mobSkill != null) {
|
if (mobSkill != null) {
|
||||||
p.writeShort(mobSkill.getSkillId());
|
writeMobSkillId(p, mobSkill.getId());
|
||||||
p.writeShort(mobSkill.getSkillLevel());
|
|
||||||
|
|
||||||
switch (s.getKey()) {
|
switch (s.getKey()) {
|
||||||
case WEAPON_REFLECT -> pCounter = mobSkill.getX();
|
case WEAPON_REFLECT -> pCounter = mobSkill.getX();
|
||||||
@@ -2917,8 +2918,7 @@ public class PacketCreator {
|
|||||||
writeLongMaskD(p, statups);
|
writeLongMaskD(p, statups);
|
||||||
for (Pair<Disease, Integer> statup : statups) {
|
for (Pair<Disease, Integer> statup : statups) {
|
||||||
p.writeShort(statup.getRight().shortValue());
|
p.writeShort(statup.getRight().shortValue());
|
||||||
p.writeShort(skill.getSkillId());
|
writeMobSkillId(p, skill.getId());
|
||||||
p.writeShort(skill.getSkillLevel());
|
|
||||||
p.writeInt((int) skill.getDuration());
|
p.writeInt((int) skill.getDuration());
|
||||||
}
|
}
|
||||||
p.writeShort(0); // ??? wk charges have 600 here o.o
|
p.writeShort(0); // ??? wk charges have 600 here o.o
|
||||||
@@ -2936,8 +2936,7 @@ public class PacketCreator {
|
|||||||
if (statup.getLeft() == Disease.POISON) {
|
if (statup.getLeft() == Disease.POISON) {
|
||||||
p.writeShort(statup.getRight().shortValue());
|
p.writeShort(statup.getRight().shortValue());
|
||||||
}
|
}
|
||||||
p.writeShort(skill.getSkillId());
|
writeMobSkillId(p, skill.getId());
|
||||||
p.writeShort(skill.getSkillLevel());
|
|
||||||
}
|
}
|
||||||
p.writeShort(0); // same as give_buff
|
p.writeShort(0); // same as give_buff
|
||||||
p.writeShort(900);//Delay
|
p.writeShort(900);//Delay
|
||||||
@@ -3051,8 +3050,7 @@ public class PacketCreator {
|
|||||||
if (statup.getLeft() == Disease.POISON) {
|
if (statup.getLeft() == Disease.POISON) {
|
||||||
p.writeShort(statup.getRight().shortValue());
|
p.writeShort(statup.getRight().shortValue());
|
||||||
}
|
}
|
||||||
p.writeShort(skill.getSkillId());
|
writeMobSkillId(p, skill.getId());
|
||||||
p.writeShort(skill.getSkillLevel());
|
|
||||||
}
|
}
|
||||||
p.writeShort(0); // same as give_buff
|
p.writeShort(0); // same as give_buff
|
||||||
p.writeShort(900);//Delay
|
p.writeShort(900);//Delay
|
||||||
@@ -3932,8 +3930,7 @@ public class PacketCreator {
|
|||||||
for (Map.Entry<MonsterStatus, Integer> stat : stati.entrySet()) {
|
for (Map.Entry<MonsterStatus, Integer> stat : stati.entrySet()) {
|
||||||
p.writeShort(stat.getValue());
|
p.writeShort(stat.getValue());
|
||||||
if (mse.isMonsterSkill()) {
|
if (mse.isMonsterSkill()) {
|
||||||
p.writeShort(mse.getMobSkill().getSkillId());
|
writeMobSkillId(p, mse.getMobSkill().getId());
|
||||||
p.writeShort(mse.getMobSkill().getSkillLevel());
|
|
||||||
} else {
|
} else {
|
||||||
p.writeInt(mse.getSkill().getId());
|
p.writeInt(mse.getSkill().getId());
|
||||||
}
|
}
|
||||||
@@ -3984,11 +3981,15 @@ public class PacketCreator {
|
|||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Packet spawnMist(int objId, int ownerChrId, int skill, int level, Mist mist) {
|
public static Packet spawnMobMist(int objId, int ownerMobId, MobSkillId msId, Mist mist) {
|
||||||
|
return spawnMist(objId, ownerMobId, msId.type().getId(), msId.level(), mist);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Packet spawnMist(int objId, int ownerId, int skill, int level, Mist mist) {
|
||||||
OutPacket p = OutPacket.create(SendOpcode.SPAWN_MIST);
|
OutPacket p = OutPacket.create(SendOpcode.SPAWN_MIST);
|
||||||
p.writeInt(objId);
|
p.writeInt(objId);
|
||||||
p.writeInt(mist.isMobMist() ? 0 : mist.isPoisonMist() ? 1 : mist.isRecoveryMist() ? 4 : 2); // mob mist = 0, player poison = 1, smokescreen = 2, unknown = 3, recovery = 4
|
p.writeInt(mist.isMobMist() ? 0 : mist.isPoisonMist() ? 1 : mist.isRecoveryMist() ? 4 : 2); // mob mist = 0, player poison = 1, smokescreen = 2, unknown = 3, recovery = 4
|
||||||
p.writeInt(ownerChrId);
|
p.writeInt(ownerId);
|
||||||
p.writeInt(skill);
|
p.writeInt(skill);
|
||||||
p.writeByte(level);
|
p.writeByte(level);
|
||||||
p.writeShort(mist.getSkillDelay()); // Skill delay
|
p.writeShort(mist.getSkillDelay()); // Skill delay
|
||||||
|
|||||||
@@ -3,12 +3,10 @@ package tools.mapletools;
|
|||||||
import provider.*;
|
import provider.*;
|
||||||
import provider.wz.DataType;
|
import provider.wz.DataType;
|
||||||
import provider.wz.WZFiles;
|
import provider.wz.WZFiles;
|
||||||
import server.life.Element;
|
import server.life.*;
|
||||||
import server.life.ElementalEffectiveness;
|
|
||||||
import server.life.LifeFactory.BanishInfo;
|
import server.life.LifeFactory.BanishInfo;
|
||||||
import server.life.LifeFactory.loseItem;
|
import server.life.LifeFactory.loseItem;
|
||||||
import server.life.LifeFactory.selfDestruction;
|
import server.life.LifeFactory.selfDestruction;
|
||||||
import server.life.MonsterStats;
|
|
||||||
import tools.Pair;
|
import tools.Pair;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
@@ -110,9 +108,12 @@ public class MonsterStatFetcher {
|
|||||||
Data monsterSkillData = monsterInfoData.getChildByPath("skill");
|
Data monsterSkillData = monsterInfoData.getChildByPath("skill");
|
||||||
if (monsterSkillData != null) {
|
if (monsterSkillData != null) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
List<Pair<Integer, Integer>> skills = new ArrayList<>();
|
Set<MobSkillId> skills = new HashSet<>();
|
||||||
while (monsterSkillData.getChildByPath(Integer.toString(i)) != null) {
|
while (monsterSkillData.getChildByPath(Integer.toString(i)) != null) {
|
||||||
skills.add(new Pair<>(DataTool.getInt(i + "/skill", monsterSkillData, 0), DataTool.getInt(i + "/level", monsterSkillData, 0)));
|
int skillId = DataTool.getInt(i + "/skill", monsterSkillData, 0);
|
||||||
|
MobSkillType type = MobSkillType.from(skillId);
|
||||||
|
int skillLevel = DataTool.getInt(i + "/level", monsterSkillData, 0);
|
||||||
|
skills.add(new MobSkillId(type, skillLevel));
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
stats.setSkills(skills);
|
stats.setSkills(skills);
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package server.life;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class MobSkillFactoryTest {
|
||||||
|
|
||||||
|
@TempDir
|
||||||
|
private Path wzPath;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setWzPath() {
|
||||||
|
MockitoAnnotations.openMocks(this);
|
||||||
|
writeTestFileToTempDir();
|
||||||
|
System.setProperty("wz-path", "%s/wz".formatted(wzPath.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeTestFileToTempDir() {
|
||||||
|
try {
|
||||||
|
String testFileContents = readTestFileContents();
|
||||||
|
writeTempDirFile(testFileContents);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String readTestFileContents() throws IOException {
|
||||||
|
return new String(getClass()
|
||||||
|
.getClassLoader()
|
||||||
|
.getResourceAsStream("MobSkill-test.img.xml")
|
||||||
|
.readAllBytes()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeTempDirFile(String fileContents) throws IOException {
|
||||||
|
Path tempDirDirectory = wzPath.resolve("wz/Skill.wz");
|
||||||
|
Files.createDirectories(tempDirDirectory);
|
||||||
|
Path tempDirFile = Files.createFile(tempDirDirectory.resolve("MobSkill.img.xml"));
|
||||||
|
Files.writeString(tempDirFile, fileContents);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldLoadExistingMobSkill() {
|
||||||
|
MobSkill mobSkill = MobSkillFactory.getMobSkill(MobSkillType.ATTACK_UP, 1);
|
||||||
|
|
||||||
|
assertNotNull(mobSkill);
|
||||||
|
assertAll("MobSkill",
|
||||||
|
() -> assertEquals(115, mobSkill.getX()),
|
||||||
|
() -> assertEquals(5, mobSkill.getMpCon()),
|
||||||
|
() -> assertEquals(40_000, mobSkill.getCoolTime()),
|
||||||
|
() -> assertEquals(30_000, mobSkill.getDuration())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldThrowExceptionOnNonExisting() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> MobSkillFactory.getMobSkill(MobSkillType.DEFENSE_UP, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,522 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<imgdir name="MobSkill.img">
|
||||||
|
<imgdir name="100">
|
||||||
|
<imgdir name="level">
|
||||||
|
<imgdir name="1">
|
||||||
|
<int name="x" value="115"/>
|
||||||
|
<int name="mpCon" value="5"/>
|
||||||
|
<int name="interval" value="40"/>
|
||||||
|
<int name="time" value="30"/>
|
||||||
|
<imgdir name="effect">
|
||||||
|
<canvas name="0" width="128" height="128">
|
||||||
|
<vector name="origin" x="62" y="65"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="1" width="128" height="127">
|
||||||
|
<vector name="origin" x="61" y="64"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="2" width="131" height="130">
|
||||||
|
<vector name="origin" x="65" y="65"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="3" width="141" height="140">
|
||||||
|
<vector name="origin" x="68" y="70"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="4" width="123" height="123">
|
||||||
|
<vector name="origin" x="58" y="62"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="5" width="126" height="122">
|
||||||
|
<vector name="origin" x="63" y="63"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="6" width="111" height="112">
|
||||||
|
<vector name="origin" x="54" y="57"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="7" width="24" height="23">
|
||||||
|
<vector name="origin" x="9" y="15"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
<int name="a0" value="255"/>
|
||||||
|
<int name="a1" value="0"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="3"/>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="mob">
|
||||||
|
<canvas name="0" width="19" height="23">
|
||||||
|
<vector name="origin" x="9" y="44"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="2"/>
|
||||||
|
<int name="icon" value="1"/>
|
||||||
|
</imgdir>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="2">
|
||||||
|
<int name="x" value="130"/>
|
||||||
|
<int name="mpCon" value="5"/>
|
||||||
|
<int name="interval" value="40"/>
|
||||||
|
<int name="time" value="30"/>
|
||||||
|
<imgdir name="effect">
|
||||||
|
<canvas name="0" width="128" height="128">
|
||||||
|
<vector name="origin" x="62" y="65"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="1" width="128" height="127">
|
||||||
|
<vector name="origin" x="61" y="64"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="2" width="131" height="130">
|
||||||
|
<vector name="origin" x="65" y="65"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="3" width="141" height="140">
|
||||||
|
<vector name="origin" x="68" y="70"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="4" width="123" height="123">
|
||||||
|
<vector name="origin" x="58" y="62"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="5" width="126" height="122">
|
||||||
|
<vector name="origin" x="63" y="63"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="6" width="111" height="112">
|
||||||
|
<vector name="origin" x="54" y="57"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="7" width="24" height="23">
|
||||||
|
<vector name="origin" x="9" y="15"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
<int name="a0" value="255"/>
|
||||||
|
<int name="a1" value="0"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="3"/>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="mob">
|
||||||
|
<canvas name="0" width="19" height="23">
|
||||||
|
<vector name="origin" x="9" y="44"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="2"/>
|
||||||
|
<int name="icon" value="1"/>
|
||||||
|
</imgdir>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="3">
|
||||||
|
<int name="x" value="130"/>
|
||||||
|
<int name="hp" value="50"/>
|
||||||
|
<int name="mpCon" value="5"/>
|
||||||
|
<int name="interval" value="40"/>
|
||||||
|
<int name="time" value="30"/>
|
||||||
|
<imgdir name="effect">
|
||||||
|
<canvas name="0" width="128" height="128">
|
||||||
|
<vector name="origin" x="62" y="65"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="1" width="128" height="127">
|
||||||
|
<vector name="origin" x="61" y="64"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="2" width="131" height="130">
|
||||||
|
<vector name="origin" x="65" y="65"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="3" width="141" height="140">
|
||||||
|
<vector name="origin" x="68" y="70"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="4" width="123" height="123">
|
||||||
|
<vector name="origin" x="58" y="62"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="5" width="126" height="122">
|
||||||
|
<vector name="origin" x="63" y="63"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="6" width="111" height="112">
|
||||||
|
<vector name="origin" x="54" y="57"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="7" width="24" height="23">
|
||||||
|
<vector name="origin" x="9" y="15"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
<int name="a0" value="255"/>
|
||||||
|
<int name="a1" value="0"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="3"/>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="mob">
|
||||||
|
<canvas name="0" width="19" height="23">
|
||||||
|
<vector name="origin" x="9" y="44"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="2"/>
|
||||||
|
<int name="icon" value="1"/>
|
||||||
|
</imgdir>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="4">
|
||||||
|
<int name="x" value="100"/>
|
||||||
|
<int name="mpCon" value="5"/>
|
||||||
|
<int name="interval" value="30"/>
|
||||||
|
<int name="time" value="60"/>
|
||||||
|
<imgdir name="mob">
|
||||||
|
<canvas name="0" width="84" height="74">
|
||||||
|
<vector name="origin" x="42" y="37"/>
|
||||||
|
<int name="delay" value="120"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="1" width="84" height="75">
|
||||||
|
<vector name="origin" x="43" y="36"/>
|
||||||
|
<int name="delay" value="120"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="2" width="84" height="75">
|
||||||
|
<vector name="origin" x="43" y="36"/>
|
||||||
|
<int name="delay" value="120"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="3" width="85" height="75">
|
||||||
|
<vector name="origin" x="44" y="35"/>
|
||||||
|
<int name="delay" value="120"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="4" width="84" height="74">
|
||||||
|
<vector name="origin" x="42" y="37"/>
|
||||||
|
<int name="delay" value="120"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="5" width="85" height="73">
|
||||||
|
<vector name="origin" x="43" y="38"/>
|
||||||
|
<int name="delay" value="120"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="2"/>
|
||||||
|
<int name="repeat" value="1"/>
|
||||||
|
</imgdir>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="5">
|
||||||
|
<int name="x" value="100"/>
|
||||||
|
<int name="mpCon" value="5"/>
|
||||||
|
<int name="interval" value="30"/>
|
||||||
|
<int name="time" value="60"/>
|
||||||
|
<imgdir name="mob">
|
||||||
|
<canvas name="0" width="84" height="74">
|
||||||
|
<vector name="origin" x="42" y="37"/>
|
||||||
|
<int name="delay" value="120"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="1" width="84" height="75">
|
||||||
|
<vector name="origin" x="43" y="36"/>
|
||||||
|
<int name="delay" value="120"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="2" width="84" height="75">
|
||||||
|
<vector name="origin" x="43" y="36"/>
|
||||||
|
<int name="delay" value="120"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="3" width="85" height="75">
|
||||||
|
<vector name="origin" x="44" y="35"/>
|
||||||
|
<int name="delay" value="120"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="4" width="84" height="74">
|
||||||
|
<vector name="origin" x="42" y="37"/>
|
||||||
|
<int name="delay" value="120"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="5" width="85" height="73">
|
||||||
|
<vector name="origin" x="43" y="38"/>
|
||||||
|
<int name="delay" value="120"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="2"/>
|
||||||
|
<int name="repeat" value="1"/>
|
||||||
|
</imgdir>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="6">
|
||||||
|
<int name="x" value="100"/>
|
||||||
|
<int name="mpCon" value="1"/>
|
||||||
|
<int name="interval" value="5"/>
|
||||||
|
<int name="time" value="1"/>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="7">
|
||||||
|
<int name="x" value="1"/>
|
||||||
|
<int name="mpCon" value="1"/>
|
||||||
|
<int name="interval" value="8"/>
|
||||||
|
<int name="time" value="1"/>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="8">
|
||||||
|
<int name="x" value="100"/>
|
||||||
|
<int name="mpCon" value="1"/>
|
||||||
|
<int name="interval" value="90"/>
|
||||||
|
<int name="time" value="10"/>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="9">
|
||||||
|
<int name="x" value="110"/>
|
||||||
|
<int name="time" value="6000"/>
|
||||||
|
<imgdir name="effect">
|
||||||
|
<canvas name="0" width="10" height="10">
|
||||||
|
<vector name="origin" x="-2" y="32"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="3"/>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="mob">
|
||||||
|
<canvas name="0" width="10" height="10">
|
||||||
|
<vector name="origin" x="-2" y="32"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="2"/>
|
||||||
|
<int name="icon" value="1"/>
|
||||||
|
</imgdir>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="10">
|
||||||
|
<int name="x" value="120"/>
|
||||||
|
<int name="time" value="6000"/>
|
||||||
|
<imgdir name="effect">
|
||||||
|
<canvas name="0" width="10" height="10">
|
||||||
|
<vector name="origin" x="-2" y="32"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="3"/>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="mob">
|
||||||
|
<canvas name="0" width="10" height="10">
|
||||||
|
<vector name="origin" x="-2" y="32"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="2"/>
|
||||||
|
<int name="icon" value="1"/>
|
||||||
|
</imgdir>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="11">
|
||||||
|
<int name="x" value="130"/>
|
||||||
|
<int name="time" value="6000"/>
|
||||||
|
<imgdir name="effect">
|
||||||
|
<canvas name="0" width="10" height="10">
|
||||||
|
<vector name="origin" x="-2" y="32"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="3"/>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="mob">
|
||||||
|
<canvas name="0" width="10" height="10">
|
||||||
|
<vector name="origin" x="-2" y="32"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="2"/>
|
||||||
|
<int name="icon" value="1"/>
|
||||||
|
</imgdir>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="12">
|
||||||
|
<int name="x" value="140"/>
|
||||||
|
<int name="time" value="6000"/>
|
||||||
|
<imgdir name="effect">
|
||||||
|
<canvas name="0" width="10" height="10">
|
||||||
|
<vector name="origin" x="-2" y="32"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="3"/>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="mob">
|
||||||
|
<canvas name="0" width="10" height="10">
|
||||||
|
<vector name="origin" x="-2" y="32"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="2"/>
|
||||||
|
<int name="icon" value="1"/>
|
||||||
|
</imgdir>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="13">
|
||||||
|
<int name="x" value="150"/>
|
||||||
|
<int name="time" value="6000"/>
|
||||||
|
<imgdir name="effect">
|
||||||
|
<canvas name="0" width="10" height="10">
|
||||||
|
<vector name="origin" x="-2" y="32"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="3"/>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="mob">
|
||||||
|
<canvas name="0" width="10" height="10">
|
||||||
|
<vector name="origin" x="-2" y="32"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="2"/>
|
||||||
|
<int name="icon" value="1"/>
|
||||||
|
</imgdir>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="14">
|
||||||
|
<int name="x" value="160"/>
|
||||||
|
<int name="time" value="6000"/>
|
||||||
|
<imgdir name="effect">
|
||||||
|
<canvas name="0" width="10" height="10">
|
||||||
|
<vector name="origin" x="-2" y="32"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="3"/>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="mob">
|
||||||
|
<canvas name="0" width="10" height="10">
|
||||||
|
<vector name="origin" x="-2" y="32"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="2"/>
|
||||||
|
<int name="icon" value="1"/>
|
||||||
|
</imgdir>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="15">
|
||||||
|
<int name="x" value="170"/>
|
||||||
|
<int name="time" value="6000"/>
|
||||||
|
<imgdir name="effect">
|
||||||
|
<canvas name="0" width="10" height="10">
|
||||||
|
<vector name="origin" x="-2" y="32"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="3"/>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="mob">
|
||||||
|
<canvas name="0" width="10" height="10">
|
||||||
|
<vector name="origin" x="-2" y="32"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="2"/>
|
||||||
|
<int name="icon" value="1"/>
|
||||||
|
</imgdir>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="16">
|
||||||
|
<int name="x" value="170"/>
|
||||||
|
<int name="time" value="15"/>
|
||||||
|
<imgdir name="effect">
|
||||||
|
<canvas name="0" width="10" height="10">
|
||||||
|
<vector name="origin" x="-2" y="32"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="3"/>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="mob">
|
||||||
|
<canvas name="0" width="19" height="23">
|
||||||
|
<vector name="origin" x="9" y="44"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="2"/>
|
||||||
|
<int name="icon" value="1"/>
|
||||||
|
</imgdir>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="17">
|
||||||
|
<int name="x" value="100"/>
|
||||||
|
<int name="mpCon" value="1"/>
|
||||||
|
<int name="interval" value="15"/>
|
||||||
|
<int name="time" value="1"/>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="18">
|
||||||
|
<int name="x" value="100"/>
|
||||||
|
<int name="mpCon" value="1"/>
|
||||||
|
<int name="interval" value="5"/>
|
||||||
|
<int name="time" value="1"/>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="19">
|
||||||
|
<int name="x" value="170"/>
|
||||||
|
<int name="time" value="15"/>
|
||||||
|
<imgdir name="effect">
|
||||||
|
<canvas name="0" width="10" height="10">
|
||||||
|
<vector name="origin" x="-2" y="32"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="3"/>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="mob">
|
||||||
|
<canvas name="0" width="19" height="23">
|
||||||
|
<vector name="origin" x="9" y="44"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="2"/>
|
||||||
|
<int name="icon" value="1"/>
|
||||||
|
</imgdir>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="20">
|
||||||
|
<int name="x" value="115"/>
|
||||||
|
<int name="mpCon" value="5"/>
|
||||||
|
<int name="interval" value="40"/>
|
||||||
|
<int name="time" value="30"/>
|
||||||
|
<imgdir name="effect">
|
||||||
|
<canvas name="0" width="128" height="128">
|
||||||
|
<vector name="origin" x="62" y="65"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="1" width="128" height="127">
|
||||||
|
<vector name="origin" x="61" y="64"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="2" width="131" height="130">
|
||||||
|
<vector name="origin" x="65" y="65"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="3" width="141" height="140">
|
||||||
|
<vector name="origin" x="68" y="70"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="4" width="123" height="123">
|
||||||
|
<vector name="origin" x="58" y="62"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="5" width="126" height="122">
|
||||||
|
<vector name="origin" x="63" y="63"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="6" width="111" height="112">
|
||||||
|
<vector name="origin" x="54" y="57"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
</canvas>
|
||||||
|
<canvas name="7" width="24" height="23">
|
||||||
|
<vector name="origin" x="9" y="15"/>
|
||||||
|
<int name="delay" value="100"/>
|
||||||
|
<int name="a0" value="255"/>
|
||||||
|
<int name="a1" value="0"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="3"/>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="mob">
|
||||||
|
<canvas name="0" width="49" height="54">
|
||||||
|
<vector name="origin" x="22" y="72"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="2"/>
|
||||||
|
<int name="icon" value="1"/>
|
||||||
|
</imgdir>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="21">
|
||||||
|
<string name="info" value="넥스1/넥스2"/>
|
||||||
|
<int name="x" value="120"/>
|
||||||
|
<int name="time" value="15"/>
|
||||||
|
<int name="mpCon" value="20"/>
|
||||||
|
<int name="interval" value="30"/>
|
||||||
|
<int name="hp" value="70"/>
|
||||||
|
<imgdir name="effect">
|
||||||
|
<canvas name="0" width="10" height="10">
|
||||||
|
<vector name="origin" x="-2" y="32"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="3"/>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="mob">
|
||||||
|
<canvas name="0" width="19" height="23">
|
||||||
|
<vector name="origin" x="9" y="44"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="2"/>
|
||||||
|
<int name="icon" value="1"/>
|
||||||
|
</imgdir>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="22">
|
||||||
|
<string name="info" value="넥스3/넥스4"/>
|
||||||
|
<int name="x" value="140"/>
|
||||||
|
<int name="time" value="15"/>
|
||||||
|
<int name="mpCon" value="20"/>
|
||||||
|
<int name="interval" value="30"/>
|
||||||
|
<int name="hp" value="70"/>
|
||||||
|
<imgdir name="effect">
|
||||||
|
<canvas name="0" width="10" height="10">
|
||||||
|
<vector name="origin" x="-2" y="32"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="3"/>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="mob">
|
||||||
|
<canvas name="0" width="19" height="23">
|
||||||
|
<vector name="origin" x="9" y="44"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="2"/>
|
||||||
|
<int name="icon" value="1"/>
|
||||||
|
</imgdir>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="23">
|
||||||
|
<string name="info" value="넥스5/넥스6"/>
|
||||||
|
<int name="x" value="160"/>
|
||||||
|
<int name="time" value="15"/>
|
||||||
|
<int name="mpCon" value="20"/>
|
||||||
|
<int name="interval" value="30"/>
|
||||||
|
<int name="hp" value="70"/>
|
||||||
|
<imgdir name="effect">
|
||||||
|
<canvas name="0" width="10" height="10">
|
||||||
|
<vector name="origin" x="-2" y="32"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="3"/>
|
||||||
|
</imgdir>
|
||||||
|
<imgdir name="mob">
|
||||||
|
<canvas name="0" width="19" height="23">
|
||||||
|
<vector name="origin" x="9" y="44"/>
|
||||||
|
</canvas>
|
||||||
|
<int name="pos" value="2"/>
|
||||||
|
<int name="icon" value="1"/>
|
||||||
|
</imgdir>
|
||||||
|
</imgdir>
|
||||||
|
</imgdir>
|
||||||
|
</imgdir>
|
||||||
|
</imgdir>
|
||||||
Reference in New Issue
Block a user