├── ParquetClassLibrary
├── PublicAPI.Shipped.txt
├── ParquetIcon.ico
├── Properties
│ ├── ParquetIcon.ico
│ └── Parquet Logo Large.png
├── Beings
│ ├── BeingStatus.cs
│ ├── IMutableCritterModel.cs
│ ├── IMutablePronounGroup.cs
│ ├── IMutableBeingModel.cs
│ ├── CritterModel.cs
│ └── IMutableCharacterModel.cs
├── Parquets
│ ├── ParquetStatus.cs
│ ├── GatherEffect.cs
│ ├── CollectEffect.cs
│ ├── EntryType.cs
│ ├── IMutableFloorModel.cs
│ ├── IMutableCollectibleModel.cs
│ ├── IMutableBlockModel.cs
│ ├── IMutableFurnishingModel.cs
│ ├── IMutableParquetModel.cs
│ ├── FloorModel.cs
│ └── CollectibleModel.cs
├── Scripts
│ ├── RunState.cs
│ ├── IMutableScriptModel.cs
│ ├── IMutableInteractionModel.cs
│ ├── Commands.cs
│ └── ScriptModel.cs
├── IDeeplyCloneable.cs
├── Items
│ ├── ModificationTool.cs
│ ├── GatheringTool.cs
│ ├── ItemType.cs
│ ├── IMutableItemModel.cs
│ └── InventoryConfiguration.cs
├── ILogger.cs
├── IntExtensions.cs
├── Crafts
│ ├── IMutableCraftingRecipe.cs
│ └── CraftConfiguration.cs
├── IReadOnlyGrid.cs
├── IVisibleData.cs
├── Rooms
│ ├── ReadOnlyRoomCollectionExtensions.cs
│ ├── IMutableRoomRecipe.cs
│ └── RoomConfiguration.cs
├── IMutableModelCollection.cs
├── Biomes
│ ├── IMutableBiomeRecipe.cs
│ └── BiomeConfiguration.cs
├── IGrid.cs
├── Games
│ └── IMutableGameModel.cs
├── Regions
│ ├── IMutableRegionModel.cs
│ └── ChunkTopography.cs
├── LoggerDefault.cs
├── LibraryState.cs
├── LogLevel.cs
├── GlobalSuppressions.cs
├── AssemblyInfo.cs
├── IReadOnlyListOfTagsExtensions.cs
├── IReadOnlyCollectionOfTagsExtensions.cs
├── IMutableModel.cs
├── IDictionaryExtensions.cs
├── Delimiters.cs
├── EmptyTolerantEnumConverter.cs
├── SeriesConverter.cs
└── ParquetClassLibrary.csproj
├── ExampleData
├── InteractionStatuses.csv
├── ScriptStatuses.csv
├── InventoryConfiguration.csv
├── GameStatuses.csv
├── CraftConfiguration.csv
├── RoomConfiguration.csv
├── BiomeConfiguration.csv
├── CharacterStatuses.csv
├── ScriptModels.csv
├── CraftingRecipes.csv
├── GameModels.csv
├── PronounGroups.csv
├── CritterModels.csv
├── RoomRecipes.csv
├── InteractionModels.csv
├── RegionModels.csv
├── ItemModels.csv
├── FurnishingModels.csv
├── FloorModels.csv
├── CharacterModels.csv
├── CollectibleModels.csv
├── BlockModels.csv
└── BiomeRecipes.csv
├── ParquetRunner
├── ParquetIcon.ico
├── AssemblyInfoRunner.cs
└── ParquetRunner.csproj
├── Documentation
├── Algorithms
│ ├── Extrema.png
│ ├── Scanning.png
│ ├── 4-Connected.png
│ ├── Just-Outside.png
│ ├── Valid_Rooms.png
│ └── Invalid_Rooms.png
├── Parquet_Project_Logo.png
├── Home.md
├── How_Elevation_Works_In_Parquet.md
├── 3-How_Parquets_Work.md
├── Lessons_Learned_Living_Document.md
├── 1-Terminology.md
├── 2-How_Parquet_Handles_Game_Objects.md
└── Parquet_CSharp_Style_Guide.md
├── ParquetUnitTests
├── ParquetIcon.ico
├── Regions
│ ├── ChunkDetailUnitTest.cs
│ ├── RegionStatusUnitTestExtensions.cs
│ └── RegionModelUnitTest.cs
├── RecipeElementUnitTest.cs
├── GlobalSuppressions.cs
├── Parquets
│ ├── FloorModelUnitTest.cs
│ ├── CollectibleModelUnitTest.cs
│ ├── ParquetPackUnitTest.cs
│ ├── BlockModelUnitTest.cs
│ └── FurnishingModelUnitTest.cs
├── AllUnitTest.cs
├── Beings
│ ├── CharacterModelUnitTest.cs
│ └── CritterModelUnitTest.cs
├── AssemblyInfoUnitTest.cs
├── Crafts
│ └── CraftingRecipeUnitTest.cs
├── ITypeConverterTest.cs
├── Point2DUnitTest.cs
├── Items
│ ├── InventorySlotUnitTest.cs
│ ├── ItemModelUnitTest.cs
│ └── InventoryCollectionUnitTest.cs
├── RangeUnitTest.cs
├── Rooms
│ └── RoomRecipeUnitTest.cs
└── ParquetUnitTests.csproj
├── CONTRIBUTING.md
├── LICENSE.txt
├── .gitattributes
├── .gitignore
├── CHANGELOG.md
├── README.md
└── Parquet.sln
/ParquetClassLibrary/PublicAPI.Shipped.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ExampleData/InteractionStatuses.csv:
--------------------------------------------------------------------------------
1 | Key,State,StepCounter
2 |
--------------------------------------------------------------------------------
/ExampleData/ScriptStatuses.csv:
--------------------------------------------------------------------------------
1 | Key,State,ProgramCounter
2 |
--------------------------------------------------------------------------------
/ExampleData/InventoryConfiguration.csv:
--------------------------------------------------------------------------------
1 | DefaultCapacity
2 | 16
3 |
--------------------------------------------------------------------------------
/ExampleData/GameStatuses.csv:
--------------------------------------------------------------------------------
1 | Key,PlayerCharacterID,CurrentScriptID
2 |
--------------------------------------------------------------------------------
/ExampleData/CraftConfiguration.csv:
--------------------------------------------------------------------------------
1 | IngredientCount,ProductCount
2 | 1–5,1–5
3 |
--------------------------------------------------------------------------------
/ExampleData/RoomConfiguration.csv:
--------------------------------------------------------------------------------
1 | MinWalkableSpaces,MaxWalkableSpaces
2 | 4,121
3 |
--------------------------------------------------------------------------------
/ExampleData/BiomeConfiguration.csv:
--------------------------------------------------------------------------------
1 | LandThresholdFactor,LiquidThresholdFactor,RoomThresholdFactor
2 | 1.25,0.25,0.67
3 |
--------------------------------------------------------------------------------
/ExampleData/CharacterStatuses.csv:
--------------------------------------------------------------------------------
1 | Key,Position,SpawnAt,RoomAssignment,CurrentBehaviorID,BiomeTimeRemaining,Inventory
2 |
--------------------------------------------------------------------------------
/ParquetRunner/ParquetIcon.ico:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:9c9b243e39c6d4933c94db387904a8f793158d74c12506ed7923223bac432145
3 | size 16798
4 |
--------------------------------------------------------------------------------
/Documentation/Algorithms/Extrema.png:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:0fbcce5f8ec3e909a3b4fcdc1dca4c1aff0344550852c909b346ef62f60d73fd
3 | size 1822
4 |
--------------------------------------------------------------------------------
/Documentation/Algorithms/Scanning.png:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:d240c923a616637e947c512d4f28d114ac53c1ff2c08f583e4772f27ddadd19c
3 | size 2445
4 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/ParquetIcon.ico:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:9c9b243e39c6d4933c94db387904a8f793158d74c12506ed7923223bac432145
3 | size 16798
4 |
--------------------------------------------------------------------------------
/ParquetUnitTests/ParquetIcon.ico:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:9c9b243e39c6d4933c94db387904a8f793158d74c12506ed7923223bac432145
3 | size 16798
4 |
--------------------------------------------------------------------------------
/Documentation/Algorithms/4-Connected.png:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:37999f968c372a70580b128aa9e7cf8035aa8ea013177739650555c501e27482
3 | size 2723
4 |
--------------------------------------------------------------------------------
/Documentation/Algorithms/Just-Outside.png:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:d27ba0508f266897c9cd2cc049331aaa6312f742ba96d82b19962c927ba6a9ab
3 | size 3487
4 |
--------------------------------------------------------------------------------
/Documentation/Algorithms/Valid_Rooms.png:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:e5c4fad30180a752ca4a4714933410b6a546d322bede47b82504088ed84b8e42
3 | size 17436
4 |
--------------------------------------------------------------------------------
/Documentation/Parquet_Project_Logo.png:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:f0ee6cfa83116050aaaaf087c4ffcd8e64ac3a2161d6bdfb7c281e4bc7d6a1f4
3 | size 3800
4 |
--------------------------------------------------------------------------------
/Documentation/Algorithms/Invalid_Rooms.png:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:a4ccabdf2cd180761b64def2de4a856ef21059e6aedb68bb1316abf667f13676
3 | size 20033
4 |
--------------------------------------------------------------------------------
/ExampleData/ScriptModels.csv:
--------------------------------------------------------------------------------
1 | ID,Name,Description,Comment,Tags,Nodes
2 | -120000,Script Test,Test,Test,Test-Tag,
3 | 120000,Dotdot Startup,,,,C·MISSY·DOTDOT❟S·DOTDOT·Howdy!❟=··Greeting
4 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Properties/ParquetIcon.ico:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:9c9b243e39c6d4933c94db387904a8f793158d74c12506ed7923223bac432145
3 | size 16798
4 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Properties/Parquet Logo Large.png:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:f0ee6cfa83116050aaaaf087c4ffcd8e64ac3a2161d6bdfb7c281e4bc7d6a1f4
3 | size 3800
4 |
--------------------------------------------------------------------------------
/ExampleData/CraftingRecipes.csv:
--------------------------------------------------------------------------------
1 | ID,Name,Description,Comment,Tags,Products,Ingredients
2 | -80000,Crafting Recipe Test,Test,Test,,1·Test Tag,1·Test Tag
3 | 80000,Ultra-Bread,Delicious and ultra-versatile.,,loaf,1·Bread,1·Flour
4 |
--------------------------------------------------------------------------------
/ExampleData/GameModels.csv:
--------------------------------------------------------------------------------
1 | ID,Name,Description,Comment,Tags,IsEpisode,EpisodeTitle,EpisodeNumber,PlayerCharacterID,FirstScriptID
2 | -1,Game Test,Test,Test,,False,,0,None,None
3 | 1,First Game,,This tests the graphic loading.,,False,,0,60001,120000
4 |
--------------------------------------------------------------------------------
/ExampleData/PronounGroups.csv:
--------------------------------------------------------------------------------
1 | Subjective,Objective,Determiner,Possessive,Reflexive
2 | e,em,eir,eirs,emself
3 | fae,faer,faer,faers,faerself
4 | she,her,her,hers,herself
5 | they,them,their,theirs,themself
6 | xe,xem,xyr,xyrs,xemself
7 | ze,zer,zir,zirs,zirself
8 |
--------------------------------------------------------------------------------
/ExampleData/CritterModels.csv:
--------------------------------------------------------------------------------
1 | ID,Name,Description,Comment,Tags,NativeBiomeID,PrimaryBehaviorID,AvoidsIDs,SeeksIDs
2 | -50000,Test Critter,Test,Test,,None,120000,,
3 | 50000,Ladybug,Dainty and red!,,,70004,120005,,
4 | 50001,Blue Milkweed Beetle,Dark and dutiful!,,,70003,120001,,
5 |
--------------------------------------------------------------------------------
/ExampleData/RoomRecipes.csv:
--------------------------------------------------------------------------------
1 | ID,Name,Description,Comment,Tags,MinimumWalkableSpaces,OptionallyRequiredFurnishings,OptionallyRequiredWalkableFloors,OptionallyRequiredPerimeterBlocks
2 | -90000,Room Recipe Test,Test,Test,,20,1·Test Tag,1·Test Tag,1·Test Tag
3 | 90000,Room,A basic space.,n/a,,4,,,
4 |
--------------------------------------------------------------------------------
/ParquetRunner/AssemblyInfoRunner.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | // Make no promises to maintain public services.
5 | [assembly: ComVisible(false)]
6 |
7 | // Runner is an API consumer, not an API provider.
8 | [assembly: CLSCompliant(false)]
9 |
10 |
--------------------------------------------------------------------------------
/ExampleData/InteractionModels.csv:
--------------------------------------------------------------------------------
1 | ID,Name,Description,Comment,Tags,PrerequisitesIDs,StepsIDs,OutcomesIDs
2 | -130000,Interaction Test,Test,Test,,,,
3 | 130000,Scarf Talk,prompt for getting scarves,"It's mighty cold out; take this with you, dearie.",,,,
4 | 130001,Find A Scarf,The quest to find a scarf!,,,,,
5 |
--------------------------------------------------------------------------------
/ExampleData/RegionModels.csv:
--------------------------------------------------------------------------------
1 | ID,Name,Description,Comment,Tags,BackgroundColor,RegionToTheNorth,RegionToTheEast,RegionToTheSouth,RegionToTheWest,RegionAbove,RegionBelow
2 | -110000,Map Test,Test,Test,,#000000,-110000,-110000,-110000,-110000,-110000,-110000
3 | 110000,Simple Map,,,,#FFFFFF,None,None,None,None,None,None
4 |
--------------------------------------------------------------------------------
/ExampleData/ItemModels.csv:
--------------------------------------------------------------------------------
1 | ID,Name,Description,Comment,Tags,Subtype,Worth,Rarity,StackMax,EffectWhileHeldID,EffectWhenUsedID,ParquetID
2 | -140000,Item Test,Test,Test,,Consumable,10,3,99,None,-120000,None
3 | 140000,Red Apple,Red and tasty!,,,Consumable,2,1,30,120000,120001,None
4 | 140001,Green Apple,Green for baking!,,Frizzle's Fave,Consumable,2,1,30,None,None,20001
5 |
--------------------------------------------------------------------------------
/ExampleData/FurnishingModels.csv:
--------------------------------------------------------------------------------
1 | ID,Name,Description,Comment,Tags,ItemID,AddsToBiome,AddsToRoom,IsWalkable,Entry,IsEnclosing,IsFlammable,IsOpenable
2 | -30000,3 Furnishing Test Parquet,Test,Test,,-140003,,,True,Up,True,False,False
3 | 30000,Chair,,,,140003,,Usable Surface,True,None,False,True,False
4 | 30001,Table,,,,140004,,Seating,False,None,True,True,False
5 | 30002,Wooden Door,,,,140005,,,True,Room,True,True,True
6 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Beings/BeingStatus.cs:
--------------------------------------------------------------------------------
1 | namespace Parquet.Beings
2 | {
3 | ///
4 | /// Tracks the status of a during play.
5 | /// Instances of this class are mutable during play.
6 | ///
7 | public abstract class BeingStatus : Status
8 | where T : BeingModel
9 | {
10 | // Currently, everything needed for tracking BeingModels is provided by Status.
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/ExampleData/FloorModels.csv:
--------------------------------------------------------------------------------
1 | ID,Name,Description,Comment,Tags,ItemID,AddsToBiome,AddsToRoom,ModTool,TrenchName
2 | -10000,1 Floor Test Parquet,Test,Test,,-140000,,,None,dark hole
3 | 10000,grass,,,,None,,,Shovel,dirty pit
4 | 10001,weeds,,,,140001,Swampy,,Shovel,muddy pit
5 | 10002,sand,,,,None,Deserted,,Shovel,sandy pit
6 | 10003,snow,,,,None,Frozen,,Shovel,snowy pit
7 | 10004,stone floor,,,,None,,Stone,Hammer,stony trench
8 | 10005,red brick,,,,None,,Brick,Hammer,red brick channel
9 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Parquets/ParquetStatus.cs:
--------------------------------------------------------------------------------
1 | namespace Parquet.Parquets
2 | {
3 | ///
4 | /// Tracks the status of a during play.
5 | /// Instances of this class are mutable during play.
6 | ///
7 | public abstract class ParquetStatus : Status
8 | where T : ParquetModel
9 | {
10 | // Currently, everything needed for tracking ParquetModels is provided by Status.
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/ExampleData/CharacterModels.csv:
--------------------------------------------------------------------------------
1 | ID,Name,Description,Comment,Tags,NativeBiomeID,PrimaryBehaviorID,AvoidsIDs,SeeksIDs,PronounKey,StoryCharacterID,StartingLocation,StartingQuestIDs,StartingDialogueID,StartingInventory
2 | -60000,Test§NPC,Test,Test,Test-Tag,None,120000,,,xe/xem,TEST_NPC,None·0–0,,None,
3 | 60000,Missy§Titan,Sharp and sweet.,Come on in!,,70004,120001,,,she/her,MISSY_TITAN,None·0–0,130000,None,
4 | 60001,DotDot§Dot,Loves to understand how things work.,,,70010,120002,,,they/them,DOTDOT,130001·2–2,,None,140000·5❟140001·2
5 |
--------------------------------------------------------------------------------
/ParquetUnitTests/Regions/ChunkDetailUnitTest.cs:
--------------------------------------------------------------------------------
1 | using Parquet.Regions;
2 | using Xunit;
3 |
4 | namespace ParquetUnitTests.Maps
5 | {
6 | ///
7 | /// Unit tests .
8 | ///
9 | public class ChunkDetailUnitTest
10 | {
11 | [Fact]
12 | internal void DefaultChunkTypeIsEmptyTest()
13 | {
14 | var defaultChunk = new ChunkDetail();
15 |
16 | Assert.Equal(ChunkDetail.None, defaultChunk);
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ExampleData/CollectibleModels.csv:
--------------------------------------------------------------------------------
1 | ID,Name,Description,Comment,Tags,ItemID,AddsToBiome,AddsToRoom,CollectionEffect,EffectAmount
2 | -40000,4 Collectible Test Parquet,Test,Test,Test-Tag,-140001,,,None,0
3 | 40000,Flower,,,,140000,,,Item,0
4 | 40001,Pine Cone,,,,140001,Forested,,Item,0
5 | 40002,Seeds,,,,140002,Swampy,,Item,0
6 | 40003,Bones,,,,140003,Deserted,Grave,Item,0
7 | 40004,Ice Crystal,,,,140004,Frozen,,Item,0
8 | 40005,Copper Ore,,,,140005,,,Item,0
9 | 40006,Star,,,,None,,,BiomeTime,10
10 | 40007,Water Bottle,,,,None,,,BiomeTime,10
11 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Scripts/RunState.cs:
--------------------------------------------------------------------------------
1 | namespace Parquet.Scripts
2 | {
3 | ///
4 | /// Status of a in an .
5 | ///
6 | public enum RunState
7 | {
8 | /// This script has not yet begun execution.
9 | Unstarted = 0,
10 | /// This script is currently executing.
11 | InProgress,
12 | /// This script is completed execution.
13 | Completed,
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Parquets/GatherEffect.cs:
--------------------------------------------------------------------------------
1 | namespace Parquet.Parquets
2 | {
3 | ///
4 | /// IDs for effects that can happen when a character gathers a .
5 | ///
6 | public enum GatheringEffect
7 | {
8 | /// Gathering this parquet has no effect.
9 | None = 0,
10 | /// Awards the a given .
11 | Item,
12 | /// Replaces this parquet with a .
13 | Collectible,
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/IDeeplyCloneable.cs:
--------------------------------------------------------------------------------
1 | namespace Parquet
2 | {
3 | ///
4 | /// Classes implementing this interface provide the means to duplicate an instance of that class via a deep copy.
5 | ///
6 | /// The class to be deeply cloned.
7 | public interface IDeeplyCloneable
8 | {
9 | ///
10 | /// Creates a new instance that is a deep copy of the current instance.
11 | ///
12 | /// A new instance with the same characteristics as the current instance.
13 | T DeepClone();
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Parquets/CollectEffect.cs:
--------------------------------------------------------------------------------
1 | namespace Parquet.Parquets
2 | {
3 | ///
4 | /// IDs for effects that can happen when a character encounters a Collectible.
5 | ///
6 | public enum CollectingEffect
7 | {
8 | /// Collecting this parquet has no effect.
9 | None = 0,
10 | /// Awards the a given .
11 | Item,
12 | /// Allows the to remain safely in the longer.
13 | BiomeTime,
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 | Thank you for your interest in contributing to Parquet!
3 |
4 | This project is released as-is, with no gurantee of future maintenance.
5 | Work may continue as time allows.
6 |
7 | This project may be forked in accordance with the license terms
8 | You may also report issues or suggest improvements, but please
9 | respect that these may not be applied or acted on.
10 |
11 | Because this project is a means for the creator, Paige, to expand their
12 | own knowledge and experience, contributions at present are invite-only.
13 |
14 | However, you're always welcome fork the library and/or use its tools
15 | in your own projects, provided you respect the licensing.
16 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Parquets/EntryType.cs:
--------------------------------------------------------------------------------
1 | namespace Parquet.Parquets
2 | {
3 | ///
4 | /// Whether and how a communicates to an adjacent .
5 | ///
6 | public enum EntryType
7 | {
8 | /// This furnishing does not communicate to another map or room.
9 | None = 0,
10 | /// This furnishing communicates between rooms on the same map.
11 | Room,
12 | /// This furnishing communicates to the map above.
13 | Up,
14 | /// This furnishing communicates to the map below.
15 | Down,
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Documentation/Home.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | Parquet targets the following features:
4 |
5 | 1) A peaceful 2D overworld map that may be explored.
6 | 2) Simple free-form building mechanics allowing player characters to create homes in the world.
7 | 3) Resource collection mechanics allowing players to upgrade their homes and tools.
8 | 4) A simple crafting system allowing players to unlock new tools and building materials.
9 | 5) Interactive NPCs.
10 | 6) A quest system encouraging players to build particular building types.
11 | 7) Dialogue and narrative delivery.
12 |
13 | This is a work in progress; for now, please read all the pages listed and the [Project Read Me](https://github.com/mxashlynn/Parquet/blob/master/README.md).
14 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Items/ModificationTool.cs:
--------------------------------------------------------------------------------
1 | namespace Parquet.Items
2 | {
3 | ///
4 | /// IDs for tools that Characters can use to modify s and s.
5 | ///
6 | ///
7 | /// The tool subtypes are hard-coded, but individual s themselves are configured in CSV files.
8 | ///
9 | public enum ModificationTool
10 | {
11 | /// This parquet cannot be modified.
12 | None = 0,
13 | /// This parquet can be modified with a shovel-like tool.
14 | Shovel,
15 | /// This parquet can be modified with a hammer-like tool.
16 | Hammer,
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Scripts/IMutableScriptModel.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Parquet.Scripts
4 | {
5 | ///
6 | /// Facilitates editing of a from design tools while maintaining a read-only face for use during play.
7 | ///
8 | ///
9 | /// By design, subtypes of should never themselves use .
10 | /// IMutableScriptModel is for use only by external types that require read/write access to model properties.
11 | ///
12 | public interface IMutableScriptModel : IMutableModel
13 | {
14 | ///
15 | /// A series of imperative, procedural commands.
16 | ///
17 | public ICollection Nodes { get; }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/ILogger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Parquet
4 | {
5 | ///
6 | /// Represents a type used to perform logging.
7 | /// Typically this type is provided by the game or tool that uses Parquet.
8 | ///
9 | ///
10 | /// Inspired by Microsoft.Extensions.Logging.ILogger but simpler.
11 | ///
12 | public interface ILogger
13 | {
14 | ///
15 | /// Writes a log entry.
16 | ///
17 | /// The severity of the event being logged.
18 | /// A message summarizing the event being logged.
19 | /// The exception related to this event, if any.
20 | void Log(LogLevel logLevel, string message, Exception exception);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/ParquetUnitTests/RecipeElementUnitTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Parquet;
3 | using Xunit;
4 |
5 | namespace ParquetUnitTests
6 | {
7 | ///
8 | /// Unit tests .
9 | ///
10 | public class RecipeElementUnitTest
11 | {
12 | [Fact]
13 | internal void ZeroAmountsFailTest()
14 | {
15 | static void TestCodeZero()
16 | {
17 | var _ = new RecipeElement(0, "test");
18 | }
19 |
20 | Assert.Throws(TestCodeZero);
21 | }
22 |
23 | [Fact]
24 | internal void NegativeeAountsFailTest()
25 | {
26 | static void TestCodeNegative()
27 | {
28 | var _ = new RecipeElement(-1, "test");
29 | }
30 |
31 | Assert.Throws(TestCodeNegative);
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Beings/IMutableCritterModel.cs:
--------------------------------------------------------------------------------
1 | namespace Parquet.Beings
2 | {
3 | ///
4 | /// Facilitates editing of a from design tools while maintaining a read-only face for use during play.
5 | ///
6 | ///
7 | /// By design, subtypes of should never themselves use .
8 | /// IMutableCritterModel is for use only by external types that require read/write access to model properties.
9 | ///
10 | public interface IMutableCritterModel : IMutableBeingModel
11 | {
12 | // This class is intentionally left empty.
13 | // Currently, everything needed for editing CritterModels is provided by IBeingModelEdit.
14 | // However, that may change in the future. This class exists to simplify external code
15 | // and to allow for future expansion of CritterModel.
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Parquets/IMutableFloorModel.cs:
--------------------------------------------------------------------------------
1 | using Parquet.Items;
2 |
3 | namespace Parquet.Parquets
4 | {
5 | ///
6 | /// Facilitates editing of a from design tools while maintaining a read-only face for use during play.
7 | ///
8 | ///
9 | /// By design, subtypes of should never themselves use .
10 | /// IMutableFloorModel is for use only by external types that require read/write access to model properties.
11 | ///
12 | public interface IMutableFloorModel : IMutableParquetModel
13 | {
14 | /// The tool used to dig out or fill in the floor.
15 | public ModificationTool ModTool { get; set; }
16 |
17 | /// Player-facing name of the parquet, used when it has been dug out.
18 | public string TrenchName { get; set; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Items/GatheringTool.cs:
--------------------------------------------------------------------------------
1 | namespace Parquet.Items
2 | {
3 | ///
4 | /// IDs for tools that Characters can use to gather s.
5 | ///
6 | ///
7 | /// The tool subtypes are hard-coded, but individual s themselves are configured in CSV files.
8 | ///
9 | public enum GatheringTool
10 | {
11 | /// This parquet cannot be gathered.
12 | None = 0,
13 | /// This parquet can be gathered by using a pick-like tool.
14 | Pick,
15 | /// This parquet can be gathered by using a axe-like tool.
16 | Axe,
17 | /// This parquet can be gathered by using a shovel-like tool.
18 | Shovel,
19 | /// This parquet can be gathered by using a bucket-like tool.
20 | Bucket,
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/ExampleData/BlockModels.csv:
--------------------------------------------------------------------------------
1 | ID,Name,Description,Comment,Tags,ItemID,AddsToBiome,AddsToRoom,GatherTool,GatherEffect,CollectibleID,IsFlammable,IsLiquid,MaxToughness
2 | -20000,2 Block Test Parquet,"This descriptiong tests ""escaped"" double quotes and commas, ya know?",Test,Test-Tag,-140002,,,None,None,-40000,False,False,10
3 | 20000,Bush,A short shrub.,This is just a test.,,None,,,Axe,Collectible,40000,True,False,1
4 | 20001,Tree,A large woody plant.,,,None,Forested❟Wood,None❟Natural,Axe,Collectible,40001,True,False,4
5 | 20003,Sandstone Boulder,A major block of stone made from accreted sand.,,,None,Deserted,,Pick,None,None,False,False,10
6 | 20004,Ice Boulder,A huge chunk of frozen water.,,,None,Frozen,,Pick,None,None,False,False,8
7 | 20005,Grey Brick,Standard blocks for building a wall.,,,None,,Brick,Pick,None,None,False,False,8
8 | 20006,Fresh Water,"Come, wash your face!",,,140001,,,Bucket,Item,None,False,True,1
9 | 20007,Lava,Molten silicates from the Earth's mantle.,,,140002,Volcanic,,Bucket,Item,None,False,True,1
10 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/IntExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Parquet
2 | {
3 | ///
4 | /// Provides extension methods to the built in integer type.
5 | ///
6 | internal static class IntExtensions
7 | {
8 | /// Ensures an integer falls within the given range.
9 | /// Integer to normalize.
10 | /// The lowest valid value for the integer.
11 | /// The highest valid value for the integer.
12 | /// The integer, normalized.
13 | public static int Normalize(this int value, int lowerBound, int upperBound)
14 | {
15 | if (value < lowerBound)
16 | {
17 | value = lowerBound;
18 | }
19 | else if (value > upperBound)
20 | {
21 | value = upperBound;
22 | }
23 |
24 | return value;
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Beings/IMutablePronounGroup.cs:
--------------------------------------------------------------------------------
1 | namespace Parquet.Beings
2 | {
3 | ///
4 | /// Facilitates editing of a from design tools while maintaining a read-only face for use during play.
5 | ///
6 | public interface IMutablePronounGroup
7 | {
8 | /// Personal pronoun used as the subject of a verb.
9 | public string Subjective { get; set; }
10 |
11 | /// Personal pronoun used as the indirect object of a preposition or verb.
12 | public string Objective { get; set; }
13 |
14 | /// Personal pronoun used to attribute possession.
15 | public string Determiner { get; set; }
16 |
17 | /// Personal pronoun used to indicate a relationship.
18 | public string Possessive { get; set; }
19 |
20 | /// Personal pronoun used to indicate the user.
21 | public string Reflexive { get; set; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Crafts/IMutableCraftingRecipe.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Parquet.Crafts
4 | {
5 | ///
6 | /// Facilitates editing of a from design tools while maintaining a read-only face for use during play.
7 | ///
8 | ///
9 | /// By design, subtypes of should never themselves use .
10 | /// IMutableCraftingRecipe is for use only by external types that require read/write access to model properties.
11 | ///
12 | public interface IMutableCraftingRecipe : IMutableModel
13 | {
14 | /// The types and amounts of s created by following this recipe.
15 | public ICollection Products { get; }
16 |
17 | /// All materials and their quantities needed to follow this recipe once.
18 | public ICollection Ingredients { get; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Beings/IMutableBeingModel.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Parquet.Beings
4 | {
5 | ///
6 | /// Facilitates editing of a from design tools while maintaining a read-only face for use during play.
7 | ///
8 | public interface IMutableBeingModel : IMutableModel
9 | {
10 | /// The of the in which this character is at home.
11 | public ModelID NativeBiomeID { get; set; }
12 |
13 | /// The of the governing the way this being acts.
14 | public ModelID PrimaryBehaviorID { get; set; }
15 |
16 | /// Types of parquets this avoids, if any.
17 | public ICollection AvoidsIDs { get; }
18 |
19 | /// Types of parquets this seeks out, if any.
20 | public ICollection SeeksIDs { get; }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Parquets/IMutableCollectibleModel.cs:
--------------------------------------------------------------------------------
1 | namespace Parquet.Parquets
2 | {
3 | ///
4 | /// Facilitates editing of a from design tools while maintaining a read-only face for use during play.
5 | ///
6 | ///
7 | /// By design, subtypes of should never themselves use .
8 | /// IMutableCollectibleModel is for use only by external types that require read/write access to model properties.
9 | ///
10 | public interface IMutableCollectibleModel : IMutableParquetModel
11 | {
12 | /// The effect generated when a character encounters this Collectible.
13 | public CollectingEffect CollectionEffect { get; set; }
14 |
15 | ///
16 | /// The scale in points of the effect.
17 | /// For example, how much to alter a stat if the is set to alter a stat.
18 | ///
19 | public int EffectAmount { get; set; }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/IReadOnlyGrid.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using CsvHelper.TypeConversion;
3 |
4 | namespace Parquet
5 | {
6 | ///
7 | /// A two-dimensional collection that functions much like an immutable array.
8 | ///
9 | /// For serialization, implementing classes need to guarantee stable iteration order.
10 | /// The type collected.
11 | public interface IReadOnlyGrid : IReadOnlyCollection, IDeeplyCloneable>
12 | where TElement : ITypeConverter
13 | {
14 | /// Gets the number of elements in the Y dimension of the .
15 | public int Rows { get; }
16 |
17 | /// Gets the number of elements in the X dimension of the .
18 | public int Columns { get; }
19 |
20 | /// Read-only access to any object in the grid.
21 | public TElement this[int y, int x] { get; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Paige Ashlynn
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/ParquetUnitTests/GlobalSuppressions.cs:
--------------------------------------------------------------------------------
1 | // This file is used by Code Analysis to maintain SuppressMessage
2 | // attributes that are applied to this project.
3 | // Project-level suppressions either have no target or are given
4 | // a specific target and scoped to a namespace, type, member, etc.
5 |
6 | using System.Diagnostics.CodeAnalysis;
7 |
8 | [assembly: SuppressMessage("Style", "IDE0022:Use expression body for methods",
9 | Justification = "Prefer keeping unit tests in Arrange-Act-Assert form.",
10 | Scope = "namespaceanddescendants", Target = "~N:ParquetUnitTests")]
11 |
12 | [assembly: SuppressMessage("Style", "IDE0017:Simplify object initialization",
13 | Justification = "Prefer keeping unit tests in Arrange-Act-Assert form.",
14 | Scope = "namespaceanddescendants", Target = "~N:ParquetUnitTests")]
15 |
16 | [assembly: SuppressMessage("Performance", "CA1814:Prefer jagged arrays over multidimensional",
17 | Justification = "Parquet requires multidimensional arrays.",
18 | Scope = "namespaceanddescendants", Target = "~N:ParquetUnitTests")]
19 |
--------------------------------------------------------------------------------
/ParquetUnitTests/Parquets/FloorModelUnitTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Parquet;
3 | using Parquet.Parquets;
4 | using Xunit;
5 |
6 | namespace ParquetUnitTests.Parquets
7 | {
8 | ///
9 | /// Unit tests .
10 | ///
11 | public class FloorModelUnitTest
12 | {
13 | #region Test Values
14 | /// Identifier used when creating a new floor.
15 | private static readonly ModelID newFloorID = TestModels.TestFloor.ID - 1;
16 | #endregion
17 |
18 | [Fact]
19 | internal void ValidFloorIDsArePermittedTest()
20 | {
21 | var testFloor = new FloorModel(newFloorID, "will be created", "", "");
22 |
23 | Assert.NotNull(testFloor);
24 | }
25 |
26 | [Fact]
27 | internal void InvalidFloorIDsRaiseExceptionTest()
28 | {
29 | var badFloorID = TestModels.TestBlock.ID;
30 |
31 | void TestCode()
32 | {
33 | var _ = new CollectibleModel(badFloorID, "will fail", "", "");
34 | }
35 |
36 | Assert.Throws(TestCode);
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/ParquetUnitTests/AllUnitTest.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using Parquet;
3 | using Xunit;
4 |
5 | namespace ParquetUnitTests
6 | {
7 | ///
8 | /// Unit tests .
9 | ///
10 | public class AllUnitTest
11 | {
12 | #region Values for Tests
13 | ///
14 | /// The highest defined in
15 | /// except for .Maximum.
16 | ///
17 | internal static readonly ModelID MaximumIDRangeUpperLimit = typeof(All).GetFields()
18 | .Where(fieldInfo => fieldInfo.FieldType.IsGenericType
19 | && fieldInfo.FieldType == typeof(Range)
20 | && fieldInfo.Name != nameof(All.ItemIDs))
21 | .Select(fieldInfo => fieldInfo.GetValue(null))
22 | .Cast>()
23 | .Select(range => range.Maximum)
24 | .Max();
25 | #endregion
26 |
27 | [Fact]
28 | internal void ItemIDMinimumIsGreaterThanMaximumDefinedRangeUpperBoundTest()
29 | {
30 | Assert.True(All.ItemIDs.Minimum > MaximumIDRangeUpperLimit);
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Scripts/IMutableInteractionModel.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Parquet.Scripts
4 | {
5 | ///
6 | /// Facilitates editing of a from design tools while maintaining a read-only face for use during play.
7 | ///
8 | ///
9 | /// By design, subtypes of should never themselves use .
10 | /// IMutableInteractionModel is for use only by external types that require read/write access to model properties.
11 | ///
12 | public interface IMutableInteractionModel : IMutableModel
13 | {
14 | ///
15 | /// Describes the criteria for beginning this interaction.
16 | ///
17 | public ICollection PrerequisitesIDs { get; }
18 |
19 | ///
20 | /// Everything this interaction entails.
21 | ///
22 | public ICollection StepsIDs { get; }
23 |
24 | ///
25 | /// Describes the results of finishing this interaction.
26 | ///
27 | public ICollection OutcomesIDs { get; }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/IVisibleData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Parquet
4 | {
5 | ///
6 | /// Indicates an external view onto associated data should be updated.
7 | ///
8 | /// The instance that raised the event.
9 | /// Additional event data.
10 | /// true if the operation succeeded; otherwise, false.
11 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1711:Identifiers should not have incorrect suffix",
12 | Justification = "False positive.")]
13 | public delegate bool DataChangedEventHandler(object sender, EventArgs eventData);
14 |
15 | ///
16 | /// Raises events when an external view onto associated data should be updated.
17 | ///
18 | public interface IVisibleData
19 | {
20 | ///
21 | /// Raised when an external view onto associated data should be updated.
22 | ///
23 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1003:Use generic event handler instances",
24 | Justification = "False positive.")]
25 | public event DataChangedEventHandler VisibleDataChanged;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ExampleData/BiomeRecipes.csv:
--------------------------------------------------------------------------------
1 | ID,Name,Description,Comment,Tags,Tier,IsRoomBased,IsLiquidBased,ParquetCriteria,EntryRequirements
2 | -70000,Biome Test,Test,Test,,1,False,True,None,
3 | 70000,Alpine,,,,4,False,False,None,ForestKey❟CavernKey❟Tier2Key❟Tier3Key❟SeasideKey❟TundraKey❟AlpineKey
4 | 70001,Cavern,,,,3,False,False,None,ForestKey❟CavernKey❟Tier2Key❟Tier3Key
5 | 70002,Desert,,,,2,False,False,Deserted,SeasideKey❟DesertKey❟Tier2Key
6 | 70003,Field,,,,0,False,False,None,
7 | 70004,Forest,,,,1,False,False,Forested,ForestKey
8 | 70005,Heavens,Heavenly!,sky,"What a nice place, but don't miss your step!",5,False,True,Heavenly,HeavenlyKey
9 | 70006,Inferno,Infernal!,pit,"What a horrendous place, so don't miss your step!",5,False,True,Volcanic,InfernalKey
10 | 70007,Ruins,,,,4,False,False,Ruinous,ForestKey❟CavernKey❟Tier2Key❟Tier3Key❟ForestKey❟SwampKey❟RuinsKey
11 | 70008,Seaside,,,,1,False,True,Coastal,SeasideKey
12 | 70009,Swamp,,,,2,False,False,Swampy,ForestKey❟SwampKey❟Tier2Key
13 | 70010,Town,,,,0,True,False,Urban,
14 | 70011,Tundra,,,,2,False,False,Frozen,SeasideKey❟TundraKey❟Tier2Key
15 | 70012,Volcano,,,,4,False,True,Volcanic,ForestKey❟CavernKey❟Tier2Key❟Tier3Key❟SeasideKey❟DesertKey❟VolcanoKey
16 | 70013,Prism,,,,4,False,False,Crystaline,ForestKey❟CavernKey❟Tier2Key❟Tier3Key❟SeasideKey❟TundraKey❟AlpineKey
17 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Items/ItemType.cs:
--------------------------------------------------------------------------------
1 | namespace Parquet.Items
2 | {
3 | ///
4 | /// Represents the different categories of s that may be carried and used.
5 | ///
6 | ///
7 | /// The subtypes are hard-coded, but individual items themselves are configured in CSV files.
8 | ///
9 | public enum ItemType
10 | {
11 | /// This item corresponds to no particular category.
12 | Other = 0,
13 | /// This item may be used only once.
14 | Consumable,
15 | /// This item may be worn, carried, or otherwise employed in an ongoing fashion.
16 | Equipment,
17 | /// This item unlocks a mechanic, domain, or door.
18 | KeyItem,
19 | /// This item may be used in crafting a recipe.
20 | Material,
21 | /// This item may contain other items.
22 | Storage,
23 | /// This item may be used multiple times to gather parquets.
24 | ToolForGathering,
25 | /// This item may be used multiple times to alter parquets.
26 | ToolForModification,
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Rooms/ReadOnlyRoomCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 |
4 | namespace Parquet.Rooms
5 | {
6 | ///
7 | /// Convenience extensions to .
8 | ///
9 | public static class ReadOnlyRoomCollectionExtensions
10 | {
11 | ///
12 | /// Returns the at the given position, if there is one.
13 | ///
14 | /// The current collection of s.
15 | /// An in-bounds position to search for a .
16 | /// The specified if found; otherwise, null.
17 | public static Room GetRoomAtOrNull(this IReadOnlyCollection rooms, Point2D position)
18 | => rooms?.FirstOrDefault(room => room.ContainsPosition(position)) ?? null;
19 |
20 | ///
21 | /// Returns a that represents the current .
22 | ///
23 | /// The representation.
24 | public static string ToString(this IReadOnlyCollection rooms)
25 | => $"{rooms?.Count ?? 0} {nameof(Rooms)}";
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/IMutableModelCollection.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Parquet
4 | {
5 | ///
6 | /// Facilitates updating elements of a from design tools
7 | /// while maintaining a read-only facade during play.
8 | ///
9 | /// The type collected, typically a concrete descendant of .
10 | public interface IMutableModelCollection : ICollection, IVisibleData
11 | where TModel : Model
12 | {
13 | ///
14 | /// Removes the associated with the given from the collection.
15 | ///
16 | /// The ID for a valid, defined contained in this collection.
17 | public bool Remove(ModelID id);
18 |
19 | ///
20 | /// Replaces a contained with the given whose
21 | /// is identical to that of the model being replaced.
22 | ///
23 | /// A valid, defined contained in this collection.
24 | public void Replace(TModel model);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/ParquetUnitTests/Beings/CharacterModelUnitTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Parquet;
3 | using Parquet.Beings;
4 | using Xunit;
5 |
6 | namespace ParquetUnitTests.Beings
7 | {
8 | ///
9 | /// Unit tests .
10 | ///
11 | public class CharacterModelUnitTest
12 | {
13 | #region Test Values
14 | /// Identifier used when creating a new block.
15 | private static readonly ModelID newCharacterID = TestModels.TestCharacter.ID - 1;
16 | #endregion
17 |
18 | [Fact]
19 | internal void ValidCharacterIDsArePermittedTest()
20 | {
21 | var newCharacter = new CharacterModel(newCharacterID, "Character", "will be created", "", null, All.BiomeRecipeIDs.Minimum, All.ScriptIDs.Minimum);
22 |
23 | Assert.NotNull(newCharacter);
24 | }
25 |
26 | [Fact]
27 | internal void InvalidCharacterIDsRaiseExceptionTest()
28 | {
29 | var badCharacterID = TestModels.TestBlock.ID - 1;
30 |
31 | void TestCode()
32 | {
33 | var _ = new CharacterModel(badCharacterID, "Character", "will fail", "", null, All.BiomeRecipeIDs.Minimum, All.ScriptIDs.Minimum);
34 | }
35 |
36 | Assert.Throws(TestCode);
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/ParquetUnitTests/Beings/CritterModelUnitTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Parquet;
3 | using Parquet.Beings;
4 | using Xunit;
5 |
6 | namespace ParquetUnitTests.Beings
7 | {
8 | ///
9 | /// Unit tests .
10 | ///
11 | public class CritterModelUnitTest
12 | {
13 | #region Test Values
14 | /// Identifier used when creating a new block.
15 | private static readonly ModelID newCritterID = TestModels.TestCritter.ID - 1;
16 | #endregion
17 |
18 | [Fact]
19 | internal void ValidCritterIDsArePermittedTest()
20 | {
21 | var newCritter = new CritterModel(newCritterID, "will be created", "", "", null,
22 | All.BiomeRecipeIDs.Minimum, All.ScriptIDs.Minimum);
23 |
24 | Assert.NotNull(newCritter);
25 | }
26 |
27 | [Fact]
28 | internal void InvalidCritterIDsRaiseExceptionTest()
29 | {
30 | var badCritterID = TestModels.TestBlock.ID - 1;
31 |
32 | void TestCode()
33 | {
34 | var _ = new CritterModel(badCritterID, "will fail", "", "", null,
35 | All.BiomeRecipeIDs.Minimum, All.ScriptIDs.Minimum);
36 | }
37 |
38 | Assert.Throws(TestCode);
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Documentation/How_Elevation_Works_In_Parquet.md:
--------------------------------------------------------------------------------
1 | # Elevation
2 |
3 | Parquet is a 2D game library, so the concept of elevation exists only in rudimentary form.
4 |
5 | Any given Map Region may be assigned to one of three possible elevations: above, level with, or below ground. Level is the default.
6 |
7 | Map Regions are connected to one another via Exit Points. Exit Points are arbitrary and do not consider elevation (or adjacency of any kind).
8 |
9 | However, a Map Region with Above Ground elevation may have another Map Region designated below it. Likewise, a Below Ground Map Region may have another region above it. And a Level Ground region may have both.
10 |
11 |
12 |
13 | # Liquid Blocks Flow Down
14 |
15 | Liquid Blocks that flow onto an empty space in a Map Region that has another region below it will flow down to that lower elevation Map Region automatically.
16 |
17 | # Beings Travel Up & Down
18 |
19 | Similarly, Critters and Characters that walk onto an empty space in a Map Region with another region below will fall through to the lower region.
20 |
21 | Finally, a Character may build an item like a staircase or ladder at an arbitrary point in a Map Region that has another Map Region designated Above it in order to climb up.
22 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Biomes/IMutableBiomeRecipe.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Parquet.Biomes
4 | {
5 | ///
6 | /// Facilitates editing of a from design tools while maintaining a read-only face for use during play.
7 | ///
8 | public interface IMutableBiomeRecipe : IMutableModel
9 | {
10 | ///
11 | /// A rating indicating where in the progression this falls.
12 | /// Must be non-negative. Higher values indicate later Biomes.
13 | ///
14 | public int Tier { get; set; }
15 |
16 | /// Determines whether or not this is defined in terms of s.
17 | public bool IsRoomBased { get; set; }
18 |
19 | /// Determines whether or not this is defined in terms of liquid parquets.
20 | public bool IsLiquidBased { get; set; }
21 |
22 | /// Describes the parquets that make up this .
23 | public ModelTag ParquetCriteria { get; set; }
24 |
25 | /// Describes the s a needs to safely access this biome.
26 | public ICollection EntryRequirements { get; }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/IGrid.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using CsvHelper.TypeConversion;
3 |
4 | namespace Parquet
5 | {
6 | ///
7 | /// A two-dimensional collection that functions much like an array.
8 | /// Instances of this class are mutable during play.
9 | ///
10 | /// For serialization, implementing classes need to guarantee stable iteration order.
11 | /// The type collected.
12 | public interface IGrid : IEnumerable, IDeeplyCloneable>
13 | where TElement : ITypeConverter
14 | {
15 | /// Separates s within this .
16 | public string GridDelimiter { get; }
17 |
18 | /// Gets the number of elements in the Y dimension of the .
19 | public int Rows { get; }
20 |
21 | /// Gets the number of elements in the X dimension of the .
22 | public int Columns { get; }
23 |
24 | /// Gets the total number of elements contained in the .
25 | public int Count { get; }
26 |
27 | /// Access to any object in the grid.
28 | public ref TElement this[int y, int x] { get; }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Parquets/IMutableBlockModel.cs:
--------------------------------------------------------------------------------
1 | using Parquet.Items;
2 |
3 | namespace Parquet.Parquets
4 | {
5 | ///
6 | /// Facilitates editing of a from design tools while maintaining a read-only face for use during play.
7 | ///
8 | ///
9 | /// By design, subtypes of should never themselves use .
10 | /// IMutableBlockModel is for use only by external types that require read/write access to model properties.
11 | ///
12 | public interface IMutableBlockModel : IMutableParquetModel
13 | {
14 | /// The tool used to remove the block.
15 | public GatheringTool GatherTool { get; set; }
16 |
17 | /// The effect generated when a character gathers this Block.
18 | public GatheringEffect GatherEffect { get; set; }
19 |
20 | /// The Collectible spawned when a character gathers this Block.
21 | public ModelID CollectibleID { get; set; }
22 |
23 | /// Whether or not the block is flammable.
24 | public bool IsFlammable { get; set; }
25 |
26 | /// Whether or not the block is a liquid.
27 | public bool IsLiquid { get; set; }
28 |
29 | /// The block's native toughness.
30 | public int MaxToughness { get; set; }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Games/IMutableGameModel.cs:
--------------------------------------------------------------------------------
1 | namespace Parquet.Games
2 | {
3 | ///
4 | /// Facilitates editing of a from design tools while maintaining a read-only face for use during play.
5 | ///
6 | ///
7 | /// By design, subtypes of should never themselves use .
8 | /// IMutableGameModel is for use only by external types that require read/write access to model properties.
9 | ///
10 | public interface IMutableGameModel : IMutableModel
11 | {
12 | /// If true this game is part of a longer sequence of games.
13 | public bool IsEpisode { get; set; }
14 |
15 | /// Subtitle, if any. This will be used as the title of the episode if is true.
16 | public string EpisodeTitle { get; set; }
17 |
18 | /// Number of this episode in its sequence, if any.
19 | public int EpisodeNumber { get; set; }
20 |
21 | /// The of the that the player controls at the outset.
22 | public ModelID PlayerCharacterID { get; set; }
23 |
24 | /// The of the to run when play begins.
25 | public ModelID FirstScriptID { get; set; }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # GitHub Language Detection
2 | Design/* linguist-documentation
3 |
4 | # Normalize Line Endings to LineFeed
5 | *.cs text=auto
6 | *.csv text=auto
7 | *.htm text=auto
8 | *.html text=auto
9 | *.md text=auto
10 | *.text text=auto
11 | *.txt text=auto
12 | *.xml text=auto
13 | LICENSE text=auto
14 |
15 | # LFS
16 | *.7z filter=lfs diff=lfs merge=lfs -text
17 | *.apk filter=lfs diff=lfs merge=lfs -text
18 | *.app filter=lfs diff=lfs merge=lfs -text
19 | *.bmp filter=lfs diff=lfs merge=lfs -text
20 | *.dll filter=lfs diff=lfs merge=lfs -text
21 | *.docx filter=lfs diff=lfs merge=lfs -text
22 | *.exe filter=lfs diff=lfs merge=lfs -text
23 | *.gif filter=lfs diff=lfs merge=lfs -text
24 | *.ico filter=lfs diff=lfs merge=lfs -text
25 | *.jpeg filter=lfs diff=lfs merge=lfs -text
26 | *.jpg filter=lfs diff=lfs merge=lfs -text
27 | *.kra filter=lfs diff=lfs merge=lfs -text
28 | *.mod filter=lfs diff=lfs merge=lfs -text
29 | *.mp3 filter=lfs diff=lfs merge=lfs -text
30 | *.ogg filter=lfs diff=lfs merge=lfs -text
31 | *.otf filter=lfs diff=lfs merge=lfs -text
32 | *.pages filter=lfs diff=lfs merge=lfs -text
33 | *.pcx filter=lfs diff=lfs merge=lfs -text
34 | *.png filter=lfs diff=lfs merge=lfs -text
35 | *.rar filter=lfs diff=lfs merge=lfs -text
36 | *.svg filter=lfs diff=lfs merge=lfs -text
37 | *.ttf filter=lfs diff=lfs merge=lfs -text
38 | *.uge filter=lfs diff=lfs merge=lfs -text
39 | *.wav filter=lfs diff=lfs merge=lfs -text
40 | *.xnb filter=lfs diff=lfs merge=lfs -text
41 | *.zip filter=lfs diff=lfs merge=lfs -text
42 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Rooms/IMutableRoomRecipe.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Parquet.Rooms
4 | {
5 | ///
6 | /// Facilitates editing of a from design tools while maintaining a read-only face for use during play.
7 | ///
8 | ///
9 | /// By design, subtypes of should never themselves use .
10 | /// IMutableRoomRecipe is for use only by external types that require read/write access to model properties.
11 | ///
12 | public interface IMutableRoomRecipe : IMutableModel
13 | {
14 | /// Minimum number of open spaces needed for this to register.
15 | public int MinimumWalkableSpaces { get; set; }
16 |
17 | /// A list of categories this requires.
18 | public ICollection OptionallyRequiredFurnishings { get; }
19 |
20 | /// An optional list of categories this requires.
21 | public ICollection OptionallyRequiredWalkableFloors { get; }
22 |
23 | /// An optional list of categories this requires as walls.
24 | public ICollection OptionallyRequiredPerimeterBlocks { get; }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/ParquetUnitTests/AssemblyInfoUnitTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 | using Parquet;
4 | using Xunit;
5 |
6 | // Make no promises to maintain internal services.
7 | [assembly: ComVisible(false)]
8 |
9 | // ParquetUnitTests are API consumers, not API providers.
10 | [assembly: CLSCompliant(false)]
11 |
12 | namespace ParquetUnitTests
13 | {
14 | ///
15 | /// Unit tests .
16 | ///
17 | public class AssemblyInfoUnitTest
18 | {
19 | #region Values for Tests
20 | /// This is the canonical invalid library version string used in serialization tests.
21 | private const string invalidLibVersion = "0.0.0.0";
22 |
23 | /// This is the string used if the library version cannot be loaded.
24 | private const string errorLibVersion = "?.?.?.?";
25 | #endregion
26 |
27 | [Fact]
28 | internal void SupportedLibraryVersionIsDefinedTest()
29 | {
30 | Assert.False(string.IsNullOrEmpty(AssemblyInfo.LibraryVersion));
31 | }
32 |
33 | [Fact]
34 | internal void SupportedLibraryVersionIsNotInvalidTest()
35 | {
36 | Assert.NotEqual(invalidLibVersion, AssemblyInfo.LibraryVersion);
37 | }
38 |
39 | [Fact]
40 | internal void SupportedLibraryVersionWasFoundTest()
41 | {
42 | Assert.NotEqual(errorLibVersion, AssemblyInfo.LibraryVersion);
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Regions/IMutableRegionModel.cs:
--------------------------------------------------------------------------------
1 | namespace Parquet.Regions
2 | {
3 | ///
4 | /// Facilitates editing of characteristics from design tools while maintaining a read-only face during play.
5 | ///
6 | public interface IMutableRegionModel : IMutableModel
7 | {
8 | /// A color to display in any empty areas of the region.
9 | string BackgroundColor { get; set; }
10 |
11 | /// The of the region to the north of this one.
12 | ModelID RegionToTheNorthID { get; set; }
13 |
14 | /// The of the region to the east of this one.
15 | ModelID RegionToTheEastID { get; set; }
16 |
17 | /// The of the region to the south of this one.
18 | ModelID RegionToTheSouthID { get; set; }
19 |
20 | /// The of the region to the west of this one.
21 | ModelID RegionToTheWestID { get; set; }
22 |
23 | /// The of the region above this one.
24 | ModelID RegionAboveID { get; set; }
25 |
26 | /// The of the region below this one.
27 | ModelID RegionBelowID { get; set; }
28 |
29 | /// Instructions on how to procedurally generate this region.
30 | MapChunkGrid MapChunks { get; set; }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Parquets/IMutableFurnishingModel.cs:
--------------------------------------------------------------------------------
1 | namespace Parquet.Parquets
2 | {
3 | ///
4 | /// Facilitates editing of a from design tools while maintaining a read-only face for use during play.
5 | ///
6 | ///
7 | /// By design, subtypes of should never themselves use .
8 | /// IMutableFurnishingModel is for use only by external types that require read/write access to model properties.
9 | ///
10 | public interface IMutableFurnishingModel : IMutableParquetModel
11 | {
12 | /// If true this may be walked on.
13 | public bool IsWalkable { get; set; }
14 |
15 | /// Indicates if and how this serves as an entry to a or .
16 | public EntryType Entry { get; set; }
17 |
18 | /// If true this serves as part of a perimeter of a .
19 | public bool IsEnclosing { get; set; }
20 |
21 | /// If true the may catch fire.
22 | public bool IsFlammable { get; set; }
23 |
24 | /// If true this may be opened and closed.
25 | public bool IsOpenable { get; set; }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/LoggerDefault.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Parquet
4 | {
5 | ///
6 | /// Performs logging.
7 | ///
8 | ///
9 | /// This is very bare bones and is intended to be replaced by a more appropriate logger provided by the application.
10 | ///
11 | ///
12 | internal class LoggerDefault : ILogger
13 | {
14 | ///
15 | /// Writes a log entry.
16 | ///
17 | /// The severity of the event being logged.
18 | /// A message summarizing the event being logged.
19 | /// The exception related to this event, if any.
20 | public void Log(LogLevel logLevel, string message = null, Exception exception = null)
21 | {
22 | if (logLevel == LogLevel.Debug && !LibraryState.IsDebugMode)
23 | {
24 | // Ignore debug logs when not running in debug mode.
25 | return;
26 | }
27 |
28 | var destination = logLevel == LogLevel.Info
29 | ? Console.Out
30 | : Console.Error;
31 | var nonNullMessage = !string.IsNullOrEmpty(message)
32 | ? message
33 | : exception is not null
34 | ? exception.Message
35 | : $"Parquet {nameof(LoggerDefault)} Trace at {DateTime.Now}.";
36 |
37 | destination.WriteLine(nonNullMessage);
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/LibraryState.cs:
--------------------------------------------------------------------------------
1 | namespace Parquet
2 | {
3 | ///
4 | /// Represents how the library was built and how it is currently being used.
5 | ///
6 | public static class LibraryState
7 | {
8 | #pragma warning disable IDE0079 // Remove unnecessary suppression -- conditional compilation.
9 | #pragma warning disable RS0016 // Add public types and members to the declared API -- conditional compilation.
10 | ///
11 | /// true if the library was built with the symbol DEBUG defined; false otherwise.
12 | ///
13 | public const bool IsDebugMode =
14 | #if DEBUG
15 | true;
16 | #else
17 | false;
18 | #endif
19 | #pragma warning restore RS0016 // Add public types and members to the declared API -- conditional compilation.
20 | #pragma warning restore IDE0079 // Remove unnecessary suppression -- conditional compilation.
21 |
22 | ///
23 | /// Set this to true if the library is being used by a game;
24 | /// set it to false if it is being used by an editor or tool.
25 | ///
26 | ///
27 | /// s are designed to be immutable during play
28 | /// but mutable at design time.
29 | /// By preventing model mutation when this is set to true, Parquet
30 | /// removes some of the responsibility to respect this distinction from the
31 | /// game developer.
32 | ///
33 | public static bool IsPlayMode { get; set; }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Parquets/IMutableParquetModel.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Parquet.Parquets
4 | {
5 | ///
6 | /// Facilitates editing of a from design tools while maintaining a read-only face for use during play.
7 | ///
8 | ///
9 | /// By design, subtypes of should never themselves use .
10 | /// IMutableParquetModel is for use only by external types that require read/write access to model properties.
11 | ///
12 | public interface IMutableParquetModel : IMutableModel
13 | {
14 | ///
15 | /// The of the awarded to the player when a character gathers or collects this parquet.
16 | ///
17 | public ModelID ItemID { get; set; }
18 |
19 | ///
20 | /// Describes the (s) that this parquet helps form.
21 | /// Guaranteed to never be null.
22 | ///
23 | public ICollection AddsToBiome { get; }
24 |
25 | ///
26 | /// A property of the parquet that can, for example, be used by s.
27 | /// Guaranteed to never be null.
28 | ///
29 | ///
30 | /// Allows the creation of classes of constructs, for example "wooden", "golden", "rustic", or "fancy" rooms.
31 | ///
32 | public ICollection AddsToRoom { get; }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/LogLevel.cs:
--------------------------------------------------------------------------------
1 | namespace Parquet
2 | {
3 | ///
4 | /// Defines logging severity levels.
5 | ///
6 | public enum LogLevel
7 | {
8 | ///
9 | /// Events of interest during development only.
10 | /// Used while implementing and testing.
11 | /// Library developers should see these events.
12 | ///
13 | Debug = 0,
14 |
15 | ///
16 | /// Normal and expected events that track application flow.
17 | /// Used to diagnose issues after release.
18 | /// Game developers should see these events.
19 | ///
20 | Info = 1,
21 |
22 | ///
23 | /// Unexpected events that do not interrupt normal operation.
24 | /// Used to diagnose issues after release.
25 | /// Game developers should see these events.
26 | ///
27 | Warning = 2,
28 |
29 | ///
30 | /// Abnormal events that interrupt operation, but from which the application can recover.
31 | /// Used to report a failure in the current activity, not an application-wide failure.
32 | /// Game players should see these events.
33 | ///
34 | Error = 3,
35 |
36 | ///
37 | /// Fatal events from which the application cannot recover.
38 | /// Used to report an application or system failures that requires immediate attention.
39 | /// Game players should see these events.
40 | /// These events typically require that the game and library be restarted.
41 | ///
42 | Fatal = 4,
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/ParquetUnitTests/Regions/RegionStatusUnitTestExtensions.cs:
--------------------------------------------------------------------------------
1 |
2 | // TODO [TESTING] This needs to use RegionStatus instead of RegionModel.
3 | /*
4 | namespace ParquetUnitTests.Maps
5 | {
6 | ///
7 | /// Provides extension methods for the used in unit testing.
8 | ///
9 | internal static class RegionStatusUnitTestExtensions
10 | {
11 | /// Fills the region with a test pattern.
12 | internal static RegionModel FillTestPattern(this RegionModel regionModel)
13 | {
14 | for (var x = 0; x < RegionStatus.ParquetsPerRegionDimension; x++)
15 | {
16 | for (var y = 0; y < RegionStatus.ParquetsPerRegionDimension; y++)
17 | {
18 | regionModel.ParquetDefinitions[y, x].FloorID = TestModels.TestFloor.ID;
19 | }
20 |
21 | regionModel.ParquetDefinitions[0, x].BlockID = TestModels.TestBlock.ID;
22 | regionModel.ParquetDefinitions[RegionModel.ParquetsPerRegionDimension, 1].BlockID = TestModels.TestBlock.ID;
23 | }
24 | for (var y = 0; y < RegionStatus.ParquetsPerRegionDimension; y++)
25 | {
26 | regionModel.ParquetDefinitions[y, 0].BlockID = TestModels.TestBlock.ID;
27 | regionModel.ParquetDefinitions[y, RegionModel.ParquetsPerRegionDimension - 1].BlockID = TestModels.TestBlock.ID;
28 | }
29 | regionModel.ParquetDefinitions[2, 1].FurnishingID = TestModels.TestFurnishing.ID;
30 | regionModel.ParquetDefinitions[3, 3].CollectibleID = TestModels.TestCollectible.ID;
31 |
32 | return regionModel;
33 | }
34 | }
35 | }
36 | */
37 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/GlobalSuppressions.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | // This file is used by Code Analysis to maintain SuppressMessage
3 | // attributes. Project-level suppressions have no target.
4 |
5 | [assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types",
6 | Justification = "Parquet logs errors and returns results; it does not throw exceptions.",
7 | Scope = "namespaceanddescendants", Target = "~N:Parquet")]
8 |
9 | [assembly: SuppressMessage("Naming", "CA1725:Parameter names should match base declaration",
10 | Justification = "This rule inhibits clarity as it prefers general terms to specific terms. Also, it creates confusion by forcing non-ItemModels to be called 'item' (for example, when implementing IEnumerableConverter).",
11 | Scope = "namespaceanddescendants", Target = "~N:Parquet")]
12 |
13 | [assembly: SuppressMessage("Performance", "CA1814:Prefer jagged arrays over multidimensional",
14 | Justification = "Parquet requires rectangular multidimensional arrays.",
15 | Scope = "namespaceanddescendants", Target = "~N:Parquet")]
16 |
17 | [assembly: SuppressMessage("Usage", "CA2225:Operator overloads have named alternates",
18 | Justification = "This would defeat the purpose of making these classes implicitly interchangeable.",
19 | Scope = "namespaceanddescendants", Target = "~N:Parquet")]
20 |
21 | [assembly: SuppressMessage("Major Code Smell", "S3358:Ternary operators should not be nested",
22 | Justification = "This rule is incorrect.",
23 | Scope = "namespaceanddescendants", Target = "~N:Parquet")]
24 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Regions/ChunkTopography.cs:
--------------------------------------------------------------------------------
1 | namespace Parquet.Regions
2 | {
3 | /// Indicates the basic form that the parquets in a take.
4 | public enum ChunkTopography
5 | {
6 | /// Indicates there are no parquets in this topography.
7 | Empty = 0,
8 | /// Indicates parquets entirely fill this topography.
9 | Solid,
10 | /// Indicates parquets are spread evenly throughout this topography.
11 | Scattered,
12 | /// Indicates parquets appear in clumps throughout this topography.
13 | Clustered,
14 | /// Indicates a central grouping of parquets in this topography.
15 | Central,
16 | /// Indicates parquets are grouped to the north end of this topography.
17 | North,
18 | /// Indicates parquets are grouped on both the north and east end of this topography.
19 | NorthEast,
20 | /// Indicates parquets are grouped to the east end of this topography.
21 | East,
22 | /// Indicates parquets are grouped on both the south and east end of this topography.
23 | SouthEast,
24 | /// Indicates parquets are grouped to the south end of this topography.
25 | South,
26 | /// Indicates parquets are grouped on both the south and west end of this topography.
27 | SouthWest,
28 | /// Indicates parquets are grouped to the west end of this topography.
29 | West,
30 | /// Indicates parquets are grouped on both the north and west end of this topography.
31 | NorthWest,
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Items/IMutableItemModel.cs:
--------------------------------------------------------------------------------
1 | namespace Parquet.Items
2 | {
3 | ///
4 | /// Facilitates editing of a from design tools while maintaining a read-only face for use during play.
5 | ///
6 | ///
7 | /// By design, subtypes of should never themselves use .
8 | /// IMutableItemModel is for use only by external types that require read/write access to model properties.
9 | ///
10 | public interface IMutableItemModel : IMutableModel
11 | {
12 | /// The type of item this is.
13 | public ItemType Subtype { get; set; }
14 |
15 | /// In-game value of the item. Must be non-negative.
16 | public int Worth { get; set; }
17 |
18 | /// How relatively rare this item is.
19 | public int Rarity { get; set; }
20 |
21 | /// How many of the item may share a single inventory slot.
22 | public int StackMax { get; set; }
23 |
24 | ///
25 | /// The of the generating the in-game effect caused by
26 | /// keeping the item in a 's .
27 | ///
28 | public ModelID EffectWhileHeldID { get; set; }
29 |
30 | ///
31 | /// The of the generating the in-game effect caused by
32 | /// using (consuming) the item.
33 | ///
34 | public ModelID EffectWhenUsedID { get; set; }
35 |
36 | /// The parquet that corresponds to this item, if any.
37 | public ModelID ParquetID { get; set; }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 |
6 | // Make no promises to maintain public services.
7 | [assembly: ComVisible(false)]
8 |
9 | // Show warnings on CLS-noncompliant statements to better support .NET languages other than C#.
10 | [assembly: CLSCompliant(true)]
11 |
12 | // Allow unit tests to access classes and members with internal accessibility.
13 | [assembly: InternalsVisibleTo("ParquetUnitTests")]
14 |
15 | // TODO [Tests] Once unit tests are reliable, this should probably be removed and test content in Runner slimmed down.
16 | // Allow smoke test to access classes and members with internal accessibility.
17 | [assembly: InternalsVisibleTo("ParquetRunner")]
18 |
19 | namespace Parquet
20 | {
21 | ///
22 | /// Provides assembly-wide information.
23 | ///
24 | [SuppressMessage("Performance", "CA1815:Override equals and operator equals on value types",
25 | Justification = "Comparing two AssemblyInfos is nonsensical.")]
26 | public readonly struct AssemblyInfo
27 | {
28 | /// Describes the version of the class library itself.
29 | ///
30 | /// The version has the format "{Major}.{Minor}.{Patch}.{Build}".
31 | /// - Major: Enhancements or fixes that break the API or its serialized data.
32 | /// - Minor: Enhancements that do not break the API or its serialized data.
33 | /// - Patch: Fixes that do not break the API or its serialized data.
34 | /// - Build: Procedural updates that do not imply any changes, such as when rebuilding for APK/IPA submission.
35 | ///
36 | public static readonly string LibraryVersion = typeof(ModelID).Assembly?.GetName()?.Version?.ToString() ?? "?.?.?.?";
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # ============= #
2 | # IDE generated #
3 | # ============= #
4 | /[Aa]ssets/Plugins/Editor/JetBrains*
5 | /[Aa]pp[Pp]ackages/
6 | /[Dd]ebug/
7 | /[Rr]elease/
8 | /[Rr]eleases/
9 | /x86/
10 | /x64/
11 | [Bb]in/
12 | [Bb]in-[Rr]elease/
13 | [Bb]uild/
14 | [Bb]uilds/
15 | [Bb]ld/
16 | [Dd]ebugPublic/
17 | [Ee]xported[Oo]bj/
18 | .idea/
19 | .builds/
20 | .consulo/
21 | .idea/
22 | .vs/
23 | *.[Cc]ache
24 | *.[Cc]ache[Ff]ile
25 | *.csproj.user
26 | *.mdb
27 | *.pidb
28 | *.pdb
29 | *.svd
30 | *.temp
31 | *.tmp
32 | *.tmp_proj
33 | *.unityproj
34 | *.userprefs
35 |
36 | # ============== #
37 | # Tool generated #
38 | # ============== #
39 | [Ll]ibrary/
40 | [Oo]bj/
41 | [Tt]emp/
42 | [Uu]nity[Gg]enerated/
43 | [Ss]witchIL2CPPCache/
44 | [Ss]witchIL2CPPStats/
45 | [Ll]ogs/
46 | .gradle/
47 | _ReSharper*/
48 | *.pidb.meta
49 | *.[Dd]otCover
50 | *.[Dd]otSettings.user
51 | *.[Rr]e[Ss]harper
52 | sysinfo.txt
53 |
54 | # =============== #
55 | # NuGet generated #
56 | # =============== #
57 | **/[Pp]ackages/*
58 | /[Aa]rtifacts/
59 | *.nupkg
60 | *.nuget.props
61 | *.nuget.targets
62 | project.lock.json
63 | project.assets.json
64 |
65 | # ===================== #
66 | # Text editor generated #
67 | # ===================== #
68 | ~$*
69 | *~
70 |
71 | # ============ #
72 | # OS generated #
73 | # ============ #
74 | .DS_Store
75 | .DS_Store?
76 | ._*
77 | .Spotlight-V100
78 | .Trashes
79 | ehthumbs.db
80 | Thumbs.db
81 | Icon?
82 | Icons?
83 |
84 | # ====== #
85 | # Builds #
86 | # ====== #
87 | *.apk
88 | *.app
89 | *.dll
90 | *.exe
91 | *.iap
92 | *.unitypackage
93 | ParquetClassLibrary/Parquet.xml
94 | ParquetClassLibrary/Parquet.Documentation.xml
95 | ParquetRunner/ParquetRunner.Documentation.xml
96 | ParquetUnitTests/ParquetUnitTests.Documentation.xml
97 |
98 | # ================== #
99 | # Paige Stuff #
100 | # ================== #
101 | *.old
102 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/IReadOnlyListOfTagsExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 |
4 | namespace Parquet
5 | {
6 | ///
7 | /// Extensions to the interface for working with s.
8 | ///
9 | public static class IReadOnlyListOfTagsExtensions
10 | {
11 | ///
12 | /// Determines whether this contains a
13 | /// beginning with the given prefix.
14 | ///
15 | /// The to search.
16 | /// The prefix to search for.
17 | /// true if this and the given instance are identical, ignoring case; otherwise, false.
18 | /// This is a convenience for client code and not used by the library itself.
19 | public static bool ContainsPrefixOrdinalIgnoreCase(this IReadOnlyList collection, ModelTag prefix)
20 | => collection.Any(tag => tag.StartsWithOrdinalIgnoreCase(prefix));
21 |
22 | ///
23 | /// Determines whether this contains the given .
24 | ///
25 | /// The to search.
26 | /// The to search for.
27 | /// true if this and the given instance are identical, ignoring case; otherwise, false.
28 | /// This is a convenience for client code and not used by the library itself.
29 | public static bool ContainsOrdinalIgnoreCase(this IReadOnlyList collection, ModelTag tag)
30 | => collection.Any(aTag => aTag.EqualsOrdinalIgnoreCase(tag));
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/ParquetUnitTests/Crafts/CraftingRecipeUnitTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Parquet;
4 | using Parquet.Crafts;
5 | using Xunit;
6 |
7 | namespace ParquetUnitTests.Crafts
8 | {
9 | ///
10 | /// Unit tests .
11 | ///
12 | public class CraftingRecipeUnitTest
13 | {
14 | #region Test Values
15 | /// Identifier used when creating a new block.
16 | private static readonly ModelID newCraftingRecipeID = TestModels.TestCraftingRecipe.ID - 1;
17 |
18 | /// A valid minimal list of test ingredients.
19 | private static readonly IReadOnlyList ingredientList = new List
20 | {
21 | new RecipeElement(1, "ingredient"),
22 | };
23 |
24 | /// A valid minimal list of test products.
25 | private static readonly IReadOnlyList productList = new List
26 | {
27 | new RecipeElement(1, "product"),
28 | };
29 | #endregion
30 |
31 | [Fact]
32 | internal void ValidCraftingRecipeIDsArePermittedTest()
33 | {
34 | var newCraftingRecipe = new CraftingRecipe(newCraftingRecipeID, "will be created", "", "", null,
35 | productList, ingredientList);
36 |
37 | Assert.NotNull(newCraftingRecipe);
38 | }
39 |
40 | [Fact]
41 | internal void InvalidCraftingRecipeIDsThrowTest()
42 | {
43 | var badCraftingRecipeID = TestModels.TestBlock.ID - 1;
44 |
45 | void TestCode()
46 | {
47 | var _ = new CraftingRecipe(badCraftingRecipeID, "will fail", "", "", null,
48 | productList, ingredientList);
49 | }
50 |
51 | Assert.Throws(TestCode);
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/ParquetUnitTests/Parquets/CollectibleModelUnitTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Parquet;
3 | using Parquet.Parquets;
4 | using Xunit;
5 |
6 | namespace ParquetUnitTests.Parquets
7 | {
8 | ///
9 | /// Unit tests .
10 | ///
11 | public class CollectibleModelUnitTest
12 | {
13 | #region Test Values
14 | /// Identifier used when creating a new collectible.
15 | private static readonly ModelID newCollectibleID = TestModels.TestCollectible.ID - 1;
16 | #endregion
17 |
18 | [Fact]
19 | internal void ValidCollectibleIDsArePermittedTest()
20 | {
21 | var testCollectible = new CollectibleModel(newCollectibleID, "will be created", "", "");
22 |
23 | Assert.NotNull(testCollectible);
24 | }
25 |
26 | [Fact]
27 | internal void InvalidCollectibleIDsRaiseExceptionTest()
28 | {
29 | var badCollectibleID = TestModels.TestBlock.ID;
30 |
31 | void TestCode()
32 | {
33 | var _ = new CollectibleModel(badCollectibleID, "will fail", "", "");
34 | }
35 |
36 | Assert.Throws(TestCode);
37 | }
38 |
39 | [Fact]
40 | internal void ValidItemIDsArePermittedTest()
41 | {
42 | ModelID goodItemID = -All.ItemIDs.Minimum;
43 |
44 | var testBlock = new CollectibleModel(newCollectibleID, "will be created", "", "", null, goodItemID);
45 |
46 | Assert.NotNull(testBlock);
47 | }
48 |
49 | [Fact]
50 | internal void InvalidItemIDsRaiseExceptionTest()
51 | {
52 | var badItemID = TestModels.TestBlock.ID;
53 |
54 | void TestCode()
55 | {
56 | var _ = new CollectibleModel(newCollectibleID, "will fail", "", "", null, badItemID);
57 | }
58 |
59 | Assert.Throws(TestCode);
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/IReadOnlyCollectionOfTagsExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 |
4 | namespace Parquet
5 | {
6 | ///
7 | /// Extensions to the interface for working with s.
8 | ///
9 | public static class IReadOnlyCollectionOfTagsExtensions
10 | {
11 | ///
12 | /// Determines whether this contains a
13 | /// beginning with the given prefix.
14 | ///
15 | /// The to search.
16 | /// The prefix to search for.
17 | /// true if this and the given instance are identical, ignoring case; otherwise, false.
18 | /// This is a convenience for client code and not used by the library itself.
19 | public static bool ContainsPrefixOrdinalIgnoreCase(this IReadOnlyCollection collection, ModelTag prefix)
20 | => collection.Any(tag => tag.StartsWithOrdinalIgnoreCase(prefix));
21 |
22 | ///
23 | /// Determines whether this contains the given .
24 | ///
25 | /// The to search.
26 | /// The to search for.
27 | /// true if this and the given instance are identical, ignoring case; otherwise, false.
28 | /// This is a convenience for client code and not used by the library itself.
29 | public static bool ContainsOrdinalIgnoreCase(this IReadOnlyCollection collection, ModelTag tag)
30 | => collection.Any(aTag => aTag.EqualsOrdinalIgnoreCase(tag));
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/IMutableModel.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Parquet
4 | {
5 | ///
6 | /// Facilitates editing of a from design tools while maintaining a read-only face for use during play.
7 | ///
8 | ///
9 | /// and its subtypes are not editable once created. This is because during play, these classes serve
10 | /// as definitions, which need to remain fixed. Any state that changes during runtime is instead housed in a corresponding
11 | /// State class.
12 | ///
13 | /// However, at design time even definitions need to be changeable. To support this easily while still preserving the fixedness
14 | /// of Model and company during play, and its subtypes provide interfaces that may be used to safely
15 | /// make changes to the properties of their corresponding models.
16 | ///
17 | /// By design, subtypes of should never themselves use their interface.
18 | /// IMutableModel is for use only by external types (such as those in a design-time tool) that require read/write access to model
19 | /// properties.
20 | ///
21 | public interface IMutableModel : IVisibleData
22 | {
23 | /// Game-wide unique identifier.
24 | /// Be cautious editing this.
25 | public ModelID ID { get; set; }
26 |
27 | /// Player-facing name.
28 | public string Name { get; set; }
29 |
30 | /// Player-facing description.
31 | public string Description { get; set; }
32 |
33 | /// Optional comment.
34 | /// Could be used for designer's notes or to implement an in-game dialogue with or on the .
35 | public string Comment { get; set; }
36 |
37 | /// Any additional functionality this item has, e.g. contributing to a .
38 | public ICollection Tags { get; }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/ParquetUnitTests/Parquets/ParquetPackUnitTest.cs:
--------------------------------------------------------------------------------
1 | using Parquet.Parquets;
2 | using Xunit;
3 |
4 | namespace ParquetUnitTests.Parquets
5 | {
6 | ///
7 | /// Unit tests .
8 | ///
9 | public class ParquetPackUnitTest
10 | {
11 | [Fact]
12 | internal void ParquetPackIsEmptyWhenAllFieldsAreNullTest()
13 | {
14 | var pack = new ParquetModelPack();
15 |
16 | Assert.True(pack.IsEmpty);
17 | }
18 |
19 | [Fact]
20 | internal void ParquetPackDotEmptyIsEmpty()
21 | {
22 | Assert.True(ParquetModelPack.Empty.IsEmpty);
23 | }
24 |
25 | [Fact]
26 | internal void IdenticalStacksAreEqualTest()
27 | {
28 | var pack1 = new ParquetModelPack(TestModels.TestFloor.ID,
29 | TestModels.TestBlock.ID,
30 | TestModels.TestFurnishing.ID,
31 | TestModels.TestCollectible.ID);
32 | var pack2 = new ParquetModelPack(TestModels.TestFloor.ID,
33 | TestModels.TestBlock.ID,
34 | TestModels.TestFurnishing.ID,
35 | TestModels.TestCollectible.ID);
36 |
37 | Assert.Equal(pack1, pack2);
38 | }
39 |
40 | [Fact]
41 | internal void DifferingStacksAreUnequalTest()
42 | {
43 | var pack1 = new ParquetModelPack(TestModels.TestFloor.ID,
44 | TestModels.TestBlock.ID,
45 | TestModels.TestFurnishing.ID,
46 | TestModels.TestCollectible.ID);
47 | var pack2 = new ParquetModelPack(TestModels.TestFloor.ID - 1,
48 | TestModels.TestBlock.ID - 1,
49 | TestModels.TestFurnishing.ID - 1,
50 | TestModels.TestCollectible.ID - 1);
51 |
52 | Assert.NotEqual(pack1, pack2);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Documentation/3-How_Parquets_Work.md:
--------------------------------------------------------------------------------
1 | A **parquet** is the smallest interactive unit of the game world. These are the units with which the player builds and that are available in the map editor.
2 |
3 | The word "parquet" refers to a specific part of game logic, not a graphical component or style.
4 |
5 | Parquets are defined once and used many times throughout the game world's 2D map. Once a type of parquet is defined, each parquet of that type shares all the mechanical attributes of the definition. For example, all silver coin collectibles award the player the same amount of money.
6 |
7 | Some types of parquets, notably *floors* and *blocks*, may have statuses that are particular to a specific location. For example, the block at the player's location may have been damaged while other blocks have not.
8 |
9 | Parquets are normally arranged in **Parquet Packs**. In a ptack, a given location on the map may hold four parquets of different types simultaneously, arranged as follows from top to bottom:
10 |
11 | * Collectibles
12 | * Furnishings
13 | * Blocks
14 | * Floors
15 |
16 | As an example, a Parquet Pack might consist of:
17 |
18 | * a feather Collectible
19 | * on a birdhouse Furnishing
20 | * in a pine tree Block
21 | * growing on a grass forest Floor.
22 |
23 | Another example is:
24 |
25 | * a rubber duck Collectible
26 | * and no Furnishing
27 | * on a water Block
28 | * sitting in a *dug out* porcelain basin Floor.
29 |
30 | The player automatically interacts with Collectibles when moving onto their location. Other parquets require mediation for interaction.
31 |
32 | Furnishings, Blocks, and Floors require tools to be interacted with. These interactions can result in Floors changing state (e.g. *filled in* vs. *dug out*), Blocks becoming Collectibles (chopping down a tree to form wood), or in being directly removed from the map and added to the player's inventory as **Items**.
33 |
34 | When Furnishings and Blocks are transferred from the map to the inventory they are said to be **Gather**ed.
35 | Collectibles are said to be **Collect**ed, since this happens without the mediation of tools. This distinction is important on a game design and implementation level but is invisible to the player.
36 |
--------------------------------------------------------------------------------
/ParquetUnitTests/ITypeConverterTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Reflection;
4 | using CsvHelper.TypeConversion;
5 | using Parquet;
6 | using Xunit;
7 |
8 | namespace ParquetUnitTests
9 | {
10 | ///
11 | /// Tests that classes implementing also provide a ConverterFactory static property.
12 | ///
13 | public class ITypeConverterTest
14 | {
15 | ///
16 | /// Ensures that all concrete classes implementing the ITypeConverter interface also implement the ConverterFactory static property.
17 | ///
18 | [Fact]
19 | internal void AllTypeConvertersProvideFactoriesTest()
20 | {
21 | // This discarded value is here to ensure that ParquetClassLibrary is loaded.
22 | var _ = new Point2D(1, 2);
23 | var converterProviders = AppDomain.CurrentDomain
24 | .GetAssemblies()
25 | .Where(assembly => assembly.GetName().Name == "ParquetClassLibrary")
26 | .SelectMany(assembly => assembly.GetExportedTypes())
27 | .Where(type => typeof(ITypeConverter).IsAssignableFrom(type)
28 | && !type.IsInterface
29 | && !type.IsAbstract);
30 |
31 | var factoryProviders = AppDomain.CurrentDomain
32 | .GetAssemblies()
33 | .Where(assembly => assembly.GetName().Name == "ParquetClassLibrary")
34 | .SelectMany(assembly => assembly.GetExportedTypes())
35 | .Where(type => type.GetRuntimeProperties()
36 | .Any(info => info.Name == nameof(ModelTag.ConverterFactory)));
37 |
38 | foreach (var provider in converterProviders)
39 | {
40 | Assert.Contains(provider, factoryProviders);
41 | }
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/ParquetUnitTests/Point2DUnitTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Parquet;
3 | using Xunit;
4 |
5 | namespace ParquetUnitTests
6 | {
7 | ///
8 | /// Unit tests .
9 | ///
10 | public class Point2DUnitTest
11 | {
12 | #region Test Values
13 | private readonly Point2D PointTwoTwo = new(2, 2);
14 | private readonly Point2D PointNegativeUnit = new(-1, -1);
15 | #endregion
16 |
17 | [Fact]
18 | internal void ZeroPointTest()
19 | {
20 | Assert.Equal(0, Point2D.Origin.X);
21 | Assert.Equal(0, Point2D.Origin.Y);
22 | Assert.Equal(0, Point2D.Origin.Magnitude);
23 | }
24 |
25 | [Fact]
26 | internal void UnitPointTest()
27 | {
28 | Assert.Equal(1, Point2D.Unit.X);
29 | Assert.Equal(1, Point2D.Unit.Y);
30 | Assert.Equal(1, Point2D.Unit.Magnitude);
31 | }
32 |
33 | [Theory]
34 | [InlineData(-4096, -4096)]
35 | [InlineData(-1, 1)]
36 | [InlineData(0, 0)]
37 | [InlineData(1, -1)]
38 | [InlineData(4096, 4096)]
39 | internal void NewPointTest(int x, int y)
40 | {
41 | var testPoint = new Point2D(x, y);
42 | var magnitude = Convert.ToInt32(Math.Floor(Math.Sqrt((x * x) + (y * y))));
43 |
44 | Assert.Equal(x, testPoint.X);
45 | Assert.Equal(y, testPoint.Y);
46 | Assert.Equal(magnitude, testPoint.Magnitude);
47 | }
48 |
49 | [Fact]
50 | internal void PointSumTest()
51 | {
52 | Assert.Equal(PointTwoTwo, Point2D.Unit + Point2D.Unit);
53 | Assert.Equal(Point2D.Origin, Point2D.Unit + PointNegativeUnit);
54 | }
55 |
56 | [Fact]
57 | internal void PointDifferenceTest()
58 | {
59 | Assert.Equal(Point2D.Origin, Point2D.Unit - Point2D.Unit);
60 | Assert.Equal(Point2D.Origin, PointNegativeUnit - PointNegativeUnit);
61 | }
62 |
63 | [Fact]
64 | internal void ScalarMultiplicationTest()
65 | {
66 | Assert.Equal(PointTwoTwo, 2 * Point2D.Unit);
67 | Assert.Equal(Point2D.Unit, -1 * PointNegativeUnit);
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/IDictionaryExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Globalization;
4 | using Parquet.Properties;
5 |
6 | namespace Parquet
7 | {
8 | ///
9 | /// Provides extension methods to .
10 | ///
11 | internal static class IDictionaryExtensions
12 | {
13 | /// Clears the dictionary, then attempts to add all the given elements.
14 | /// The being altered.
15 | /// The new content for the .
16 | /// true if every add succeeded; false otherwise.
17 | public static bool TryReplaceWith(this IDictionary dictionaryToAlter, IReadOnlyDictionary keyValuePairs)
18 | {
19 | try
20 | {
21 | dictionaryToAlter.ReplaceWith(keyValuePairs);
22 | }
23 | catch (Exception replaceException)
24 | {
25 | Logger.Log(LogLevel.Error,
26 | string.Format(CultureInfo.CurrentCulture, Resources.ErrorCannotReplaceDictionary, replaceException.Message),
27 | replaceException);
28 |
29 | return false;
30 | }
31 |
32 | return true;
33 | }
34 |
35 | /// Clears the dictionary, then adds all the given elements.
36 | /// The being altered.
37 | /// The new content for the .
38 | /// true if every add succeeded; false otherwise.
39 | internal static void ReplaceWith(this IDictionary dictionaryToAlter, IReadOnlyDictionary keyValuePairs)
40 | {
41 | dictionaryToAlter.Clear();
42 | foreach (KeyValuePair kvp in keyValuePairs ?? new Dictionary())
43 | {
44 | dictionaryToAlter[kvp.Key] = kvp.Value;
45 | }
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/ParquetUnitTests/Regions/RegionModelUnitTest.cs:
--------------------------------------------------------------------------------
1 | using Parquet;
2 | using Parquet.Regions;
3 | using Xunit;
4 |
5 | namespace ParquetUnitTests.Maps
6 | {
7 | ///
8 | /// Unit tests .
9 | ///
10 | public class RegionModelUnitTest
11 | {
12 | #region Values for Tests
13 | private const string testColor = "#FF8822EE";
14 | private const string testName = "Test Region";
15 | private static readonly RegionModel defaultRegion = new(TestModels.TestRegionModel.ID - 1, nameof(defaultRegion), "", "");
16 | #endregion
17 |
18 | #region Region Map Initialization
19 | [Fact]
20 | internal void NewDefaultMapRegionModelTest()
21 | {
22 | Assert.Equal(RegionModel.DefaultColor, defaultRegion.BackgroundColor);
23 | }
24 |
25 | [Fact]
26 | internal void NewCustomMapRegionModelTest()
27 | {
28 | var customRegion = new RegionModel(TestModels.TestRegionModel.ID - 1, testName, "", "", null, testColor);
29 |
30 | Assert.Equal(testName, customRegion.Name);
31 | Assert.Equal(testColor, customRegion.BackgroundColor);
32 | }
33 | #endregion
34 |
35 | #region Whole Region Characteristics Editing
36 | #pragma warning disable IDE0079 // Remove unnecessary suppression -- conditional compilation.
37 | #pragma warning disable CS0162 // Unreachable code detected -- conditional compilation.
38 | [Fact]
39 | internal void MapRegionModelMayBeEditedTest()
40 | {
41 | if (LibraryState.IsDebugMode)
42 | {
43 | var customRegion = new RegionModel(TestModels.TestRegionModel.ID - 1, testName, "", "", null, testColor);
44 | IMutableRegionModel editableRegion = customRegion;
45 |
46 | editableRegion.Name = testName;
47 | editableRegion.BackgroundColor = testColor;
48 |
49 | Assert.Equal(testName, customRegion.Name);
50 | Assert.Equal(testColor, customRegion.BackgroundColor);
51 | }
52 | }
53 | #pragma warning restore CS0162 // Unreachable code detected -- conditional compilation.
54 | #pragma warning restore IDE0079 // Remove unnecessary suppression -- conditional compilation.
55 | #endregion
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Items/InventoryConfiguration.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 |
4 | namespace Parquet.Items
5 | {
6 | ///
7 | /// Provides rules for working with an .
8 | ///
9 | public static class InventoryConfiguration
10 | {
11 | #region Class Defaults
12 | /// The capacity to use for an when the configuration cannot be read.
13 | private const int FallbackCapacity = 16;
14 | #endregion
15 |
16 | #region Characteristics
17 | /// The capacity to use for an when none is specified.
18 | public static int DefaultCapacity { get; set; } = FallbackCapacity;
19 | #endregion
20 |
21 | #region Self Serialization
22 | ///
23 | /// Reads data from the appropriate file.
24 | ///
25 | /// The instances read.
26 | public static void GetRecord()
27 | {
28 | using var reader = new StreamReader(FilePath);
29 |
30 | // Skip the header.
31 | reader.ReadLine();
32 |
33 | // Read in the value.
34 | var value = reader.ReadLine();
35 |
36 | // Parse.
37 | DefaultCapacity = int.TryParse(value, out var parsedCapacity)
38 | ? parsedCapacity
39 | : Logger.DefaultWithParseLog(value, nameof(DefaultCapacity), FallbackCapacity);
40 | }
41 |
42 | ///
43 | /// Writes data to the appropriate file.
44 | ///
45 | public static void PutRecord()
46 | {
47 | using var writer = new StreamWriter(FilePath, false, new UTF8Encoding(true, true));
48 | writer.Write($"{nameof(DefaultCapacity)}\n");
49 | writer.Write($"{DefaultCapacity}\n");
50 | }
51 |
52 | ///
53 | /// Returns the filename and path associated with 's definition file.
54 | ///
55 | /// A full path to the associated file.
56 | public static string FilePath
57 | => $"{All.ProjectDirectory}/{nameof(InventoryConfiguration)}.csv";
58 | #endregion
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Delimiters.cs:
--------------------------------------------------------------------------------
1 | namespace Parquet
2 | {
3 | ///
4 | /// Provides a unified source of serialization separators for the library.
5 | ///
6 | ///
7 | /// Every string listed here is reserved by Parquet and cannot be stored in any
8 | /// or other library type.
9 | /// Doing so will cause deserialization to fail.
10 | ///
11 | public static class Delimiters
12 | {
13 | // NOTE that currently delimiters are not stored as chars because PronounGroup requires compile-time
14 | // delimiter concatenation, which is currently only possible with strings. (Still true in .NET 6/C# 10.)
15 |
16 | /// Separator for encoding the dimensions of implementations.
17 | public const string DimensionalDelimiter = "×";
18 |
19 | /// Separator for introducting the content of an implementations.
20 | public const string DimensionalTerminator = "≡";
21 |
22 | /// Separates primitives within serialized s and s.
23 | public const string ElementDelimiter = "–";
24 |
25 | /// Separates properties within a class when in serialization.
26 | public const string InternalDelimiter = "·";
27 |
28 | /// Separates metadata within serialized s.
29 | public const string MapChunkDelimiter = "∟";
30 |
31 | /// Separates family and personal names within serialized s.
32 | public const string NameDelimiter = "§";
33 |
34 | /// Marks out tags that need to be replaced with pronouns from a s.
35 | public const string PronounDelimiter = "|";
36 |
37 | /// Separates collections within files.
38 | public const string PrimaryDelimiter = ",";
39 |
40 | /// Separates objects within unnested, non- collections.
41 | public const string SecondaryDelimiter = "❟";
42 |
43 | /// Separates objects within nested or paired collections.
44 | public const string TertiaryDelimiter = "❠";
45 |
46 | /// Separates objects within s.
47 | public const string PackDelimiter = "⚭";
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/ParquetUnitTests/Items/InventorySlotUnitTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Parquet;
3 | using Parquet.Items;
4 | using Xunit;
5 |
6 | namespace ParquetUnitTests.Items
7 | {
8 | ///
9 | /// Unit tests .
10 | ///
11 | public class InventorySlotUnitTest
12 | {
13 | #region Test Values
14 | private const int None = 0;
15 | private const int One = 1;
16 | private const int Some = 10;
17 | #endregion
18 |
19 | [Fact]
20 | internal void CannotStoreSomeOfNothingTest()
21 | {
22 | static void TestCode()
23 | {
24 | var _ = new InventorySlot(ModelID.None, Some);
25 | }
26 |
27 | Assert.Throws(TestCode);
28 | }
29 |
30 | [Fact]
31 | internal void CannotStoreNoneOfSomethingTest()
32 | {
33 | static void TestCode()
34 | {
35 | var _ = new InventorySlot(TestModels.TestItem1.ID, None);
36 | }
37 |
38 | Assert.Throws(TestCode);
39 | }
40 |
41 | [Fact]
42 | internal void CannotStoreNonItemTest()
43 | {
44 | static void TestCode()
45 | {
46 | var _ = new InventorySlot(TestModels.TestBlock.ID, Some);
47 | }
48 |
49 | Assert.Throws(TestCode);
50 | }
51 |
52 |
53 | [Fact]
54 | internal void CountMatchesItemsStoredTest()
55 | {
56 | var slot = new InventorySlot(TestModels.TestItem1.ID, Some);
57 |
58 | Assert.Equal(Some, slot.Count);
59 | }
60 |
61 | [Fact]
62 | internal void SlotAddTest()
63 | {
64 | var slot = new InventorySlot(TestModels.TestItem1.ID, One);
65 | var remainder1 = 0;
66 | var remainder2 = One + Some;
67 |
68 | Assert.Equal(remainder1, slot.Give(Some));
69 | Assert.Equal(remainder2, slot.Give(TestModels.TestItem1.StackMax));
70 | }
71 |
72 | [Fact]
73 | internal void SlotRemoveTest()
74 | {
75 | var slot = new InventorySlot(TestModels.TestItem1.ID, Some);
76 | var remainder1 = 0;
77 | var remainder2 = TestModels.TestItem1.StackMax - (Some - One);
78 |
79 | Assert.Equal(remainder1, slot.Take(One));
80 | Assert.Equal(remainder2, slot.Take(TestModels.TestItem1.StackMax));
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/ParquetUnitTests/Items/ItemModelUnitTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Parquet;
4 | using Parquet.Items;
5 | using Xunit;
6 |
7 | namespace ParquetUnitTests.Items
8 | {
9 | ///
10 | /// Unit tests .
11 | ///
12 | public class ItemModelUnitTest
13 | {
14 | #region Test Values
15 | /// Identifier used when creating a new block.
16 | private static readonly ModelID newItemID = TestModels.TestItem1.ID - 1;
17 |
18 | /// A valid number of s to stack.
19 | private const int goodStackMax = 99;
20 |
21 | ///
22 | private static readonly IList TestTagList = new List() { "Test-Tag" };
23 | #endregion
24 |
25 | [Fact]
26 | internal void ValidItemIDsArePermittedTest()
27 | {
28 | var newItem = new ItemModel(newItemID, "will be created", "", "", TestTagList, ItemType.Consumable,
29 | 1, 1, goodStackMax, 0, 0, TestModels.TestBlock.ID);
30 |
31 | Assert.NotNull(newItem);
32 | }
33 |
34 | [Fact]
35 | internal void InvalidItemIDsRaiseExceptionTest()
36 | {
37 | var badItemID = TestModels.TestBlock.ID - 1;
38 |
39 | void TestCode()
40 | {
41 | var _ = new ItemModel(badItemID, "will fail", "", "", TestTagList, ItemType.Consumable,
42 | 1, 1, goodStackMax, 0, 0, TestModels.TestBlock.ID);
43 | }
44 |
45 | Assert.Throws(TestCode);
46 | }
47 |
48 | [Fact]
49 | internal void StackMaxMustBePositiveTest()
50 | {
51 | var badStackMaxZero = 0;
52 | var badStackMaxNegativeOne = -1;
53 |
54 | void TestCodeZero()
55 | {
56 | var _ = new ItemModel(newItemID, "will fail", "", "", TestTagList, ItemType.Consumable,
57 | 1, 1, badStackMaxZero, 0, 0, TestModels.TestBlock.ID);
58 | }
59 |
60 | void TestCodeNegativeOne()
61 | {
62 | var _ = new ItemModel(newItemID, "will fail", "", "", TestTagList, ItemType.Consumable,
63 | 1, 1, badStackMaxNegativeOne, 0, 0, TestModels.TestBlock.ID);
64 | }
65 |
66 | Assert.Throws(TestCodeZero);
67 | Assert.Throws(TestCodeNegativeOne);
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Beings/CritterModel.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Parquet.Beings
4 | {
5 | ///
6 | /// Models the definition for a simple in-game actor, such as a friendly mob with limited interaction.
7 | ///
8 | public class CritterModel : BeingModel, IMutableCritterModel
9 | {
10 | #region Class Defaults
11 | /// Indicates an uninitialized critter.
12 | public static CritterModel Unused { get; } = new CritterModel(ModelID.None, nameof(Unused), "", "");
13 | #endregion
14 |
15 | #region Initialization
16 | ///
17 | /// Initializes a new instance of the class.
18 | ///
19 | ///
20 | /// Unique identifier for the . Cannot be null.
21 | /// Must be a .
22 | ///
23 | /// Player-friendly name of the . Cannot be null or empty.
24 | /// Player-friendly description of the .
25 | /// Comment of, on, or by the .
26 | /// Any additional information about the .
27 | /// The in which this is most comfortable.
28 | /// The rules that govern how this acts. Cannot be null.
29 | /// Any parquets this avoids.
30 | /// Any parquets this seeks.
31 | public CritterModel(ModelID id, string name, string description, string comment,
32 | IEnumerable tags = null, ModelID? nativeBiomeID = null,
33 | ModelID? primaryBehaviorID = null, IEnumerable avoidsIDs = null,
34 | IEnumerable seeksIDs = null)
35 | : base(All.CritterIDs, id, name, description, comment, tags,
36 | nativeBiomeID, primaryBehaviorID, avoidsIDs, seeksIDs)
37 | => Precondition.IsInRange(id, All.CritterIDs, nameof(id));
38 | #endregion
39 |
40 | #region IMutableCritterModel Implementation
41 | // Currently, everything needed for editing CritterModels is provided by IBeingModelEdit.
42 | #endregion
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Beings/IMutableCharacterModel.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Parquet.Items;
3 |
4 | namespace Parquet.Beings
5 | {
6 | ///
7 | /// Facilitates editing of a from design tools while maintaining a read-only face for use during play.
8 | ///
9 | ///
10 | /// By design, subtypes of should never themselves use .
11 | /// IMutableCharacterModel is for use only by external types that require read/write access to model properties.
12 | ///
13 | public interface IMutableCharacterModel : IMutableBeingModel
14 | {
15 | /// Player-facing personal name.
16 | public string PersonalName { get; set; }
17 |
18 | /// Player-facing family name.
19 | public string FamilyName { get; set; }
20 |
21 | ///
22 | /// A key for the the uses,
23 | /// stored as "/.
24 | ///
25 | public string PronounKey { get; set; }
26 |
27 | /// The story character that this represents.
28 | ///
29 | /// This identifier provides a link between software character classes
30 | /// and the characters written of in a game's narrative that they represent. The goal
31 | /// is that these identifiers be able to span any number of shipped titles, allowing a
32 | /// sequel title to import data from prior titles in such a way that one game's NPC
33 | /// can become another game's protagonist.
34 | ///
35 | public string StoryCharacterID { get; set; }
36 |
37 | /// The the begins at.
38 | public Location StartingLocation { get; set; }
39 |
40 | /// The s that this either offers or has undertaken.
41 | /// Typically, NPCs offer quests, player characters undertake them.
42 | public ICollection StartingQuestIDs { get; }
43 |
44 | /// Dialogue lines this can say.
45 | public ModelID StartingDialogueID { get; set; }
46 |
47 | /// The set of belongings that this begins with.
48 | public InventoryCollection StartingInventory { get; }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/ParquetUnitTests/Parquets/BlockModelUnitTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Parquet;
3 | using Parquet.Parquets;
4 | using Xunit;
5 |
6 | namespace ParquetUnitTests.Parquets
7 | {
8 | ///
9 | /// Unit tests .
10 | ///
11 | public class BlockModelUnitTest
12 | {
13 | #region Test Values
14 | /// Identifier used when creating a new block.
15 | private static readonly ModelID newBlockID = TestModels.TestBlock.ID - 1;
16 | #endregion
17 |
18 | [Fact]
19 | internal void ValidBlockIDsArePermittedTest()
20 | {
21 | var testBlock = new BlockModel(newBlockID, "will be created", "", "");
22 |
23 | Assert.NotNull(testBlock);
24 | }
25 |
26 | [Fact]
27 | internal void InvalidBlockIDsRaiseExceptionTest()
28 | {
29 | var badBlockID = TestModels.TestFloor.ID;
30 |
31 | void TestCode()
32 | {
33 | var _ = new BlockModel(badBlockID, "will fail", "", "");
34 | }
35 |
36 | Assert.Throws(TestCode);
37 | }
38 |
39 | [Fact]
40 | internal void ValidItemIDsArePermittedTest()
41 | {
42 | ModelID goodItemID = -All.ItemIDs.Minimum;
43 |
44 | var testBlock = new BlockModel(newBlockID, "will be created", "", "", null, goodItemID);
45 |
46 | Assert.NotNull(testBlock);
47 | }
48 |
49 | [Fact]
50 | internal void InvalidItemIDsRaiseExceptionTest()
51 | {
52 | var badItemID = TestModels.TestBlock.ID;
53 |
54 | void TestCode()
55 | {
56 | var _ = new BlockModel(newBlockID, "will fail", "", "", null, badItemID);
57 | }
58 |
59 | Assert.Throws(TestCode);
60 | }
61 |
62 | [Fact]
63 | internal void ValidCollectibleIDsArePermittedTest()
64 | {
65 | var goodCollectibleID = TestModels.TestCollectible.ID;
66 |
67 | var testBlock = new BlockModel(newBlockID, "will be created", "", "", collectibleID: goodCollectibleID);
68 |
69 | Assert.NotNull(testBlock);
70 | }
71 |
72 | [Fact]
73 | internal void InvalidCollectibleIDsRaiseExceptionTest()
74 | {
75 | var badCollectibleID = TestModels.TestBlock.ID;
76 |
77 | void TestCode()
78 | {
79 | var _ = new BlockModel(newBlockID, "will fail", "", "", collectibleID: badCollectibleID);
80 | }
81 |
82 | Assert.Throws(TestCode);
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/ParquetUnitTests/Parquets/FurnishingModelUnitTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Parquet;
3 | using Parquet.Parquets;
4 | using Xunit;
5 |
6 | namespace ParquetUnitTests.Parquets
7 | {
8 | ///
9 | /// Unit tests .
10 | ///
11 | public class FurnishingModelUnitTest
12 | {
13 | #region Test Values
14 | /// Identifier used when creating a new furnishing.
15 | private static readonly ModelID newFurnishingID = TestModels.TestFurnishing.ID - 1;
16 | #endregion
17 |
18 | [Fact]
19 | internal void ValidCollectibleIDsArePermittedTest()
20 | {
21 | var testFurnishing = new FurnishingModel(newFurnishingID, "will be created", "", "");
22 |
23 | Assert.NotNull(testFurnishing);
24 | }
25 |
26 | [Fact]
27 | internal void InvalidCollectibleIDsRaiseExceptionTest()
28 | {
29 | var badFurnishingID = TestModels.TestBlock.ID;
30 |
31 | void TestCode()
32 | {
33 | var _ = new FurnishingModel(badFurnishingID, "will fail", "", "");
34 | }
35 |
36 | Assert.Throws(TestCode);
37 | }
38 |
39 | [Fact]
40 | internal void ValidItemIDsArePermittedTest()
41 | {
42 | ModelID goodItemID = -All.ItemIDs.Minimum;
43 |
44 | var testBlock = new FurnishingModel(newFurnishingID, "will be created", "", "", null, goodItemID);
45 |
46 | Assert.NotNull(testBlock);
47 | }
48 |
49 | [Fact]
50 | internal void InvalidItemIDsRaiseExceptionTest()
51 | {
52 | var badItemID = TestModels.TestBlock.ID;
53 |
54 | void TestCode()
55 | {
56 | var _ = new FurnishingModel(newFurnishingID, "will fail", "", "", null, badItemID);
57 | }
58 |
59 | Assert.Throws(TestCode);
60 | }
61 |
62 | [Fact]
63 | internal void ValidSwapIDsArePermittedTest()
64 | {
65 | var goodItemID = newFurnishingID - 1;
66 |
67 | var testBlock = new FurnishingModel(newFurnishingID, "will be created", "", "", itemID: goodItemID);
68 |
69 | Assert.NotNull(testBlock);
70 | }
71 |
72 | [Fact]
73 | internal void InvalidSwapIDsRaiseExceptionTest()
74 | {
75 | var badItemID = TestModels.TestBlock.ID;
76 |
77 | void TestCode()
78 | {
79 | var _ = new FurnishingModel(newFurnishingID, "will fail", "", "", itemID: badItemID);
80 | }
81 |
82 | Assert.Throws(TestCode);
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Scripts/Commands.cs:
--------------------------------------------------------------------------------
1 | namespace Parquet.Scripts
2 | {
3 | ///
4 | /// IDs for commands used in s.
5 | ///
6 | /// Note that these commands are encoded in upper case, but the library ignores case when reading or comparing them.
7 | public static class Commands
8 | {
9 | /// Indicates non-command. NOP.
10 | public const string None = "";
11 |
12 | /// Display the given text as an alert by the user interface.
13 | public const string Alert = "A";
14 |
15 | /// Calls the given to stand near another given .
16 | public const string CallCharacter = "C";
17 |
18 | /// Lower the given flag.
19 | public const string ClearFlag = "!";
20 |
21 | /// Allot the given number and type of to the given .
22 | public const string GiveItem = "I";
23 |
24 | /// Allot the given to the given .
25 | public const string GiveQuest = "Q";
26 |
27 | /// Immediately load and begin processing the given .
28 | public const string Jump = "J";
29 |
30 | /// If the given variable is set, load and begin processing the given .
31 | public const string JumpIf = "F";
32 |
33 | /// Place the given at the given .
34 | public const string Put = "P";
35 |
36 | /// Display the given text as dialogue spoken by the given .
37 | public const string Say = "S";
38 |
39 | /// Allot the given to the given .
40 | public const string SetBehavior = "B";
41 |
42 | /// Allot the given to the given .
43 | public const string SetDialogue = "D";
44 |
45 | /// Allot the given to the given .
46 | public const string SetPronoun = "R";
47 |
48 | /// Raise the given flag.
49 | public const string SetFlag = "=";
50 |
51 | /// Highlight the given via the UI, perhaps by camera movement or particle effect.
52 | public const string ShowLocation = "L";
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 | This file reflects changes at each project milestone.
3 |
4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5 | and this project adheres to [Semantic Versioning 2.0.0](https://semver.org/).
6 |
7 |
8 |
22 |
23 | ## [0.4.0] - 2021-02-23
24 | ### Pre-Alpha 3 API Revision
25 | - This update changes the API in far-reaching ways in order to separate design-time and play-time concerns in map-related types.
26 | #### Adds
27 | - Adds Status type as a base class for all mutable-at-play types, analogous to Models.
28 | - Derives a Status-based partner class for most Model classes.
29 | - Adds Pack container type as base for ParquetModelPack and ParquetStatusPack.
30 | - Adds LibraryState static class to track build and run characteristics, particularly Debug-vs-Release and Play-vs-Edit.
31 | #### Removes
32 | - MapModel abstract class, as there is only one map-related Model in the new hierarchy.
33 | #### Changes
34 | - Merges the definitional elements (procedural generation instructions and metadata) of MapRegion and MapSketch into single RegionModel.
35 | - Merges the mutable elements (parquets and their statuses) of MapRegion and MapSketch into a single RegionStatus class.
36 | - Changes MapChunk to a stand-alone class rather than a Model subtype.
37 | - Renames namespace from Map to Region.
38 | - Alters the BeingModel class family to better interoperate with the new RegionModel class family.
39 | - Changes the name of Inventory class to InventoryCollection to avoid confusion in the BeingStatus classes.
40 | - Updates the serialized example data for RegionModel and BeingModel class families.
41 | - Reimplements cloning to enforce deep copying.
42 | - Many minor fixes and corrections along the way.
43 |
44 | ## [0.3.56] - 2021-02-12
45 | #### Adds
46 | - General purpose Tags collection to the base Model type.
47 | #### Removes
48 | - ItemModel.ItemTags collection, as Model.Tags makes it redundant.
49 |
50 | ## [0.3.52] - 2021-01-29
51 | #### Removes
52 | - Roller command line utility. It is now built as part of Scribe instead.
53 |
54 | ## [0.3.0] - 2020-08-11
55 | ### Pre-Alpha 2 Milestone
56 | #### Added
57 | - Major mechanical systems.
58 | #### Changes
59 | - Many small changes to support Scribe GUI editor.
60 |
61 | ## [0.2.0] - 2020-04-18
62 | ### Pre-Alpha 1 Milestone
63 | #### Added
64 | - Models for all major game systems.
65 | - Roller command line utility.
66 |
67 | ## [0.1.0] - 2019-01-28
68 | #### Added
69 | - Initial commit.
70 |
71 | ## [0.0.0] - 2018-12-05
72 | #### Project begun.
73 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/EmptyTolerantEnumConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using CsvHelper;
4 | using CsvHelper.Configuration;
5 | using CsvHelper.TypeConversion;
6 | using Parquet.Properties;
7 |
8 | namespace Parquet
9 | {
10 | ///
11 | /// Converts an to and from a
12 | /// providing sensible default values in case of null or the .
13 | ///
14 | public class EmptyTolerantEnumConverter : EnumConverter
15 | {
16 | /// Stores the type information for the kind of enumeration being converted.
17 | private readonly Type EnumType;
18 |
19 | ///
20 | /// Creates a new for the given .
21 | ///
22 | /// The type of the enumeration.
23 | public EmptyTolerantEnumConverter(Type type)
24 | : base(type)
25 | => EnumType = (type?.IsEnum ?? false)
26 | ? type
27 | : DefaultWithCastLog(nameof(type), type?.Name ?? "null", nameof(Enum), default(Type));
28 |
29 | ///
30 | /// Convenience method that logs a casting error and returns the given default value.
31 | /// This is a fatal error as the library will be unable to handle a default type in place of an enumeration.
32 | ///
33 | /// The type of value to return.
34 | /// The value that could not be case.
35 | /// The name of type received.
36 | /// The name of type expected.
37 | /// The default value to return.
38 | /// The default value given.
39 | private static T DefaultWithCastLog(string value, string actualTypeName, string expectedTypeName, T defaultValue)
40 | {
41 | Logger.Log(LogLevel.Fatal, string.Format(CultureInfo.CurrentCulture, Resources.ErrorInvalidCast,
42 | value, actualTypeName, expectedTypeName), null);
43 |
44 | return defaultValue;
45 | }
46 |
47 | ///
48 | /// Converts the string to an object.
49 | ///
50 | /// The string to convert.
51 | /// The for the current record.
52 | /// The for the member being created.
53 | /// The object created from the string.
54 | public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
55 | => string.IsNullOrEmpty(text)
56 | ? Activator.CreateInstance(EnumType)
57 | : base.ConvertFromString(text, row, memberMapData);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/ParquetRunner/ParquetRunner.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ParquetRunner
6 |
7 | Runner
8 | ParquetRunner
9 | 0.4.12.0
10 | 0.4.0.0
11 | 0.4.12.0
12 | Parquet Pre-Alpha 3
13 |
14 | Paige Ashlynn
15 | Girl Potion
16 | A simple smoke test routine.
17 | 2018-2022 Paige Ashlynn
18 | ParquetIcon.ico
19 | Parquet Logo Large.png
20 | LICENSE.txt
21 | http://parquet.gay/
22 | https://github.com/mxashlynn/Parquet
23 | git
24 |
25 | This code is not meant for external use.
26 | en-US
27 |
28 | Exe
29 | net6.0
30 | latest
31 | CS1701;CS1702
32 | False
33 | NU1605;CS1591
34 | True
35 | AllEnabledByDefault
36 | latest
37 | 5
38 | warnings
39 | True
40 | ParquetRunner
41 | ParquetRunner.RunnerProgram
42 |
43 | False
44 | False
45 | False
46 | Off
47 | False
48 | True
49 | Debug;Release
50 | bin\
51 | disable
52 | send
53 |
54 |
55 |
56 |
57 | DEBUG
58 |
59 |
60 |
61 | TRACE
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | True
71 | \
72 |
73 |
74 | True
75 | \
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Rooms/RoomConfiguration.cs:
--------------------------------------------------------------------------------
1 | using System.Globalization;
2 | using System.IO;
3 | using System.Text;
4 |
5 | namespace Parquet.Rooms
6 | {
7 | ///
8 | /// Provides parameters for s.
9 | ///
10 | public static class RoomConfiguration
11 | {
12 | #region Class Defaults
13 | /// Minimum walkable spaces to use if the configuration cannot be read.
14 | private const int DefaultMinWalkableSpaces = 4;
15 |
16 | /// Maximum walkable spaces to use if the configuration cannot be read.
17 | private const int DefaultMaxWalkableSpaces = 121;
18 | #endregion
19 |
20 | #region Characteristics
21 | /// Minimum number of open walkable spaces needed for any room to register.
22 | public static int MinWalkableSpaces { get; set; } = DefaultMinWalkableSpaces;
23 |
24 | /// Maximum number of open walkable spaces needed for any room to register.
25 | public static int MaxWalkableSpaces { get; set; } = DefaultMaxWalkableSpaces;
26 |
27 | /// Minimum number of enclosing spaces needed for any room to register.
28 | public static int MperimeterSpaces => MinWalkableSpaces * 3;
29 | #endregion
30 |
31 | #region Self Serialization
32 | ///
33 | /// Reads data from the appropriate file.
34 | ///
35 | /// The instances read.
36 | public static void GetRecord()
37 | {
38 | using var reader = new StreamReader(FilePath);
39 |
40 | // Skip the header.
41 | reader.ReadLine();
42 | // Read in the values.
43 | var valueLine = reader.ReadLine();
44 | var values = valueLine.Split(Delimiters.PrimaryDelimiter);
45 |
46 | // Parse.
47 | MinWalkableSpaces = int.TryParse(values[0], All.SerializedNumberStyle, CultureInfo.InvariantCulture, out var temp1)
48 | ? temp1
49 | : Logger.DefaultWithParseLog(values[0], nameof(MinWalkableSpaces), DefaultMinWalkableSpaces);
50 | MaxWalkableSpaces = int.TryParse(values[1], All.SerializedNumberStyle, CultureInfo.InvariantCulture, out var temp2)
51 | ? temp2
52 | : Logger.DefaultWithParseLog(values[1], nameof(MaxWalkableSpaces), DefaultMinWalkableSpaces);
53 | }
54 |
55 | ///
56 | /// Writes data to the appropriate file.
57 | ///
58 | public static void PutRecord()
59 | {
60 | using var writer = new StreamWriter(FilePath, false, new UTF8Encoding(true, true));
61 | writer.Write($"{nameof(MinWalkableSpaces)}{Delimiters.PrimaryDelimiter}{nameof(MaxWalkableSpaces)}\n");
62 | writer.Write($"{MinWalkableSpaces}{Delimiters.PrimaryDelimiter}{MaxWalkableSpaces}\n");
63 | }
64 |
65 | ///
66 | /// Returns the filename and path associated with 's definition file.
67 | ///
68 | /// A full path to the associated file.
69 | public static string FilePath
70 | => $"{All.ProjectDirectory}/{nameof(RoomConfiguration)}.csv";
71 | #endregion
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Scripts/ScriptModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.Linq;
5 | using CsvHelper.Configuration.Attributes;
6 |
7 | namespace Parquet.Scripts
8 | {
9 | ///
10 | /// Models a series of imperative, procedural commands.
11 | /// This might be an AI behavior, for example.
12 | ///
13 | public class ScriptModel : Model, IMutableScriptModel
14 | {
15 | #region Characteristics
16 | /// A series of imperative, procedural commands.
17 | // TODO [SCRIPTING] This likely needs to be a sorted list. Is there an IReadOnlySortedList?
18 | // ANSWER: The closest thing I can find is SortedList which only implements ISortedCollection
19 | // and ISortedDictionary. See for more: https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.sortedlist-2?view=net-5.0
20 | [Index(5)]
21 | public IReadOnlyList Nodes { get; }
22 | #endregion
23 |
24 | #region Initialization
25 | ///
26 | /// Initializes a new instance of the class.
27 | ///
28 | /// Unique identifier for the . Cannot be null.
29 | /// Player-friendly name of the . Cannot be null or empty.
30 | /// Player-friendly description of the .
31 | /// Comment of, on, or by the .
32 | /// Any additional information about this .
33 | /// Describes the criteria for completing this .
34 | public ScriptModel(ModelID id, string name, string description, string comment,
35 | IEnumerable tags = null, IEnumerable nodes = null)
36 | : base(All.ScriptIDs, id, name, description, comment, tags)
37 | => Nodes = (nodes ?? Enumerable.Empty()).ToList();
38 | #endregion
39 |
40 | #region IMutableScriptModel Implementation
41 | /// A series of imperative, procedural commands.
42 | ///
43 | /// By design, subtypes of should never themselves use .
44 | /// IMutableScriptModel is for external types that require read/write access.
45 | ///
46 | [Ignore]
47 | ICollection IMutableScriptModel.Nodes
48 | => LibraryState.IsPlayMode
49 | ? Logger.DefaultWithImmutableInPlayLog(nameof(Nodes), new Collection())
50 | : (ICollection)Nodes;
51 | #endregion
52 |
53 | #region Utilities
54 | ///
55 | /// Yields an for each , in order.
56 | ///
57 | /// The action to perform for the current node.
58 | public IEnumerable GetActions()
59 | {
60 | for (var i = 0; i < Nodes.Count; i++)
61 | {
62 | yield return Nodes[i].GetAction();
63 | }
64 | }
65 | #endregion
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Documentation/Lessons_Learned_Living_Document.md:
--------------------------------------------------------------------------------
1 | # These are some lessons learned during the course of the project's development:
2 |
3 | * As of .Net Core 2.2, the System.Console class does not offer a robust means of interacting with terminals on MacOS or Linux. For this reason, the decision was made to forgo a textmode library runner for the time being and focus on building graphical tools.
4 |
5 | * Because of the timing problems inherent in calling methods defined in child classes from parent classes in C#, there is no clean or simple way to ensure in an abstract class that all children classes implement a particular named, field-like, unchanging, readonly value that is defined in the children classes AND can be relied on in the parent’s constructor. For this reason, the parquet classes that inherit from ParquetParent pass constant values needed for runtime ID validation to their base class as part of their Constructor.
6 |
7 | * Because there may be at least as many items as parquets the ItemID range has been defined using reflection to determine the amount of space in the parquet ranges.
8 |
9 | * Visual Studio 2019 seems to occasionally not run unit tests as expected, instead reporting old results stored in the cache. Clearing the cache folder does the trick. This post explains how clearing the test cache can be made a part of the project Clean build step: https://blogs.msdn.microsoft.com/ploeh/2006/07/13/cleaning-away-the-testresults-folder/
10 |
11 | * I considered merging MapSpace with BeingLocation because they are nearly the same thing, but in this case the domains where they are used are so distinct it seemed wiser to reflect in their comments where and how they are used. This decision can easily be changed down the road if the domains do end up overlapping.
12 |
13 | * .Net Core doesn't support loading unmanaged libraries as well as Mono or .Net Framework did. Specifically, as of Jan 2020 .Net Core does not automatically map libraries with one name on one platform to another name on another platform. Worse yet, the library import code does not allow the client developer to provide the mapping either. There are various workarounds. But it may be easiest simply to retarget from Core to Framework where possible when it comes time to release. This situation may be a nonissue by the time Parquet is ready.
14 |
15 | * Since C# does not support static methods in interfaces and also cannot invoke static methods using a generic type argument, one way to support access to class-scoped members within a generic method is to provide each implementing class of a given interface with a static method, and then compose a unit test to ensure via reflection that all classes that implement the interface do indeed implement a method with that signature. A single instance of the class can be cached in a dictionary, matching `typeof(ImplementingClass)` with `ImplementingClass.StaticMethod`, thus creating a simple runtime static fill C#'s gap.
16 |
17 | * It is sometimes practical to provide a public parameterless constructor to a class that otherwise should not have one, so that the class may be dealt with more easily from within generic methods. While messy from an API perspective, the limitations of C#'s generics prevent cleaner approaches.
18 |
19 | * In WindowsForms, SelectedValue only works if ValueMember has been set. When not using MS Databinding, use SelectedItem instead.
20 |
21 | * As of Sept. 2020, setting ComboBox.SelectedItem to NULL, or ListBox.SelectedItem to NULL, causes the control to deselect, the same as setting SelectedIndex to -1. This appears to be undocumented behavior, at least from what I can find on MSDN, but I am relying on it for now.
22 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Crafts/CraftConfiguration.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text;
3 |
4 | namespace Parquet.Crafts
5 | {
6 | ///
7 | /// Provides parameters for s.
8 | ///
9 | public static class CraftConfiguration
10 | {
11 | #region Class Defaults
12 | /// A fall-back number of ingredient categories per recipe.
13 | private static Range DefaultIngredientCount { get; } = new Range(1, 5);
14 |
15 | /// A fall-back number of product categories per recipe.
16 | private static Range DefaultProductCount { get; } = new Range(1, 5);
17 | #endregion
18 |
19 | #region Characteristics
20 | /// Number of ingredient categories per recipe.
21 | public static Range IngredientCount { get; set; } = DefaultIngredientCount;
22 |
23 | /// Number of product categories per recipe.
24 | public static Range ProductCount { get; set; } = DefaultProductCount;
25 | #endregion
26 |
27 | #region Self Serialization
28 | ///
29 | /// Reads data from the appropriate file.
30 | ///
31 | /// The instances read.
32 | public static void GetRecord()
33 | {
34 | using var reader = new StreamReader(FilePath);
35 |
36 | // Skip the header.
37 | reader.ReadLine();
38 | // Read in the values.
39 | var valueLine = reader.ReadLine();
40 | var serializedRanges = valueLine.Split(Delimiters.PrimaryDelimiter);
41 | var ingredientValues = serializedRanges[0].Split(Delimiters.ElementDelimiter);
42 | var productValues = serializedRanges[1].Split(Delimiters.ElementDelimiter);
43 |
44 | // Parse.
45 | IngredientCount = int.TryParse(ingredientValues[0], out var tempMin)
46 | && int.TryParse(ingredientValues[1], out var tempMax)
47 | ? new Range(tempMin, tempMax)
48 | : Logger.DefaultWithParseLog(serializedRanges[0], nameof(IngredientCount), DefaultIngredientCount);
49 | ProductCount = int.TryParse(productValues[0], out tempMin)
50 | && int.TryParse(productValues[1], out tempMax)
51 | ? new Range(tempMin, tempMax)
52 | : Logger.DefaultWithParseLog(serializedRanges[1], nameof(ProductCount), DefaultProductCount);
53 | }
54 |
55 | ///
56 | /// Writes data to the appropriate file.
57 | ///
58 | public static void PutRecord()
59 | {
60 | using var writer = new StreamWriter(FilePath, false, new UTF8Encoding(true, true));
61 | writer.Write($"{nameof(IngredientCount)}{Delimiters.PrimaryDelimiter}{nameof(ProductCount)}\n");
62 | writer.Write($"{IngredientCount.Minimum}{Delimiters.ElementDelimiter}" +
63 | $"{IngredientCount.Maximum}{Delimiters.PrimaryDelimiter}" +
64 | $"{ProductCount.Minimum}{Delimiters.ElementDelimiter}" +
65 | $"{ProductCount.Maximum}\n");
66 | }
67 |
68 | ///
69 | /// Returns the filename and path associated with 's definition file.
70 | ///
71 | /// A full path to the associated file.
72 | public static string FilePath
73 | => $"{All.ProjectDirectory}/{nameof(CraftConfiguration)}.csv";
74 | #endregion
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/ParquetUnitTests/RangeUnitTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Parquet;
4 | using Xunit;
5 |
6 | namespace ParquetUnitTests
7 | {
8 | ///
9 | /// Unit tests .
10 | ///
11 | public class RangeUnitTest
12 | {
13 | #region Values for Tests
14 | private const int lowerBound = 0;
15 | private const int upperBound = 10;
16 | #endregion
17 |
18 | [Fact]
19 | internal void RangeMustBeWillDefinedTest()
20 | {
21 | Assert.Throws(() => { var _ = new Range(upperBound, lowerBound); });
22 | }
23 |
24 | [Fact]
25 | internal void WellDefinedRangeIsValidTest()
26 | {
27 | var range = new Range(lowerBound, upperBound);
28 |
29 | Assert.True(range.IsValid());
30 | }
31 |
32 | [Fact]
33 | internal void WellDefinedRangeContainsItselfTest()
34 | {
35 | var range = new Range(lowerBound, upperBound);
36 |
37 | Assert.True(range.ContainsRange(range));
38 | }
39 |
40 | [Fact]
41 | internal void RangeContainsStrictlySmallerRangeTest()
42 | {
43 | var range = new Range(lowerBound, upperBound);
44 | var smallerRange = new Range(lowerBound + 1, upperBound - 1);
45 |
46 | Assert.True(range.ContainsRange(smallerRange));
47 | }
48 |
49 | [Fact]
50 | internal void AverageIsWithinRangeTest()
51 | {
52 | var range = new Range(lowerBound, upperBound);
53 | var average = (lowerBound + upperBound) / 2;
54 |
55 | Assert.True(range.ContainsValue(average));
56 | }
57 |
58 | [Fact]
59 | internal void MinimumIsWithinRangeTest()
60 | {
61 | var range = new Range(lowerBound, upperBound);
62 |
63 | Assert.True(range.ContainsValue(lowerBound));
64 | }
65 |
66 | [Fact]
67 | internal void MaximumIsWithinRangeTest()
68 | {
69 | var range = new Range(lowerBound, upperBound);
70 |
71 | Assert.True(range.ContainsValue(upperBound));
72 | }
73 |
74 | [Fact]
75 | internal void GreaterThanMaximumIsNotWithinRangeTest()
76 | {
77 | var range = new Range(lowerBound, upperBound);
78 | var greater = upperBound + 1;
79 |
80 | Assert.False(range.ContainsValue(greater));
81 | }
82 |
83 | [Fact]
84 | internal void LessThanMinimumIsNotWithinRangeTest()
85 | {
86 | var range = new Range(lowerBound, upperBound);
87 | var lesser = lowerBound - 1;
88 |
89 | Assert.False(range.ContainsValue(lesser));
90 | }
91 |
92 | [Fact]
93 | internal void EmptyRangeCollectionContainsNothingTest()
94 | {
95 | var range = new Range(lowerBound, upperBound);
96 | var emptyCollection = new List>();
97 |
98 | Assert.False(emptyCollection.ContainsRange(range));
99 | Assert.False(emptyCollection.ContainsValue(lowerBound));
100 | }
101 |
102 | [Fact]
103 | internal void KnownRangeCanBeFoundInRangeCollectionTest()
104 | {
105 | var range = new Range(lowerBound, upperBound);
106 | var collection = new List> { range };
107 |
108 | Assert.True(collection.ContainsRange(range));
109 | }
110 |
111 | [Fact]
112 | internal void KnownValueCanBeFoundInRangeCollectionTest()
113 | {
114 | var range = new Range(lowerBound, upperBound);
115 | var collection = new List> { range };
116 |
117 | Assert.True(collection.ContainsValue(lowerBound));
118 | }
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/ParquetUnitTests/Rooms/RoomRecipeUnitTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Parquet;
4 | using Parquet.Parquets;
5 | using Parquet.Rooms;
6 | using Xunit;
7 |
8 | namespace ParquetUnitTests.Rooms
9 | {
10 | public class RoomRecipeUnitTest
11 | {
12 | #region Test Values
13 | private static readonly ParquetModelPack TestWall = new(TestModels.TestFloor.ID, TestModels.TestBlock.ID, ModelID.None, ModelID.None);
14 |
15 | private static readonly ParquetModelPack TestWalk = new(TestModels.TestFloor.ID, ModelID.None, ModelID.None, ModelID.None);
16 |
17 | private static readonly ParquetModelPack TestEntry = new(TestModels.TestFloor.ID, ModelID.None, TestModels.TestFurnishing.ID, ModelID.None);
18 |
19 | private static readonly IReadOnlySet TestPerimeter = new HashSet
20 | {
21 | new MapSpace(0, 0, TestWall, null),
22 | new MapSpace(1, 0, TestWall, null),
23 | new MapSpace(2, 0, TestWall, null),
24 | new MapSpace(3, 0, TestWall, null),
25 | new MapSpace(0, 1, TestWall, null),
26 | new MapSpace(3, 1, TestWall, null),
27 | new MapSpace(0, 2, TestWall, null),
28 | new MapSpace(3, 2, TestWall, null),
29 | new MapSpace(0, 3, TestWall, null),
30 | new MapSpace(1, 3, TestWall, null),
31 | new MapSpace(2, 3, TestWall, null),
32 | new MapSpace(3, 3, TestWall, null),
33 | };
34 |
35 | private static readonly IReadOnlySet TestWalkableArea = new HashSet
36 | {
37 | new MapSpace(1, 1, TestWalk, null),
38 | new MapSpace(2, 1, TestWalk, null),
39 | new MapSpace(1, 2, TestWalk, null),
40 | new MapSpace(2, 2, TestEntry, null),
41 | };
42 |
43 | private static readonly IReadOnlyList TestRequiredFurnishings = TestModels.TestRecipeElementList;
44 |
45 | private static readonly RoomRecipe MinimalRecipe =
46 | new(-All.RoomRecipeIDs.Minimum, "Minimal Room Recipe", "", "", null, RoomConfiguration.MinWalkableSpaces, TestRequiredFurnishings);
47 |
48 | private static readonly Room MinimalRoom = new(TestWalkableArea, TestPerimeter);
49 | #endregion
50 |
51 | [Fact]
52 | internal void MinimumWalkableSpacesBelowGlobalMinimumThrowsTest()
53 | {
54 | var BadMinimum = RoomConfiguration.MinWalkableSpaces - 1;
55 |
56 | void HasBadRequiredBlocks()
57 | {
58 | var _ = new RoomRecipe(-All.RoomRecipeIDs.Minimum, "will fail", "", "", null, BadMinimum);
59 | }
60 |
61 | Assert.Throws(HasBadRequiredBlocks);
62 | }
63 |
64 | [Fact]
65 | internal void MinimumWalkableSpacesAboveGlobalMaximumThrowsTest()
66 | {
67 | var BadMinimum = RoomConfiguration.MaxWalkableSpaces + 1;
68 |
69 | void HasBadRequiredBlocks()
70 | {
71 | var _ = new RoomRecipe(-All.RoomRecipeIDs.Minimum, "will fail", "", "", null, BadMinimum);
72 | }
73 |
74 | Assert.Throws(HasBadRequiredBlocks);
75 | }
76 |
77 | [Fact]
78 | internal void StricterRoomRequirementsGenerateHigherPriorityTest()
79 | {
80 | var stricterRecipe = TestModels.TestRoomRecipe;
81 |
82 | Assert.True(MinimalRecipe.Priority < stricterRecipe.Priority);
83 | }
84 |
85 | [Fact]
86 | internal void KnownMismatchReturnsFalse()
87 | {
88 | var stricterRecipe = TestModels.TestRoomRecipe;
89 |
90 | Assert.False(stricterRecipe.Matches(MinimalRoom));
91 | }
92 |
93 | [Fact]
94 | internal void KnownMatchReturnsTrue()
95 | {
96 | Assert.True(MinimalRecipe.Matches(MinimalRoom));
97 | }
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/Documentation/1-Terminology.md:
--------------------------------------------------------------------------------
1 | Here are definitions for some of the terms used in this class library.
2 |
3 | ## Parquets
4 |
5 | * A **parquet** in everyday speech is a flooring composed of wooden blocks in a geometrical arrangement. In the United States it is pronounced /pɑːɹˈkeɪ/, i.e. *"par-kay"*.
6 |
7 | * Within the context of this library, a **parquet** is the smallest interactive unit of the game world. These are the units with which the player builds and that are available in the map editor. Oftentimes in the context of other libraries or games the word "tile" is used to mean something similar; the word "parquet" is employed here to avoid ambiguity.
8 |
9 | * **Parquet Packs** hold parquets during game play. Up to four parquets of distinct types may occupy a ptack.
10 |
11 | * **Floor**s are a type of parquet. They are the lowest layer of parquets in a ptack, providing a background and context for other parquets and the various *character*s that inhabit the world. Floors may be either filled in or dug out; if they are dug out they can contain liquid blocks. Natural surfaces are implemented as floors. Examples of floors:
12 |
13 | * Sand
14 | * Mosaic Tile
15 | * Metal Catwalk
16 | * Dropcloth
17 |
18 | * **Block**s are a type of parquet. They exist in a layer above floors and below furnishings. Blocks are the most complex type of parquet, often the layer with which the player and other characters are interacting. They may be **gather**ed directly or broken down into **collectible**s. They may also be crafted and placed in the world by the player. Examples of blocks:
19 |
20 | * Seawater
21 | * Inlaid Brick
22 | * Plasteel Plating
23 | * Plastered Drywall
24 |
25 | Liquid blocks can only exist on top of floors with the *dug out* property set.
26 |
27 | * **Furnishing**s are a type of parquet. They exist in a layer above blocks and below collectibles. Furnishings are complex parquets that can have special behaviors. Often they are crafted objects and may be placed or removed by the player. Examples of furnishings:
28 |
29 | * Anchored Buoy
30 | * Palace Gate
31 | * Photon Forge
32 | * Padded Sawhorse
33 |
34 | * **Collectible**s are a type of parquet. Constituting the topmost layer, collectibles are simple parquets that represent items on the map that characters (such as the player) may pick up. Often, they form as a result of breaking down blocks. Examples of collectibles:
35 |
36 | * Seashell
37 | * Heart (that grants health)
38 | * 1 Federation Credit
39 | * Wrist & Ankle Restraints
40 |
41 | *See [How Parquets Work](https://github.com/mxashlynn/Parquet/wiki/How-Parquets-Work).*
42 |
43 | ## Inventory-Related Terms
44 |
45 | * Characters including the player carries objects from the game world around with them in their **Inventory**.
46 |
47 | * An **Item** is any game object in the inventory. Items never exist on the map, although they may be equivalent to parquets that exist on the map. In the right circumstances, the player may remove parquets from the map. When a player does so, the parquets become items.
48 |
49 | ## Map-Related Terms
50 |
51 | * The **Map** is a 2D space, the entirety of the playable game world. Once play has begun the Map is, at the atomic level, composed of parquets.
52 |
53 | * **Map Region**s are sections of the game world in play. Exactly one of these is in play at a time. Map Regions are composed of *Parquet Packs*. All of a given Map Region's parquets exists on the same elevation level, although regions may be higher or lower than one another. Exits from the current region to other regions may be defined at any specific location.
54 |
55 | * **Map Chunk Grid**s are sections of the game world equivalent to Map Regions, but in an uninitialized state before play begins. They consist of Map Chunks together with locations that have been designated as entrances or exits. Map Regions may be generated from Map Chunk Grids in a procedural process at the time of load.
56 |
57 | * **Map Chunk**s are small subsections of the Map. They come in two varieties. Custom map chunks are "Handmade" segments of parquets which will be loaded exactly as they are laid out in the editor. Procedural map chunks consist of instructions describing the parquets to procedurally generate in this location on the grid.
58 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Parquet
2 |
3 | This class library implements mechanics and models for a 2D sandbox game focused on building, crafting, and narrative, more or less in that priority order.
4 |
5 | A game built with this system offers many of the features of contemporary quest-driven building games but in a simple, top-down world and without combat.
6 |
7 | # Version 0.4 Warning
8 |
9 | Code and documentation are incomplete and under rapid development. Expect frequent breaking changes. Maybe don't use this yet; when the [Alpha milestone](https://github.com/mxashlynn/Parquet/milestone/2) has been hit the project should be much more usable.
10 |
11 | Design and usage will be [fully documented](https://github.com/mxashlynn/Parquet/tree/master/Documentation) once the [Beta milestone](https://github.com/mxashlynn/Parquet/milestone/3) has been reached.
12 |
13 | Development milestone deadlines are tentative right now.
14 | Because this is one of several side projects that I maintain in addition to my fulltime dayjob, it will be a long time before Parquet is ready.
15 |
16 | # Goals
17 |
18 | This project has two goals:
19 |
20 | 1. To provide a foundation for games developed in C# stacks;
21 | 2. To provide a learning exercise for the lead developer.
22 |
23 | Due to goal 2, no attempt has been made to design the most elegant, efficient, general-purpose, or powerful library possible.
24 | What has been attempted is a cleanly-coded, clearly-documented, easily maintainable class library.
25 |
26 | # Features
27 |
28 | Parquet targets the following features:
29 |
30 | 1. A peaceful 2D top-down overworld map that may be explored.
31 | 2. Simple free-form building mechanics allowing player characters to create homes in the world.
32 | 3. Resource collection mechanics allowing players to upgrade their homes and tools.
33 | 4. A simple crafting system allowing players to unlock new tools and building materials.
34 | 5. Interactive noncombatant NPCs.
35 | 6. A quest system encouraging players to build particular building types.
36 | 7. Dialogue and narrative delivery.
37 | 8. Data-driven design with all game models customizable from CSV files.
38 |
39 | # Repository Structure
40 |
41 | The solution contains several related projects.
42 | Each C# namespace gets its own folder.
43 |
44 | - **Documentation**
45 | - How to use the library and its tools.
46 | - **ExampleData**
47 | - Configuration files used in developing and testing the library.
48 | - **ParquetClassLibrary**
49 | - The library itself. The root namespace contains classes for working with [Models](https://github.com/mxashlynn/Parquet/blob/master/Documentation/2-How_Parquet_Handles_Game_Objects.md) and Statuses.
50 | - **Beings**, including player characters and NPCs.
51 | - **Biomes**, sets of characteristics that Regions may share.
52 | - **Crafts**, recipes and mini-games that produce Items.
53 | - **Items**, objects that Beings may carry, including parquets.
54 | - **Parquets**, [the basic units of play](https://github.com/mxashlynn/Parquet/blob/master/Documentation/3-How_Parquets_Work.md).
55 | - **Properties** error strings, icons, and other static content.
56 | - **Regions**, collections of parquets and Beings which together make up the game world.
57 | - **Rooms**, built from parquets and [recognized at runtime](https://github.com/mxashlynn/Parquet/blob/master/Documentation/4.-Room_Detection_and_Type_Assignment.md).
58 | - **Scripts**, used to define Being Interactions and Item effects.
59 | - **ParquetRunner**
60 | - A simple smoke test.
61 | - **ParquetUnitTests**
62 | - Unit tests for ParquetClassLibrary.
63 |
64 | # Requirements
65 |
66 | To work with this repository you will need:
67 |
68 | - [.NET 5](https://dotnet.microsoft.com/download/dotnet/5.0)
69 | - [CSVHelper 19](https://joshclose.github.io/CsvHelper/)
70 | - [XUnit 2.4](https://github.com/xunit/xunit) (to compile the unit tests only)
71 |
72 | # Contributors
73 | - Library design and code by [Paige Ashlynn](https://github.com/mxashlynn/).
74 | - Special thanks to [Mint Gould](https://github.com/WispyMouse), [Caidence Stone](https://github.com/caidencestone), [Ashley Hauck](https://github.com/khyperia), [Emma Maassen](https://github.com/Enichan), and Lillian Harris for help with code reviews, mathematics, algorithms, game design, and technical decisions.
75 |
--------------------------------------------------------------------------------
/ParquetUnitTests/Items/InventoryCollectionUnitTest.cs:
--------------------------------------------------------------------------------
1 | using Parquet.Items;
2 | using Xunit;
3 |
4 | namespace ParquetUnitTests.Items
5 | {
6 | ///
7 | /// Unit tests .
8 | ///
9 | public class InventoryCollectionUnitTest
10 | {
11 | #region Test Values
12 | private const int TestCapacity = 30;
13 | #endregion
14 |
15 | [Fact]
16 | internal void NewInventoryHasGivenCapacityTest()
17 | {
18 | var inventory = new InventoryCollection(TestCapacity);
19 |
20 | Assert.Equal(TestCapacity, inventory.Capacity);
21 | }
22 |
23 | /* TODO [TESTING] Introduce mutable unit tests!
24 | [Fact]
25 | internal void NewInventoryContainsOneSlotsForEachItemTest()
26 | {
27 | var inventory = new Inventory(TestCapacity);
28 | inventory.Give(TestModels.TestItem1.ID);
29 | inventory.Give(TestModels.TestItem2.ID);
30 | inventory.Give(TestModels.TestItem3.ID);
31 | inventory.Give(TestModels.TestItem4.ID);
32 | var numberOfItemTypes = 4;
33 |
34 | Assert.Equal(numberOfItemTypes, inventory.Count);
35 | }
36 |
37 | [Fact]
38 | internal void InventoryContainsGivenNumbersOfItemsTest()
39 | {
40 | var inventory = new Inventory(TestCapacity);
41 | var numberOfItems = 6;
42 | inventory.Give(TestModels.TestItem1.ID, numberOfItems);
43 |
44 | Assert.Equal(numberOfItems, inventory.Contains(TestModels.TestItem1.ID));
45 | }
46 |
47 | [Fact]
48 | internal void InventoryHasGivenItemsTest()
49 | {
50 | var inventory = new Inventory(TestCapacity);
51 | var slots = new List
52 | {
53 | new InventorySlot(TestModels.TestItem2.ID, 4),
54 | new InventorySlot(TestModels.TestItem3.ID, 40),
55 | new InventorySlot(TestModels.TestItem4.ID, 400),
56 | };
57 | foreach (var slot in slots)
58 | {
59 | inventory.Give(slot);
60 | }
61 |
62 | Assert.True(inventory.Has(slots));
63 | }
64 |
65 | [Fact]
66 | internal void InventoryReturnsCorrectRemainderAfterTakingItemsTest()
67 | {
68 | var inventory = new Inventory(TestCapacity);
69 | var slots = new List
70 | {
71 | new InventorySlot(TestModels.TestItem2.ID, 4),
72 | new InventorySlot(TestModels.TestItem3.ID, 40),
73 | new InventorySlot(TestModels.TestItem4.ID, 400),
74 | };
75 | var amountToTake = 10;
76 |
77 | foreach (var slot in slots)
78 | {
79 | inventory.Give(slot);
80 | var expectedRemainder = slot.Count > amountToTake
81 | ? 0
82 | : amountToTake - slot.Count;
83 | Assert.Equal(expectedRemainder, inventory.Take(slot.ItemID, amountToTake));
84 | }
85 | }
86 |
87 | [Fact]
88 | internal void InventoryMergesItemsTest()
89 | {
90 | var inventory = new Inventory(TestCapacity);
91 | var slots = new List
92 | {
93 | new InventorySlot(TestModels.TestItem1.ID, 4),
94 | new InventorySlot(TestModels.TestItem1.ID, 40),
95 | new InventorySlot(TestModels.TestItem1.ID, 400),
96 | };
97 | var total = 444;
98 | foreach (var slot in slots)
99 | {
100 | inventory.Give(slot);
101 | }
102 |
103 | Assert.Equal(total, inventory.Contains(TestModels.TestItem1.ID));
104 | }
105 |
106 | [Fact]
107 | internal void InventoryDoesNotContainItemsNotGivenTest()
108 | {
109 | var inventory = new Inventory(TestCapacity);
110 | inventory.Give(TestModels.TestItem1.ID, 4);
111 | var notInInventoryID = TestModels.TestItem1.ID + 10;
112 |
113 | Assert.Equal(0, inventory.Contains(notInInventoryID));
114 | }
115 | */
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/ParquetUnitTests/ParquetUnitTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ParquetUnitTests
6 |
7 | Parquet
8 | ParquetUnitTests
9 | 0.4.12.0
10 | 0.4.0.0
11 | 0.4.12.0
12 | Parquet Pre-Alpha 3
13 |
14 | Paige Ashlynn
15 | Girl Potion
16 | Unit tests for Parquet.
17 | 2018-2022 Paige Ashlynn
18 | ParquetIcon.ico
19 | Parquet Logo Large.png
20 | LICENSE.txt
21 | http://parquet.gay/
22 | https://github.com/mxashlynn/Parquet
23 | git
24 |
25 | This code is not meant for external use.
26 | en-US
27 |
28 | net6.0
29 | latest
30 | CS1701;CS1702
31 | False
32 | NU1605;CS1591
33 | True
34 | AllEnabledByDefault
35 | latest
36 | 5
37 | warnings
38 | True
39 | ParquetUnitTests
40 |
41 | False
42 | False
43 | False
44 | Off
45 | False
46 | True
47 | Debug;Release
48 | bin\
49 | disable
50 | send
51 |
52 |
53 |
54 |
55 | DEBUG
56 | 5
57 |
58 |
59 |
60 | TRACE
61 | 5
62 |
63 |
64 |
65 | ..\.vs\Parquet\v16\TestStore\0
66 | bin\
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | all
79 | runtime; build; native; contentfiles; analyzers; buildtransitive
80 |
81 |
82 | runtime; build; native; contentfiles; analyzers; buildtransitive
83 | all
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 | True
95 | \
96 |
97 |
98 | True
99 | \
100 |
101 |
102 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/SeriesConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using CsvHelper;
6 | using CsvHelper.Configuration;
7 | using CsvHelper.TypeConversion;
8 |
9 | namespace Parquet
10 | {
11 | ///
12 | /// Type converter for any collection that implements .
13 | ///
14 | /// The type collected.
15 | /// The type of the collection.
16 | public class SeriesConverter : CollectionGenericConverter
17 | where TCollection : ICollection, new()
18 | where TElement : ITypeConverter, new()
19 | {
20 | /// Allows the converter to construct itself statically.
21 | internal static SeriesConverter ConverterFactory { get; } =
22 | new SeriesConverter();
23 |
24 | /// Allows the converter to construct its contents.
25 | internal static readonly TElement ElementFactory = new();
26 |
27 | ///
28 | /// Converts the given 1D collection into a record column.
29 | ///
30 | /// The collection to convert.
31 | /// The current context and configuration.
32 | /// Mapping info for a member to a CSV field or property.
33 | /// The given collection serialized.
34 | public override string ConvertToString(object collection, IWriterRow row, MemberMapData memberMapData)
35 | {
36 | Precondition.IsNotNull(collection, nameof(collection));
37 | if (collection is not TCollection series)
38 | {
39 | return Logger.DefaultWithConvertLog(collection?.ToString() ?? "null", nameof(TCollection), "");
40 | }
41 |
42 | if (series.Count < 1
43 | || (series.Count == 1
44 | && series.Contains(ElementFactory)))
45 | {
46 | return "";
47 | }
48 |
49 | var result = new StringBuilder();
50 | foreach (var element in series)
51 | {
52 | result.Append(element.ConvertToString(element, row, memberMapData));
53 | result.Append(Delimiters.SecondaryDelimiter[0]);
54 | }
55 | result.Remove(result.Length - Delimiters.SecondaryDelimiter.Length, Delimiters.SecondaryDelimiter.Length);
56 |
57 | return result.ToString();
58 | }
59 |
60 | ///
61 | /// Converts the given record column to a 1D collection.
62 | ///
63 | /// The record column to convert to an object.
64 | /// The for the current record.
65 | /// The for the member being created.
66 | /// The created from the record column.
67 | public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
68 | => ConvertFromString(text, row, memberMapData, Delimiters.SecondaryDelimiter);
69 |
70 | ///
71 | /// Converts the given record column to a 1D collection.
72 | ///
73 | /// The record column to convert to an object.
74 | /// The for the current record.
75 | /// The for the member being created.
76 | /// The string used to separate elements in the series.
77 | /// The created from the record column.
78 | public object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData, string delimiter)
79 | {
80 | var collection = new TCollection();
81 | if (string.IsNullOrEmpty(text)
82 | || string.Equals(nameof(ModelID.None), text, StringComparison.OrdinalIgnoreCase)
83 | || string.Equals(nameof(Enumerable.Empty), text, StringComparison.OrdinalIgnoreCase))
84 | {
85 | return collection;
86 | }
87 |
88 | var textCollection = text.Split(delimiter);
89 | foreach (var currentText in textCollection)
90 | {
91 | collection.Add((TElement)ElementFactory.ConvertFromString(currentText, row, memberMapData));
92 | }
93 | return collection;
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Biomes/BiomeConfiguration.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text;
4 | using Parquet.Regions;
5 |
6 | namespace Parquet.Biomes
7 | {
8 | ///
9 | /// Provides rules for determining a 's .
10 | ///
11 | public static class BiomeConfiguration
12 | {
13 | #region Class Defaults
14 | /// Used in computing thresholds.
15 | private const int ParquetsPerLayer = RegionStatus.ParquetsPerRegionDimension * RegionStatus.ParquetsPerRegionDimension;
16 | #endregion
17 |
18 | #region Characteristics
19 | ///
20 | /// There must be at least this percentage of non-liquid s in a given
21 | /// to generate the associated with them.
22 | ///
23 | public static double LandThresholdFactor { get; set; } = 1.25;
24 |
25 | /// How many of a layers' worth of parquets must contribute to a land-based .
26 | public static int LandThreshold
27 | => (int)Math.Round(ParquetsPerLayer * LandThresholdFactor, 0, MidpointRounding.AwayFromZero);
28 |
29 | ///
30 | /// There must be at least this percentage of liquid s in a given
31 | /// to generate the associated with them.
32 | ///
33 | public static double LiquidThresholdFactor { get; set; } = 0.25;
34 |
35 | /// How many of a layers' worth of parquets must contribute to a Liquid-based .
36 | public static int LiquidThreshold
37 | => (int)Math.Round(ParquetsPerLayer * LiquidThresholdFactor, 0, MidpointRounding.AwayFromZero);
38 |
39 | ///
40 | /// There must be at least this percentage of s included in rooms in a given
41 | /// to generate the associated with them.
42 | ///
43 | public static double RoomThresholdFactor { get; set; } = 0.67;
44 |
45 | /// How many of a layers' worth of parquets must contribute to a room-based .
46 | public static int RoomThreshold
47 | => (int)Math.Round(ParquetsPerLayer * RoomThresholdFactor, 0, MidpointRounding.AwayFromZero);
48 | #endregion
49 |
50 | #region Self Serialization
51 | ///
52 | /// Reads data from the appropriate file.
53 | ///
54 | /// The instances read.
55 | public static void GetRecord()
56 | {
57 | using var reader = new StreamReader(FilePath);
58 |
59 | // Skip the header.
60 | reader.ReadLine();
61 | // Read in the values.
62 | var valueLine = reader.ReadLine();
63 | var values = valueLine.Split(Delimiters.PrimaryDelimiter);
64 |
65 | // Parse.
66 | LandThresholdFactor = double.TryParse(values[0], out var temp)
67 | ? temp
68 | : Logger.DefaultWithParseLog(values[0], nameof(LandThresholdFactor), 1.0);
69 | LiquidThresholdFactor = double.TryParse(values[1], out temp)
70 | ? temp
71 | : Logger.DefaultWithParseLog(values[1], nameof(LiquidThresholdFactor), 1.0);
72 | RoomThresholdFactor = double.TryParse(values[2], out temp)
73 | ? temp
74 | : Logger.DefaultWithParseLog(values[2], nameof(RoomThresholdFactor), 1.0);
75 | }
76 |
77 | ///
78 | /// Writes data to the appropriate file.
79 | ///
80 | public static void PutRecord()
81 | {
82 | using var writer = new StreamWriter(FilePath, false, new UTF8Encoding(true, true));
83 | writer.Write($"{nameof(LandThresholdFactor)}{Delimiters.PrimaryDelimiter}{nameof(LiquidThresholdFactor)}{Delimiters.PrimaryDelimiter}{nameof(RoomThresholdFactor)}\n");
84 | writer.Write($"{LandThresholdFactor}{Delimiters.PrimaryDelimiter}{LiquidThresholdFactor}{Delimiters.PrimaryDelimiter}{RoomThresholdFactor}\n");
85 | }
86 |
87 | ///
88 | /// Returns the filename and path associated with 's definition file.
89 | ///
90 | /// A full path to the associated file.
91 | public static string FilePath
92 | => $"{All.ProjectDirectory}/{nameof(BiomeConfiguration)}.csv";
93 | #endregion
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/ParquetClassLibrary.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Parquet
6 |
7 | Parquet
8 | Parquet
9 | 0.4.12.0
10 | 0.4.0.0
11 | 0.4.12.0
12 | Parquet Pre-Alpha 3
13 |
14 | Paige Ashlynn
15 | Girl Potion
16 | Mechanics and models for 2D building, crafting, and narrative sandbox games.
17 | 2018-2022 Paige Ashlynn
18 | ParquetIcon.ico
19 | Parquet Logo Large.png
20 | README.md
21 | LICENSE.txt
22 | http://parquet.gay/
23 | https://github.com/mxashlynn/Parquet
24 | git
25 | C# .net6 net6 game library
26 | Code and documentation are incomplete and under rapid development. Expect frequent breaking changes.
27 | en-US
28 |
29 | Library
30 | net6.0
31 | latest
32 | CS1701;CS1702;CS0809
33 | False
34 | NU1605;CS1591
35 | True
36 | AllEnabledByDefault
37 | latest
38 | 5
39 | warnings
40 | False
41 | Parquet
42 |
43 | True
44 | True
45 | True
46 | Off
47 | True
48 | snupkg
49 | True
50 | Debug;Release
51 | disable
52 | send
53 |
54 |
55 |
56 | DEBUG
57 | True
58 |
59 |
60 |
61 | TRACE
62 | Parquet.Documentation.xml
63 |
64 |
65 |
66 |
67 |
68 |
69 | all
70 | runtime; build; native; contentfiles; analyzers; buildtransitive
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | True
79 | True
80 | Resources.resx
81 |
82 |
83 |
84 |
85 |
86 | ResXFileCodeGenerator
87 | Resources.Designer.cs
88 |
89 |
90 |
91 |
92 |
93 | True
94 | \
95 |
96 |
97 | True
98 | \
99 |
100 |
101 | True
102 | \
103 |
104 |
105 | True
106 | \
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/Parquet.sln:
--------------------------------------------------------------------------------
1 | Microsoft Visual Studio Solution File, Format Version 12.00
2 | # Visual Studio Version 16
3 | VisualStudioVersion = 16.0.29613.14
4 | MinimumVisualStudioVersion = 10.0.40219.1
5 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ParquetClassLibrary", "ParquetClassLibrary\ParquetClassLibrary.csproj", "{5D426EB2-83DD-44E3-8404-E9914CE98FE7}"
6 | EndProject
7 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ParquetUnitTests", "ParquetUnitTests\ParquetUnitTests.csproj", "{6F347E83-0546-4447-A0ED-73C2ED3626F2}"
8 | EndProject
9 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ParquetRunner", "ParquetRunner\ParquetRunner.csproj", "{BD78B49F-9E73-4D20-91C2-99F9E417AC93}"
10 | EndProject
11 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{33AB7F7D-6D20-4F53-AC7F-386C8E1144C3}"
12 | ProjectSection(SolutionItems) = preProject
13 | .editorconfig = .editorconfig
14 | .gitattributes = .gitattributes
15 | .gitignore = .gitignore
16 | CHANGELOG.md = CHANGELOG.md
17 | CONTRIBUTING.md = CONTRIBUTING.md
18 | LICENSE.txt = LICENSE.txt
19 | README.md = README.md
20 | EndProjectSection
21 | EndProject
22 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ExampleData", "ExampleData", "{75B6EFDC-819B-4CA1-A77A-9014E51C53B0}"
23 | ProjectSection(SolutionItems) = preProject
24 | ExampleData\BiomeConfiguration.csv = ExampleData\BiomeConfiguration.csv
25 | ExampleData\BiomeModels.csv = ExampleData\BiomeModels.csv
26 | ExampleData\BlockModels.csv = ExampleData\BlockModels.csv
27 | ExampleData\CharacterModels.csv = ExampleData\CharacterModels.csv
28 | ExampleData\CollectibleModels.csv = ExampleData\CollectibleModels.csv
29 | ExampleData\CraftConfiguration.csv = ExampleData\CraftConfiguration.csv
30 | ExampleData\CraftingRecipes.csv = ExampleData\CraftingRecipes.csv
31 | ExampleData\CritterModels.csv = ExampleData\CritterModels.csv
32 | ExampleData\FloorModels.csv = ExampleData\FloorModels.csv
33 | ExampleData\FurnishingModels.csv = ExampleData\FurnishingModels.csv
34 | ExampleData\InteractionModels.csv = ExampleData\InteractionModels.csv
35 | ExampleData\ItemModels.csv = ExampleData\ItemModels.csv
36 | ExampleData\MapChunks.csv = ExampleData\MapChunks.csv
37 | ExampleData\MapRegions.csv = ExampleData\MapRegions.csv
38 | ExampleData\MapRegionSketches.csv = ExampleData\MapRegionSketches.csv
39 | ExampleData\PronounGroups.csv = ExampleData\PronounGroups.csv
40 | ExampleData\RoomConfiguration.csv = ExampleData\RoomConfiguration.csv
41 | ExampleData\RoomRecipes.csv = ExampleData\RoomRecipes.csv
42 | ExampleData\ScriptModels.csv = ExampleData\ScriptModels.csv
43 | EndProjectSection
44 | EndProject
45 | Global
46 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
47 | Debug|Any CPU = Debug|Any CPU
48 | Release|Any CPU = Release|Any CPU
49 | EndGlobalSection
50 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
51 | {5D426EB2-83DD-44E3-8404-E9914CE98FE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
52 | {5D426EB2-83DD-44E3-8404-E9914CE98FE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
53 | {5D426EB2-83DD-44E3-8404-E9914CE98FE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
54 | {5D426EB2-83DD-44E3-8404-E9914CE98FE7}.Release|Any CPU.Build.0 = Release|Any CPU
55 | {6F347E83-0546-4447-A0ED-73C2ED3626F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
56 | {6F347E83-0546-4447-A0ED-73C2ED3626F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
57 | {6F347E83-0546-4447-A0ED-73C2ED3626F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
58 | {6F347E83-0546-4447-A0ED-73C2ED3626F2}.Release|Any CPU.Build.0 = Release|Any CPU
59 | {BD78B49F-9E73-4D20-91C2-99F9E417AC93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
60 | {BD78B49F-9E73-4D20-91C2-99F9E417AC93}.Debug|Any CPU.Build.0 = Debug|Any CPU
61 | {BD78B49F-9E73-4D20-91C2-99F9E417AC93}.Release|Any CPU.ActiveCfg = Release|Any CPU
62 | {BD78B49F-9E73-4D20-91C2-99F9E417AC93}.Release|Any CPU.Build.0 = Release|Any CPU
63 | EndGlobalSection
64 | GlobalSection(SolutionProperties) = preSolution
65 | HideSolutionNode = FALSE
66 | EndGlobalSection
67 | GlobalSection(NestedProjects) = preSolution
68 | {75B6EFDC-819B-4CA1-A77A-9014E51C53B0} = {33AB7F7D-6D20-4F53-AC7F-386C8E1144C3}
69 | EndGlobalSection
70 | GlobalSection(ExtensibilityGlobals) = postSolution
71 | SolutionGuid = {3A31AC43-AA2B-4E9F-8DE8-CD3BFCED9B1E}
72 | EndGlobalSection
73 | GlobalSection(MonoDevelopProperties) = preSolution
74 | Policies = $0
75 | $0.DotNetNamingPolicy = $1
76 | $1.DirectoryNamespaceAssociation = PrefixedHierarchical
77 | $1.ResourceNamePolicy = MSBuild
78 | $0.TextStylePolicy = $2
79 | $2.inheritsSet = null
80 | $2.scope = application/xml
81 | $0.XmlFormattingPolicy = $3
82 | $3.scope = application/xml
83 | $0.StandardHeader = $4
84 | $0.VersionControlPolicy = $5
85 | $5.CommitMessageStyle = $6
86 | $6.FileSeparator = ", "
87 | $6.LineAlign = 0
88 | $6.InterMessageLines = 0
89 | $6.Wrap = False
90 | EndGlobalSection
91 | EndGlobal
92 |
--------------------------------------------------------------------------------
/Documentation/2-How_Parquet_Handles_Game_Objects.md:
--------------------------------------------------------------------------------
1 | The vast majority of game objects in a given Parquet game consist of details set at design time that do not change at any point during play. Parquet tracks these as "Models".
2 |
3 | Here is how the system works.
4 |
5 | ## Models
6 | A **Model** could be considered the fundamental concept in the Parquet library. It represents the parts of a game object that do not change over time, or from one instance to another.
7 |
8 | ## ModelIDs and ModelCollections
9 |
10 | As part of their definition, every Model has an identifier, providing a means to track and rapidly swap large numbers of models at runtime.
11 |
12 | Many different game objects can then share a single definition when working out their game rules and behaviors. For example, we might define what it means to be a bunny and then introduce many bunnies around the place; they would all share our original definition. In this sense, you could think of Models as equivalent to C# classes.
13 |
14 | As the analogy with C# classes would suggest, Parquet also provides a means to track individual instances of each model -- a way to have a collection of five individual bunnies, rather than simply the idea of a bunny.
15 |
16 | These individuals are similar to C# object instances, and Parquet represents them as **ModelID**s.
17 |
18 | Individual game objects reference their models, and those models live within **ModelCollection**s. A ModelCollection can contain any number of models of the same general sort -- bunnies with other critters, wooden planks with other types of flooring, and so on.
19 |
20 | ## All: Where Models Are Defined
21 |
22 | An ModelID is used to look up the singular Model from the appropriate ModelCollection. Most of the time, this is done via one of the canonical model collections provided by the "**All**" super-collection.
23 |
24 | This allows (hopefully!) fluent constructions in code such as
25 |
26 | ```cs
27 | block1 = All.Parquets.Get(1);
28 | ```
29 |
30 | where `1` is the ModelID.
31 |
32 | Using lines of code like this, the library looks up the game object definitions for any particular game object as needed when game rules are checked or game elements interact.
33 |
34 | All is populated during initialization and remains immutable afterward, serving as the source of truth about the parts of game objects that do not change during play.
35 |
36 | ## Model Status
37 |
38 | Of course, even in a Parquet game many game objects have details that _do_ change over time or from one instance to another.
39 |
40 | For example, one of the bunnies might hop over from the clover down to the brook, and the game would need to track where exactly that specific bunny had moved.
41 |
42 | If individual game objects must have mutable state like this, then a separate partner class captures those details. Such classes always end in the word "Status"; examples include **ParquetStatus** for floors and blocks, or **BeingStatus** for player characters and NPCs.
43 |
44 | ## Model Types and Sub-Types
45 |
46 | Most of the time when we are talking about a group of Models we are talking about a group of very similar Models, such as those bunnies.
47 |
48 | Many of the game objects that Parquet deals with can be grouped together like this into collections with shared characteristics. In such a collection, Parquet guarantees that all the given Models have the same general type of information in their definition.
49 |
50 | The types of Entities that Parquet knows about are:
51 | - Being Models
52 | - Character Models
53 | - Critter Models
54 | - Biome Models
55 | - Interaction Models
56 | - Dialogue Models
57 | - Quest Models
58 | - Item Models
59 | - Parquet Models
60 | - Floor Models
61 | - Block Models
62 | - Furnishing Models
63 | - Collectible Models
64 | - Recipes
65 | - Crafting Recipes
66 | - Room Recipes
67 |
68 | Similar to how a C# compiler provides type-checking for C# objects, Parquet defines valid ID ranges for all Model types and sub-types.
69 |
70 | ## Tags: Grouping Models Across Types
71 |
72 | Sometimes we want to talk about Models that are not in the same formal group but nevertheless share a characteristic. For example, we might be interested to note that one of the bunnies is bright pink, and that there are similar pink flowers down by the brook.
73 |
74 | For scenarios like this, Parquet provides a way to identify characteristics outside the scope of the formal definition. Narrative, aesthetic, or mechanical features can be tagged to indicate what informal attributes they exhibit.
75 |
76 | This allows for the definition of Models that rely on a loose category of other Models. For example, a volcanic BiomeModel might be interested in every ParquetModel that has the "Volcanic" tag, or a candy CraftingRecipe might be interested in any Item Model with the "Sugary" tag.
77 |
78 | To support this kind of flexibility, more than one **ModelTag** can coexist on a specific Model.
79 |
80 | ## More Details
81 |
82 | For more technical details, please read the remarks on the [Model](https://github.com/mxashlynn/Parquet/blob/master/ParquetClassLibrary/Model.cs) class and related classes.
83 |
--------------------------------------------------------------------------------
/Documentation/Parquet_CSharp_Style_Guide.md:
--------------------------------------------------------------------------------
1 | Code in this library works to adhere to the following standards, with the understanding that rare particular circumstances may require a different approach.
2 |
3 | ## Overall
4 |
5 | - Generally, favor verbosity in names.
6 | - Generally, favor more short lines of code over fewer long lines of code.
7 | - An exception is made for fluent concatenation of method calls, as in Linq.
8 | - Generally, favor more comments than fewer.
9 | - Generally, follow Microsoft's [C# Coding Conventions](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/coding-conventions), with the following additions and changes.
10 |
11 | ## Project Layout
12 |
13 | - One folder per namespace.
14 | - Child namespaces should be plural, i.e. `ParquetClassLibrary.Utilities`.
15 | - One namespace per folder.
16 | - One namespace per file, unless you are embedding namespaces to hide internal classes.
17 | - One class per file, except for internal classes or static classes that collect method extensions.
18 | - If you have a class of type `CustomType` and it is frequently collected in arrays, extension methods of the form
19 | ```cs
20 | Method(this CutomType[] inArrayArgument)
21 | ```
22 | should be located in a class named `CustomTypeArrayExtensions` at the bottom of the file in which `CustomType` is defined.
23 |
24 | ## File Format
25 | - Spaces, not tabs.
26 | - 4 spaces for each indentation level.
27 | - If continuation lines are not indented automatically, indent them until they match logically with the line they continue, as follows:
28 | ```cs
29 | var leastYValue = Spaces
30 | .Select(space => space.Position.Y)
31 | .Min();
32 | ```
33 |
34 | ## File Layout
35 | - One blank line between using directives and namespace declarations.
36 | - One blank line between class definitions.
37 | - One blank line between every property, field, and method.
38 | - Generally, one statement or definition per line.
39 | - Long statements should be broken up into multiple lines to aid readability.
40 | - Linq statements should be broken up across multiple lines to aid readability as follows:
41 | ```cs
42 | var greatestXValue = Spaces
43 | .Select(space => space.Position.X)
44 | .Max();
45 | ```
46 | - Whenever possible, the ternary operator `?:` should be broken up across multiple lines in this fashion:
47 | ```cs
48 | return useCapitalLeters
49 | ? "Hello, World!"
50 | : "hello, world!";
51 | ```
52 | - Using directives before the outermost namespace, unless you are embedding the namespace reference to hide it from client code.
53 | - Use the `#region` macro liberally to collect related code elements.
54 | - In particular, if a class is defined as `someClass : ISomeInterface`, then the implementation of that interface should be grouped together in a region as `#region ISomeInterface Implementation`
55 | - Many Parquet classes are separated in this way:
56 | ```cs
57 | public class SomeClass : IInterface1, IInterface2
58 | {
59 | #region Class Defaults
60 | // Any constants, readonly static variables, or default values.
61 | #endregion
62 |
63 | #region Characteristics
64 | // Fields representing state that does NOT change while that game is running.
65 | #endregion
66 |
67 | #region Status
68 | // Fields representing state that DOES change while that game is running.
69 | #endregion
70 |
71 | #region Initialization
72 | // Constructors.
73 | #endregion
74 |
75 | #region IInterface1 Implementation
76 | // Methods implementing IInterface1
77 | #endregion
78 |
79 | #region IInterface2 Implementation
80 | // Methods implementing IInterface2
81 | #endregion
82 |
83 | #region Utilities
84 | // ToString() and other general purpose helper methods.
85 | #endregion
86 | }
87 | ```
88 | ## Comment Conventions
89 | - Comments go on separate lines, not on the same line.
90 | - Comments go above the line or block they are commenting on, not below it.
91 | - Comments should be full sentences whenever possible.
92 | - Fill out the XML comment section for all methods and properties, even private ones. Paige relies heavily on these.
93 | - Think of `#region` titles as a type of comment.
94 | ## Other Conventions
95 | - Write each logical condition in an `if` statement on a separate line as follows:
96 | ```cs
97 | if (null != someVariable
98 | && null != someOtherVariable
99 | {
100 | // Do something.
101 | }
102 | ```
103 | - If a method or property can be simplified into a single statement without compromising its safety or legibility, write it in expression-bodied form. For example:
104 | ```cs
105 | public bool IsEmpty
106 | => EntityID.None == Floor
107 | && EntityID.None == Block
108 | && EntityID.None == Furnishing
109 | && EntityID.None == Collectible;
110 | ```
111 | ## Finally
112 | I am always open to constructive criticism on these guidelines, and for considering special cases.
--------------------------------------------------------------------------------
/ParquetClassLibrary/Parquets/FloorModel.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Diagnostics.CodeAnalysis;
3 | using CsvHelper.Configuration.Attributes;
4 | using Parquet.Biomes;
5 | using Parquet.Items;
6 | using Parquet.Properties;
7 |
8 | namespace Parquet.Parquets
9 | {
10 | ///
11 | /// Configurations for a sandbox parquet walking surface.
12 | ///
13 | [SuppressMessage("Design", "CA1033:Interface methods should be callable by subtypes",
14 | Justification = "By design, subtypes of Model should never themselves use IMutableModel or derived interfaces to access their own members. The IMutableModel family of interfaces is for external types that require read/write access.")]
15 | public class FloorModel : ParquetModel, IMutableFloorModel
16 | {
17 | #region Class Defaults
18 | /// A name to employ for parquets when IsTrench is set, if none is provided.
19 | [Ignore]
20 | public string DefaultTrenchName { get; } = Resources.PlayerFacingDefaultTrenchName;
21 |
22 | /// The set of values that are allowed for Floor IDs.
23 | public static Range Bounds
24 | => All.FloorIDs;
25 | #endregion
26 |
27 | #region Characteristics
28 | /// The tool used to dig out or fill in the floor.
29 | [Index(8)]
30 | public ModificationTool ModTool { get; private set; }
31 |
32 | /// Player-facing name of the parquet, used when it has been dug out.
33 | [Index(9)]
34 | public string TrenchName { get; private set; }
35 | #endregion
36 |
37 | #region Initialization
38 | ///
39 | /// Initializes a new instance of the class.
40 | ///
41 | /// Unique identifier for the . Cannot be null.
42 | /// Player-friendly name of the . Cannot be null.
43 | /// Player-friendly description of the .
44 | /// Comment of, on, or by the .
45 | /// Any additional information about the .
46 | /// The of the awarded to the player when a character gathers this .
47 | /// Which, if any, this helps to generate.
48 | /// Describes which, if any, (s) this helps form.
49 | /// The tool used to modify this .
50 | /// The name to use for this when it has been dug out.
51 | public FloorModel(ModelID id, string name, string description, string comment,
52 | IEnumerable tags = null, ModelID? itemID = null,
53 | IEnumerable addsToBiome = null, IEnumerable addsToRoom = null,
54 | ModificationTool modTool = ModificationTool.None, string trenchName = null)
55 | : base(Bounds, id, name, description, comment, tags, itemID, addsToBiome, addsToRoom)
56 | {
57 | ModTool = modTool;
58 | TrenchName =
59 | // TODO [Robustness] Are there places we use IsNullOrEmpty where IsNullOrWhiteSpace would be more appropriate?
60 | string.IsNullOrWhiteSpace(trenchName)
61 | ? DefaultTrenchName
62 | : trenchName;
63 | }
64 | #endregion
65 |
66 | #region IMutableFloorModel Implementation
67 | /// The tool used to dig out or fill in the floor.
68 | ///
69 | /// By design, subtypes of should never themselves use .
70 | /// IMutableFloorModel is for external types that require read/write access.
71 | ///
72 | [Ignore]
73 | ModificationTool IMutableFloorModel.ModTool
74 | {
75 | get => ModTool;
76 | set => ModTool = LibraryState.IsPlayMode
77 | ? Logger.DefaultWithImmutableInPlayLog(nameof(ModTool), ModTool)
78 | : value;
79 | }
80 |
81 | /// Player-facing name of the parquet, used when it has been dug out.
82 | ///
83 | /// By design, subtypes of should never themselves use .
84 | /// IMutableFloorModel is for external types that require read/write access.
85 | ///
86 | [Ignore]
87 | string IMutableFloorModel.TrenchName
88 | {
89 | get => TrenchName;
90 | set => TrenchName = LibraryState.IsPlayMode
91 | ? Logger.DefaultWithImmutableInPlayLog(nameof(TrenchName), TrenchName)
92 | : value;
93 | }
94 | #endregion
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/ParquetClassLibrary/Parquets/CollectibleModel.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Diagnostics.CodeAnalysis;
3 | using CsvHelper.Configuration.Attributes;
4 | using Parquet.Biomes;
5 |
6 | namespace Parquet.Parquets
7 | {
8 | ///
9 | /// Configurations for a sandbox collectible object, such as crafting materials.
10 | ///
11 | [SuppressMessage("Design", "CA1033:Interface methods should be callable by subtypes",
12 | Justification = "By design, subtypes of Model should never themselves use IMutableModel or derived interfaces to access their own members. The IMutableModel family of interfaces is for external types that require read/write access.")]
13 | public class CollectibleModel : ParquetModel, IMutableCollectibleModel
14 | {
15 | #region Class Defaults
16 | /// The set of values that are allowed for Collectible IDs.
17 | public static Range Bounds
18 | => All.CollectibleIDs;
19 | #endregion
20 |
21 | #region Characteristics
22 | /// The effect generated when a character encounters this Collectible.
23 | [Index(8)]
24 | public CollectingEffect CollectionEffect { get; private set; }
25 |
26 | ///
27 | /// The scale in points of the effect.
28 | /// For example, how much to alter a stat if the is set to alter a stat.
29 | ///
30 | [Index(9)]
31 | public int EffectAmount { get; private set; }
32 | #endregion
33 |
34 | #region Initialization
35 | ///
36 | /// Initializes a new instance of the class.
37 | ///
38 | /// Unique identifier for the . Cannot be null.
39 | /// Player-friendly name of the . Cannot be null.
40 | /// Player-friendly description of the .
41 | /// Comment of, on, or by the .
42 | /// Any additional information about the .
43 | /// The of the that this corresponds to, if any.
44 | /// A set of flags indicating which, if any, this parquet helps to generate.
45 | /// A set of flags indicating which, if any, this parquet helps to generate.
46 | /// Effect of this .
47 | /// The scale in points of the effect. For example, how much to alter a stat if is set to alter a stat.
48 | public CollectibleModel(ModelID id, string name, string description, string comment,
49 | IEnumerable tags = null, ModelID? itemID = null,
50 | IEnumerable addsToBiome = null, IEnumerable addsToRoom = null,
51 | CollectingEffect collectionEffect = CollectingEffect.None, int effectAmount = 0)
52 | : base(Bounds, id, name, description, comment, tags, itemID, addsToBiome, addsToRoom)
53 | {
54 | CollectionEffect = collectionEffect;
55 | EffectAmount = effectAmount;
56 | }
57 | #endregion
58 |
59 | #region IMutableCollectibleModel Implementation
60 | /// The effect generated when a character encounters this Collectible.
61 | ///
62 | /// By design, subtypes of should never themselves use .
63 | /// IMutableCollectibleModel is for external types that require read/write access.
64 | ///
65 | [Ignore]
66 | CollectingEffect IMutableCollectibleModel.CollectionEffect
67 | {
68 | get => CollectionEffect;
69 | set => CollectionEffect = LibraryState.IsPlayMode
70 | ? Logger.DefaultWithImmutableInPlayLog(nameof(CollectionEffect), CollectionEffect)
71 | : value;
72 | }
73 |
74 | ///
75 | /// The scale in points of the effect.
76 | /// For example, how much to alter a stat if the is set to alter a stat.
77 | ///
78 | ///
79 | /// By design, subtypes of should never themselves use .
80 | /// IMutableCollectibleModel is for external types that require read/write access.
81 | ///
82 | [Ignore]
83 | int IMutableCollectibleModel.EffectAmount
84 | {
85 | get => EffectAmount;
86 | set => EffectAmount = LibraryState.IsPlayMode
87 | ? Logger.DefaultWithImmutableInPlayLog(nameof(EffectAmount), EffectAmount)
88 | : value;
89 | }
90 | #endregion
91 | }
92 | }
93 |
--------------------------------------------------------------------------------