using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Data; using Uber.Collections; using Uber.HabboHotel.Pets; using Uber.HabboHotel.RoomBots; using Uber.HabboHotel.Pathfinding; using Uber.HabboHotel.Items; using Uber.HabboHotel.GameClients; using Uber.Messages; using Uber.Storage; using System.Collections; namespace Uber.HabboHotel.Rooms { class Room { private uint Id; public string Name; public string Description; public string Type; public string Owner; public string Password; public int Category; public int State; public int UsersNow; public int UsersMax; public string ModelName; public string CCTs; public int Score; public SafeList Tags; public bool AllowPets; public bool AllowPetsEating; public bool AllowWalkthrough; public bool Hidewall; public bool IsMuted; public SafeList UserList; public int UserCounter = 0; private int IdleTime; public RoomIcon myIcon; public List UsersWithRights; private Dictionary Bans; public RoomEvent Event; public string Wallpaper; public string Floor; public string Landscape; public SafeList Items; public MoodlightData MoodlightData; public SafeList ActiveTrades; public Dictionary UserEffects; public bool KeepAlive; public MatrixState[,] Matrix; public bool[,] UserMatrix; public Coord[,] BedMatrix; public double[,] HeightMatrix; public double[,] TopStackHeight; public Boolean HasOngoingEvent { get { return (Event != null); } } public RoomIcon Icon { get { return myIcon; } set { myIcon = value; } } public int UserCount { get { int i = 0; for (int e = 0; e < UserList.Count; e++) { var User = UserList[e]; if (User == null || User.IsBot || User.IsSpectator || User.IsPet) { continue; } i++; } return i; } } public int TagCount { get { return Tags.Count; } } public RoomModel Model { get { return UberEnvironment.GetGame().GetRoomManager().GetModel(ModelName); } } public uint RoomId { get { return Id; } } public List FloorItems { get { var FloorItems = new List(); for (int i = 0; i < this.Items.Count; i++) { var Item = this.Items[i]; if (Item == null) { continue; } if (Item.IsFloorItem) { FloorItems.Add(Item); } } return FloorItems.ToList(); } } public List WallItems { get { var WallItems = new List(); for (int i = 0; i < this.Items.Count; i++) { var Item = this.Items[i]; if (Item == null) { continue; } if (Item.IsWallItem) { WallItems.Add(Item); } } return WallItems.ToList(); } } private int GetVirtualId { get { lock (_lock) { UserCounter++; return UserCounter; } } } public Boolean CanTradeInRoom { get { return (!IsPublic); } } public bool IsPublic { get { return (Type == "public"); } } public int PetCount { get { int c = 0; for (int e = 0; e < this.UserList.Count; e++) { var User = this.UserList[e]; if (User == null) { continue; } if (!User.IsPet) { c++; } } return c; } } private readonly Object _lock; public Room(uint mId, string mName, string mDescription, string mType, string mOwner, int mCategory, int mState, int mUsersMax, string mModelName, string mCCTs, int mScore, SafeList mTags, bool mAllowPets, bool mAllowPetsEating, bool mAllowWalkthrough, bool mHidewall, RoomIcon mIcon, string mPassword, string mWallpaper, string mFloor, string mLandscape) { // Start Information this.Id = mId; this.Name = mName; this.Description = mDescription; this.Owner = mOwner; this.Category = mCategory; this.Type = mType; this.State = mState; this.Score = mScore; this.ModelName = mModelName; this.CCTs = mCCTs; this.myIcon = mIcon; this.Password = mPassword; this.Wallpaper = mWallpaper; this.Floor = mFloor; this.Landscape = mLandscape; // End Information // Start Users this.UsersNow = 0; this.UserCounter = 0; this.UsersMax = mUsersMax; this.UserList = null; this.UserList = new SafeList(); this.UserMatrix = new bool[Model.MapSizeX, Model.MapSizeY]; // End Users // Start Public this.Tags = mTags; this.Event = null; // End Public // Start Booleans this.AllowPets = mAllowPets; this.AllowPetsEating = mAllowPetsEating; this.AllowWalkthrough = mAllowWalkthrough; this.Hidewall = mHidewall; this.IsMuted = false; // End Booleans // Start Collections this.Bans = new Dictionary(); this.Items = new SafeList(); this.ActiveTrades = new SafeList(); this.UserEffects = new Dictionary(); // Start Room State this.IdleTime = 0; this.KeepAlive = true; // End Room State this._lock = new Object(); lock (_lock) { this.LoadRights(); this.LoadFurniture(); this.GenerateMaps(); if (IsPublic) { this.InitBots(); } this.InitPets(); } UberEnvironment.GetLogging().WriteLine("Room -> [" + RoomId + "] " + Name + " loaded.", Core.LogLevel.Debug); } public void InitBots() { var Bots = UberEnvironment.GetGame().GetBotManager().GetBotsForRoom(RoomId); foreach (RoomBot Bot in Bots) { DeployBot(Bot); } } public void InitPets() { DataTable Data = null; using (DatabaseClient dbClient = UberEnvironment.GetDatabase().GetClient()) { dbClient.AddParamWithValue("roomid", RoomId); Data = dbClient.ReadDataTable("SELECT * FROM user_pets WHERE room_id = @roomid"); } if (Data == null) { return; } foreach (DataRow Row in Data.Rows) { Pet Pet = UberEnvironment.GetGame().GetCatalog().GeneratePetFromRow(Row); DeployBot(new RoomBot(Pet.PetId, RoomId, "pet", "freeroam", Pet.Name, "", Pet.Look, Pet.X, Pet.Y, (int)Pet.Z, 0, 0, 0, 0, 0), Pet); } } public RoomUser DeployBot(RoomBot Bot) { return DeployBot(Bot, null); } public RoomUser DeployBot(RoomBot Bot, Pet PetData) { RoomUser BotUser = new RoomUser(0, RoomId, GetVirtualId); if ((Bot.X > 0 && Bot.Y > 0) && Bot.X <= Model.MapSizeX && Bot.Y <= Model.MapSizeY) { BotUser.SetPos(Bot.X, Bot.Y, Bot.Z); BotUser.SetRot(Bot.Rot); } else { Bot.X = Model.DoorX; Bot.Y = Model.DoorY; BotUser.SetPos(Model.DoorX, Model.DoorY, Model.DoorZ); BotUser.SetRot(Model.DoorOrientation); } UserMatrix[Bot.X, Bot.Y] = true; BotUser.BotData = Bot; BotUser.BotAI = Bot.GenerateBotAI(BotUser.VirtualId); if (BotUser.IsPet) { BotUser.BotAI.Init(Convert.ToInt32(Bot.BotId), BotUser.VirtualId, RoomId); BotUser.PetData = PetData; BotUser.PetData.VirtualId = BotUser.VirtualId; } else { BotUser.BotAI.Init(-1, BotUser.VirtualId, RoomId); } UserList.Add(BotUser); UpdateUserStatus(BotUser); BotUser.UpdateNeeded = true; ServerMessage EnterMessage = new ServerMessage(28); EnterMessage.AppendBoolean(true); BotUser.Serialize(EnterMessage); SendMessage(EnterMessage); BotUser.BotAI.OnSelfEnterRoom(); if (BotUser.IsPet) { // std / pla BotUser.AddStatus("gst sml", ""); } return BotUser; } public void RemoveBot(int VirtualId, bool Kicked) { RoomUser User = GetRoomUserByVirtualId(VirtualId); if (User == null || !User.IsBot) { return; } User.BotAI.OnSelfLeaveRoom(Kicked); ServerMessage LeaveMessage = new ServerMessage(29); LeaveMessage.AppendRawInt32(User.VirtualId); SendMessage(LeaveMessage); UserMatrix[User.X, User.Y] = false; if (UserList.Contains(User)) { UserList.Remove(User); } } public void OnUserSay(RoomUser User, string Message, bool Shout) { for(int i = 0; i < this.UserList.Count; i++) { RoomUser Usr = this.UserList[i]; if (!Usr.IsBot) { continue; } if (Shout) { Usr.BotAI.OnUserShout(User, Message); } else if (!Shout) { Usr.BotAI.OnUserSay(User, Message); } } } public void RegenerateUserMatrix() { this.UserMatrix = new bool[Model.MapSizeX, Model.MapSizeY]; for (int i = 0; i < UserList.Count; i++) { RoomUser User = UserList[i]; this.UserMatrix[User.X, User.Y] = true; } } public void GenerateMaps() { // Create matrix arrays Matrix = new MatrixState[Model.MapSizeX, Model.MapSizeY]; BedMatrix = new Coord[Model.MapSizeX, Model.MapSizeY]; HeightMatrix = new double[Model.MapSizeX, Model.MapSizeY]; TopStackHeight = new double[Model.MapSizeX, Model.MapSizeY]; // Fill in the basic data based purely on the heightmap for (int line = 0; line < Model.MapSizeY; line++) { for (int chr = 0; chr < Model.MapSizeX; chr++) { Matrix[chr, line] = MatrixState.BLOCKED; BedMatrix[chr, line] = new Coord(chr, line); HeightMatrix[chr, line] = 0; TopStackHeight[chr, line] = 0.0; if (chr == Model.DoorX && line == Model.DoorY) { Matrix[chr, line] = MatrixState.WALKABLE_LASTSTEP; } else if (Model.SqState[chr, line] == SquareState.OPEN) { Matrix[chr, line] = MatrixState.WALKABLE; } else if (Model.SqState[chr, line] == SquareState.SEAT) { Matrix[chr, line] = MatrixState.WALKABLE_LASTSTEP; } } } // Loop through the items in the room for(int i = 0; i < Items.Count; i++) { RoomItem Item = Items[i]; // If we're dealing with anything other than a floor item, skip if (!Item.IsFloorItem) { continue; } // If this is a rug, ignore it. if (Item.GetBaseItem().Height <= 0) { continue; } // Make sure we're the highest item here! if (TopStackHeight[Item.X, Item.Y] <= Item.Z) { TopStackHeight[Item.X, Item.Y] = Item.Z; // If this item is walkable and on the floor, allow users to walk here. if (Item.GetBaseItem().Walkable) { Matrix[Item.X, Item.Y] = MatrixState.WALKABLE; HeightMatrix[Item.X, Item.Y] = Item.GetBaseItem().Height; } // If this item is a gate, open, and on the floor, allow users to walk here. else if (Item.Z <= (Model.SqFloorHeight[Item.X, Item.Y] + 0.1) && Item.GetBaseItem().InteractionType.ToLower() == "gate" && Item.ExtraData == "1") { Matrix[Item.X, Item.Y] = MatrixState.WALKABLE; } // If this item is a set or a bed, make it's square walkable (but only if last step) else if (Item.GetBaseItem().IsSeat || Item.GetBaseItem().InteractionType.ToLower() == "bed") { Matrix[Item.X, Item.Y] = MatrixState.WALKABLE_LASTSTEP; } // Finally, if it's none of those, block the square. else { Matrix[Item.X, Item.Y] = MatrixState.BLOCKED; } } var Points = GetAffectedTiles(Item.GetBaseItem().Length, Item.GetBaseItem().Width, Item.X, Item.Y, Item.Rot); if (Points == null) { Points = new Dictionary(); } foreach (AffectedTile Tile in Points.Values) { // Make sure we're the highest item here! if (TopStackHeight[Tile.X, Tile.Y] <= Item.Z) { TopStackHeight[Tile.X, Tile.Y] = Item.Z; // If this item is walkable and on the floor, allow users to walk here. if (Item.GetBaseItem().Walkable) { Matrix[Tile.X, Tile.Y] = MatrixState.WALKABLE; HeightMatrix[Tile.X, Tile.Y] = Item.GetBaseItem().Height; } // If this item is a gate, open, and on the floor, allow users to walk here. else if (Item.Z <= (Model.SqFloorHeight[Item.X, Item.Y] + 0.1) && Item.GetBaseItem().InteractionType.ToLower() == "gate" && Item.ExtraData == "1") { Matrix[Tile.X, Tile.Y] = MatrixState.WALKABLE; } // If this item is a set or a bed, make it's square walkable (but only if last step) else if (Item.GetBaseItem().IsSeat || Item.GetBaseItem().InteractionType.ToLower() == "bed") { Matrix[Tile.X, Tile.Y] = MatrixState.WALKABLE_LASTSTEP; } // Finally, if it's none of those, block the square. else { Matrix[Tile.X, Tile.Y] = MatrixState.BLOCKED; } } // Set bad maps if (Item.GetBaseItem().InteractionType.ToLower() == "bed") { if (Item.Rot == 0 || Item.Rot == 4) { BedMatrix[Tile.X, Tile.Y].y = Item.Y; } if (Item.Rot == 2 || Item.Rot == 6) { BedMatrix[Tile.X, Tile.Y].x = Item.X; } } } } } public void LoadRights() { this.UsersWithRights = null; this.UsersWithRights = new List(); DataTable Data = null; using (DatabaseClient dbClient = UberEnvironment.GetDatabase().GetClient()) { Data = dbClient.ReadDataTable("SELECT user_id FROM room_rights WHERE room_id = '" + Id + "'"); } if (Data == null) { return; } foreach (DataRow Row in Data.Rows) { this.UsersWithRights.Add((uint)Row["user_id"]); } } public void LoadFurniture() { this.Items = null; this.Items = new SafeList(); DataTable Data = null; using (DatabaseClient dbClient = UberEnvironment.GetDatabase().GetClient()) { Data = dbClient.ReadDataTable("SELECT * FROM items WHERE room_id = '" + Id + "'"); } if (Data == null) { return; } foreach (DataRow Row in Data.Rows) { RoomItem Item = new RoomItem((uint)Row["id"], RoomId, (uint)Row["base_item"], (string)Row["extra_data"], (int)Row["x"], (int)Row["y"], (Double)Row["z"], (int)Row["rot"], (string)Row["wall_pos"], (int)Row["fw_count"]); if (Item.GetBaseItem().InteractionType.ToLower() == "dimmer" && this.MoodlightData == null) { MoodlightData = new MoodlightData(Item.Id); } if (Item.GetBaseItem().InteractionType.ToLower() == "firework") { Item.Interactor.OnPlace(null, Item); } this.Items.Add(Item); } } public Boolean CheckRights(GameClient Session) { return CheckRights(Session, false); } public Boolean CheckRights(GameClient Session, bool RequireOwnership) { if (Session.GetHabbo().Username.ToLower() == Owner.ToLower()) { return true; } if (Session.GetHabbo().HasFuse("fuse_admin") || Session.GetHabbo().HasFuse("fuse_any_room_controller")) { return true; } if (!RequireOwnership) { if (Session.GetHabbo().HasFuse("fuse_any_room_rights")) { return true; } if (UsersWithRights.Contains(Session.GetHabbo().Id)) { return true; } } return false; } public RoomItem GetItem(uint Id) { for (int i = 0; i < Items.Count; i++) { RoomItem Item = Items[i]; if (Item.Id == Id) { return Item; } } return null; } public void RemoveFurniture(GameClient Session, uint Id, bool Delete) { RoomItem Item = GetItem(Id); if (Item == null) { return; } Item.Interactor.OnRemove(Session, Item); if (Item.IsWallItem) { ServerMessage Message = new ServerMessage(84); Message.AppendRawUInt(Item.Id); Message.AppendBreak(); Message.AppendBoolean(false); SendMessage(Message); } else if (Item.IsFloorItem) { ServerMessage Message = new ServerMessage(94); Message.AppendRawUInt(Item.Id); Message.AppendBreak(); Message.AppendBoolean(false); SendMessage(Message); } Items.Remove(Item); if (Delete) { using (DatabaseClient dbClient = UberEnvironment.GetDatabase().GetClient()) { dbClient.ExecuteQuery("DELETE FROM items WHERE id = '" + Id + "' LIMIT 1"); } } else { using (DatabaseClient dbClient = UberEnvironment.GetDatabase().GetClient()) { dbClient.ExecuteQuery("UPDATE items SET room_id = '0', x = '0', y = '0', z = '0', rot = '0', wall_pos = '', user_id = '" + Session.GetHabbo().Id + "' WHERE id = '" + Id + "' LIMIT 1"); } } this.GenerateMaps(); this.UpdateUserStatusses(); } public bool CanWalk(int X, int Y, Double Z, Boolean LastStep, GameClient Session) { if (Session != null && GetRoomUserByHabbo(Session.GetHabbo().Username) != null && GetRoomUserByHabbo(Session.GetHabbo().Username).AllowOverride) { return true; } if (X < 0 || X >= Model.MapSizeX || Y >= Model.MapSizeY || Y < 0) { return false; } if (SquareHasUsers(X, Y, LastStep)) { return false; } if (Matrix[X, Y] == MatrixState.BLOCKED) { return false; } else if (Matrix[X, Y] == MatrixState.WALKABLE_LASTSTEP && !LastStep) { return false; } return true; } public RoomItem GetRoomItemOnTile(int X, int Y) { for (int i = 0; i < FloorItems.Count; i++) { RoomItem Item = FloorItems[i]; if (Item == null) { continue; } if (Item.X == X && Item.Y == Y) { return Item; } } return null; } public void ProcessRoom() { int i = 0; // Loop through all furni and process them if they want to be processed for (int iu = 0; iu < Items.Count; iu++) { RoomItem Item = Items[iu]; if (!Item.UpdateNeeded) { continue; } Item.ProcessUpdates(); } // Loop through all users and bots and process them var ToRemove = new List(); for (int ie = 0; ie < UserList.Count; ie++) { RoomUser User = UserList[ie]; if (User == null) { ToRemove.Add(User); continue; } User.IdleTime++; if (!User.IsAsleep && User.IdleTime >= 600) { User.IsAsleep = true; ServerMessage FallAsleep = new ServerMessage(486); FallAsleep.AppendInt32(User.VirtualId); FallAsleep.AppendBoolean(true); SendMessage(FallAsleep); } if (User.NeedsAutokick && !ToRemove.Contains(User)) { ToRemove.Add(User); } if (User.CarryItemID > 0) { User.CarryTimer--; if (User.CarryTimer <= 0) { User.CarryItem(0); } } bool invalidSetStep = false; if (User.SetStep) { if (CanWalk(User.SetX, User.SetY, 0, true, User.GetClient()) || User.AllowOverride) { UserMatrix[User.X, User.Y] = false; User.X = User.SetX; User.Y = User.SetY; User.Z = User.SetZ; UserMatrix[User.X, User.Y] = true; UpdateUserStatus(User); } else { invalidSetStep = true; } User.SetStep = false; } if (User.PathRecalcNeeded) { PathFinder PathFinder = new PathFinder(Matrix, HeightMatrix, Model.MapSizeX, Model.MapSizeY); User.GoalX = User.PathRecalcX; User.GoalY = User.PathRecalcY; Coord CurrentPos = new Coord(User.X, User.Y); Coord Goal = new Coord(User.GoalX, User.GoalY); List Route = PathFinder.FindPath(CurrentPos, Goal); if (Route != null) { Route.Remove(Route[0]); User.Path = Route; User.IsWalking = true; User.PathRecalcNeeded = false; } else { User.PathRecalcNeeded = false; User.Path.Clear(); } } if (User.IsWalking) { if (invalidSetStep || User.Path.Count < 1 || User.GoalX == User.X && User.Y == User.GoalY) { User.Path.Clear(); User.IsWalking = false; User.RemoveStatus("mv"); User.PathRecalcNeeded = false; if (User.X == Model.DoorX && User.Y == Model.DoorY && !ToRemove.Contains(User) && !User.IsBot) { ToRemove.Add(User); } UpdateUserStatus(User); } else { WorkBestTile(User); PathFinderNode NextStep = User.Path[0]; User.Path.Remove(NextStep); int nextX = NextStep.X; int nextY = NextStep.Y; User.RemoveStatus("mv"); bool LastStep = false; if (nextX == User.GoalX && nextY == User.GoalY) { LastStep = true; } if (CanWalk(nextX, nextY, 0, LastStep, User.GetClient()) || User.AllowOverride) { double nextZ = SqAbsoluteHeight(nextX, nextY); User.Statusses.Remove("lay"); User.Statusses.Remove("sit"); User.AddStatus("mv", nextX + "," + nextY + "," + nextZ.ToString().Replace(',', '.')); int newRot; if (User.WalkBackwards) { newRot = Rotation.CalculateBackwards(User.X, User.Y, nextX, nextY); } else { newRot = Rotation.Calculate(User.X, User.Y, nextX, nextY); } User.RotBody = newRot; User.RotHead = newRot; User.SetStep = true; User.SetX = BedMatrix[nextX, nextY].x; User.SetY = BedMatrix[nextX, nextY].y; User.SetZ = nextZ; } else { User.IsWalking = false; } } User.UpdateNeeded = true; } else { if (User.Statusses.ContainsKey("mv")) { User.RemoveStatus("mv"); User.UpdateNeeded = true; } } if (User.IsBot) { User.BotAI.OnTimerTick(); } else { i++; } } foreach (RoomUser toRemove in ToRemove) { RemoveUserFromRoom(toRemove.GetClient(), true, false); } ToRemove = null; // Update idle time if (i >= 1) { this.IdleTime = 0; } else { this.IdleTime++; } // If room has been idle for a while if (this.IdleTime >= 60 && this.KeepAlive) { UberEnvironment.GetGame().GetRoomManager().RequestRoomUnload(RoomId); } ServerMessage Updates = SerializeStatusUpdates(false); if (Updates != null) { SendMessage(Updates); } } public RoomUser GetUserByTile(Coord Coord) { for (int i = 0; i < UserList.Count; i++) { RoomUser User = UserList[i]; if (User == null) { continue; } if (User.Coordinate == Coord) { return User; } } return null; } private void WorkBestTile(RoomUser User) { if (User == null) { return; } if (User.Path.Count > 1) { PathFinderNode skipNode = User.Path[1]; if (TilesTouching(User.X, User.Y, skipNode.X, skipNode.Y)) { if (CanWalk(skipNode.X, skipNode.Y, 0, false, User.GetClient())) { // Work Diagonal int checkX1 = 0; int checkX2 = 0; if (skipNode.X > User.X) { checkX1 = -1; checkX2 = 1; } else { checkX1 = 1; checkX2 = -1; } if (CanWalk(skipNode.X + checkX1, skipNode.Y, 0, false, User.GetClient()) && CanWalk(User.X + checkX2, User.Y, 0, false, User.GetClient())) { PathFinderNode nextNode = User.Path[0]; User.Path.Remove(nextNode); } } } } } #region User handling (General) public void AddUserToRoom(GameClient Session, bool Spectator) { if (Session == null) { return; } RoomUser User = new RoomUser(Session.GetHabbo().Id, RoomId, GetVirtualId); if (Spectator) { User.IsSpectator = true; } else { User.SetPos(Model.DoorX, Model.DoorY, Model.DoorZ); User.SetRot(Model.DoorOrientation); if (CheckRights(Session, true)) { User.AddStatus("flatcrtl", "useradmin"); } else if (CheckRights(Session)) { User.AddStatus("flatcrtl", ""); } if (!User.IsBot && User.GetClient().GetHabbo().IsTeleporting) { RoomItem Item = GetItem(User.GetClient().GetHabbo().TeleporterId); if (Item != null) { User.SetPos(Item.X, Item.Y, Item.Z); User.SetRot(Item.Rot); Item.InteractingUser2 = Session.GetHabbo().Id; Item.ExtraData = "2"; Item.UpdateState(false, true); } } User.GetClient().GetHabbo().IsTeleporting = false; User.GetClient().GetHabbo().TeleporterId = 0; ServerMessage EnterMessage = new ServerMessage(28); EnterMessage.AppendBoolean(true); User.Serialize(EnterMessage); SendMessage(EnterMessage); } UserList.Add(User); Session.GetHabbo().OnEnterRoom(Id); if (!Spectator) { UpdateUserCount(); for (int i = 0; i < UserList.Count; i++) { RoomUser Usr = UserList[i]; if (!Usr.IsBot) { continue; } Usr.BotAI.OnUserEnterRoom(User); } } } public void RemoveUserFromRoom(GameClient Session, bool NotifyClient, bool NotifyKick) { // Checking Start if (Session == null) { return; // nothing to do with an empty gameclient } RoomUser ReceivedUser = GetRoomUserByHabbo(Session.GetHabbo().Id); if (ReceivedUser == null) { return; // already removed } if (!UserList.Contains(ReceivedUser)) { return; // Theres no fking user! } // Checking Ends // Send a message if (NotifyClient) { if (NotifyKick) { Session.GetMessageHandler().GetResponse().Init(33); Session.GetMessageHandler().GetResponse().AppendInt32(4008); Session.GetMessageHandler().SendResponse(); } Session.GetMessageHandler().GetResponse().Init(18); Session.GetMessageHandler().SendResponse(); } if (!ReceivedUser.IsSpectator) { // The tile will be unlocked this.UserMatrix[ReceivedUser.X, ReceivedUser.Y] = false; // Send Leave Message ServerMessage Message = new ServerMessage(29); Message.AppendRawInt32(ReceivedUser.VirtualId); SendMessage(Message); // Handle Actions that where done if (Session.GetHabbo() != null) { if (HasActiveTrade(Session.GetHabbo().Id)) { TryStopTrade(Session.GetHabbo().Id); } if ((Session.GetHabbo().Username.ToLower() == Owner.ToLower()) && HasOngoingEvent) { // Kills Event ServerMessage KillEvent = new ServerMessage(370); KillEvent.AppendStringWithBreak("-1"); SendMessage(KillEvent); Event = null; } Session.GetHabbo().OnLeaveRoom(); } // Removes Pets that has been planted. if (Session.GetHabbo() != null) { for (int i = 0; i < UserList.Count; i++) { RoomUser User = UserList[i]; if (User == null || !User.IsPet) { continue; } if (User.PetData.OwnerId == Session.GetHabbo().Id && Owner != Session.GetHabbo().Username) { User.BotAI.OnUserLeaveRoom(Session); Session.GetHabbo().GetInventoryComponent().AddPet(User.PetData); RemoveBot(User.VirtualId, false); } } } } if (UserList.Contains(ReceivedUser)) { UserList.Remove(ReceivedUser); } ReceivedUser = null; // clean up user :D // Updates Count UpdateUserCount(); // waarom this. ervoor? idk? doe ik soms :D } public RoomUser GetPet(uint PetId) { for (int i = 0; i < UserList.Count; i++) { RoomUser User = UserList[i]; if (User.IsBot && User.IsPet && User.PetData != null && User.PetData.PetId == PetId) { return User; } } return null; } public bool RoomContainsPet(uint PetId) { return (GetPet(PetId) != null); } public void UpdateUserCount() { this.UsersNow = UserCount; using (DatabaseClient dbClient = UberEnvironment.GetDatabase().GetClient()) { dbClient.ExecuteQuery("UPDATE rooms SET users_now = '" + this.UsersNow + "' WHERE id = '" + Id + "' LIMIT 1"); } } public RoomUser GetRoomUserByVirtualId(int VirtualId) { for (int i = 0; i < UserList.Count; i++) { RoomUser User = UserList[i]; if (User.VirtualId == VirtualId) { return User; } } return null; } public RoomUser GetRoomUserByHabbo(uint Id) { for (int i = 0; i < UserList.Count; i++) { RoomUser User = UserList[i]; if (User.IsBot || User == null) { continue; } if (User.HabboId == Id) { return User; } } return null; } public RoomUser GetRoomUserByHabbo(string Name) { for (int i = 0; i < UserList.Count; i++) { RoomUser User = UserList[i]; if (User.IsBot || User == null) { continue; } if (User.GetClient().GetHabbo().Username.ToLower() == Name.ToLower()) { return User; } } return null; } #endregion #region Communication public void SendMessage(ServerMessage Message) { for (int i = 0; i < UserList.Count; i++) { RoomUser User = UserList[i]; if (User.IsBot || User.GetClient() == null) { continue; } User.GetClient().SendMessage(Message); } } public void SendMessageToUsersWithRights(ServerMessage Message) { for (int i = 0; i < UserList.Count; i++) { RoomUser User = UserList[i]; if (User.IsBot || User.GetClient() == null) { continue; } if (!CheckRights(User.GetClient())) { continue; } User.GetClient().SendMessage(Message); } } #endregion public void Destroy() { // Send kick message. No users should be left in this room, but if they are for whatever reason, they should leave. SendMessage(new ServerMessage(18)); // Clear user list and dispose of the engine thread lock (_lock) { this.IdleTime = 0; this.KeepAlive = false; this.Items = null; this.ActiveTrades = null; this.UsersWithRights = null; this.Tags = null; this.UserMatrix = null; this.UsersNow = 0; this.UserEffects = null; this.UserCounter = 0; this.Event = null; } this.SavePetsClean(); this.UserList = null; UberEnvironment.GetLogging().WriteLine("Room <- [" + RoomId + "] " + Name + " disposed.", Core.LogLevel.Debug); } internal void SavePetsClean() { for (int i = 0; i < UserList.Count; i++) { RoomUser User = UserList[i]; if (User == null) { continue; } if (!User.IsPet) { continue; } using (DatabaseClient dbClient = UberEnvironment.GetDatabase().GetClient()) { dbClient.AddParamWithValue("x", User.X); dbClient.AddParamWithValue("Y", User.Y); dbClient.AddParamWithValue("z", Convert.ToString(User.Z).Replace(",",".")); dbClient.AddParamWithValue("petid", User.PetData.PetId); dbClient.ExecuteQuery("UPDATE user_pets SET x = @x,y = @y,z = @z WHERE id = @petid LIMIT 1"); } } } public ServerMessage SerializeStatusUpdates(Boolean All) { var Users = new List(); for (int i = 0; i < UserList.Count; i++) { RoomUser User = UserList[i]; if (!All) { if (!User.UpdateNeeded) { continue; } User.UpdateNeeded = false; } Users.Add(User); } if (Users.Count == 0) { return null; } ServerMessage Message = new ServerMessage(34); Message.AppendInt32(Users.Count); for (int i = 0; i < Users.Count; i++) { RoomUser User = Users[i]; User.SerializeStatus(Message); } return Message; } #region Room Bans public Boolean UserIsBanned(uint Id) { return Bans.ContainsKey(Id); } public void RemoveBan(uint Id) { Bans.Remove(Id); } public void AddBan(uint Id) { Bans.Add(Id, UberEnvironment.GetUnixTimestamp()); } public Boolean HasBanExpired(uint Id) { if (!UserIsBanned(Id)) { return true; } Double diff = UberEnvironment.GetUnixTimestamp() - Bans[Id]; if (diff > 900) { return true; } return false; } #endregion public int ItemCountByType(String InteractionType) { int i = 0; for (int ie = 0; ie < Items.Count; ie++) { RoomItem Item = Items[ie]; if (Item.GetBaseItem().InteractionType.ToLower() == InteractionType.ToLower()) { i++; } } return i; } #region Trading public bool HasActiveTrade(RoomUser User) { if (User.IsBot || User == null) { return false; } return HasActiveTrade(User.GetClient().GetHabbo().Id); } public bool HasActiveTrade(uint UserId) { foreach (Trade Trade in ActiveTrades) { if (Trade.ContainsUser(UserId)) { return true; } } return false; } public Trade GetUserTrade(RoomUser User) { if (User.IsBot || User == null) { return null; } return GetUserTrade(User.GetClient().GetHabbo().Id); } public Trade GetUserTrade(uint UserId) { foreach (Trade Trade in ActiveTrades) { if (Trade.ContainsUser(UserId)) { return Trade; } } return null; } public void TryStartTrade(RoomUser UserOne, RoomUser UserTwo) { if (UserOne == null || UserTwo == null || UserOne.IsBot || UserTwo.IsBot || UserOne.IsTrading || UserTwo.IsTrading || HasActiveTrade(UserOne) || HasActiveTrade(UserTwo)) { return; } ActiveTrades.Add(new Trade(UserOne.GetClient().GetHabbo().Id, UserTwo.GetClient().GetHabbo().Id, RoomId)); } public void TryStopTrade(uint UserId) { Trade Trade = GetUserTrade(UserId); if (Trade == null) { return; } Trade.CloseTrade(UserId); ActiveTrades.Remove(Trade); } #endregion #region Furni handling and stacking public bool SetFloorItem(GameClient Session, RoomItem Item, int newX, int newY, int newRot, bool newItem) { // Find affected tiles var AffectedTiles = GetAffectedTiles(Item.GetBaseItem().Length, Item.GetBaseItem().Width, newX, newY, newRot); // Verify tiles are valid if (!ValidTile(newX, newY)) { return false; } foreach (AffectedTile Tile in AffectedTiles.Values) { if (!ValidTile(Tile.X, Tile.Y)) { return false; } } // Start calculating new Z coordinate Double newZ = Model.SqFloorHeight[newX, newY]; // Is the item trying to stack on itself!? if (Item.Rot == newRot && Item.X == newX && Item.Y == newY && Item.Z != newZ) { return false; } // Make sure this tile is open and there are no users here if (Model.SqState[newX, newY] != SquareState.OPEN) { return false; } foreach (AffectedTile Tile in AffectedTiles.Values) { if (Model.SqState[Tile.X, Tile.Y] != SquareState.OPEN) { return false; } } // And that we have no users if (!Item.GetBaseItem().IsSeat) { if (SquareHasUsers(newX, newY)) { return false; } foreach (AffectedTile Tile in AffectedTiles.Values) { if (SquareHasUsers(Tile.X, Tile.Y)) { return false; } } } // Find affected objects var ItemsOnTile = GetFurniObjects(newX, newY); var ItemsAffected = new List(); var ItemsComplete = new List(); foreach (AffectedTile Tile in AffectedTiles.Values) { List Temp = GetFurniObjects(Tile.X, Tile.Y); if (Temp != null) { ItemsAffected.AddRange(Temp); } } if (ItemsOnTile == null) ItemsOnTile = new List(); ItemsComplete.AddRange(ItemsOnTile); ItemsComplete.AddRange(ItemsAffected); // Check for items in the stack that do not allow stacking on top of them foreach (RoomItem I in ItemsComplete) { if (I.Id == Item.Id) { continue; } if (!I.GetBaseItem().Stackable) { return false; } } // If this is a rotating action, maintain item at current height if (Item.Rot != newRot && Item.X == newX && Item.Y == newY) { newZ = Item.Z; } // Are there any higher objects in the stack!? foreach (RoomItem I in ItemsComplete) { if (I.Id == Item.Id) { continue; // cannot stack on self } if (I.TotalHeight > newZ) { newZ = I.TotalHeight; } } // Verify the rotation is correct if (newRot != 0 && newRot != 2 && newRot != 4 && newRot != 6 && newRot != 8) { newRot = 0; } Item.X = newX; Item.Y = newY; Item.Z = newZ; Item.Rot = newRot; Item.Interactor.OnPlace(Session, Item); if (newItem) { using (DatabaseClient dbClient = UberEnvironment.GetDatabase().GetClient()) { dbClient.AddParamWithValue("extra_data", Item.ExtraData); dbClient.ExecuteQuery("UPDATE items SET room_id = '" + RoomId + "', x = '" + Item.X + "', y = '" + Item.Y + "', z = '" + Item.Z.ToString().Replace(',','.') + "', rot = '" + Item.Rot + "', wall_pos = '' WHERE id = '" + Item.Id + "' LIMIT 1"); } Items.Add(Item); ServerMessage Message = new ServerMessage(93); Item.Serialize(Message); SendMessage(Message); } else { using (DatabaseClient dbClient = UberEnvironment.GetDatabase().GetClient()) { dbClient.ExecuteQuery("UPDATE items SET x = '" + Item.X + "', y = '" + Item.Y + "', z = '" + Item.Z.ToString().Replace(',', '.') + "', rot = '" + Item.Rot + "', wall_pos = '' WHERE id = '" + Item.Id + "' LIMIT 1"); } ServerMessage Message = new ServerMessage(95); Item.Serialize(Message); SendMessage(Message); } GenerateMaps(); UpdateUserStatusses(); return true; } public bool SetWallItem(GameClient Session, RoomItem Item) { Item.Interactor.OnPlace(Session, Item); switch (Item.GetBaseItem().InteractionType.ToLower()) { case "dimmer": if (MoodlightData == null) { MoodlightData = new MoodlightData(Item.Id); Item.ExtraData = MoodlightData.GenerateExtraData(); } break; } using (DatabaseClient dbClient = UberEnvironment.GetDatabase().GetClient()) { dbClient.AddParamWithValue("extra_data", Item.ExtraData); dbClient.ExecuteQuery("UPDATE items SET room_id = '" + RoomId + "', x = '0', y = '0', z = '0', rot = '0', wall_pos = '" + Item.WallPos + "' WHERE id = '" + Item.Id + "' LIMIT 1"); } Items.Add(Item); ServerMessage Message = new ServerMessage(83); Item.Serialize(Message); SendMessage(Message); return true; } #endregion public void UpdateUserStatusses() { for (int i = 0; i < UserList.Count; i++) { RoomUser User = UserList[i]; UpdateUserStatus(User); } } public Boolean OpenPlace(Coord Coord) { if (Coord == null) { return false; } // invalid add all nothing to do :D if (Coord.x > this.Model.MapSizeX || Coord.y > this.Model.MapSizeY) { return false; } // outside map, no items and invalid? var List = this.FloorItems; for (int i = 0; i < List.Count; i++) { RoomItem Item = List[i]; if (Item == null || Item.IsWallItem || Item.GetBaseItem().Walkable || Item.GetBaseItem().Stackable) { continue; } if (Item.Coordinate == Coord) { return false; } } return true; } public double SqAbsoluteHeight(int X, int Y) { var ItemsOnSquare = GetFurniObjects(X, Y); double HighestStack = 0; bool deduct = false; double deductable = 0.0; if (ItemsOnSquare == null) { ItemsOnSquare = new List(); } if (ItemsOnSquare != null) { foreach (RoomItem Item in ItemsOnSquare) { if (Item.TotalHeight > HighestStack) { if (Item.GetBaseItem().IsSeat || Item.GetBaseItem().InteractionType.ToLower() == "bed") { deduct = true; deductable = Item.GetBaseItem().Height; } else { deduct = false; } HighestStack = Item.TotalHeight; } } } double floorHeight = Model.SqFloorHeight[X, Y]; double stackHeight = HighestStack - Model.SqFloorHeight[X, Y]; if (deduct) { stackHeight -= deductable; } if (stackHeight < 0) { stackHeight = 0; } return (floorHeight + stackHeight); } public void UpdateUserStatus(RoomUser User) { if (User.Statusses.ContainsKey("lay") || User.Statusses.ContainsKey("sit")) { User.Statusses.Remove("lay"); User.Statusses.Remove("sit"); User.UpdateNeeded = true; } double newZ = SqAbsoluteHeight(User.X, User.Y); if (newZ != User.Z) { User.Z = newZ; User.UpdateNeeded = true; } if (Model.SqState[User.X, User.Y] == SquareState.SEAT) { if (!User.Statusses.ContainsKey("sit")) { User.Statusses.Add("sit", "1.0"); } User.Z = Model.SqFloorHeight[User.X, User.Y]; User.RotHead = Model.SqSeatRot[User.X, User.Y]; User.RotBody = Model.SqSeatRot[User.X, User.Y]; User.UpdateNeeded = true; } var ItemsOnSquare = GetFurniObjects(User.X, User.Y); if (ItemsOnSquare == null) { ItemsOnSquare = new List(); if (UserEffects.ContainsKey(User.HabboId)) { ApplyUserEffect(User, 0); } } foreach (RoomItem Item in ItemsOnSquare) { if (Item.GetBaseItem().IsSeat) { if (!User.Statusses.ContainsKey("sit")) { User.Statusses.Add("sit", Item.GetBaseItem().Height.ToString().Replace(',', '.')); } User.Z = Item.Z; User.RotHead = Item.Rot; User.RotBody = Item.Rot; User.UpdateNeeded = true; ApplyUserEffect(User, 0); } else if (Item.GetBaseItem().InteractionType.ToLower() == "bed") { if (!User.Statusses.ContainsKey("lay")) { User.Statusses.Add("lay", Item.GetBaseItem().Height.ToString().Replace(',', '.') + " null"); } User.Z = Item.Z; User.RotHead = Item.Rot; User.RotBody = Item.Rot; User.UpdateNeeded = true; ApplyUserEffect(User, 0); } else if (Item.GetBaseItem().InteractionType.ToLower() == "water1" || Item.GetBaseItem().InteractionType.ToLower() == "water2") { if (Item.GetBaseItem().Name.ToLower() == "bw_water_1") { ApplyUserEffect(User, 30); } if (Item.GetBaseItem().Name.ToLower() == "bw_water_2") { ApplyUserEffect(User, 29); } if (Item.GetBaseItem().Name.ToLower() == "hween10_pond") { ApplyUserEffect(User, 28); } if (User.GetClient().GetHabbo().CurrentQuestId == 19) { UberEnvironment.GetGame().GetQuestManager().UpdateQuest(19, User.GetClient()); } } else if (Item.GetBaseItem().Name.ToLower() == "es_skating_ice") { if (User.GetClient().GetHabbo().Gender.ToLower() == "m") { ApplyUserEffect(User, 38); } else { ApplyUserEffect(User, 39); } } else if (Item.GetBaseItem().InteractionType.ToLower() == "ball") { // todo soccer :D } else { ApplyUserEffect(User, 0); } } } public void ApplyUserEffect(RoomUser User, int EffectId) { if (UserEffects.ContainsKey(User.HabboId)) { if (UserEffects[User.HabboId] == EffectId) { return; } UserEffects.Remove(User.HabboId); } ServerMessage Message = new ServerMessage(485); Message.AppendInt32(User.VirtualId); Message.AppendInt32(EffectId); SendMessage(Message); User.UpdateNeeded = true; if (EffectId != 0) { UserEffects.Add(User.HabboId, EffectId); } } public void ApplyUserEffect(RoomUser User, int EffectId, bool Limit) { ServerMessage Message = new ServerMessage(485); Message.AppendInt32(User.VirtualId); Message.AppendInt32(EffectId); SendMessage(Message); User.UpdateNeeded = true; } public bool ValidTile(int X, int Y) { if (X < 0 || Y < 0 || X >= Model.MapSizeX || Y >= Model.MapSizeY) { return false; } return true; } public void TurnHeads(int X, int Y, uint SenderId) // Todo: fix glitch ? or habbos issue ?? idk O.O { /*foreach (RoomUser User in UserList) { if (User.HabboId == SenderId) { continue; } User.SetRot(Rotation.Calculate(User.X, User.Y, X, Y), true); }*/ } public List GetFurniObjects(int X, int Y) { var Results = new List(); foreach (RoomItem Item in FloorItems) { if (Item.X == X && Item.Y == Y) { Results.Add(Item); } var PointList = GetAffectedTiles(Item.GetBaseItem().Length, Item.GetBaseItem().Width, Item.X, Item.Y, Item.Rot); foreach (AffectedTile Tile in PointList.Values) { if (Tile.X == X && Tile.Y == Y) { Results.Add(Item); } } } if (Results.Count > 0) { return Results; } return null; } public RoomItem FindItem(uint Id) { for (int i = 0; i < Items.Count; i++) { RoomItem Item = Items[i]; if (Item.Id == Id) { return Item; } } return null; } public Dictionary GetAffectedTiles(int Length, int Width, int PosX, int PosY, int Rotation) { int x = 0; var PointList = new Dictionary(); if (Length > 1) { if (Rotation == 0 || Rotation == 4) { for (int i = 1; i < Length; i++) { PointList.Add(x++, new AffectedTile(PosX, PosY + i, i)); for (int j = 1; j < Width; j++) { PointList.Add(x++, new AffectedTile(PosX + j, PosY + i, (i < j) ? j : i)); } } } else if (Rotation == 2 || Rotation == 6) { for (int i = 1; i < Length; i++) { PointList.Add(x++, new AffectedTile(PosX + i, PosY, i)); for (int j = 1; j < Width; j++) { PointList.Add(x++, new AffectedTile(PosX + i, PosY + j, (i < j) ? j : i)); } } } } if (Width > 1) { if (Rotation == 0 || Rotation == 4) { for (int i = 1; i < Width; i++) { PointList.Add(x++, new AffectedTile(PosX + i, PosY, i)); for (int j = 1; j < Length; j++) { PointList.Add(x++, new AffectedTile(PosX + i, PosY + j, (i < j) ? j : i)); } } } else if (Rotation == 2 || Rotation == 6) { for (int i = 1; i < Width; i++) { PointList.Add(x++, new AffectedTile(PosX, PosY + i, i)); for (int j = 1; j < Length; j++) { PointList.Add(x++, new AffectedTile(PosX + j, PosY + i, (i < j) ? j : i)); } } } } return PointList; } public bool SquareHasUsers(int X, int Y, bool LastStep) { if (AllowWalkthrough) { return false; } return SquareHasUsers(X, Y); } public bool SquareHasUsers(int X, int Y) { Coord Coord = BedMatrix[X, Y]; return UserMatrix[Coord.x, Coord.y]; } //This function is based on the one from "Holograph Emulator" public string WallPositionCheck(string wallPosition) { //:w=3,2 l=9,63 l try { if (wallPosition.Contains(Convert.ToChar(13))) { return null; } if (wallPosition.Contains(Convert.ToChar(9))) { return null; } string[] posD = wallPosition.Split(' '); if (posD[2] != "l" && posD[2] != "r") return null; string[] widD = posD[0].Substring(3).Split(','); int widthX = int.Parse(widD[0]); int widthY = int.Parse(widD[1]); if (widthX < 0 || widthY < 0 || widthX > 200 || widthY > 200) return null; string[] lenD = posD[1].Substring(2).Split(','); int lengthX = int.Parse(lenD[0]); int lengthY = int.Parse(lenD[1]); if (lengthX < 0 || lengthY < 0 || lengthX > 200 || lengthY > 200) return null; return ":w=" + widthX + "," + widthY + " " + "l=" + lengthX + "," + lengthY + " " + posD[2]; } catch { return null; } } public bool TilesTouching(int X1, int Y1, int X2, int Y2) { if (!(Math.Abs(X1 - X2) > 1 || Math.Abs(Y1 - Y2) > 1)) return true; if (X1 == X2 && Y1 == Y2) return true; return false; } public int TileDistance(int X1, int Y1, int X2, int Y2) { return Math.Abs(X1 - X2) + Math.Abs(Y1 - Y2); } } public class AffectedTile { int mX; int mY; int mI; public AffectedTile(int x, int y, int i) { mX = x; mY = y; mI = i; } public int X { get { return mX; } } public int Y { get { return mY; } } public int I { get { return mI; } } } }