Skip to content

Commit

Permalink
[ExampleMod] Reorganize ModConfigShowcases (tModLoader#2527)
Browse files Browse the repository at this point in the history
* [ExampleMod] Reorganize ModConfigShowcases

* Split ModConfigShowcases file into separate files (one file per each config section)

* Move custom data types from ModConfigShowcases to CustomDataTypes namespace (except Corner data type that is present in CornetElement class)

* Move custom UI (ConfigElements) from ModConfigShowcases to CustomDataTypes namespace

* Move ModConfigShowcases to separate folder
  • Loading branch information
NothyariS0 authored Jun 9, 2022
1 parent 991558a commit 75e2a87
Show file tree
Hide file tree
Showing 17 changed files with 913 additions and 755 deletions.
49 changes: 49 additions & 0 deletions ExampleMod/Common/Configs/CustomDataTypes/ClassUsedAsKey.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using System.ComponentModel;
using Terraria.ModLoader.Config;

// This file defines a custom data type that can be used as a key in dictionary.
namespace ExampleMod.Common.Configs.CustomDataTypes
{
[TypeConverter(typeof(ToFromStringConverter<ClassUsedAsKey>))]
public class ClassUsedAsKey
{
// When you save data from a dictionary into a file (json), you need to represent the key as a string
// But to get the object back, you need a TypeConverter, and this example shows how to implement one

// You start with the [TypeConverter(typeof(ToFromStringConverter<NameOfClassHere>))] attribute above the class
// For this to work, you need the usual Equals and GetHashCode overrides as explained in the other examples,
// plus ToString and FromString, which are used to transform your object into a string and back

public bool SomeBool { get; set; }
public int SomeNumber { get; set; }

public override bool Equals(object obj) {
if (obj is ClassUsedAsKey other)
return SomeBool == other.SomeBool && SomeNumber == other.SomeNumber;
return base.Equals(obj);
}

public override int GetHashCode() {
return new { SomeBool, SomeNumber }.GetHashCode();
}

// Here you need to write how the string representation of your object will look like so it is easy to reconstruct again
// Inside the json file, it will look something like this: "True, 5"
public override string ToString() {
return $"{SomeBool}, {SomeNumber}";
}

// Here you need to create an object from the given string (reverting ToString basically)
// This has to be static and it must be named FromString
public static ClassUsedAsKey FromString(string s) {
// This following code depends on your implementation of ToString, here we just have two values separated by a ','
string[] vars = s.Split(new char[] { ',' }, 2, StringSplitOptions.RemoveEmptyEntries);
// The System.Convert class provides methods to transform data types between each other, here using the string overload
return new ClassUsedAsKey {
SomeBool = Convert.ToBoolean(vars[0]),
SomeNumber = Convert.ToInt32(vars[1])
};
}
}
}
31 changes: 31 additions & 0 deletions ExampleMod/Common/Configs/CustomDataTypes/ComplexData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Terraria.ModLoader.Config;

// This file defines custom data type that contains variety of other data types and can be used in ModConfig classes.
namespace ExampleMod.Common.Configs.CustomDataTypes
{
public class ComplexData
{
public List<int> ListOfInts = new List<int>();

public SimpleData nestedSimple = new SimpleData();

[Range(2f, 3f)]
[Increment(.25f)]
[DrawTicks]
[DefaultValue(2f)]
public float IncrementalFloat = 2f;
public override bool Equals(object obj) {
if (obj is ComplexData other)
return ListOfInts.SequenceEqual(other.ListOfInts) && IncrementalFloat == other.IncrementalFloat && nestedSimple.Equals(other.nestedSimple);
return base.Equals(obj);
}

public override int GetHashCode() {
return new { ListOfInts, nestedSimple, IncrementalFloat }.GetHashCode();
}
}
}
27 changes: 27 additions & 0 deletions ExampleMod/Common/Configs/CustomDataTypes/Gradient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Microsoft.Xna.Framework;
using System.ComponentModel;
using Terraria.ModLoader.Config;

// This file defines custom data type that represents Gradient data type that can be used in ModConfig classes.
namespace ExampleMod.Common.Configs.CustomDataTypes
{
public class Gradient
{
[Tooltip("The color the gradient starts at")]
[DefaultValue(typeof(Color), "0, 0, 255, 255")]
public Color start = Color.Blue; // For sub-objects, you'll want to make sure to set defaults in constructor or field initializer.
[Tooltip("The color the gradient ends at")]
[DefaultValue(typeof(Color), "255, 0, 0, 255")]
public Color end = Color.Red;

public override bool Equals(object obj) {
if (obj is Gradient other)
return start == other.start && end == other.end;
return base.Equals(obj);
}

public override int GetHashCode() {
return new { start, end }.GetHashCode();
}
}
}
29 changes: 29 additions & 0 deletions ExampleMod/Common/Configs/CustomDataTypes/Pair.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Terraria.ModLoader.Config;

// This file defines custom simple data type with two fields - boolean value and integer value.
namespace ExampleMod.Common.Configs.CustomDataTypes
{
[BackgroundColor(0, 255, 255)]
[Label("Pair label")]
public class Pair
{
public bool enabled;
public int boost;

// If you override ToString, it will show up appended to the Label in the ModConfig UI.
public override string ToString() {
return $"Boost: {(enabled ? "" + boost : "disabled")}";
}

// Implementing Equals and GetHashCode are critical for any classes you use.
public override bool Equals(object obj) {
if (obj is Pair other)
return enabled == other.enabled && boost == other.boost;
return base.Equals(obj);
}

public override int GetHashCode() {
return new { boost, enabled }.GetHashCode();
}
}
}
16 changes: 16 additions & 0 deletions ExampleMod/Common/Configs/CustomDataTypes/SampleEnum.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Terraria.ModLoader.Config;

// This file defines an enum data type that can be used in ModConfig classes.
namespace ExampleMod.Common.Configs.CustomDataTypes
{
public enum SampleEnum
{
Weird,
Odd,
// Enum members can be individually labeled as well
[Label("Strange Label")]
Strange,
[Label("$Mods.ExampleMod.Config.SampleEnumLabels.Peculiar")]
Peculiar
}
}
37 changes: 37 additions & 0 deletions ExampleMod/Common/Configs/CustomDataTypes/SimpleData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System.ComponentModel;
using Terraria.ModLoader.Config;

// This file defines custom data type that contains variety of simple data types
// and can be used in ModConfig classes.
namespace ExampleMod.Common.Configs.CustomDataTypes
{
[BackgroundColor(255, 7, 7)]
public class SimpleData
{
[Header("Awesome")]
public int boost;
public float percent;

[Header("Lame")]
public bool enabled;

[DrawTicks]
[OptionStrings(new string[] { "Pikachu", "Charmander", "Bulbasaur", "Squirtle" })]
[DefaultValue("Bulbasaur")]
public string FavoritePokemon;

public SimpleData() {
FavoritePokemon = "Bulbasaur";
}

public override bool Equals(object obj) {
if (obj is SimpleData other)
return boost == other.boost && percent == other.percent && enabled == other.enabled && FavoritePokemon == other.FavoritePokemon;
return base.Equals(obj);
}

public override int GetHashCode() {
return new { boost, percent, enabled, FavoritePokemon }.GetHashCode();
}
}
}
74 changes: 74 additions & 0 deletions ExampleMod/Common/Configs/CustomUI/CornerElement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System;
using Terraria;
using Terraria.GameContent;
using Terraria.ModLoader.Config;
using Terraria.ModLoader.Config.UI;
using Terraria.UI;

// ATTENTION: Below this point is custom config UI element.
// Be aware that mods using custom config elements will break with the next few tModLoader updates until their design is finalized.
// You will need to be very active in updating your mod if you use these as they can break in any update.

// This file defines a custom ConfigElement based on Corner enum
// with custom drawing implemented that can be used in ModConfig classes.
namespace ExampleMod.Common.Configs.CustomUI
{
// This custom config UI element shows a completely custom config element that handles setting and getting the values in addition to custom drawing.
[JsonConverter(typeof(StringEnumConverter))]
[CustomModConfigItem(typeof(CornerElement))]
public enum Corner
{
TopLeft,
TopRight,
BottomLeft,
BottomRight
}

class CornerElement : ConfigElement
{
Texture2D circleTexture;
string[] valueStrings;

public override void OnBind() {
base.OnBind();
circleTexture = Main.Assets.Request<Texture2D>("Images/UI/Settings_Toggle", ReLogic.Content.AssetRequestMode.ImmediateLoad).Value;
valueStrings = Enum.GetNames(MemberInfo.Type);
TextDisplayFunction = () => MemberInfo.Name + ": " + GetStringValue();
if (LabelAttribute != null) {
TextDisplayFunction = () => LabelAttribute.Label + ": " + GetStringValue();
}
}

void SetValue(Corner value) => SetObject(value);

Corner GetValue() => (Corner)GetObject();

string GetStringValue() {
return valueStrings[(int)GetValue()];
}

public override void Click(UIMouseEvent evt) {
base.Click(evt);
SetValue(GetValue().NextEnum());
}

public override void RightClick(UIMouseEvent evt) {
base.RightClick(evt);
SetValue(GetValue().PreviousEnum());
}

public override void Draw(SpriteBatch spriteBatch) {
base.Draw(spriteBatch);
CalculatedStyle dimensions = GetDimensions();
var circleSourceRectangle = new Rectangle(0, 0, (circleTexture.Width - 2) / 2, circleTexture.Height);
spriteBatch.Draw(TextureAssets.MagicPixel.Value, new Rectangle((int)(dimensions.X + dimensions.Width - 25), (int)(dimensions.Y + 4), 22, 22), Color.LightGreen);
Corner corner = GetValue();
var circlePositionOffset = new Vector2((int)corner % 2 * 8, (int)corner / 2 * 8);
spriteBatch.Draw(circleTexture, new Vector2(dimensions.X + dimensions.Width - 25, dimensions.Y + 4) + circlePositionOffset, circleSourceRectangle, Color.White);
}
}
}
20 changes: 20 additions & 0 deletions ExampleMod/Common/Configs/CustomUI/CustomFloatElement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.ModLoader.Config.UI;

// ATTENTION: Below this point is custom config UI element.
// Be aware that mods using custom config elements will break with the next few tModLoader updates until their design is finalized.
// You will need to be very active in updating your mod if you use these as they can break in any update.

// This file defines a custom ConfigElement based on Float data type
// with custom slider coloring method implemented that can be used in ModConfig classes.
namespace ExampleMod.Common.Configs.CustomUI
{

class CustomFloatElement : FloatElement
{
public CustomFloatElement() {
ColorMethod = new Utils.ColorLerpMethod((percent) => Color.Lerp(Color.BlueViolet, Color.Aquamarine, percent));
}
}
}
65 changes: 65 additions & 0 deletions ExampleMod/Common/Configs/CustomUI/GradientElement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Newtonsoft.Json;
using System;
using Terraria.GameContent;
using Terraria.ModLoader.Config;
using Terraria.ModLoader.Config.UI;
using ExampleMod.Common.Configs.CustomDataTypes;

// ATTENTION: Below this point is custom config UI element.
// Be aware that mods using custom config elements will break with the next few tModLoader updates until their design is finalized.
// You will need to be very active in updating your mod if you use these as they can break in any update.

// This file defines a custom ConfigElement based on Gradient data type
// with custom drawing implemented that can be used in ModConfig classes.
namespace ExampleMod.Common.Configs.CustomUI
{
// This custom config UI element uses vanilla config elements paired with custom drawing.
class GradientElement : ConfigElement
{
public override void OnBind() {
base.OnBind();

object subitem = MemberInfo.GetValue(Item);

if (subitem == null) {
subitem = Activator.CreateInstance(MemberInfo.Type);
JsonConvert.PopulateObject("{}", subitem, ConfigManager.serializerSettings);
MemberInfo.SetValue(Item, subitem);
}

// item is the owner object instance, memberinfo is the Info about this field in item

int height = 30;
int order = 0;

foreach (PropertyFieldWrapper variable in ConfigManager.GetFieldsAndProperties(subitem)) {
var wrapped = ConfigManager.WrapIt(this, ref height, variable, subitem, order++);

if (List != null) {
wrapped.Item1.Left.Pixels -= 20;
wrapped.Item1.Width.Pixels += 20;
}
}
}

public override void Draw(SpriteBatch spriteBatch) {
base.Draw(spriteBatch);
var hitbox = GetInnerDimensions().ToRectangle();
var g = MemberInfo.GetValue(Item) as Gradient;
if (g != null) {
int left = (hitbox.Left + hitbox.Right) / 2;
int right = hitbox.Right;
int steps = right - left;
for (int i = 0; i < steps; i += 1) {
float percent = (float)i / steps;
spriteBatch.Draw(TextureAssets.MagicPixel.Value, new Rectangle(left + i, hitbox.Y, 1, 30), Color.Lerp(g.start, g.end, percent));
}

//Main.spriteBatch.Draw(TextureAssets.MagicPixel.Value, new Rectangle(hitbox.X + hitbox.Width / 2, hitbox.Y, hitbox.Width / 4, 30), g.start);
//Main.spriteBatch.Draw(TextureAssets.MagicPixel.Value, new Rectangle(hitbox.X + 3 * hitbox.Width / 4, hitbox.Y, hitbox.Width / 4, 30), g.end);
}
}
}
}
Loading

0 comments on commit 75e2a87

Please sign in to comment.