using System; using ModernUO.Serialization; using Server.Collections; using Server.ContextMenus; using Server.Gumps; using Server.Multis; namespace Server.Engines.Mahjong; [SerializationGenerator(1, false)] public partial class MahjongGame : Item, ISecurable { public const int MaxPlayers = 4; public const int BaseScore = 30000; [SerializedIgnoreDupe] [SerializableField(0)] [SerializedCommandProperty(AccessLevel.GameMaster)] private SecureLevel _level; [SerializedIgnoreDupe] [SerializableField(1, setter: "private")] private MahjongTile[] _tiles; [SerializedIgnoreDupe] [SerializableField(2, setter: "private")] private MahjongDealerIndicator _dealerIndicator; [SerializedIgnoreDupe] [SerializableField(3, setter: "private")] private MahjongWallBreakIndicator _wallBreakIndicator; [SerializedIgnoreDupe] [SerializableField(4, setter: "private")] private MahjongDices _dices; [SerializedIgnoreDupe] [SerializableField(5, setter: "private")] private MahjongPlayers _players; private DateTime _lastReset; [Constructible] public MahjongGame() : base(0xFAA) { BuildWalls(); _dealerIndicator = new MahjongDealerIndicator(this, new Point2D(300, 300), MahjongPieceDirection.Up, MahjongWind.North); _wallBreakIndicator = new MahjongWallBreakIndicator(this, new Point2D(335, 335)); _dices = new MahjongDices(this); _players = new MahjongPlayers(this, MaxPlayers, BaseScore); _lastReset = Core.Now; _level = SecureLevel.CoOwners; } public override double DefaultWeight => 5.0; [SerializableField(6, fieldChanged: nameof(OnShowScoresChanged))] [SerializedCommandProperty(AccessLevel.GameMaster)] private bool _showScores; private void OnShowScoresChanged(bool oldValue, bool newValue) { if (newValue) { _players.SendPlayersPacket(true, true); } _players.SendGeneralPacket(true, true); _players.SendLocalizedMessage(newValue ? 1062777 : 1062778); // The dealer has enabled/disabled score display. } [SerializableField(7, fieldChanged: nameof(OnSpectatorVisionChanged))] [SerializedCommandProperty(AccessLevel.GameMaster)] [InvalidateProperties] private bool _spectatorVision; private void OnSpectatorVisionChanged(bool oldValue, bool newValue) { if (_players.IsInGamePlayer(_players.DealerPosition)) { _players.Dealer.NetState.SendMahjongGeneralInfo(this); } _players.SendTilesPacket(false, true); _players.SendLocalizedMessage(newValue ? 1062715 : 1062716); // The dealer has enabled/disabled Spectator Vision. } private void BuildHorizontalWall( ref int index, int x, int y, int stackLevel, MahjongPieceDirection direction, MahjongTileTypeGenerator typeGenerator ) { for (var i = 0; i < 17; i++) { var position = new Point2D(x + i * 20, y); Tiles[index + i] = new MahjongTile( this, index + i, typeGenerator.Next(), position, stackLevel, direction, false ); } index += 17; this.MarkDirty(); } private void BuildVerticalWall( ref int index, int x, int y, int stackLevel, MahjongPieceDirection direction, MahjongTileTypeGenerator typeGenerator ) { for (var i = 0; i < 17; i++) { var position = new Point2D(x, y + i * 20); Tiles[index + i] = new MahjongTile( this, index + i, typeGenerator.Next(), position, stackLevel, direction, false ); } index += 17; this.MarkDirty(); } private void BuildWalls() { Tiles = new MahjongTile[136]; var typeGenerator = new MahjongTileTypeGenerator(); var i = 0; BuildHorizontalWall(ref i, 165, 110, 0, MahjongPieceDirection.Up, typeGenerator); BuildHorizontalWall(ref i, 165, 115, 1, MahjongPieceDirection.Up, typeGenerator); BuildVerticalWall(ref i, 530, 165, 0, MahjongPieceDirection.Left, typeGenerator); BuildVerticalWall(ref i, 525, 165, 1, MahjongPieceDirection.Left, typeGenerator); BuildHorizontalWall(ref i, 165, 530, 0, MahjongPieceDirection.Down, typeGenerator); BuildHorizontalWall(ref i, 165, 525, 1, MahjongPieceDirection.Down, typeGenerator); BuildVerticalWall(ref i, 110, 165, 0, MahjongPieceDirection.Right, typeGenerator); BuildVerticalWall(ref i, 115, 165, 1, MahjongPieceDirection.Right, typeGenerator); } public override void GetProperties(IPropertyList list) { base.GetProperties(list); if (_spectatorVision) { list.Add(1062717); // Spectator Vision Enabled } else { list.Add(1062718); // Spectator Vision Disabled } } public override void GetContextMenuEntries(Mobile from, ref PooledRefList list) { base.GetContextMenuEntries(from, ref list); _players.CheckPlayers(); if (from.Alive && IsAccessibleTo(from) && _players.GetInGameMobiles(true, false).Count == 0) { list.Add(new ResetGameEntry()); } SetSecureLevelEntry.AddTo(from, this, ref list); } public override void OnDoubleClick(Mobile from) { _players.CheckPlayers(); _players.Join(from); } public void ResetGame(Mobile from) { if (Core.Now - _lastReset < TimeSpan.FromSeconds(5.0)) { return; } _lastReset = Core.Now; if (from != null) { _players.SendLocalizedMessage(1062771, from.Name); // ~1_name~ has reset the game. } _players.SendRelievePacket(true, true); BuildWalls(); DealerIndicator = new MahjongDealerIndicator(this, new Point2D(300, 300), MahjongPieceDirection.Up, MahjongWind.North); WallBreakIndicator = new MahjongWallBreakIndicator(this, new Point2D(335, 335)); Players = new MahjongPlayers(this, MaxPlayers, BaseScore); } public void ResetWalls(Mobile from) { if (Core.Now - _lastReset < TimeSpan.FromSeconds(5.0)) { return; } _lastReset = Core.Now; BuildWalls(); _players.SendTilesPacket(true, true); if (from != null) { _players.SendLocalizedMessage(1062696); // The dealer rebuilds the wall. } } public int GetStackLevel(MahjongPieceDim dim) { var level = -1; foreach (var tile in _tiles) { if (tile.StackLevel > level && dim.IsOverlapping(tile.Dimensions)) { level = tile.StackLevel; } } return level; } private void Deserialize(IGenericReader reader, int number) { _level = (SecureLevel)reader.ReadInt(); var length = reader.ReadInt(); _tiles = new MahjongTile[length]; for (var i = 0; i < length; i++) { var tile = _tiles[i] = new MahjongTile(this); tile.Deserialize(reader); } _dealerIndicator = new MahjongDealerIndicator(this); _dealerIndicator.Deserialize(reader); _wallBreakIndicator = new MahjongWallBreakIndicator(this); _wallBreakIndicator.Deserialize(reader); _dices = new MahjongDices(this); _dices.Deserialize(reader); _players = new MahjongPlayers(this); _players.Deserialize(reader); _showScores = reader.ReadBool(); _spectatorVision = reader.ReadBool(); } [AfterDeserialization] private void AfterDeserialization() { _lastReset = Core.Now; } private class ResetGameEntry : ContextMenuEntry { public ResetGameEntry() : base(6162) { } public override void OnClick(Mobile from, IEntity target) { if (from.CheckAlive() && target is MahjongGame { Deleted: false } game && game.IsAccessibleTo(from) && game.Players.GetInGameMobiles(true, false).Count == 0) { game.ResetGame(from); } } } }