Compare commits

...

179 commits
0.5.1 ... main

Author SHA1 Message Date
Ell
0922ecefc5 0.42.2 2024-06-15 12:48:45 +02:00
Ell
977ad2d401 0.42.1 2024-06-09 20:51:37 +02:00
Ell
c963a9781c 0.42.0 2024-06-09 17:07:26 +02:00
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
Ell
468e72a071 update! 2021-02-18 19:12:01 +01:00
Ell
01a02b6df7 update to the new release of MLEM 2021-02-18 16:18:04 +01:00
Ell
c70129110f update to net5 2021-02-18 01:27:07 +01:00
Ell
18366975a1 improve the run configurations 2021-02-17 18:25:45 +01:00
Ell
78486b7f60 put stuff on the wiki after all, using a different tool this time around 2021-02-17 03:51:06 +01:00
Ell
30cc17bdda move the API to the repo itself because the wikis don't support folders, annoyingly 2021-02-17 00:17:57 +01:00
Ell
b4feb0a515 made the api docs into a wiki 2021-02-16 23:36:56 +01:00
Ell
2ee8c1b792 added a list of cheats to the mod readme 2021-02-03 23:52:55 +01:00
Ell
ec070db217 put API docs in repo 2021-02-02 09:16:46 +01:00
Ell
5d0ec63187 full API Reference on the wiki 2021-02-02 09:00:50 +01:00
Ell
79b3dece5c update mod template to 0.6.0 2021-01-27 18:48:23 +01:00
Ell
0234f4fd2d update 2021-01-14 21:32:21 +01:00
22 changed files with 339 additions and 142 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.

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,54 +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!";
// 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;
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) {
Construct = (i, t, c, m, p) => new CustomTable(i, t, c, m, p)
});
// 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.42.0,0.42.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)
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));
}
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>netcoreapp3.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="TinyLifeApi" Version="0.5.1" />
<PackageReference Include="TinyLifeApi" Version="0.42.2" />
<PackageReference Include="ExtremelySimpleLogger" Version="1.2.1" />
<PackageReference Include="MLEM.Data" Version="4.3.0-20" />
<PackageReference Include="MLEM.Extended" Version="4.3.0-20" />
<PackageReference Include="MLEM.Startup" Version="4.3.0-20" />
<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,37 +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.
Since Tiny Life uses early versions of some of my libraries, you will also have to add [my server](https://nuget.ellpeck.de/) to your NuGet config. You can do so using the following command:
```
dotnet nuget add source https://nuget.ellpeck.de/v3/index.json --name "Ellpeck"
```
This repository also contains a little script called `Run.sh` that you can use to automatically build your mod, copy it into the `Mods` directory of your Tiny Life instance and run the game. Just be sure to modify all of the paths first.
The game's API is currently in the process of being documented. This documentation is easily accessible by opening any of the API's files in your IDE. In the future, there will additionally be a web version of the API documentation.
## 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.
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.

12
Run.sh
View file

@ -1,12 +0,0 @@
#!/bin/bash
GAME_DIR="path/to/Tiny Life"
# move to the script directory
cd "$(dirname "$0")"
# build the mod
dotnet build
# copy the mod to the mods folder
cp ./bin/Debug/netcoreapp3.0/* "$LOCALAPPDATA/Tiny Life/Mods" -r
# run the game
cd "$GAME_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);