diff --git a/gameserver/src/main/java/brainwine/gameserver/entity/player/Placement.java b/gameserver/src/main/java/brainwine/gameserver/entity/player/Placement.java new file mode 100644 index 0000000..aa2411f --- /dev/null +++ b/gameserver/src/main/java/brainwine/gameserver/entity/player/Placement.java @@ -0,0 +1,32 @@ +package brainwine.gameserver.entity.player; + +import brainwine.gameserver.item.Item; + +/** + * Simple model for keeping track of the last placed item of a player. + * Mainly used for linking switches to doors, trapdoors etc. + */ +public class Placement { + + private final int x; + private final int y; + private final Item item; + + public Placement(int x, int y, Item item) { + this.x = x; + this.y = y; + this.item = item; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public Item getItem() { + return item; + } +} diff --git a/gameserver/src/main/java/brainwine/gameserver/entity/player/Player.java b/gameserver/src/main/java/brainwine/gameserver/entity/player/Player.java index 56b3ec2..00ec473 100644 --- a/gameserver/src/main/java/brainwine/gameserver/entity/player/Player.java +++ b/gameserver/src/main/java/brainwine/gameserver/entity/player/Player.java @@ -3,6 +3,7 @@ package brainwine.gameserver.entity.player; import java.beans.ConstructorProperties; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -26,6 +27,8 @@ import brainwine.gameserver.entity.EntityType; import brainwine.gameserver.entity.FacingDirection; import brainwine.gameserver.item.Item; import brainwine.gameserver.item.ItemRegistry; +import brainwine.gameserver.item.ItemUseType; +import brainwine.gameserver.item.Layer; import brainwine.gameserver.item.LootGraphic; import brainwine.gameserver.loot.Loot; import brainwine.gameserver.server.Message; @@ -51,6 +54,7 @@ import brainwine.gameserver.server.messages.ZoneStatusMessage; import brainwine.gameserver.server.pipeline.Connection; import brainwine.gameserver.server.requests.BlocksIgnoreRequest; import brainwine.gameserver.server.requests.BlocksRequest; +import brainwine.gameserver.util.MapHelper; import brainwine.gameserver.util.MathUtils; import brainwine.gameserver.zone.Chunk; import brainwine.gameserver.zone.MetaBlock; @@ -103,6 +107,7 @@ public class Player extends Entity implements CommandExecutor { private final Map activeChunks = new HashMap<>(); private final Map dialogs = new HashMap<>(); private String clientVersion; + private Placement lastPlacement; private Item heldItem = Item.AIR; private int teleportX; private int teleportY; @@ -243,6 +248,7 @@ public class Player extends Entity implements CommandExecutor { */ public void onDisconnect() { lastHeartbeat = 0; + lastPlacement = null; if(zone != null) { zone.removePlayer(this); @@ -418,6 +424,53 @@ public class Player extends Entity implements CommandExecutor { return heldItem; } + public void trackPlacement(int x, int y, Item item) { + if(item.getUses().isEmpty() || !zone.areCoordinatesInBounds(x, y)) { + return; + } + + boolean linked = false; + + if(lastPlacement != null) { + if(item.hasUse(ItemUseType.SWITCHED) && !item.hasUse(ItemUseType.SWITCH)) { + linked = tryLinkSwitchedItem(x, y, item); + } + } + + if(!linked) { + lastPlacement = new Placement(x, y, item); + } + } + + private boolean tryLinkSwitchedItem(int x, int y, Item item) { + int pX = lastPlacement.getX(); + int pY = lastPlacement.getY(); + Item pItem = lastPlacement.getItem(); + boolean linked = false; + + if(pItem.hasUse(ItemUseType.SWITCH)) { + MetaBlock metaBlock = zone.getMetaBlock(pX, pY); + Map metadata = metaBlock == null ? null : metaBlock.getMetadata(); + + if(metadata != null) { + MapHelper.appendList(metadata, ">", Arrays.asList(x, y)); + + if(!(item.getUse(ItemUseType.SWITCHED) instanceof String)) { + int mod = zone.getBlock(pX, pY).getFrontMod(); + zone.updateBlock(x, y, Layer.FRONT, item, mod, null, null); + } + + linked = true; + + if(!pItem.hasUse(ItemUseType.MULTI) || MapHelper.getList(metadata, ">", Collections.emptyList()).size() >= 20) { + lastPlacement = null; + } + } + } + + return linked; + } + public double getMiningRange() { return MathUtils.lerp(3.0, 5.0, (double)getSkillLevel(Skill.MINING) / MAX_SKILL_LEVEL); } diff --git a/gameserver/src/main/java/brainwine/gameserver/item/Item.java b/gameserver/src/main/java/brainwine/gameserver/item/Item.java index ec54c9a..58bda42 100644 --- a/gameserver/src/main/java/brainwine/gameserver/item/Item.java +++ b/gameserver/src/main/java/brainwine/gameserver/item/Item.java @@ -236,6 +236,10 @@ public class Item { return false; } + public Object getUse(ItemUseType type) { + return useConfigs.get(type); + } + public Map getUses() { return useConfigs; } diff --git a/gameserver/src/main/java/brainwine/gameserver/item/ItemUseType.java b/gameserver/src/main/java/brainwine/gameserver/item/ItemUseType.java index 4f905e6..573b519 100644 --- a/gameserver/src/main/java/brainwine/gameserver/item/ItemUseType.java +++ b/gameserver/src/main/java/brainwine/gameserver/item/ItemUseType.java @@ -12,6 +12,9 @@ public enum ItemUseType { CHANGE, FIELDABLE, FLY, + MULTI, + SWITCH, + SWITCHED, TELEPORT, ZONE_TELEPORT, diff --git a/gameserver/src/main/java/brainwine/gameserver/server/requests/BlockMineRequest.java b/gameserver/src/main/java/brainwine/gameserver/server/requests/BlockMineRequest.java index d3930a1..3f4b44c 100644 --- a/gameserver/src/main/java/brainwine/gameserver/server/requests/BlockMineRequest.java +++ b/gameserver/src/main/java/brainwine/gameserver/server/requests/BlockMineRequest.java @@ -1,15 +1,22 @@ package brainwine.gameserver.server.requests; +import java.util.Collections; +import java.util.List; +import java.util.Map; + import brainwine.gameserver.entity.player.Player; import brainwine.gameserver.item.Action; import brainwine.gameserver.item.Item; +import brainwine.gameserver.item.ItemUseType; import brainwine.gameserver.item.Layer; import brainwine.gameserver.item.ModType; import brainwine.gameserver.server.PlayerRequest; import brainwine.gameserver.server.messages.BlockChangeMessage; import brainwine.gameserver.server.messages.InventoryMessage; +import brainwine.gameserver.util.MapHelper; import brainwine.gameserver.util.MathUtils; import brainwine.gameserver.zone.Block; +import brainwine.gameserver.zone.MetaBlock; import brainwine.gameserver.zone.Zone; public class BlockMineRequest extends PlayerRequest { @@ -36,6 +43,7 @@ public class BlockMineRequest extends PlayerRequest { } Block block = zone.getBlock(x, y); + MetaBlock metaBlock = zone.getMetaBlock(x, y); if(block.getItem(layer) != item) { fail(player, "Could not find the item you're trying to mine."); @@ -57,6 +65,32 @@ public class BlockMineRequest extends PlayerRequest { return; } + if(metaBlock != null) { + Map metadata = metaBlock.getMetadata(); + + if(!metaBlock.hasOwner() && item.hasUse(ItemUseType.SWITCH)) { + List> positions = MapHelper.getList(metadata, ">", Collections.emptyList()); + + for(List position : positions) { + Block target = zone.getBlock(position.get(0), position.get(1)); + + if(target != null) { + Item switchedItem = target.getFrontItem(); + + if(switchedItem.hasUse(ItemUseType.SWITCHED)) { + fail(player, String.format("This switch cannot be mined before its %s.", switchedItem.getTitle().toLowerCase())); + return; + } + } + } + } + + if(item.hasUse(ItemUseType.CONTAINER) && metadata.containsKey("$")) { + fail(player, "Can't mine a container with loot in it."); + return; + } + } + Item inventoryItem = item.getMod() == ModType.DECAY && block.getMod(layer) > 0 ? item.getDecayInventoryItem() : item.getInventoryItem(); zone.updateBlock(x, y, layer, 0, 0, player); diff --git a/gameserver/src/main/java/brainwine/gameserver/server/requests/BlockPlaceRequest.java b/gameserver/src/main/java/brainwine/gameserver/server/requests/BlockPlaceRequest.java index 848d96b..193048b 100644 --- a/gameserver/src/main/java/brainwine/gameserver/server/requests/BlockPlaceRequest.java +++ b/gameserver/src/main/java/brainwine/gameserver/server/requests/BlockPlaceRequest.java @@ -68,6 +68,7 @@ public class BlockPlaceRequest extends PlayerRequest { zone.updateBlock(x, y, layer, item, mod, player); player.getInventory().removeItem(item); + player.trackPlacement(x, y, item); } private void fail(Player player, String reason) {