using System; using System.IO; using System.Text; using Newtonsoft.Json; using Omegasis.BuildHealth.Framework; using StardewModdingAPI; using StardewModdingAPI.Events; using StardewValley; namespace Omegasis.BuildHealth { /// The mod entry point. public class BuildHealth : Mod { /********* ** Properties *********/ /// The mod settings and player data. private ModConfig Config; /// The XP points needed to reach the next level. private double ExpToNextLevel = 20; /// The player's current XP points. private double CurrentExp; /// The player's current level. private int CurrentLevel; /// The health points to add to the player's base health due to their current level. private int CurrentLevelHealthBonus; /// The initial health bonus to apply regardless of the player's level, from the config file. private int BaseHealthBonus; /// Whether to reset all changes by the mod to the default values (i.e. start over). private bool ClearModEffects; /// The player's original max health value, excluding mod effects. private int OriginalMaxHealth; /// Whether the player recently gained XP for tool use. private bool HasRecentToolExp; /// Whether the player was eating last time we checked. private bool WasEating; /// The player's health last time we checked it. private int LastHealth; /// Whether the player has loaded a save. private bool IsLoaded; /// Whether the player has collapsed today. private bool WasCollapsed; /********* ** Public methods *********/ /// The mod entry point, called after the mod is first loaded. /// Provides simplified APIs for writing mods. public override void Entry(IModHelper helper) { GameEvents.UpdateTick += this.GameEvents_UpdateTick; GameEvents.OneSecondTick += this.GameEvents_OneSecondTick; TimeEvents.AfterDayStarted += this.TimeEvents_AfterDayStarted; SaveEvents.AfterLoad += this.SaveEvents_AfterLoaded; var configPath = Path.Combine(helper.DirectoryPath, "BuildHealthConfig.json"); if (!File.Exists(configPath)) { this.Config = new ModConfig { CurrentLevel = 0, MaxLevel = 100, HealthIncreasePerLevel = 1, CurrentExp = 0, ExpToNextLevel = 20, ExpCurve = 1.15, ExpForEating = 2, ExpForSleeping = 10, ExpForToolUse = 1, BaseHealthBonus = 0, CurrentLevelHealthBonus = 0 }; File.WriteAllBytes(configPath, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(this.Config))); } else { this.Config = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(File.ReadAllBytes(configPath))); this.Monitor.Log("Found BuildHealth config file."); } this.Monitor.Log("BuildHealth Initialization Completed"); } /// The method invoked once per second during a game update. /// The event sender. /// The event data. public void GameEvents_OneSecondTick(object sender, EventArgs e) { // nerf how quickly tool xp is gained (I hope) if (this.HasRecentToolExp) this.HasRecentToolExp = false; } /// The method invoked when the game updates (roughly 60 times per second). /// The event sender. /// The event data. public void GameEvents_UpdateTick(object sender, EventArgs e) { // give XP when player finishes eating if (Game1.isEating) this.WasEating = true; else if (this.WasEating) { this.CurrentExp += this.Config.ExpForEating; this.WasEating = false; } // give XP when player uses tool if (!this.HasRecentToolExp && Game1.player.usingTool) { this.CurrentExp += this.Config.ExpForToolUse; this.HasRecentToolExp = true; } // give XP for taking damage var player = Game1.player; if (this.LastHealth > player.health) { this.CurrentExp += this.LastHealth - player.health; this.LastHealth = player.health; } else if (this.LastHealth < player.health) this.LastHealth = player.health; // give XP when player stays up too late or collapses if (!this.WasCollapsed && Game1.farmerShouldPassOut) { this.CurrentExp += this.Config.ExpForCollapsing; this.WasCollapsed = true; this.Monitor.Log("The player has collapsed!"); } } /// The method invoked after a new day starts. /// The event sender. /// The event data. public void TimeEvents_AfterDayStarted(object sender, EventArgs e) { // reset data this.LastHealth = Game1.player.maxHealth; this.WasCollapsed = false; if (!this.IsLoaded) return; // update settings this.UpdateClearSetting(); var player = StardewValley.Game1.player; this.CurrentExp += this.Config.ExpForSleeping; if (this.OriginalMaxHealth == 0) this.OriginalMaxHealth = player.maxHealth; //grab the initial Health value if (this.ClearModEffects) { this.LoadClearSettings(); //This will run when the character goes to sleep. It will increase their sleeping skill. player.maxHealth = this.OriginalMaxHealth; this.ExpToNextLevel = this.Config.ExpToNextLevel; this.CurrentExp = this.Config.CurrentExp; this.CurrentLevelHealthBonus = 0; this.OriginalMaxHealth = player.maxHealth; this.BaseHealthBonus = 0; this.CurrentLevel = 0; this.Monitor.Log("BuildHealth Reset!"); } if (!this.ClearModEffects && this.CurrentLevel < this.Config.MaxLevel) { while (this.CurrentExp >= this.ExpToNextLevel) { this.CurrentLevel += 1; this.CurrentExp = this.CurrentExp - this.ExpToNextLevel; this.ExpToNextLevel = (this.Config.ExpCurve * this.ExpToNextLevel); player.maxHealth += this.Config.HealthIncreasePerLevel; this.CurrentLevelHealthBonus += this.Config.HealthIncreasePerLevel; } } this.ClearModEffects = false; this.WriteConfig(); } /// The method invoked after the player loads a save. /// The event sender. /// The event data. public void SaveEvents_AfterLoaded(object sender, EventArgs e) { // initialise this.LoadConfig(); this.WriteConfig(); this.IsLoaded = true; // grab initial health var player = Game1.player; if (this.OriginalMaxHealth == 0) this.OriginalMaxHealth = player.maxHealth; // set max health player.maxHealth = this.BaseHealthBonus + this.CurrentLevelHealthBonus + this.OriginalMaxHealth; // reset if needed if (this.ClearModEffects) { player.maxHealth = this.OriginalMaxHealth; this.Monitor.Log("BuildHealth Reset!"); } // save config this.LastHealth = Game1.player.maxHealth; this.LoadConfig(); this.WriteConfig(); } /// Update the settings needed for from the latest config file on disk. void LoadClearSettings() { if (!Directory.Exists(Path.Combine(Helper.DirectoryPath, "PlayerData"))) Directory.CreateDirectory(Path.Combine(Helper.DirectoryPath, "PlayerData")); string path = Path.Combine(Helper.DirectoryPath, "PlayerData", $"BuildHealth_data_{Game1.player.name}.txt"); if (!File.Exists(path)) { this.ClearModEffects = false; this.OriginalMaxHealth = 0; this.BaseHealthBonus = 0; } else { string[] text = File.ReadAllLines(path); this.BaseHealthBonus = Convert.ToInt32(text[9]); this.ClearModEffects = Convert.ToBoolean(text[14]); this.OriginalMaxHealth = Convert.ToInt32(text[16]); } } /// Update based on the latest config file on disk. private void UpdateClearSetting() { if (!Directory.Exists(Path.Combine(Helper.DirectoryPath, "PlayerData"))) Directory.CreateDirectory(Path.Combine(Helper.DirectoryPath, "PlayerData")); string path = Path.Combine(Helper.DirectoryPath, "PlayerData", $"BuildHealth_data_{Game1.player.name}.txt"); if (!File.Exists(path)) { this.ClearModEffects = false; this.OriginalMaxHealth = 0; this.BaseHealthBonus = 0; } else { string[] text = File.ReadAllLines(path); this.ClearModEffects = Convert.ToBoolean(text[14]); } } /// Load the configuration settings. private void LoadConfig() { if (!Directory.Exists(Path.Combine(Helper.DirectoryPath, "PlayerData"))) Directory.CreateDirectory(Path.Combine(Helper.DirectoryPath, "PlayerData")); string path = Path.Combine(Helper.DirectoryPath, "PlayerData", $"BuildHealth_data_{Game1.player.name}.txr"); if (!File.Exists(path)) { this.ExpToNextLevel = this.Config.ExpToNextLevel; this.CurrentExp = this.Config.CurrentExp; this.CurrentLevel = this.Config.CurrentLevel; this.BaseHealthBonus = this.Config.BaseHealthBonus; this.CurrentLevelHealthBonus = this.Config.CurrentLevelHealthBonus; this.ClearModEffects = false; this.OriginalMaxHealth = 0; } else { string[] text = File.ReadAllLines(path); this.CurrentLevel = Convert.ToInt32(text[3]); this.ExpToNextLevel = Convert.ToDouble(text[7]); this.CurrentExp = Convert.ToDouble(text[5]); this.BaseHealthBonus = Convert.ToInt32(text[9]); this.CurrentLevelHealthBonus = Convert.ToInt32(text[11]); this.ClearModEffects = Convert.ToBoolean(text[14]); this.OriginalMaxHealth = Convert.ToInt32(text[16]); } } /// Save the configuration settings. private void WriteConfig() { if (!Directory.Exists(Path.Combine(Helper.DirectoryPath, "PlayerData"))) Directory.CreateDirectory(Path.Combine(Helper.DirectoryPath, "PlayerData")); string path = Path.Combine(Helper.DirectoryPath, "PlayerData", $"BuildHealth_data_{Game1.player.name}.txt"); string[] text = new string[20]; text[0] = "Player: Build Health Data. Modification can cause errors. Edit at your own risk."; text[1] = "===================================================================================="; text[2] = "Player Current Level:"; text[3] = this.CurrentLevel.ToString(); text[4] = "Player Current XP:"; text[5] = this.CurrentExp.ToString(); text[6] = "Xp to next Level:"; text[7] = this.ExpToNextLevel.ToString(); text[8] = "Initial Health Bonus:"; text[9] = this.BaseHealthBonus.ToString(); text[10] = "Additional Health Bonus:"; text[11] = this.CurrentLevelHealthBonus.ToString(); text[12] = "======================================================================================="; text[13] = "RESET ALL MOD EFFECTS? This will effective start you back at square 1. Also good if you want to remove this mod."; text[14] = this.ClearModEffects.ToString(); text[15] = "OLD Health AMOUNT: This is the initial value of the Player's Health before this mod took over."; text[16] = this.OriginalMaxHealth.ToString(); File.WriteAllLines(path, text); } } }