diff --git a/CustomFixedDialogue/CustomFixedDialogue.csproj b/CustomFixedDialogue/CustomFixedDialogue.csproj
new file mode 100644
index 00000000..950e707d
--- /dev/null
+++ b/CustomFixedDialogue/CustomFixedDialogue.csproj
@@ -0,0 +1,69 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {5CFBC975-48DC-40A0-9478-E81F76D7C8C2}
+ Library
+ Properties
+ CustomFixedDialogue
+ CustomFixedDialogue
+ v4.5.2
+ 512
+ true
+
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ G:\ga\Stardew Valley\smapi-internal\0Harmony.dll
+
+
+ G:\ga\Stardew Valley\StardewModdingAPI.exe
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
\ No newline at end of file
diff --git a/CustomFixedDialogue/DialoguePatches.cs b/CustomFixedDialogue/DialoguePatches.cs
new file mode 100644
index 00000000..4f8a57f5
--- /dev/null
+++ b/CustomFixedDialogue/DialoguePatches.cs
@@ -0,0 +1,89 @@
+using StardewModdingAPI;
+using StardewValley;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace CustomFixedDialogue
+{
+ internal class DialoguePatches
+ {
+ private static IMonitor Monitor;
+ private static IModHelper Helper;
+ private static string prefix = "CustomFixedDialogue";
+ public static void Initialize(IMonitor monitor, IModHelper helper)
+ {
+ Monitor = monitor;
+ Helper = helper;
+ }
+ public static void LocalizedContentManager_LoadString_Postfix(string path, ref string __result)
+ {
+ try
+ {
+ if (path.StartsWith("Data\\ExtraDialogue"))
+ {
+ __result = $"{prefix}{path.Replace("Data\\ExtraDialogue:", "ExtraDialogue_")}^{__result}";
+ Monitor.Log($"edited dialogue: {__result}");
+ }
+ else if (path.StartsWith("Strings\\StringsFromCSFiles:NPC.cs."))
+ {
+ __result = $"{prefix}{path.Replace("Strings\\StringsFromCSFiles:", "")}^{__result}";
+ Monitor.Log($"edited dialogue: {__result}");
+ }
+ }
+ catch (Exception ex)
+ {
+ Monitor.Log($"Failed in {nameof(LocalizedContentManager_LoadString_Postfix)}:\n{ex}", LogLevel.Error);
+ }
+ }
+
+
+ public static void LocalizedContentManager_LoadString_Postfix2(string path, ref string __result)
+ {
+ try
+ {
+ if (path.StartsWith("Data\\ExtraDialogue"))
+ {
+ __result = $"{prefix}{path.Replace("Data\\ExtraDialogue:", "ExtraDialogue_")}^{__result}";
+ }
+ else if (path.StartsWith("Strings\\StringsFromCSFiles:NPC.cs."))
+ {
+ __result = $"{prefix}{path.Replace("Strings\\StringsFromCSFiles:", "")}^{__result}";
+ }
+ }
+ catch (Exception ex)
+ {
+ Monitor.Log($"Failed in {nameof(LocalizedContentManager_LoadString_Postfix2)}:\n{ex}", LogLevel.Error);
+ }
+ }
+
+ public static void Dialogue_parseDialogueString_Prefix(Dialogue __instance, ref string masterString)
+ {
+ try
+ {
+ if (masterString.StartsWith(prefix))
+ {
+ Dictionary dialogueDic = Helper.Content.Load>($"Characters/Dialogue/{__instance.speaker.Name}", ContentSource.GameContent);
+ string key = masterString.Substring(prefix.Length).Split('^')[0];
+ if (dialogueDic.ContainsKey(key))
+ {
+ Monitor.Log($"{__instance.speaker.Name} has dialogue for {key}", LogLevel.Debug);
+ masterString = dialogueDic[key];
+ }
+ else
+ {
+ masterString = string.Join("^", masterString.Split('^').Skip(1));
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Monitor.Log($"Failed in {nameof(Dialogue_parseDialogueString_Prefix)}:\n{ex}", LogLevel.Error);
+ }
+ }
+ public static void Dialogue_CTOR_Prefix()
+ {
+ Monitor.Log($"WORKING NOW", LogLevel.Error);
+ }
+ }
+}
\ No newline at end of file
diff --git a/CustomFixedDialogue/ModConfig.cs b/CustomFixedDialogue/ModConfig.cs
new file mode 100644
index 00000000..8e04dbe1
--- /dev/null
+++ b/CustomFixedDialogue/ModConfig.cs
@@ -0,0 +1,6 @@
+namespace CustomFixedDialogue
+{
+ public class ModConfig
+ {
+ }
+}
\ No newline at end of file
diff --git a/CustomFixedDialogue/ModEntry.cs b/CustomFixedDialogue/ModEntry.cs
new file mode 100644
index 00000000..4abaa467
--- /dev/null
+++ b/CustomFixedDialogue/ModEntry.cs
@@ -0,0 +1,50 @@
+using Harmony;
+using StardewModdingAPI;
+using StardewValley;
+using System;
+using System.Reflection;
+
+namespace CustomFixedDialogue
+{
+ public class ModEntry : Mod
+ {
+ public static ModEntry context;
+
+ internal static ModConfig Config;
+
+
+ /// The mod entry point, called after the mod is first loaded.
+ /// Provides simplified APIs for writing mods.
+ public override void Entry(IModHelper helper)
+ {
+ context = this;
+ Config = Helper.ReadConfig();
+
+ DialoguePatches.Initialize(Monitor, helper);
+
+ var harmony = HarmonyInstance.Create(ModManifest.UniqueID);
+ HarmonyInstance.DEBUG = true;
+
+ /*
+ harmony.Patch(
+ original: AccessTools.Constructor(typeof(Dialogue), new Type[] { typeof(string), typeof(NPC) }),
+ prefix: new HarmonyMethod(typeof(DialoguePatches), nameof(DialoguePatches.Dialogue_CTOR_Prefix)),
+ postfix: new HarmonyMethod(typeof(DialoguePatches), nameof(DialoguePatches.Dialogue_CTOR_Prefix))
+ );
+ */
+ harmony.Patch(
+ original: AccessTools.Method(typeof(Dialogue), "parseDialogueString"),
+ prefix: new HarmonyMethod(typeof(DialoguePatches), nameof(DialoguePatches.Dialogue_parseDialogueString_Prefix))
+ );
+
+ harmony.Patch(
+ original: AccessTools.Method(typeof(LocalizedContentManager), nameof(LocalizedContentManager.LoadString), new Type[] { typeof(string) }),
+ postfix: new HarmonyMethod(typeof(DialoguePatches), nameof(DialoguePatches.LocalizedContentManager_LoadString_Postfix))
+ );
+ harmony.Patch(
+ original: AccessTools.Method(typeof(LocalizedContentManager), nameof(LocalizedContentManager.LoadString), new Type[] { typeof(string), typeof(object) }),
+ postfix: new HarmonyMethod(typeof(DialoguePatches), nameof(DialoguePatches.LocalizedContentManager_LoadString_Postfix2))
+ );
+ }
+ }
+}
diff --git a/CustomFixedDialogue/Properties/AssemblyInfo.cs b/CustomFixedDialogue/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..4d0beb31
--- /dev/null
+++ b/CustomFixedDialogue/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("CustomFixedDialogue")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("CustomFixedDialogue")]
+[assembly: AssemblyCopyright("Copyright © 2020")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("5cfbc975-48dc-40a0-9478-e81f76d7c8c2")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/CustomFixedDialogue/manifest.json b/CustomFixedDialogue/manifest.json
new file mode 100644
index 00000000..395ff14a
--- /dev/null
+++ b/CustomFixedDialogue/manifest.json
@@ -0,0 +1,22 @@
+{
+ "Name": "Custom Fixed Dialogue",
+ "Author": "aedenthorn",
+ "Version": "0.1.1",
+ "Description": "Customize fixed dialogue for each NPC.",
+ "UniqueID": "aedenthorn.CustomFixedDialogue",
+ "EntryDll": "CustomFixedDialogue.dll",
+ "MinimumApiVersion": "3.4.0",
+ "ModUpdater": {
+ "Repository": "StardewValleyMods",
+ "User": "aedenthorn",
+ "Directory": "_releases",
+ "ModFolder": "CustomFixedDialogue"
+ },
+ "UpdateKeys": ["Nexus:6358"],
+ "Dependencies": [
+ {
+ "UniqueID": "Platonymous.ModUpdater",
+ "IsRequired": false
+ },
+ ]
+}
\ No newline at end of file
diff --git a/CustomFixedDialogue/packages.config b/CustomFixedDialogue/packages.config
new file mode 100644
index 00000000..02bb7162
--- /dev/null
+++ b/CustomFixedDialogue/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/MultipleSpouses 1.0.4.zip b/MultipleSpouses 1.0.4.zip
new file mode 100644
index 00000000..af916125
Binary files /dev/null and b/MultipleSpouses 1.0.4.zip differ
diff --git a/MultipleSpouses/HelperEvents.cs b/MultipleSpouses/HelperEvents.cs
index b8163911..30345a00 100644
--- a/MultipleSpouses/HelperEvents.cs
+++ b/MultipleSpouses/HelperEvents.cs
@@ -273,6 +273,7 @@ public static void GameLoop_OneSecondUpdateTicked(object sender, OneSecondUpdate
{
if (allSpouses.Contains(character.Name))
{
+
if (Misc.IsInBed(fh, character.GetBoundingBox()))
{
character.farmerPassesThrough = true;
diff --git a/ProceduralDungeons/ProceduralDungeons.csproj b/ProceduralDungeons/ProceduralDungeons.csproj
index e4b7802f..49e94823 100644
--- a/ProceduralDungeons/ProceduralDungeons.csproj
+++ b/ProceduralDungeons/ProceduralDungeons.csproj
@@ -14,6 +14,7 @@
true
+ true
true
diff --git a/RandomNPC/ModEntry.cs b/RandomNPC/ModEntry.cs
index 55454b17..57a3759a 100644
--- a/RandomNPC/ModEntry.cs
+++ b/RandomNPC/ModEntry.cs
@@ -205,6 +205,8 @@ private void UpdateTicking(object sender, UpdateTickingEventArgs e)
foreach (RNPC rnpc in RNPCs)
{
NPC npc = Game1.getCharacterFromName(rnpc.nameID);
+ if (npc == null)
+ continue;
GameLocation currentLocation = npc.currentLocation;
int dir = npc.getDirection();
if (currentLocation.isCollidingPosition(npc.nextPosition(dir), Game1.viewport, false, 0, false, npc))
diff --git a/StardewValleyMods.sln b/StardewValleyMods.sln
index d08a8ef0..515f8fc2 100644
--- a/StardewValleyMods.sln
+++ b/StardewValleyMods.sln
@@ -37,11 +37,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OutdoorButterflyHutch", "Ou
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Swim", "Swim\Swim.csproj", "{D06D6A79-D6AA-45E4-9E39-9D150F8A5365}"
EndProject
-Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "_releases", "_releases\_releases.shproj", "{DAE274FA-6F23-42E4-B235-DF130EED1A00}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CustomFixedDialogue", "CustomFixedDialogue\CustomFixedDialogue.csproj", "{5CFBC975-48DC-40A0-9478-E81F76D7C8C2}"
+EndProject
+Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "_releases", "_releases\_releases.shproj", "{047040E0-3D07-46E5-852B-D1D45CD57BB8}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
- _releases\_releases.projitems*{dae274fa-6f23-42e4-b235-df130eed1a00}*SharedItemsImports = 13
+ _releases\_releases.projitems*{047040e0-3d07-46e5-852b-d1d45cd57bb8}*SharedItemsImports = 13
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -186,6 +188,14 @@ Global
{D06D6A79-D6AA-45E4-9E39-9D150F8A5365}.Release|Any CPU.Build.0 = Release|Any CPU
{D06D6A79-D6AA-45E4-9E39-9D150F8A5365}.Release|x86.ActiveCfg = Release|Any CPU
{D06D6A79-D6AA-45E4-9E39-9D150F8A5365}.Release|x86.Build.0 = Release|Any CPU
+ {5CFBC975-48DC-40A0-9478-E81F76D7C8C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5CFBC975-48DC-40A0-9478-E81F76D7C8C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5CFBC975-48DC-40A0-9478-E81F76D7C8C2}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {5CFBC975-48DC-40A0-9478-E81F76D7C8C2}.Debug|x86.Build.0 = Debug|Any CPU
+ {5CFBC975-48DC-40A0-9478-E81F76D7C8C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5CFBC975-48DC-40A0-9478-E81F76D7C8C2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5CFBC975-48DC-40A0-9478-E81F76D7C8C2}.Release|x86.ActiveCfg = Release|Any CPU
+ {5CFBC975-48DC-40A0-9478-E81F76D7C8C2}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/_releases/Swim 0.9.1.zip b/Swim 0.9.2.zip
similarity index 91%
rename from _releases/Swim 0.9.1.zip
rename to Swim 0.9.2.zip
index 5105c8ba..4609428b 100644
Binary files a/_releases/Swim 0.9.1.zip and b/Swim 0.9.2.zip differ
diff --git a/Swim/AbigailProjectile.cs b/Swim/AbigailProjectile.cs
new file mode 100644
index 00000000..eba35319
--- /dev/null
+++ b/Swim/AbigailProjectile.cs
@@ -0,0 +1,85 @@
+using Microsoft.Xna.Framework;
+using StardewValley;
+using StardewValley.Monsters;
+using StardewValley.Network;
+using StardewValley.Projectiles;
+using StardewValley.TerrainFeatures;
+
+namespace Swim
+{
+ 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 firingSound, bool explode, bool damagesMonsters = false, GameLocation location = null, Character firer = null, bool spriteFromObjectSheet = false, BasicProjectile.onCollisionBehavior collisionBehavior = null) : base(damageToFarmer, parentSheetIndex, bouncesTillDestruct, tailLength, rotationVelocity, xVelocity, yVelocity, startingPosition, collisionSound, firingSound, explode, true, location, firer, true, null)
+ {
+ IgnoreLocationCollision = true;
+ myCollisionSound = collisionSound;
+ myExplode = explode;
+ }
+
+ public override void behaviorOnCollisionWithMonster(NPC n, GameLocation location)
+ {
+ this.explosionAnimation(location);
+ if (n is Monster)
+ {
+ location.characters.Remove(n);
+ return;
+ }
+ }
+ private void explosionAnimation(GameLocation location)
+ {
+ Rectangle sourceRect = Game1.getSourceRectForStandardTileSheet(this.spriteFromObjectSheet ? Game1.objectSpriteSheet : Projectile.projectileSheet, this.currentTileSheetIndex, -1, -1);
+ sourceRect.X += 28;
+ sourceRect.Y += 28;
+ sourceRect.Width = 8;
+ sourceRect.Height = 8;
+ int whichDebris = 12;
+ int value = this.currentTileSheetIndex.Value;
+ switch (value)
+ {
+ 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)
+ {
+ whichDebris = 14;
+ }
+ break;
+ }
+ if (this.spriteFromObjectSheet)
+ {
+ Game1.createRadialDebris(location, whichDebris, (int)(this.position.X + 32f) / 64, (int)(this.position.Y + 32f) / 64, 6, false, -1, false, -1);
+ }
+ else
+ {
+ Game1.createRadialDebris(location, "TileSheets\\Projectiles", sourceRect, 4, (int)this.position.X + 32, (int)this.position.Y + 32, 12, (int)(this.position.Y / 64f) + 1);
+ }
+ if (myCollisionSound != null && !this.myCollisionSound.Equals(""))
+ {
+ location.playSound(this.myCollisionSound, NetAudio.SoundContext.Default);
+ }
+ destroyMe = true;
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/Swim/DiveMapData.cs b/Swim/DiveMapData.cs
index 9e928cb0..fe454ce6 100644
--- a/Swim/DiveMapData.cs
+++ b/Swim/DiveMapData.cs
@@ -13,6 +13,7 @@ public class DiveMap
public string Name { get; set; }
public List Features { get; set; } = new List();
public List DiveLocations { get; set; } = new List();
+ public List EdgeWarps { get; set; } = new List();
}
public class DiveLocation
@@ -34,4 +35,15 @@ public class DivePosition
public int X { get; set; }
public int Y { get; set; }
}
+ public class EdgeWarp
+ {
+ public string ThisMapEdge { get; set; } = null;
+ public int FirstTile { get; set; }
+ public int LastTile { get; set; }
+ public string OtherMapName { get; set; }
+ public bool DestinationHorizontal { get; set; }
+ public int OtherMapIndex { get; set; }
+ public int OtherMapFirstTile { get; set; }
+ public int OtherMapLastTile { get; set; }
+ }
}
\ No newline at end of file
diff --git a/Swim/ModEntry.cs b/Swim/ModEntry.cs
index 6d4414c5..54a55bf2 100644
--- a/Swim/ModEntry.cs
+++ b/Swim/ModEntry.cs
@@ -183,6 +183,11 @@ public override void Entry(IModHelper helper)
prefix: new HarmonyMethod(typeof(SwimPatches), nameof(SwimPatches.GameLocation_isCollidingPosition_Prefix))
);
+ harmony.Patch(
+ original: AccessTools.Method(typeof(GameLocation), nameof(GameLocation.performTouchAction)),
+ prefix: new HarmonyMethod(typeof(SwimPatches), nameof(SwimPatches.GameLocation_performTouchAction_Prefix))
+ );
+
harmony.Patch(
original: AccessTools.Method(typeof(GameLocation), nameof(GameLocation.checkAction)),
prefix: new HarmonyMethod(typeof(SwimPatches), nameof(SwimPatches.GameLocation_checkAction_Prefix))
@@ -249,16 +254,16 @@ private void GameLoop_SaveLoaded(object sender, SaveLoadedEventArgs e)
}
}
- Monitor.Log($"Reading content pack from assets/underwater-map-content.json");
+ Monitor.Log($"Reading content pack from assets/swim-map-content.json");
try
{
- DiveMapData myData = Helper.Data.ReadJsonFile("assets/underwater-map-content.json");
+ DiveMapData myData = Helper.Data.ReadJsonFile("assets/swim-map-content.json");
ReadDiveMapData(myData);
}
catch (Exception ex)
{
- Monitor.Log($"assets/underwater-map-content.json file read error. Exception: {ex}", LogLevel.Warn);
+ Monitor.Log($"assets/swim-map-content.json file read error. Exception: {ex}", LogLevel.Warn);
}
@@ -308,6 +313,32 @@ private void GameLoop_SaveLoaded(object sender, SaveLoadedEventArgs e)
Game1.player.changeIntoSwimsuit();
}
+ public void ReadDiveMapData(DiveMapData data)
+ {
+ foreach (DiveMap map in data.Maps)
+ {
+ if (Game1._locationLookup.ContainsKey(map.Name))
+ {
+ if (!diveMaps.ContainsKey(map.Name))
+ {
+ diveMaps.Add(map.Name, map);
+ Monitor.Log($"added dive map info for {map.Name}", LogLevel.Debug);
+ }
+ else
+ {
+ Monitor.Log($"dive map info already exists for {map.Name}", LogLevel.Warn);
+ }
+
+ }
+ else
+ {
+ Monitor.Log($"dive map info not loaded for {map.Name}, check you have the map installed", LogLevel.Warn);
+ }
+ }
+ }
+
+
+
public static int ticksUnderwater = 0;
public static int bubbleOffset = 0;
@@ -377,28 +408,25 @@ private void GameLoop_GameLaunched(object sender, GameLaunchedEventArgs e)
JsonAssets.LoadAssets(Path.Combine(base.Helper.DirectoryPath, "assets/json-assets"));
}
- }
+ // fix dive maps
- public void ReadDiveMapData(DiveMapData data)
- {
- foreach (DiveMap map in data.Maps)
+ foreach (IContentPack contentPack in Helper.ContentPacks.GetOwned())
{
- if (Game1._locationLookup.ContainsKey(map.Name))
+ try
{
- if (!diveMaps.ContainsKey(map.Name))
- {
- diveMaps.Add(map.Name, map);
- Monitor.Log($"added dive map info for {map.Name}", LogLevel.Debug);
- }
- else
+ Monitor.Log($"Reading content pack: {contentPack.Manifest.Name} {contentPack.Manifest.Version} from {contentPack.DirectoryPath}");
+ DiveMapData data = contentPack.ReadJsonFile("content.json");
+ foreach(DiveMap map in data.Maps)
{
- Monitor.Log($"dive map info already exists for {map.Name}", LogLevel.Warn);
+ if (map.Features.Contains("FixWaterTiles") && !changeLocations.ContainsKey(map.Name))
+ {
+ changeLocations.Add(map.Name, false);
+ }
}
-
}
- else
+ catch
{
- Monitor.Log($"dive map info not loaded for {map.Name}, check you have the map installed", LogLevel.Warn);
+ Monitor.Log($"couldn't read content.json in content pack {contentPack.Manifest.Name}", LogLevel.Warn);
}
}
}
@@ -534,7 +562,7 @@ private void Input_ButtonPressed(object sender, ButtonPressedEventArgs e)
Game1.currentLocation.overlayObjects[Game1.player.getTileLocation() + new Vector2(0, 1)] = new Chest(0, new List- () { Helper.Input.IsDown(SButton.LeftShift) ? (Item)(new StardewValley.Object(434, 1)) : (new Hat(scubaMaskID)) }, Game1.player.getTileLocation() + new Vector2(0, 1), false, 0);
}
- if (e.Button == config.DiveKey && diveMaps.ContainsKey(Game1.currentLocation.Name))
+ if (e.Button == config.DiveKey && diveMaps.ContainsKey(Game1.currentLocation.Name) && diveMaps[Game1.currentLocation.Name].DiveLocations.Count > 0)
{
Point pos = Game1.player.getTileLocationPoint();
Location loc = new Location(pos.X, pos.Y);
@@ -544,56 +572,31 @@ private void Input_ButtonPressed(object sender, ButtonPressedEventArgs e)
return;
}
- string newName = null;
- DivePosition dp = null;
-
DiveMap dm = diveMaps[Game1.currentLocation.Name];
-
+ DiveLocation diveLocation = null;
foreach(DiveLocation dl in dm.DiveLocations)
{
if (dl.GetRectangle().X == -1 || dl.GetRectangle().Contains(loc))
{
- newName = dl.OtherMapName;
- dp = dl.OtherMapPos;
+ diveLocation = dl;
break;
}
}
- if (newName == null)
+ if (diveLocation == null)
{
Monitor.Log($"No dive destination for this point on this map", LogLevel.Debug);
return;
}
- if (!Game1._locationLookup.ContainsKey(newName))
+ if (!Game1._locationLookup.ContainsKey(diveLocation.OtherMapName))
{
- Monitor.Log($"Can't find destination map named {newName}", LogLevel.Warn);
+ Monitor.Log($"Can't find destination map named {diveLocation.OtherMapName}", LogLevel.Warn);
return;
}
- Monitor.Log($"warping to {newName}", LogLevel.Debug);
-
- if (dp == null)
- {
- Monitor.Log($"warping to current position");
- dp = new DivePosition()
- {
- X = pos.X,
- Y = pos.Y
- };
- }
-
- Game1.playSound("pullItemFromWater");
- Game1.warpFarmer(newName, dp.X, dp.Y, false);
- if (!IsMapUnderwater(Game1.currentLocation.Name))
- {
- bubbles.Clear();
- isUnderwater = true;
- }
- else
- {
- isUnderwater = false;
- }
+ Monitor.Log($"warping to {diveLocation.OtherMapName}", LogLevel.Debug);
+ DiveTo(diveLocation);
return;
}
@@ -601,6 +604,7 @@ private void Input_ButtonPressed(object sender, ButtonPressedEventArgs e)
{
config.ReadyToSwim = !config.ReadyToSwim;
Helper.WriteConfig(config);
+ Monitor.Log($"Ready to swim: {config.ReadyToSwim}");
return;
}
@@ -655,6 +659,67 @@ private void GameLoop_UpdateTicked(object sender, UpdateTickedEventArgs e)
AbigailCaveTick();
}
+ if (Game1.activeClickableMenu == null)
+ {
+ if (IsMapUnderwater(Game1.currentLocation.Name))
+ {
+ if (isUnderwater)
+ {
+ if (oxygen > 0)
+ {
+ if (!IsWearingScubaGear())
+ oxygen--;
+ }
+ else
+ {
+ Game1.playSound("pullItemFromWater");
+ isUnderwater = false;
+ DiveLocation diveLocation = diveMaps[Game1.currentLocation.Name].DiveLocations.Last();
+ DiveTo(diveLocation);
+ }
+ }
+ }
+ else
+ {
+ if (oxygen < MaxOxygen())
+ oxygen++;
+ if (oxygen < MaxOxygen())
+ oxygen++;
+ }
+ }
+
+ if (isJumping)
+ {
+ float difx = endJumpLoc.X - startJumpLoc.X;
+ float dify = endJumpLoc.Y - startJumpLoc.Y;
+ float completed = Game1.player.freezePause / (float)config.JumpTimeInMilliseconds;
+ if (Game1.player.freezePause <= 0)
+ {
+ Game1.player.position.Value = endJumpLoc;
+ isJumping = false;
+ if (willSwim)
+ {
+ Game1.currentLocation.playSound("waterSlosh", NetAudio.SoundContext.Default);
+ Game1.player.swimming.Value = true;
+ }
+ else
+ {
+ if (!config.SwimSuitAlways)
+ Game1.player.changeOutOfSwimSuit();
+ }
+ return;
+ }
+ Game1.player.position.Value = new Vector2(endJumpLoc.X - (difx * completed), endJumpLoc.Y - (dify * completed) - (float)Math.Sin(completed * Math.PI) * 64);
+ return;
+ }
+
+
+
+ if (!config.ReadyToSwim)
+ {
+ return;
+ }
+
if (Game1.player.swimming) {
if (!IsInWater() && !isJumping)
{
@@ -664,54 +729,105 @@ private void GameLoop_UpdateTicked(object sender, UpdateTickedEventArgs e)
Game1.player.changeOutOfSwimSuit();
}
- if(Game1.player.position.Y > Game1.viewport.Y + Game1.viewport.Height - 16)
+ DiveMap dm = null;
+ Point edgePos = Game1.player.getTileLocationPoint();
+
+ if (diveMaps.ContainsKey(Game1.currentLocation.Name))
+ {
+ dm = diveMaps[Game1.currentLocation.Name];
+ }
+
+ 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 (Game1.currentLocation.Name == "Mountain")
+ if (dm != null)
{
- Game1.warpFarmer("Town",94,0, false);
- }
- else if (Game1.currentLocation.Name == "Town")
- {
- Game1.warpFarmer("Beach", 59, 0, false);
+ EdgeWarp edge = dm.EdgeWarps.Find((x) => x.ThisMapEdge == "Bottom" && x.FirstTile <= edgePos.X && x.LastTile >= edgePos.X);
+ if (edge != null)
+ {
+ Point pos = 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)
+ 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 (Game1.currentLocation.Name == "Town")
- {
- Game1.warpFarmer("Mountain",72,40, false);
- }
- else if (Game1.currentLocation.Name == "Beach" && Game1.player.position.X / 64 < 93)
+
+ if (dm != null)
{
- Game1.warpFarmer("Town", 90, 109, false);
+ EdgeWarp edge = dm.EdgeWarps.Find((x) => x.ThisMapEdge == "Top" && x.FirstTile <= edgePos.X && x.LastTile >= edgePos.X);
+ if (edge != null)
+ {
+ Point pos = 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)
+ 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)
+ {
+ EdgeWarp edge = dm.EdgeWarps.Find((x) => x.ThisMapEdge == "Right" && x.FirstTile <= edgePos.Y && x.LastTile >= edgePos.Y);
+ if (edge != null)
+ {
+ Point pos = GetEdgeWarpDestination(edgePos.Y, edge);
+ if (pos != Point.Zero)
+ {
+ Monitor.Log("warping east");
+ Game1.warpFarmer(edge.OtherMapName, pos.X, pos.Y, false);
+ }
+ }
+ }
+
if (Game1.currentLocation.Name == "Forest")
{
- if(Game1.player.position.Y / 64 > 74)
- Game1.warpFarmer("Beach", 0,13, false);
+ if (Game1.player.position.Y / 64 > 74)
+ Game1.warpFarmer("Beach", 0, 13, false);
else
- Game1.warpFarmer("Town", 0,100, false);
+ Game1.warpFarmer("Town", 0, 100, false);
}
}
- else if(Game1.player.position.X < Game1.viewport.X - 32)
+ 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)
+ {
+ EdgeWarp edge = dm.EdgeWarps.Find((x) => x.ThisMapEdge == "Left" && x.FirstTile <= edgePos.X && x.LastTile >= edgePos.X);
+ if (edge != null)
+ {
+ Point pos = GetEdgeWarpDestination(edgePos.Y, edge);
+ if (pos != Point.Zero)
+ {
+ Monitor.Log("warping west");
+ Game1.warpFarmer(edge.OtherMapName, pos.X, pos.Y, false);
+ }
+ }
+ }
+
if (Game1.currentLocation.Name == "Town")
{
- Game1.warpFarmer("Forest",119,43, false);
+ Game1.warpFarmer("Forest", 119, 43, false);
}
else if (Game1.currentLocation.Name == "Beach")
{
- Game1.warpFarmer("Forest",119,111, false);
+ Game1.warpFarmer("Forest", 119, 111, false);
}
}
+
if (Game1.player.bathingClothes && IsWearingScubaGear() && !config.SwimSuitAlways)
Game1.player.changeOutOfSwimSuit();
else if (!Game1.player.bathingClothes && (!IsWearingScubaGear() || config.SwimSuitAlways))
@@ -744,59 +860,6 @@ private void GameLoop_UpdateTicked(object sender, UpdateTickedEventArgs e)
}
- if (Game1.activeClickableMenu == null)
- {
- if (IsMapUnderwater(Game1.currentLocation.Name))
- {
- if (isUnderwater)
- {
- if (oxygen > 0)
- {
- if(!IsWearingScubaGear())
- oxygen--;
- }
- else
- {
- Game1.playSound("pullItemFromWater");
- isUnderwater = false;
- Point pos = Game1.player.getTileLocationPoint();
- Game1.warpFarmer(diveMaps[Game1.currentLocation.Name].DiveLocations.Last().OtherMapName, pos.X, pos.Y, false);
- }
- }
- }
- else
- {
- if (oxygen < MaxOxygen())
- oxygen++;
- if (oxygen < MaxOxygen())
- oxygen++;
- }
- }
-
- if (isJumping)
- {
- float difx = endJumpLoc.X - startJumpLoc.X;
- float dify = endJumpLoc.Y - startJumpLoc.Y;
- float completed = Game1.player.freezePause / (float)config.JumpTimeInMilliseconds;
- if(Game1.player.freezePause <= 0)
- {
- Game1.player.position.Value = endJumpLoc;
- isJumping = false;
- if (willSwim)
- {
- Game1.currentLocation.playSound("waterSlosh", NetAudio.SoundContext.Default);
- Game1.player.swimming.Value = true;
- }
- else
- {
- if(!config.SwimSuitAlways)
- Game1.player.changeOutOfSwimSuit();
- }
- return;
- }
- Game1.player.position.Value = new Vector2(endJumpLoc.X - (difx * completed), endJumpLoc.Y - (dify * completed) - (float)Math.Sin(completed * Math.PI) * 64);
- return;
- }
CheckIfMyButtonDown();
if (!myButtonDown || Game1.player.millisecondsPlayed - lastJump < 250 || IsMapUnderwater(Game1.currentLocation.Name))
@@ -924,9 +987,62 @@ private void GameLoop_UpdateTicked(object sender, UpdateTickedEventArgs e)
}
+ private Point GetEdgeWarpDestination(int idxPos, EdgeWarp edge)
+ {
+ try
+ {
+ int idx = 1 + idxPos - edge.FirstTile;
+ int length = 1 + edge.LastTile - edge.FirstTile;
+ int otherLength = 1 + edge.OtherMapLastTile - edge.OtherMapFirstTile;
+ int otherIdx = (int)Math.Round((idx / (float)length) * otherLength);
+ int tileIdx = edge.OtherMapFirstTile - 1 + otherIdx;
+ if (edge.DestinationHorizontal == true)
+ {
+ Monitor.Log($"idx {idx} length {length} otherIdx {otherIdx} tileIdx {tileIdx} warp point: {tileIdx},{edge.OtherMapIndex}");
+ return new Point(tileIdx, edge.OtherMapIndex);
+ }
+ else
+ {
+ Monitor.Log($"warp point: {edge.OtherMapIndex},{tileIdx}");
+ return new Point(edge.OtherMapIndex, tileIdx);
+ }
+ }
+ catch
+ {
+
+ }
+ return Point.Zero;
+ }
+
+ private void DiveTo(DiveLocation diveLocation)
+ {
+ DivePosition dp = diveLocation.OtherMapPos;
+ if (dp == null)
+ {
+ Point pos = Game1.player.getTileLocationPoint();
+ dp = new DivePosition()
+ {
+ X = pos.X,
+ Y = pos.Y
+ };
+ }
+ if (!IsMapUnderwater(Game1.currentLocation.Name))
+ {
+ bubbles.Clear();
+ isUnderwater = true;
+ }
+ else
+ {
+ isUnderwater = false;
+ }
+
+ Game1.playSound("pullItemFromWater");
+ Game1.warpFarmer(diveLocation.OtherMapName, dp.X, dp.Y, false);
+ }
+
private bool IsMapUnderwater(string name)
{
- return diveMaps.ContainsKey(Game1.currentLocation.Name) && diveMaps[Game1.currentLocation.Name].Features.Contains("Underwater");
+ return diveMaps.ContainsKey(name) && diveMaps[name].Features.Contains("Underwater");
}
public static int abigailTicks;
@@ -1070,12 +1186,9 @@ private void AbigailCaveTick()
}
}
- if (v != Vector2.Zero && Game1.player.millisecondsPlayed - lastProjectile > 500)
+ if (v != Vector2.Zero && Game1.player.millisecondsPlayed - lastProjectile > 350)
{
- Game1.currentLocation.projectiles.Add(new BasicProjectile(1, 383, 0, 0, 0, v.X * 6, v.Y * 6, new Vector2(Game1.player.getStandingX() - 24, Game1.player.getStandingY() - 48), "Cowboy_monsterDie", "Cowboy_gunshot", false, true, Game1.currentLocation, Game1.player, true, null)
- {
- IgnoreLocationCollision = true
- });
+ Game1.currentLocation.projectiles.Add(new AbigailProjectile(1, 383, 0, 0, 0, v.X * 6, v.Y * 6, new Vector2(Game1.player.getStandingX() - 24, Game1.player.getStandingY() - 48), "Cowboy_monsterDie", "Cowboy_gunshot", false, true, Game1.currentLocation, Game1.player, true));
lastProjectile = Game1.player.millisecondsPlayed;
}
@@ -1171,12 +1284,12 @@ private bool IsInWater()
return IsMapUnderwater(Game1.currentLocation.Name)
||
- (tiles != null
+ (tiles != null
&& (
- (tiles.GetLength(0) > p.X && tiles.GetLength(1) > p.Y && tiles[p.X, p.Y])
+ (p.X >= 0 && p.Y >= 0 && tiles.GetLength(0) > p.X && tiles.GetLength(1) > p.Y && tiles[p.X, p.Y])
||
(Game1.player.swimming &&
- (tiles.GetLength(0) <= p.X || tiles.GetLength(1) <= p.Y)
+ (p.X < 0 || p.Y < 0 || tiles.GetLength(0) <= p.X || tiles.GetLength(1) <= p.Y)
)
)
);
@@ -1878,7 +1991,7 @@ public bool CanEdit(IAssetInfo asset)
/// A helper which encapsulates metadata about an asset and enables changes to it.
public void Edit(IAssetData asset)
{
- Monitor.Log("Editing asset" + asset.AssetName);
+ Monitor.Log("Editing asset: " + asset.AssetName);
string mapName = asset.AssetName.Replace("Maps/", "").Replace("Maps\\", "");
@@ -1904,7 +2017,7 @@ public void Edit(IAssetData asset)
{
if (tile.TileIndexProperties.ContainsKey("Passable"))
{
- tile.TileIndexProperties.Remove("Passable");
+ //tile.TileIndexProperties.Remove("Passable");
}
}
if (map.Data.GetLayer("AlwaysFront") != null)
@@ -1914,7 +2027,7 @@ public void Edit(IAssetData asset)
{
if (tile.TileIndexProperties.ContainsKey("Passable"))
{
- tile.TileIndexProperties.Remove("Passable");
+ //tile.TileIndexProperties.Remove("Passable");
}
}
}
diff --git a/Swim/Swim.csproj b/Swim/Swim.csproj
index daf62ea0..ba873fa5 100644
--- a/Swim/Swim.csproj
+++ b/Swim/Swim.csproj
@@ -44,6 +44,7 @@
+
@@ -68,7 +69,7 @@
-
+
diff --git a/Swim/SwimPatches.cs b/Swim/SwimPatches.cs
index aa3acdf4..77ae8ca4 100644
--- a/Swim/SwimPatches.cs
+++ b/Swim/SwimPatches.cs
@@ -100,7 +100,7 @@ public static void Farmer_updateCommon_Prefix(Farmer __instance, ref float[] __s
try
{
__state = new float[0];
- if(__instance.swimming && ModEntry.changeLocations.ContainsKey(Game1.currentLocation.Name))
+ if(__instance.swimming && ModEntry.changeLocations.ContainsKey(Game1.currentLocation.Name) && Config.ReadyToSwim)
{
__state = new float[]{
__instance.stamina,
@@ -261,6 +261,48 @@ 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[]
+ {
+ ' '
+ })[0];
+ if (text == "PoolEntrance")
+ {
+ if (!Game1.player.swimming)
+ {
+ Config.ReadyToSwim = false;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Monitor.Log($"Failed in {nameof(GameLocation_performTouchAction_Prefix)}:\n{ex}", LogLevel.Error);
+ }
+ }
+ public static void GameLocation_performTouchAction_Postfix(string fullActionString)
+ {
+ try
+ {
+ string text = fullActionString.Split(new char[]
+ {
+ ' '
+ })[0];
+ if (text == "PoolEntrance")
+ {
+ if (Game1.player.swimming)
+ {
+ Config = Helper.ReadConfig();
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Monitor.Log($"Failed in {nameof(GameLocation_performTouchAction_Postfix)}:\n{ex}", LogLevel.Error);
+ }
+ }
public static void GameLocation_checkAction_Prefix(GameLocation __instance, Location tileLocation, xTile.Dimensions.Rectangle viewport, Farmer who)
{
try
diff --git a/Swim/assets/underwater-map-content.json b/Swim/assets/swim-map-content.json
similarity index 51%
rename from Swim/assets/underwater-map-content.json
rename to Swim/assets/swim-map-content.json
index 260a8955..a9d94e99 100644
--- a/Swim/assets/underwater-map-content.json
+++ b/Swim/assets/swim-map-content.json
@@ -33,6 +33,28 @@
"OtherMapName":"UnderwaterBeach",
},
],
+ "EdgeWarps":[
+ {
+ "ThisMapEdge":"Top",
+ "FirstTile": 57,
+ "LastTile": 62,
+ "DestinationHorizontal": true,
+ "OtherMapIndex": 109,
+ "OtherMapFirstTile": 88,
+ "OtherMapLastTile": 92,
+ "OtherMapName":"Town",
+ },
+ {
+ "ThisMapEdge":"Right",
+ "FirstTile": 14,
+ "LastTile": 26,
+ "DestinationHorizontal": false,
+ "OtherMapIndex": 119,
+ "OtherMapFirstTile": 106,
+ "OtherMapLastTile": 119,
+ "OtherMapName":"Beach",
+ },
+ ]
},
{
"Name":"ScubaCrystalCave",
@@ -64,7 +86,29 @@
}
},
],
- },
+ "EdgeWarps":[
+ {
+ "ThisMapEdge":"Right",
+ "FirstTile": 39,
+ "LastTile": 48,
+ "DestinationHorizontal": false,
+ "OtherMapIndex": 0,
+ "OtherMapFirstTile": 96,
+ "OtherMapLastTile": 103,
+ "OtherMapName":"Town",
+ },
+ {
+ "ThisMapEdge":"Right",
+ "FirstTile": 106,
+ "LastTile": 119,
+ "DestinationHorizontal": false,
+ "OtherMapIndex": 0,
+ "OtherMapFirstTile": 14,
+ "OtherMapLastTile": 26,
+ "OtherMapName":"Beach",
+ },
+ ]
+ },
{
"Name":"ScubaCave",
"Features":[
@@ -118,11 +162,61 @@
},
{
"Name":"Mountain",
+ "Features":[
+ "FixWaterTiles"
+ ],
"DiveLocations": [
{
"OtherMapName":"UnderwaterMountain",
},
],
+ "EdgeWarps":[
+ {
+ "ThisMapEdge":"Bottom",
+ "FirstTile": 64,
+ "LastTile": 78,
+ "DestinationHorizontal": true,
+ "OtherMapIndex": 0,
+ "OtherMapFirstTile": 92,
+ "OtherMapLastTile": 96,
+ "OtherMapName":"Town",
+ },
+ ],
+ },
+ {
+ "Name":"Town",
+ "EdgeWarps":[
+ {
+ "ThisMapEdge":"Bottom",
+ "FirstTile": 88,
+ "LastTile": 92,
+ "DestinationHorizontal": true,
+ "OtherMapIndex": 0,
+ "OtherMapFirstTile": 57,
+ "OtherMapLastTile": 62,
+ "OtherMapName":"Beach",
+ },
+ {
+ "ThisMapEdge":"Top",
+ "FirstTile": 92,
+ "LastTile": 96,
+ "DestinationHorizontal": true,
+ "OtherMapIndex": 40,
+ "OtherMapFirstTile": 64,
+ "OtherMapLastTile": 78,
+ "OtherMapName":"Mountain",
+ },
+ {
+ "ThisMapEdge":"Left",
+ "FirstTile": 96,
+ "LastTile": 103,
+ "DestinationHorizontal": false,
+ "OtherMapIndex": 119,
+ "OtherMapFirstTile": 39,
+ "OtherMapLastTile": 48,
+ "OtherMapName":"Town",
+ },
+ ]
},
]
}
\ No newline at end of file
diff --git a/Swim/manifest.json b/Swim/manifest.json
index 6e698214..2bc7a37d 100644
--- a/Swim/manifest.json
+++ b/Swim/manifest.json
@@ -1,7 +1,7 @@
{
"Name": "Swim Mod",
"Author": "aedenthorn",
- "Version": "0.9.2",
+ "Version": "0.9.6",
"Description": "Allows you to swim.",
"UniqueID": "aedenthorn.Swim",
"EntryDll": "Swim.dll",
diff --git a/_releases/CustomFixedDialogue 0.1.0.zip b/_releases/CustomFixedDialogue 0.1.0.zip
new file mode 100644
index 00000000..d6a0a02b
Binary files /dev/null and b/_releases/CustomFixedDialogue 0.1.0.zip differ
diff --git a/_releases/CustomFixedDialogue 0.1.1.zip b/_releases/CustomFixedDialogue 0.1.1.zip
new file mode 100644
index 00000000..a4eb6afe
Binary files /dev/null and b/_releases/CustomFixedDialogue 0.1.1.zip differ
diff --git a/_releases/MultipleSpouses 1.0.3.zip b/_releases/MultipleSpouses 1.0.3.zip
deleted file mode 100644
index 88b550b3..00000000
Binary files a/_releases/MultipleSpouses 1.0.3.zip and /dev/null differ
diff --git a/_releases/Swim 0.9.6.zip b/_releases/Swim 0.9.6.zip
new file mode 100644
index 00000000..15032c6f
Binary files /dev/null and b/_releases/Swim 0.9.6.zip differ
diff --git a/_releases/_releases.projitems b/_releases/_releases.projitems
new file mode 100644
index 00000000..10fcc700
--- /dev/null
+++ b/_releases/_releases.projitems
@@ -0,0 +1,18 @@
+
+
+
+ $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
+ true
+ 047040e0-3d07-46e5-852b-d1d45cd57bb8
+
+
+ _releases
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/_releases/_releases.shproj b/_releases/_releases.shproj
new file mode 100644
index 00000000..fe0a5da6
--- /dev/null
+++ b/_releases/_releases.shproj
@@ -0,0 +1,13 @@
+
+
+
+ 047040e0-3d07-46e5-852b-d1d45cd57bb8
+ 14.0
+
+
+
+
+
+
+
+