2020-11-25 00:33:47 +01:00
using System.Collections.Generic ;
2021-05-22 18:14:05 +02:00
using System.Linq ;
2020-11-25 00:33:47 +01:00
using ExtremelySimpleLogger ;
using MLEM.Data ;
using MLEM.Data.Content ;
2020-11-28 17:05:46 +01:00
using MLEM.Textures ;
2022-09-13 14:12:56 +02:00
using MLEM.Ui ;
using MLEM.Ui.Elements ;
2020-11-25 00:33:47 +01:00
using TinyLife ;
2021-05-22 17:39:17 +02:00
using TinyLife.Actions ;
using TinyLife.Emotions ;
2020-11-25 00:33:47 +01:00
using TinyLife.Mods ;
using TinyLife.Objects ;
using TinyLife.Utilities ;
2022-04-19 20:38:25 +02:00
using TinyLife.World ;
2020-11-25 00:33:47 +01:00
2021-12-25 18:05:47 +01:00
namespace ExampleMod ;
2020-11-25 00:33:47 +01:00
2021-12-23 23:23:56 +01:00
public class ExampleMod : Mod {
2020-11-25 00:33:47 +01:00
2021-12-23 23:23:56 +01:00
// the logger that we can use to log info about this mod
public static Logger Logger { get ; private set ; }
2022-11-22 19:25:15 +01:00
public static ExampleModOptions Options { get ; private set ; }
2021-05-22 17:39:17 +02:00
2021-12-23 23:23:56 +01:00
public static EmotionModifier GrassSittingModifier { get ; private set ; }
2020-11-25 00:33:47 +01:00
2021-12-23 23:23:56 +01:00
// visual data about this mod
public override string Name = > "Example Mod" ;
public override string Description = > "This is the example mod for Tiny Life!" ;
2022-12-20 13:24:55 +01:00
public override TextureRegion Icon = > this . uiTextures [ new Point ( 0 , 0 ) ] ;
2020-11-25 00:33:47 +01:00
2022-12-20 13:24:55 +01:00
private Dictionary < Point , TextureRegion > customTops ;
private Dictionary < Point , TextureRegion > customHairs ;
private Dictionary < Point , TextureRegion > customBottoms ;
private Dictionary < Point , TextureRegion > uiTextures ;
2022-05-26 13:20:57 +02:00
private Dictionary < Point , TextureRegion > wallpaperTextures ;
2020-11-28 17:05:46 +01:00
2022-03-09 16:12:57 +01:00
public override void AddGameContent ( GameImpl game , ModInfo info ) {
2021-12-23 23:23:56 +01:00
// 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
2022-03-21 18:56:23 +01:00
// if this is not specified, the Furniture class is used, which is used for furniture without special animations or data
2022-11-22 19:25:15 +01:00
ConstructedType = typeof ( ExampleModTable ) ,
2021-12-23 23:23:56 +01:00
// 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 ( )
} ) ;
2021-05-17 23:35:34 +02:00
2021-12-23 23:23:56 +01:00
// adding custom clothing
var darkShirt = new Clothes ( "ExampleMod.DarkShirt" , ClothesLayer . Shirt ,
2023-04-24 20:19:46 +02:00
// 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 color scheme
// if the item should have multiple layers, multiple color schemes can be supplied here (see docs above)
ColorScheme . WarmDark
) { Icon = this . Icon } ;
2021-12-23 23:23:56 +01:00
Clothes . Register ( darkShirt ) ;
// adding some more custom clothing
2022-12-20 13:24:55 +01:00
Clothes . Register ( new Clothes ( "ExampleMod.PastelPants" , ClothesLayer . Pants , this . customBottoms , new Point ( 4 , 0 ) , 100 , ClothesIntention . Everyday , ColorScheme . Pastel ) { Icon = this . Icon } ) ;
Clothes . Register ( new Clothes ( "ExampleMod.PastelShoes" , ClothesLayer . Shoes , this . customBottoms , new Point ( 0 , 0 ) , 100 , ClothesIntention . Everyday , ColorScheme . Pastel ) { Icon = this . Icon } ) ;
Clothes . Register ( new Clothes ( "ExampleMod.WeirdHair" , ClothesLayer . Hair , this . customHairs , new Point ( 0 , 0 ) , 0 , ClothesIntention . None , ColorScheme . Modern ) { Icon = this . Icon } ) ;
2021-05-22 17:39:17 +02:00
2021-12-23 23:23:56 +01:00
// 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 ) = > {
2022-01-01 17:06:02 +01:00
if ( person . CurrentOutfit . Clothes . TryGetValue ( ClothesLayer . Shirt , out var shirt ) & & shirt . Type = = darkShirt )
2022-09-13 14:12:56 +02:00
s * = ExampleMod . Options . DarkShirtSpeedIncrease ;
2021-12-23 23:23:56 +01:00
} ;
}
} ;
2020-11-25 00:33:47 +01:00
2021-12-23 23:23:56 +01:00
// adding a simple action: sitting down in the grass, which also gives us a nice emotion modifier
2022-11-22 19:25:15 +01:00
ActionType . Register ( new ActionType . TypeSettings ( "ExampleMod.SitOnGrass" , ObjectCategory . Ground , typeof ( ExampleModGrassSitAction ) ) {
2021-12-23 23:23:56 +01:00
// we set this action to be executable only on grass tiles, not on other ground
2022-08-18 12:30:04 +02:00
CanExecute = ( actionInfo , _ ) = > {
2022-03-21 18:56:23 +01:00
if ( ! actionInfo . Map . IsInBounds ( actionInfo . ActionLocation . ToPoint ( ) ) )
2022-05-24 13:35:56 +02:00
return CanExecuteResult . Hidden ;
2022-03-21 18:56:23 +01:00
var tile = actionInfo . Map . GetTile ( actionInfo . ActionLocation . ToPoint ( ) ) ;
// 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
2022-05-24 13:35:56 +02:00
return tile . Name . StartsWith ( "Grass" ) ? CanExecuteResult . Valid : CanExecuteResult . Hidden ;
2021-12-23 23:23:56 +01:00
} ,
Ai = {
// we allow the action to be done even if the solved needs aren't low enough on a person
CanDoRandomly = true ,
2022-03-21 18:56:23 +01:00
// the solved needs indicate when the AI should mark this action as important, they don't actually have to match the action's behavior
2021-12-23 23:23:56 +01:00
SolvedNeeds = new [ ] { NeedType . Energy } ,
2022-09-13 14:12:56 +02:00
// make people more likely to sit down in the grass if they're uncomfortable
2021-12-23 23:23:56 +01:00
PassivePriority = p = > p . Emotion = = EmotionType . Uncomfortable ? 150 : 25
} ,
2022-09-13 14:12:56 +02:00
// since this action doesn't use objects (like chairs etc.), we set a texture to display instead
2022-12-20 13:24:55 +01:00
Texture = this . uiTextures [ new Point ( 1 , 0 ) ]
2021-12-23 23:23:56 +01:00
} ) ;
2022-04-11 14:26:07 +02:00
2022-03-21 18:56:23 +01:00
// we use this emotion modifier in SitDownOnGrassAction
2022-06-15 10:53:51 +02:00
ExampleMod . GrassSittingModifier = EmotionModifier . Register (
2022-12-20 13:24:55 +01:00
new EmotionModifier ( "ExampleMod.GrassSitting" , this . uiTextures [ new Point ( 1 , 0 ) ] , EmotionType . Happy ) ) ;
2022-04-19 20:38:25 +02:00
// 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 ) ;
2021-12-23 23:23:56 +01:00
}
2021-02-18 19:12:01 +01:00
2022-03-09 16:12:57 +01:00
public override void Initialize ( Logger logger , RawContentManager content , RuntimeTexturePacker texturePacker , ModInfo info ) {
2022-06-15 10:53:51 +02:00
ExampleMod . Logger = logger ;
2022-11-22 19:25:15 +01:00
ExampleMod . Options = info . LoadOptions ( ( ) = > new ExampleModOptions ( ) ) ;
2020-11-25 00:33:47 +01:00
2021-12-23 23:23:56 +01:00
// 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
2023-04-24 11:44:11 +02:00
texturePacker . Add ( new UniformTextureAtlas ( content . Load < Texture2D > ( "CustomTops" ) , 4 , 11 ) , r = > this . customTops = r ) ;
texturePacker . Add ( new UniformTextureAtlas ( content . Load < Texture2D > ( "CustomHairs" ) , 4 , 5 ) , r = > this . customHairs = r ) ;
2022-12-20 13:24:55 +01:00
texturePacker . Add ( new UniformTextureAtlas ( content . Load < Texture2D > ( "CustomBottomsShoes" ) , 8 , 6 ) , r = > this . customBottoms = r ) ;
texturePacker . Add ( new UniformTextureAtlas ( content . Load < Texture2D > ( "UiTextures" ) , 8 , 8 ) , r = > this . uiTextures = r ) ;
2022-04-19 20:38:25 +02:00
// wallpaper textures require special treatment to work with openings, the x and y values are passed to the UniformTextureAtlas constructor
2022-05-24 13:35:56 +02:00
WallMode . ApplyMasks ( content . Load < Texture2D > ( "Wallpapers" ) , 4 , 5 , texturePacker , r = > this . wallpaperTextures = r ) ;
2021-12-23 23:23:56 +01:00
}
2020-11-25 00:33:47 +01:00
2022-03-09 16:12:57 +01:00
public override IEnumerable < string > GetCustomFurnitureTextures ( ModInfo info ) {
2021-12-23 23:23:56 +01:00
// 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" ;
2020-11-25 00:33:47 +01:00
}
2021-12-23 23:23:56 +01:00
2022-09-13 14:12:56 +02:00
// 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}" ) ) ;
2022-12-23 23:40:21 +01:00
group . AddChild ( new Slider ( Anchor . AutoLeft , new Vector2 ( 1 , 10 ) , 5 , 5 ) {
2022-09-13 14:12:56 +02:00
CurrentValue = ExampleMod . Options . DarkShirtSpeedIncrease ,
OnValueChanged = ( _ , v ) = > {
ExampleMod . Options . DarkShirtSpeedIncrease = v ;
2022-09-17 12:33:20 +02:00
info . SaveOptions ( ExampleMod . Options ) ;
2022-09-13 14:12:56 +02:00
}
} ) ;
}
2022-06-17 18:05:34 +02:00
}