From e440df03dba52d7bda7064d8d84322ab62e4efcf Mon Sep 17 00:00:00 2001 From: Vera Aguilera Puerto <6766154+Zumorica@users.noreply.github.com> Date: Sat, 18 Sep 2021 10:40:38 +0200 Subject: [PATCH] =?UTF-8?q?Adds=20parch=C3=ADs=20game=20board,=20improves?= =?UTF-8?q?=20tabletop=20system=20to=20support=20other=20games.=20(#4610)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/TabletopGameComponent.cs | 14 +++ Content.Server/Tabletop/TabletopChessSetup.cs | 80 ++++++++++++++++++ .../Tabletop/TabletopParchisSetup.cs | 60 +++++++++++++ Content.Server/Tabletop/TabletopSetup.cs | 12 +++ .../Tabletop/TabletopSystem.Chess.cs | 73 ---------------- Content.Server/Tabletop/TabletopSystem.cs | 30 +++++-- Resources/Locale/en-US/tabletop/tabletop.ftl | 5 ++ .../Entities/Objects/Fun/Tabletop/chess.yml | 3 + .../Entities/Objects/Fun/Tabletop/parchis.yml | 74 ++++++++++++++++ .../Fun/Tabletop/parchis.rsi/board.png | Bin 0 -> 451 bytes .../Fun/Tabletop/parchis.rsi/meta.json | 14 +++ .../Fun/Tabletop/parchis_pieces.rsi/blue.png | Bin 0 -> 215 bytes .../Fun/Tabletop/parchis_pieces.rsi/green.png | Bin 0 -> 215 bytes .../Fun/Tabletop/parchis_pieces.rsi/meta.json | 23 +++++ .../Fun/Tabletop/parchis_pieces.rsi/red.png | Bin 0 -> 215 bytes .../Tabletop/parchis_pieces.rsi/yellow.png | Bin 0 -> 215 bytes .../Tabletop/parchis_tabletop.rsi/board.png | Bin 0 -> 9877 bytes .../Tabletop/parchis_tabletop.rsi/meta.json | 14 +++ 18 files changed, 320 insertions(+), 82 deletions(-) create mode 100644 Content.Server/Tabletop/TabletopChessSetup.cs create mode 100644 Content.Server/Tabletop/TabletopParchisSetup.cs create mode 100644 Content.Server/Tabletop/TabletopSetup.cs delete mode 100644 Content.Server/Tabletop/TabletopSystem.Chess.cs create mode 100644 Resources/Prototypes/Entities/Objects/Fun/Tabletop/parchis.yml create mode 100644 Resources/Textures/Objects/Fun/Tabletop/parchis.rsi/board.png create mode 100644 Resources/Textures/Objects/Fun/Tabletop/parchis.rsi/meta.json create mode 100644 Resources/Textures/Objects/Fun/Tabletop/parchis_pieces.rsi/blue.png create mode 100644 Resources/Textures/Objects/Fun/Tabletop/parchis_pieces.rsi/green.png create mode 100644 Resources/Textures/Objects/Fun/Tabletop/parchis_pieces.rsi/meta.json create mode 100644 Resources/Textures/Objects/Fun/Tabletop/parchis_pieces.rsi/red.png create mode 100644 Resources/Textures/Objects/Fun/Tabletop/parchis_pieces.rsi/yellow.png create mode 100644 Resources/Textures/Objects/Fun/Tabletop/parchis_tabletop.rsi/board.png create mode 100644 Resources/Textures/Objects/Fun/Tabletop/parchis_tabletop.rsi/meta.json diff --git a/Content.Server/Tabletop/Components/TabletopGameComponent.cs b/Content.Server/Tabletop/Components/TabletopGameComponent.cs index 0e755193d6..6e3a5b9105 100644 --- a/Content.Server/Tabletop/Components/TabletopGameComponent.cs +++ b/Content.Server/Tabletop/Components/TabletopGameComponent.cs @@ -2,6 +2,8 @@ using Content.Shared.Verbs; using Robust.Shared.GameObjects; using Robust.Shared.Localization; +using Robust.Shared.Maths; +using Robust.Shared.Serialization.Manager.Attributes; namespace Content.Server.Tabletop.Components { @@ -13,6 +15,18 @@ namespace Content.Server.Tabletop.Components { public override string Name => "TabletopGame"; + [DataField("boardName")] + public string BoardName { get; } = "tabletop-default-board-name"; + + [DataField("setup", required: true)] + public TabletopSetup Setup { get; } = new TabletopChessSetup(); + + [DataField("size")] + public Vector2i Size { get; } = (300, 300); + + [DataField("cameraZoom")] + public Vector2 CameraZoom { get; } = Vector2.One; + /// /// A verb that allows the player to start playing a tabletop game. /// diff --git a/Content.Server/Tabletop/TabletopChessSetup.cs b/Content.Server/Tabletop/TabletopChessSetup.cs new file mode 100644 index 0000000000..9b5dfbd09d --- /dev/null +++ b/Content.Server/Tabletop/TabletopChessSetup.cs @@ -0,0 +1,80 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.Map; +using Robust.Shared.Serialization.Manager.Attributes; + +namespace Content.Server.Tabletop +{ + public class TabletopChessSetup : TabletopSetup + { + [DataField("boardPrototype")] + public string ChessBoardPrototype { get; } = "ChessBoardTabletop"; + + // TODO: Un-hardcode the rest of entity prototype IDs, probably. + + public override void SetupTabletop(MapId mapId, IEntityManager entityManager) + { + var chessboard = entityManager.SpawnEntity(ChessBoardPrototype, new MapCoordinates(-1, 0, mapId)); + chessboard.Transform.Anchored = true; + + SpawnPieces(entityManager, new MapCoordinates(-4.5f, 3.5f, mapId)); + } + + private void SpawnPieces(IEntityManager entityManager, MapCoordinates topLeft, float separation = 1f) + { + var (mapId, x, y) = topLeft; + + // Spawn all black pieces + SpawnPiecesRow(entityManager, "Black", topLeft, separation); + SpawnPawns(entityManager, "Black", new MapCoordinates(x, y - separation, mapId) , separation); + + // Spawn all white pieces + SpawnPawns(entityManager, "White", new MapCoordinates(x, y - 6 * separation, mapId) , separation); + SpawnPiecesRow(entityManager, "White", new MapCoordinates(x, y - 7 * separation, mapId), separation); + + // Extra queens + entityManager.SpawnEntity("BlackQueen", new MapCoordinates(x + 9 * separation + 9f / 32, y - 3 * separation, mapId)); + entityManager.SpawnEntity("WhiteQueen", new MapCoordinates(x + 9 * separation + 9f / 32, y - 4 * separation, mapId)); + } + + // TODO: refactor to load FEN instead + private void SpawnPiecesRow(IEntityManager entityManager, string color, MapCoordinates left, float separation = 1f) + { + const string piecesRow = "rnbqkbnr"; + + var (mapId, x, y) = left; + + for (int i = 0; i < 8; i++) + { + switch (piecesRow[i]) + { + case 'r': + entityManager.SpawnEntity(color + "Rook", new MapCoordinates(x + i * separation, y, mapId)); + break; + case 'n': + entityManager.SpawnEntity(color + "Knight", new MapCoordinates(x + i * separation, y, mapId)); + break; + case 'b': + entityManager.SpawnEntity(color + "Bishop", new MapCoordinates(x + i * separation, y, mapId)); + break; + case 'q': + entityManager.SpawnEntity(color + "Queen", new MapCoordinates(x + i * separation, y, mapId)); + break; + case 'k': + entityManager.SpawnEntity(color + "King", new MapCoordinates(x + i * separation, y, mapId)); + break; + } + } + } + + // TODO: refactor to load FEN instead + private void SpawnPawns(IEntityManager entityManager, string color, MapCoordinates left, float separation = 1f) + { + var (mapId, x, y) = left; + + for (int i = 0; i < 8; i++) + { + entityManager.SpawnEntity(color + "Pawn", new MapCoordinates(x + i * separation, y, mapId)); + } + } + } +} diff --git a/Content.Server/Tabletop/TabletopParchisSetup.cs b/Content.Server/Tabletop/TabletopParchisSetup.cs new file mode 100644 index 0000000000..89d1001851 --- /dev/null +++ b/Content.Server/Tabletop/TabletopParchisSetup.cs @@ -0,0 +1,60 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.Map; +using Robust.Shared.Serialization.Manager.Attributes; + +namespace Content.Server.Tabletop +{ + public class TabletopParchisSetup : TabletopSetup + { + [DataField("boardPrototype")] + public string ParchisBoardPrototype { get; } = "ParchisBoardTabletop"; + + [DataField("redPiecePrototype")] + public string RedPiecePrototype { get; } = "RedParchisPiece"; + + [DataField("greenPiecePrototype")] + public string GreenPiecePrototype { get; } = "GreenParchisPiece"; + + [DataField("yellowPiecePrototype")] + public string YellowPiecePrototype { get; } = "YellowParchisPiece"; + + [DataField("bluePiecePrototype")] + public string BluePiecePrototype { get; } = "BlueParchisPiece"; + + public override void SetupTabletop(MapId mapId, IEntityManager entityManager) + { + var board = entityManager.SpawnEntity(ParchisBoardPrototype, new MapCoordinates(0, 0, mapId)); + board.Transform.Anchored = true; + + const float x1 = 6.25f; + const float x2 = 4.25f; + + const float y1 = 6.25f; + const float y2 = 4.25f; + + // Red pieces. + entityManager.SpawnEntity(RedPiecePrototype, new MapCoordinates(-x1, -y1, mapId)); + entityManager.SpawnEntity(RedPiecePrototype, new MapCoordinates(-x1, -y2, mapId)); + entityManager.SpawnEntity(RedPiecePrototype, new MapCoordinates(-x2, -y1, mapId)); + entityManager.SpawnEntity(RedPiecePrototype, new MapCoordinates(-x2, -y2, mapId)); + + // Green pieces. + entityManager.SpawnEntity(GreenPiecePrototype, new MapCoordinates(x1, -y1, mapId)); + entityManager.SpawnEntity(GreenPiecePrototype, new MapCoordinates(x1, -y2, mapId)); + entityManager.SpawnEntity(GreenPiecePrototype, new MapCoordinates(x2, -y1, mapId)); + entityManager.SpawnEntity(GreenPiecePrototype, new MapCoordinates(x2, -y2, mapId)); + + // Yellow pieces. + entityManager.SpawnEntity(YellowPiecePrototype, new MapCoordinates(x1, y1, mapId)); + entityManager.SpawnEntity(YellowPiecePrototype, new MapCoordinates(x1, y2, mapId)); + entityManager.SpawnEntity(YellowPiecePrototype, new MapCoordinates(x2, y1, mapId)); + entityManager.SpawnEntity(YellowPiecePrototype, new MapCoordinates(x2, y2, mapId)); + + // Blue pieces. + entityManager.SpawnEntity(BluePiecePrototype, new MapCoordinates(-x1, y1, mapId)); + entityManager.SpawnEntity(BluePiecePrototype, new MapCoordinates(-x1, y2, mapId)); + entityManager.SpawnEntity(BluePiecePrototype, new MapCoordinates(-x2, y1, mapId)); + entityManager.SpawnEntity(BluePiecePrototype, new MapCoordinates(-x2, y2, mapId)); + } + } +} diff --git a/Content.Server/Tabletop/TabletopSetup.cs b/Content.Server/Tabletop/TabletopSetup.cs new file mode 100644 index 0000000000..75710dc92f --- /dev/null +++ b/Content.Server/Tabletop/TabletopSetup.cs @@ -0,0 +1,12 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.Map; +using Robust.Shared.Serialization.Manager.Attributes; + +namespace Content.Server.Tabletop +{ + [ImplicitDataDefinitionForInheritors] + public abstract class TabletopSetup + { + public abstract void SetupTabletop(MapId mapId, IEntityManager entityManager); + } +} diff --git a/Content.Server/Tabletop/TabletopSystem.Chess.cs b/Content.Server/Tabletop/TabletopSystem.Chess.cs deleted file mode 100644 index 28fb5d6694..0000000000 --- a/Content.Server/Tabletop/TabletopSystem.Chess.cs +++ /dev/null @@ -1,73 +0,0 @@ -using Robust.Shared.Map; - -namespace Content.Server.Tabletop -{ - public partial class TabletopSystem - { - private void SetupChessBoard(MapId mapId) - { - var chessboard = EntityManager.SpawnEntity("ChessBoardTabletop", new MapCoordinates(-1, 0, mapId)); - chessboard.Transform.Anchored = true; - - SpawnPieces(new MapCoordinates(-4.5f, 3.5f, mapId)); - } - - private void SpawnPieces(MapCoordinates topLeft, float separation = 1f) - { - var (mapId, x, y) = topLeft; - - // Spawn all black pieces - SpawnPiecesRow("Black", topLeft, separation); - SpawnPawns("Black", new MapCoordinates(x, y - separation, mapId) , separation); - - // Spawn all white pieces - SpawnPawns("White", new MapCoordinates(x, y - 6 * separation, mapId) , separation); - SpawnPiecesRow("White", new MapCoordinates(x, y - 7 * separation, mapId), separation); - - // Extra queens - EntityManager.SpawnEntity("BlackQueen", new MapCoordinates(x + 9 * separation + 9f / 32, y - 3 * separation, mapId)); - EntityManager.SpawnEntity("WhiteQueen", new MapCoordinates(x + 9 * separation + 9f / 32, y - 4 * separation, mapId)); - } - - // TODO: refactor to load FEN instead - private void SpawnPiecesRow(string color, MapCoordinates left, float separation = 1f) - { - const string piecesRow = "rnbqkbnr"; - - var (mapId, x, y) = left; - - for (int i = 0; i < 8; i++) - { - switch (piecesRow[i]) - { - case 'r': - EntityManager.SpawnEntity(color + "Rook", new MapCoordinates(x + i * separation, y, mapId)); - break; - case 'n': - EntityManager.SpawnEntity(color + "Knight", new MapCoordinates(x + i * separation, y, mapId)); - break; - case 'b': - EntityManager.SpawnEntity(color + "Bishop", new MapCoordinates(x + i * separation, y, mapId)); - break; - case 'q': - EntityManager.SpawnEntity(color + "Queen", new MapCoordinates(x + i * separation, y, mapId)); - break; - case 'k': - EntityManager.SpawnEntity(color + "King", new MapCoordinates(x + i * separation, y, mapId)); - break; - } - } - } - - // TODO: refactor to load FEN instead - private void SpawnPawns(string color, MapCoordinates left, float separation = 1f) - { - var (mapId, x, y) = left; - - for (int i = 0; i < 8; i++) - { - EntityManager.SpawnEntity(color + "Pawn", new MapCoordinates(x + i * separation, y, mapId)); - } - } - } -} diff --git a/Content.Server/Tabletop/TabletopSystem.cs b/Content.Server/Tabletop/TabletopSystem.cs index b6b3b246b4..35551592a8 100644 --- a/Content.Server/Tabletop/TabletopSystem.cs +++ b/Content.Server/Tabletop/TabletopSystem.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; using Content.Server.Tabletop.Components; +using Content.Shared.ActionBlocker; +using Content.Shared.Interaction; using Content.Shared.Tabletop; using Content.Shared.Tabletop.Events; using JetBrains.Annotations; @@ -8,6 +10,7 @@ using Robust.Server.Player; using Robust.Shared.GameObjects; using Robust.Shared.GameStates; using Robust.Shared.IoC; +using Robust.Shared.Localization; using Robust.Shared.Map; using Robust.Shared.Maths; using DrawDepth = Content.Shared.DrawDepth.DrawDepth; @@ -19,6 +22,7 @@ namespace Content.Server.Tabletop { [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly ViewSubscriberSystem _viewSubscriberSystem = default!; + [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; /// /// All tabletop games currently in progress. Sessions are associated with an entity UID, which acts as a @@ -31,10 +35,17 @@ namespace Content.Server.Tabletop SubscribeNetworkEvent(OnTabletopMove); SubscribeNetworkEvent(OnDraggingPlayerChanged); SubscribeNetworkEvent(OnStopPlaying); + SubscribeLocalEvent(OnTabletopActivate); SubscribeLocalEvent(OnGameShutdown); SubscribeLocalEvent(GetCompState); } + private void OnTabletopActivate(EntityUid uid, TabletopGameComponent component, ActivateInWorldEvent args) + { + if(_actionBlockerSystem.CanInteract(args.User)) + OpenTable(args.User, args.Target); + } + private void GetCompState(EntityUid uid, TabletopDraggableComponent component, ref ComponentGetState args) { args.State = new TabletopDraggableComponentState(component.DraggingPlayer); @@ -47,19 +58,19 @@ namespace Content.Server.Tabletop /// The entity with which the tabletop game session will be associated. public void OpenTable(IEntity user, IEntity table) { - if (user.PlayerSession() is not { } playerSession) return; + if (user.PlayerSession() is not { } playerSession + || !table.TryGetComponent(out TabletopGameComponent? tabletop)) return; // Make sure we have a session, and add the player to it - var session = EnsureSession(table.Uid); + var session = EnsureSession(table.Uid, tabletop); session.StartPlaying(playerSession); // Create a camera for the user to use // TODO: set correct coordinates, depending on the piece the game was started from - IEntity camera = CreateCamera(user, new MapCoordinates(0, 0, session.MapId)); + IEntity camera = CreateCamera(tabletop, user, new MapCoordinates(0, 0, session.MapId)); // Tell the client to open a viewport for the tabletop game - // TODO: use actual title/size from prototype, for now we assume its chess - RaiseNetworkEvent(new TabletopPlayEvent(table.Uid, camera.Uid, "Chess", (274 + 64, 274)), playerSession.ConnectedClient); + RaiseNetworkEvent(new TabletopPlayEvent(table.Uid, camera.Uid, Loc.GetString(tabletop.BoardName), tabletop.Size), playerSession.ConnectedClient); } /// @@ -67,7 +78,7 @@ namespace Content.Server.Tabletop /// /// The entity UID to ensure a session for. /// The created/stored tabletop game session. - private TabletopSession EnsureSession(EntityUid uid) + private TabletopSession EnsureSession(EntityUid uid, TabletopGameComponent tabletop) { // We already have a session, return it // TODO: if tables are connected, treat them as a single entity @@ -88,8 +99,7 @@ namespace Content.Server.Tabletop var session = _gameSessions[uid]; // Since this is the first time opening this session, set up the game - // TODO: don't assume we're playing chess - SetupChessBoard(session.MapId); + tabletop.Setup.SetupTabletop(session.MapId, EntityManager); return session; } @@ -169,10 +179,11 @@ namespace Content.Server.Tabletop /// /// Create a camera entity for a user to control, and add the user to the view subscribers. /// + /// The tabletop to create the camera for. /// The user entity to create this camera for and add to the view subscribers. /// The map coordinates to spawn this camera at. // TODO: this can probably be generalized into a "CctvSystem" or whatever - private IEntity CreateCamera(IEntity user, MapCoordinates coordinates) + private IEntity CreateCamera(TabletopGameComponent tabletop, IEntity user, MapCoordinates coordinates) { // Spawn an empty entity at the coordinates var camera = EntityManager.SpawnEntity(null, coordinates); @@ -180,6 +191,7 @@ namespace Content.Server.Tabletop // Add an eye component and disable FOV var eyeComponent = camera.EnsureComponent(); eyeComponent.DrawFov = false; + eyeComponent.Zoom = tabletop.CameraZoom; // Add the user to the view subscribers. If there is no player session, just skip this step if (user.PlayerSession() is { } playerSession) diff --git a/Resources/Locale/en-US/tabletop/tabletop.ftl b/Resources/Locale/en-US/tabletop/tabletop.ftl index 126177e30e..854b71a020 100644 --- a/Resources/Locale/en-US/tabletop/tabletop.ftl +++ b/Resources/Locale/en-US/tabletop/tabletop.ftl @@ -1,5 +1,10 @@ ## TabletopGameComponent tabletop-verb-play-game = Play Game +tabletop-default-board-name = Board Game ## Chess +tabletop-chess-board-name = Chess tabletop-chess-flip = Flip + +## Parchís +tabletop-parchis-board-name = Parchís diff --git a/Resources/Prototypes/Entities/Objects/Fun/Tabletop/chess.yml b/Resources/Prototypes/Entities/Objects/Fun/Tabletop/chess.yml index 79f63df0f7..c4cd6956a8 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/Tabletop/chess.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/Tabletop/chess.yml @@ -9,6 +9,9 @@ sprite: Objects/Fun/Tabletop/chessboard.rsi state: chessboard - type: TabletopGame + boardName: tabletop-chess-board-name + size: 338, 274 + setup: !type:TabletopChessSetup # Chessboard tabletop item (item only visible in tabletop game) - type: entity diff --git a/Resources/Prototypes/Entities/Objects/Fun/Tabletop/parchis.yml b/Resources/Prototypes/Entities/Objects/Fun/Tabletop/parchis.yml new file mode 100644 index 0000000000..43f694a529 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Fun/Tabletop/parchis.yml @@ -0,0 +1,74 @@ +# Parchís board item (normal in game item you can hold in your hand) +- type: entity + parent: BaseItem + id: ParchisBoard + name: parchís board + description: Cross and circle board game famous for destroying countless friendships. + components: + - type: Sprite + sprite: Objects/Fun/Tabletop/parchis.rsi + state: board + - type: TabletopGame + boardName: tabletop-parchis-board-name + size: 574, 574 + setup: + !type:TabletopParchisSetup + +# Parchís tabletop item (item only visible in tabletop game) +- type: entity + id: ParchisBoardTabletop + name: parchís + abstract: true + components: + - type: Sprite + sprite: Objects/Fun/Tabletop/parchis_tabletop.rsi + state: board + noRot: false + drawdepth: FloorTiles + +# Parchís pieces +- type: entity + id: BaseParchisPiece + parent: BaseItem + abstract: true + components: + - type: TabletopDraggable + - type: Sprite + netsync: false + noRot: true + sprite: Objects/Fun/Tabletop/parchis_pieces.rsi + - type: Appearance + visuals: + - type: TabletopItemVisualizer + +- type: entity + id: RedParchisPiece + name: red piece + parent: BaseParchisPiece + components: + - type: Sprite + state: red + +- type: entity + id: GreenParchisPiece + name: green piece + parent: BaseParchisPiece + components: + - type: Sprite + state: green + +- type: entity + id: YellowParchisPiece + name: yellow piece + parent: BaseParchisPiece + components: + - type: Sprite + state: yellow + +- type: entity + id: BlueParchisPiece + name: blue piece + parent: BaseParchisPiece + components: + - type: Sprite + state: blue diff --git a/Resources/Textures/Objects/Fun/Tabletop/parchis.rsi/board.png b/Resources/Textures/Objects/Fun/Tabletop/parchis.rsi/board.png new file mode 100644 index 0000000000000000000000000000000000000000..79f577cafe3b6940c73bc881856ca8433ab14944 GIT binary patch literal 451 zcmV;!0X+VRP)Px$eMv+?R5*>Tl)p;?VHn3hih~hEgAF<~2SHa7C}|B5Q6%I^v}W;3);3mDZ728Oc5~oN zKqaB9uD6KHG_zD}QJCD8Ru)O}HDq~`u|m}}An`TA&R(`{g8NG^+3am+B7j8SGY6&q zZedNL>x*3gYUvm3kzsZ7E|TIn@Tty^LapOz`#C+|rAPkss%a84*+k_Q#)Bb=Rx26h z)*PBKn)hhjKcVwZ3V6SD4oZ)ytfs0vX)kpvU002ovPDHLkV1n5tzCZu~ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/Tabletop/parchis.rsi/meta.json b/Resources/Textures/Objects/Fun/Tabletop/parchis.rsi/meta.json new file mode 100644 index 0000000000..66a7d89f26 --- /dev/null +++ b/Resources/Textures/Objects/Fun/Tabletop/parchis.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Zumorica", + "size": { + "x": 18, + "y": 18 + }, + "states": [ + { + "name": "board" + } + ] +} diff --git a/Resources/Textures/Objects/Fun/Tabletop/parchis_pieces.rsi/blue.png b/Resources/Textures/Objects/Fun/Tabletop/parchis_pieces.rsi/blue.png new file mode 100644 index 0000000000000000000000000000000000000000..c9918b429348f926b8a2665f194cd65650880a97 GIT binary patch literal 215 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjjKx9jP7LeL$-D$|CVIL!hIn)) zCoC{bP;r|$sp({W&ZF;ga}-y|uV;#3o17%*$C+~Y=hCE&tA75-F?96az;%e{c&dS3 z_lC$OMTb=nTXY;BPRO>f^M1hN#d|!JQ`mKxAftq&#ER!t5)R*3&(<1b9Xt?i;PZ)* z=faj13$6$);<7H%Rb08lZ9B_+*OYLJ8#)KsPpmTP+}JV0YyL9^29Mvda=(siI|CiZ N;OXk;vd$@?2>{_mPEG&- literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/Tabletop/parchis_pieces.rsi/green.png b/Resources/Textures/Objects/Fun/Tabletop/parchis_pieces.rsi/green.png new file mode 100644 index 0000000000000000000000000000000000000000..6faef466c4618841065df74136ba85a873dc6bb9 GIT binary patch literal 215 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjjKx9jP7LeL$-D$|CVIL!hIn)) zCoC{bP;r|$sp({W&ZF;ga}-y|uV;#3o17%*$C+|?sqwwU(9b1p0*>ArxDN3gPc_i% z-VoWO=&e1*;<3Fg9oAwd_FPq zT-dT=!4;uJT-HUpiYs@xZD*P9ni6htL+2p-iB(3O8#`ur&40$gV5%4^XCE-f2Ix2j MPgg&ebxsLQ00;|982|tP literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/Tabletop/parchis_pieces.rsi/meta.json b/Resources/Textures/Objects/Fun/Tabletop/parchis_pieces.rsi/meta.json new file mode 100644 index 0000000000..5412a69967 --- /dev/null +++ b/Resources/Textures/Objects/Fun/Tabletop/parchis_pieces.rsi/meta.json @@ -0,0 +1,23 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Zumorica", + "size": { + "x": 24, + "y": 24 + }, + "states": [ + { + "name": "red" + }, + { + "name": "green" + }, + { + "name": "yellow" + }, + { + "name": "blue" + } + ] +} diff --git a/Resources/Textures/Objects/Fun/Tabletop/parchis_pieces.rsi/red.png b/Resources/Textures/Objects/Fun/Tabletop/parchis_pieces.rsi/red.png new file mode 100644 index 0000000000000000000000000000000000000000..5e0c0aef6ff0396a1005eda92dcc8248ca96544f GIT binary patch literal 215 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjjKx9jP7LeL$-D$|CVIL!hIn)) zCoC{bP;r|$sp({W&ZF;ga}-y|uV;#3o17%*$C+|i+hym5RWB?4_AXG3U`=d$Y$P$= z^@h$t0fnmtPSX|?oG_J{uUf#iwC(YxmQJP19Sl7@JXeZsc@%y(J+qV8n(#nZLhUC5 zTSin!Ko)0UtL)Bc6GCDZM>m~U+N3R$GwlI$O6bfZ5iUMU{fikGawf;hZ4R2N2y`5S Mr>mdKI;Vst0MF%2tpET3 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/Tabletop/parchis_pieces.rsi/yellow.png b/Resources/Textures/Objects/Fun/Tabletop/parchis_pieces.rsi/yellow.png new file mode 100644 index 0000000000000000000000000000000000000000..2a2fd48301e801514594d4ba761def5843590a3a GIT binary patch literal 215 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjjKx9jP7LeL$-D$|CVIL!hIn)) zCoC{bP;r|$sp({W&ZF;ga}-y|uV;#3o17%*$C+|i{cG*9Rch-yrY}&9U`=d$Y$P$= z^@h$t0fnmtPSX|?oG_J{uUf#iwC(YxmQJP19Sl7@JXeZsc@%y(J+qV8n(#nZLhUC5 zTSin!Ko)0UtL)Bc6GCDZM>m~U+N3R$GwlI$O6bfZ5iUMU{fikGIuc^#6z;`70XmMs M)78&qol`;+0HCByq5uE@ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Fun/Tabletop/parchis_tabletop.rsi/board.png b/Resources/Textures/Objects/Fun/Tabletop/parchis_tabletop.rsi/board.png new file mode 100644 index 0000000000000000000000000000000000000000..ddc8bd35899bd451264f335835b8537f5ad06aeb GIT binary patch literal 9877 zcmbt)cU)83wr+qZM3E9KLAnV7qEeI+>7pSZ9Sb4?0*dsG3Wg?CLW_t}q;C+Vssscn z(vc<|31uU_1XLgqBH_-k_c>?p_wKp(-FN@s$I4oBtu^PEV|-(b?^}_E`dSC}9o+|k zKn`eQFBn50%qYem+g@LMt*I&O=HYtB-sv_3A|8*6SHU)@^EX+V zl=;6?9M)(Q(e!>Jd?G95Y9`yE^M#*ZJ!<<+==_r~R8y0Y+bPALrKa9)?t=p#uW&)t z)JI2C58lja+z|~uTq?*TS_p?w79 zKVA9tJKx-gcIIyOl>BLa_vfY^3Z=b`1y&A?JdNB}`Xnr|egz)mUkeVk7x){{>#eR@ zT6{g%snJ*(coEB$nSnoW@D@_%wf^H!6uHIhC#!kwh0~$td{)!XwO~hWhdAl1wN+&v zQ#KaH(tF?4Dt5JSaSiWrIMlMK>XSMG`#oT9(Rx_zQ>hZnP2uu+pw1sf@5OX1xth>|;) zuif~jp{S@cmU4P)*H|ENpVg9c?diWZTl$xnAj|zj6FdFC()%TuAi`Kk;4~BliAxn` zf)IF65Ot4WCKw7iJ3xofjBaPAL7OgW6h9cw?Fk6C>I)NtO z_O?{S^&cvAsxY{fVMqLdY*ZaWgE{q`ZBBzYOgi*$tM5`OJFjv2G$&P+Ox*4t9vse_ zta<3`y}nRO7Ml;NH(+B-VfnrA*ged~WUREz(2nR8T^z>9$OBvRaa>KNK z4vTfCX;qy&K1*ulB*#)zxtV9&KkeP;=I2>GFHECsw~YT}3q^|GoWpw+$eojYr`E`p za48OwU5VuLWX`4ypHj13{rbagomKpiiM(M6DP`%pC$9rUOAGf)8~BlY2hKBY;dbY`Lkg~HQdMQ zfJqN#C>i8z`}zIWmVic$U3x&OEk_JF?Sp@HX`69eqY24&uK94K`6ak`5x#P zppJvWpbmVLo%NOQ{?}YroVz*QTKE|;gG6z?yxm$WpnV?%#aFq=Y=MP*@+Q*?LgfD; z#{Qj2(kc7*u&MD9X_4hN)Pa?+Kcp+G^Hx}Yh+u_1ajXLrVkynxFlP9DEYpw4gad}D zzy?U9A+H8=lBd-RExdXXFjh0uz2`WV8QE~?zMif~w>p6%T>_;O8SKH(eZL3L`UjB~5R+5$z*!wl&)Mxbt?o#gyr&V_PjVc(P&|nT4 z88=+&s}eFUK7YAPMXgT2^G}EP9k@dNHIc_YwmC%d0~*~1m||+C?{)uc$aR_X09Win zpz^Y3pJgX<<9oL$OH$S4Y7Zz6r{scga8$_~lP5ZkN@d2$nOv~dBgaclYcNahvzhL6 z-9c|{4Lk`DRJoeqB#Do2XkgW{v<#qoteYA!FUf>Mul7dAE7We!S>V^lEJ&i2=%wb? z$;n54GQIa``8@}Je;5qC%GKN|k!5@17C!ZujukE#dMt$xiHkiXx|Hi+S`3FqyZsD4K zVM*{c1NsAsdE81>Ik6sAgj=1jY`N!)+gx89&)W%0&mvJ5>W0#5Q&o=S&{hi7mBB<9 zIQs6j%}B-k{^%8FzYj;n@G&c7Tu|>gW%|q7-D@rP`5r{HOYoU$PheY)POQy0EsaIv zKj3ys?@ec~5=8)(AOwOo*x(Tbe5|`DveG?#!>OQ9>5Yt-bY-mEIp*(-nU9L|K8{!Q zOO=!zYR|hCu(QA?m&7M(4IQ-OfEDB@rRN-oam#WwXXWr>k-%LuYj0!|60_4}@^$JaZ7hK@e|EO*0_T`06jbL#ph$9rC|PUjHtz7#VJ^ z&p#6~Dc0bl_T6W>X5^;1;EMBV8kIFc7dU(vM`gLfP&ReoBx5W5MuiMn@|*&{j*gp0 zvA@I%2X#aX+!siQ{UT%td^D6-Y0u<(qYRDZ(=86z!K@bwJ@*0ys$652ImKf^fY@kAqHnV#@gVyc_aIS*fi>;sB$m}-T-ly@694KL$stU#6XrEiBB6z6Z$=d`~OPc|CI&) zndAR(Ui(_Zz;91(Ri)oo^06d?T=oo91aZ~c7 z%rs@z*NxP&!tz5D%$%dBPEheSODOL-y*{|KdL#g!tmv4U-`T|8KZa+V1%M+g8#t1r z$(*F*Z7->QfC0H%_1V?Qwy~?4y<@ZAE)Ndl)N2?{(f`pamX%cMJU&GaJi42Gj!yq) zaP#;4JWaAzE!1*;rE^W@FOu&Zzg z-wTGyDx8H||2S^QljkICbBtwpKN1!9zUGoSRZwCK*Q=uE?dM6xVKWTDNclOd$~@pB z#1(rrb-lxT+VbwHHJWj@Vw_!Nuaau}M$*zD%NVjC+Af~JkL?fFn5^EJA zehVhU+y{dzY9zRE$S|+5!Jn%qNQWbWHlZ+Gl+N?>___yBSck9<0*PS&@*TM7IybTe z1k;%acx5BoNipp2x>+H)+(@n{;h;Si+G--XBkX$ewwdp-W>M3kl-s?wuV%M!;!INuxMrX_4f=W~oWH%r`Ih8muGDVkaDi2rl zb=}$CN{Jo{k$`I72|JJaVEKI7E-D`0%Y)qH+ZX@M{}$nh1|eP=!@()UkHc+mw)fav z)b<~YAeg8jH}a!YxRIOtZKk^ocFt{WEotpgK|P8RI$m}si;q#Gt~6mM$axqOtPMG` zXmTO>TWnj|5*Kn4B%<_my0;W&%SBBtx!wn=y|*5LzL+rW8ZG|5LRs<9`cB#8!1mkY zGKAXQB6gD-71w1y*$7Z6DerUHMf&JDw>@r&N zx*mF$;%u$juq_}*X1{iA?IVbzH-@VPPrk^_bOfgvGxpjzV9y30#v(*-Y=Ua1GqEi6 z6!irni2PiWna?9oGJI|y@pcFl?gx_B;+2eiR}@UgCIrQDX*Te=nz1Tt{kk&i%ne!A z#$WNuY=jVNi$hcUQhq%AwbC>Za8F$~uWI$?Eu*yO-AQw5r!)wWo|tp^m;{4_98t`o zM;A7{C;lNDq4CeE*fbk?n@7|>BHq_4y%rtRgDV(P_YFp|cxGR~_iH6MUBdU$KI7pi{oYxr&?BEPR?)#B{6 z9si-Tont|Lq95HOx`CZL!gb_YX`rv1#8)Q}$Cm%9pp2_Yk8qD1xC(fj$P4 zK4n6E62?u?jf}yaF@`@wd=cyRz7Fs#7oX55f?*U_vd482xsiq(06cj_=fKfnP)2fz zu0?Q*ci(wpCUu%q%krMuh~IJz{8=Baqc>JM0F(&xOQ3DiuXHWqr;rY}!(1&Z3rv_!n&Xe);NMGUHL%9#w|d3(j#3C6Ss} zB;C>E37h-ip%%990Cp=}Q|ArbD1?VE@m4k1&pho~L-fTP4fwlooCHdTr%ey2ucW zBa4?V4+`?yCp%9$NS4NU-mH2cL^x{6RXPp#C_81>nQ4L)ZE0-vaX7b8auM&8zdkjZ zt0C(%E;Ea6lTeeyEEMAe%LZOZzG-^dc6Dd};^{mU=e|RZ0%1fkicgrDD>>7|$U|%n zaed8O#g-gUD`B9~WUl`h@n!LO&rJ*M_&4@(Av}n1{|8cIkv3|taT<;H3D!kx&)tqK znHlw#yKebs7L}&Tb>W&H7I~WLr&nufcggggC8hP*)wr!-X&Yw6zZJ&tN(6|VDortw z*PA->@p&okjC}GTPqfSnGyQ^%)=9atA@Md9jB)GahUw#PawhEyDFjQF6kP`arNE|N z6V&{fM&&&vq2Jz&mvwwmxJ0ozEn__5kvmMfa81UzywJbJ<@RfG+rqtGv^=i}uTt#M zHU6di^Z{y?qTO}R#J9Hd1=SxEK9Kuya|n`xW9RAuy<>Q8#K5^b*}bxvqFeebo`ZvF;6xjoYR@!hv3^7&HRW7fR`N867^;Y~fYbIqHb&k1YtVcb2$;mn4te%Np zg3}?SW3Be#<&Wr_lW(0e&X;vcNVwZVpWF3Fq?AqYWWPX`&hDU6W@~p+-to78@jyGi zo>|v>g}Cl_-O~|~nj0RtU0b5?n^t5zw`uq16qn`?R@NXQo=VE;fTGcE zUQgb}-)+f!Hm;5%K|b#SzRs7p4-Zcw+vXQ z-fnw4=O}el(PTNY$Qt@7ezBt3ERNmT@P4gykaE*wxD4}cEN;|dCPA;lh(T7+@hXQ~ zTYCExz~HzBcO~CAuWRpIHMd-jh{hE&5KEoc1MQ-9fZ&!5I19tD_h8l;>}Y}V>^*Ix z(Tf1!gC6QdGalPYt`OKx=uC|^10)7TA51Ig$F#?23>+o1zfBxA?JN$=n1F1V^6BR$ zh&I_KKtFL~z#!QfuThGAVcqpgqh2_&?){%!PgieEl;aRch42EH;iH!t9^0Fj2&;RD z1JUsq3QqDAu~BxVoDd^dVHHmwL~L`9b^l-N~SvY`vhLqz?k} zB@!;KdAfysSEKh-4q9>qSJ1=VI+TxCs4_@UuklcF(Gn0zs&WTbkd_0K3{pqflCjA2 zH!y`FSC$4+6Wg?-3w|B6bYL{vrP$+(Yeg10Y~A@KBIw}@(34H<-ojtLQ~JrknO;?| zx#});LD=<@O%APE$O3madG@rhN*VV^~@fw}gBvGFzOr*r}ng_-1|;%jb4z%%US_Ys~M6f-uEV|Z{- zrnmg05p#k6(@xMVb&{w;Vq<%qB(LDTo-Hi$fx!q7SVOjfWd>wPM*xXymp5jnJ$=Xl z%uk+yBXLW@I#%gd)o%AKrV;E}fNN6#dMEZNl(?qs(N<8d6oGdM*w-FD!DU$_(+d|Z zu>_~nC7#?2QuK(8PZSTrnc+AhU)u>+8(Zhx)`u;3FCtN{69FHNh+;IXlzwZ%wqs$- z?Es&1_eK~7Rp>-BZrc9&%09W4!TDXESl^(k6~2`(K?>FSl`eKmsbDHc#O;&sY>2x* zW=mAclO)t~!niE;020n7)CrzEciYp?Q@~Nh7c@ez%)74coGEgy9U!Kb^&dtS78*?S zQ+!`LqaD+~y3VN%5+^+9+r%%oYbSMxO1JOMbdT9FVGOO!q z>WG%{r=#T$5WY2442BI7Q{9d^E~8O}HDvp__{Ls^A?pL0?bm4mL6aVXsb6ZS2>S=Iw`P^i-2GRW?#$xe_}p=1X$In3za{qQh48 zOv8<5+TYEFZH}W#I0|G{1&XDjE!4P=p1ft8vQnsc;+y#jMV;d2ft>27rk~F zJJbugtm#ym&JF$Lq2;Tjw*%qjx&Hn-H@o;%+xPAh#>~2J?frxVU90u9au)Q{F|4qS zwJk3|KQ2_9gbX}fJV^Gw5JC6A%^y_*bELRLL}WWHVY%;by#^O53B56nByZQ}SXkQ% zWZA0y*;h14q~Jqje#~{N32yWHo%H5HE_rO_`rl&k){0waD5kw|RMNa)#1?bTs*jQJ z5t%m!jEsw2tk(B{CIWO7ptRkksTPqKqNvUy4}-ZvsRg4--vbCUH8Pf$A%uzNxBQ>& zz>i}X043glWVlol?#(E=W1JA860wN3N8%DUc8M%|7=isH?}D_Bv8oIqZ7$>CB~W)0 zra#i+)UC;WHjw2cZus!$rWuiq6Vma2RQU3}Ds_N5btQf<)7N^3)V{G&71iEwyqyKA zeYl2B&wD!;{BV2QAbu!Jt`Q&qO_-N%;LpkWIkP9qhxBaa3E(7+jGr%cTXv=GXJnk| zvPu46RA%f%b%c*MzMI%~Z5aWp4 zuR^pee9BJN#R>KJSL&JGTV4!1m9Dflr`FA@4$foK24mu@t16WFx35Quw^`n^wb8fo z5_0rHsb~;_7)ka=xi{X6v%`<=8DNwA0ziY=3DPV<2)v@_FDw@!Pp&9;Aw{WodvlV> zlha7nwop+f4;3)BiE?%e;knsh3OYe96faEdeN1#>M7(k#H`4FLyUGg_s!ijNB#z z&Q;%;I?^33F7fD_IOu{yi80*I=;OSkmBKVuBj)cFpY|Tc%E_J=G{>=gXM-y~L1fxr zfHfY&{?2Y4a@vTw9R`1{jsL8bP$vr-Y%V08ThvWegPA9iJC{q;O%NPKaL;myN&~8s zr;%+A907ic+d4$_0!&v7YZZtP*=xky#|EDl!9-{$)Y$_D$~r_Y0*?6OqI|Igf&%!2 zfbGq-*tazcORE89cFvH%uP^!zVHiv(y>vQ7&7XQ~&{y4Kx3khyX;>sHhl2WoK~t%I z*!p}ndXCj&kglZCk|oSU^mc!MP!^6IyOxrvok^yIQ&(@-ssrNwp!M=6yoyDcj93Td zprhdlPe4p#Fo7eyB0+$n_1AA#zX^CEUHtkIX~idiLCYNyj{~$^DC^M5kS3M9331|p zC1?Z6W|rSnz$=K5)W7ebUF@|(8e||}HSwm1S^sfeqnV??PicPSh~beur~ipA_IF~f zD{uR}@UQu;Uflf+5IYyHkk(j6|3P>&11fQs6h}>dPibK&C4VwiKx~md@+`cT^OGiP znHk53W;4;RmacaN-Tx0Rvxo-h=&tU0+M)NIT#}EB; z#k(r=DuY!zjMU5%b5K(>7+X$(o1!Q_p_ z_-Yr0{th%)Q6El?k-y6l0_WxxRKh~L* zkA;aWl;@d`9^4~Qi3S;7fI{rOC%m#hK?OmYtzO{~PeP*%BQoQ;S;uWCu!J_S9IItv zzL@0Y87t*K7XTV?r42Q@N=)*)v*o=iYQ-PV;iAV*96GA*3@qca96+mwz6{kaRCf!W?2rcpFjQrBeJPX zn?O2=pYY%QjYDeuh z;idpFo_g7!*yF!ZxRdPp}izG%f} zOaJ?ahrS!s^p&AhI~}~2x6srg!ZchssCBj(J7H6$HbthzY6Vo5mr>51d(;0%UJLpO zg%#K`c&;>+qx*yNOQppyi4QrYqtPg?o_tA%S$cuQGw7~ zYXplT1sJ_pQ4K8gduchs{uH1jz&Tg?TCLW#35Yx3ssFZzL5J|2OxM@^lJ2zebFFDv z2>@9pc}?TN4f@HI#t$c&56J_nG3e=T-mz8si{SE4iRDe&KT{D~BY?|2j8@Od5ztcH zrVM52AH0;U(k>%qsa$^t0)f`)xch$59keIRK`uj-ESY^lF6oCk@`k z27|h|7MT>EKL~&(*M2dHuO|lI&rlLTsLukG)yt5B)2WQVq=j9egGN*twNy_7S5V4a zQ~@Wd2PExHG)5$YAF$XW6!_87VN1iqK)isk4$+$RuY9pI1UV&%PpM*j+GUcP$ zxr;pCRyNd?y@C1>kZ~3?>i^mI2>%MD|Kg*CR0CS5`p;6zZ?tcVnjoTfRqI_{;4cOt zW9z3BVtI$ov~ap<#i`!{vWFJy%(uF$?M!|hpFe&Wqo@F!0WyDZ2f2dxIeR!K7MqlT&;O_a5x}N4X}Rmpq4m$kJap& z0Z<`ogg3}C7LGt&k`udk^g4SpjP*RGG#fo}O^LP_4?%)yhH{8SjD2XTCfL}>ZpG|8vUlHOAKh%Q}a24D>(8*!=`8+u}aGH!oF!e zu))n7xi|RlcJm4>_`6aUQ*wE0U)@=p9?T>k7BmEX_}^=XI_*jRxWbwv!<-t-?B}R! zcL(X$86OWwx1o^&7_Gp3aQNz<*uk6%X8dFpa5qe|a8w=YPtF>_l=}nFIJO39Dr&w; ze}F-U2sZFK&{wxo`9zpgHRB+5+=A-5fCq!LojNM=ev4XVy2SKlo;JTlsQv>GUXeF9 zccUQi