From cecdb97b195529437d0e4c2b0d2eb81c1d841b0b Mon Sep 17 00:00:00 2001 From: randy Date: Tue, 13 May 2025 14:29:51 -0600 Subject: [PATCH] Several Housing improvements Can enter house from any house portal in the world, and can exit to any portal the owner has the construction level to move their house to. Can enter house even if owner is not home. House is no longer unloaded when the owner leaves, instead is unloaded when the last player leaves. Guests are kicked when owner logs out. Locking house persists after logout/login. Using the Building mode buttons when outside the house allows remotely locking/unlocking the house. Teleport to House and Tablet now teleport to the last house that was entered through a house portal, if it is online and unlocked. Falls back to your own house if that fails. --- .../item/HouseTeleTabOptionListener.kt | 24 +++- .../skill/construction/HouseManager.java | 7 +- .../global/skill/construction/HouseZone.java | 4 +- .../construction/PortalOptionPlugin.java | 121 +++++++++++++++++- .../skill/magic/modern/ModernListeners.kt | 24 +++- .../core/game/node/entity/player/Player.java | 1 + .../entity/player/info/login/PlayerSaver.kt | 1 + 7 files changed, 165 insertions(+), 17 deletions(-) diff --git a/Server/src/main/content/global/handlers/item/HouseTeleTabOptionListener.kt b/Server/src/main/content/global/handlers/item/HouseTeleTabOptionListener.kt index 61ac1b029..ee6e3f1b8 100644 --- a/Server/src/main/content/global/handlers/item/HouseTeleTabOptionListener.kt +++ b/Server/src/main/content/global/handlers/item/HouseTeleTabOptionListener.kt @@ -5,24 +5,34 @@ import core.game.interaction.IntType import core.game.interaction.InteractionListener import core.game.node.entity.player.link.TeleportManager import core.game.node.item.Item +import core.game.node.entity.player.Player +import core.game.world.repository.Repository class HouseTeleTabOptionListener : InteractionListener { override fun defineListeners() { val homeTabID = 8013 on(homeTabID, IntType.ITEM, "break") {player, node -> - var hasHouse = player.houseManager.location.exitLocation != null - if (!hasHouse) { - sendMessage( player, "You must have a house to teleport to before attempting that.") - return@on false + //Snowscape: teleport to the last house you entered, if possible + var owner: Player + val lastHouseOwner = Repository.getPlayerByName(player.getAttribute("lasthouseentered", null)) + if (lastHouseOwner != null && !lastHouseOwner.getHouseManager().isLocked()) { + owner = lastHouseOwner + } else { + var hasHouse = player.houseManager.location.exitLocation != null + if (!hasHouse) { + sendMessage( player, "You must have a house to teleport to before attempting that.") + return@on false + } + owner = player } closeInterface(player) lock(player, 5) if (inInventory(player, node.id)) { - player.houseManager.preEnter(player, false) - val location = player.houseManager.getEnterLocation() + owner.houseManager.preEnter(player, false) + val location = owner.houseManager.getEnterLocation() if (teleport(player, location, TeleportManager.TeleportType.TELETABS)) { removeItem(player, Item(node.id, 1)) - player.houseManager.postEnter(player, false) + owner.houseManager.postEnter(player, false) } } return@on true diff --git a/Server/src/main/content/global/skill/construction/HouseManager.java b/Server/src/main/content/global/skill/construction/HouseManager.java index 01a37f377..65ee6c3d9 100644 --- a/Server/src/main/content/global/skill/construction/HouseManager.java +++ b/Server/src/main/content/global/skill/construction/HouseManager.java @@ -101,6 +101,9 @@ public final class HouseManager { public void parse(JSONObject data){ location = HouseLocation.values()[Integer.parseInt( data.get("location").toString())]; style = HousingStyle.values()[Integer.parseInt( data.get("style").toString())]; + if (data.get("locked") != null) { + locked = true; + } Object servRaw = data.get("servant"); if(servRaw != null){ servant = Servant.parse((JSONObject) servRaw); @@ -225,7 +228,9 @@ public final class HouseManager { */ public void toggleBuildingMode(Player player, boolean enable) { if (!isInHouse(player)) { - player.getPacketDispatch().sendMessage("Building mode really only helps if you're in a house."); + //player.getPacketDispatch().sendMessage("Building mode really only helps if you're in a house."); + setLocked(enable); + player.sendMessage("Your house is now "+(player.getHouseManager().isLocked() ? "locked." : "unlocked." )); return; } if (buildingMode != enable) { diff --git a/Server/src/main/content/global/skill/construction/HouseZone.java b/Server/src/main/content/global/skill/construction/HouseZone.java index ccd426c19..33f875362 100644 --- a/Server/src/main/content/global/skill/construction/HouseZone.java +++ b/Server/src/main/content/global/skill/construction/HouseZone.java @@ -92,7 +92,9 @@ public final class HouseZone extends MapZone { if (e instanceof Player) { Player p = (Player) e; // The below tears down the house if the owner was the one who left - if (house == p.getHouseManager()) { + //if (house == p.getHouseManager()) { + // Snowscape: tear down the house when the last person leaves, allowing guests to stay in the house without the owner. + if (!house.getHouseRegion().isViewed() && (!house.hasDungeon() || !house.getDungeonRegion().isViewed())) { house.expelGuests(p); int toRemove = previousRegion; int dungRemove = previousDungeon; diff --git a/Server/src/main/content/global/skill/construction/PortalOptionPlugin.java b/Server/src/main/content/global/skill/construction/PortalOptionPlugin.java index 23d951dac..23d9621a2 100644 --- a/Server/src/main/content/global/skill/construction/PortalOptionPlugin.java +++ b/Server/src/main/content/global/skill/construction/PortalOptionPlugin.java @@ -8,6 +8,7 @@ import core.game.dialogue.DialoguePlugin; import core.game.interaction.OptionHandler; import core.game.node.Node; import core.game.node.entity.player.Player; +import core.game.node.entity.skill.Skills; import core.game.node.scenery.Scenery; import core.plugin.Initializable; import core.plugin.Plugin; @@ -33,6 +34,7 @@ public final class PortalOptionPlugin extends OptionHandler { SceneryDefinition.forId(13405).getHandlers().put("option:lock", this); SceneryDefinition.forId(13405).getHandlers().put("option:enter", this); ClassScanner.definePlugin(new PortalDialogue()); + ClassScanner.definePlugin(new PortalExitDialogue()); return this; } @@ -40,7 +42,8 @@ public final class PortalOptionPlugin extends OptionHandler { public boolean handle(Player player, Node node, String option) { Scenery object = node.asScenery(); if (object.getId() == 13405 && option.equals("enter")) { - HouseManager.leave(player); + player.getDialogueInterpreter().open("houseExitDialogue"); + //HouseManager.leave(player); return true; } if (option.equals("lock")) { @@ -111,11 +114,14 @@ public final class PortalOptionPlugin extends OptionHandler { break; } //If the Player has a house, but does not have their house moved to the portal they are interacting with + /* Snowscape: allow entering house from anywhere if (player.getHouseManager().getLocation().getPortalId() != player.getAttribute("con:portal", -1)) { player.getPacketDispatch().sendMessage("Your house is in " + player.getHouseManager().getLocation().getName() + "."); player.sendMessage("Speak with an estate agent to change your house location."); break; } + */ + player.removeAttribute("lasthouseentered"); player.getHouseManager().enter(player, buttonId == 2); break; case 3: @@ -134,10 +140,12 @@ public final class PortalOptionPlugin extends OptionHandler { player.getPacketDispatch().sendMessage("You aren't a friend of yourself!"); return Unit.INSTANCE; } + /* Snowscape: allow entering house if host is not home. if (!p.getHouseManager().isLoaded()) { player.getPacketDispatch().sendMessage("This player is not at home right now."); return Unit.INSTANCE; } + */ if (p.getHouseManager().isBuildingMode()) { player.getPacketDispatch().sendMessage("This player is in building mode."); return Unit.INSTANCE; @@ -146,6 +154,7 @@ public final class PortalOptionPlugin extends OptionHandler { player.getPacketDispatch().sendMessage("The other player has locked their house."); return Unit.INSTANCE; } + player.setAttribute("/save:lasthouseentered", (String) value); p.setAttribute("poh_owner", (String) value); p.getHouseManager().enter(player, false); return Unit.INSTANCE; @@ -162,4 +171,114 @@ public final class PortalOptionPlugin extends OptionHandler { return new int[] { DialogueInterpreter.getDialogueKey("con:portal") }; } } + + + + /** + * Snowscape: select destination via dialogue when leaving the house + * + */ + @PluginManifest(type= PluginType.DIALOGUE) + private class PortalExitDialogue extends DialoguePlugin { + + /** + * Constructs a new {@code PortalExitDialogue} {@code Object} + */ + public PortalExitDialogue() { + + } + + /** + * Constructs a new {@code PortalExitDialogue} {@code Object} + * @param player The player. + */ + public PortalExitDialogue(Player player) { + super(player); + } + + @Override + public DialoguePlugin newInstance(Player player) { + return new PortalExitDialogue(player); + } + + @Override + public boolean open(Object... args) { + options("Leave house (home location)", "Rimmington", "Taverly", "Pollnivneach", "Next Page"); + stage = 1; + return true; + } + + @Override + public boolean handle(int interfaceId, int buttonId) { + switch (stage) { + case 1: + switch (buttonId) { + case 1: + HouseManager.leave(player); + end(); + break; + case 2: + attemptHouseExit(HouseLocation.RIMMINGTON); + end(); + break; + case 3: + attemptHouseExit(HouseLocation.TAVERLY); + end(); + break; + case 4: + attemptHouseExit(HouseLocation.POLLNIVNEACH); + end(); + break; + case 5: + options("Rellekka", "Brimhaven","Yanille", "Cancel"); + stage = 2; + break; + } + break; + case 2: + switch (buttonId) { + case 1: + attemptHouseExit(HouseLocation.RELLEKKA); + end(); + break; + case 2: + attemptHouseExit(HouseLocation.BRIMHAVEN); + end(); + break; + case 3: + attemptHouseExit(HouseLocation.YANILLE); + end(); + break; + case 4: + end(); + break; + } + } + return true; + } + + @Override + public int[] getIds() { + return new int[] { DialogueInterpreter.getDialogueKey("houseExitDialogue") }; + } + + public void attemptHouseExit(HouseLocation targetLocation){ + int level = 0; + String message = null; + if (player.getHouseManager().isInHouse(player)) { + level = getStatLevel(player, Skills.CONSTRUCTION); + message = "You need "; + } else { + level = getStatLevel(Repository.getPlayerByName(player.getAttribute("lasthouseentered", null)), Skills.CONSTRUCTION); + message = "The house owner needs "; + } + + if (targetLocation.getLevelRequirement() <= level){ + //player.animate(Animation.RESET); + player.getProperties().setTeleportLocation(targetLocation.getExitLocation()); + } else { + player.sendMessage(message + targetLocation.getLevelRequirement() + " Construction to for you to teleport to " + targetLocation.getName() + "."); + } + } + } } \ No newline at end of file diff --git a/Server/src/main/content/global/skill/magic/modern/ModernListeners.kt b/Server/src/main/content/global/skill/magic/modern/ModernListeners.kt index 16c7f8fbe..6b96e386a 100644 --- a/Server/src/main/content/global/skill/magic/modern/ModernListeners.kt +++ b/Server/src/main/content/global/skill/magic/modern/ModernListeners.kt @@ -33,6 +33,7 @@ import org.rs09.consts.Scenery import org.rs09.consts.Sounds import core.game.system.task.Pulse +import core.game.world.repository.Repository class ModernListeners : SpellListener("modern"){ override fun defineListeners() { @@ -370,17 +371,26 @@ class ModernListeners : SpellListener("modern"){ player.sendMessage("A magical force prevents you from teleporting.") return } - val hasHouse = player.houseManager.location.exitLocation != null - if(!hasHouse){ - player.sendMessage("You do not have a house you can teleport to.") - return + + //Snowscape: teleport to the last house you entered, if possible + var houseOwner: Player + val lastHouseOwner = Repository.getPlayerByName(player.getAttribute("lasthouseentered", null)) + if (lastHouseOwner != null && !lastHouseOwner.getHouseManager().isLocked()) { + houseOwner = lastHouseOwner + } else { + val hasHouse = player.houseManager.location.exitLocation != null + if(!hasHouse){ + player.sendMessage("You do not have a house you can teleport to.") + return + } + houseOwner = player } - player.houseManager.preEnter(player, false) + houseOwner.houseManager.preEnter(player, false) val teleType = TeleportManager.TeleportType.NORMAL - val loc = player.houseManager.getEnterLocation() + val loc = houseOwner.houseManager.getEnterLocation() player.teleporter.send(loc, teleType) - player.houseManager.postEnter(player, false) //this actually runs when the teleport is SUBMITTED rather than EXECUTED, but this is fine + houseOwner.houseManager.postEnter(player, false) //this actually runs when the teleport is SUBMITTED rather than EXECUTED, but this is fine removeRunes(player) addXP(player,30.0) setDelay(player,true) diff --git a/Server/src/main/core/game/node/entity/player/Player.java b/Server/src/main/core/game/node/entity/player/Player.java index 52c286223..436e83475 100644 --- a/Server/src/main/core/game/node/entity/player/Player.java +++ b/Server/src/main/core/game/node/entity/player/Player.java @@ -378,6 +378,7 @@ public class Player extends Entity { interfaceManager.closeSingleTab(); super.clear(); getZoneMonitor().clear(); + houseManager.expelGuests(this); HouseManager.leave(this); UpdateSequence.getRenderablePlayers().remove(this); sendLogoutEvents(); diff --git a/Server/src/main/core/game/node/entity/player/info/login/PlayerSaver.kt b/Server/src/main/core/game/node/entity/player/info/login/PlayerSaver.kt index 2709ae12a..56d018d9a 100644 --- a/Server/src/main/core/game/node/entity/player/info/login/PlayerSaver.kt +++ b/Server/src/main/core/game/node/entity/player/info/login/PlayerSaver.kt @@ -191,6 +191,7 @@ class PlayerSaver (val player: Player){ fun saveHouseData(root: JSONObject){ val manager = player.houseManager val houseData = JSONObject() + if (manager.isLocked) houseData.put("locked", true) houseData.put("location",manager.location.ordinal.toString()) houseData.put("style",manager.style.ordinal.toString()) if(manager.hasServant()){