Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Max Health and Mana Manipulation API #2909

Merged
merged 63 commits into from
Sep 23, 2022

Conversation

absoluteAquarian
Copy link
Contributor

@absoluteAquarian absoluteAquarian commented Sep 9, 2022

What is the new feature?

This PR aims to make modifying the player's maximum health and mana, as well as their displays, more friendly to mod developers.

Features include:

  • a proper API for modifying Player.statLifeMax and Player.statManaMax
  • being able to hook into the vanilla drawing for the hearts, stars and bars in the vanilla resource display sets via ModResourceOverlay
  • allowing mod developers to define their own display sets, which can be chosen from the Settings menu, via ModResourceDisplaySet
    • choosing ModResourceDisplaySet displays is handled via the pre-existing option in the ingame Settings>Video menu
    • consequently, modded display sets are also saved to config.json
  • removal of any code which limited the values of Player.statLifeMax and Player.statManaMax
  • several new properties in Player for tracking how many Life Crystals, Life Fruit and Mana Crystals have been consumed
    • Player.ConsumedLifeCrystals
    • Player.ConsumedLifeFruit
    • Player.ConsumedManaCrystals
  • a new helper method for increasing the player's health/mana and displaying the combat text via Player.UseHealthMaxIncreasingItem() and Player.UseManaMaxIncreasingItem()

Why should this be part of tModLoader?

Modifying the player's maximum health and mana currently requires using Player.statLifeMax2 and Player.statManaMax2, which -- even though it works -- is clunky and goes against the purpose of those fields: "temporary" increases/decreases.

Furthermore, modifying how vanilla's hearts/stars/bars for health and mana are drawn is cumbersome, requiring a method detour or IL edit to be used, which can cause conflicts when multiple mods try to do so at once.

As a side note, any legacy usage of modifying Player.statLifeMax2 and Player.statManaMax2 will still be supported, but mod developers should be encouraged to move to the new API.

Are there alternative designs?

n/a

Sample usage for the new feature

Modifying the player's maximum health:

// source:  Common/Players/ExampleStatIncreasePlayer.cs
// NOTE: The full source contains code for handling increasing the player's max mana as well.
//       It has been reduced for brevity's sake.

public int exampleLifeFruits;

public override void ModifyMaxStats(out StatModifier health, out StatModifier mana) {
	health = StatModifier.Default;
	health.Base = exampleLifeFruits * ExampleLifeFruit.LifePerFruit;
	// Alternatively:  health = StatModifier.Default with { Base = exampleLifeFruits * ExampleLifeFruit.LifePerFruit };
	mana = StatModifier.Default;
}

Drawing custom hearts over the hearts in the Classic resource display set:

// source:  Common/UI/ResourceOverlay/VanillaLifeOverlay.cs
// NOTE: The full source contains code for handling the Fancy and Bars display sets.
//       It has been reduced for brevity's sake.

public override void PostDrawResource(ResourceOverlayDrawContext context) {
	// Hearts are replaced in groups of two
	if (context.resourceNumber > 2 * Main.LocalPlayer.GetModPlayer<ExampleLifeFruitPlayer>().exampleLifeFruits)
		return;

	Asset<Texture2D> asset = context.texture;

	// "context" contains information used to draw the resource
	// If you want to draw directly on top of the vanilla hearts, just replace the texture and have the context draw the new texture
	if (asset == TextureAssets.Heart || asset == TextureAssets.Heart2) {
		context.texture = ModContent.Request<Texture2D>("ExampleMod/Common/UI/ResourceOverlay/ClassicHeartOverlay", AssetRequestMode.ImmediateLoad);
		context.Draw();
	}
}

ExampleMod updates

  • Content/Items/Consumables/ExampleLifeFruit.cs was updated to use the new API
  • added Content/Items/Consumables/ExampleManaCrystal.cs, which serves as an example mana increasing item
  • added the Common/UI/ResourceOverlay folder, which contains examples of modifying vanilla's resource display sets
  • added the Common/UI/ExampleDisplaySets folder, which contains an example of using ModResourceDisplaySet
  • ExampleLifeFruitPlayer was renamed to ExampleStatIncreasePlayer and was moved to Common/Players
  • updated the netcode handling in ExampleMod.Networking.cs to account for the new ExampleManaCrystal stats
  • updated the en-US.hjson localization file to add an entry for ExampleReversedBarsDisplay (the ModResourceDisplaySet example)

Additional Information

  • the netcode handling in ModLoader/Default/ModLoaderMod.cs was modified to be more friendly to future additions
    • consequently, the netcode for ModAccessorySlotPlayer was also modified
  • a lot of vanilla code that was checking Player.statLifeMax and/or Player.statManaMax were changed to check the Player.ConsumedLifeCrystals, Player.ConsumedLifeFruit and Player.ConsumedManaCrystals properties instead, since that's what was really happening in the first place
  • added two new AmountOfLifeHearts and AmountOfManaStars properties to PlayerStatsSnapshot and changed the LifePerSegment and ManaPerSegment fields to be getter properties dependent on these new properties
  • PlayerResourceSetsManager (aka Main.ResourceSetsManager) was expanded to include ModResourceDisplaySet instances
    • assuming that the ModResourceDisplaySet was loaded, you can get the instance via Main.ResourceSetsManager.GetDisplaySet<T>()
    • you can also get the instance via Main.ResourceSetsManager.GetDisplaySet("ModName/Name")
  • usage of IPlayerResourceDisplaySet.NameKey was removed in favor of a DisplayedName getter property, though the property still exists for whoever might want to use it

- initial changes to the health/mana API
* added a helper function in Player for using maximum health- and mana-increasing items
* modified the AchivementHelper code in the event that achievements are enabled in the future
* added fields in Player indicating how many Life Crystals, Life Fruit and Mana Crystals they have consumed
* updated ExampleMod to account for the new API
! This PR is not complete.  UI still needs to be addressed as well as some other things.

- modified ExampleLifeFruit to better mimic how Life Fruits work

- better comment in ExampleLifeFruit

- better comment 2
- also modified all vanilla uses of checking if statLifeMax or statManaMax were above/below a certain number with checks for consumed item counts in order to properly account for the new API
- updated ExampleLifeFruitPlayer to account for the new API
- removed ModifyBaseMaxStats, PreModifyMaxStats and PostModifyMaxStats in favor of a StatModifier hook
- made the setters for ConsumedLifeCrystals, ConsumedLifeFruit and ConsumedManaCrystals public for better API accessibility (a mod may want to just set these directly)
- added code to save/load ConsumedLifeCrystals, ConsumedLifeFruit and ConsumedManaCrystals
- changed ExampleLifeFruit's usage of ModifyMaxStats to be less confusing
- moved duplicated code to a PlayerLoader helper method, ResetMaxStatsToVanilla
- added Utils.Clamp usage
- clamped the values for the ConsumedLifeCrystals, ConsumeLifeFruit and ConsumedManaCrystals properties
- added/updated XML summaries
… still TODO)

- also removed the sets added to ItemID due to them not having a use
- updated ExampleMod with examples of drawing with an overlay for the Classic display set
- fixed a copy/paste typo
- all existing `ModResourceOverlay` hooks were converted into more "general" Resource hooks
- added `IResourceDrawSource` to `ResourceOverlayDrawContext`
- updated ExampleMod
- updated XML summaries
- added missing `IResourceDrawSource` classes for the panel drawing
- fixed a bug in `PlayerIO` which prevented the ConsumedX properties in Player to save properly
- deserializing `null` as a `TagCompound` actually results in an empty tag, so i added a proper check in `PlayerIO` for when the fallback system to load the ConsumedX properties should be used
- `ModResourceOverlay` and `ResourceOverlayLoader` API changes
- the `AmountOfHearts` property in `PlayerStatsSnapshot` was renamed to `AmountOfLifeHearts`
- added helper methods to `ResourceOverlayLoader` for drawing resources for modder convenience
… ExampleLifeFruit + added the sprite for ExampleManaCrystal
…d.HandlePacket to be more friendly to additional cases in the future
- made ExampleLifeFruit actually use its texture
- renamed VanillaHealthOverlay to VanillaLifeOverlay
- renamed the "ClassicHealthOverlay" image to "ClassicLifeOverlay"
- renamed the "FancyHealthOverlay_X" images to "FancyLifeOverlay_X"
- added ExampleManaCrystal
- added VanillaManaOverlay
- renamed ExampleLifeFruitPlayer to ExampleStatIncreasePlayer and moved it to `Common/Players`
- renamed ExampleLifeFruitSystem to ExampleStatIncreaseSystem and moved it to `Common/Systems`
- made VanillaLifeOverlay "replace" the bars from right to left
- fixed some bugs caused by `float` precision limits
- properly implemented PreDrawResourceDisplay and PostDrawResourceDisplay in the Fancy and Bars display sets
- expanded PlayerResourceSetsManager to include modded display sets
- updated XML summaries
@absoluteAquarian absoluteAquarian marked this pull request as ready for review September 12, 2022 18:38
- moved the code reponsible for adding the modded display sets to `Main.ResourceSetsManager` to before `Mod.SetupContent()` is called so that they're available for use in `Mod.PostSetupContent()` and onwards
- made the Classic display set be "registered" after the Bars display set
- changed the logic of `PlayerResourceSetsManager.CycleResourceSet()` to go from first to last instead of last to first
- removed `ResourceSlotId` in favor of just checking asset instances
- added a new `ResourceStat` property to `ResourceOverlayDrawContext`
- fixed a bug that caused the display cycling to get stuck between Classic and Swapped Bars
- modified ExampleMod to account for the new changes
- updated XML comments
Copy link
Member

@Chicken-Bones Chicken-Bones left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incredibly, I think this is the only change still required

ExampleMod/Common/UI/ResourceOverlay/VanillaLifeOverlay.cs Outdated Show resolved Hide resolved
- made `ResourceOverlayDrawContext.resourceNumber` start at 0 by default
- made the Bars panel drawing have `resourceNumber` start at -1
- added `ResourceIndexOffset` to `ResourceDrawSettings`
- actually tested the changed display set cycling code
- various bug fixes
@Chicken-Bones Chicken-Bones changed the title [1.4] Max Health and Mana Manipulation API Max Health and Mana Manipulation API Sep 23, 2022
@Chicken-Bones Chicken-Bones merged commit f569dc9 into tModLoader:1.4 Sep 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Benefit - Medium Complexity - High Feature - Hook Request for new modding functionality Requestor-Modders Issues or PRs adding or fixing TML modder API
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants