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.
This commit is contained in:
randy 2025-05-13 14:29:51 -06:00
parent 52185005aa
commit cecdb97b19
7 changed files with 165 additions and 17 deletions

View file

@ -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 ->
//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

View file

@ -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) {

View file

@ -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;

View file

@ -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("<col=FF0000>Your house is in " + player.getHouseManager().getLocation().getName() + ".");
player.sendMessage("<col=FF0000>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() + ".");
}
}
}
}

View file

@ -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
}
//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)

View file

@ -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();

View file

@ -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()){