mirror of
https://github.com/2006-Scape/apollo.git
synced 2026-07-07 08:39:17 +00:00
Initial implementation of ground items
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
plugin {
|
||||||
|
name = "ground_item"
|
||||||
|
packageName = "org.apollo.game.plugin.entity"
|
||||||
|
authors = ["Ryley"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import org.apollo.game.message.impl.InventoryItemMessage
|
||||||
|
|
||||||
|
on { InventoryItemMessage::class }
|
||||||
|
.where { option == 5 }
|
||||||
|
.then {
|
||||||
|
// This is just a stub, for now.
|
||||||
|
// Several other things need to be done here:
|
||||||
|
// - Items may only be dropped from your inventory
|
||||||
|
// - Items dropped must be removed from your inventory
|
||||||
|
// - Items must be checked to ensure they have a 'drop' option
|
||||||
|
val item = it.inventory.get(slot)
|
||||||
|
it.addGroundItem(item, it.position)
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import org.apollo.game.GameConstants
|
||||||
|
import org.apollo.game.model.entity.GroundItem
|
||||||
|
import org.apollo.game.scheduling.ScheduledTask
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [ScheduledTask] that manages the globalization and expiration of [GroundItem]s.
|
||||||
|
*/
|
||||||
|
class GroundItemSynchronizationTask(private val groundItem: GroundItem) : ScheduledTask(DELAY, false) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The delay between executions of this task, in pulses.
|
||||||
|
*/
|
||||||
|
const val DELAY = 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The amount of time, in pulses, in which this [GroundItem] will be globally visible.
|
||||||
|
*/
|
||||||
|
const val TRADABLE_TIME_UNTIL_GLOBAL = 60000 / GameConstants.PULSE_DELAY
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The amount of time, in pulses, in which this [GroundItem] will expire and be removed from the [World].
|
||||||
|
*/
|
||||||
|
const val UNTRADABLE_TIME_UNTIL_EXPIRE = 180000 / GameConstants.PULSE_DELAY
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The amount of time, in pulses, in which this [GroundItem] will expire and be removed from the [World].
|
||||||
|
*/
|
||||||
|
const val TIME_UNTIL_EXPIRE = 180000 / GameConstants.PULSE_DELAY
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The amount of game pulses this [ScheduledTask] has been alive.
|
||||||
|
*/
|
||||||
|
private var pulses = 0
|
||||||
|
|
||||||
|
override fun execute() {
|
||||||
|
val world = groundItem.world
|
||||||
|
val owner = world.playerRepository[groundItem.ownerIndex]
|
||||||
|
val untradable = false // TODO: item.getDefinition().isTradable();
|
||||||
|
|
||||||
|
if (!groundItem.isAvailable) {
|
||||||
|
stop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Untradable items never go global
|
||||||
|
if (untradable) {
|
||||||
|
if (pulses >= UNTRADABLE_TIME_UNTIL_EXPIRE) {
|
||||||
|
world.removeGroundItem(owner, groundItem)
|
||||||
|
stop()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groundItem.isGlobal) {
|
||||||
|
if (pulses >= TIME_UNTIL_EXPIRE) {
|
||||||
|
world.removeGroundItem(owner, groundItem)
|
||||||
|
stop()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (pulses >= TRADABLE_TIME_UNTIL_GLOBAL) {
|
||||||
|
groundItem.globalize()
|
||||||
|
world.addGroundItem(owner, groundItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pulses++
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import org.apollo.game.message.impl.RemoveTileItemMessage
|
||||||
|
import org.apollo.game.message.impl.SendTileItemMessage
|
||||||
|
import org.apollo.game.model.Item
|
||||||
|
import org.apollo.game.model.Position
|
||||||
|
import org.apollo.game.model.World
|
||||||
|
import org.apollo.game.model.entity.GroundItem
|
||||||
|
import org.apollo.game.model.entity.Player
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spawns a new local [GroundItem] for this Player at the specified [Position].
|
||||||
|
*/
|
||||||
|
fun Player.addGroundItem(item: Item, position: Position) {
|
||||||
|
world.addGroundItem(this, GroundItem.dropped(world, position, item, this))
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun World.addGroundItem(player: Player, item: GroundItem) {
|
||||||
|
val region = regionRepository.fromPosition(item.position)
|
||||||
|
|
||||||
|
if (item.isGlobal) {
|
||||||
|
region.addEntity(item, true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
groundItems.computeIfAbsent(player.encodedName, { HashSet<GroundItem>() }) += item
|
||||||
|
|
||||||
|
val offset = region.getPositionOffset(item)
|
||||||
|
player.send(SendTileItemMessage(item.item, offset))
|
||||||
|
|
||||||
|
schedule(GroundItemSynchronizationTask(item))
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun World.removeGroundItem(player: Player, item: GroundItem) {
|
||||||
|
val region = regionRepository.fromPosition(item.position)
|
||||||
|
|
||||||
|
if (item.isGlobal) {
|
||||||
|
region.removeEntity(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
val items = groundItems[player.encodedName] ?: return
|
||||||
|
items -= item
|
||||||
|
|
||||||
|
val offset = region.getPositionOffset(item)
|
||||||
|
player.send(RemoveTileItemMessage(item.item, offset))
|
||||||
|
}
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
package org.apollo.game.model;
|
package org.apollo.game.model;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import org.apollo.Service;
|
import org.apollo.Service;
|
||||||
import org.apollo.cache.IndexedFileSystem;
|
import org.apollo.cache.IndexedFileSystem;
|
||||||
@@ -19,11 +16,7 @@ import org.apollo.game.model.area.Region;
|
|||||||
import org.apollo.game.model.area.RegionRepository;
|
import org.apollo.game.model.area.RegionRepository;
|
||||||
import org.apollo.game.model.area.collision.CollisionManager;
|
import org.apollo.game.model.area.collision.CollisionManager;
|
||||||
import org.apollo.game.model.area.collision.CollisionUpdateListener;
|
import org.apollo.game.model.area.collision.CollisionUpdateListener;
|
||||||
import org.apollo.game.model.entity.Entity;
|
import org.apollo.game.model.entity.*;
|
||||||
import org.apollo.game.model.entity.EntityType;
|
|
||||||
import org.apollo.game.model.entity.MobRepository;
|
|
||||||
import org.apollo.game.model.entity.Npc;
|
|
||||||
import org.apollo.game.model.entity.Player;
|
|
||||||
import org.apollo.game.model.event.Event;
|
import org.apollo.game.model.event.Event;
|
||||||
import org.apollo.game.model.event.EventListener;
|
import org.apollo.game.model.event.EventListener;
|
||||||
import org.apollo.game.model.event.EventListenerChainSet;
|
import org.apollo.game.model.event.EventListenerChainSet;
|
||||||
@@ -33,6 +26,9 @@ import org.apollo.game.scheduling.Scheduler;
|
|||||||
import org.apollo.game.scheduling.impl.NpcMovementTask;
|
import org.apollo.game.scheduling.impl.NpcMovementTask;
|
||||||
import org.apollo.util.NameUtil;
|
import org.apollo.util.NameUtil;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The world class is a singleton which contains objects like the {@link MobRepository} for players and NPCs. It should
|
* The world class is a singleton which contains objects like the {@link MobRepository} for players and NPCs. It should
|
||||||
* only contain things relevant to the in-game world and not classes which deal with I/O and such (these may be better
|
* only contain things relevant to the in-game world and not classes which deal with I/O and such (these may be better
|
||||||
@@ -96,6 +92,11 @@ public final class World {
|
|||||||
*/
|
*/
|
||||||
private final MobRepository<Player> playerRepository = new MobRepository<>(WorldConstants.MAXIMUM_PLAYERS);
|
private final MobRepository<Player> playerRepository = new MobRepository<>(WorldConstants.MAXIMUM_PLAYERS);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link Map} of player usernames to their local {@link Set} of {@link GroundItem}s.
|
||||||
|
*/
|
||||||
|
private final Map<Long, Set<GroundItem>> groundItems = new HashMap<>(WorldConstants.MAXIMUM_PLAYERS);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link Map} of player usernames and the player objects.
|
* A {@link Map} of player usernames and the player objects.
|
||||||
*/
|
*/
|
||||||
@@ -181,6 +182,15 @@ public final class World {
|
|||||||
return playerRepository;
|
return playerRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link Map} of player usernames to their {@link Set} of {@link GroundItem}s
|
||||||
|
*
|
||||||
|
* @return The map.
|
||||||
|
*/
|
||||||
|
public Map<Long, Set<GroundItem>> getGroundItems() {
|
||||||
|
return groundItems;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the plugin manager.
|
* Gets the plugin manager.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import org.apollo.game.model.area.update.UpdateOperation;
|
|||||||
import org.apollo.game.model.entity.Entity;
|
import org.apollo.game.model.entity.Entity;
|
||||||
import org.apollo.game.model.entity.EntityType;
|
import org.apollo.game.model.entity.EntityType;
|
||||||
import org.apollo.game.model.entity.obj.DynamicGameObject;
|
import org.apollo.game.model.entity.obj.DynamicGameObject;
|
||||||
|
import org.apollo.game.model.entity.obj.StaticGameObject;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -200,8 +201,8 @@ public final class Region {
|
|||||||
*/
|
*/
|
||||||
public Set<RegionUpdateMessage> encode(int height) {
|
public Set<RegionUpdateMessage> encode(int height) {
|
||||||
Set<RegionUpdateMessage> additions = entities.values().stream()
|
Set<RegionUpdateMessage> additions = entities.values().stream()
|
||||||
.flatMap(Set::stream) // TODO fix this to work for ground items + projectiles
|
.flatMap(Set::stream) // TODO: Stop hurting my eyeballs.
|
||||||
.filter(entity -> entity instanceof DynamicGameObject && entity.getPosition().getHeight() == height)
|
.filter(entity -> !entity.getEntityType().isMob() && !(entity instanceof StaticGameObject) && (!(entity instanceof DynamicGameObject) || entity.getPosition().getHeight() == height))
|
||||||
.map(entity -> ((GroupableEntity) entity).toUpdateOperation(this, EntityUpdateType.ADD).toMessage())
|
.map(entity -> ((GroupableEntity) entity).toUpdateOperation(this, EntityUpdateType.ADD).toMessage())
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
@@ -269,6 +270,33 @@ public final class Region {
|
|||||||
return ImmutableSet.copyOf(filtered);
|
return ImmutableSet.copyOf(filtered);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the position offset for the specified {@link Entity}.
|
||||||
|
*
|
||||||
|
* @param entity The Entity.
|
||||||
|
* @return The position offset.
|
||||||
|
*/
|
||||||
|
public int getPositionOffset(Entity entity) {
|
||||||
|
return getPositionOffset(entity.getPosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the position offset for the specified {@link Position}.
|
||||||
|
*
|
||||||
|
* @param position The Entity.
|
||||||
|
* @return The position offset.
|
||||||
|
*/
|
||||||
|
public int getPositionOffset(Position position) {
|
||||||
|
RegionCoordinates coordinates = getCoordinates();
|
||||||
|
int dx = position.getX() - coordinates.getAbsoluteX();
|
||||||
|
int dy = position.getY() - coordinates.getAbsoluteY();
|
||||||
|
|
||||||
|
Preconditions.checkArgument(dx >= 0 && dx < Region.SIZE, position + " not in expected Region of " + toString() + ".");
|
||||||
|
Preconditions.checkArgument(dy >= 0 && dy < Region.SIZE, position + " not in expected Region of " + toString() + ".");
|
||||||
|
|
||||||
|
return dx << 4 | dy;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the {@link Set} of {@link RegionCoordinates} of Regions that are viewable from the specified
|
* Gets the {@link Set} of {@link RegionCoordinates} of Regions that are viewable from the specified
|
||||||
* {@link Position}.
|
* {@link Position}.
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ public abstract class UpdateOperation<E extends Entity> {
|
|||||||
* @return The RegionUpdateMessage.
|
* @return The RegionUpdateMessage.
|
||||||
*/
|
*/
|
||||||
public final RegionUpdateMessage inverse() {
|
public final RegionUpdateMessage inverse() {
|
||||||
int offset = getPositionOffset(entity.getPosition());
|
int offset = region.getPositionOffset(entity);
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ADD:
|
case ADD:
|
||||||
@@ -71,7 +71,7 @@ public abstract class UpdateOperation<E extends Entity> {
|
|||||||
* @return The Message.
|
* @return The Message.
|
||||||
*/
|
*/
|
||||||
public final RegionUpdateMessage toMessage() {
|
public final RegionUpdateMessage toMessage() {
|
||||||
int offset = getPositionOffset(entity.getPosition());
|
int offset = region.getPositionOffset(entity);
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ADD:
|
case ADD:
|
||||||
@@ -99,21 +99,4 @@ public abstract class UpdateOperation<E extends Entity> {
|
|||||||
*/
|
*/
|
||||||
protected abstract RegionUpdateMessage remove(int offset);
|
protected abstract RegionUpdateMessage remove(int offset);
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the position offset for the specified {@link Position}.
|
|
||||||
*
|
|
||||||
* @param position The Position.
|
|
||||||
* @return The position offset.
|
|
||||||
*/
|
|
||||||
private final int getPositionOffset(Position position) {
|
|
||||||
RegionCoordinates coordinates = region.getCoordinates();
|
|
||||||
int dx = position.getX() - coordinates.getAbsoluteX();
|
|
||||||
int dy = position.getY() - coordinates.getAbsoluteY();
|
|
||||||
|
|
||||||
Preconditions.checkArgument(dx >= 0 && dx < Region.SIZE, position + " not in expected Region of " + region + ".");
|
|
||||||
Preconditions.checkArgument(dy >= 0 && dy < Region.SIZE, position + " not in expected Region of " + region + ".");
|
|
||||||
|
|
||||||
return dx << 4 | dy;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -9,6 +9,8 @@ import org.apollo.game.model.area.update.GroupableEntity;
|
|||||||
import org.apollo.game.model.area.update.ItemUpdateOperation;
|
import org.apollo.game.model.area.update.ItemUpdateOperation;
|
||||||
import org.apollo.game.model.area.update.UpdateOperation;
|
import org.apollo.game.model.area.update.UpdateOperation;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link Item} displayed on the ground.
|
* An {@link Item} displayed on the ground.
|
||||||
*
|
*
|
||||||
@@ -17,19 +19,19 @@ import org.apollo.game.model.area.update.UpdateOperation;
|
|||||||
public final class GroundItem extends Entity implements GroupableEntity {
|
public final class GroundItem extends Entity implements GroupableEntity {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new GroundItem.
|
* Creates a new <strong>global</strong> GroundItem.
|
||||||
*
|
*
|
||||||
* @param world The {@link World} containing the GroundItem.
|
* @param world The {@link World} containing the GroundItem.
|
||||||
* @param position The {@link Position} of the Item.
|
* @param position The {@link Position} of the Item.
|
||||||
* @param item The Item displayed on the ground.
|
* @param item The Item displayed on the ground.
|
||||||
* @return The GroundItem.
|
* @return The GroundItem.
|
||||||
*/
|
*/
|
||||||
public static GroundItem create(World world, Position position, Item item) {
|
public static GroundItem createGlobal(World world, Position position, Item item) {
|
||||||
return new GroundItem(world, position, item, -1);
|
return new GroundItem(world, position, item, -1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new dropped GroundItem.
|
* Creates a new <strong>non-global</strong> dropped GroundItem.
|
||||||
*
|
*
|
||||||
* @param world The {@link World} containing the GroundItem.
|
* @param world The {@link World} containing the GroundItem.
|
||||||
* @param position The {@link Position} of the Item.
|
* @param position The {@link Position} of the Item.
|
||||||
@@ -38,7 +40,7 @@ public final class GroundItem extends Entity implements GroupableEntity {
|
|||||||
* @return The GroundItem.
|
* @return The GroundItem.
|
||||||
*/
|
*/
|
||||||
public static GroundItem dropped(World world, Position position, Item item, Player owner) {
|
public static GroundItem dropped(World world, Position position, Item item, Player owner) {
|
||||||
return new GroundItem(world, position, item, owner.getIndex());
|
return new GroundItem(world, position, item, owner.getIndex(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -51,6 +53,16 @@ public final class GroundItem extends Entity implements GroupableEntity {
|
|||||||
*/
|
*/
|
||||||
private final Item item;
|
private final Item item;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the global state of this GroundItem.
|
||||||
|
*/
|
||||||
|
private boolean global;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the availability state of this GroundItem.
|
||||||
|
*/
|
||||||
|
private boolean available = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the GroundItem.
|
* Creates the GroundItem.
|
||||||
*
|
*
|
||||||
@@ -58,18 +70,20 @@ public final class GroundItem extends Entity implements GroupableEntity {
|
|||||||
* @param position The {@link Position} of the GroundItem.
|
* @param position The {@link Position} of the GroundItem.
|
||||||
* @param item The Item displayed on the ground.
|
* @param item The Item displayed on the ground.
|
||||||
* @param index The index of the {@link Player} who dropped this GroundItem.
|
* @param index The index of the {@link Player} who dropped this GroundItem.
|
||||||
|
* @param global The global state of this GroundItem.
|
||||||
*/
|
*/
|
||||||
private GroundItem(World world, Position position, Item item, int index) {
|
private GroundItem(World world, Position position, Item item, int index, boolean global) {
|
||||||
super(world, position);
|
super(world, position);
|
||||||
this.item = item;
|
this.item = item;
|
||||||
this.index = index;
|
this.index = index;
|
||||||
|
this.global = global;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (obj instanceof GroundItem) {
|
if (obj instanceof GroundItem) {
|
||||||
GroundItem other = (GroundItem) obj;
|
GroundItem other = (GroundItem) obj;
|
||||||
return position.equals(other.position);
|
return item.equals(other.item) && position.equals(other.position) && global == other.global;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -99,9 +113,48 @@ public final class GroundItem extends Entity implements GroupableEntity {
|
|||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the global state of this GroundItem.
|
||||||
|
*
|
||||||
|
* @return {@code true} iff this GroundItem is global.
|
||||||
|
*/
|
||||||
|
public boolean isGlobal() {
|
||||||
|
return global;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Globalizes this GroundItem.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException If this GroundItem is already global.
|
||||||
|
*/
|
||||||
|
public void globalize() {
|
||||||
|
if (global) {
|
||||||
|
throw new IllegalStateException("Ground item state has already been set to global.");
|
||||||
|
}
|
||||||
|
global = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the availability of this GroundItem.
|
||||||
|
*
|
||||||
|
* @return {@code true} iff this GroundItem is available to be picked up.
|
||||||
|
*/
|
||||||
|
public boolean isAvailable() {
|
||||||
|
return available;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether or not this GroundItem is available.
|
||||||
|
*
|
||||||
|
* @param available {@code true} if this GroundItem is available to be picked up, otherwise {@code false}.
|
||||||
|
*/
|
||||||
|
public void setAvailable(boolean available) {
|
||||||
|
this.available = available;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return position.hashCode() * 31 + item.hashCode();
|
return Objects.hash(item, position, global);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user