Compare commits

...

164 commits
0.7.0 ... main

Author SHA1 Message Date
Ell 339411aed6 0.41.2 2024-05-01 17:59:47 +02:00
Ell ef04313a7a 0.41.1 2024-04-21 10:33:56 +02:00
Ell 17e0f6dbfb 0.41.0 2024-04-14 17:59:01 +02:00
Ell 728553e329 0.40.1 2024-03-10 16:38:23 +01:00
Ell 6d6e95727b 0.40.0 2024-03-09 19:54:31 +01:00
Ell 0c2057801e 0.39.1 2024-02-04 12:36:01 +01:00
Ell 7676e8e294 0.39.0 2024-02-02 18:15:15 +01:00
jpiolho a07d8ba5c7
Custom tile example (#2)
* Added custom tile example

* Fixed whitespace
2024-01-27 13:50:18 +01:00
Ell f20a1dc1ae 0.38.4 2024-01-07 16:53:45 +01:00
Ell 9f2694178d use collection expressions in whole project 2023-12-21 17:34:40 +01:00
Ell 25d51baafb 0.38.3 2023-12-19 16:55:34 +01:00
Ell 855feece79 don't include various ide settings by default 2023-12-16 20:22:50 +01:00
Ell 3a4f8d5413 added some more base game dependencies to example mod for ease of use 2023-12-16 17:15:12 +01:00
Ell cefa4b4f13 0.38.2 2023-12-14 11:04:47 +01:00
Ell f911f01ce9 0.38.1 2023-12-13 14:46:38 +01:00
Ell 80309a73a5 small cake cleanup 2023-12-10 17:25:20 +01:00
Ell 4a7a3e8e44 fixed running on *nix causing log output to appear twice 2023-12-10 15:46:33 +01:00
Ell eb4bebe12a added the ability to pass arguments to the cake script 2023-12-08 13:37:12 +01:00
Ell 4a1a06ac94 0.38.0 2023-12-06 13:30:16 +01:00
Ell 89b120e61c rider what 2023-12-01 13:45:52 +01:00
Ell cd1445af8a allow including ansi code in logs for modders 2023-12-01 13:44:56 +01:00
Ell 1620b0f462 0.37.5 2023-11-18 12:57:03 +01:00
Ell 41e2b9ad10 0.37.4 2023-11-06 14:08:50 +01:00
Ell 27dcdb7b00 0.37.3 2023-11-05 20:57:08 +01:00
Ell aac7975b29 0.37.2 2023-11-04 19:27:37 +01:00
Ell dcbe2af6cf 0.37.1 2023-10-30 11:03:04 +01:00
Ell 3bb4161937 0.37.0 2023-10-25 14:35:53 +02:00
Ell a6c4173e4d 0.37.0-pre.3 2023-10-18 17:54:31 +02:00
Ell aa62777102 0.37.0-pre.2 2023-10-12 21:19:35 +02:00
Ell ae5196eef5 0.37.0-pre.1 2023-10-11 16:05:30 +02:00
Ell 715b11dbd9 0.36.2 2023-09-17 15:04:12 +02:00
Ell 1885b79dd7 0.36.1 2023-08-25 12:01:31 +02:00
Ell 682c713961 0.36.0 2023-08-15 13:17:18 +02:00
Ell 24e330553b 0.35.4 2023-08-02 09:26:23 +02:00
Ell d36bcb69bc 0.35.3 2023-08-01 14:10:36 +02:00
Ell 472e5b2b3d 0.35.2 2023-07-28 12:27:46 +02:00
Ell e1e14b3e2f 0.35.1 2023-07-22 12:03:52 +02:00
Ell c4a68727af 0.35.0 2023-07-20 14:34:15 +02:00
Ell ee6be0821a 0.34.0 2023-06-25 15:43:59 +02:00
Ell 5a96eecf96 dep update 2023-05-29 22:59:36 +02:00
Ell faa96c9406 0.33.2 2023-05-29 13:04:24 +02:00
Ell 473fef5f3d added issue tracker URL to example mod 2023-05-29 11:26:33 +02:00
Ell 2fab5e57af 0.33.1 2023-05-27 10:02:32 +02:00
Ell df2e0266d6 0.33.0 2023-05-26 15:16:14 +02:00
Ell 70f98b24cf 0.32.4 2023-05-11 14:56:06 +02:00
Ell 0e6af45024 swap initialize and addgamecontent for clarity 2023-05-10 23:50:26 +02:00
Ell 9c78aabbda (maybe??) condense the example mod into a single file 2023-05-10 23:45:59 +02:00
Ell 22e2dd39a2 0.32.3 2023-05-08 12:13:56 +02:00
Ell 5985b3948a reduced steam thumbnail sizes 2023-05-08 11:17:49 +02:00
Ell 2012b517b8 0.32.2 2023-05-04 13:26:40 +02:00
Ell 3516871064 0.32.1 2023-05-03 23:00:35 +02:00
Ell 51b4d9e2e7 0.32.0 2023-05-02 17:28:57 +02:00
Ell ea73c23d7f 0.31.4 2023-04-27 18:53:11 +02:00
Ell 6aecdffd2e 0.31.3 2023-04-26 21:12:38 +02:00
Ell e282328f28 0.31.2 2023-04-26 14:51:12 +02:00
Ell 0a018706e4 pad textures by default 2023-04-24 23:07:49 +02:00
Ell c068b255a5 print the log at least once even if the game exits early 2023-04-24 21:38:11 +02:00
Ell ffa44436d6 improved clothing color scheme docs 2023-04-24 20:19:46 +02:00
Ell 485e86342d updated example mod and docs for new clothing texture layouts 2023-04-24 11:44:11 +02:00
Ell 150a8eae24 allow mods to have a steam workshop thumbnail 2023-04-23 14:19:23 +02:00
Ell f6c82cefd3 0.31.1 2023-04-17 12:49:49 +02:00
Ell 661b7e5b7a 0.31.0 2023-04-14 13:00:32 +02:00
Ell 7493d4b896 debug saves in example mod build script 2023-04-09 15:43:43 +02:00
Ell bd006fed3a 0.30.1 2023-04-02 22:44:10 +02:00
Ell d2d234a274 0.30.0 2023-03-31 13:26:11 +02:00
Ell b45f9565eb 0.29.5 2023-02-20 12:52:23 +01:00
Ell 724e4ec478 0.29.4 2023-02-14 15:06:54 +01:00
Ell 0d6a0fad1a 0.29.3 2023-01-26 14:16:20 +01:00
Ell b79bf56474 0.29.2 2023-01-19 11:28:10 +01:00
Ell 9aad424ae7 0.29.1 2023-01-17 11:27:33 +01:00
Ell 63c8f836c6 0.29.0 2023-01-16 12:23:53 +01:00
Ell 2c8205eebb removed MG.Extended dependency 2023-01-07 20:50:08 +01:00
Ell fe33c07209 0.28.3 2022-12-28 18:09:02 +01:00
Ell f0f4f17bb7 improved mod options 2022-12-23 23:40:21 +01:00
Ell 4865bd81a7 0.28.2 2022-12-23 11:26:47 +01:00
Ell 17f55129c2 well this is embarrassing (0.28.1) 2022-12-20 14:00:14 +01:00
Ell 66987d3838 0.28.0 2022-12-20 13:24:55 +01:00
Ell c8c7d3c528 updated cake 2022-12-12 19:21:22 +01:00
Ell 0ba898838a use simpler file and class naming for the example mod 2022-11-22 19:25:15 +01:00
Ell 438d203bc9 cleaned up the build script and added a clean task 2022-11-22 19:17:33 +01:00
Ell 260d3437ab 0.27.0 2022-11-12 13:53:21 +01:00
Ell 0834f5427c 0.26.0 2022-10-18 13:03:08 +02:00
Ell 51d005872f 0.25.3 2022-09-19 15:43:08 +02:00
Ell dcb31c9ed0 0.25.2 2022-09-17 12:33:20 +02:00
Ell fa0514c7a2 0.25.1 2022-09-13 14:12:56 +02:00
Ell ccd87ee252 whoop 2022-09-12 16:52:31 +02:00
Ell 056827822b improved build.cake formatting 2022-09-12 16:50:17 +02:00
Ell 307ad45de9 finally added a proper build script to the example mod 2022-09-12 13:21:27 +02:00
Ell 1097f6d8f9 0.25.0 2022-09-05 14:19:34 +02:00
Ell d9a47dd846 lovely code cleanup 2022-08-18 12:30:04 +02:00
Ell d6cd9bd6e0 0.24.1 2022-08-02 23:19:56 +02:00
Ell 4c33872b7d 0.24.0 2022-07-28 14:58:32 +02:00
Ell cd9a25dd3f 0.23.0 2022-07-04 16:50:34 +02:00
Ell d68c089bdc added editorconfig 2022-06-17 18:05:34 +02:00
Ell 29bc52b5b7 qualify static members explicitly 2022-06-15 10:53:51 +02:00
Ell 19e56dc912 0.22.1 2022-06-10 12:18:24 +02:00
Ell 9daa788fde 0.22.0 2022-06-09 20:12:05 +02:00
Ell dacc8c87e4 0.21.2 2022-05-26 13:20:57 +02:00
Ell 2e332b0224 0.21.0 2022-05-24 13:35:56 +02:00
Ell 5ebf12519e 0.20.4 2022-04-27 12:59:03 +02:00
Ell 04a745703b added a custom wallpaper example 2022-04-19 20:38:25 +02:00
Ell f0a4bf01d6 code cleanup 2022-04-11 14:26:07 +02:00
Ell dc555efa7b 0.20.3 2022-04-04 14:21:15 +02:00
Ell d7c776e8d2 0.20.2 2022-03-29 11:47:36 +02:00
Ell 301906e176 0.20.1 2022-03-28 18:01:32 +02:00
Ell f19ae32543 clean up example mod 2022-03-21 18:56:23 +01:00
Ell 7767cd7f18 include harmony in the example mod 2022-03-09 16:26:16 +01:00
Ell 52d32fe822 0.20.0 2022-03-09 16:12:57 +01:00
Ell a40d08209b exit scripts if the build fails 2022-01-08 16:13:14 +01:00
Ell 2f19a03d30 made run and publish scripts mac-compliant 2022-01-08 16:09:10 +01:00
Ell ce5eb6e03a 0.19.1 2022-01-08 12:41:02 +01:00
Ell 8723b0ebb4 switch MLEM to a release version 2022-01-02 13:16:46 +01:00
Ell b4e3511e48 0.19.0 2022-01-01 17:06:02 +01:00
Ell 6aca205bfd code cleanup 2021-12-25 18:05:47 +01:00
Ell 1943ccd7a3 file-scoped namespaces, or The Diff to End All Diffs 2021-12-23 23:23:56 +01:00
Ell e7f00e5c81 some cleaning up 2021-12-14 00:12:05 +01:00
Ell 381599b278 about time for .net 6 2021-12-08 23:34:22 +01:00
Ell f293c56d72 0.18.1 2021-11-26 23:42:21 +01:00
Ell 3fd79cb9d8 0.18.0 2021-11-23 16:23:58 +01:00
Ell 82167f9e38 bleh zip command 2021-11-18 10:26:47 +01:00
Ell 4a45ad5206 actually make publish scripts create zip files on all platforms 2021-11-18 10:22:20 +01:00
Ell 4af436681d added publishing scripts to the example mod 2021-11-09 02:25:33 +01:00
Ell 54f3646d24 0.17.5
Oh God.
2021-10-20 11:18:21 +02:00
Ell d3cbbf157e 0.17.4
It's hotfix city in here!
2021-10-20 10:52:15 +02:00
Ell 0f7634d8cd 0.17.3 2021-10-19 22:59:45 +02:00
Ell 69db77d3e1 0.17.2 2021-10-18 18:49:07 +02:00
Ell dc4cfb201a 0.17.0 2021-10-14 20:30:36 +02:00
Ell 0451c72f34 Merge remote-tracking branch 'origin/main' 2021-09-20 15:43:35 +02:00
Ell a8332bfdb0 add verbose output to run scripts 2021-09-20 15:43:20 +02:00
Runecroon f62307e5c1
Fix minor typo (#1)
- Change word On to lowercase
2021-09-19 15:03:27 +02:00
Ell 9b4ea07d11 0.16.1 2021-09-09 19:52:02 +02:00
Ell ddbc173855 0.16.0 2021-08-30 20:44:25 +02:00
Ell ad1801a866 added documentation website 2021-08-04 05:44:15 +02:00
Ell 633a7d2762 added some more comments to the example mod 2021-08-02 03:11:08 +02:00
Ell 0a3d3aa3f0 0.15.1 lmao 2021-07-27 17:03:36 +02:00
Ell a3ea83fb01 0.14.0 2021-07-15 17:58:55 +02:00
Ell b4d1a8e512 0.13.1 2021-07-08 01:16:15 +02:00
Ell 2698242e5b added info about the EditCurrentActionSpot cheat 2021-07-07 16:25:53 +02:00
Ell 405ba156cd 0.13.0 2021-07-06 17:42:11 +02:00
Ell 6ba84e6c33 fixed the example action breaking when clicking out of bounds 2021-06-29 13:40:31 +02:00
Ell 31e9187f90 added AI settings to the example action 2021-06-28 02:00:59 +02:00
Ell b7dafb75e6 fixed the custom table being terrible 2021-06-26 15:57:19 +02:00
Ell 461ab8a8db added information on saving/loading data 2021-06-26 15:49:37 +02:00
Ell 45c21080c5 added some more clothes to the example mod 2021-06-25 23:40:01 +02:00
Ell 018a4cf656 added some helpful links to the readme 2021-06-25 23:31:36 +02:00
Ell 132af39ecc 0.12.2! 2021-06-22 19:41:30 +02:00
Ell d53eded42d updated to 0.12.1 2021-06-18 23:20:28 +02:00
Ell 24ae30c839 stop using obsolete WornClothes 2021-06-17 19:49:03 +02:00
Ell fe3a119a8b updated to 0.12.0 2021-06-17 19:46:15 +02:00
Ell 22d39dc8fe explain any kind of custom content in the readme 2021-06-17 18:46:02 +02:00
Ell 0ce7b27ef4 update to 0.11.0 2021-06-05 17:48:28 +02:00
Ell c2b0843104 updated the mod readme 2021-06-01 17:21:30 +02:00
Ell d0bf7ce010 updated some links 2021-05-28 22:21:20 +02:00
Ell d9c670eec8 give the example table object spots 2021-05-22 18:14:05 +02:00
Ell 83d2cf5f24 added an example action and emotion modifier 2021-05-22 17:39:17 +02:00
Ell 3c2203cf84 updated to 0.10.1! 2021-05-22 17:23:21 +02:00
Ell eb883d8100 updated documentation and localization 2021-05-22 16:48:20 +02:00
Ell 1c3a191811 update to 0.10.0 2021-05-17 23:35:34 +02:00
Ell 5e627deb0d update! 2021-04-30 18:29:55 +02:00
Ell 5932732681 removed mod template license to allow for people to choose their own 2021-04-25 16:06:01 +02:00
Ell 92d7f28fcb fixed linux run script 2021-04-22 20:01:01 +02:00
Ell 9d3f0ba1b8 update 2021-04-18 17:35:45 +02:00
Ell 7746558f2a update 2021-03-29 08:59:08 +02:00
Ell 8c6c4051bf update 2021-03-27 19:32:20 +01:00
23 changed files with 339 additions and 171 deletions

12
.config/dotnet-tools.json Normal file
View file

@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"cake.tool": {
"version": "3.0.0",
"commands": [
"dotnet-cake"
]
}
}
}

4
.gitignore vendored
View file

@ -1,4 +1,6 @@
bin/
obj/
/packages/
.idea
.idea/
.vs/
.vscode/

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 876 B

View file

@ -1,3 +1,3 @@
ExampleMod.CustomTableUp
loc 0 0 32 32
piv 16 16
piv 16 20

Binary file not shown.

Before

Width:  |  Height:  |  Size: 535 B

After

Width:  |  Height:  |  Size: 534 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -1,8 +0,0 @@
{
"BuildMode": {
"ExampleMod.CustomTable": "Custom Table"
},
"Clothes": {
"ExampleMod.DarkShirt": "Dark Shirt"
}
}

View file

@ -0,0 +1,22 @@
{
"BuildMode": {
"ExampleMod.CustomTable": "Custom Table",
"ExampleMod.CrossedWallpaper": "Crossed Wallpaper",
"ExampleMod.CustomTile": "Custom Tile"
},
"Clothes": {
"ExampleMod.DarkShirt": "Dark Shirt",
"ExampleMod.PastelPants": "Pastel Pants",
"ExampleMod.PastelShoes": "Pastel Shoes",
"ExampleMod.WeirdHair": "Weird Hair"
},
"Emotions": {
"ExampleMod.GrassSitting": "Comfy Green Ground"
},
"Actions": {
"ExampleMod.SitOnGrass": "Sit on Grass"
},
"Ui": {
"ExampleMod.DarkShirtSpeedOption": "Dark Shirt Speed"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 535 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 211 B

After

Width:  |  Height:  |  Size: 316 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 706 B

View file

@ -1,23 +0,0 @@
using System;
using Microsoft.Xna.Framework;
using TinyLife.Objects;
using TinyLife.World;
namespace ExampleMod {
public class CustomTable : Furniture {
public CustomTable(Guid id, FurnitureType type, int[] colors, Map map, Vector2 pos) : base(id, type, colors, map, pos) {
}
public override void OnAdded() {
base.OnAdded();
ExampleMod.Logger.Info("We were added at " + this.Position);
}
public override void OnRemoved() {
base.OnRemoved();
ExampleMod.Logger.Info("We were removed from " + this.Position);
}
}
}

View file

@ -1,58 +1,221 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using ExtremelySimpleLogger;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MLEM.Data;
using MLEM.Data.Content;
using MLEM.Textures;
using MLEM.Ui;
using MLEM.Ui.Elements;
using TinyLife;
using TinyLife.Actions;
using TinyLife.Emotions;
using TinyLife.Mods;
using TinyLife.Objects;
using TinyLife.Utilities;
using TinyLife.World;
using Action = TinyLife.Actions.Action;
namespace ExampleMod {
public class ExampleMod : Mod {
namespace ExampleMod;
// the logger that we can use to log info about this mod
public static Logger Logger { get; private set; }
public class ExampleMod : Mod {
// visual data about this mod
public override string Name => "Example Mod";
public override string Description => "This is the example mod for Tiny Life!";
public override TextureRegion Icon => this.uiTextures[0, 0];
// the logger that we can use to log info about this mod
public static Logger Logger { get; private set; }
public static ExampleOptions Options { get; private set; }
private UniformTextureAtlas customClothes;
private UniformTextureAtlas uiTextures;
public static EmotionModifier GrassSittingModifier { get; private set; }
public override void AddGameContent(GameImpl game) {
// adding a custom furniture item
FurnitureType.Register(new FurnitureType.TypeSettings("ExampleMod.CustomTable", new Point(1, 1), ObjectCategory.Table, 150, ColorScheme.SimpleWood) {
ConstructedType = typeof(CustomTable),
Icon = this.Icon
});
// visual data about this mod
public override string Name => "Example Mod";
public override string Description => "This is the example mod for Tiny Life!";
public override TextureRegion Icon => this.uiTextures[new Point(0, 0)];
public override string IssueTrackerUrl => "https://github.com/Ellpeck/TinyLifeExampleMod/issues";
public override string TestedVersionRange => "[0.41.0,0.41.2]";
// adding custom clothing
Clothes.Register(new Clothes("ExampleMod.DarkShirt", ClothesLayer.Shirt,
this.customClothes[0, 0], // the top left in-world region (the rest will be auto-gathered from the atlas)
100, this.Icon, false, ColorScheme.WarmDark));
}
private Dictionary<Point, TextureRegion> customTops;
private Dictionary<Point, TextureRegion> customHairs;
private Dictionary<Point, TextureRegion> customBottoms;
private Dictionary<Point, TextureRegion> uiTextures;
private Dictionary<Point, TextureRegion> wallpaperTextures;
private Dictionary<Point, TextureRegion> tileTextures;
public override void Initialize(Logger logger, RawContentManager content, RuntimeTexturePacker texturePacker) {
Logger = logger;
// loads a texture atlas with the given amount of separate texture regions in the x and y axes
// we submit it to the texture packer to increase rendering performance. The callback is invoked once packing is completed
texturePacker.Add(content.Load<Texture2D>("CustomClothes"), r => this.customClothes = new UniformTextureAtlas(r, 4, 6));
texturePacker.Add(content.Load<Texture2D>("UiTextures"), r => this.uiTextures = new UniformTextureAtlas(r, 8, 8));
}
public override IEnumerable<string> GetCustomFurnitureTextures() {
// tell the game about our custom furniture texture
// this needs to be a path to a data texture atlas, relative to our "Content" directory
// the texture atlas combines the png texture and the .atlas information
// see https://mlem.ellpeck.de/api/MLEM.Data.DataTextureAtlas.html for more info
yield return "CustomFurniture";
}
public override void Initialize(Logger logger, RawContentManager content, RuntimeTexturePacker texturePacker, ModInfo info) {
ExampleMod.Logger = logger;
ExampleMod.Options = info.LoadOptions(() => new ExampleOptions());
// loads a texture atlas with the given amount of separate texture regions in the x and y axes
// we submit it to the texture packer to increase rendering performance. The callback is invoked once packing is completed
// additionally, we pad all texture regions by 1 pixel, so that rounding errors during rendering don't cause visual artifacts
texturePacker.Add(new UniformTextureAtlas(content.Load<Texture2D>("CustomTops"), 4, 11), r => this.customTops = r, 1, true);
texturePacker.Add(new UniformTextureAtlas(content.Load<Texture2D>("CustomHairs"), 4, 5), r => this.customHairs = r, 1, true);
texturePacker.Add(new UniformTextureAtlas(content.Load<Texture2D>("CustomBottomsShoes"), 8, 6), r => this.customBottoms = r, 1, true);
texturePacker.Add(new UniformTextureAtlas(content.Load<Texture2D>("UiTextures"), 8, 8), r => this.uiTextures = r, 1, true);
texturePacker.Add(new UniformTextureAtlas(content.Load<Texture2D>("Tiles"), 4, 2), r => this.tileTextures = r, 1, true);
// wallpaper textures require special treatment to work with openings, the x and y values are passed to the UniformTextureAtlas constructor
WallMode.ApplyMasks(content.Load<Texture2D>("Wallpapers"), 4, 5, texturePacker, r => this.wallpaperTextures = r);
}
}
public override void AddGameContent(GameImpl game, ModInfo info) {
// adding a custom furniture item
FurnitureType.Register(new FurnitureType.TypeSettings("ExampleMod.CustomTable", new Point(1, 1), ObjectCategory.Table, 150, ColorScheme.SimpleWood) {
// specify the type that should be constructed when this furniture type is placed
// if this is not specified, the Furniture class is used, which is used for furniture without special animations or data
ConstructedType = typeof(ExampleTable),
// specifying icons for custom clothes and furniture is optional, but using the mod's icon helps users recognize a mod's features
Icon = this.Icon,
// allow chairs and plates to be slotted into and onto the table
ObjectSpots = ObjectSpot.TableSpots(new Point(1, 1)).ToArray()
});
// adding custom clothing
var darkShirt = new Clothes("ExampleMod.DarkShirt", ClothesLayer.Shirt,
// the top left in-world region
// additional regions will be auto-gathered from the atlas according to the rules described in https://docs.tinylifegame.com/articles/creating_textures.html
this.customTops, new Point(0, 0),
// the price
100,
// the clothes item's use cases
ClothesIntention.Everyday | ClothesIntention.Workout,
// the clothes item's style preferences, which influence randomly generated tinies slightly
// neutral style preferences have the same chance to be picked for all tinies, others have a 25% chance for mismatched preferences
StylePreference.Neutral,
// the clothes item's color scheme
// if the item should have multiple layers, multiple color schemes can be supplied here (see docs above)
ColorScheme.WarmDark
) {Icon = this.Icon};
Clothes.Register(darkShirt);
// adding some more custom clothing
Clothes.Register(new Clothes("ExampleMod.PastelPants", ClothesLayer.Pants, this.customBottoms, new Point(4, 0), 100, ClothesIntention.Everyday, StylePreference.Neutral, ColorScheme.Pastel) {Icon = this.Icon});
Clothes.Register(new Clothes("ExampleMod.PastelShoes", ClothesLayer.Shoes, this.customBottoms, new Point(0, 0), 100, ClothesIntention.Everyday, StylePreference.Neutral, ColorScheme.Pastel) {Icon = this.Icon});
Clothes.Register(new Clothes("ExampleMod.WeirdHair", ClothesLayer.Hair, this.customHairs, new Point(0, 0), 0, ClothesIntention.None, StylePreference.Neutral, ColorScheme.Modern) {Icon = this.Icon});
// adding an event subscription to people
MapObject.OnEventsAttachable += o => {
if (o is Person person) {
// changing the walk speed to be doubled if a person is wearing our dark shirt
person.OnGetWalkSpeed += (ref float s) => {
if (person.CurrentOutfit.Clothes.TryGetValue(ClothesLayer.Shirt, out var shirt) && shirt.Type == darkShirt)
s *= ExampleMod.Options.DarkShirtSpeedIncrease;
};
}
};
// adding a simple action: sitting down in the grass, which also gives us a nice emotion modifier
ActionType.Register(new ActionType.TypeSettings("ExampleMod.SitOnGrass", ObjectCategory.Ground, typeof(ExampleGrassSitAction)) {
// we set this action to be executable only on grass tiles, not on other ground
CanExecute = (actionInfo, _) => {
if (!actionInfo.GoalMap.IsInBounds(actionInfo.ActionLocation.ToPoint()))
return CanExecuteResult.Hidden;
var tile = actionInfo.GoalMap.GetTile(actionInfo.ActionLocation.ToPoint(), (int) actionInfo.ActionFloor);
// hidden means the action won't be displayed in the ring menu, Valid means the player (or AI) is able to enqueue and execute it
return tile.Name.StartsWith("Grass") ? CanExecuteResult.Valid : CanExecuteResult.Hidden;
},
Ai = {
// we allow the action to be done even if the solved needs aren't low enough on a person
CanDoRandomly = true,
// the solved needs indicate when the AI should mark this action as important, they don't actually have to match the action's behavior
SolvedNeeds = [NeedType.Energy],
// make people more likely to sit down in the grass if they're uncomfortable
PassivePriority = p => p.Emotion == EmotionType.Uncomfortable ? 150 : 25
},
// since this action doesn't use objects (like chairs etc.), we set a texture to display instead
Texture = this.uiTextures[new Point(1, 0)]
});
// we use this emotion modifier in SitDownOnGrassAction
ExampleMod.GrassSittingModifier = EmotionModifier.Register(
new EmotionModifier("ExampleMod.GrassSitting", this.uiTextures[new Point(1, 0)], EmotionType.Happy));
// adding a custom wallpaper (we're using the top left texture region, which is why we pass 0, 0 as the texture coordinate)
Wallpaper.Register("ExampleMod.CrossedWallpaper", 15, this.wallpaperTextures, new Point(0, 0), ColorScheme.Modern, this.Icon);
// adding a custom tile
Tile.Register("ExampleMod.CustomTile", 8, this.tileTextures, new Point(0, 0), ColorScheme.Bricks, icon: this.Icon);
}
public override IEnumerable<string> GetCustomFurnitureTextures(ModInfo info) {
// tell the game about our custom furniture texture
// this needs to be a path to a data texture atlas, relative to our "Content" directory
// the texture atlas combines the png texture and the .atlas information
// see https://mlem.ellpeck.de/api/MLEM.Data.DataTextureAtlas.html for more info
yield return "CustomFurniture";
}
// this method can be overridden to populate the section in the mod tab of the game's options menu where this mod's options should be displayed
// this mod uses the ModOptions class to manage its options, though that is optional
// in general, options should be stored in the ModInfo.OptionsFile file that is given to the mod by the game
public override void PopulateOptions(Group group, ModInfo info) {
group.AddChild(new Paragraph(Anchor.AutoLeft, 1, _ => $"{Localization.Get(LnCategory.Ui, "ExampleMod.DarkShirtSpeedOption")}: {ExampleMod.Options.DarkShirtSpeedIncrease}"));
group.AddChild(new Slider(Anchor.AutoLeft, new Vector2(1, 10), 5, 5) {
CurrentValue = ExampleMod.Options.DarkShirtSpeedIncrease,
OnValueChanged = (_, v) => {
ExampleMod.Options.DarkShirtSpeedIncrease = v;
info.SaveOptions(ExampleMod.Options);
}
});
}
}
// these options are saved and loaded in ExampleMod
public class ExampleOptions {
public float DarkShirtSpeedIncrease = 2;
}
// we use a multi action because we want to walk to the location, and then execute the main sitting part
// see ExampleTable for information on how to store custom action-specific information to disk as well
public class ExampleGrassSitAction : MultiAction {
public ExampleGrassSitAction(ActionType type, ActionInfo info) : base(type, info) {}
protected override IEnumerable<Action> CreateFirstActions() {
// we want to walk to the location clicked, so we use the current action info
yield return ActionType.GoHere.Construct<Action>(this.Info);
}
protected override void AndThenUpdate(GameTime time, TimeSpan passedInGame, float speedMultiplier) {
base.AndThenUpdate(time, passedInGame, speedMultiplier);
// set our person to look like they're sitting on the ground
this.Person.CurrentPose = Pose.SittingGround;
// restore need and lower emotions
this.Person.RestoreNeed(NeedType.Energy, 0.5F, this.Info, speedMultiplier);
this.Person.LowerEmotion(EmotionType.Uncomfortable, 0.0001F, speedMultiplier);
}
protected override CompletionType AndThenIsCompleted() {
// we want to complete our action once 10 minutes of sitting time have passed
return this.CompleteIfTimeUp(TimeSpan.FromMinutes(10));
}
protected override void AndThenOnCompleted(CompletionType type) {
base.AndThenOnCompleted(type);
// this method is called when the action completes in any way, even if it fails
if (type == CompletionType.Completed) {
// once we're finished sitting, we want to get a nice emotion modifier for it
this.Person.AddEmotion(ExampleMod.GrassSittingModifier, 2, TimeSpan.FromHours(1), this.Type);
}
}
}
// note that having a custom class for a furniture item like this is entirely optional
// but it allows for additional functionalities as displayed in this example
public class ExampleTable : Furniture {
// anything whose base classes have the DataContract attribute automatically gets saved and loaded to and from disk
// this means that you can add custom DataMember members to have them saved and loaded
[DataMember]
public float TestValue;
public ExampleTable(Guid id, FurnitureType type, int[] colors, Map map, Vector2 pos, float floor) : base(id, type, colors, map, pos, floor) {
this.TestValue = Furniture.Random.NextSingle();
}
}

View file

@ -1,23 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="TinyLifeApi" Version="0.7.0" />
<PackageReference Include="TinyLifeApi" Version="0.41.2" />
<PackageReference Include="ExtremelySimpleLogger" Version="1.2.1" />
<PackageReference Include="MLEM.Data" Version="4.3.0" />
<PackageReference Include="MLEM.Extended" Version="4.3.0" />
<PackageReference Include="MLEM.Startup" Version="4.3.0" />
<PackageReference Include="MonoGame.Extended" Version="3.8.0" />
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.0.1641" />
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.303" />
<PackageReference Include="Lib.Harmony" Version="2.2.2" />
<PackageReference Include="ExtremelySimpleLogger" Version="1.4.1" />
<PackageReference Include="MLEM.Data" Version="6.3.0" />
<PackageReference Include="MLEM.Extended" Version="6.3.0" />
<PackageReference Include="MLEM.Startup" Version="6.3.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="DynamicEnums" Version="1.2.0" />
<PackageReference Include="Coroutine" Version="2.1.5" />
</ItemGroup>
<ItemGroup>
<Content Include="./Content/**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="SteamThumbnail.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

21
LICENSE
View file

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2020 Ellpeck
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

7
NuGet.config Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<!-- For preview versions of MLEM, which are sometimes required -->
<add key="Ellpeck" value="https://nuget.ellpeck.de/v3/index.json" />
</packageSources>
</configuration>

View file

@ -1,52 +1,4 @@
# Tiny Life Example Mod
An example mod for my game Tiny Life. Use this template repository to create your own mod!
# Installing mods
Installing a mod is pretty simple:
- Find the `Tiny Life` folder (you can open it from the game's options menu)
- Find the `Mods` folder in there
- If you received the mod you want to install as a `zip` (or any other kind of) archive, extract it first
- Put the mod's `dll` as well as its `Content` folder into the `Mods` folder.
Done! Now just start the game and the mod should automatically load. If there are any errors, they'll be logged in the `Log.txt` file in the `Tiny Life` folder.
# Creating mods
To create a mod, all you have to do is create a repository [from this template](https://github.com/Ellpeck/TinyLifeExampleMod/generate) and open the project contained in it using Visual Studio, Rider or any other kind of C# IDE. The code that is already there contains some examples. Once you're done checking them out, you can just delete them and start fresh.
This repository also contains a little `Run` script for [windows](./Run.bat) and [linux and mac](./Run.sh) that automatically builds your mod, copies it into the `Mods` directory of your Tiny Life instance and runs the game. Note that the script will only work correctly if you've previously launched Tiny Life manually.
The game's API is **fully documented**. The documentation is easily accessible by opening any of the API's files in your IDE. The most updated version can be found on [the wiki](https://github.com/Ellpeck/TinyLifeExampleMod/wiki) as well.
## Distributing mods
To distribute your mod to other people, all you have to do is go into the `bin/Debug/netcoreapp3.0` folder after building and copy your mod's `dll` and the `Content` directory. You can either send them to your friends directly or pack them into an archive first.
## Updating mods
To change the version of Tiny Life that your mod is compiled against, simply go into [the project file](https://github.com/Ellpeck/TinyLifeExampleMod/blob/main/ExampleMod.csproj) and change the `TinyLifeApi` version. Note that some other dependencies might also have been updated, which needs to be [taken into account](https://github.com/Ellpeck/TinyLifeExampleMod#dependency-version-history).
## Where's the source code?
The NuGet package for the Tiny Life API just contains a [reference assembly](https://docs.microsoft.com/en-us/dotnet/standard/assembly/reference-assemblies) so that people can't just download the game from NuGet and play it. Since most of the public API is documented, you won't have to look at the source code in most cases, anyway.
Technically, you can download the game [from itch](https://ellpeck.itch.io/tiny-life) and then decompile it to see the implementation's code, but due to its license, copying it is not allowed.
## Dependency version history
Since the mod is compiled against the same dependencies as Tiny Life, it also needs to have the same versions of those dependencies for mods to work correctly with the game. Each update to this repository is [tagged](https://github.com/Ellpeck/TinyLifeExampleMod/tags) with the game's version number. If you want to develop for a certain version, just check that tag's [project file](https://github.com/Ellpeck/TinyLifeExampleMod/blob/main/ExampleMod.csproj) to see the required dependency versions.
# Testing Cheats
You can open up the game's cheat menu by pressing the `F1` key. Here is a list of some of the more useful cheats for mod development and testing. Keep in mind that `[arguments]` need to be replaced with your custom value.
- `ShowNonBuyable` allows you to buy plates, pots and the like
- `Need [name] [percentage <= 1]` changes the current person's needs
- `NoNeed` stops need reduction for everyone
- `Needless` fills all needs back up
- `Needy` reduces all needs to zero
- `EditWorld` allows you to place things outside of lots
- `IgnorePlacementRules` allows you to place colliding furniture
- `EditPerson` opens a character editor with all functionality enabled
- `Gib` gives you 10,000 tiny bucks
- `ExtremelyFast` allows you to select the 15x speed option all the time
- `Skill [name] [level]` sets the current person's skill level
- `Held [object name or null]` sets the current person's held object
- `Job [name] [level]` stes the current person's job and job level
- `Die` kills the current person
- `Emotion [modifier name] [level] [seconds]` adds an emotion modifier to the current person
- `Friendship [name without spaces] [percentage <= 1]` sets the friendship level between the named partner and the current person
- `Romance [name without spaces] [percentage <= 1]` sets the romance level between the named partner and the current person
To learn how to use the example mod repository and create your own mods, check out the [Modding Basics](https://docs.tinylifegame.com/articles/mod_basics.html) documentation.

11
Run.bat
View file

@ -1,11 +0,0 @@
@echo off
rem move to the script directory
cd /d "%~dp0"
rem build the mod
dotnet build
rem copy the mod to the mods folder
robocopy ./bin/Debug/net5.0/ "%LOCALAPPDATA%/Tiny Life/Mods" /e /is
rem run the game
set /p dir=<"%LOCALAPPDATA%/Tiny Life/GameDir"
cd /d %dir%
"Tiny Life.exe"

11
Run.sh
View file

@ -1,11 +0,0 @@
#!/bin/bash
# move to the script directory
cd "$(dirname "$0")"
# build the mod
dotnet build
# copy the mod to the mods folder
cp ./bin/Debug/net5.0/* "$LOCALAPPDATA/Tiny Life/Mods" -r
# run the game
dir=$(<"$LOCALAPPDATA/Tiny Life/GameDir")
cd $dir
"./Tiny Life.exe"

BIN
SteamThumbnail.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 B

78
build.cake Normal file
View file

@ -0,0 +1,78 @@
using System.Diagnostics;
using System.Linq;
using System.Threading;
var target = Argument("target", "Run");
var config = Argument("configuration", "Release");
var args = Argument("args", "");
var tinyLifeDir = $"{Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)}/Tiny Life";
Task("Clean").Does(() => {
EnsureDirectoryDoesNotExist($"bin/{config}");
EnsureDirectoryDoesNotExist($"{tinyLifeDir}/Mods/_Dev");
});
Task("Build").DoesForEach(GetFiles("**/*.csproj"), p => {
DotNetBuild(p.FullPath, new DotNetBuildSettings {
Configuration = config
});
});
Task("CopyToMods").IsDependentOn("Build").Does(() => {
var dir = $"{tinyLifeDir}/Mods/_Dev";
EnsureDirectoryExists(dir);
var files = GetFiles($"bin/{config}/net*/**/*");
CopyFiles(files, dir, true);
});
Task("Run").IsDependentOn("CopyToMods").Does(() => {
// start the tiny life process
var exeDir = System.IO.File.ReadAllText($"{tinyLifeDir}/GameDir");
var process = Process.Start(new ProcessStartInfo($"{exeDir}/Tiny Life") {
Arguments = $"-v --skip-splash --skip-preloads --debug-saves --ansi {args}",
RedirectStandardOutput = true,
RedirectStandardError = true
});
// make sure the output buffers (which we ignore) don't fill up
process.BeginOutputReadLine();
process.BeginErrorReadLine();
// we wait a bit to make sure the process has generated a new log file
Thread.Sleep(1000);
// attach to the newest log file
var logsDir = $"{tinyLifeDir}/Logs";
var log = System.IO.Directory.EnumerateFiles(logsDir).OrderByDescending(System.IO.File.GetCreationTime).FirstOrDefault();
if (log != null) {
using (var stream = new FileStream(log, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {
using (var reader = new StreamReader(stream)) {
var lastPos = 0L;
do {
if (stream.Length > lastPos) {
stream.Seek(lastPos, SeekOrigin.Begin);
string line;
while ((line = reader.ReadLine()) != null)
Information(line);
lastPos = stream.Position;
}
Thread.Sleep(10);
} while (!process.HasExited);
}
}
}
Information($"Tiny Life exited with exit code {process.ExitCode}");
});
Task("Publish").IsDependentOn("Build").DoesForEach(() => GetDirectories($"bin/{config}/net*"), d => {
var dllFile = GetFiles($"{d}/**/*.dll").FirstOrDefault();
if (dllFile == null)
throw new Exception($"Couldn't find built mod in {d}");
var dllName = System.IO.Path.GetFileNameWithoutExtension(dllFile.ToString());
var zipLoc = $"{d.GetParent()}/{dllName}.zip";
Zip(d, zipLoc, GetFiles($"{d}/**/*"));
Information($"Published {dllName} to {zipLoc}");
});
RunTarget(target);