1
0
mirror of https://github.com/Rogiel/l2jserver2 synced 2025-12-07 16:03:10 +00:00

Initial work in the attack service

Signed-off-by: Rogiel <rogiel@rogiel.com>
This commit is contained in:
2011-05-25 20:22:02 -03:00
parent f955208c2e
commit 96bf7df86f
10108 changed files with 41199 additions and 21558 deletions

View File

@@ -31,6 +31,7 @@ import com.l2jserver.game.net.packet.client.AdminCommandPacket;
import com.l2jserver.game.net.packet.client.AuthLoginPacket;
import com.l2jserver.game.net.packet.client.CharacterActionPacket;
import com.l2jserver.game.net.packet.client.CharacterAppearingPacket;
import com.l2jserver.game.net.packet.client.CharacterAttackRequestPacket;
import com.l2jserver.game.net.packet.client.CharacterChatMessagePacket;
import com.l2jserver.game.net.packet.client.CharacterCreatePacket;
import com.l2jserver.game.net.packet.client.CharacterRequestActionUse;
@@ -181,6 +182,8 @@ public class Lineage2PacketReader extends OneToOneDecoder {
return CharacterRequestActionUse.class;
case CharacterRequestOpenMap.OPCODE:
return CharacterRequestOpenMap.class;
case CharacterAttackRequestPacket.OPCODE:
return CharacterAttackRequestPacket.class;
default:
logger.warn("Unknown opcode: 0x{}", Integer.toHexString(opcode));
break;

View File

@@ -32,6 +32,7 @@ import com.l2jserver.model.world.L2Character;
import com.l2jserver.service.game.character.ActorIsNotAttackableServiceException;
import com.l2jserver.service.game.character.CannotSetTargetServiceException;
import com.l2jserver.service.game.character.CharacterService;
import com.l2jserver.service.game.npc.NotAttackableNPCServiceException;
import com.l2jserver.util.dimensional.Coordinate;
/**
@@ -121,7 +122,7 @@ public class CharacterAttackRequestPacket extends AbstractClientPacket {
final ObjectID<Actor> id = idResolver.resolve(objectId);
if (!(id instanceof ActorID)) {
conn.write(ActionFailedPacket.SHARED_INSTANCE);
log.warn("Player {} is trying to attack {} which is not an actor",
log.warn("Player {} is trying to attack {}, which is not an actor",
character, id);
return;
}
@@ -132,6 +133,8 @@ public class CharacterAttackRequestPacket extends AbstractClientPacket {
conn.sendActionFailed();
} catch (ActorIsNotAttackableServiceException e) {
conn.sendActionFailed();
} catch (NotAttackableNPCServiceException e) {
conn.sendActionFailed();
}
}
}

View File

@@ -0,0 +1,93 @@
/*
* This file is part of l2jserver <l2jserver.com>.
*
* l2jserver is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* l2jserver 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with l2jserver. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.game.net.packet.server;
import java.util.Collections;
import java.util.List;
import org.jboss.netty.buffer.ChannelBuffer;
import com.l2jserver.game.net.Lineage2Connection;
import com.l2jserver.game.net.packet.AbstractServerPacket;
import com.l2jserver.model.server.AttackHit;
import com.l2jserver.model.world.Actor;
import com.l2jserver.util.factory.CollectionFactory;
/**
* This packet informs the client of an attack issued
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
* @see AttackHit
*/
public class ActorAttackPacket extends AbstractServerPacket {
/**
* The packet OPCODE
*/
public static final int OPCODE = 0x33;
/**
* The attacker actor
*/
private final Actor attacker;
/**
* List of hits to be sent
*/
private final List<AttackHit> hits = CollectionFactory.newList();
public ActorAttackPacket(Actor attacker, AttackHit... hits) {
super(OPCODE);
this.attacker = attacker;
Collections.addAll(this.hits, hits);
}
@Override
public void write(Lineage2Connection conn, ChannelBuffer buffer) {
buffer.writeInt(attacker.getID().getID());
final AttackHit first = hits.get(0);
buffer.writeInt(first.getTarget().getID().getID());
buffer.writeInt((int) first.getDamage());
buffer.writeByte(first.getFlagsByte());
buffer.writeInt(attacker.getPoint().getX());
buffer.writeInt(attacker.getPoint().getY());
buffer.writeInt(attacker.getPoint().getZ());
buffer.writeShort(hits.size() - 1);
if (hits.size() > 1) {
boolean skipFirst = false;
for (final AttackHit hit : hits) {
if (!skipFirst) {
skipFirst = true;
continue;
}
buffer.writeInt(hit.getTarget().getID().getID());
buffer.writeInt((int) hit.getDamage());
buffer.writeByte(hit.getFlagsByte());
}
}
buffer.writeInt(first.getTarget().getPoint().getX());
buffer.writeInt(first.getTarget().getPoint().getY());
buffer.writeInt(first.getTarget().getPoint().getZ());
}
public ActorAttackPacket add(AttackHit hit) {
hits.add(hit);
return this;
}
}

View File

@@ -0,0 +1,92 @@
/*
* This file is part of l2jserver <l2jserver.com>.
*
* l2jserver is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* l2jserver 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with l2jserver. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.game.net.packet.server;
import java.util.Map;
import java.util.Map.Entry;
import org.jboss.netty.buffer.ChannelBuffer;
import com.l2jserver.game.net.Lineage2Connection;
import com.l2jserver.game.net.packet.AbstractServerPacket;
import com.l2jserver.game.net.packet.server.CharacterCreateFailPacket.Reason;
import com.l2jserver.model.world.Actor;
import com.l2jserver.util.factory.CollectionFactory;
/**
* This packet notifies the client that the chosen character has been
* successfully selected.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
* @see Reason
*/
public class ActorStatusUpdatePacket extends AbstractServerPacket {
/**
* The packet OPCODE
*/
public static final int OPCODE = 0x18;
/**
* The stats the can be updated with the packet
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public enum Stat {
LEVEL(0x01), EXPERIENCE(0x02), STR(0x03), DEX(0x04), CON(0x05), INT(
0x06), WIT(0x07), MEN(0x08),
HP(0x09), MAX_HP(0x0a), MP(0x0b), MAX_MP(0x0c),
SP(0x0d), LOAD(0x0e), MAX_LOAD(0x0f),
PHYSICAL_ATK(0x11), ATTACK_SPEED(0x12), PHYSICAL_DEFENSE(0x13), EVASION(
0x14), ACCURACY(0x15), CRITICAL(0x16), MAGICAL_ATTACK(0x17), CAST_SPEED(
0x18), MAGICAL_DEFENSE(0x19), PVP_FLAG(0x1a), KARMA(0x1b),
CP(0x21), MAX_CP(0x22);
public final int id;
Stat(int id) {
this.id = id;
}
}
private final Map<Stat, Integer> update = CollectionFactory.newMap();
private final Actor actor;
public ActorStatusUpdatePacket(Actor actor) {
super(OPCODE);
this.actor = actor;
}
@Override
public void write(Lineage2Connection conn, ChannelBuffer buffer) {
buffer.writeInt(actor.getID().getID());
buffer.writeInt(update.size());
for (Entry<Stat, Integer> entry : update.entrySet()) {
buffer.writeInt(entry.getKey().id);
buffer.writeInt(entry.getValue());
}
}
public ActorStatusUpdatePacket add(Stat stat, int value) {
update.put(stat, value);
return this;
}
}

View File

@@ -21,7 +21,7 @@ import org.jboss.netty.buffer.ChannelBuffer;
import com.l2jserver.game.net.Lineage2Connection;
import com.l2jserver.game.net.packet.AbstractServerPacket;
import com.l2jserver.game.net.packet.server.CharacterCreateFailPacket.Reason;
import com.l2jserver.model.world.L2Character;
import com.l2jserver.model.world.Actor;
/**
* This packet notifies the client that the chosen character has been
@@ -30,7 +30,7 @@ import com.l2jserver.model.world.L2Character;
* @author <a href="http://www.rogiel.com">Rogiel</a>
* @see Reason
*/
public class CharacterPositionPacket extends AbstractServerPacket {
public class ActorUpdatePositionPacket extends AbstractServerPacket {
/**
* The packet OPCODE
*/
@@ -39,19 +39,19 @@ public class CharacterPositionPacket extends AbstractServerPacket {
/**
* The selected character
*/
private final L2Character character;
private final Actor actor;
public CharacterPositionPacket(L2Character character) {
public ActorUpdatePositionPacket(Actor actor) {
super(OPCODE);
this.character = character;
this.actor = actor;
}
@Override
public void write(Lineage2Connection conn, ChannelBuffer buffer) {
buffer.writeInt(character.getID().getID());
buffer.writeInt(character.getPoint().getX());
buffer.writeInt(character.getPoint().getY());
buffer.writeInt(character.getPoint().getZ());
buffer.writeInt((int) character.getPoint().getAngle());
buffer.writeInt(actor.getID().getID());
buffer.writeInt(actor.getPoint().getX());
buffer.writeInt(actor.getPoint().getY());
buffer.writeInt(actor.getPoint().getZ());
buffer.writeInt((int) actor.getPoint().getAngle());
}
}

View File

@@ -0,0 +1,150 @@
/*
* This file is part of l2jserver <l2jserver.com>.
*
* l2jserver is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* l2jserver 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with l2jserver. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.model.server;
import java.util.Collections;
import java.util.List;
import com.l2jserver.model.world.Actor;
import com.l2jserver.util.factory.CollectionFactory;
/**
* This class represents an attack hit.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class AttackHit {
/**
* The actor attacking <tt>target</tt>
*/
private final Actor attacker;
/**
* The actor being attacked by <tt>attacker</tt>
*/
private final Actor target;
/**
* The damage dealt by this hit
*/
private double damage;
/**
* This hit flags (i.e. critical)
*/
private List<AttackHitFlag> flags = CollectionFactory.newList();
/**
* An enumeration containing all possible attack flags
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public enum AttackHitFlag {
/**
* The hit used soulshot
*/
SOULSHOT((byte) 0x10),
/**
* The attack dealt critical damage
*/
CRITICAL((byte) 0x20),
/**
* The shield blocked this hit
*/
SHIELD_BLOCKED((byte) 0x40),
/**
* The <tt>attacker</tt> missed the hit
*/
MISS((byte) 0x80);
/**
* The byte value of the flag
*/
public final byte flag;
AttackHitFlag(byte flag) {
this.flag = flag;
}
}
/**
* Creates a new instance
*
* @param attacker
* the actor attacking <tt>target</tt>
* @param target
* the actor being attacked by <tt>attacker</tt>
*/
public AttackHit(Actor attacker, Actor target) {
this.attacker = attacker;
this.target = target;
}
/**
* Creates a new instance
*
* @param attacker
* the actor attacking <tt>target</tt>
* @param target
* the actor being attacked by <tt>attacker</tt>
* @param flags
* the hit flags
*/
public AttackHit(Actor attacker, Actor target, AttackHitFlag... flags) {
this.attacker = attacker;
this.target = target;
Collections.addAll(this.flags, flags);
}
/**
* @return the attacker
*/
public Actor getAttacker() {
return attacker;
}
/**
* @return the target
*/
public Actor getTarget() {
return target;
}
/**
* @return the damage
*/
public double getDamage() {
return damage;
}
/**
* @return the flags
*/
public List<AttackHitFlag> getFlags() {
return flags;
}
/**
* @return the flags as a byte value
*/
public byte getFlagsByte() {
byte flags = 0;
for (final AttackHitFlag flag : this.flags) {
if (flag == AttackHitFlag.MISS)
return AttackHitFlag.MISS.flag;
flags |= flag.flag;
}
return flags;
}
}

View File

@@ -34,6 +34,7 @@ import com.l2jserver.model.id.template.ItemTemplateID;
import com.l2jserver.model.id.template.NPCTemplateID;
import com.l2jserver.model.world.Actor.ActorSex;
import com.l2jserver.model.world.NPC;
import com.l2jserver.model.world.npc.controller.NPCController;
import com.l2jserver.util.jaxb.ItemTemplateIDAdapter;
import com.l2jserver.util.jaxb.NPCTemplateIDAdapter;
@@ -47,8 +48,8 @@ public class NPCTemplate extends ActorTemplate<NPC> {
@XmlAttribute(name = "id")
@XmlJavaTypeAdapter(value = NPCTemplateIDAdapter.class)
protected NPCTemplateID id = null;
@XmlAttribute(name = "type")
protected String type = null;
@XmlAttribute(name = "controller")
protected Class<? extends NPCController> controller;
@XmlElement(name = "info")
protected NPCInformationMetadata info = null;
@@ -274,10 +275,10 @@ public class NPCTemplate extends ActorTemplate<NPC> {
}
/**
* @return the type
* @return the controller class
*/
public String getType() {
return type;
public Class<? extends NPCController> getControllerClass() {
return controller;
}
public String getName() {

View File

@@ -0,0 +1,98 @@
/*
* This file is part of l2jserver <l2jserver.com>.
*
* l2jserver is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* l2jserver 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with l2jserver. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.model.world.character.event;
import com.l2jserver.model.id.ObjectID;
import com.l2jserver.model.server.AttackHit;
import com.l2jserver.model.world.Actor;
import com.l2jserver.model.world.L2Character;
import com.l2jserver.model.world.Player;
import com.l2jserver.model.world.WorldObject;
/**
* Event triggered once a character attacks something
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class CharacterAttackEvent implements CharacterEvent {
/**
* The character that is attacking
*/
private final L2Character character;
/**
* The character that is being attacked
*/
private final L2Character target;
/**
* The {@link AttackHit} object for this attack
*/
private final AttackHit hit;
/**
* Creates a new instance
*
* @param character
* the character
* @param point
* the character being attacked
*/
public CharacterAttackEvent(L2Character character, L2Character target,
AttackHit hit) {
this.character = character;
this.target = target;
this.hit = hit;
}
/**
* @return the target
*/
public L2Character getTarget() {
return target;
}
/**
* @return the hit
*/
public AttackHit getHit() {
return hit;
}
@Override
public Player getPlayer() {
return character;
}
@Override
public Actor getActor() {
return character;
}
@Override
public WorldObject getObject() {
return character;
}
@Override
public L2Character getCharacter() {
return character;
}
@Override
public ObjectID<?>[] getDispatchableObjects() {
return new ObjectID<?>[] { character.getID(), target.getID() };
}
}

View File

@@ -0,0 +1,32 @@
/*
* This file is part of l2jserver <l2jserver.com>.
*
* l2jserver is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* l2jserver 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with l2jserver. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.model.world.npc.calculator;
import com.l2jserver.model.world.L2Character;
import com.l2jserver.util.calculator.Calculator;
import com.l2jserver.util.calculator.Function;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
*/
public class NPCCalculator extends Calculator<L2Character> {
public NPCCalculator(
Function<L2Character>... functions) {
super(functions);
}
}

View File

@@ -31,32 +31,38 @@ import com.l2jserver.util.html.markup.HtmlTemplate;
import com.l2jserver.util.html.markup.MarkupTag;
/**
* The {@link AbstractNPCController} handful methods for controlling NPCs.
* The {@link BaseNPCController} handful methods for controlling NPCs. This
* implementation is also used for {@link NPC NPCs} that don't have any special
* behavior.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class AbstractNPCController implements NPCController {
public class BaseNPCController implements NPCController {
@Override
public void action(NPC npc, Lineage2Connection conn, L2Character character,
String... args) throws L2Exception {
final String... args) throws L2Exception {
if (args.length == 2) {
if (args[0].equals("Chat")) {
talk(npc, conn, character,
Arrays.copyOfRange(args, 1, args.length));
return;
if (talk(npc, conn, character,
Arrays.copyOfRange(args, 1, args.length)))
return;
}
} else if (args.length == 0 || args.length == 1) {
talk(npc, conn, character, new String[0]);
return;
} else {
final HtmlTemplate template = new HtmlTemplate() {
@Override
protected void build(MarkupTag body) {
body.text("Sorry ${name}, but you cannot interact with me yet!");
}
}.register("name", character.getName());
conn.write(new NPCHtmlMessagePacket(npc, template));
// default action is talk
if (talk(npc, conn, character, new String[0]))
return;
}
// action not handled message
final HtmlTemplate template = new HtmlTemplate() {
@Override
protected void build(MarkupTag body) {
body.text(
"Sorry ${name}, but the action you have requested is not yet implemented.")
.p();
body.text("Arguments: " + Arrays.toString(args));
}
}.register("name", character.getName());
conn.write(new NPCHtmlMessagePacket(npc, template));
conn.sendActionFailed();
}
@@ -71,16 +77,21 @@ public class AbstractNPCController implements NPCController {
* the interacting character
* @param args
* the action arguments
* @return true if chat message was sent
* @throws L2Exception
*/
protected void talk(NPC npc, Lineage2Connection conn,
protected boolean talk(NPC npc, Lineage2Connection conn,
L2Character character, String... args) throws L2Exception {
String id = null;
if (args.length >= 1) {
id = args[0];
}
conn.write(new NPCHtmlMessagePacket(npc, getHTML(npc, id)));
final String html = getHTML(npc, id);
if (html == null)
return false;
conn.write(new NPCHtmlMessagePacket(npc, html));
conn.sendActionFailed();
return true;
}
/**

View File

@@ -0,0 +1,48 @@
/*
* This file is part of l2jserver <l2jserver.com>.
*
* l2jserver is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* l2jserver 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with l2jserver. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.model.world.npc.controller;
import com.google.inject.Inject;
import com.l2jserver.game.net.Lineage2Connection;
import com.l2jserver.game.net.packet.server.ActorStatusUpdatePacket;
import com.l2jserver.game.net.packet.server.ActorStatusUpdatePacket.Stat;
import com.l2jserver.model.world.L2Character;
import com.l2jserver.model.world.NPC;
import com.l2jserver.service.game.character.CharacterService;
import com.l2jserver.util.exception.L2Exception;
/**
* This controller is used to control teleporters (e.g. gatekeepers)
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class MonsterController extends BaseNPCController {
/**
* The {@link CharacterService}
*/
@Inject
protected CharacterService charService;
@Override
public void action(NPC npc, Lineage2Connection conn, L2Character character,
String... args) throws L2Exception {
// send hp update
conn.write(new ActorStatusUpdatePacket(npc).add(Stat.MAX_HP,
(int) npc.getTemplate().getMaximumHP()).add(Stat.HP,
(int) npc.getHP()));
}
}

View File

@@ -0,0 +1,52 @@
/*
* This file is part of l2jserver <l2jserver.com>.
*
* l2jserver is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* l2jserver 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with l2jserver. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.model.world.npc.controller;
import java.util.Arrays;
import com.l2jserver.game.net.Lineage2Connection;
import com.l2jserver.game.net.packet.server.NPCHtmlMessagePacket;
import com.l2jserver.model.world.L2Character;
import com.l2jserver.model.world.NPC;
import com.l2jserver.util.exception.L2Exception;
import com.l2jserver.util.html.markup.HtmlTemplate;
import com.l2jserver.util.html.markup.MarkupTag;
/**
* This is a pseudo-controller used as an placeholder for controllers that were
* not yet implemented.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class NotImplementedNPCController extends BaseNPCController {
@Override
public void action(NPC npc, Lineage2Connection conn, L2Character character,
final String... args) throws L2Exception {
// action not handled
final HtmlTemplate template = new HtmlTemplate() {
@Override
protected void build(MarkupTag body) {
body.text(
"Sorry ${name}, but the action you have requested is not yet implemented.")
.p();
body.text("Arguments: " + Arrays.toString(args));
}
}.register("name", character.getName());
conn.write(new NPCHtmlMessagePacket(npc, template));
conn.sendActionFailed();
}
}

View File

@@ -30,7 +30,7 @@ import com.l2jserver.util.exception.L2Exception;
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class TeleporterController extends AbstractNPCController {
public class TeleporterController extends BaseNPCController {
/**
* The {@link SpawnService}
*/
@@ -47,11 +47,13 @@ public class TeleporterController extends AbstractNPCController {
final TeleportationTemplate tele = teleportationIdProvider
.createID(Integer.parseInt(args[1])).getTemplate();
if (tele == null) {
// TODO chat
// TODO notify user that his destination is invalid
conn.sendActionFailed();
return;
} else {
// TODO remove items from character inventory
spawnService.teleport(character, tele.getCoordinate());
return;
}
}
}

View File

@@ -0,0 +1,43 @@
/*
* This file is part of l2jserver <l2jserver.com>.
*
* l2jserver is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* l2jserver 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with l2jserver. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.game;
import com.l2jserver.model.server.AttackHit;
import com.l2jserver.model.world.Actor;
import com.l2jserver.service.Service;
import com.l2jserver.service.core.threading.AsyncFuture;
/**
* This service handles attacking. It can schedule auto-attack events and also
* deals the damage.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface AttackService extends Service {
/**
* Schedules an attack command. The attack order will be put in queue and
* the will be executed as soon as possible.
*
* @param attacker
* the actor attacking <tt>target</tt>
* @param target
* the actor receiving the attack from <tt>attacker</tt>
* @return the {@link AsyncFuture} that can be used to retrieve
* {@link AttackHit} object, once the attack has been executed.
*/
AsyncFuture<AttackHit> attack(Actor attacker, Actor target);
}

View File

@@ -0,0 +1,86 @@
/*
* This file is part of l2jserver <l2jserver.com>.
*
* l2jserver is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* l2jserver 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with l2jserver. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.game;
import java.util.concurrent.Callable;
import com.google.common.base.Preconditions;
import com.google.inject.Inject;
import com.l2jserver.model.server.AttackHit;
import com.l2jserver.model.world.Actor;
import com.l2jserver.service.AbstractService;
import com.l2jserver.service.core.threading.AsyncFuture;
import com.l2jserver.service.core.threading.ThreadService;
import com.l2jserver.service.game.world.event.WorldEventDispatcher;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class AttackServiceImpl extends AbstractService implements AttackService {
/**
* The {@link ThreadService} is used to schedule asynchronous attacks
*/
private final ThreadService threadService;
/**
* The {@link WorldEventDispatcher} is used to dispatch attack events to the
* world
*/
private final WorldEventDispatcher eventDispatcher;
@Inject
public AttackServiceImpl(ThreadService threadService,
WorldEventDispatcher eventDispatcher) {
this.threadService = threadService;
this.eventDispatcher = eventDispatcher;
}
@Override
public AsyncFuture<AttackHit> attack(Actor attacker, Actor target) {
Preconditions.checkNotNull(attacker, "attacker");
Preconditions.checkNotNull(target, "target");
Preconditions.checkArgument(!attacker.equals(target),
"attacker must not be equal to target");
return threadService.async(new AttackCallable(attacker, target));
}
/**
* {@link Callable} implementation used to execute attacks.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
private class AttackCallable implements Callable<AttackHit> {
/**
* The attacker
*/
private final Actor attacker;
/**
* The target
*/
private final Actor target;
public AttackCallable(Actor attacker, Actor target) {
this.attacker = attacker;
this.target = target;
}
@Override
public AttackHit call() throws Exception {
return null;
}
}
}

View File

@@ -19,6 +19,7 @@ package com.l2jserver.service.game.character;
import com.l2jserver.model.world.Actor;
import com.l2jserver.model.world.L2Character;
import com.l2jserver.service.Service;
import com.l2jserver.service.game.npc.NotAttackableNPCServiceException;
import com.l2jserver.service.game.spawn.AlreadySpawnedServiceException;
import com.l2jserver.service.game.spawn.NotSpawnedServiceException;
import com.l2jserver.service.game.spawn.SpawnPointNotFoundServiceException;
@@ -79,10 +80,13 @@ public interface CharacterService extends Service {
* if target cannot be set
* @throws ActorIsNotAttackableServiceException
* if the target is not attackable
* @throws NotAttackableNPCServiceException
* if the actor is not attackable
*/
void attack(L2Character character, Actor target)
throws CannotSetTargetServiceException,
ActorIsNotAttackableServiceException;
ActorIsNotAttackableServiceException,
NotAttackableNPCServiceException;
/**
* Jails the given <tt>character</tt> for <tt>time</tt> seconds.

View File

@@ -59,6 +59,8 @@ import com.l2jserver.service.game.chat.ChatChannel;
import com.l2jserver.service.game.chat.ChatChannelListener;
import com.l2jserver.service.game.chat.ChatMessageDestination;
import com.l2jserver.service.game.chat.ChatService;
import com.l2jserver.service.game.npc.NPCService;
import com.l2jserver.service.game.npc.NotAttackableNPCServiceException;
import com.l2jserver.service.game.spawn.AlreadySpawnedServiceException;
import com.l2jserver.service.game.spawn.NotSpawnedServiceException;
import com.l2jserver.service.game.spawn.SpawnPointNotFoundServiceException;
@@ -104,6 +106,10 @@ public class CharacterServiceImpl extends AbstractService implements
* The {@link SpawnService}
*/
private final SpawnService spawnService;
/**
* The {@link NPCService}
*/
private final NPCService npcService;
/**
* The {@link ItemDAO}
*/
@@ -118,12 +124,13 @@ public class CharacterServiceImpl extends AbstractService implements
public CharacterServiceImpl(WorldService worldService,
WorldEventDispatcher eventDispatcher, ChatService chatService,
NetworkService networkService, SpawnService spawnService,
ItemDAO itemDao) {
NPCService npcService, ItemDAO itemDao) {
this.worldService = worldService;
this.eventDispatcher = eventDispatcher;
this.chatService = chatService;
this.networkService = networkService;
this.spawnService = spawnService;
this.npcService = npcService;
this.itemDao = itemDao;
}
@@ -334,7 +341,8 @@ public class CharacterServiceImpl extends AbstractService implements
character.setTargetID(target.getID());
eventDispatcher.dispatch(new CharacterTargetSelectedEvent(
character, target));
conn.write(new CharacterTargetSelectedPacket(target));
conn.write(new CharacterTargetSelectedPacket(target, character
.getLevel() - target.getLevel()));
} else {
// this indicates an inconsistency: reset target and throws an
// exception
@@ -347,7 +355,8 @@ public class CharacterServiceImpl extends AbstractService implements
@Override
public void attack(L2Character character, Actor target)
throws CannotSetTargetServiceException,
ActorIsNotAttackableServiceException {
ActorIsNotAttackableServiceException,
NotAttackableNPCServiceException {
Preconditions.checkNotNull(character, "character");
Preconditions.checkNotNull(target, "target");
final CharacterID id = character.getID();
@@ -362,7 +371,10 @@ public class CharacterServiceImpl extends AbstractService implements
// first try to target this, if it is not already
target(character, target);
// TODO issue attack
npcService.attack(npc, conn, character);
} else {
// TODO throw an exception
conn.sendActionFailed();
}
}

View File

@@ -18,6 +18,7 @@ package com.l2jserver.service.game.npc;
import java.util.List;
import com.l2jserver.game.net.Lineage2Connection;
import com.l2jserver.game.net.packet.client.CharacterActionPacket.CharacterAction;
import com.l2jserver.model.template.NPCTemplate;
import com.l2jserver.model.world.L2Character;
@@ -76,11 +77,13 @@ public interface NPCService extends Service {
*
* @param npc
* the npc
* @param conn
* the {@link Lineage2Connection} object
* @param attacker
* the character
* @throws NotAttackableNPCServiceException
* if {@link NPC} is not attackable
*/
void attack(NPC npc, L2Character attacker)
void attack(NPC npc, Lineage2Connection conn, L2Character attacker)
throws NotAttackableNPCServiceException;
}

View File

@@ -17,21 +17,27 @@
package com.l2jserver.service.game.npc;
import java.util.List;
import java.util.Map;
import com.google.common.base.Preconditions;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.l2jserver.db.dao.NPCDAO;
import com.l2jserver.game.net.Lineage2Connection;
import com.l2jserver.game.net.packet.client.CharacterActionPacket.CharacterAction;
import com.l2jserver.game.net.packet.server.ActorAttackPacket;
import com.l2jserver.model.server.AttackHit;
import com.l2jserver.model.world.L2Character;
import com.l2jserver.model.world.NPC;
import com.l2jserver.model.world.npc.controller.TeleporterController;
import com.l2jserver.model.world.npc.controller.NPCController;
import com.l2jserver.service.AbstractService;
import com.l2jserver.service.game.character.CharacterService;
import com.l2jserver.service.game.spawn.AlreadySpawnedServiceException;
import com.l2jserver.service.game.spawn.SpawnPointNotFoundServiceException;
import com.l2jserver.service.game.spawn.SpawnService;
import com.l2jserver.service.network.NetworkService;
import com.l2jserver.util.exception.L2Exception;
import com.l2jserver.util.factory.CollectionFactory;
/**
* Default {@link NPCService} implementation
@@ -47,6 +53,10 @@ public class NPCServiceImpl extends AbstractService implements NPCService {
* The {@link NetworkService} used to discover {@link Lineage2Connection}
*/
private final NetworkService networkService;
/**
* The {@link CharacterService}
*/
private final CharacterService characterService;
/**
* The {@link NPCDAO}
@@ -54,17 +64,26 @@ public class NPCServiceImpl extends AbstractService implements NPCService {
private final NPCDAO npcDao;
/**
* Temporary only
* The {@link Injector} used to create {@link NPCController} instances
*/
@Inject
private TeleporterController controller;
private Injector injector;
/**
* The map containing all active controllers
*/
private Map<Class<? extends NPCController>, NPCController> controllers = CollectionFactory
.newMap();
@Inject
public NPCServiceImpl(SpawnService spawnService,
NetworkService networkService, NPCDAO npcDao) {
NetworkService networkService, CharacterService characterService,
NPCDAO npcDao, Injector injector) {
this.spawnService = spawnService;
this.networkService = networkService;
this.characterService = characterService;
this.npcDao = npcDao;
this.injector = injector;
}
@Override
@@ -77,6 +96,7 @@ public class NPCServiceImpl extends AbstractService implements NPCService {
final Lineage2Connection conn = networkService.discover(character
.getID());
try {
final NPCController controller = getController(npc);
controller.action(npc, conn, character, new String[0]);
} catch (L2Exception e) {
throw new ActionServiceException(e);
@@ -94,6 +114,7 @@ public class NPCServiceImpl extends AbstractService implements NPCService {
final Lineage2Connection conn = networkService.discover(character
.getID());
try {
final NPCController controller = getController(npc);
controller.action(npc, conn, character, args);
} catch (L2Exception e) {
throw new ActionServiceException(e);
@@ -111,9 +132,27 @@ public class NPCServiceImpl extends AbstractService implements NPCService {
}
@Override
public void attack(NPC npc, L2Character attacker)
public void attack(NPC npc, Lineage2Connection conn, L2Character attacker)
throws NotAttackableNPCServiceException {
Preconditions.checkNotNull(npc, "npc");
Preconditions.checkNotNull(conn, "conn");
Preconditions.checkNotNull(attacker, "attacker");
conn.write(new ActorAttackPacket(conn.getCharacter(), new AttackHit(
conn.getCharacter(), npc)));
}
private NPCController getController(NPC npc) {
// make sure everything's synchronized-no duplicated instances
synchronized (controllers) {
final Class<? extends NPCController> controllerClass = npc
.getTemplate().getControllerClass();
NPCController controller = controllers.get(controllerClass);
if (controller == null) {
controller = injector.getInstance(controllerClass);
controllers.put(controllerClass, controller);
}
return controller;
}
}
}

View File

@@ -73,6 +73,6 @@ public class XMLMappingTest {
final NPCTemplate t = (NPCTemplate) u
.unmarshal(new File("data/templates/npc/teleporter/30059-Trisha.xml"));
System.out.println(t.getName());
System.out.println(t.getControllerClass());
}
}

View File

@@ -28,11 +28,13 @@ import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.MarshalException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.SchemaOutputResolver;
import javax.xml.transform.Result;
@@ -61,6 +63,10 @@ import com.l2jserver.model.template.NPCTemplate.NPCInformationMetadata.NPCTitleM
import com.l2jserver.model.template.NPCTemplate.TalkMetadata;
import com.l2jserver.model.template.TeleportationTemplate.TeleportRestriction;
import com.l2jserver.model.world.Actor.ActorSex;
import com.l2jserver.model.world.npc.controller.BaseNPCController;
import com.l2jserver.model.world.npc.controller.NPCController;
import com.l2jserver.model.world.npc.controller.NotImplementedNPCController;
import com.l2jserver.model.world.npc.controller.TeleporterController;
import com.l2jserver.service.game.template.XMLTemplateService.TeleportationTemplateContainer;
import com.l2jserver.util.dimensional.Coordinate;
import com.l2jserver.util.factory.CollectionFactory;
@@ -74,6 +80,9 @@ public class NPCTemplateConverter {
private static final File L2J_HTML_FOLDER = new File(
"../L2J_DataPack_BETA/data/html");
private static final Map<String, Class<? extends NPCController>> controllers = CollectionFactory
.newMap();
private static List<NPCTemplate> templates = CollectionFactory.newList();
private static Collection<File> htmlScannedFiles;
private static TeleportationTemplateContainer teleportation = new TeleportationTemplateContainer();
@@ -81,7 +90,11 @@ public class NPCTemplateConverter {
@SuppressWarnings("unchecked")
public static void main(String[] args) throws SQLException, IOException,
ClassNotFoundException, JAXBException {
controllers.put("L2Teleporter", TeleporterController.class);
controllers.put("L2CastleTeleporter", TeleporterController.class);
controllers.put("L2Npc", BaseNPCController.class);
Class.forName("com.mysql.jdbc.Driver");
final File target = new File("data/templates");
System.out.println("Scaning legacy HTML files...");
@@ -150,9 +163,11 @@ public class NPCTemplateConverter {
st.execute();
final ResultSet rs = st.getResultSet();
while (rs.next()) {
NPCTemplate t = fillNPC(rs);
Object[] result = fillNPC(rs);
NPCTemplate t = (NPCTemplate) result[0];
String type = (String) result[1];
String folder = createFolder(t.type);
String folder = createFolder(type);
if (folder.isEmpty()) {
m.setProperty(Marshaller.JAXB_SCHEMA_LOCATION,
"npc ../npc.xsd");
@@ -171,14 +186,14 @@ public class NPCTemplateConverter {
file.getParentFile().mkdirs();
templates.add(t);
// try {
// m.marshal(t, getXMLSerializer(new FileOutputStream(file)));
// } catch (MarshalException e) {
// System.err
// .println("Could not generate XML template file for "
// + t.getName() + " - " + t.getID());
// file.delete();
// }
try {
m.marshal(t, getXMLSerializer(new FileOutputStream(file)));
} catch (MarshalException e) {
System.err
.println("Could not generate XML template file for "
+ t.getName() + " - " + t.getID());
file.delete();
}
}
System.out.println("Generated " + templates.size() + " templates");
@@ -203,12 +218,14 @@ public class NPCTemplateConverter {
}
}
private static NPCTemplate fillNPC(ResultSet rs) throws SQLException,
private static Object[] fillNPC(ResultSet rs) throws SQLException,
IOException {
final NPCTemplate template = new NPCTemplate();
template.id = new NPCTemplateID(rs.getInt("idTemplate"), null);
template.type = createParentType(rs.getString("type"));
template.controller = controllers.get(rs.getString("type"));
if (template.controller == null)
template.controller = NotImplementedNPCController.class;
template.info = new NPCInformationMetadata();
template.info.nameMetadata = new NPCNameMetadata();
@@ -232,6 +249,7 @@ public class NPCTemplateConverter {
// template.info.attackable = rs.getBoolean("attackable");
template.info.targetable = rs.getBoolean("targetable");
template.info.aggressive = rs.getBoolean("aggro");
template.info.attackable = true; // FIXME
template.info.stats = new NPCStatsMetadata();
@@ -292,7 +310,7 @@ public class NPCTemplateConverter {
template.droplist = fillDropList(rs, template.id.getID());
template.talk = fillHtmlChat(template.id.getID());
return template;
return new Object[] { template, createParentType(rs.getString("type")) };
}
private static List<DropItemMetadata> fillDropList(ResultSet npcRs,