package com.eu.habbo.habbohotel.rooms; import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.bots.Bot; import com.eu.habbo.habbohotel.items.Item; import com.eu.habbo.habbohotel.items.interactions.InteractionGuildGate; import com.eu.habbo.habbohotel.items.interactions.InteractionHabboClubGate; import com.eu.habbo.habbohotel.items.interactions.InteractionWater; import com.eu.habbo.habbohotel.items.interactions.InteractionWaterItem; import com.eu.habbo.habbohotel.pets.Pet; import com.eu.habbo.habbohotel.pets.RideablePet; import com.eu.habbo.habbohotel.users.DanceType; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.messages.outgoing.generic.alerts.CustomNotificationComposer; import com.eu.habbo.messages.outgoing.rooms.users.RoomUserStatusComposer; import com.eu.habbo.plugin.Event; import com.eu.habbo.plugin.events.roomunit.RoomUnitLookAtPointEvent; import com.eu.habbo.plugin.events.roomunit.RoomUnitSetGoalEvent; import com.eu.habbo.plugin.events.users.UserIdleEvent; import com.eu.habbo.plugin.events.users.UserTakeStepEvent; import com.eu.habbo.threading.runnables.RoomUnitKick; import com.eu.habbo.util.pathfinding.Rotation; import gnu.trove.map.TMap; import gnu.trove.map.hash.THashMap; import gnu.trove.set.hash.THashSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; import java.util.stream.Collectors; public class RoomUnit { private static final Logger LOGGER = LoggerFactory.getLogger(RoomUnit.class); public boolean isWiredTeleporting = false; public boolean isLeavingTeleporter = false; private final ConcurrentHashMap status; private final THashMap cacheable; public boolean canRotate = true; public boolean animateWalk = false; public boolean cmdTeleport = false; public boolean cmdSit = false; public boolean cmdStand = false; public boolean cmdLay = false; public boolean sitUpdate = false; public boolean isTeleporting = false; public boolean isKicked; public int kickCount = 0; private int id; private RoomTile startLocation; private RoomTile previousLocation; private double previousLocationZ; private RoomTile currentLocation; private RoomTile goalLocation; private double z; private int tilesWalked; private boolean inRoom; private boolean canWalk; private boolean fastWalk = false; private boolean statusUpdate = false; private boolean invisible = false; private boolean lastCycleStatus = false; private boolean canLeaveRoomByDoor = true; private RoomUserRotation bodyRotation = RoomUserRotation.NORTH; private RoomUserRotation headRotation = RoomUserRotation.NORTH; private DanceType danceType; private RoomUnitType roomUnitType; private Deque path = new LinkedList<>(); private int handItem; private long handItemTimestamp; private int walkTimeOut; private int effectId; private int effectEndTimestamp; private ScheduledFuture moveBlockingTask; private int idleTimer; private Room room; private RoomRightLevels rightsLevel = RoomRightLevels.NONE; private THashSet overridableTiles; public RoomUnit() { this.id = 0; this.inRoom = false; this.canWalk = true; this.status = new ConcurrentHashMap<>(); this.cacheable = new THashMap<>(); this.roomUnitType = RoomUnitType.UNKNOWN; this.danceType = DanceType.NONE; this.handItem = 0; this.handItemTimestamp = 0; this.walkTimeOut = Emulator.getIntUnixTimestamp(); this.effectId = 0; this.isKicked = false; this.overridableTiles = new THashSet<>(); } public void clearWalking() { this.goalLocation = null; this.startLocation = this.currentLocation; this.inRoom = false; this.status.clear(); this.cacheable.clear(); } public void stopWalking() { synchronized (this.status) { this.status.remove(RoomUnitStatus.MOVE); this.setGoalLocation(this.currentLocation); } } public boolean cycle(Room room) { try { Habbo rider = null; if (this.getRoomUnitType() == RoomUnitType.PET) { Pet pet = room.getPet(this); if (pet instanceof RideablePet) { rider = ((RideablePet) pet).getRider(); } } if (rider != null) { // copy things from rider if (this.status.containsKey(RoomUnitStatus.MOVE) && !rider.getRoomUnit().getStatusMap().containsKey(RoomUnitStatus.MOVE)) { this.status.remove(RoomUnitStatus.MOVE); } if (rider.getRoomUnit().getCurrentLocation().x != this.getX() || rider.getRoomUnit().getCurrentLocation().y != this.getY()) { this.status.put(RoomUnitStatus.MOVE, rider.getRoomUnit().getCurrentLocation().x + "," + rider.getRoomUnit().getCurrentLocation().y + "," + (rider.getRoomUnit().getCurrentLocation().getStackHeight())); this.setPreviousLocation(rider.getRoomUnit().getPreviousLocation()); this.setPreviousLocationZ(rider.getRoomUnit().getPreviousLocation().getStackHeight()); this.setCurrentLocation(rider.getRoomUnit().getCurrentLocation()); this.setZ(rider.getRoomUnit().getCurrentLocation().getStackHeight()); } return this.statusUpdate; } if (!this.isWalking() && !this.isKicked) { if (this.status.remove(RoomUnitStatus.MOVE) == null) { Habbo habboT = room.getHabbo(this); if (habboT != null) { habboT.getHabboInfo().getRiding().getRoomUnit().status.remove(RoomUnitStatus.MOVE); } return true; } } if (this.status.remove(RoomUnitStatus.SIT) != null) this.statusUpdate = true; if (this.status.remove(RoomUnitStatus.MOVE) != null) this.statusUpdate = true; if (this.status.remove(RoomUnitStatus.LAY) != null) this.statusUpdate = true; for (Map.Entry set : this.status.entrySet()) { if (set.getKey().removeWhenWalking) { this.status.remove(set.getKey()); } } if (this.path == null || this.path.isEmpty()) return true; boolean canfastwalk = true; Habbo habboT = room.getHabbo(this); if (habboT != null) { if (habboT.getHabboInfo().getRiding() != null) canfastwalk = false; } RoomTile next = this.path.poll(); boolean overrideChecks = next != null && this.canOverrideTile(next); if (this.path.isEmpty()) { this.sitUpdate = true; if (next != null && next.hasUnits() && !overrideChecks) { return false; } } Deque peekPath = room.getLayout().findPath(this.currentLocation, this.path.peek(), this.goalLocation, this); if (peekPath == null) peekPath = new LinkedList<>(); if (peekPath.size() >= 3) { if (path.isEmpty()) return true; path.pop(); //peekPath.pop(); //Start peekPath.removeLast(); //End if (peekPath.peek() != next) { next = peekPath.poll(); for (int i = 0; i < peekPath.size(); i++) { this.path.addFirst(peekPath.removeLast()); } } } if (canfastwalk && this.fastWalk) { if (this.path.size() > 1) { next = this.path.poll(); } } if (next == null) return true; Habbo habbo = room.getHabbo(this); this.status.remove(RoomUnitStatus.DEAD); if (habbo != null) { if (this.isIdle()) { UserIdleEvent event = new UserIdleEvent(habbo, UserIdleEvent.IdleReason.WALKED, false); Emulator.getPluginManager().fireEvent(event); if (!event.isCancelled()) { if (!event.idle) { room.unIdle(habbo); this.idleTimer = 0; } } } if (Emulator.getPluginManager().isRegistered(UserTakeStepEvent.class, false)) { Event e = new UserTakeStepEvent(habbo, room.getLayout().getTile(this.getX(), this.getY()), next); Emulator.getPluginManager().fireEvent(e); if (e.isCancelled()) return true; } } HabboItem item = room.getTopItemAt(next.x, next.y); //if(!(this.path.size() == 0 && canSitNextTile)) { if (!room.tileWalkable(next)) { this.room = room; this.path.clear(); this.findPath(); if (this.path.isEmpty()) { this.status.remove(RoomUnitStatus.MOVE); return false; } next = (RoomTile)this.path.pop(); } } boolean canSitNextTile = room.canSitAt(next.x, next.y); if (canSitNextTile) { HabboItem tallestChair = room.getTallestChair(next); if (tallestChair != null) item = tallestChair; } if (next.equals(this.goalLocation) && next.state == RoomTileState.SIT && !overrideChecks) { if (item == null || item.getZ() - this.getZ() > RoomLayout.MAXIMUM_STEP_HEIGHT) { this.status.remove(RoomUnitStatus.MOVE); return false; } } double zHeight = 0.0D; /*if (((habbo != null && habbo.getHabboInfo().getRiding() != null) || isRiding) && next.equals(this.goalLocation) && (next.state == RoomTileState.SIT || next.state == RoomTileState.LAY)) { this.status.remove(RoomUnitStatus.MOVE); return false; }*/ if (habbo != null) { if (habbo.getHabboInfo().getRiding() != null) { zHeight += 1.0D; } } HabboItem habboItem = room.getTopItemAt(this.getX(), this.getY()); if (habboItem != null) { if (habboItem != item || !RoomLayout.pointInSquare(habboItem.getX(), habboItem.getY(), habboItem.getX() + habboItem.getBaseItem().getWidth() - 1, habboItem.getY() + habboItem.getBaseItem().getLength() - 1, next.x, next.y)) habboItem.onWalkOff(this, room, new Object[]{this.getCurrentLocation(), next}); } this.tilesWalked++; RoomUserRotation oldRotation = this.getBodyRotation(); this.setRotation(RoomUserRotation.values()[Rotation.Calculate(this.getX(), this.getY(), next.x, next.y)]); if (item != null) { if (item != habboItem || !RoomLayout.pointInSquare(item.getX(), item.getY(), item.getX() + item.getBaseItem().getWidth() - 1, item.getY() + item.getBaseItem().getLength() - 1, this.getX(), this.getY())) { if (item.canWalkOn(this, room, null)) { item.onWalkOn(this, room, new Object[]{this.getCurrentLocation(), next}); } else if (item instanceof InteractionGuildGate || item instanceof InteractionHabboClubGate) { this.setRotation(oldRotation); this.tilesWalked--; this.setGoalLocation(this.currentLocation); this.status.remove(RoomUnitStatus.MOVE); room.sendComposer(new RoomUserStatusComposer(this).compose()); if (item instanceof InteractionHabboClubGate && habbo != null) { habbo.getClient().sendResponse(new CustomNotificationComposer(CustomNotificationComposer.GATE_NO_HC)); } return false; } } else { item.onWalk(this, room, new Object[]{this.getCurrentLocation(), next}); } zHeight += item.getZ(); if (!item.getBaseItem().allowSit() && !item.getBaseItem().allowLay()) { zHeight += Item.getCurrentHeight(item); } } else { zHeight += room.getLayout().getHeightAtSquare(next.x, next.y); } this.setPreviousLocation(this.getCurrentLocation()); this.setStatus(RoomUnitStatus.MOVE, next.x + "," + next.y + "," + zHeight); if (habbo != null) { if (habbo.getHabboInfo().getRiding() != null) { RoomUnit ridingUnit = habbo.getHabboInfo().getRiding().getRoomUnit(); if (ridingUnit != null) { ridingUnit.setPreviousLocationZ(this.getZ()); this.setZ(zHeight - 1.0); ridingUnit.setRotation(RoomUserRotation.values()[Rotation.Calculate(this.getX(), this.getY(), next.x, next.y)]); ridingUnit.setPreviousLocation(this.getCurrentLocation()); ridingUnit.setGoalLocation(this.getGoal()); ridingUnit.setStatus(RoomUnitStatus.MOVE, next.x + "," + next.y + "," + (zHeight - 1.0)); room.sendComposer(new RoomUserStatusComposer(ridingUnit).compose()); //ridingUnit.setZ(zHeight - 1.0); } } } //room.sendComposer(new RoomUserStatusComposer(this).compose()); this.setZ(zHeight); this.setCurrentLocation(room.getLayout().getTile(next.x, next.y)); this.resetIdleTimer(); if (habbo != null) { HabboItem topItem = room.getTopItemAt(next.x, next.y); boolean isAtDoor = next.x == room.getLayout().getDoorX() && next.y == room.getLayout().getDoorY(); boolean publicRoomKicks = !room.isPublicRoom() || Emulator.getConfig().getBoolean("hotel.room.public.doortile.kick"); boolean invalidated = topItem != null && topItem.invalidatesToRoomKick(); if (this.canLeaveRoomByDoor && isAtDoor && publicRoomKicks && !invalidated) { Emulator.getThreading().run(new RoomUnitKick(habbo, room, false), 500); } } return false; } catch (Exception e) { LOGGER.error("Caught exception", e); return false; } } public int getId() { return this.id; } public void setId(int id) { this.id = id; } public RoomTile getCurrentLocation() { return this.currentLocation; } public void setCurrentLocation(RoomTile location) { if (location != null) { if (this.currentLocation != null) { this.currentLocation.removeUnit(this); } this.currentLocation = location; location.addUnit(this); } } public short getX() { return this.currentLocation.x; } public short getY() { return this.currentLocation.y; } public double getZ() { return this.z; } public void setZ(double z) { this.z = z; if (this.room != null) { Bot bot = this.room.getBot(this); if (bot != null) { bot.needsUpdate(true); } } } public boolean isInRoom() { return this.inRoom; } public synchronized void setInRoom(boolean inRoom) { this.inRoom = inRoom; } public RoomUnitType getRoomUnitType() { return this.roomUnitType; } public synchronized void setRoomUnitType(RoomUnitType roomUnitType) { this.roomUnitType = roomUnitType; } public void setRotation(RoomUserRotation rotation) { this.bodyRotation = rotation; this.headRotation = rotation; } public RoomUserRotation getBodyRotation() { return this.bodyRotation; } public void setBodyRotation(RoomUserRotation bodyRotation) { this.bodyRotation = bodyRotation; } public RoomUserRotation getHeadRotation() { return this.headRotation; } public void setHeadRotation(RoomUserRotation headRotation) { this.headRotation = headRotation; } public DanceType getDanceType() { return this.danceType; } public synchronized void setDanceType(DanceType danceType) { this.danceType = danceType; } public void setCanWalk(boolean value) { this.canWalk = value; } public boolean canWalk() { return this.canWalk; } public boolean isFastWalk() { return this.fastWalk; } public void setFastWalk(boolean fastWalk) { this.fastWalk = fastWalk; } public RoomTile getStartLocation() { return this.startLocation; } public int tilesWalked() { return this.tilesWalked; } public RoomTile getGoal() { return this.goalLocation; } public void setGoalLocation(RoomTile goalLocation) { if (goalLocation != null) { // if (goalLocation.state != RoomTileState.INVALID) { this.setGoalLocation(goalLocation, false); } //} } public void setGoalLocation(RoomTile goalLocation, boolean noReset) { if (Emulator.getPluginManager().isRegistered(RoomUnitSetGoalEvent.class, false)) { Event event = new RoomUnitSetGoalEvent(this.room, this, goalLocation); Emulator.getPluginManager().fireEvent(event); if (event.isCancelled()) return; } /// Set start location this.startLocation = this.currentLocation; if (goalLocation != null && !noReset) { boolean isWalking = this.hasStatus(RoomUnitStatus.MOVE); this.goalLocation = goalLocation; this.findPath(); ///< Quadral: this is where we start formulating a path if (!this.path.isEmpty()) { this.tilesWalked = isWalking ? this.tilesWalked : 0; this.cmdSit = false; } else { this.goalLocation = this.currentLocation; } } } public void setLocation(RoomTile location) { if (location != null) { this.startLocation = location; setPreviousLocation(location); setCurrentLocation(location); this.goalLocation = location; } } public RoomTile getPreviousLocation() { return this.previousLocation; } public void setPreviousLocation(RoomTile previousLocation) { this.previousLocation = previousLocation; this.previousLocationZ = this.z; } public double getPreviousLocationZ() { return this.previousLocationZ; } public void setPreviousLocationZ(double z) { this.previousLocationZ = z; } public void setPathFinderRoom(Room room) { this.room = room; } public void findPath() { if (this.room != null && this.room.getLayout() != null && this.goalLocation != null && (this.goalLocation.isWalkable() || this.room.canSitOrLayAt(this.goalLocation.x, this.goalLocation.y) || this.canOverrideTile(this.goalLocation))) { Deque path = this.room.getLayout().findPath(this.currentLocation, this.goalLocation, this.goalLocation, this); if (path != null) this.path = path; } } public boolean isAtGoal() { return this.currentLocation.equals(this.goalLocation); } public boolean isWalking() { return !this.isAtGoal() && this.canWalk; } public String getStatus(RoomUnitStatus key) { return this.status.get(key); } public ConcurrentHashMap getStatusMap() { return this.status; } public void removeStatus(RoomUnitStatus key) { this.status.remove(key); } public void setStatus(RoomUnitStatus key, String value) { if (key != null && value != null) { this.status.put(key, value); } } public boolean hasStatus(RoomUnitStatus key) { return this.status.containsKey(key); } public void clearStatus() { this.status.clear(); } public void statusUpdate(boolean update) { this.statusUpdate = update; } public boolean needsStatusUpdate() { return this.statusUpdate; } public TMap getCacheable() { return this.cacheable; } public int getHandItem() { return this.handItem; } public void setHandItem(int handItem) { this.handItem = handItem; this.handItemTimestamp = System.currentTimeMillis(); } public long getHandItemTimestamp() { return this.handItemTimestamp; } public int getEffectId() { return this.effectId; } public void setEffectId(int effectId, int endTimestamp) { this.effectId = effectId; this.effectEndTimestamp = endTimestamp; } public int getEffectEndTimestamp() { return this.effectEndTimestamp; } public int getWalkTimeOut() { return this.walkTimeOut; } public void setWalkTimeOut(int walkTimeOut) { this.walkTimeOut = walkTimeOut; } public void increaseIdleTimer() { this.idleTimer++; } public boolean isIdle() { return this.idleTimer > Room.IDLE_CYCLES; //Amount of room cycles / 2 = seconds. } public int getIdleTimer() { return this.idleTimer; } public void resetIdleTimer() { this.idleTimer = 0; } public void setIdle() { this.idleTimer = Room.IDLE_CYCLES + 1; } public void lookAtPoint(RoomTile location) { if (!this.canRotate) return; if (Emulator.getPluginManager().isRegistered(RoomUnitLookAtPointEvent.class, false)) { Event lookAtPointEvent = new RoomUnitLookAtPointEvent(this.room, this, location); Emulator.getPluginManager().fireEvent(lookAtPointEvent); if (lookAtPointEvent.isCancelled()) return; } if (this.status.containsKey(RoomUnitStatus.LAY)) { return; } if (!this.status.containsKey(RoomUnitStatus.SIT)) { this.bodyRotation = (RoomUserRotation.values()[Rotation.Calculate(this.getX(), this.getY(), location.x, location.y)]); } RoomUserRotation rotation = (RoomUserRotation.values()[Rotation.Calculate(this.getX(), this.getY(), location.x, location.y)]); if (Math.abs(rotation.getValue() - this.bodyRotation.getValue()) <= 1) { this.headRotation = rotation; } } public Deque getPath() { return this.path; } public void setPath(Deque path) { this.path = path; } public RoomRightLevels getRightsLevel() { return this.rightsLevel; } public void setRightsLevel(RoomRightLevels rightsLevel) { this.rightsLevel = rightsLevel; } public boolean isInvisible() { return this.invisible; } public void setInvisible(boolean invisible) { this.invisible = invisible; } public Room getRoom() { return room; } public void setRoom(Room room) { this.room = room; } public boolean canOverrideTile(RoomTile tile) { if (tile == null || room == null || room.getLayout() == null) return false; if (room.getItemsAt(tile).stream().anyMatch(i -> i.canOverrideTile(this, room, tile))) return true; int tileIndex = (tile.x & 0xFF) | (tile.y << 12); return this.overridableTiles.contains(tileIndex); } public void addOverrideTile(RoomTile tile) { int tileIndex = (tile.x & 0xFF) | (tile.y << 12); if (!this.overridableTiles.contains(tileIndex)) { this.overridableTiles.add(tileIndex); } } public void removeOverrideTile(RoomTile tile) { if (room == null || room.getLayout() == null) return; int tileIndex = (tile.x & 0xFF) | (tile.y << 12); this.overridableTiles.remove(tileIndex); } public void clearOverrideTiles() { this.overridableTiles.clear(); } public boolean canLeaveRoomByDoor() { return canLeaveRoomByDoor; } public void setCanLeaveRoomByDoor(boolean canLeaveRoomByDoor) { this.canLeaveRoomByDoor = canLeaveRoomByDoor; } public boolean canForcePosture() { if (this.room == null) return false; HabboItem topItem = this.room.getTopItemAt(this.getX(), this.getY()); return topItem == null || (!(topItem instanceof InteractionWater) && !(topItem instanceof InteractionWaterItem)); } public RoomTile getClosestTile(List tiles) { return tiles.stream().min(Comparator.comparingDouble(a -> a.distance(this.getCurrentLocation()))).orElse(null); } public RoomTile getClosestAdjacentTile(short x, short y, boolean diagonal) { if(room == null) return null; RoomTile baseTile = room.getLayout().getTile(x, y); if (baseTile == null) return null; List rotations = new ArrayList<>(); rotations.add(RoomUserRotation.SOUTH.getValue()); rotations.add(RoomUserRotation.NORTH.getValue()); rotations.add(RoomUserRotation.EAST.getValue()); rotations.add(RoomUserRotation.WEST.getValue()); if (diagonal) { rotations.add(RoomUserRotation.NORTH_EAST.getValue()); rotations.add(RoomUserRotation.NORTH_WEST.getValue()); rotations.add(RoomUserRotation.SOUTH_EAST.getValue()); rotations.add(RoomUserRotation.SOUTH_WEST.getValue()); } return this.getClosestTile( rotations.stream() .map(rotation -> room.getLayout().getTileInFront(baseTile, rotation)) .filter(t -> t != null && t.isWalkable() && (this.getCurrentLocation().equals(t) || !room.hasHabbosAt(t.x, t.y))) .collect(Collectors.toList()) ); } public ScheduledFuture getMoveBlockingTask() { return moveBlockingTask; } public void setMoveBlockingTask(ScheduledFuture moveBlockingTask) { this.moveBlockingTask = moveBlockingTask; } }