diff --git a/Swim/AbigailProjectile.cs b/Swim/AbigailProjectile.cs index 5bf55cdc..0d1bdbb1 100644 --- a/Swim/AbigailProjectile.cs +++ b/Swim/AbigailProjectile.cs @@ -1,4 +1,5 @@ using Microsoft.Xna.Framework; +using Newtonsoft.Json.Linq; using StardewValley; using StardewValley.Monsters; using StardewValley.Network; @@ -12,13 +13,12 @@ public class AbigailProjectile : BasicProjectile private string myCollisionSound; private bool myExplode; - public AbigailProjectile(int damageToFarmer, int ParentSheetIndex, int bouncesTillDestruct, int tailLength, float rotationVelocity, float xVelocity, float yVelocity, Vector2 startingPosition, string collisionSound, string bounceSound, string firingSound, bool explode, bool damagesMonsters = false, GameLocation location = null, Character firer = null, onCollisionBehavior collisionBehavior = null, string shotItemId = null) : base(damageToFarmer, ParentSheetIndex, bouncesTillDestruct, tailLength, rotationVelocity, xVelocity, yVelocity, startingPosition, collisionSound, bounceSound, firingSound, explode, true, location, firer, null, null) + public AbigailProjectile(int damageToFarmer, int ParentSheetIndex, int bouncesTillDestruct, int tailLength, float rotationVelocity, float xVelocity, float yVelocity, Vector2 startingPosition, string collisionSound, string bounceSound, string firingSound, bool explode, bool damagesMonsters = false, GameLocation location = null, Character firer = null, onCollisionBehavior collisionBehavior = null, string shotItemId = null) : base(damageToFarmer, ParentSheetIndex, bouncesTillDestruct, tailLength, rotationVelocity, xVelocity, yVelocity, startingPosition, collisionSound, bounceSound, firingSound, explode, true, location, firer, collisionBehavior, shotItemId) { IgnoreLocationCollision = true; myCollisionSound = collisionSound; myExplode = explode; } - public override void behaviorOnCollisionWithMonster(NPC n, GameLocation location) { explosionAnimation(location); @@ -31,41 +31,37 @@ public override void behaviorOnCollisionWithMonster(NPC n, GameLocation location protected override void explosionAnimation(GameLocation location) { Rectangle sourceRect = GetSourceRect(); - sourceRect.X += 28; - sourceRect.Y += 28; - sourceRect.Width = 8; - sourceRect.Height = 8; int whichDebris = 12; - int value = currentTileSheetIndex.Value; - switch (value) + + if(itemId.Value != null) { - case 378: - whichDebris = 0; - break; - case 379: - case 381: - case 383: - case 385: - break; - case 380: - whichDebris = 2; - break; - case 382: - whichDebris = 4; - break; - case 384: - whichDebris = 6; - break; - case 386: - whichDebris = 10; - break; - default: - if (value == 390) - { + sourceRect.X += 28; + sourceRect.Y += 28; + sourceRect.Width = 8; + sourceRect.Height = 8; + switch (itemId.Value) + { + case "(O)378": + whichDebris = 0; + break; + case "(O)380": + whichDebris = 2; + break; + case "(O)382": + whichDebris = 4; + break; + case "(O)384": + whichDebris = 6; + break; + case "(O)386": + whichDebris = 10; + break; + case "(O)390": whichDebris = 14; - } - break; + break; + } } + if (itemId.Value != null) { Game1.createRadialDebris(location, whichDebris, (int)(position.X + 32f) / 64, (int)(position.Y + 32f) / 64, 6, false, -1, false); diff --git a/Swim/IJsonAssetsApi.cs b/Swim/IJsonAssetsApi.cs index 4cb727d9..266b1b57 100644 --- a/Swim/IJsonAssetsApi.cs +++ b/Swim/IJsonAssetsApi.cs @@ -2,8 +2,8 @@ { public interface IJsonAssetsApi { - int GetClothingId(string name); - int GetHatId(string name); + string GetClothingId(string name); + string GetHatId(string name); void LoadAssets(string path); } } diff --git a/Swim/ModConfig.cs b/Swim/ModConfig.cs index 8696914c..2813ce78 100644 --- a/Swim/ModConfig.cs +++ b/Swim/ModConfig.cs @@ -8,6 +8,7 @@ public class ModConfig public bool ReadyToSwim { get; set; } public bool SwimIndoors { get; set; } public bool SwimSuitAlways { get; set; } + public bool DisplayHatWithSwimsuit { get; set; } public bool NoAutoSwimSuit { get; set; } public bool ShowOxygenBar { get; set; } public int JumpTimeInMilliseconds { get; set; } @@ -22,6 +23,7 @@ public class ModConfig public bool AddCrabs { get; set; } public bool BreatheSound { get; set; } public bool EnableClickToSwim { get; set; } + public bool MustClickOnOppositeTerrain { get; set; } // In click to swim, whether you must click on land to leave water (and vice versa) or can just click in the direction of land. public int MineralPerThousandMin { get; set; } public int MineralPerThousandMax { get; set; } public int CrabsPerThousandMin { get; set; } @@ -55,7 +57,9 @@ public ModConfig() SwimIndoors = false; ShowOxygenBar = true; SwimSuitAlways = false; + DisplayHatWithSwimsuit = true; EnableClickToSwim = true; + MustClickOnOppositeTerrain = false; BreatheSound = true; SwimRestoresVitals = false; diff --git a/Swim/ModEntry.cs b/Swim/ModEntry.cs index 96164da1..5bc58b1f 100644 --- a/Swim/ModEntry.cs +++ b/Swim/ModEntry.cs @@ -2,12 +2,16 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using StardewModdingAPI; +using StardewModdingAPI.Events; using StardewModdingAPI.Utilities; using StardewValley; +using StardewValley.GameData.Locations; +using StardewValley.GameData.Shirts; using StardewValley.Menus; using StardewValley.Tools; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using xTile; using xTile.Dimensions; @@ -19,16 +23,16 @@ namespace Swim public class ModEntry : Mod { - public static ModConfig config; + public static ModConfig Config; public static IMonitor SMonitor; - public static IJsonAssetsApi JsonAssets; + public static IModHelper SHelper; + //public static IJsonAssetsApi JsonAssets; public static ModEntry context; public static PerScreen OxygenBarTexture = new PerScreen(); - public static readonly PerScreen scubaMaskID = new PerScreen(); - public static readonly PerScreen scubaFinsID = new PerScreen(); - public static readonly PerScreen scubaTankID = new PerScreen(); - public static readonly PerScreen myButtonDown = new PerScreen(() => false); + public static readonly PerScreen scubaMaskID = new PerScreen(); + public static readonly PerScreen scubaFinsID = new PerScreen(); + public static readonly PerScreen scubaTankID = new PerScreen(); public static readonly PerScreen oxygen = new PerScreen(() => 0); public static readonly PerScreen lastUpdateMs = new PerScreen(() => 0); public static readonly PerScreen willSwim = new PerScreen(() => false); @@ -36,6 +40,7 @@ public class ModEntry : Mod public static readonly PerScreen oldMariner = new PerScreen(); public static readonly PerScreen marinerQuestionsWrongToday = new PerScreen(() => false); public static readonly PerScreen myRand = new PerScreen(() => new Random()); + public static PerScreen locationIsPool = new PerScreen(() => false); //public static readonly PerScreen> diveMaps = new PerScreen>(() => new Dictionary()); public static Dictionary diveMaps = new Dictionary(); @@ -64,25 +69,28 @@ public class ModEntry : Mod public override void Entry(IModHelper helper) { - config = Helper.ReadConfig(); - if (!config.EnableMod) - return; + Config = Helper.ReadConfig(); context = this; SMonitor = Monitor; - - SwimPatches.Initialize(Monitor, helper, config); - SwimDialog.Initialize(Monitor, helper, config); - SwimMaps.Initialize(Monitor, helper, config); - SwimHelperEvents.Initialize(Monitor, helper, config); - SwimUtils.Initialize(Monitor, helper, config); + SHelper = helper; + + // Without the config only option, the player would not be able to re-enable the mod after they disabled it because we never added the config options. + helper.Events.GameLoop.GameLaunched += Config.EnableMod ? SwimHelperEvents.GameLoop_GameLaunched : GameLoop_GameLaunched_ConfigOnly; + + if (!Config.EnableMod) + return; + + SwimPatches.Initialize(Monitor, helper, Config); + SwimDialog.Initialize(Monitor, helper, Config); + SwimMaps.Initialize(Monitor, helper, Config); + SwimHelperEvents.Initialize(Monitor, helper, Config); + SwimUtils.Initialize(Monitor, helper, Config); helper.Events.GameLoop.UpdateTicked += SwimHelperEvents.GameLoop_UpdateTicked; helper.Events.Input.ButtonPressed += SwimHelperEvents.Input_ButtonPressed; - helper.Events.Input.ButtonReleased += SwimHelperEvents.Input_ButtonReleased; helper.Events.GameLoop.DayStarted += SwimHelperEvents.GameLoop_DayStarted; - helper.Events.GameLoop.GameLaunched += SwimHelperEvents.GameLoop_GameLaunched; helper.Events.GameLoop.SaveLoaded += SwimHelperEvents.GameLoop_SaveLoaded; helper.Events.GameLoop.Saving += SwimHelperEvents.GameLoop_Saving; helper.Events.Display.RenderedHud += SwimHelperEvents.Display_RenderedHud; @@ -161,17 +169,22 @@ public override void Entry(IModHelper helper) ); harmony.Patch( - original: AccessTools.Method(typeof(GameLocation), nameof(GameLocation.performTouchAction)), + original: AccessTools.Method(typeof(GameLocation), nameof(GameLocation.performTouchAction), new Type[] {typeof(string), typeof(Vector2)}), prefix: new HarmonyMethod(typeof(SwimPatches), nameof(SwimPatches.GameLocation_performTouchAction_Prefix)) ); + harmony.Patch( + original: AccessTools.Method(typeof(GameLocation), nameof(GameLocation.performTouchAction), new Type[] { typeof(string[]), typeof(Vector2) }), + prefix: new HarmonyMethod(typeof(SwimPatches), nameof(SwimPatches.GameLocation_performTouchAction_PrefixArray)) + ); + harmony.Patch( original: AccessTools.Method(typeof(GameLocation), nameof(GameLocation.checkAction)), prefix: new HarmonyMethod(typeof(SwimPatches), nameof(SwimPatches.GameLocation_checkAction_Prefix)) ); harmony.Patch( - original: AccessTools.Method(typeof(GameLocation), nameof(GameLocation.isCollidingPosition), new Type[] { typeof(Rectangle), typeof(xTile.Dimensions.Rectangle), typeof(bool), typeof(int), typeof(bool), typeof(Character), typeof(bool), typeof(bool), typeof(bool) }), + original: AccessTools.Method(typeof(GameLocation), nameof(GameLocation.isCollidingPosition), new Type[] { typeof(Rectangle), typeof(xTile.Dimensions.Rectangle), typeof(bool), typeof(int), typeof(bool), typeof(Character), typeof(bool), typeof(bool), typeof(bool), typeof(bool) }), postfix: new HarmonyMethod(typeof(SwimPatches), nameof(SwimPatches.GameLocation_isCollidingPosition_Postfix)) ); @@ -179,13 +192,300 @@ public override void Entry(IModHelper helper) original: AccessTools.Method(typeof(GameLocation), nameof(GameLocation.sinkDebris)), postfix: new HarmonyMethod(typeof(SwimPatches), nameof(SwimPatches.GameLocation_sinkDebris_Postfix)) ); + + harmony.Patch( + original: AccessTools.Method(typeof(FarmerRenderer), nameof(FarmerRenderer.drawHairAndAccesories)), + transpiler: new HarmonyMethod(typeof(SwimPatches), nameof(SwimPatches.FarmerRenderer_drawHairAndAccessories_Transpiler)) + ); } - private void Content_AssetRequested(object sender, StardewModdingAPI.Events.AssetRequestedEventArgs e) + private void Content_AssetRequested(object sender, AssetRequestedEventArgs e) { if (e.NameWithoutLocale.StartsWith("aedenthorn.Swim/Fishies")) { - e.LoadFromModFile($"assets/{e.NameWithoutLocale.ToString().Substring("aedenthorn.Swim/".Length)}.png", StardewModdingAPI.Events.AssetLoadPriority.Exclusive); + e.LoadFromModFile($"assets/{e.NameWithoutLocale.ToString().Substring("aedenthorn.Swim/".Length)}.png", AssetLoadPriority.Exclusive); + } + else if (e.NameWithoutLocale.IsEquivalentTo("Portraits\\Mariner")) + { + e.LoadFrom(() => {return Game1.content.Load("Portraits\\Gil");}, AssetLoadPriority.Low); + } + } + + public static void GameLoop_GameLaunched_ConfigOnly(object sender, GameLaunchedEventArgs e) + { + setupModConfig(); + } + + public static void setupModConfig() + { + // get Generic Mod Config Menu's API (if it's installed) + var configMenu = SHelper.ModRegistry.GetApi("spacechase0.GenericModConfigMenu"); + if (configMenu != null) + { + // Register mod. + configMenu.Register( + mod: ModEntry.context.ModManifest, + reset: () => Config = new ModConfig(), + save: () => SHelper.WriteConfig(Config) + ); + + #region Region: Basic Options. + + configMenu.AddBoolOption( + mod: ModEntry.context.ModManifest, + name: () => "Mod Enabled?", + tooltip: () => "Enables and Disables mod. Requires game restart to go into effect.", + getValue: () => Config.EnableMod, + setValue: value => Config.EnableMod = value + ); + configMenu.AddBoolOption( + mod: ModEntry.context.ModManifest, + name: () => "Auto-Swim enabled?", + tooltip: () => "Allow character to jump to the water automatically, when you walk to land edge.", + getValue: () => Config.ReadyToSwim, + setValue: value => Config.ReadyToSwim = value + ); + configMenu.AddBoolOption( + mod: ModEntry.context.ModManifest, + name: () => "ShowOxygenBar", + tooltip: () => "Define, will oxygen bar draw or not, when you dive to the water.", + getValue: () => Config.ShowOxygenBar, + setValue: value => Config.ShowOxygenBar = value + ); + configMenu.AddBoolOption( + mod: ModEntry.context.ModManifest, + name: () => "SwimSuitAlways", + tooltip: () => "If set's true, your character will always wear a swimsuit.", + getValue: () => Config.SwimSuitAlways, + setValue: value => Config.SwimSuitAlways = value + ); + configMenu.AddBoolOption( + mod: ModEntry.context.ModManifest, + name: () => "NoAutoSwimSuit", + tooltip: () => "If set's false, character will NOT wear a swimsuit automatically when you enter the water.", + getValue: () => Config.NoAutoSwimSuit, + setValue: value => Config.NoAutoSwimSuit = value + ); + configMenu.AddBoolOption( + mod: ModEntry.context.ModManifest, + name: () => "DisplayHatWithSwimsuit", + tooltip: () => "If set to true, will display your hat while you are wearing your swimming suit.", + getValue: () => Config.DisplayHatWithSwimsuit, + setValue: value => Config.DisplayHatWithSwimsuit = value + ); + configMenu.AddBoolOption( + mod: ModEntry.context.ModManifest, + name: () => "AllowActionsWhileInSwimsuit", + tooltip: () => "Allow you to use items, while you're swimming (may cause some visual bugs).", + getValue: () => Config.AllowActionsWhileInSwimsuit, + setValue: value => Config.AllowActionsWhileInSwimsuit = value + ); + configMenu.AddBoolOption( + mod: ModEntry.context.ModManifest, + name: () => "AllowRunningWhileInSwimsuit", + tooltip: () => "Allow you to run, while you're swimming (may cause some visual bugs).", + getValue: () => Config.AllowRunningWhileInSwimsuit, + setValue: value => Config.AllowRunningWhileInSwimsuit = value + ); + configMenu.AddBoolOption( + mod: ModEntry.context.ModManifest, + name: () => "EnableClickToSwim", + tooltip: () => "Enables or Disables possibility to manual jump to the water (by clicking certain key).", + getValue: () => Config.EnableClickToSwim, + setValue: value => Config.EnableClickToSwim = value + ); + configMenu.AddBoolOption( + mod: ModEntry.context.ModManifest, + name: () => "MustClickOnOppositeTerrain", + tooltip: () => "Whether you must click on land to leave the water (or vice versa) or can just click in the direction of land (when using click to swim).", + getValue: () => Config.MustClickOnOppositeTerrain, + setValue: value => Config.MustClickOnOppositeTerrain = value + ); + configMenu.AddBoolOption( + mod: ModEntry.context.ModManifest, + name: () => "SwimRestoresVitals", + tooltip: () => "If set's true, your HP and Energy will restore, while you're swimming (like in Bath).", + getValue: () => Config.SwimRestoresVitals, + setValue: value => Config.SwimRestoresVitals = value + ); + #endregion + + #region Region: Key Binds. + + configMenu.AddKeybind( + mod: ModEntry.context.ModManifest, + name: () => "Enable Auto-Swimming", + tooltip: () => "Enables and Disables auto-swimming option.", + getValue: () => Config.SwimKey, + setValue: value => Config.SwimKey = value + ); + configMenu.AddKeybind( + mod: ModEntry.context.ModManifest, + name: () => "Toggle Swimsuit", + tooltip: () => "Change character cloth to swimsuit and vice versa.", + getValue: () => Config.SwimSuitKey, + setValue: value => Config.SwimSuitKey = value + ); + configMenu.AddKeybind( + mod: ModEntry.context.ModManifest, + name: () => "Dive", + tooltip: () => "Change character cloth to swimsuit and vice versa.", + getValue: () => Config.DiveKey, + setValue: value => Config.DiveKey = value + ); + configMenu.AddKeybind( + mod: ModEntry.context.ModManifest, + name: () => "Manual Jump", + tooltip: () => "Allow you to jump into the water by clicking a certain key.", + getValue: () => Config.ManualJumpButton, + setValue: value => Config.ManualJumpButton = value + ); + #endregion + + #region Region: Advanced Tweaks. + + configMenu.AddNumberOption( + mod: ModEntry.context.ModManifest, + name: () => "JumpTimeInMilliseconds", + tooltip: () => "Sets jumping animation time.", + getValue: () => Config.JumpTimeInMilliseconds, + setValue: value => Config.JumpTimeInMilliseconds = value + ); + + configMenu.AddNumberOption( + mod: ModEntry.context.ModManifest, + name: () => "OxygenMult", + tooltip: () => "Sets oxygen multiplier (Energy * Mult = O2).", + getValue: () => Config.OxygenMult, + setValue: value => Config.OxygenMult = value + ); + configMenu.AddNumberOption( + mod: ModEntry.context.ModManifest, + name: () => "BubbleMult", + tooltip: () => "Set's quantity multiplier of bubbles.", + getValue: () => Config.BubbleMult, + setValue: value => Config.BubbleMult = value + ); + configMenu.AddBoolOption( + mod: ModEntry.context.ModManifest, + name: () => "AddFishies", + tooltip: () => "Allow fishes to spawn in underwater.", + getValue: () => Config.AddFishies, + setValue: value => Config.AddFishies = value + ); + configMenu.AddBoolOption( + mod: ModEntry.context.ModManifest, + name: () => "AddCrabs", + tooltip: () => "Allow crabs to spawn in underwater.", + getValue: () => Config.AddCrabs, + setValue: value => Config.AddCrabs = value + ); + configMenu.AddBoolOption( + mod: ModEntry.context.ModManifest, + name: () => "BreatheSound", + tooltip: () => "If sets true, while you're underwater you will hear breathe sound.", + getValue: () => Config.BreatheSound, + setValue: value => Config.BreatheSound = value + ); + configMenu.AddNumberOption( + mod: ModEntry.context.ModManifest, + name: () => "MineralPerThousandMin", + tooltip: () => "Sets minimal quantity, that can be meet underwater.", + getValue: () => Config.MineralPerThousandMin, + setValue: value => Config.MineralPerThousandMin = value + ); + configMenu.AddNumberOption( + mod: ModEntry.context.ModManifest, + name: () => "MineralPerThousandMax", + tooltip: () => "Sets maximal quantity, that can be meet underwater.", + getValue: () => Config.MineralPerThousandMax, + setValue: value => Config.MineralPerThousandMax = value + ); + configMenu.AddNumberOption( + mod: ModEntry.context.ModManifest, + name: () => "CrabsPerThousandMin", + tooltip: () => "Sets minimal quantity, that can be meet underwater.", + getValue: () => Config.CrabsPerThousandMin, + setValue: value => Config.CrabsPerThousandMin = value + ); + configMenu.AddNumberOption( + mod: ModEntry.context.ModManifest, + name: () => "CrabsPerThousandMax", + tooltip: () => "Sets maximal quantity, that can be meet underwater.", + getValue: () => Config.CrabsPerThousandMax, + setValue: value => Config.CrabsPerThousandMax = value + ); + configMenu.AddNumberOption( + mod: ModEntry.context.ModManifest, + name: () => "PercentChanceCrabIsMimic", + tooltip: () => "Sets chance to change crab by the mimic one.", + getValue: () => Config.PercentChanceCrabIsMimic, + setValue: value => Config.PercentChanceCrabIsMimic = value + ); + configMenu.AddNumberOption( + mod: ModEntry.context.ModManifest, + name: () => "MinSmolFishies", + tooltip: () => "Sets minimal quantity, that can be meet underwater.", + getValue: () => Config.MinSmolFishies, + setValue: value => Config.MinSmolFishies = value + ); + configMenu.AddNumberOption( + mod: ModEntry.context.ModManifest, + name: () => "MaxSmolFishies", + tooltip: () => "Sets maximal quantity, that can be meet underwater.", + getValue: () => Config.MaxSmolFishies, + setValue: value => Config.MaxSmolFishies = value + ); + configMenu.AddNumberOption( + mod: ModEntry.context.ModManifest, + name: () => "BigFishiesPerThousandMin", + tooltip: () => "Sets minimal quantity, that can be meet underwater.", + getValue: () => Config.BigFishiesPerThousandMin, + setValue: value => Config.BigFishiesPerThousandMin = value + ); + configMenu.AddNumberOption( + mod: ModEntry.context.ModManifest, + name: () => "BigFishiesPerThousandMax", + tooltip: () => "Sets maximal quantity, that can be meet underwater.", + getValue: () => Config.BigFishiesPerThousandMax, + setValue: value => Config.BigFishiesPerThousandMax = value + ); + configMenu.AddNumberOption( + mod: ModEntry.context.ModManifest, + name: () => "OceanForagePerThousandMin", + tooltip: () => "Sets minimal quantity, that can be meet underwater.", + getValue: () => Config.OceanForagePerThousandMin, + setValue: value => Config.OceanForagePerThousandMin = value + ); + configMenu.AddNumberOption( + mod: ModEntry.context.ModManifest, + name: () => "OceanForagePerThousandMax", + tooltip: () => "Sets maximal quantity, that can be meet underwater.", + getValue: () => Config.OceanForagePerThousandMax, + setValue: value => Config.OceanForagePerThousandMax = value + ); + configMenu.AddNumberOption( + mod: ModEntry.context.ModManifest, + name: () => "MinOceanChests", + tooltip: () => "Sets minimal quantity, that can be meet in underwater biome ocean.", + getValue: () => Config.MinOceanChests, + setValue: value => Config.MinOceanChests = value + ); + configMenu.AddNumberOption( + mod: ModEntry.context.ModManifest, + name: () => "MaxOceanChests", + tooltip: () => "Sets maximal quantity, that can be meet in underwater biome ocean", + getValue: () => Config.MaxOceanChests, + setValue: value => Config.MaxOceanChests = value + ); + configMenu.AddTextOption( + mod: ModEntry.context.ModManifest, + name: () => "JumpDistanceMult", + tooltip: () => "Multiply jump sensitivity by this amount", + getValue: () => Config.TriggerDistanceMult + "", + setValue: delegate (string value) { if (float.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var f)) { Config.TriggerDistanceMult = f; } } + ); + #endregion } } diff --git a/Swim/SeaCrab.cs b/Swim/SeaCrab.cs index c5e9f908..39cf8983 100644 --- a/Swim/SeaCrab.cs +++ b/Swim/SeaCrab.cs @@ -25,7 +25,7 @@ public SeaCrab() : base() public SeaCrab(Vector2 position) : base(position) { - Sprite.LoadTexture("aedenthorn.Swim/Fishies/" + crabTextures[Game1.random.Next(100) < ModEntry.config.PercentChanceCrabIsMimic ? 1 : 0]); + Sprite.LoadTexture("aedenthorn.Swim/Fishies/" + crabTextures[Game1.random.Next(100) < ModEntry.Config.PercentChanceCrabIsMimic ? 1 : 0]); moveTowardPlayerThreshold.Value = 1; damageToFarmer.Value = 0; } diff --git a/Swim/SwimHelperEvents.cs b/Swim/SwimHelperEvents.cs index ca5ed9c9..4e064a0d 100644 --- a/Swim/SwimHelperEvents.cs +++ b/Swim/SwimHelperEvents.cs @@ -7,6 +7,8 @@ using StardewModdingAPI.Events; using StardewModdingAPI.Utilities; using StardewValley; +using StardewValley.Buffs; +using StardewValley.GameData.Shirts; using StardewValley.Locations; using StardewValley.Menus; using StardewValley.Monsters; @@ -60,7 +62,6 @@ public class SwimHelperEvents internal static Texture2D bubbleTexture; private static readonly PerScreen lastBreatheSound = new PerScreen(); - private static readonly PerScreen surfacing = new PerScreen(); public static void Initialize(IMonitor monitor, IModHelper helper, ModConfig config) { @@ -71,6 +72,11 @@ public static void Initialize(IMonitor monitor, IModHelper helper, ModConfig con public static void Player_Warped(object sender, WarpedEventArgs e) { + if(!e.IsLocalPlayer) + { + return; + } + if (e.NewLocation.Name == "Custom_ScubaAbigailCave") { abigailTicks.Value = 0; @@ -78,7 +84,7 @@ public static void Player_Warped(object sender, WarpedEventArgs e) Game1.player.changeOutOfSwimSuit(); - if (Game1.player.hat.Value != null && Game1.player.hat.Value.ParentSheetIndex != 0) + if (Game1.player.hat.Value != null && Game1.player.hat.Value.ItemId != "0") Game1.player.addItemToInventory(Game1.player.hat.Value); Game1.player.hat.Value = new Hat("0"); Game1.player.doEmote(9); @@ -87,6 +93,8 @@ public static void Player_Warped(object sender, WarpedEventArgs e) { //SwimMaps.SwitchToWaterTiles(e.NewLocation); } + + ModEntry.locationIsPool.Value = false; } public static void Player_InventoryChanged(object sender, InventoryChangedEventArgs e) @@ -94,17 +102,17 @@ public static void Player_InventoryChanged(object sender, InventoryChangedEventA if (e.Player != Game1.player) return; - if (!Game1.player.mailReceived.Contains("ScubaTank") && ModEntry.scubaTankID.Value != -1 && e.Added != null && e.Added.Count() > 0 && e.Added.FirstOrDefault() != null && e.Added.FirstOrDefault().GetType() == typeof(Clothing) && e.Added.FirstOrDefault().ParentSheetIndex == ModEntry.scubaTankID.Value) + if (!Game1.player.mailReceived.Contains("ScubaTank") && ModEntry.scubaTankID.Value != "" && e.Added != null && e.Added.Count() > 0 && e.Added.FirstOrDefault() != null && e.Added.FirstOrDefault().GetType() == typeof(Clothing) && e.Added.FirstOrDefault().ItemId == ModEntry.scubaTankID.Value) { Monitor.Log("Player found scuba tank"); Game1.player.mailReceived.Add("ScubaTank"); } - if (!Game1.player.mailReceived.Contains("ScubaMask") && ModEntry.scubaMaskID.Value != -1 && e.Added != null && e.Added.Count() > 0 && e.Added.FirstOrDefault() != null && e.Added.FirstOrDefault().GetType() == typeof(Hat) && (e.Added.FirstOrDefault() as Hat).ItemId == ModEntry.scubaMaskID.Value + "") + if (!Game1.player.mailReceived.Contains("ScubaMask") && ModEntry.scubaMaskID.Value != "" && e.Added != null && e.Added.Count() > 0 && e.Added.FirstOrDefault() != null && e.Added.FirstOrDefault().GetType() == typeof(Hat) && (e.Added.FirstOrDefault() as Hat).ItemId == ModEntry.scubaMaskID.Value + "") { Monitor.Log("Player found scuba mask"); Game1.player.mailReceived.Add("ScubaMask"); } - if (!Game1.player.mailReceived.Contains("ScubaFins") && ModEntry.scubaFinsID.Value != -1 && e.Added != null && e.Added.Count() > 0 && e.Added.FirstOrDefault() != null && e.Added.FirstOrDefault().GetType() == typeof(Boots) && e.Added.FirstOrDefault().ParentSheetIndex == ModEntry.scubaFinsID.Value) + if (!Game1.player.mailReceived.Contains("ScubaFins") && ModEntry.scubaTankID.Value != "" && e.Added != null && e.Added.Count() > 0 && e.Added.FirstOrDefault() != null && e.Added.FirstOrDefault().GetType() == typeof(Boots) && e.Added.FirstOrDefault().ItemId == ModEntry.scubaFinsID.Value) { Monitor.Log("Player found scuba fins"); Game1.player.mailReceived.Add("ScubaFins"); @@ -126,47 +134,51 @@ public static void GameLoop_Saving(object sender, SavingEventArgs e) public static void GameLoop_SaveLoaded(object sender, SaveLoadedEventArgs e) { // load scuba gear ids - - if (ModEntry.JsonAssets != null) + + if(DataLoader.Boots(Game1.content).ContainsKey("Swim_ScubaFins")) { - ModEntry.scubaMaskID.Value = ModEntry.JsonAssets.GetHatId("Scuba Mask"); - ModEntry.scubaTankID.Value = ModEntry.JsonAssets.GetClothingId("Scuba Tank"); + ModEntry.scubaFinsID.Value = "Swim_ScubaFins"; - if (ModEntry.scubaMaskID.Value == -1) + Monitor.Log($"Swim mod item #1 ID is {ModEntry.scubaFinsID.Value}."); + if (Game1.player.boots.Value != null && Game1.player.boots.Value.Name == "Scuba Fins" && Game1.player.boots.Value.ItemId != ModEntry.scubaFinsID.Value) { - Monitor.Log("Can't get ID for Swim mod item #1. Some functionality will be lost."); - } - else - { - Monitor.Log(string.Format("Swim mod item #1 ID is {0}.", ModEntry.scubaMaskID.Value)); + Game1.player.boots.Value = ItemRegistry.Create(ModEntry.scubaFinsID.Value); } + } + else + { + Monitor.Log("Could not find scuba fins! Do you have the swim items content pack installed?", LogLevel.Warn); + } - if (ModEntry.scubaTankID.Value == -1) - { - Monitor.Log("Can't get ID for Swim mod item #2. Some functionality will be lost."); - } - else - { - Monitor.Log(string.Format("Swim mod item #2 ID is {0}.", ModEntry.scubaTankID.Value)); - } + if (DataLoader.Shirts(Game1.content).ContainsKey("Swim_ScubaTank")) + { + ModEntry.scubaTankID.Value = "Swim_ScubaTank"; - try + Monitor.Log($"Swim mod item #2 ID is {ModEntry.scubaTankID.Value}."); + if (Game1.player.shirtItem.Value != null && Game1.player.shirtItem.Value.Name == "Scuba Tank" && Game1.player.shirtItem.Value.ItemId != ModEntry.scubaTankID.Value) { - ModEntry.scubaFinsID.Value = Helper.GameContent.Load>(@"Data/Boots").First(x => x.Value.StartsWith("Scuba Fins")).Key; + Game1.player.shirtItem.Value = ItemRegistry.Create(ModEntry.scubaTankID.Value); } - catch - { - Monitor.Log("Can't get ID for Swim mod item #3. Some functionality will be lost."); - } - if (ModEntry.scubaFinsID.Value != -1) + } + else + { + Monitor.Log("Could not find scuba tank! Do you have the swim items content pack installed?", LogLevel.Warn); + } + + if (DataLoader.Hats(Game1.content).ContainsKey("Swim_ScubaMask")) + { + ModEntry.scubaMaskID.Value = "Swim_ScubaMask"; + + Monitor.Log($"Swim mod item #3 ID is {ModEntry.scubaMaskID.Value}."); + if (Game1.player.hat.Value != null && Game1.player.hat.Value.Name == "Scuba Mask" && Game1.player.hat.Value.ItemId != ModEntry.scubaMaskID.Value) { - Monitor.Log(string.Format("Swim mod item #3 ID is {0}.", ModEntry.scubaFinsID.Value)); - if (Game1.player.boots.Value != null && Game1.player.boots.Value != null && Game1.player.boots.Value.Name == "Scuba Fins" && Game1.player.boots.Value.ParentSheetIndex != ModEntry.scubaFinsID.Value) - { - Game1.player.boots.Value = new Boots(ModEntry.scubaFinsID.Value + ""); - } + Game1.player.hat.Value = ItemRegistry.Create(ModEntry.scubaMaskID.Value); } } + else + { + Monitor.Log("Could not find scuba mask! Do you have the swim items content pack installed?", LogLevel.Warn); + } // load dive maps @@ -261,8 +273,10 @@ public static void Display_RenderedHud(object sender, RenderedHudEventArgs e) public static void GameLoop_GameLaunched(object sender, GameLaunchedEventArgs e) { + ModEntry.setupModConfig(); // load scuba gear + /* ModEntry.JsonAssets = Helper.ModRegistry.GetApi("spacechase0.JsonAssets"); bool flag = ModEntry.JsonAssets == null; if (flag) @@ -272,7 +286,7 @@ public static void GameLoop_GameLaunched(object sender, GameLaunchedEventArgs e) else { ModEntry.JsonAssets.LoadAssets(Path.Combine(Helper.DirectoryPath, "assets/json-assets")); - } + }*/ // fix dive maps @@ -302,262 +316,6 @@ public static void GameLoop_GameLaunched(object sender, GameLaunchedEventArgs e) { LoadBreatheSound(); } - - // get Generic Mod Config Menu's API (if it's installed) - var configMenu = Helper.ModRegistry.GetApi("spacechase0.GenericModConfigMenu"); - if (configMenu != null) - { - // Register mod. - configMenu.Register( - mod: ModEntry.context.ModManifest, - reset: () => Config = new ModConfig(), - save: () => Helper.WriteConfig(Config) - ); - - #region Region: Basic Options. - - configMenu.AddBoolOption( - mod: ModEntry.context.ModManifest, - name: () => "Mod Enabled?", - tooltip: () => "Enables and Disables mod.", - getValue: () => Config.EnableMod, - setValue: value => Config.EnableMod = value - ); - configMenu.AddBoolOption( - mod: ModEntry.context.ModManifest, - name: () => "Auto-Swim enabled?", - tooltip: () => "Allow character to jump to the water automatically, when you walk to land edge.", - getValue: () => Config.ReadyToSwim, - setValue: value => Config.ReadyToSwim = value - ); - configMenu.AddBoolOption( - mod: ModEntry.context.ModManifest, - name: () => "ShowOxygenBar", - tooltip: () => "Define, will oxygen bar draw or not, when you dive to the water.", - getValue: () => Config.ShowOxygenBar, - setValue: value => Config.ShowOxygenBar = value - ); - configMenu.AddBoolOption( - mod: ModEntry.context.ModManifest, - name: () => "SwimSuitAlways", - tooltip: () => "If set's true, your character will always wear a swimsuit.", - getValue: () => Config.SwimSuitAlways, - setValue: value => Config.SwimSuitAlways = value - ); - configMenu.AddBoolOption( - mod: ModEntry.context.ModManifest, - name: () => "NoAutoSwimSuit", - tooltip: () => "If set's false, character will NOT wear a swimsuit automatically, when you enter the water.", - getValue: () => Config.NoAutoSwimSuit, - setValue: value => Config.NoAutoSwimSuit = value - ); - configMenu.AddBoolOption( - mod: ModEntry.context.ModManifest, - name: () => "AllowActionsWhileInSwimsuit", - tooltip: () => "Allow you to use items, while you're swimming (may cause some visual bugs).", - getValue: () => Config.AllowActionsWhileInSwimsuit, - setValue: value => Config.AllowActionsWhileInSwimsuit = value - ); - configMenu.AddBoolOption( - mod: ModEntry.context.ModManifest, - name: () => "AllowRunningWhileInSwimsuit", - tooltip: () => "Allow you to run, while you're swimming (may cause some visual bugs).", - getValue: () => Config.AllowRunningWhileInSwimsuit, - setValue: value => Config.AllowRunningWhileInSwimsuit = value - ); - configMenu.AddBoolOption( - mod: ModEntry.context.ModManifest, - name: () => "EnableClickToSwim", - tooltip: () => "Enables or Disables possibility to manual jump to the water (by clicking certain key).", - getValue: () => Config.EnableClickToSwim, - setValue: value => Config.EnableClickToSwim = value - ); - configMenu.AddBoolOption( - mod: ModEntry.context.ModManifest, - name: () => "SwimRestoresVitals", - tooltip: () => "If set's true, your HP and Energy will restore, while you're swimming (like in Bath).", - getValue: () => Config.SwimRestoresVitals, - setValue: value => Config.SwimRestoresVitals = value - ); - #endregion - - #region Region: Key Binds. - - configMenu.AddKeybind( - mod: ModEntry.context.ModManifest, - name: () => "Enable Auto-Swimming", - tooltip: () => "Enables and Disables auto-swimming option.", - getValue: () => Config.SwimKey, - setValue: value => Config.SwimKey = value - ); - configMenu.AddKeybind( - mod: ModEntry.context.ModManifest, - name: () => "Toggle Swimsuit", - tooltip: () => "Change character cloth to swimsuit and vice versa.", - getValue: () => Config.SwimSuitKey, - setValue: value => Config.SwimSuitKey = value - ); - configMenu.AddKeybind( - mod: ModEntry.context.ModManifest, - name: () => "Dive", - tooltip: () => "Change character cloth to swimsuit and vice versa.", - getValue: () => Config.DiveKey, - setValue: value => Config.DiveKey = value - ); - configMenu.AddKeybind( - mod: ModEntry.context.ModManifest, - name: () => "Manual Jump", - tooltip: () => "Allow you to jump into the water by clicking a certain key.", - getValue: () => Config.ManualJumpButton, - setValue: value => Config.ManualJumpButton = value - ); - #endregion - - #region Region: Advanced Tweaks. - - configMenu.AddNumberOption( - mod: ModEntry.context.ModManifest, - name: () => "JumpTimeInMilliseconds", - tooltip: () => "Sets jumping animation time.", - getValue: () => Config.JumpTimeInMilliseconds, - setValue: value => Config.JumpTimeInMilliseconds = value - ); - - configMenu.AddNumberOption( - mod: ModEntry.context.ModManifest, - name: () => "OxygenMult", - tooltip: () => "Sets oxygen multiplier (Energy * Mult = O2).", - getValue: () => Config.OxygenMult, - setValue: value => Config.OxygenMult = value - ); - configMenu.AddNumberOption( - mod: ModEntry.context.ModManifest, - name: () => "BubbleMult", - tooltip: () => "Set's quantity multiplier of bubbles.", - getValue: () => Config.BubbleMult, - setValue: value => Config.BubbleMult = value - ); - configMenu.AddBoolOption( - mod: ModEntry.context.ModManifest, - name: () => "AddFishies", - tooltip: () => "Allow fishes to spawn in underwater.", - getValue: () => Config.AddFishies, - setValue: value => Config.AddFishies = value - ); - configMenu.AddBoolOption( - mod: ModEntry.context.ModManifest, - name: () => "AddCrabs", - tooltip: () => "Allow crabs to spawn in underwater.", - getValue: () => Config.AddCrabs, - setValue: value => Config.AddCrabs = value - ); - configMenu.AddBoolOption( - mod: ModEntry.context.ModManifest, - name: () => "BreatheSound", - tooltip: () => "If sets true, while you're underwater you will hear breathe sound.", - getValue: () => Config.BreatheSound, - setValue: value => Config.BreatheSound = value - ); - configMenu.AddNumberOption( - mod: ModEntry.context.ModManifest, - name: () => "MineralPerThousandMin", - tooltip: () => "Sets minimal quantity, that can be meet underwater.", - getValue: () => Config.MineralPerThousandMin, - setValue: value => Config.MineralPerThousandMin = value - ); - configMenu.AddNumberOption( - mod: ModEntry.context.ModManifest, - name: () => "MineralPerThousandMax", - tooltip: () => "Sets maximal quantity, that can be meet underwater.", - getValue: () => Config.MineralPerThousandMax, - setValue: value => Config.MineralPerThousandMax = value - ); - configMenu.AddNumberOption( - mod: ModEntry.context.ModManifest, - name: () => "CrabsPerThousandMin", - tooltip: () => "Sets minimal quantity, that can be meet underwater.", - getValue: () => Config.CrabsPerThousandMin, - setValue: value => Config.CrabsPerThousandMin = value - ); - configMenu.AddNumberOption( - mod: ModEntry.context.ModManifest, - name: () => "CrabsPerThousandMax", - tooltip: () => "Sets maximal quantity, that can be meet underwater.", - getValue: () => Config.CrabsPerThousandMax, - setValue: value => Config.CrabsPerThousandMax = value - ); - configMenu.AddNumberOption( - mod: ModEntry.context.ModManifest, - name: () => "PercentChanceCrabIsMimic", - tooltip: () => "Sets chance to change crab by the mimic one.", - getValue: () => Config.PercentChanceCrabIsMimic, - setValue: value => Config.PercentChanceCrabIsMimic = value - ); - configMenu.AddNumberOption( - mod: ModEntry.context.ModManifest, - name: () => "MinSmolFishies", - tooltip: () => "Sets minimal quantity, that can be meet underwater.", - getValue: () => Config.MinSmolFishies, - setValue: value => Config.MinSmolFishies = value - ); - configMenu.AddNumberOption( - mod: ModEntry.context.ModManifest, - name: () => "MaxSmolFishies", - tooltip: () => "Sets maximal quantity, that can be meet underwater.", - getValue: () => Config.MaxSmolFishies, - setValue: value => Config.MaxSmolFishies = value - ); - configMenu.AddNumberOption( - mod: ModEntry.context.ModManifest, - name: () => "BigFishiesPerThousandMin", - tooltip: () => "Sets minimal quantity, that can be meet underwater.", - getValue: () => Config.BigFishiesPerThousandMin, - setValue: value => Config.BigFishiesPerThousandMin = value - ); - configMenu.AddNumberOption( - mod: ModEntry.context.ModManifest, - name: () => "BigFishiesPerThousandMax", - tooltip: () => "Sets maximal quantity, that can be meet underwater.", - getValue: () => Config.BigFishiesPerThousandMax, - setValue: value => Config.BigFishiesPerThousandMax = value - ); - configMenu.AddNumberOption( - mod: ModEntry.context.ModManifest, - name: () => "OceanForagePerThousandMin", - tooltip: () => "Sets minimal quantity, that can be meet underwater.", - getValue: () => Config.OceanForagePerThousandMin, - setValue: value => Config.OceanForagePerThousandMin = value - ); - configMenu.AddNumberOption( - mod: ModEntry.context.ModManifest, - name: () => "OceanForagePerThousandMax", - tooltip: () => "Sets maximal quantity, that can be meet underwater.", - getValue: () => Config.OceanForagePerThousandMax, - setValue: value => Config.OceanForagePerThousandMax = value - ); - configMenu.AddNumberOption( - mod: ModEntry.context.ModManifest, - name: () => "MinOceanChests", - tooltip: () => "Sets minimal quantity, that can be meet in underwater biome ocean.", - getValue: () => Config.MinOceanChests, - setValue: value => Config.MinOceanChests = value - ); - configMenu.AddNumberOption( - mod: ModEntry.context.ModManifest, - name: () => "MaxOceanChests", - tooltip: () => "Sets maximal quantity, that can be meet in underwater biome ocean", - getValue: () => Config.MaxOceanChests, - setValue: value => Config.MaxOceanChests = value - ); - configMenu.AddTextOption( - mod: ModEntry.context.ModManifest, - name: () => "JumpDistanceMult", - tooltip: () => "Multiply jump sensitivity by this amount", - getValue: () => Config.TriggerDistanceMult +"", - setValue: delegate(string value) { if (float.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var f)) { Config.TriggerDistanceMult = f; } } - ); - #endregion - } } private static void LoadBreatheSound() @@ -639,22 +397,10 @@ public static void GameLoop_DayStarted(object sender, DayStartedEventArgs e) ModEntry.oxygen.Value = SwimUtils.MaxOxygen(); } - public static void Input_ButtonReleased(object sender, ButtonReleasedEventArgs e) - { - if (Game1.player == null) - { - ModEntry.myButtonDown.Value = false; - return; - } - SwimUtils.CheckIfMyButtonDown(); - } - - public static void Input_ButtonPressed(object sender, ButtonPressedEventArgs e) { if (Game1.player == null || Game1.player.currentLocation == null) { - ModEntry.myButtonDown.Value = false; return; } @@ -690,11 +436,13 @@ public static void Input_ButtonPressed(object sender, ButtonPressedEventArgs e) if (e.Button == Config.DiveKey && Game1.activeClickableMenu == null && !Game1.player.UsingTool && ModEntry.diveMaps.ContainsKey(Game1.player.currentLocation.Name) && ModEntry.diveMaps[Game1.player.currentLocation.Name].DiveLocations.Count > 0) { + Monitor.Log("Trying to dive!"); Point pos = Game1.player.TilePoint; Location loc = new Location(pos.X, pos.Y); if (!SwimUtils.IsInWater()) { + Monitor.Log("Not in water"); return; } @@ -764,36 +512,7 @@ public static void GameLoop_UpdateTicked(object sender, UpdateTickedEventArgs e) if (Game1.activeClickableMenu == null) { - if (ModEntry.isUnderwater.Value) - { - if (ModEntry.oxygen.Value >= 0) - { - if (!SwimUtils.IsWearingScubaGear()) - ModEntry.oxygen.Value--; - else - { - if (ModEntry.oxygen.Value < SwimUtils.MaxOxygen()) - ModEntry.oxygen.Value++; - if (ModEntry.oxygen.Value < SwimUtils.MaxOxygen()) - ModEntry.oxygen.Value++; - } - } - if (ModEntry.oxygen.Value < 0 && !surfacing.Value) - { - surfacing.Value = true; - Game1.playSound("pullItemFromWater"); - DiveLocation diveLocation = ModEntry.diveMaps[Game1.player.currentLocation.Name].DiveLocations.Last(); - SwimUtils.DiveTo(diveLocation); - } - } - else - { - surfacing.Value = false; - if (ModEntry.oxygen.Value < SwimUtils.MaxOxygen()) - ModEntry.oxygen.Value++; - if (ModEntry.oxygen.Value < SwimUtils.MaxOxygen()) - ModEntry.oxygen.Value++; - } + SwimUtils.updateOxygenValue(); } if (SwimUtils.IsWearingScubaGear()) @@ -844,16 +563,6 @@ public static void GameLoop_UpdateTicked(object sender, UpdateTickedEventArgs e) if (!SwimUtils.CanSwimHere()) return; - // only if ready to swim from here on! - var readyToAutoSwim = Config.ReadyToSwim; - var manualSwim = Helper.Input.IsDown(Config.ManualJumpButton); - - // !IMP: Conditions, with locations (i.e. locations with restricted swimming), must be checked here. - if ((!readyToAutoSwim && !manualSwim) || !Context.IsPlayerFree) - { - return; - } - if (Game1.player.swimming.Value && !SwimUtils.IsInWater() && !isJumping.Value) { Monitor.Log("Swimming out of water"); @@ -870,162 +579,117 @@ public static void GameLoop_UpdateTicked(object sender, UpdateTickedEventArgs e) Game1.player.changeOutOfSwimSuit(); } - if (Game1.player.swimming.Value) + if (!Game1.player.swimming.Value && SwimUtils.IsInWater() && !isJumping.Value) { - DiveMap dm = null; - Point edgePos = Game1.player.TilePoint; + Monitor.Log("In water not swimming"); - if (ModEntry.diveMaps.ContainsKey(Game1.player.currentLocation.Name)) - { - dm = ModEntry.diveMaps[Game1.player.currentLocation.Name]; - } + ModEntry.willSwim.Value = true; + Game1.player.freezePause = Config.JumpTimeInMilliseconds; + Game1.player.currentLocation.playSound("dwop"); + isJumping.Value = true; + startJumpLoc.Value = Game1.player.position.Value; + endJumpLoc.Value = Game1.player.position.Value; - if (Game1.player.position.Y > Game1.viewport.Y + Game1.viewport.Height - 16) - { - Game1.player.position.Value = new Vector2(Game1.player.position.X, Game1.viewport.Y + Game1.viewport.Height - 17); - if (dm != null) - { - EdgeWarp edge = dm.EdgeWarps.Find((x) => x.ThisMapEdge == "Bottom" && x.FirstTile <= edgePos.X && x.LastTile >= edgePos.X); - if (edge != null) - { - Point pos = SwimUtils.GetEdgeWarpDestination(edgePos.X, edge); - if (pos != Point.Zero) - { - Monitor.Log("warping south"); - Game1.warpFarmer(edge.OtherMapName, pos.X, pos.Y, false); - } - } - } - } - else if (Game1.player.position.Y < Game1.viewport.Y - 16) - { - Game1.player.position.Value = new Vector2(Game1.player.position.X, Game1.viewport.Y - 15); - if (dm != null) - { - EdgeWarp edge = dm.EdgeWarps.Find((x) => x.ThisMapEdge == "Top" && x.FirstTile <= edgePos.X && x.LastTile >= edgePos.X); - if (edge != null) - { - Point pos = SwimUtils.GetEdgeWarpDestination(edgePos.X, edge); - if (pos != Point.Zero) - { - Monitor.Log("warping north"); - Game1.warpFarmer(edge.OtherMapName, pos.X, pos.Y, false); - } - } - } - } - else if (Game1.player.position.X > Game1.viewport.X + Game1.viewport.Width - 32) - { - Game1.player.position.Value = new Vector2(Game1.viewport.X + Game1.viewport.Width - 33, Game1.player.position.Y); + Game1.player.swimming.Value = true; + if (!Game1.player.bathingClothes.Value && !SwimUtils.IsWearingScubaGear() && !Config.NoAutoSwimSuit) + Game1.player.changeIntoSwimsuit(); + } - if (dm != null) - { - EdgeWarp edge = dm.EdgeWarps.Find((x) => x.ThisMapEdge == "Right" && x.FirstTile <= edgePos.Y && x.LastTile >= edgePos.Y); - if (edge != null) - { - Point pos = SwimUtils.GetEdgeWarpDestination(edgePos.Y, edge); - if (pos != Point.Zero) - { - Monitor.Log("warping east"); - Game1.warpFarmer(edge.OtherMapName, pos.X, pos.Y, false); - } - } - } - if (Game1.player.currentLocation.Name == "Forest") - { - if (Game1.player.position.Y / 64 > 74) - Game1.warpFarmer("Beach", 0, 13, false); - else - Game1.warpFarmer("Town", 0, 100, false); - } - } - else if (Game1.player.position.X < Game1.viewport.X - 32) - { - Game1.player.position.Value = new Vector2(Game1.viewport.X - 31, Game1.player.position.Y); + // !IMP: Conditions, with locations (i.e. locations with restricted swimming), must be checked here + if (!Context.IsPlayerFree) + { + return; + } - if (dm != null) - { - EdgeWarp edge = dm.EdgeWarps.Find((x) => x.ThisMapEdge == "Left" && x.FirstTile <= edgePos.Y && x.LastTile >= edgePos.Y); - if (edge != null) - { - Point pos = SwimUtils.GetEdgeWarpDestination(edgePos.Y, edge); - if (pos != Point.Zero) - { - Monitor.Log("warping west"); - Game1.warpFarmer(edge.OtherMapName, pos.X, pos.Y, false); - } - } - } + if (Game1.player.swimming.Value && tryToWarp()) // Returns true if it is warping + return; - if (Game1.player.currentLocation.Name == "Town") - { - Game1.warpFarmer("Forest", 119, 43, false); - } - else if (Game1.player.currentLocation.Name == "Beach") + if (Game1.player.swimming.Value) + { + if (SwimUtils.IsWearingScubaGear() && !Config.SwimSuitAlways && SwimUtils.IsMapUnderwater(Game1.currentLocation.Name)) + { + if(Game1.player.bathingClothes.Value) { - Game1.warpFarmer("Forest", 119, 111, false); + Game1.player.changeOutOfSwimSuit(); } } - - if (Game1.player.bathingClothes.Value && SwimUtils.IsWearingScubaGear() && !Config.SwimSuitAlways) - Game1.player.changeOutOfSwimSuit(); - else if (!Game1.player.bathingClothes.Value && !Config.NoAutoSwimSuit && (!SwimUtils.IsWearingScubaGear() || Config.SwimSuitAlways)) + else if (!Game1.player.bathingClothes.Value && !Config.NoAutoSwimSuit) Game1.player.changeIntoSwimsuit(); - if (Game1.player.boots.Value != null && ModEntry.scubaFinsID.Value != -1 && Game1.player.boots.Value.indexInTileSheet.Value == ModEntry.scubaFinsID.Value) + + if (Game1.player.boots.Value != null && ModEntry.scubaFinsID.Value != "" && Game1.player.boots.Value.ItemId == ModEntry.scubaFinsID.Value) { - string buffId = "42883167"; + string buffId = "Swim_ScubaFinsSpeed"; Buff buff = Game1.player.buffs.AppliedBuffs.Values.FirstOrDefault((Buff p) => p.Equals(buffId)); if (buff == null) { - BuffsDisplay buffsDisplay = Game1.buffsDisplay; - Buff buff2 = new Buff("42883167", "Scuba Fins", Helper.Translation.Get("scuba-fins")); - - buff = buff2; - buffsDisplay.updatedIDs.Add(buff2.id); + buff = new Buff(buffId, "Scuba Fins", Helper.Translation.Get("scuba-fins"), 50, Game1.content.Load("TileSheets/BuffsIcons"), 9, new BuffEffects() {Speed = {2}}); + + Game1.player.applyBuff(buff); + } + else + { + buff.millisecondsDuration = 50; } - buff.millisecondsDuration = 50; } + + Game1.player.canOnlyWalk = true; + Game1.player.setRunning(false); } - else + + if(!SwimUtils.isSafeToTryJump()) { - if (SwimUtils.IsInWater() && !isJumping.Value) - { - Monitor.Log("In water not swimming"); + return; + } - ModEntry.willSwim.Value = true; - Game1.player.freezePause = Config.JumpTimeInMilliseconds; - Game1.player.currentLocation.playSound("dwop"); - isJumping.Value = true; - startJumpLoc.Value = Game1.player.position.Value; - endJumpLoc.Value = Game1.player.position.Value; + int direction = Game1.player.FacingDirection; + bool didJump = tryToJumpInDirection(direction); // Try to jump in the direction the player is facing - Game1.player.swimming.Value = true; - if (!Game1.player.bathingClothes.Value && !SwimUtils.IsWearingScubaGear() && !Config.NoAutoSwimSuit) - Game1.player.changeIntoSwimsuit(); - } + // If we didn't just jump, + if (!didJump && Helper.Input.IsDown(Config.ManualJumpButton) && SwimUtils.isMouseButton(Config.ManualJumpButton) && Config.EnableClickToSwim) + { + try + { + int xTile = (int)Math.Round((Game1.viewport.X + Game1.getOldMouseX()) / 64f); + int yTile = (int)Math.Round((Game1.viewport.Y + Game1.getOldMouseY()) / 64f); + //Monitor.Log($"Click tile: ({xTile}, {yTile}), Player tile: ({Game1.player.TilePoint.X}, {Game1.player.TilePoint.Y})"); + bool clickTileIsWater = Game1.player.currentLocation.waterTiles[xTile, yTile]; + bool isClickingOnOppositeTerrain = clickTileIsWater != Game1.player.swimming.Value; + if (isClickingOnOppositeTerrain || !Config.MustClickOnOppositeTerrain) + { + // Set the direction to the direction of the cursor relative to the player. + direction = SwimUtils.GetDirection(Game1.player.TilePoint.X, Game1.player.TilePoint.Y, xTile, yTile); + if(direction != Game1.player.FacingDirection) + { + tryToJumpInDirection(direction); + } + } + else // If the player is pressing the manual swim button, manual swim is on, they are not clicking on the opposite terrain, and mustClickOnOppositeTerrain is true + { + return; + } + } + catch + { + // Assiming this happens when the game can't get the mouse position + Monitor.Log("Error in manual direction calculation!"); + } } + } - SwimUtils.CheckIfMyButtonDown(); - - if (!ModEntry.myButtonDown.Value || Game1.player.millisecondsPlayed - lastJump.Value < 250 || SwimUtils.IsMapUnderwater(Game1.player.currentLocation.Name)) - return; - - // !IMP: Conditions with hand tools (i.e. rods), must be placed here. - if ((Helper.Input.IsDown(SButton.MouseLeft) && !Game1.player.swimming.Value && (Game1.player.CurrentTool is WateringCan || Game1.player.CurrentTool is FishingRod)) || - (Helper.Input.IsDown(SButton.MouseRight) && !Game1.player.swimming.Value && Game1.player.CurrentTool is MeleeWeapon)) - return; + public static bool tryToJumpInDirection(int direction) // Returns whether or not it is jumping + { + double distance = -1; + int maxDistance = 0; - List tiles = SwimUtils.GetTilesInDirection(5); + List tiles = SwimUtils.GetTilesInDirection(5, direction); Vector2 jumpLocation = Vector2.Zero; - double distance = -1; - int maxDistance = 0; - switch (Game1.player.FacingDirection) + switch (direction) { case 0: distance = Math.Abs(Game1.player.position.Y - tiles.Last().Y * Game1.tileSize); @@ -1044,24 +708,8 @@ public static void GameLoop_UpdateTicked(object sender, UpdateTickedEventArgs e) maxDistance = (int)Math.Round(Config.TriggerDistanceLeft * Config.TriggerDistanceMult); break; } - if (Helper.Input.IsDown(SButton.MouseLeft)) - { - try - { - int xTile = (Game1.viewport.X + Game1.getOldMouseX()) / 64; - int yTile = (Game1.viewport.Y + Game1.getOldMouseY()) / 64; - bool water = Game1.player.currentLocation.waterTiles[xTile, yTile]; - if (Game1.player.swimming.Value != water) - { - distance = -1; - } - } - catch - { - } - } - //Monitor.Value.Log("Distance: " + distance); + //Monitor.Log("Distance: " + distance); bool nextToLand = Game1.player.swimming.Value && !SwimUtils.IsWaterTile(tiles[tiles.Count - 2]) && distance < maxDistance; @@ -1084,7 +732,7 @@ public static void GameLoop_UpdateTicked(object sender, UpdateTickedEventArgs e) if (nextToLand || nextToWater) { - //Monitor.Value.Log("okay to jump"); + Monitor.Log("okay to jump"); for (int i = 0; i < tiles.Count; i++) { Vector2 tileV = tiles[i]; @@ -1095,7 +743,7 @@ public static void GameLoop_UpdateTicked(object sender, UpdateTickedEventArgs e) Tile tile = Game1.player.currentLocation.map.GetLayer("Buildings").PickTile(new Location((int)tileV.X * Game1.tileSize, (int)tileV.Y * Game1.tileSize), Game1.viewport.Size); isWater = SwimUtils.IsWaterTile(tileV); isPassable = (nextToLand && !isWater && SwimUtils.IsTilePassable(Game1.player.currentLocation, new Location((int)tileV.X, (int)tileV.Y), Game1.viewport)) || (nextToWater && isWater && (tile == null || tile.TileIndex == 76)); - //Monitor.Value.Log($"Trying {tileV} is passable {isPassable} isWater {isWater}"); + Monitor.Log($"Trying {tileV} is passable {isPassable} isWater {isWater}"); if (!SwimUtils.IsTilePassable(Game1.player.currentLocation, new Location((int)tileV.X, (int)tileV.Y), Game1.viewport) && !isWater && nextToLand) { //Monitor.Value.Log($"Nixing {tileV}"); @@ -1128,6 +776,7 @@ public static void GameLoop_UpdateTicked(object sender, UpdateTickedEventArgs e) if (jumpLocation != Vector2.Zero) { + Game1.player.faceDirection(direction); lastJump.Value = Game1.player.millisecondsPlayed; //Monitor.Value.Log("got swim location"); if (Game1.player.swimming.Value) @@ -1150,8 +799,10 @@ public static void GameLoop_UpdateTicked(object sender, UpdateTickedEventArgs e) isJumping.Value = true; startJumpLoc.Value = Game1.player.position.Value; endJumpLoc.Value = new Vector2(jumpLocation.X * Game1.tileSize, jumpLocation.Y * Game1.tileSize); + return true; } + return false; } public static void AbigailCaveTick() @@ -1288,8 +939,9 @@ public static void AbigailCaveTick() if (v != Vector2.Zero && Game1.player.millisecondsPlayed - lastProjectile.Value > 350) { - Game1.player.currentLocation.projectiles.Add(new AbigailProjectile(1, 383, 0, 0, 0, v.X * 6, v.Y * 6, new Vector2(Game1.player.StandingPixel.X - 24, Game1.player.StandingPixel.Y - 48), "Cowboy_monsterDie", null, "Cowboy_gunshot", false, true, Game1.player.currentLocation, Game1.player)); + Game1.player.currentLocation.projectiles.Add(new AbigailProjectile(1, 3, 0, 0, 0, v.X * 6, v.Y * 6, new Vector2(Game1.player.StandingPixel.X - 24, Game1.player.StandingPixel.Y - 48), "Cowboy_monsterDie", null, "Cowboy_gunshot", false, true, Game1.player.currentLocation, Game1.player, shotItemId: "(O)382")); lastProjectile.Value = Game1.player.millisecondsPlayed; + Game1.player.faceDirection(SwimUtils.GetDirection(0, 0, v.X, v.Y)); } foreach (SButton button in abigailShootButtons) @@ -1313,7 +965,6 @@ public static void AbigailCaveTick() } } - abigailTicks.Value++; if (abigailTicks.Value > 80000 / 16f) { @@ -1371,5 +1022,102 @@ public static void AbigailCaveTick() } } + public static bool tryToWarp() + { + DiveMap dm = null; + Point edgePos = Game1.player.TilePoint; + + string locationName = Game1.player.currentLocation.Name == "BeachNightMarket" ? "Beach" : Game1.player.currentLocation.Name; + + if (ModEntry.diveMaps.ContainsKey(locationName)) + { + dm = ModEntry.diveMaps[locationName]; + } + else + { + return false; + } + + if (Game1.player.position.Y > Game1.viewport.Y + Game1.viewport.Height - 16) + { + Game1.player.position.Value = new Vector2(Game1.player.position.X, Game1.viewport.Y + Game1.viewport.Height - 17); + if (dm != null) + { + Monitor.Log($"Trying to warp from ({edgePos.X}, {edgePos.Y})"); + EdgeWarp edge = dm.EdgeWarps.Find((x) => x.ThisMapEdge == "Bottom" && x.FirstTile <= edgePos.X && x.LastTile >= edgePos.X); + if (edge != null) + { + Point pos = SwimUtils.GetEdgeWarpDestination(edgePos.X, edge); + if (pos != Point.Zero) + { + Monitor.Log("warping south"); + Game1.warpFarmer(edge.OtherMapName, pos.X, pos.Y, false); + return true; + } + } + } + } + else if (Game1.player.position.Y < Game1.viewport.Y - 16) + { + Game1.player.position.Value = new Vector2(Game1.player.position.X, Game1.viewport.Y - 15); + + if (dm != null) + { + Monitor.Log($"Trying to warp from ({edgePos.X}, {edgePos.Y})"); + EdgeWarp edge = dm.EdgeWarps.Find((x) => x.ThisMapEdge == "Top" && x.FirstTile <= edgePos.X && x.LastTile >= edgePos.X); + if (edge != null) + { + Point pos = SwimUtils.GetEdgeWarpDestination(edgePos.X, edge); + if (pos != Point.Zero) + { + Monitor.Log("warping north"); + Game1.warpFarmer(edge.OtherMapName, pos.X, pos.Y, false); + return true; + } + } + } + } + else if (Game1.player.position.X > Game1.viewport.X + Game1.viewport.Width - 32) + { + Game1.player.position.Value = new Vector2(Game1.viewport.X + Game1.viewport.Width - 33, Game1.player.position.Y); + + if (dm != null) + { + Monitor.Log($"Trying to warp from ({edgePos.X}, {edgePos.Y})"); + EdgeWarp edge = dm.EdgeWarps.Find((x) => x.ThisMapEdge == "Right" && x.FirstTile <= edgePos.Y && x.LastTile >= edgePos.Y); + if (edge != null) + { + Point pos = SwimUtils.GetEdgeWarpDestination(edgePos.Y, edge); + if (pos != Point.Zero) + { + Monitor.Log("warping east"); + Game1.warpFarmer(edge.OtherMapName, pos.X, pos.Y, false); + return true; + } + } + } + } + else if (Game1.player.position.X < Game1.viewport.X - 32) + { + Game1.player.position.Value = new Vector2(Game1.viewport.X - 31, Game1.player.position.Y); + + if (dm != null) + { + Monitor.Log($"Trying to warp from ({edgePos.X}, {edgePos.Y})"); + EdgeWarp edge = dm.EdgeWarps.Find((x) => x.ThisMapEdge == "Left" && x.FirstTile <= edgePos.Y && x.LastTile >= edgePos.Y); + if (edge != null) + { + Point pos = SwimUtils.GetEdgeWarpDestination(edgePos.Y, edge); + if (pos != Point.Zero) + { + Monitor.Log("warping west"); + Game1.warpFarmer(edge.OtherMapName, pos.X, pos.Y, false); + return true; + } + } + } + } + return false; + } } } diff --git a/Swim/SwimMaps.cs b/Swim/SwimMaps.cs index 6365c183..d877a36a 100644 --- a/Swim/SwimMaps.cs +++ b/Swim/SwimMaps.cs @@ -2,6 +2,7 @@ using StardewModdingAPI; using StardewValley; using StardewValley.Objects; +using StardewValley.TerrainFeatures; using StardewValley.Tools; using System; using System.Collections.Generic; @@ -27,19 +28,38 @@ public static void Initialize(IMonitor monitor, IModHelper helper, ModConfig con Helper = helper; } + public static Object SpawnForageItem(GameLocation location, Vector2 position, string itemID) + { + Object item = ItemRegistry.Create(itemID.StartsWith("(") ? itemID : "(O)" + itemID); + Monitor.Log($"Spawning forage {item.Name} at ({position.X}, {position.Y})"); + location.numberOfSpawnedObjectsOnMap++; + location.overlayObjects[position] = item; + item.IsSpawnedObject = true; + item.CanBeGrabbed = true; + return item; + } + + public static Object SpawnWorldItem(GameLocation location, Vector2 position, string itemID) + { + Object item = ItemRegistry.Create(itemID.StartsWith("(") ? itemID : "(O)" + itemID); + Monitor.Log($"Spawning world item {item.Name} at ({position.X}, {position.Y})"); + location.overlayObjects[position] = item; + return item; + } + public static void AddScubaChest(GameLocation gameLocation, Vector2 pos, string which) { if (which == "ScubaTank" && !Game1.player.mailReceived.Contains(which)) { - gameLocation.overlayObjects[pos] = new Chest( new List() { new Clothing(ModEntry.scubaTankID.Value+"") }, pos, false, 0); + gameLocation.overlayObjects[pos] = new Chest( new List() { new Clothing(ModEntry.scubaTankID.Value) }, pos, false, 0); } else if (which == "ScubaMask" && !Game1.player.mailReceived.Contains(which)) { - gameLocation.overlayObjects[pos] = new Chest( new List() { new Hat(ModEntry.scubaMaskID.Value + "") }, pos, false, 0); + gameLocation.overlayObjects[pos] = new Chest( new List() { new Hat(ModEntry.scubaMaskID.Value )}, pos, false, 0); } else if (which == "ScubaFins" && !Game1.player.mailReceived.Contains(which)) { - gameLocation.overlayObjects[pos] = new Chest( new List() { new Boots(ModEntry.scubaFinsID.Value + "") }, pos, false, 0); + gameLocation.overlayObjects[pos] = new Chest( new List() { new Boots(ModEntry.scubaFinsID.Value) }, pos, false, 0); } } public static void AddWaterTiles(GameLocation gameLocation) @@ -107,57 +127,41 @@ public static void AddMinerals(GameLocation l) } else if (chance < 0.4) { - l.overlayObjects[tile] = new Object(tile, "751") - { - MinutesUntilReady = 2 - }; + SpawnWorldItem(l, tile, "(O)751").MinutesUntilReady = 2; // Copper stone } else if (chance < 0.5) { - l.overlayObjects[tile] = new Object(tile, "290") - { - MinutesUntilReady = 4 - }; + SpawnWorldItem(l, tile, "(O)290").MinutesUntilReady = 4; // Iron stone } else if (chance < 0.55) { - l.overlayObjects[tile] = new Object(tile, "764") - { - MinutesUntilReady = 8 - }; + SpawnWorldItem(l, tile, "(O)764").MinutesUntilReady = 8; // Gold stone } else if (chance < 0.56) { - l.overlayObjects[tile] = new Object(tile, "765") - { - MinutesUntilReady = 16 - }; + SpawnWorldItem(l, tile, "(O)765").MinutesUntilReady = 16; // Iridium stone } else if (chance < 0.65) { - l.overlayObjects[tile] = new Object(tile, "80"); + SpawnForageItem(l, tile, "(O)80"); // Quartz } else if (chance < 0.74) { - l.overlayObjects[tile] = new Object(tile, "82"); + SpawnForageItem(l, tile, "(O)82"); // Fire Quartz } else if (chance < 0.83) { - l.overlayObjects[tile] = new Object(tile, "84"); + SpawnForageItem(l, tile, "(O)84"); // Frozen Tear } else if (chance < 0.90) { - l.overlayObjects[tile] = new Object(tile, "86"); + SpawnForageItem(l, tile, "(O)86"); // Earth crystal } else { string[] gems = { "4","6","8","10","12","14","40" }; string whichGem = gems[Game1.random.Next(gems.Length)]; - l.overlayObjects[tile] = new Object(tile, whichGem) - { - MinutesUntilReady = 5 - - }; + SpawnWorldItem(l, tile, whichGem).MinutesUntilReady = 5; } } } @@ -277,59 +281,35 @@ public static void AddOceanForage(GameLocation l) double chance = Game1.random.NextDouble(); if (chance < 0.25) { - l.overlayObjects[v] = new Object(v, "152") - { - CanBeGrabbed = true - }; + SpawnForageItem(l, v, "(O)152");// Seaweed } else if (chance < 0.4) { - l.overlayObjects[v] = new Object(v, "153") - { - CanBeGrabbed = true - }; + SpawnForageItem(l, v, "(O)153");// Green Algae } else if (chance < 0.6) { - l.overlayObjects[v] = new Object(v, "157") - { - CanBeGrabbed = true - }; + SpawnForageItem(l, v, "(O)157");// White Algae } else if (chance < 0.75) { - l.overlayObjects[v] = new Object(v, "372") - { - CanBeGrabbed = true - }; + SpawnForageItem(l, v, "(O)372");// Clam } else if (chance < 0.85) { - l.overlayObjects[v] = new Object(v, "393") - { - CanBeGrabbed = true - }; + SpawnForageItem(l, v, "(O)393");// Coral } else if (chance < 0.94) { - l.overlayObjects[v] = new Object(v, "397") - { - CanBeGrabbed = true - }; + SpawnForageItem(l, v, "(O)397");// Sea Urchin } else if (chance < 0.97) { - l.overlayObjects[v] = new Object(v, "394 ") - { - CanBeGrabbed = true - }; + SpawnForageItem(l, v, "(O)394");// Rainbow Shell } else { - l.overlayObjects[v] = new Object(v, "392") - { - CanBeGrabbed = true - }; + SpawnForageItem(l, v, "(O)392");// Nautilus Shell } } } @@ -463,10 +443,10 @@ public static void AddOceanTreasure(GameLocation l) switch (Game1.random.Next(3)) { case 0: - treasures.Add(new Object((537 + ((Game1.random.NextDouble() < 0.4) ? Game1.random.Next(-2, 0) : 0)) + "", Game1.random.Next(1, 4), false, -1, 0)); + treasures.Add(new Object((537 + ((Game1.random.NextDouble() < 0.4) ? Game1.random.Next(-2, 0) : 0)).ToString(), Game1.random.Next(1, 4), false, -1, 0)); break; case 1: - treasures.Add(new Object((536 + ((Game1.random.NextDouble() < 0.4) ? -1 : 0)) + "", Game1.random.Next(1, 4), false, -1, 0)); + treasures.Add(new Object((536 + ((Game1.random.NextDouble() < 0.4) ? -1 : 0)).ToString(), Game1.random.Next(1, 4), false, -1, 0)); break; case 2: treasures.Add(new Object("535", Game1.random.Next(1, 4), false, -1, 0)); @@ -484,13 +464,13 @@ public static void AddOceanTreasure(GameLocation l) treasures.Add(new Object("382", Game1.random.Next(1, 4), false, -1, 0)); break; case 1: - treasures.Add(new Object("" + ((Game1.random.NextDouble() < 0.3) ? 82 : ((Game1.random.NextDouble() < 0.5) ? 64 : 60)), Game1.random.Next(1, 3), false, -1, 0)); + treasures.Add(new Object(((Game1.random.NextDouble() < 0.3) ? 82 : ((Game1.random.NextDouble() < 0.5) ? 64 : 60)).ToString(), Game1.random.Next(1, 3), false, -1, 0)); break; case 2: - treasures.Add(new Object(""+((Game1.random.NextDouble() < 0.3) ? 84 : ((Game1.random.NextDouble() < 0.5) ? 70 : 62)), Game1.random.Next(1, 3), false, -1, 0)); + treasures.Add(new Object(((Game1.random.NextDouble() < 0.3) ? 84 : ((Game1.random.NextDouble() < 0.5) ? 70 : 62)).ToString(), Game1.random.Next(1, 3), false, -1, 0)); break; case 3: - treasures.Add(new Object("" + ((Game1.random.NextDouble() < 0.3) ? 86 : ((Game1.random.NextDouble() < 0.5) ? 66 : 68)), Game1.random.Next(1, 3), false, -1, 0)); + treasures.Add(new Object(((Game1.random.NextDouble() < 0.3) ? 86 : ((Game1.random.NextDouble() < 0.5) ? 66 : 68)).ToString(), Game1.random.Next(1, 3), false, -1, 0)); break; } if (Game1.random.NextDouble() < 0.05) @@ -585,6 +565,10 @@ public static void AddOceanTreasure(GameLocation l) Tint = tint }; } + foreach (var obj in treasures) + { + Monitor.Log($"Treasures: {obj.QualifiedItemId} {obj.DisplayName}"); + } } } diff --git a/Swim/SwimPatches.cs b/Swim/SwimPatches.cs index 31df6971..98888282 100644 --- a/Swim/SwimPatches.cs +++ b/Swim/SwimPatches.cs @@ -1,6 +1,7 @@ using HarmonyLib; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; +using Netcode; using StardewModdingAPI; using StardewValley; using System; @@ -10,7 +11,6 @@ using System.Reflection.Emit; using System.Threading.Tasks; using xTile.Dimensions; -using xTile.Tiles; namespace Swim { @@ -71,6 +71,64 @@ internal static void FarmerRenderer_draw_Postfix(Farmer who, bool __state) } } + + /// + /// Patches the check in FarmerRenderer.drawHairAndAccessories that causes the player's hat to not be drawn when they are wearing a bathing suit. + /// Instead, replaces that with a call to SwimUitls.ShouldNotDrawHat. + /// + /// + /// + public static IEnumerable FarmerRenderer_drawHairAndAccessories_Transpiler(IEnumerable instructions) + { + var codes = new List(instructions); + + /* + * + * 0 IL_0e8d: ldarg.3 + * 1 IL_0e8e: ldfld class Netcode.NetRef`1 StardewValley.Farmer::hat + * 2 IL_0e93: callvirt instance !0 class Netcode.NetFieldBase`2>::get_Value() + * 3 IL_0e98: brfalse IL_116d + * + * // We want to edit this section so that instead of checking farmer.bathingClothes, it calls SwimUtils.ShouldNotDrawHat() + * 4 IL_0e9d: ldarg.3 <- keep (it loads the Farmer object onto the stack) + * 5 IL_0e9e: ldfld class Netcode.NetBool StardewValley.Farmer::bathingClothes <- delete (we only need 3 instructions) + * 6 IL_0ea3: call bool Netcode.NetBool::op_Implicit(class Netcode.NetBool) <- replace operand with SwimUtils.ShouldNotDrawHat() + * 7 IL_0ea8: brtrue IL_116d <- keep + * + * 8 IL_0ead: ldarg.3 + * 9 IL_0eae: callvirt instance class StardewValley.FarmerSprite StardewValley.Farmer::get_FarmerSprite() + * 10 IL_0eb3: callvirt instance valuetype StardewValley.FarmerSprite/AnimationFrame StardewValley.FarmerSprite::get_CurrentAnimationFrame() + * 11 IL_0eb8: ldfld bool StardewValley.FarmerSprite/AnimationFrame::flip + */ + + try + { + // We want codes[i] to be that first ldarg. We are going to check every instruction shown above because for this method, this code is kind of generic (similar checks happen multiple times) + for (int i = 0; i < codes.Count; i++) + { + if (codes[i].opcode == OpCodes.Ldarg_3 && codes[i+1].opcode == OpCodes.Ldfld && codes[i+2].opcode == OpCodes.Callvirt && codes[i+3].opcode == OpCodes.Brfalse && + codes[i+4].opcode == OpCodes.Ldarg_3 && codes[i+5].opcode == OpCodes.Ldfld && codes[i+6].opcode == OpCodes.Call && codes[i+7].opcode == OpCodes.Brtrue && + codes[i+8].opcode == OpCodes.Ldarg_3 && codes[i+9].opcode == OpCodes.Callvirt && codes[i+10].opcode == OpCodes.Callvirt && codes[i+11].opcode == OpCodes.Ldfld && + (FieldInfo)codes[i+5].operand == AccessTools.Field(typeof(Farmer), nameof(Farmer.bathingClothes)) && (MethodInfo)codes[i+6].operand == AccessTools.Method(typeof(NetBool), "op_Implicit") && + (FieldInfo)codes[i+11].operand == AccessTools.Field(typeof(FarmerSprite.AnimationFrame), nameof(FarmerSprite.AnimationFrame.flip))) + { + Monitor.Log($"Transpiling Farmer.drawHairAndAccessories"); + + codes[i + 5].opcode = OpCodes.Nop; + codes[i + 5].operand = null; + + codes[i + 6].operand = AccessTools.Method(typeof(SwimUtils), nameof(SwimUtils.ShouldNotDrawHat)); + } + } + } + catch (Exception ex) + { + Monitor.Log($"Failed in {nameof(FarmerRenderer_drawHairAndAccessories_Transpiler)}:\n{ex}", LogLevel.Error); + } + + return codes.AsEnumerable(); + } + internal static void GameLocation_StartEvent_Postfix() { try @@ -375,14 +433,27 @@ public static void GameLocation_UpdateWhenCurrentLocation_Postfix(GameLocation _ Monitor.Log($"Failed in {nameof(GameLocation_UpdateWhenCurrentLocation_Postfix)}:\n{ex}", LogLevel.Error); } } + public static void GameLocation_performTouchAction_Prefix(string fullActionString) { try { - string text = fullActionString.Split(new char[] + string[] text = fullActionString.Split(new char[] { ' ' - })[0]; + }); + GameLocation_performTouchAction_PrefixArray(text); + } + catch (Exception ex) + { + Monitor.Log($"Failed in {nameof(GameLocation_performTouchAction_Prefix)}:\n{ex}", LogLevel.Error); + } + } + public static void GameLocation_performTouchAction_PrefixArray(string[] action) + { + try + { + string text = action[0]; if (text == "PoolEntrance") { if (!Game1.player.swimming.Value) @@ -393,7 +464,7 @@ public static void GameLocation_performTouchAction_Prefix(string fullActionStrin } catch (Exception ex) { - Monitor.Log($"Failed in {nameof(GameLocation_performTouchAction_Prefix)}:\n{ex}", LogLevel.Error); + Monitor.Log($"Failed in {nameof(GameLocation_performTouchAction_PrefixArray)}:\n{ex}", LogLevel.Error); } } public static void GameLocation_performTouchAction_Postfix(string fullActionString) @@ -460,7 +531,7 @@ public static void GameLocation_isCollidingPosition_Postfix(GameLocation __insta return; Vector2 next = SwimUtils.GetNextTile(); - //Monitor.Log($"Checking collide {SwimUtils.doesTileHaveProperty(__instance.map, (int)next.X, (int)next.Y, "Water", "Back") != null}"); + Monitor.Log($"Checking collide {SwimUtils.doesTileHaveProperty(__instance.map, (int)next.X, (int)next.Y, "Water", "Back") != null}"); if ((int)next.X <= 0 || (int)next.Y <= 0 || __instance.Map.Layers[0].LayerWidth <= (int)next.X || __instance.Map.Layers[0].LayerHeight <= (int)next.Y || SwimUtils.doesTileHaveProperty(__instance.map, (int)next.X, (int)next.Y, "Water", "Back") != null) { __result = false; @@ -478,6 +549,8 @@ public static void GameLocation_sinkDebris_Postfix(GameLocation __instance, bool if (__result == false || !Game1.IsMasterGame || !SwimUtils.DebrisIsAnItem(debris)) return; + Monitor.Log($"Sinking debris: {debris.itemId.Value} ({debris.item.Name})"); + if (ModEntry.diveMaps.ContainsKey(__instance.Name) && ModEntry.diveMaps[__instance.Name].DiveLocations.Count > 0) { Point pos = new Point((int)chunkTile.X, (int)chunkTile.Y); diff --git a/Swim/SwimUtils.cs b/Swim/SwimUtils.cs index 437662af..732da9bb 100644 --- a/Swim/SwimUtils.cs +++ b/Swim/SwimUtils.cs @@ -3,11 +3,13 @@ using Microsoft.Xna.Framework.Input; using Netcode; using StardewModdingAPI; +using StardewModdingAPI.Utilities; using StardewValley; using StardewValley.Locations; using StardewValley.TerrainFeatures; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using xTile; @@ -120,50 +122,69 @@ public static bool IsMapUnderwater(string name) return ModEntry.diveMaps.ContainsKey(name) && ModEntry.diveMaps[name].Features.Contains("Underwater"); } - - public static void CheckIfMyButtonDown() + // Replaces myButtonDown because this is what that variable really did + public static bool isSafeToTryJump() { - // !IMP: Base conditions to prevent from swimming placed here. - if (Game1.player == null || Game1.player.currentLocation == null || Game1.player.currentLocation.waterTiles == null || !Context.IsPlayerFree || Helper.Input.IsDown(SButton.LeftShift) || - Game1.player.isRidingHorse()) + // Null checks + if (Game1.player == null || Game1.player.currentLocation == null || Game1.player.currentLocation.waterTiles == null) { - ModEntry.myButtonDown.Value = false; - return; + return false; } - if (Config.ReadyToSwim && - ( - Game1.isOneOfTheseKeysDown(Game1.input.GetKeyboardState(), Game1.options.moveUpButton) || - Game1.isOneOfTheseKeysDown(Game1.input.GetKeyboardState(), Game1.options.moveDownButton) || - Game1.isOneOfTheseKeysDown(Game1.input.GetKeyboardState(), Game1.options.moveLeftButton) || - Game1.isOneOfTheseKeysDown(Game1.input.GetKeyboardState(), Game1.options.moveRightButton) || - (Game1.options.gamepadControls && - ( - Game1.input.GetGamePadState().ThumbSticks.Left.Y > 0.25 || - Game1.input.GetGamePadState().IsButtonDown(Buttons.DPadUp) || - Game1.input.GetGamePadState().ThumbSticks.Left.Y < -0.25 || - Game1.input.GetGamePadState().IsButtonDown(Buttons.DPadDown) || - Game1.input.GetGamePadState().ThumbSticks.Left.X < -0.25 || - Game1.input.GetGamePadState().IsButtonDown(Buttons.DPadLeft) || - Game1.input.GetGamePadState().ThumbSticks.Left.X > 0.25 || - Game1.input.GetGamePadState().IsButtonDown(Buttons.DPadRight) - ) - ) - ) - ) + // Player state checks + if(!Context.IsPlayerFree || !Context.CanPlayerMove || Game1.player.isRidingHorse()) { + return false; + } - ModEntry.myButtonDown.Value = true; - return; + // Modded player state checks + if(Game1.player.millisecondsPlayed - SwimHelperEvents.lastJump.Value < 250 || IsMapUnderwater(Game1.player.currentLocation.Name)) + { + return false; } - if (Helper.Input.IsDown(Config.ManualJumpButton) && !(Game1.player.CurrentTool is StardewValley.Tools.Pan) && !(Game1.player.CurrentTool is StardewValley.Tools.FishingRod) && Config.EnableClickToSwim) + // Player input checks + if(!((Game1.player.isMoving() && Config.ReadyToSwim) || (Helper.Input.IsDown(Config.ManualJumpButton) && Config.EnableClickToSwim)) || Helper.Input.IsDown(SButton.LeftShift)) { - ModEntry.myButtonDown.Value = true; - return; + return false; } - ModEntry.myButtonDown.Value = false; + return true; + } + + private static readonly PerScreen surfacing = new PerScreen(); + public static void updateOxygenValue() + { + if (ModEntry.isUnderwater.Value) + { + if (ModEntry.oxygen.Value >= 0) + { + if (!IsWearingScubaGear()) + ModEntry.oxygen.Value--; + else + { + if (ModEntry.oxygen.Value < MaxOxygen()) + ModEntry.oxygen.Value++; + if (ModEntry.oxygen.Value < MaxOxygen()) + ModEntry.oxygen.Value++; + } + } + if (ModEntry.oxygen.Value < 0 && !surfacing.Value) + { + surfacing.Value = true; + Game1.playSound("pullItemFromWater"); + DiveLocation diveLocation = ModEntry.diveMaps[Game1.player.currentLocation.Name].DiveLocations.Last(); + DiveTo(diveLocation); + } + } + else + { + surfacing.Value = false; + if (ModEntry.oxygen.Value < MaxOxygen()) + ModEntry.oxygen.Value++; + if (ModEntry.oxygen.Value < MaxOxygen()) + ModEntry.oxygen.Value++; + } } public static int CheckForBuriedItem(Farmer who) @@ -249,21 +270,24 @@ public static int CheckForBuriedItem(Farmer who) public static bool IsWearingScubaGear() { - bool tank = ModEntry.scubaTankID.Value != -1 && Game1.player.shirtItem.Value != null && Game1.player.shirtItem.Value != null && Game1.player.shirtItem.Value.ParentSheetIndex + "" == ModEntry.scubaTankID.Value + ""; - bool mask = ModEntry.scubaMaskID.Value != -1 && Game1.player.hat.Value != null && Game1.player.hat.Value != null && Game1.player.hat.Value.ItemId == ModEntry.scubaMaskID.Value + ""; + bool tank = ModEntry.scubaTankID.Value != "" && Game1.player.shirtItem.Value != null && Game1.player.shirtItem.Value.ItemId == ModEntry.scubaTankID.Value; + bool mask = ModEntry.scubaMaskID.Value != "" && Game1.player.hat.Value != null && Game1.player.hat.Value.ItemId == ModEntry.scubaMaskID.Value; return tank && mask; } public static bool IsInWater() { - var tiles = Game1.player.currentLocation.waterTiles; + WaterTiles tiles = Game1.player.currentLocation.waterTiles; Point p = Game1.player.TilePoint; if (!Game1.player.swimming.Value && Game1.player.currentLocation.map.GetLayer("Buildings")?.PickTile(new Location(p.X, p.Y) * Game1.tileSize, Game1.viewport.Size) != null) + { + //Monitor.Log("Not in water"); return false; + } - return IsMapUnderwater(Game1.player.currentLocation.Name) + bool output = IsMapUnderwater(Game1.player.currentLocation.Name) || ( tiles != null @@ -278,48 +302,50 @@ public static bool IsInWater() ) ) ); + + //Monitor.Log(output ? "In water" : "Not in water"); + return output; } - public static List GetTilesInDirection(int count) + public static List GetTilesInDirection(int count, int direction) { List tiles = new List(); - int dir = Game1.player.FacingDirection; - if (dir == 1) + if (direction == 1) { for (int i = count; i > 0; i--) { - tiles.Add(Game1.player.Position + new Vector2(i, 0)); + tiles.Add(Game1.player.TilePoint.ToVector2() + new Vector2(i, 0)); } } - if (dir == 2) + if (direction == 2) { for (int i = count; i > 0; i--) { - tiles.Add(Game1.player.Position + new Vector2(0, i)); + tiles.Add(Game1.player.TilePoint.ToVector2() + new Vector2(0, i)); } } - if (dir == 3) + if (direction == 3) { for (int i = count; i > 0; i--) { - tiles.Add(Game1.player.Position - new Vector2(i, 0)); + tiles.Add(Game1.player.TilePoint.ToVector2() - new Vector2(i, 0)); } } - if (dir == 0) + if (direction == 0) { for (int i = count; i > 0; i--) { - tiles.Add(Game1.player.Position - new Vector2(0, i)); + tiles.Add(Game1.player.TilePoint.ToVector2() - new Vector2(0, i)); } } @@ -404,6 +430,7 @@ public static string doesTileHaveProperty(Map map, int xTile, int yTile, string } if (property != null) { + //Monitor.Log("Tile has property: " + property.ToString()); return property.ToString(); } return null; @@ -477,6 +504,11 @@ public static bool IsTilePassable(GameLocation location, Location tileLocation, bool result = passable == null && tile == null && tmp != null; return result; } + + public static bool isMouseButton(SButton button) + { + return button == SButton.MouseLeft || button == SButton.MouseRight || button == SButton.MouseMiddle || button == SButton.MouseX1 || button == SButton.MouseX2; + } public static bool DebrisIsAnItem(Debris debris) { return debris.debrisType.Value == Debris.DebrisType.OBJECT || debris.debrisType.Value == Debris.DebrisType.ARCHAEOLOGY || debris.debrisType.Value == Debris.DebrisType.RESOURCE || debris.item != null; @@ -484,7 +516,65 @@ public static bool DebrisIsAnItem(Debris debris) internal static bool CanSwimHere() { - return (!Config.SwimIndoors || Game1.player.currentLocation.IsOutdoors) && Game1.player.currentLocation is not BeachNightMarket && Game1.player.currentLocation is not VolcanoDungeon && Game1.player.currentLocation is not BathHousePool; + GameLocation location = Game1.player.currentLocation; + bool result = (!Config.SwimIndoors || location.IsOutdoors) && location is not VolcanoDungeon && location is not BathHousePool && !ModEntry.locationIsPool.Value; + if (!result) + return false; + + Point playerPosition = Game1.player.TilePoint; + + string property = doesTileHaveProperty(location.Map, playerPosition.X, playerPosition.Y, "TouchAction", "Back"); + + if (property == "PoolEntrance" || property == "ChangeIntoSwimsuit") + { + Monitor.Log("The current tile is a pool entrance! Disabling swimming in this location."); + ModEntry.locationIsPool.Value = true; + return false; + } + + return true; + } + + public static bool ShouldNotDrawHat(Farmer farmer) + { + return (!Config.DisplayHatWithSwimsuit) && farmer.bathingClothes.Value; + } + + /// + /// Gets the direction of one point relative to another. + /// + /// Point 1 is the starting point, and point 2 the endpoint. + /// Returns a cardinal direction using Stardew Valley's direction system (0 is up, 1 right, 2 down, and 3 left) + /// + /// The x coordinate of the first point. + /// The y coordinate of the first point. + /// The x coordinate of the second point. + /// The y coordinate of the second point. + /// A cardinal direction using Stardew Valley's direction system (0 is up, 1 right, 2 down, and 3 left) + public static int GetDirection(float x1, float y1, float x2, float y2) + { + if (Math.Abs(x1 - x2) > Math.Abs(y1 - y2)) + { + if (x2 - x1 > 0) + { + return 1; // Right + } + else + { + return 3; // Left + } + } + else + { + if (y2 - y1 > 0) + { + return 2; // Down + } + else + { + return 0; // Up + } + } } } } \ No newline at end of file diff --git a/Swim/[CP] Swim Items/assets/boots.png b/Swim/[CP] Swim Items/assets/boots.png new file mode 100644 index 00000000..da58ae57 Binary files /dev/null and b/Swim/[CP] Swim Items/assets/boots.png differ diff --git a/Swim/[CP] Swim Items/assets/hats.png b/Swim/[CP] Swim Items/assets/hats.png new file mode 100644 index 00000000..92c36c35 Binary files /dev/null and b/Swim/[CP] Swim Items/assets/hats.png differ diff --git a/Swim/[CP] Swim Items/assets/shirts.png b/Swim/[CP] Swim Items/assets/shirts.png new file mode 100644 index 00000000..2ece6291 Binary files /dev/null and b/Swim/[CP] Swim Items/assets/shirts.png differ diff --git a/Swim/[CP] Swim Items/assets/shoeColors.png b/Swim/[CP] Swim Items/assets/shoeColors.png new file mode 100644 index 00000000..93ff9920 Binary files /dev/null and b/Swim/[CP] Swim Items/assets/shoeColors.png differ diff --git a/Swim/[CP] Swim Items/content.json b/Swim/[CP] Swim Items/content.json new file mode 100644 index 00000000..e8d1d0de --- /dev/null +++ b/Swim/[CP] Swim Items/content.json @@ -0,0 +1,41 @@ +{ + "Format": "2.0.0", + "Changes": [ + { + "Action": "EditData", + "Target": "Data/Shirts", + "Entries":{ + "Swim_ScubaTank":{ + "Name": "Scuba Tank", + "DisplayName": "{{i18n:scubatank-name}}", + "Description": "{{i18n:scubatank-desc}}", + "Price": 50000, + "Texture": "Swim/shirts", + "SpriteIndex": 0 + } + } + + }, + { + "Action": "EditData", + "Target": "Data/Hats", + "Entries": { + "Swim_ScubaMask": + "Scuba Mask/{{i18n:scubamask-desc}}/true/true//{{i18n:scubamask-name}}/0/Swim\\hats" + } + }, + { + "Action": "EditData", + "Target": "Data/Boots", + "Entries":{ + "Swim_ScubaFins": + "Scuba Fins/{{i18n:scubafins-desc}}/50000/0/0/2/{{i18n:scubafins-name}}/Swim\\shoeColors/0/Swim\\boots" + } + }, + { + "Action": "Load", + "Target": "Swim/shirts, Swim/hats, Swim/boots, Swim/shoeColors", + "FromFile": "assets/{{TargetWithoutPath}}.png" + } + ] +} \ No newline at end of file diff --git a/Swim/[CP] Swim Items/i18n/default.json b/Swim/[CP] Swim Items/i18n/default.json new file mode 100644 index 00000000..d27cb039 --- /dev/null +++ b/Swim/[CP] Swim Items/i18n/default.json @@ -0,0 +1,8 @@ +{ + "scubatank-name": "Scuba Tank", + "scubatank-desc": "A scuba tank for your respirator.", + "scubamask-name": "Scuba Mask", + "scubamask-desc": "A respirator for your scuba tank.", + "scubafins-name": "Scuba Fins", + "scubafins-desc": "Makes you swim faster!" +} \ No newline at end of file diff --git a/Swim/[CP] Swim Items/i18n/es.json b/Swim/[CP] Swim Items/i18n/es.json new file mode 100644 index 00000000..a4b6b8e9 --- /dev/null +++ b/Swim/[CP] Swim Items/i18n/es.json @@ -0,0 +1,8 @@ +{ + "scubatank-name": "Tanque de bueco", + "scubatank-desc": "Un tanque de bueco para su respirador.", + "scubamask-name": "Máscara de bueco", + "scubamask-desc": "Un respirador para su tanque de bueco.", + "scubafins-name": "Aletas de bueco", + "scubafins-desc": "¡Le hacen nadar más rápido!" +} \ No newline at end of file diff --git a/Swim/[CP] Swim Items/i18n/ko.json b/Swim/[CP] Swim Items/i18n/ko.json new file mode 100644 index 00000000..38122181 --- /dev/null +++ b/Swim/[CP] Swim Items/i18n/ko.json @@ -0,0 +1,8 @@ +{ + "scubatank-name": "스쿠버 탱크", + "scubatank-desc": "물 속에서 호흡하기 위한 스쿠버 탱크.", + "scubamask-name": "스쿠버 마스크", + "scubamask-desc": "물 속에서 호흡하기 위한 스쿠버 마스크.", + "scubafins-name": "스쿠버 핀", + "scubafins-desc": "더 빨리 헤엄칠 수 있습니다!" +} \ No newline at end of file diff --git a/Swim/[CP] Swim Items/i18n/pt.json b/Swim/[CP] Swim Items/i18n/pt.json new file mode 100644 index 00000000..bebcb8c5 --- /dev/null +++ b/Swim/[CP] Swim Items/i18n/pt.json @@ -0,0 +1,8 @@ +{ + "scubatank-name": "Tanque de mergulho", + "scubatank-desc": "Um tanque de mergulho para seu respirador.", + "scubamask-name": "Máscara de mergulho", + "scubamask-desc": "Um respirador para o seu tanque de mergulho.", + "scubafins-name": "Barbatanas de mergulho", + "scubafins-desc": "Faz você nadar mais rápido!" +} \ No newline at end of file diff --git a/Swim/[CP] Swim Items/i18n/ru.json b/Swim/[CP] Swim Items/i18n/ru.json new file mode 100644 index 00000000..f7e49ff4 --- /dev/null +++ b/Swim/[CP] Swim Items/i18n/ru.json @@ -0,0 +1,8 @@ +{ + "scubatank-name": "Костюм аквалангиста", + "scubatank-desc": "Костюм аквалангиста с кислородным баллоном.", + "scubamask-name": "Маска аквалангиста", + "scubamask-desc": "Ребризер для длительных погружений.", + "scubafins-name": "Ласты", + "scubafins-desc": "Позволяет плыть быстрее!" +} \ No newline at end of file diff --git a/Swim/[CP] Swim Items/i18n/zh.json b/Swim/[CP] Swim Items/i18n/zh.json new file mode 100644 index 00000000..0a2f84be --- /dev/null +++ b/Swim/[CP] Swim Items/i18n/zh.json @@ -0,0 +1,8 @@ +{ + "scubatank-name": "水肺深潜气瓶", + "scubatank-desc": "为你的水肺(自携式水下呼吸装置)准备一个气瓶。", + "scubamask-name": "水肺深潜面罩", + "scubamask-desc": "为你的气瓶配置的呼吸器。", + "scubafins-name": "水肺深潜脚跟鳍", + "scubafins-desc": "让你游得更快!" +} \ No newline at end of file diff --git a/Swim/[CP] Swim Items/manifest.json b/Swim/[CP] Swim Items/manifest.json new file mode 100644 index 00000000..60c8fa7c --- /dev/null +++ b/Swim/[CP] Swim Items/manifest.json @@ -0,0 +1,11 @@ +{ + "Name": "Swim Items", + "Author": "aedenthorn", + "Version": "1.0.0", + "Description": "Adds swim mod's scuba gear.", + "UniqueID": "aedenthorn.SwimItems", + "UpdateKeys": [], + "ContentPackFor": { + "UniqueID": "Pathoschild.ContentPatcher" + } +} \ No newline at end of file diff --git a/Swim/[CP] Swim Maps/assets/27.tbin b/Swim/[CP] Swim Maps/assets/27.tbin new file mode 100644 index 00000000..ca784237 Binary files /dev/null and b/Swim/[CP] Swim Maps/assets/27.tbin differ diff --git a/Swim/[CP] Swim Maps/assets/Abigail.tbin b/Swim/[CP] Swim Maps/assets/Abigail.tbin new file mode 100644 index 00000000..7ef46651 Binary files /dev/null and b/Swim/[CP] Swim Maps/assets/Abigail.tbin differ diff --git a/Swim/[CP] Swim Maps/assets/Cave.tbin b/Swim/[CP] Swim Maps/assets/Cave.tbin new file mode 100644 index 00000000..c3912abe Binary files /dev/null and b/Swim/[CP] Swim Maps/assets/Cave.tbin differ diff --git a/Swim/[CP] Swim Maps/assets/CrystalCave.tbin b/Swim/[CP] Swim Maps/assets/CrystalCave.tbin new file mode 100644 index 00000000..4127d242 Binary files /dev/null and b/Swim/[CP] Swim Maps/assets/CrystalCave.tbin differ diff --git a/Swim/[CP] Swim Maps/assets/CrystalCaveDark.tbin b/Swim/[CP] Swim Maps/assets/CrystalCaveDark.tbin new file mode 100644 index 00000000..4d48cfa6 Binary files /dev/null and b/Swim/[CP] Swim Maps/assets/CrystalCaveDark.tbin differ diff --git a/Swim/[CP] Swim Maps/assets/DesertTiles.png b/Swim/[CP] Swim Maps/assets/DesertTiles.png new file mode 100644 index 00000000..1c4ebbac Binary files /dev/null and b/Swim/[CP] Swim Maps/assets/DesertTiles.png differ diff --git a/Swim/[CP] Swim Maps/assets/ElliottHouseTiles.png b/Swim/[CP] Swim Maps/assets/ElliottHouseTiles.png new file mode 100644 index 00000000..ef03584f Binary files /dev/null and b/Swim/[CP] Swim Maps/assets/ElliottHouseTiles.png differ diff --git a/Swim/[CP] Swim Maps/assets/Forest.tbin b/Swim/[CP] Swim Maps/assets/Forest.tbin new file mode 100644 index 00000000..e4a1c19b Binary files /dev/null and b/Swim/[CP] Swim Maps/assets/Forest.tbin differ diff --git a/Swim/[CP] Swim Maps/assets/UnderwaterBeach.tbin b/Swim/[CP] Swim Maps/assets/UnderwaterBeach.tbin new file mode 100644 index 00000000..e626f47e Binary files /dev/null and b/Swim/[CP] Swim Maps/assets/UnderwaterBeach.tbin differ diff --git a/Swim/[CP] Swim Maps/assets/UnderwaterMountain.tbin b/Swim/[CP] Swim Maps/assets/UnderwaterMountain.tbin new file mode 100644 index 00000000..9cee2aa3 Binary files /dev/null and b/Swim/[CP] Swim Maps/assets/UnderwaterMountain.tbin differ diff --git a/Swim/[CP] Swim Maps/assets/UnderwaterTown.tbin b/Swim/[CP] Swim Maps/assets/UnderwaterTown.tbin new file mode 100644 index 00000000..9b373e25 Binary files /dev/null and b/Swim/[CP] Swim Maps/assets/UnderwaterTown.tbin differ diff --git a/Swim/[CP] Swim Maps/assets/mine.png b/Swim/[CP] Swim Maps/assets/mine.png new file mode 100644 index 00000000..a64f9263 Binary files /dev/null and b/Swim/[CP] Swim Maps/assets/mine.png differ diff --git a/Swim/[CP] Swim Maps/assets/mine_dark.png b/Swim/[CP] Swim Maps/assets/mine_dark.png new file mode 100644 index 00000000..a3e5d3f3 Binary files /dev/null and b/Swim/[CP] Swim Maps/assets/mine_dark.png differ diff --git a/Swim/[CP] Swim Maps/assets/mine_frost.png b/Swim/[CP] Swim Maps/assets/mine_frost.png new file mode 100644 index 00000000..87b06930 Binary files /dev/null and b/Swim/[CP] Swim Maps/assets/mine_frost.png differ diff --git a/Swim/[CP] Swim Maps/assets/spring_beach.png b/Swim/[CP] Swim Maps/assets/spring_beach.png new file mode 100644 index 00000000..afb05072 Binary files /dev/null and b/Swim/[CP] Swim Maps/assets/spring_beach.png differ diff --git a/Swim/[CP] Swim Maps/assets/spring_outdoorsTileSheet.png b/Swim/[CP] Swim Maps/assets/spring_outdoorsTileSheet.png new file mode 100644 index 00000000..8b4568ca Binary files /dev/null and b/Swim/[CP] Swim Maps/assets/spring_outdoorsTileSheet.png differ diff --git a/Swim/[CP] Swim Maps/content.json b/Swim/[CP] Swim Maps/content.json new file mode 100644 index 00000000..6ff01ee8 --- /dev/null +++ b/Swim/[CP] Swim Maps/content.json @@ -0,0 +1,71 @@ +{ + "Format": "2.0.0", + "Changes": [ + { + "Action": "Load", + "Target": "Maps/UnderwaterMountain, Maps/UnderwaterBeach, Maps/CrystalCave, Maps/CrystalCaveDark", + "FromFile": "assets/{{TargetWithoutPath}}.tbin" + }, + { + "Action": "Load", + "Target": "Maps/ScubaMaskCave", + "FromFile": "assets/Cave.tbin" + }, + { + "Action": "Load", + "Target": "Maps/ScubaAbigailCave", + "FromFile": "assets/Abigail.tbin" + }, + { + "Action": "EditData", + "Target": "Data/Locations", + "Entries": { + "Custom_UnderwaterMountain": { + "DisplayName": "Underwater Mountain", + "CreateOnLoad": { + "MapPath": "Maps/UnderwaterMountain" + }, + "CanPlantHere": false, + "ExcludeFromNpcPathfinding": true, + "MusicDefault": "starshoot" + }, + "Custom_UnderwaterBeach": { + "DisplayName": "Underwater Beach", + "CreateOnLoad": { + "MapPath": "Maps/UnderwaterBeach" + }, + "CanPlantHere": false, + "ExcludeFromNpcPathfinding": true, + "MusicDefault": "shaneTheme" + }, + "Custom_ScubaCrystalCave": { + "DisplayName": "Ancient Mariner's Cave", + "CreateOnLoad": { + "MapPath": "Maps/CrystalCaveDark" + }, + "CanPlantHere": false, + "ExcludeFromNpcPathfinding": true, + "MusicDefault": "SettlingIn" + }, + "Custom_ScubaCave": { + "DisplayName": "Scuba Mask Cave", + "CreateOnLoad": { + "MapPath": "Maps/ScubaMaskCave" + }, + "CanPlantHere": false, + "ExcludeFromNpcPathfinding": true, + "MusicDefault": "stardrop" + }, + "Custom_ScubaAbigailCave": { + "DisplayName": "Prairie King Cave", + "CreateOnLoad": { + "MapPath": "Maps/ScubaAbigailCave" + }, + "CanPlantHere": false, + "ExcludeFromNpcPathfinding": true, + "MusicDefault": "Cowboy_OVERWORLD" + } + } + } + ] +} \ No newline at end of file diff --git a/Swim/[CP] Swim Maps/manifest.json b/Swim/[CP] Swim Maps/manifest.json new file mode 100644 index 00000000..ea55f79b --- /dev/null +++ b/Swim/[CP] Swim Maps/manifest.json @@ -0,0 +1,11 @@ +{ + "Name": "Swim Mod Dive Maps", + "Author": "aedenthorn", + "Version": "0.1.1", + "Description": "Adds underwater maps.", + "UniqueID": "aedenthorn.SwimDiveMaps", + "MinimumApiVersion": "3.0", + "ContentPackFor": { + "UniqueID": "Pathoschild.ContentPatcher" + } +} diff --git a/Swim/assets/json-assets/Boots/Scuba Fins/boots.json b/Swim/assets/json-assets/Boots/Scuba Fins/boots.json index 76b24900..ae1d9bdc 100644 --- a/Swim/assets/json-assets/Boots/Scuba Fins/boots.json +++ b/Swim/assets/json-assets/Boots/Scuba Fins/boots.json @@ -5,9 +5,8 @@ "PurchasePrice": 50000, "Defense": 0, "Immunity": 0, - "NameLocalization": - { - // "es": "", + "NameLocalization": { + "es": "Aletas de bueco", "ko": "스쿠버 핀", // "de": "", // "fr": "", @@ -17,11 +16,10 @@ "pt": "Barbatanas de mergulho", "ru": "Ласты", // "tr": "", - "zh":"水肺深潜脚跟鳍", + "zh": "水肺深潜脚跟鳍" }, - "DescriptionLocalization": - { - // "es": "", + "DescriptionLocalization": { + "es": "¡Le hacen nadar más rápido!", "ko": "더 빨리 헤엄칠 수 있습니다!", // "de": "", // "fr": "", @@ -31,6 +29,6 @@ "pt": "Faz você nadar mais rápido", "ru": "Позволяет плыть быстрее!", // "tr": "", - "zh":"让你游得更快!", + "zh": "让你游得更快!" }, } diff --git a/Swim/assets/json-assets/Hats/Scuba Mask/hat.json b/Swim/assets/json-assets/Hats/Scuba Mask/hat.json index bb1e5390..7155722c 100644 --- a/Swim/assets/json-assets/Hats/Scuba Mask/hat.json +++ b/Swim/assets/json-assets/Hats/Scuba Mask/hat.json @@ -4,9 +4,8 @@ "PurchasePrice": "50000", "ShowHair": true, "IgnoreHairstyleOffset": true, - "NameLocalization": - { - // "es": "", + "NameLocalization": { + "es": "Máscara de bueco", "ko": "스쿠버 마스크", // "de": "", // "fr": "", @@ -16,11 +15,10 @@ "pt": "Máscara de mergulho", "ru": "Маска аквалангиста", // "tr": "", - "zh":"水肺深潜面罩", + "zh": "水肺深潜面罩" }, - "DescriptionLocalization": - { - // "es": "", + "DescriptionLocalization": { + "es": "Un respirador para su tanque de bueco.", "ko": "물 속에서 호흡하기 위한 스쿠버 마스크.", // "de": "", // "fr": "", @@ -30,6 +28,6 @@ "pt": "Um respirador para o seu tanque de mergulho", "ru": "Ребризер для длительных погружений.", // "tr": "", - "zh":"为你的气瓶配置的呼吸器。", + "zh": "为你的气瓶配置的呼吸器。" }, } \ No newline at end of file diff --git a/Swim/assets/json-assets/Shirts/Scuba Tank/SpriteSheet.png b/Swim/assets/json-assets/Shirts/Scuba Tank/SpriteSheet.png new file mode 100644 index 00000000..2ece6291 Binary files /dev/null and b/Swim/assets/json-assets/Shirts/Scuba Tank/SpriteSheet.png differ diff --git a/Swim/assets/json-assets/Shirts/Scuba Tank/shirt.json b/Swim/assets/json-assets/Shirts/Scuba Tank/shirt.json index e4d6a551..16c6dc56 100644 --- a/Swim/assets/json-assets/Shirts/Scuba Tank/shirt.json +++ b/Swim/assets/json-assets/Shirts/Scuba Tank/shirt.json @@ -4,9 +4,8 @@ "HasFemaleVariant": true, "Price": 100000, "Dyeable": true, - "NameLocalization": - { - // "es": "", + "NameLocalization": { + "es": "Tanque de bueco", "ko": "스쿠버 탱크", // "de": "", // "fr": "", @@ -16,11 +15,10 @@ "pt": "Tanque de mergulho", "ru": "Костюм аквалангиста", // "tr": "", - "zh":"水肺深潜气瓶", + "zh": "水肺深潜气瓶" }, - "DescriptionLocalization": - { - // "es": "", + "DescriptionLocalization": { + "es": "Un tanque de bueco para su respirador.", "ko": "물 속에서 호흡하기 위한 스쿠버 탱크.", // "de": "", // "fr": "", @@ -30,6 +28,6 @@ "pt": "Um tanque de mergulho para seu respirador", "ru": "Костюм аквалангиста с кислородным баллоном.", // "tr": "", - "zh":"为你的水肺(自携式水下呼吸装置)准备一个气瓶。", + "zh": "为你的水肺(自携式水下呼吸装置)准备一个气瓶。" }, } diff --git a/Swim/assets/json-assets/content-pack.json b/Swim/assets/json-assets/content-pack.json deleted file mode 100644 index c57481fe..00000000 --- a/Swim/assets/json-assets/content-pack.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Name": "Swim Mod Content Pack", - "Description": "JA content pack for Swim Mod", - "Version": "1.0.0", - "Author": "aedenthorn" -} diff --git a/Swim/assets/swim-map-content.json b/Swim/assets/swim-map-content.json index 1b718f89..51d62a7c 100644 --- a/Swim/assets/swim-map-content.json +++ b/Swim/assets/swim-map-content.json @@ -45,15 +45,15 @@ "OtherMapName":"Town", }, { - "ThisMapEdge":"Right", - "FirstTile": 14, - "LastTile": 26, + "ThisMapEdge": "Left", + "FirstTile": 0, + "LastTile": 49, "DestinationHorizontal": false, - "OtherMapIndex": 119, - "OtherMapFirstTile": 106, + "OtherMapIndex": 120, + "OtherMapFirstTile": 104, "OtherMapLastTile": 119, - "OtherMapName":"Beach", - }, + "OtherMapName": "Forest" + } ] }, { @@ -93,8 +93,8 @@ "LastTile": 48, "DestinationHorizontal": false, "OtherMapIndex": 0, - "OtherMapFirstTile": 96, - "OtherMapLastTile": 103, + "OtherMapFirstTile": 95, + "OtherMapLastTile": 104, "OtherMapName":"Town", }, { @@ -173,12 +173,12 @@ "EdgeWarps":[ { "ThisMapEdge":"Bottom", - "FirstTile": 64, - "LastTile": 78, + "FirstTile": 62, + "LastTile": 85, "DestinationHorizontal": true, - "OtherMapIndex": 0, - "OtherMapFirstTile": 92, - "OtherMapLastTile": 96, + "OtherMapIndex": 5, + "OtherMapFirstTile": 95, + "OtherMapLastTile": 95, "OtherMapName":"Town", }, ], @@ -198,7 +198,7 @@ }, { "ThisMapEdge":"Top", - "FirstTile": 92, + "FirstTile": 94, "LastTile": 96, "DestinationHorizontal": true, "OtherMapIndex": 40, @@ -208,8 +208,8 @@ }, { "ThisMapEdge":"Left", - "FirstTile": 96, - "LastTile": 103, + "FirstTile": 95, + "LastTile": 104, "DestinationHorizontal": false, "OtherMapIndex": 119, "OtherMapFirstTile": 39, diff --git a/Swim/manifest.json b/Swim/manifest.json index d8d87256..e822e285 100644 --- a/Swim/manifest.json +++ b/Swim/manifest.json @@ -1,16 +1,16 @@ { "Name": "Swim Mod", "Author": "aedenthorn", - "Version": "3.5.4", + "Version": "3.5.5", "Description": "Allows you to swim.", "UniqueID": "aedenthorn.Swim", "EntryDll": "Swim.dll", - "MinimumApiVersion": "3.16.0", - "UpdateKeys": ["Nexus:6326"], + "MinimumApiVersion": "4.0.0", + "UpdateKeys": [ "Nexus:6326" ], "Dependencies": [ - { + { "UniqueID": "spacechase0.JsonAssets", - "IsRequired": false - } + "IsRequired": false + } ] } \ No newline at end of file