1
0
mirror of https://github.com/Rogiel/l2jserver2 synced 2025-12-06 07:32:46 +00:00

Implements item dropping and pickup stacking

This commit is contained in:
2011-12-15 16:41:09 -02:00
parent 352735c8c9
commit f87b9dd755
19 changed files with 581 additions and 77 deletions

View File

@@ -16,6 +16,10 @@
*/ */
package com.l2jserver.model; package com.l2jserver.model;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.l2jserver.model.id.ID; import com.l2jserver.model.id.ID;
@@ -28,6 +32,8 @@ import com.l2jserver.model.id.ID;
* @author <a href="http://www.rogiel.com">Rogiel</a> * @author <a href="http://www.rogiel.com">Rogiel</a>
*/ */
public abstract class AbstractModel<T extends ID<?>> implements Model<T> { public abstract class AbstractModel<T extends ID<?>> implements Model<T> {
private final Logger log = LoggerFactory.getLogger(this.getClass());
/** /**
* The object id * The object id
*/ */
@@ -58,6 +64,7 @@ public abstract class AbstractModel<T extends ID<?>> implements Model<T> {
public void setObjectDesire(ObjectDesire desire) { public void setObjectDesire(ObjectDesire desire) {
if (desire == null) if (desire == null)
desire = ObjectDesire.NONE; desire = ObjectDesire.NONE;
log.debug("{} set desire to {}", this, desire);
this.desire = desire; this.desire = desire;
} }
@@ -69,8 +76,10 @@ public abstract class AbstractModel<T extends ID<?>> implements Model<T> {
@SuppressWarnings("javadoc") @SuppressWarnings("javadoc")
protected void desireUpdate() { protected void desireUpdate() {
if (this.desire != ObjectDesire.INSERT if (this.desire != ObjectDesire.INSERT
&& this.desire != ObjectDesire.DELETE) && this.desire != ObjectDesire.DELETE) {
log.debug("{} desires an update", this);
this.desire = ObjectDesire.UPDATE; this.desire = ObjectDesire.UPDATE;
}
} }
/** /**
@@ -79,8 +88,10 @@ public abstract class AbstractModel<T extends ID<?>> implements Model<T> {
*/ */
@SuppressWarnings("javadoc") @SuppressWarnings("javadoc")
protected void desireInsert() { protected void desireInsert() {
if (this.desire != ObjectDesire.DELETE) if (this.desire != ObjectDesire.DELETE) {
log.debug("{} desires an insert", this);
this.desire = ObjectDesire.INSERT; this.desire = ObjectDesire.INSERT;
}
} }
@Override @Override

View File

@@ -515,7 +515,7 @@ public abstract class AbstractJDBCDatabaseService extends AbstractService
this.parametize(st, object); this.parametize(st, object);
log.debug("Sending query to database for {}", object); log.debug("Sending query to database for {}", object);
rows += st.executeUpdate(); rows = st.executeUpdate();
log.debug("Query inserted or updated {} rows for {}", rows, log.debug("Query inserted or updated {} rows for {}", rows,
object); object);
@@ -616,9 +616,7 @@ public abstract class AbstractJDBCDatabaseService extends AbstractService
continue; continue;
} }
if (obj instanceof Model) { if (obj instanceof Model) {
if (((Model<?>) obj).getObjectDesire() == ObjectDesire.INSERT) { ((Model<?>) obj).setObjectDesire(ObjectDesire.NONE);
((Model<?>) obj).setObjectDesire(ObjectDesire.NONE);
}
} }
log.debug("Mapper {} returned {}", mapper, obj); log.debug("Mapper {} returned {}", mapper, obj);
list.add(obj); list.add(obj);
@@ -695,9 +693,7 @@ public abstract class AbstractJDBCDatabaseService extends AbstractService
log.debug("Mapping row {} with {}", rs, mapper); log.debug("Mapping row {} with {}", rs, mapper);
final T object = mapper.map(rs); final T object = mapper.map(rs);
if (object instanceof Model) { if (object instanceof Model) {
if (((Model<?>) object).getObjectDesire() == ObjectDesire.INSERT) { ((Model<?>) object).setObjectDesire(ObjectDesire.NONE);
((Model<?>) object).setObjectDesire(ObjectDesire.NONE);
}
} }
log.debug("Mapper {} returned {}", mapper, object); log.debug("Mapper {} returned {}", mapper, object);
return object; return object;

View File

@@ -120,6 +120,17 @@ public class Point3D extends Point {
return coordinate; return coordinate;
} }
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Point3D [" + coordinate.getX() + "," + coordinate.getY() + ","
+ coordinate.getZ() + "," + angle + "]";
}
/** /**
* Creates a new instance from the 3 points and an angle * Creates a new instance from the 3 points and an angle
* *

View File

@@ -204,7 +204,7 @@ public enum SystemMessage {
* ID: 29<br> * ID: 29<br>
* Message: You have obtained $s2 $s1. * Message: You have obtained $s2 $s1.
*/ */
YOU_PICKED_UP_S1_S2(29), YOU_PICKED_UP_S2_S1(29),
/** /**
* ID: 30<br> * ID: 30<br>

View File

@@ -41,6 +41,7 @@ import com.l2jserver.game.net.packet.client.CM_CHAR_POSITION;
import com.l2jserver.game.net.packet.client.CM_CHAR_REQ_INVENTORY; import com.l2jserver.game.net.packet.client.CM_CHAR_REQ_INVENTORY;
import com.l2jserver.game.net.packet.client.CM_CHAR_SELECT; import com.l2jserver.game.net.packet.client.CM_CHAR_SELECT;
import com.l2jserver.game.net.packet.client.CM_CHAT; import com.l2jserver.game.net.packet.client.CM_CHAT;
import com.l2jserver.game.net.packet.client.CM_DROP_ITEM;
import com.l2jserver.game.net.packet.client.CM_ENTER_WORLD; import com.l2jserver.game.net.packet.client.CM_ENTER_WORLD;
import com.l2jserver.game.net.packet.client.CM_EXT_REQ_ALL_FORTRESS_INFO; import com.l2jserver.game.net.packet.client.CM_EXT_REQ_ALL_FORTRESS_INFO;
import com.l2jserver.game.net.packet.client.CM_EXT_REQ_KEY_MAPPING; import com.l2jserver.game.net.packet.client.CM_EXT_REQ_KEY_MAPPING;
@@ -187,6 +188,8 @@ public class Lineage2PacketReader extends OneToOneDecoder {
return CM_CHAR_OPEN_MAP.class; return CM_CHAR_OPEN_MAP.class;
case CM_ATTACK.OPCODE: case CM_ATTACK.OPCODE:
return CM_ATTACK.class; return CM_ATTACK.class;
case CM_DROP_ITEM.OPCODE:
return CM_DROP_ITEM.class;
default: default:
logger.warn("Unknown packet for 0x{}", Integer.toHexString(opcode)); logger.warn("Unknown packet for 0x{}", Integer.toHexString(opcode));
break; break;

View File

@@ -118,9 +118,7 @@ public class CM_CHAR_ACTION extends AbstractClientPacket {
final NPC npc = ((NPCID) id).getObject(); final NPC npc = ((NPCID) id).getObject();
try { try {
npcService.action(npc, conn.getCharacter(), action); npcService.action(npc, conn.getCharacter(), action);
} catch (ActionServiceException e) { } catch (ActionServiceException | CannotSetTargetServiceException e) {
conn.sendActionFailed();
} catch (CannotSetTargetServiceException e) {
conn.sendActionFailed(); conn.sendActionFailed();
} }
} else if (id instanceof ItemID) { } else if (id instanceof ItemID) {
@@ -133,7 +131,7 @@ public class CM_CHAR_ACTION extends AbstractClientPacket {
} else { // update only } else { // update only
conn.updateInventoryItems(stackItem); conn.updateInventoryItems(stackItem);
} }
conn.sendSystemMessage(SystemMessage.YOU_PICKED_UP_S1_S2, Long conn.sendSystemMessage(SystemMessage.YOU_PICKED_UP_S2_S1, Long
.toString(item.getCount()), item.getTemplate() .toString(item.getCount()), item.getTemplate()
.getName()); .getName());
conn.sendActionFailed(); conn.sendActionFailed();

View File

@@ -0,0 +1,122 @@
/*
* This file is part of l2jserver2 <l2jserver2.com>.
*
* l2jserver2 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.
*
* l2jserver2 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 l2jserver2. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.game.net.packet.client;
import org.jboss.netty.buffer.ChannelBuffer;
import com.google.inject.Inject;
import com.l2jserver.game.net.Lineage2Client;
import com.l2jserver.game.net.SystemMessage;
import com.l2jserver.game.net.packet.AbstractClientPacket;
import com.l2jserver.model.id.object.ItemID;
import com.l2jserver.model.id.object.provider.ItemIDProvider;
import com.l2jserver.model.world.Item;
import com.l2jserver.service.game.item.ItemAlreadyOnGroundServiceException;
import com.l2jserver.service.game.item.ItemService;
import com.l2jserver.service.game.item.NotEnoughItemsServiceException;
import com.l2jserver.service.game.spawn.AlreadySpawnedServiceException;
import com.l2jserver.service.game.spawn.SpawnPointNotFoundServiceException;
import com.l2jserver.util.geometry.Point3D;
/**
* This packet drops items on the ground.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class CM_DROP_ITEM extends AbstractClientPacket {
/**
* The packet OPCODE
*/
public static final int OPCODE = 0x17;
/**
* The {@link ItemService}
*/
private final ItemService itemService;
private final ItemIDProvider itemIdProvider;
/**
* The item ID
*/
private int objectId;
/**
* The number of items to be dropped
*/
private long count;
/**
* The location to be dropped
*/
private Point3D point;
/**
* @param itemService
* the item service
* @param itemIdProvider
* the item id provider
*/
@Inject
public CM_DROP_ITEM(ItemService itemService, ItemIDProvider itemIdProvider) {
this.itemService = itemService;
this.itemIdProvider = itemIdProvider;
}
@Override
public void read(Lineage2Client conn, ChannelBuffer buffer) {
objectId = buffer.readInt();
count = buffer.readLong();
point = Point3D.fromXYZ(buffer.readInt(), buffer.readInt(),
buffer.readInt());
}
@Override
public void process(final Lineage2Client conn) {
final ItemID id = itemIdProvider.resolveID(objectId);
final Item item = id.getObject();
if (item == null) {
conn.sendActionFailed();
return;
}
if (!conn.getCharacterID().equals(item.getOwnerID())) {
conn.sendActionFailed();
return;
}
try {
final Item dropped = itemService.drop(item, count, point,
conn.getCharacter());
if (dropped.equals(item)) {
conn.removeInventoryItems(dropped);
} else {
conn.updateInventoryItems(item);
}
if (dropped.getCount() == 1) {
conn.sendSystemMessage(SystemMessage.YOU_DROPPED_S1, item
.getTemplate().getName());
} else {
conn.sendSystemMessage(SystemMessage.DROPPED_S1_S2, item
.getTemplate().getName(), Long.toString(dropped
.getCount()));
}
} catch (ItemAlreadyOnGroundServiceException
| AlreadySpawnedServiceException
| SpawnPointNotFoundServiceException
| NotEnoughItemsServiceException e) {
conn.sendActionFailed();
}
}
}

View File

@@ -59,6 +59,7 @@ public class SM_CHAR_INVENTORY extends AbstractServerPacket {
int slot = 0; int slot = 0;
for (Item item : inventory) { for (Item item : inventory) {
if (item.getLocation() == ItemLocation.WAREHOUSE if (item.getLocation() == ItemLocation.WAREHOUSE
|| item.getLocation() == ItemLocation.GROUND
|| item.getLocation() == null) { || item.getLocation() == null) {
continue; continue;
} }

View File

@@ -17,21 +17,22 @@
package com.l2jserver.model.world; package com.l2jserver.model.world;
import com.l2jserver.model.id.object.CharacterID; import com.l2jserver.model.id.object.CharacterID;
import com.l2jserver.model.id.object.ItemID;
import com.l2jserver.model.id.template.ItemTemplateID; import com.l2jserver.model.id.template.ItemTemplateID;
import com.l2jserver.model.template.item.ItemTemplate; import com.l2jserver.model.template.item.ItemTemplate;
import com.l2jserver.model.world.character.CharacterInventory.ItemLocation;
import com.l2jserver.model.world.character.CharacterInventory.InventoryPaperdoll; import com.l2jserver.model.world.character.CharacterInventory.InventoryPaperdoll;
import com.l2jserver.model.world.character.CharacterInventory.ItemLocation;
/** /**
* This class represents an {@link Item} in the Lineage II World. The item can * This class represents an {@link Item} in the Lineage II World. The item can
* be: * be:
* <ul> * <ul>
* <li><b>In the {@link L2Character character} inventory</b>: <tt>location</tt> * <li><b>In the {@link L2Character character} inventory</b>: <tt>location</tt>
* is {@link ItemLocation#INVENTORY}, <tt>coordinate</tt> and * is {@link ItemLocation#INVENTORY}, <tt>coordinate</tt> and <tt>paperdoll</tt>
* <tt>paperdoll</tt> are null.</li> * are null.</li>
* <li><b>In the {@link L2Character character} warehouse</b>: <tt>location</tt> * <li><b>In the {@link L2Character character} warehouse</b>: <tt>location</tt>
* is {@link ItemLocation#WAREHOUSE}, <tt>coordinate</tt> and * is {@link ItemLocation#WAREHOUSE}, <tt>coordinate</tt> and <tt>paperdoll</tt>
* <tt>paperdoll</tt> are null.</li> * are null.</li>
* <li><b>Equipped by the {@link L2Character character}</b>: <tt>location</tt> * <li><b>Equipped by the {@link L2Character character}</b>: <tt>location</tt>
* is {@link ItemLocation#PAPERDOLL}, <tt>paperdoll</tt> is not null and * is {@link ItemLocation#PAPERDOLL}, <tt>paperdoll</tt> is not null and
* <tt>coordinate</tt> is null.</li> * <tt>coordinate</tt> is null.</li>
@@ -58,7 +59,7 @@ public class Item extends PositionableObject {
/** /**
* Inventory location of this item * Inventory location of this item
*/ */
private ItemLocation location; private ItemLocation location = ItemLocation.INVENTORY;
/** /**
* Paperdoll slot for this item * Paperdoll slot for this item
*/ */
@@ -156,4 +157,9 @@ public class Item extends PositionableObject {
desireUpdate(); desireUpdate();
this.ownerID = ownerID; this.ownerID = ownerID;
} }
@Override
public ItemID getID() {
return (ItemID) super.getID();
}
} }

View File

@@ -16,12 +16,13 @@
*/ */
package com.l2jserver.model.world.character; package com.l2jserver.model.world.character;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.Set; import java.util.Set;
import com.l2jserver.model.id.object.ItemID; import com.l2jserver.model.id.object.ItemID;
import com.l2jserver.model.id.template.ItemTemplateID;
import com.l2jserver.model.world.Item; import com.l2jserver.model.world.Item;
import com.l2jserver.model.world.L2Character; import com.l2jserver.model.world.L2Character;
import com.l2jserver.util.factory.CollectionFactory; import com.l2jserver.util.factory.CollectionFactory;
@@ -76,6 +77,23 @@ public class CharacterInventory implements Iterable<Item> {
return null; return null;
} }
/**
* Removes several items from the players inventory.
*
* @param items
* the items to be removed
* @return the items that were effectivelly removed
*/
public Item[] remove(Item... items) {
final List<Item> removedItems = CollectionFactory.newList();
for (final Item item : items) {
if (this.items.remove(item)) {
removedItems.add(item);
}
}
return removedItems.toArray(new Item[removedItems.size()]);
}
/** /**
* Removes all items from the given {@link ItemID} * Removes all items from the given {@link ItemID}
* *
@@ -84,7 +102,7 @@ public class CharacterInventory implements Iterable<Item> {
* @return an array of all items removed. Can never be <code>null</code>. * @return an array of all items removed. Can never be <code>null</code>.
*/ */
public Item[] remove(ItemID itemID) { public Item[] remove(ItemID itemID) {
final ArrayList<Item> removedItems = new ArrayList<Item>(); final List<Item> removedItems = CollectionFactory.newList();
for (final Item item : items) { for (final Item item : items) {
if (item.getID().equals(itemID)) { if (item.getID().equals(itemID)) {
items.remove(item); items.remove(item);
@@ -94,6 +112,23 @@ public class CharacterInventory implements Iterable<Item> {
return removedItems.toArray(new Item[removedItems.size()]); return removedItems.toArray(new Item[removedItems.size()]);
} }
/**
* Returns all items from the given {@link ItemTemplateID}
*
* @param itemTemplateID
* the {@link ItemTemplateID}
* @return an array of all items with the given ID
*/
public Item[] getItems(ItemTemplateID itemTemplateID) {
final List<Item> allItems = CollectionFactory.newList();
for (final Item item : items) {
if (item.getTemplateID().equals(itemTemplateID)) {
allItems.add(item);
}
}
return allItems.toArray(new Item[allItems.size()]);
}
/** /**
* Get the item in the given <tt>paperdoll</tt> slot * Get the item in the given <tt>paperdoll</tt> slot
* *

View File

@@ -19,20 +19,19 @@ package com.l2jserver.model.world.item;
import com.l2jserver.model.id.ObjectID; import com.l2jserver.model.id.ObjectID;
import com.l2jserver.model.world.Actor; import com.l2jserver.model.world.Actor;
import com.l2jserver.model.world.Item; import com.l2jserver.model.world.Item;
import com.l2jserver.model.world.Player;
import com.l2jserver.model.world.WorldObject; import com.l2jserver.model.world.WorldObject;
import com.l2jserver.model.world.player.event.PlayerEvent; import com.l2jserver.model.world.actor.event.ActorEvent;
/** /**
* Event dispatched once an {@link Item} has been dropped on the ground. * Event dispatched once an {@link Item} has been dropped on the ground.
* *
* @author <a href="http://www.rogiel.com">Rogiel</a> * @author <a href="http://www.rogiel.com">Rogiel</a>
*/ */
public class ItemDropEvent implements ItemEvent, PlayerEvent { public class ItemDropEvent implements ItemEvent, ActorEvent {
/** /**
* The dropping player * The dropping actor
*/ */
private final Player player; private final Actor actor;
/** /**
* The item dropped * The item dropped
*/ */
@@ -41,13 +40,13 @@ public class ItemDropEvent implements ItemEvent, PlayerEvent {
/** /**
* Creates a new instance of this event * Creates a new instance of this event
* *
* @param player * @param actor
* the dropping player * the dropping actor
* @param item * @param item
* the dropped item * the dropped item
*/ */
public ItemDropEvent(Player player, Item item) { public ItemDropEvent(Actor actor, Item item) {
this.player = player; this.actor = actor;
this.item = item; this.item = item;
} }
@@ -56,11 +55,6 @@ public class ItemDropEvent implements ItemEvent, PlayerEvent {
return item; return item;
} }
@Override
public Player getPlayer() {
return player;
}
@Override @Override
public Item getItem() { public Item getItem() {
return item; return item;
@@ -68,7 +62,7 @@ public class ItemDropEvent implements ItemEvent, PlayerEvent {
@Override @Override
public Actor getActor() { public Actor getActor() {
return player; return actor;
} }
@Override @Override

View File

@@ -38,6 +38,10 @@ public class ItemPickUpEvent implements ItemEvent, CharacterEvent {
* The item picked up * The item picked up
*/ */
private final Item item; private final Item item;
/**
* The new item
*/
private final Item newItem;
/** /**
* Creates a new instance of this event * Creates a new instance of this event
@@ -46,10 +50,19 @@ public class ItemPickUpEvent implements ItemEvent, CharacterEvent {
* the picking up character * the picking up character
* @param item * @param item
* the picked up item * the picked up item
* @param newItem the new {@link Item}
*/ */
public ItemPickUpEvent(L2Character character, Item item) { public ItemPickUpEvent(L2Character character, Item item, Item newItem) {
this.character = character; this.character = character;
this.item = item; this.item = item;
this.newItem = newItem;
}
/**
* @return the new {@link Item}
*/
public Item getNewItem() {
return newItem;
} }
@Override @Override

View File

@@ -184,12 +184,13 @@ public abstract class JDBCItemDAO extends AbstractJDBCDAO<Item, ItemID>
@Override @Override
protected String query() { protected String query() {
return "SELECT * FROM `" + TABLE + "` WHERE `" + CHAR_ID return "SELECT * FROM `" + TABLE + "` WHERE `" + CHAR_ID
+ "` = ?"; + "` = ? AND `location` = ?";
} }
@Override @Override
protected void parametize(PreparedStatement st) throws SQLException { protected void parametize(PreparedStatement st) throws SQLException {
st.setInt(1, character.getID().getID()); st.setInt(1, character.getID().getID());
st.setString(2, ItemLocation.INVENTORY.name());
} }
@Override @Override
@@ -239,23 +240,47 @@ public abstract class JDBCItemDAO extends AbstractJDBCDAO<Item, ItemID>
@Override @Override
public boolean insert(Item item) { public boolean insert(Item item) {
throw new UnsupportedOperationException( return database.query(new InsertUpdateQuery<Item>(item) {
"Saving items is not yet implemented!"); @Override
protected String query() {
return "INSERT INTO `" + TABLE + "` (`" + ITEM_ID + "`,`"
+ TEMPLATE_ID + "`,`" + CHAR_ID + "`,`" + LOCATION
+ "`,`" + PAPERDOLL + "`,`" + COUNT + "`,`" + COORD_X
+ "`,`" + COORD_Y + "`,`" + COORD_Z
+ "`) VALUES(?,?,?,?,?,?,?,?,?)";
}
@Override
protected void parametize(PreparedStatement st, Item item)
throws SQLException {
int i = 1;
st.setInt(i++, item.getID().getID());
st.setInt(i++, item.getTemplateID().getID());
if (item.getOwnerID() != null) {
st.setInt(i++, item.getOwnerID().getID());
} else {
st.setNull(i++, Types.INTEGER);
}
st.setString(i++, item.getLocation().name());
st.setString(i++, (item.getPaperdoll() != null ? item
.getPaperdoll().name() : null));
st.setLong(i++, item.getCount());
if (item.getPoint() != null) {
st.setInt(i++, item.getPoint().getX());
st.setInt(i++, item.getPoint().getY());
st.setInt(i++, item.getPoint().getZ());
} else {
st.setNull(i++, Types.INTEGER);
st.setNull(i++, Types.INTEGER);
st.setNull(i++, Types.INTEGER);
}
}
}) > 0;
} }
@Override @Override
public boolean update(Item item) { public boolean update(Item item) {
// public static final String ITEM_ID = "item_id";
// public static final String TEMPLATE_ID = "template_id";
// public static final String CHAR_ID = JDBCCharacterDAO.CHAR_ID;
//
// public static final String LOCATION = "location";
// public static final String PAPERDOLL = "paperdoll";
// public static final String COUNT = "count";
// public static final String COORD_X = "coord_x";
// public static final String COORD_Y = "coord_y";
// public static final String COORD_Z = "coord_z";
return database.query(new InsertUpdateQuery<Item>(item) { return database.query(new InsertUpdateQuery<Item>(item) {
@Override @Override
protected String query() { protected String query() {
@@ -285,16 +310,15 @@ public abstract class JDBCItemDAO extends AbstractJDBCDAO<Item, ItemID>
st.setInt(i++, item.getPoint().getY()); st.setInt(i++, item.getPoint().getY());
st.setInt(i++, item.getPoint().getZ()); st.setInt(i++, item.getPoint().getZ());
} else { } else {
st.setInt(i++, 0); st.setNull(i++, Types.INTEGER);
st.setInt(i++, 0); st.setNull(i++, Types.INTEGER);
st.setInt(i++, 0); st.setNull(i++, Types.INTEGER);
} }
// WHERE // WHERE
st.setInt(i++, item.getID().getID()); st.setInt(i++, item.getID().getID());
} }
}) > 0; }) > 0;
} }
@Override @Override

View File

@@ -52,6 +52,33 @@ public interface ItemService extends Service {
Item action(Item item, L2Character character, CharacterAction action) Item action(Item item, L2Character character, CharacterAction action)
throws ItemNotOnGroundServiceException, NotSpawnedServiceException; throws ItemNotOnGroundServiceException, NotSpawnedServiceException;
/**
* Splits this item in two pieces
*
* @param item
* the item to be splitted
* @param count
* the amount of items that will be decreased from the argument
* {@link Item} and added to the result one.
* @return the splitted item
* @throws NotEnoughItemsServiceException
* if <code>count</code> is bigger than {@link Item#getCount()}.
*/
Item split(Item item, long count) throws NotEnoughItemsServiceException;
/**
* Stacks several items into a single one. Items must be provided by the
* same template!
*
* @param items
* the items to be stacked
* @return the stacked item (it may be a totally new item or it might reuse
* another one)
* @throws NonStackableItemsServiceException
* if the item templates says they are not stackable
*/
Item stack(Item... items) throws NonStackableItemsServiceException;
/** /**
* Picks up an dropped item and places it into another players inventory * Picks up an dropped item and places it into another players inventory
* *
@@ -68,6 +95,35 @@ public interface ItemService extends Service {
Item pickUp(Item item, L2Character character) Item pickUp(Item item, L2Character character)
throws ItemNotOnGroundServiceException, NotSpawnedServiceException; throws ItemNotOnGroundServiceException, NotSpawnedServiceException;
/**
* Drops an item on the ground. If <code>actor</code> is not
* <code>null</code> he is flagged as the dropper actor.
*
* @param item
* the item
* @param count
* the number of items to be dropped. Will cause an split.
* @param point
* the drop point. Can be <code>null</code> if
* {@link Item#getPoint()} is set.
* @param actor
* the dropping actor. Can be <code>null</code>.
* @return <code>item</code> or another instance if the item was splitted.
* @throws ItemAlreadyOnGroundServiceException
* if the item is already dropped
* @throws AlreadySpawnedServiceException
* if the item is already spawned in the {@link WorldService}
* @throws SpawnPointNotFoundServiceException
* if <code>point</code> was <code>null</code> and
* {@link Item#getPoint()} was not set.
* @throws NotEnoughItemsServiceException
* if <code>count</code> is bigger than {@link Item#getCount()}.
*/
Item drop(Item item, long count, Point3D point, Actor actor)
throws ItemAlreadyOnGroundServiceException,
AlreadySpawnedServiceException, SpawnPointNotFoundServiceException,
NotEnoughItemsServiceException;
/** /**
* Drops an item on the ground. If <code>actor</code> is not * Drops an item on the ground. If <code>actor</code> is not
* <code>null</code> he is flagged as the dropper actor. * <code>null</code> he is flagged as the dropper actor.
@@ -86,8 +142,11 @@ public interface ItemService extends Service {
* @throws SpawnPointNotFoundServiceException * @throws SpawnPointNotFoundServiceException
* if <code>point</code> was <code>null</code> and * if <code>point</code> was <code>null</code> and
* {@link Item#getPoint()} was not set. * {@link Item#getPoint()} was not set.
* @throws NotEnoughItemsServiceException
* if <code>count</code> is bigger than {@link Item#getCount()}.
*/ */
void drop(Item item, Point3D point, Actor actor) void drop(Item item, Point3D point, Actor actor)
throws ItemAlreadyOnGroundServiceException, throws ItemAlreadyOnGroundServiceException,
AlreadySpawnedServiceException, SpawnPointNotFoundServiceException; AlreadySpawnedServiceException, SpawnPointNotFoundServiceException,
NotEnoughItemsServiceException;
} }

View File

@@ -16,15 +16,22 @@
*/ */
package com.l2jserver.service.game.item; package com.l2jserver.service.game.item;
import java.util.Arrays;
import java.util.List; import java.util.List;
import com.google.common.base.Preconditions;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.l2jserver.game.net.packet.client.CM_CHAR_ACTION.CharacterAction; import com.l2jserver.game.net.packet.client.CM_CHAR_ACTION.CharacterAction;
import com.l2jserver.model.Model.ObjectDesire;
import com.l2jserver.model.dao.ItemDAO; import com.l2jserver.model.dao.ItemDAO;
import com.l2jserver.model.id.object.ItemID;
import com.l2jserver.model.id.object.provider.ItemIDProvider;
import com.l2jserver.model.id.template.ItemTemplateID;
import com.l2jserver.model.world.Actor; import com.l2jserver.model.world.Actor;
import com.l2jserver.model.world.Item; import com.l2jserver.model.world.Item;
import com.l2jserver.model.world.L2Character; import com.l2jserver.model.world.L2Character;
import com.l2jserver.model.world.character.CharacterInventory.ItemLocation; import com.l2jserver.model.world.character.CharacterInventory.ItemLocation;
import com.l2jserver.model.world.item.ItemDropEvent;
import com.l2jserver.model.world.item.ItemPickUpEvent; import com.l2jserver.model.world.item.ItemPickUpEvent;
import com.l2jserver.service.AbstractService; import com.l2jserver.service.AbstractService;
import com.l2jserver.service.AbstractService.Depends; import com.l2jserver.service.AbstractService.Depends;
@@ -56,6 +63,10 @@ public class ItemServiceImpl extends AbstractService implements ItemService {
* The {@link WorldService} event dispatcher * The {@link WorldService} event dispatcher
*/ */
private final WorldEventDispatcher eventDispatcher; private final WorldEventDispatcher eventDispatcher;
/**
* The {@link ItemID} provider
*/
private final ItemIDProvider itemIdProvider;
/** /**
* All items on the ground persisted to the database * All items on the ground persisted to the database
@@ -69,13 +80,16 @@ public class ItemServiceImpl extends AbstractService implements ItemService {
* the spawn service * the spawn service
* @param eventDispatcher * @param eventDispatcher
* the world service event dispatcher * the world service event dispatcher
* @param itemIdProvider
* the {@link ItemID} provider
*/ */
@Inject @Inject
private ItemServiceImpl(ItemDAO itemDao, SpawnService spawnService, private ItemServiceImpl(ItemDAO itemDao, SpawnService spawnService,
WorldEventDispatcher eventDispatcher) { WorldEventDispatcher eventDispatcher, ItemIDProvider itemIdProvider) {
this.itemDao = itemDao; this.itemDao = itemDao;
this.spawnService = spawnService; this.spawnService = spawnService;
this.eventDispatcher = eventDispatcher; this.eventDispatcher = eventDispatcher;
this.itemIdProvider = itemIdProvider;
} }
@Override @Override
@@ -104,6 +118,44 @@ public class ItemServiceImpl extends AbstractService implements ItemService {
return item; return item;
} }
@Override
public Item split(Item item, long count)
throws NotEnoughItemsServiceException {
if (item.getCount() < count)
throw new NotEnoughItemsServiceException();
if (item.getCount() == count)
return item;
final Item splitItem = item.getTemplate().create();
splitItem.setID(itemIdProvider.createID());
splitItem.setCount(count);
item.setCount(item.getCount() - count);
splitItem.setObjectDesire(ObjectDesire.INSERT);
return splitItem;
}
@Override
public Item stack(Item... items) throws NonStackableItemsServiceException {
Preconditions.checkState(items.length >= 2,
"items length must be 2 or greater");
// TODO implement real item stacking
final ItemTemplateID templateID = items[0].getTemplateID();
for (final Item item : items) {
if (!item.getTemplateID().equals(templateID))
throw new NonStackableItemsServiceException();
}
final Item item = items[0];
for (int i = 1; i < items.length; i++) {
item.setCount(item.getCount() + items[i].getCount());
}
return item;
}
@Override @Override
public Item pickUp(Item item, L2Character character) public Item pickUp(Item item, L2Character character)
throws ItemNotOnGroundServiceException, NotSpawnedServiceException { throws ItemNotOnGroundServiceException, NotSpawnedServiceException {
@@ -111,15 +163,75 @@ public class ItemServiceImpl extends AbstractService implements ItemService {
if (item.getLocation() != ItemLocation.GROUND) if (item.getLocation() != ItemLocation.GROUND)
throw new ItemNotOnGroundServiceException(); throw new ItemNotOnGroundServiceException();
final Item originalItem = item;
item.setLocation(ItemLocation.INVENTORY); item.setLocation(ItemLocation.INVENTORY);
item.setPaperdoll(null); item.setPaperdoll(null);
item.setOwnerID(character.getID()); item.setOwnerID(character.getID());
final Item[] items = character.getInventory().getItems(
item.getTemplateID());
if (items.length != 0) {
Item[] stackItems = Arrays.copyOf(items, items.length + 1);
stackItems[items.length] = item;
try {
item = stack(stackItems);
Item[] removedItems = character.getInventory().remove(
stackItems);
for (final Item removeItem : removedItems) {
if (!removeItem.equals(item)) {
itemDao.delete(removeItem);
itemIdProvider.destroy(removeItem.getID());
}
}
character.getInventory().add(item);
} catch (NonStackableItemsServiceException e) {
character.getInventory().add(item);
}
} else {
character.getInventory().add(item);
}
character.getInventory().add(item); character.getInventory().add(item);
this.items.remove(item);
items.remove(item); itemDao.save(item);
spawnService.unspawn(originalItem);
eventDispatcher.dispatch(new ItemPickUpEvent(character,
originalItem, item));
spawnService.unspawn(item); return item;
eventDispatcher.dispatch(new ItemPickUpEvent(character, item)); }
}
@Override
public Item drop(Item item, long count, Point3D point, Actor actor)
throws SpawnPointNotFoundServiceException,
ItemAlreadyOnGroundServiceException,
AlreadySpawnedServiceException, NotEnoughItemsServiceException {
synchronized (item) {
if (item.getLocation() == ItemLocation.GROUND)
throw new AlreadySpawnedServiceException();
final Item sourceItem = item;
item = split(sourceItem, count);
item.setLocation(ItemLocation.GROUND);
item.setPaperdoll(null);
spawnService.spawn(item, point);
eventDispatcher.dispatch(new ItemDropEvent(actor, item));
if (actor instanceof L2Character) {
if (sourceItem.equals(item)) {
((L2Character) actor).getInventory().remove(item);
}
}
itemDao.save(item);
if (!item.equals(sourceItem)) {
itemDao.save(sourceItem);
}
items.add(item);
return item; return item;
} }
@@ -128,18 +240,9 @@ public class ItemServiceImpl extends AbstractService implements ItemService {
@Override @Override
public void drop(Item item, Point3D point, Actor actor) public void drop(Item item, Point3D point, Actor actor)
throws SpawnPointNotFoundServiceException, throws SpawnPointNotFoundServiceException,
ItemAlreadyOnGroundServiceException, AlreadySpawnedServiceException { ItemAlreadyOnGroundServiceException,
synchronized (item) { AlreadySpawnedServiceException, NotEnoughItemsServiceException {
if (item.getLocation() == ItemLocation.GROUND) drop(item, item.getCount(), point, actor);
throw new AlreadySpawnedServiceException();
item.setLocation(ItemLocation.GROUND);
item.setPaperdoll(null);
// point will be set here
spawnService.spawn(item, point);
items.add(item);
}
} }
@Override @Override

View File

@@ -0,0 +1,59 @@
/*
* This file is part of l2jserver2 <l2jserver2.com>.
*
* l2jserver2 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.
*
* l2jserver2 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 l2jserver2. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.game.item;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class NonStackableItemsServiceException extends ItemServiceException {
/**
* Java Serialization API ID
*/
private static final long serialVersionUID = 1L;
/**
* Creates a new instance
*/
public NonStackableItemsServiceException() {
}
/**
* @param message
* the message
* @param cause
* the cause
*/
public NonStackableItemsServiceException(String message, Throwable cause) {
super(message, cause);
}
/**
* @param message
* the message
*/
public NonStackableItemsServiceException(String message) {
super(message);
}
/**
* @param cause
* the cause
*/
public NonStackableItemsServiceException(Throwable cause) {
super(cause);
}
}

View File

@@ -0,0 +1,59 @@
/*
* This file is part of l2jserver2 <l2jserver2.com>.
*
* l2jserver2 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.
*
* l2jserver2 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 l2jserver2. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.game.item;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public class NotEnoughItemsServiceException extends ItemServiceException {
/**
* Java Serialization API ID
*/
private static final long serialVersionUID = 1L;
/**
* Creates a new instance
*/
public NotEnoughItemsServiceException() {
}
/**
* @param message
* the message
* @param cause
* the cause
*/
public NotEnoughItemsServiceException(String message, Throwable cause) {
super(message, cause);
}
/**
* @param message
* the message
*/
public NotEnoughItemsServiceException(String message) {
super(message);
}
/**
* @param cause
* the cause
*/
public NotEnoughItemsServiceException(Throwable cause) {
super(cause);
}
}

View File

@@ -30,7 +30,10 @@ import com.l2jserver.model.world.player.event.PlayerTeleportedEvent;
import com.l2jserver.service.Service; import com.l2jserver.service.Service;
import com.l2jserver.service.core.threading.AsyncFuture; import com.l2jserver.service.core.threading.AsyncFuture;
import com.l2jserver.service.core.threading.ThreadService; import com.l2jserver.service.core.threading.ThreadService;
import com.l2jserver.service.game.character.CharacterService;
import com.l2jserver.service.game.item.ItemService;
import com.l2jserver.service.game.npc.NPCService; import com.l2jserver.service.game.npc.NPCService;
import com.l2jserver.service.game.world.WorldService;
import com.l2jserver.util.geometry.Coordinate; import com.l2jserver.util.geometry.Coordinate;
import com.l2jserver.util.geometry.Point3D; import com.l2jserver.util.geometry.Point3D;
@@ -52,6 +55,12 @@ import com.l2jserver.util.geometry.Point3D;
* do, the {@link NPC} will not be respawned. The only possible way to respawn * do, the {@link NPC} will not be respawned. The only possible way to respawn
* it is through an forced spawn (manual) or server restart. See * it is through an forced spawn (manual) or server restart. See
* {@link NPCService} if you wish to correctly unspawn an {@link NPC}. * {@link NPCService} if you wish to correctly unspawn an {@link NPC}.
* <p>
* Also note that this service is a low level implementation. In most cases, it
* might be more suitable the usage of high-level services, like
* {@link ItemService}, {@link NPCService} or {@link CharacterService}, since
* they can provide more security and dispatch more events to the
* {@link WorldService}.
* *
* @author <a href="http://www.rogiel.com">Rogiel</a> * @author <a href="http://www.rogiel.com">Rogiel</a>
*/ */

View File

@@ -46,6 +46,7 @@ import com.l2jserver.model.world.character.event.CharacterLeaveWorldEvent;
import com.l2jserver.model.world.character.event.CharacterMoveEvent; import com.l2jserver.model.world.character.event.CharacterMoveEvent;
import com.l2jserver.model.world.character.event.CharacterRunningEvent; import com.l2jserver.model.world.character.event.CharacterRunningEvent;
import com.l2jserver.model.world.character.event.CharacterWalkingEvent; import com.l2jserver.model.world.character.event.CharacterWalkingEvent;
import com.l2jserver.model.world.item.ItemDropEvent;
import com.l2jserver.model.world.item.ItemPickUpEvent; import com.l2jserver.model.world.item.ItemPickUpEvent;
import com.l2jserver.model.world.npc.event.NPCSpawnEvent; import com.l2jserver.model.world.npc.event.NPCSpawnEvent;
import com.l2jserver.model.world.player.event.PlayerTeleportedEvent; import com.l2jserver.model.world.player.event.PlayerTeleportedEvent;
@@ -115,7 +116,7 @@ public class BroadcastServiceImpl extends AbstractService implements
@Override @Override
protected boolean dispatch(WorldEvent e, PositionableObject object) { protected boolean dispatch(WorldEvent e, PositionableObject object) {
log.debug("Broadcast event received: {}", e); log.debug("Broadcast event received: {}", e);
if (e instanceof NPCSpawnEvent) { if (e instanceof NPCSpawnEvent || e instanceof ItemDropEvent) {
broadcast(conn, e.getObject()); broadcast(conn, e.getObject());
} else if (e instanceof CharacterMoveEvent) { } else if (e instanceof CharacterMoveEvent) {
final CharacterMoveEvent evt = (CharacterMoveEvent) e; final CharacterMoveEvent evt = (CharacterMoveEvent) e;
@@ -129,7 +130,7 @@ public class BroadcastServiceImpl extends AbstractService implements
|| e instanceof ActorUnspawnEvent || e instanceof ActorUnspawnEvent
|| e instanceof ItemPickUpEvent) { || e instanceof ItemPickUpEvent) {
// object is now out of sight // object is now out of sight
//FIXME pick up animation is not happening // FIXME pick up animation is not happening
conn.write(new SM_OBJECT_REMOVE(object)); conn.write(new SM_OBJECT_REMOVE(object));
} else if (e instanceof CharacterWalkingEvent) { } else if (e instanceof CharacterWalkingEvent) {
conn.write(new SM_MOVE_TYPE(((CharacterWalkingEvent) e) conn.write(new SM_MOVE_TYPE(((CharacterWalkingEvent) e)