package net.scapeemulator.game.model.player.skills.construction; import net.scapeemulator.game.model.Position; import net.scapeemulator.game.model.World; import net.scapeemulator.game.model.object.GroundObjectList; import net.scapeemulator.game.model.object.GroundObjectList.GroundObject; import net.scapeemulator.game.model.object.ObjectGroup; import net.scapeemulator.game.model.player.Player; import net.scapeemulator.game.model.player.PlayerVariables.Variable; import net.scapeemulator.game.model.player.RegionPalette; import net.scapeemulator.game.model.player.RegionPalette.Tile.Rotation; import net.scapeemulator.game.model.player.SceneRebuiltListener; import net.scapeemulator.game.model.player.skills.construction.hotspot.BuildableHotspot; import net.scapeemulator.game.model.player.skills.construction.hotspot.Hotspot; import net.scapeemulator.game.model.player.skills.construction.room.Room; import net.scapeemulator.game.model.player.skills.construction.room.RoomPlaced; import net.scapeemulator.game.model.player.skills.construction.room.RoomPosition; import net.scapeemulator.game.model.player.skills.construction.room.RoomPreview; import net.scapeemulator.game.model.player.skills.construction.room.RoomType; import net.scapeemulator.game.msg.impl.MinimapUpdateMessage; import net.scapeemulator.game.task.Action; import net.scapeemulator.game.task.Task; /** * Represents an instance of a Construction player owned house (POH). * * @author David Insley */ public class House { /** * The size of the actual house region, fit inside of the main 13x13 region * palette. Should always be an odd number to guarantee a center subregion. */ private static final int REGION_SIZE = 9; /** * Because the region palette is 13x13 but house regions can be smaller, we * need a subregion buffer around our house area. */ private static final int PALETTE_OFFSET = (RegionPalette.PALETTE_SIZE - REGION_SIZE) / 2; public static final int HOUSE_X = 4000; public static final int HOUSE_Y = 4000; /** * X and Y coordinate of the bottom left corner of the house region. */ public static final int BASE_X = HOUSE_X - ((REGION_SIZE / 2) * Room.ROOM_SIZE); public static final int BASE_Y = HOUSE_Y - ((REGION_SIZE / 2) * Room.ROOM_SIZE); /** * The owner of this house. */ private final Player owner; /** * The list of all objects in this house. */ private GroundObjectList objects; /** * A 3D array containing all rooms in the house region. Note that grass and * empty dungeon areas are not null but actually a type of room. */ private final RoomPlaced[][][] rooms; /** * The style of the house, used when calculating palette locations for the * construct region packet. */ private HouseStyle style; /** * The players selected portal in the game world used to access their POH. */ private HousePortal worldPortal; /** * Set to true when the object list is populated with all of the house * hotspots. */ private boolean populated = false; /** * Whether or not this house is locked to visitors. */ private boolean locked = false; private BuildingSession buildSession; /** * Constructs a POH with the default settings. Portal location in * Rimmington, basic wood style, with a garden and parlour. */ public House(Player owner) { this.owner = owner; worldPortal = HousePortal.RIMMINGTON; style = HouseStyle.FANCY_STONE; rooms = new RoomPlaced[4][REGION_SIZE][REGION_SIZE]; for (int height = 0; height < 4; height++) { for (int x = 0; x < REGION_SIZE; x++) { for (int y = 0; y < REGION_SIZE; y++) { rooms[height][x][y] = new RoomPlaced(this, new RoomPosition(height, x, y), Construction.defaultRoom(height), Rotation.NONE); } } } rooms[1][4][4] = new RoomPlaced(this, new RoomPosition(1, 4, 4), RoomType.GARDEN, Rotation.NONE); rooms[1][4][5] = new RoomPlaced(this, new RoomPosition(1, 4, 5), RoomType.PARLOUR, Rotation.NONE); } public void setRoom(int x, int y, int height, RoomType type, Rotation rotation) { rooms[height][x][y] = new RoomPlaced(this, new RoomPosition(height, x, y), type, rotation); populated = false; } /** * Resets the ground object list and populates it with the hotspots for each * room. */ private void populateHouse() { objects = new GroundObjectList(); for (int height = 0; height < 4; height++) { for (int x = 0; x < REGION_SIZE; x++) { for (int y = 0; y < REGION_SIZE; y++) { rooms[height][x][y].createHotspots(); } } } populated = true; } public void otherEnterPortal(Player player) { if (owner.getInHouse() != this) { player.sendMessage("They don't appear to be at home."); return; } if (locked) { player.sendMessage("You cannot access that house right now."); return; } if (buildSession != null) { player.sendMessage("That player is currently in building mode."); return; } sendHouse(player, getPortalPosition()); } public void ownerEnterPortal(boolean building) { if (!populated) { populateHouse(); } loadingInterface(owner, true); setBuildingMode(building); sendHouse(owner, getPortalPosition()); } private void sendHouse(final Player player, final Position newPos) { Position pos = new Position(HOUSE_X, HOUSE_Y, getHeightOffset() + 1); player.teleport(pos); loadingInterface(player, true); if (!populated) { populateHouse(); } RegionPalette palette = getRegionPalette(); player.setConstructedRegion(palette); player.setSceneRebuiltListener(new SceneRebuiltListener() { @Override public void sceneRebuilt() { objects.fireAllEvents(player.getGroundObjectSynchronizer()); objects.addListener(player.getGroundObjectSynchronizer()); player.teleport(newPos); loadingInterface(player, true); player.startAction(new Action<Player>(player, 5, false) { @Override public void execute() { stop(); loadingInterface(player, false); } }); } }); player.setInHouse(this); } private Position getPortalPosition() { return new Position(HOUSE_X + 1, HOUSE_Y + 2, getHeightOffset() + 1); } public void clearRoomSpace(RoomPosition pos, boolean respawnObjects) { RoomType type = Construction.defaultRoom(pos.getHouseHeight()); Position clearPos = style.getRoomPosition(type); for (int x = 0; x < Room.ROOM_SIZE; x++) { for (int y = 0; y < Room.ROOM_SIZE; y++) { for (ObjectGroup group : ObjectGroup.values()) { Position objPos = new Position(pos.getBaseX() + x, pos.getBaseY() + y, getHeightOffset() + pos.getHouseHeight()); objects.remove(objPos, group); if (respawnObjects && type != RoomType.NONE) { GroundObject obj = World.getWorld().getGroundObjects().get(group, new Position(clearPos.getX() + x, clearPos.getY() + y, clearPos.getHeight())); if (obj != null) { objects.put(objPos, obj.getId(), obj.getRotation(), obj.getType()); } } } } } } public void setBuildingMode(boolean buildingMode) { for (int height = 0; height < 4; height++) { for (int x = 0; x < REGION_SIZE; x++) { for (int y = 0; y < REGION_SIZE; y++) { RoomPlaced room = rooms[height][x][y]; room.buildingMode(buildingMode); } } } buildSession = buildingMode ? new BuildingSession() : null; } /** * Gets the Room for the given coordinates in the POH. Note that the height * is for the actual position in the world (0-3 + ownerId * 4), IE the * players actual height, not the rooms height. * * @param worldHeight the height of the position in the world * @param x the x coordinate * @param y the y coordinate * @return the Room at the given coordinates if it exists, or null if it * doesn't */ public RoomPlaced forCoords(int worldHeight, int x, int y) { int roomX = (int) (4 + ((x - HOUSE_X) / 8.0)); int roomY = (int) (4 + ((y - HOUSE_Y) / 8.0)); try { return rooms[worldHeight - getHeightOffset()][roomX][roomY]; } catch (IndexOutOfBoundsException e) { return null; } } /** * Constructs and returns the RegionPalette for this POH. * * @return the constructed region palette */ public RegionPalette getRegionPalette() { RegionPalette palette = new RegionPalette(); for (int height = 0; height < rooms.length; height++) { for (int x = 0; x < REGION_SIZE; x++) { for (int y = 0; y < REGION_SIZE; y++) { if (rooms[height][x][y].getType() != RoomType.NONE) { palette.setTile(height, x + PALETTE_OFFSET, y + PALETTE_OFFSET, rooms[height][x][y].getPaletteSourceTile()); } } } } return palette; } public int getHeightOffset() { return owner.getId() * 4; } public GroundObjectList getObjectList() { return objects; } public HouseStyle getStyle() { return style; } public Player getOwner() { return owner; } public BuildingSession getBuildingSession() { return buildSession; } private static void loadingInterface(Player player, boolean show) { if (show) { player.send(MinimapUpdateMessage.HIDE_MINIMAP); player.getInterfaceSet().openOverlay(Construction.POH_LOADING_INTERFACE); player.setActionsBlocked(true); } else { player.send(MinimapUpdateMessage.RESET); player.getInterfaceSet().closeOverlay(); player.setActionsBlocked(false); } } public class BuildingSession { private RoomPosition temp; private RoomPreview preview; private BuildableHotspot furnTemp; public void handleBuildOption(GroundObject object) { owner.turnToPosition(object.getCenterPosition()); int x = object.getPosition().getX(); int y = object.getPosition().getY(); RoomPlaced room = forCoords(object.getPosition().getHeight(), x, y); if (room == null) { return; } Hotspot hotspot = room.getHotspot(x - room.getRoomPos().getBaseX(), y - room.getRoomPos().getBaseY(), object); if (hotspot instanceof BuildableHotspot) { ((BuildableHotspot) hotspot).handleBuildOption(this); } } public void handleFurnitureBuildInterface(int itemIndex) { if (furnTemp != null) { furnTemp.handleBuildInterface(this, itemIndex); furnTemp = null; } } public void delayReveal(BuildableHotspot hotspot) { World.getWorld().getTaskScheduler().schedule(new Task(2, false) { @Override public void execute() { hotspot.buildingMode(true); } }); } public void handleSelectRoomInterface(int childId) { owner.getInterfaceSet().closeWindow(); RoomType roomType = RoomType.forInterfaceId(childId); if (temp == null || roomType == null) { return; } if(!roomType.validBuild(temp.getHouseHeight())) { owner.sendMessage("That room cannot be built on this floor."); temp = null; return; } (preview = new RoomPreview(House.this, roomType, temp)).previewRoom(); temp = null; Construction.PREVIEW_DIALOGUE.displayTo(owner); /* * Start an action so that if the player moves or does anything else * the preview is cancelled, or after 3 minutes. */ owner.startAction(new Action<Player>(owner, 500, false) { @Override public void execute() { } @Override public void stop() { super.stop(); cancelPreview(); } }); } public void initRoomBuild(RoomPosition pos) { owner.getInterfaceSet().openWindow(Construction.ROOM_CREATE_INTERFACE); temp = pos; } public void rotatePreview(Rotation rot) { if (preview == null) { return; } preview.rotate(rot); } public void cancelPreview() { if (preview == null) { return; } clearRoomSpace(preview.getRoomPos(), true); preview = null; } public void finishPreview() { if (preview == null) { return; } RoomPosition pos = preview.getRoomPos(); rooms[pos.getHouseHeight()][pos.getHouseX()][pos.getHouseY()] = new RoomPlaced(House.this, pos, preview.getType(), preview.getRoomRotation()); clearRoomSpace(pos, false); rooms[pos.getHouseHeight()][pos.getHouseX()][pos.getHouseY()].createHotspots(); setBuildingMode(true); preview = null; owner.stopAction(); sendHouse(owner, owner.getPosition()); } public void initFurnitureRemove(BuildableHotspot furnTemp) { this.furnTemp = furnTemp; if (owner.getVariables().getVar(Variable.CON_FURN_REMOVE) == 1) { finishFurnitureRemove(true); } else { Construction.FURNITURE_DELETION_DIALOGUE.displayTo(owner); } } public void finishFurnitureRemove(boolean confirm) { if (furnTemp == null) { return; } if (confirm) { furnTemp.finishRemove(this); } furnTemp = null; } public void initRoomDelete(RoomPosition pos) { if (pos.getHouseHeight() == 1) { if (rooms[2][pos.getHouseX()][pos.getHouseY()].getType() != RoomType.NONE) { owner.sendMessage("You must remove the room above that one first."); return; } } // TODO if deleting a dungeon stair, say do it from above Construction.ROOM_DELETION_DIALOGUE.displayTo(owner); temp = pos; } public void finishRoomDeletion(boolean confirm) { if (temp == null) { return; } if (confirm) { owner.getInterfaceSet().openOverlay(Construction.POH_LOADING_INTERFACE); clearRoomSpace(temp, true); rooms[temp.getHouseHeight()][temp.getHouseX()][temp.getHouseY()] = new RoomPlaced(House.this, temp, Construction.defaultRoom(temp.getHouseHeight()), Rotation.NONE); populateHouse(); setBuildingMode(true); sendHouse(owner, owner.getPosition()); } temp = null; } public Player getBuilder() { return owner; } public void setFurniturePlaceholder(BuildableHotspot temp) { furnTemp = temp; } } }