diff --git a/GeneralMods/CustomNPCFramework/Class1.cs b/GeneralMods/CustomNPCFramework/Class1.cs new file mode 100644 index 00000000..e0801aea --- /dev/null +++ b/GeneralMods/CustomNPCFramework/Class1.cs @@ -0,0 +1,84 @@ +using CustomNPCFramework.Framework.Graphics; +using CustomNPCFramework.Framework.NPCS; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using StardewModdingAPI; +using StardewValley; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CustomNPCFramework +{ + public class Class1 : Mod + { + public static IModHelper ModHelper; + public static IMonitor ModMonitor; + public static AssetManager assetManager; + public override void Entry(IModHelper helper) + { + ModHelper = this.Helper; + ModMonitor = this.Monitor; + assetManager = new AssetManager(); + initializeExamples(); + assetManager.loadAssets(); + StardewModdingAPI.Events.SaveEvents.AfterLoad += SaveEvents_LoadChar; + StardewModdingAPI.Events.LocationEvents.CurrentLocationChanged += LocationEvents_CurrentLocationChanged; + StardewModdingAPI.Events.GameEvents.UpdateTick += GameEvents_UpdateTick; + } + + private void GameEvents_UpdateTick(object sender, EventArgs e) + { + if (Game1.player.currentLocation == null) return; + foreach (var v in Game1.player.currentLocation.characters) + { + v.speed = 5; + v.MovePosition(Game1.currentGameTime, Game1.viewport, Game1.player.currentLocation); + ModMonitor.Log(v.name); + } + } + + private void LocationEvents_CurrentLocationChanged(object sender, StardewModdingAPI.Events.EventArgsCurrentLocationChanged e) + { + + } + + private void SaveEvents_LoadChar(object sender, EventArgs e) + { + string path = Path.Combine(ModHelper.DirectoryPath, "Content", "Graphics", "NPCS", "Characters", "RainMan"); + assetManager.addPathCreateDirectory(new KeyValuePair("characters", path)); + Texture2D tex=ModHelper.Content.Load(Path.Combine(getShortenedDirectory(path).Remove(0,1), "character.png")); + ExtendedNPC myNpc3 = new ExtendedNPC(new StardewValley.AnimatedSprite(tex, 0, 16, 32), new Vector2(14, 14)*Game1.tileSize, 2, "b2"); + Game1.getLocationFromName("BusStop").addCharacter(myNpc3); + myNpc3.SetMovingDown(true); + } + + public void initializeExamples() + { + string dirPath = Path.Combine(ModHelper.DirectoryPath, "Content", "Templates"); + assetManager.addPathCreateDirectory(new KeyValuePair("templates", dirPath)); + string filePath =Path.Combine(dirPath, "Example.json"); + if (File.Exists(filePath)) return; + string getRelativePath = getShortenedDirectory(filePath); + ModMonitor.Log("THIS IS THE PATH::: " + getRelativePath); + AssetInfo info = new AssetInfo("Example", new Vector2(16, 16), false); + info.writeToJson(filePath); + } + + public static string getShortenedDirectory(string path) + { + + string[] spliter = path.Split(new string[] { ModHelper.DirectoryPath },StringSplitOptions.None); + return spliter[1]; + } + + public static string getRelativeDirectory(string path) + { + string s = getShortenedDirectory(path); + return s.Remove(0, 1); + } + } +} diff --git a/GeneralMods/CustomNPCFramework/CustomNPCFramework.csproj b/GeneralMods/CustomNPCFramework/CustomNPCFramework.csproj new file mode 100644 index 00000000..8fd07e7e --- /dev/null +++ b/GeneralMods/CustomNPCFramework/CustomNPCFramework.csproj @@ -0,0 +1,70 @@ + + + + + Debug + AnyCPU + {89C7DF45-8AE5-49AC-ADA9-6312E9590829} + Library + Properties + CustomNPCFramework + CustomNPCFramework + v4.5 + 512 + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + 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/GeneralMods/CustomNPCFramework/Framework/Graphics/AssetInfo.cs b/GeneralMods/CustomNPCFramework/Framework/Graphics/AssetInfo.cs new file mode 100644 index 00000000..0ed38ff3 --- /dev/null +++ b/GeneralMods/CustomNPCFramework/Framework/Graphics/AssetInfo.cs @@ -0,0 +1,47 @@ +using Microsoft.Xna.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CustomNPCFramework.Framework.Graphics +{ + public class AssetInfo + { + public string name; + public Vector2 assetSize; + public bool randomizeUponLoad; + /// + /// A constructor use to create asset info which can then be used to create asset sheets. + /// + /// The name of the texture sheet. Can be different than the actual file name. + /// The size of the individual sprites on the texture sheet. Ex 16x16 pixels. + /// If true, the index for the asset will be randomized. Good for getting variation from a texture. + public AssetInfo(string name, Vector2 assetSize, bool randomizeUponLoad) + { + this.name = name; + this.assetSize = assetSize; + this.randomizeUponLoad = randomizeUponLoad; + } + + /// + /// Save the json to a certain location. + /// + /// + public void writeToJson(string path) + { + Class1.ModHelper.WriteJsonFile(path, this); + } + + /// + /// Read the json from a certain location. + /// + /// + /// + public static AssetInfo readFromJson(string path) + { + return Class1.ModHelper.ReadJsonFile(path); + } + } +} diff --git a/GeneralMods/CustomNPCFramework/Framework/Graphics/AssetManager.cs b/GeneralMods/CustomNPCFramework/Framework/Graphics/AssetManager.cs new file mode 100644 index 00000000..d430ee19 --- /dev/null +++ b/GeneralMods/CustomNPCFramework/Framework/Graphics/AssetManager.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CustomNPCFramework.Framework.Graphics +{ + public class AssetManager + { + public List assets; + public Dictionary paths; + + /// + /// Basic constructor. + /// + public AssetManager() + { + this.assets = new List(); + this.paths = new Dictionary(); + } + + /// + /// Default loading function from paths. + /// + public void loadAssets() + { + foreach(var path in this.paths) + { + string[] files= Directory.GetFiles(path.Value, "*.json"); + foreach(var file in files) + { + AssetInfo info = AssetInfo.readFromJson(file); + AssetSheet sheet = new AssetSheet(info,path.Value); + this.assets.Add(sheet); + } + } + } + + /// + /// Add an asset to be handled from the asset manager. + /// + /// + public void addAsset(AssetSheet asset) + { + this.assets.Add(asset); + } + + /// + /// Get an individual asset by its name. + /// + /// + /// + public AssetSheet getAssetByName(string s) + { + foreach(var v in assets) + { + if (v.assetInfo.name == s) return v; + } + return null; + } + + /// + /// Add a new path to the asset manager and create the directory for it. + /// + /// + public void addPathCreateDirectory(KeyValuePair path) + { + this.addPath(path); + string dir = Path.Combine(Class1.ModHelper.DirectoryPath, path.Value); + if (!Directory.Exists(dir)) + { + Directory.CreateDirectory(Path.Combine(Class1.ModHelper.DirectoryPath, path.Value)); + } + } + + /// + /// Add a path to the dictionary. + /// + /// + private void addPath(KeyValuePair path) + { + this.paths.Add(path.Key, path.Value); + } + + /// + /// Create appropriate directories for the path. + /// + private void createDirectoriesFromPaths() + { + foreach(var v in paths) + { + Directory.CreateDirectory(Path.Combine(Class1.ModHelper.DirectoryPath,v.Value)); + } + } + } +} diff --git a/GeneralMods/CustomNPCFramework/Framework/Graphics/AssetSheet.cs b/GeneralMods/CustomNPCFramework/Framework/Graphics/AssetSheet.cs new file mode 100644 index 00000000..d07ccdef --- /dev/null +++ b/GeneralMods/CustomNPCFramework/Framework/Graphics/AssetSheet.cs @@ -0,0 +1,140 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CustomNPCFramework.Framework.Graphics +{ + /// + /// Used to handle loading different textures and handling opperations on those textures. + /// + public class AssetSheet + { + public Texture2D texture; + public AssetInfo assetInfo; + public string path; + + public int index; + + private int widthIndex; + private int heightIndex; + private int widthIndexMax; + private int heightIndexMax; + + public Rectangle currentAsset; + + public AssetSheet(AssetInfo info,string path) + { + this.assetInfo = info; + this.texture = Class1.ModHelper.Content.Load(Class1.getShortenedDirectory(Path.Combine(path,info.name+".png")).Remove(0,1)); + + this.widthIndexMax = this.texture.Width / (int)this.assetInfo.assetSize.X; + this.heightIndexMax = this.texture.Width / (int)this.assetInfo.assetSize.Y; + this.index = 0; + if (this.assetInfo.randomizeUponLoad == false) + { + this.widthIndex = 0; + this.heightIndex = 0; + } + else + { + getRandomAssetIndicies(); + setIndex(); + } + this.currentAsset = new Rectangle(widthIndex * (int)this.assetInfo.assetSize.X, heightIndex * (int)this.assetInfo.assetSize.Y, (int)this.assetInfo.assetSize.X, (int)this.assetInfo.assetSize.Y); + } + + /// + /// Get the next graphic from the texture. + /// + public void getNext() + { + //If I can still iterate through my list but my width is maxed, increment height. + if (this.widthIndex == this.widthIndexMax - 1 && this.heightIndex != this.heightIndexMax) + { + this.widthIndex -= 0; + this.heightIndex++; + } + //If I reached the end of my image loop to 0; + else if (this.heightIndex == this.heightIndexMax && this.widthIndex == this.widthIndexMax - 1) + { + this.heightIndex = 0; + this.widthIndex = 0; + } + else + { + //If I can still iterate through my list do so. + widthIndex++; + } + this.setIndex(); + this.setAsset(); + } + + /// + /// Get the last graphic from my texture. + /// + public void getPrevious() + { + //If my width index is 0 and my height index isn't decrement my height index and set the width index to the far right. + if (this.widthIndex == 0 && this.heightIndex != 0) + { + this.heightIndex--; + this.widthIndex = this.widthIndexMax - 1; + } + //If both my height and width indicies are 0, loop to the bottom right of the texture. + else if (this.widthIndex == 0 && this.heightIndex == 0) + { + this.widthIndex = this.widthIndexMax - 1; + this.heightIndex = this.heightIndexMax - 1; + } + else + { + //Just decrement my width index by 1. + this.widthIndex--; + } + this.setIndex(); + this.setAsset(); + } + + /// + /// sets the current positioning for the rectangle index; + /// + private void setAsset() + { + this.currentAsset.X = widthIndex * (int)this.assetInfo.assetSize.X; + this.currentAsset.Y = heightIndex * (int)this.assetInfo.assetSize.Y; + } + + /// + /// Used mainly for display purposes and length purposes. + /// + public void setIndex() + { + this.index = heightIndex * widthIndexMax + widthIndex; + } + + /// + /// Sets the asset index to a random value. + /// + public void getRandomAssetIndicies() + { + Random r = new Random(DateTime.Now.Millisecond); + this.widthIndex = r.Next(0, this.widthIndexMax); + this.widthIndex = r.Next(0, this.heightIndexMax); + setIndex(); + setAsset(); + } + + /// + /// Used just to get a copy of this asset sheet. + /// + public void clone() + { + var asset = new AssetSheet(this.assetInfo,(string)this.path.Clone()); + } + } +} diff --git a/GeneralMods/CustomNPCFramework/Framework/NPCS/ExtendedNPC.cs b/GeneralMods/CustomNPCFramework/Framework/NPCS/ExtendedNPC.cs new file mode 100644 index 00000000..6c68eada --- /dev/null +++ b/GeneralMods/CustomNPCFramework/Framework/NPCS/ExtendedNPC.cs @@ -0,0 +1,341 @@ +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using StardewValley; +using StardewValley.Characters; +using StardewValley.Locations; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using xTile.Dimensions; +using xTile.ObjectModel; +using xTile.Tiles; + +namespace CustomNPCFramework.Framework.NPCS +{ + class ExtendedNPC :StardewValley.NPC + { + public ExtendedNPC() :base() + { + + } + public ExtendedNPC(StardewValley.AnimatedSprite sprite,Vector2 position,int facingDirection,string name): base(sprite, position, facingDirection, name, null) + { + + } + + + public override void MovePosition(GameTime time, xTile.Dimensions.Rectangle viewport, GameLocation currentLocation) + { + if (this.GetType() == typeof(FarmAnimal)) + this.willDestroyObjectsUnderfoot = false; + if ((double)this.xVelocity != 0.0 || (double)this.yVelocity != 0.0) + { + Microsoft.Xna.Framework.Rectangle boundingBox = this.GetBoundingBox(); + boundingBox.X += (int)this.xVelocity; + boundingBox.Y -= (int)this.yVelocity; + if (currentLocation == null || !currentLocation.isCollidingPosition(boundingBox, viewport, false, 0, false, this)) + { + this.position.X += this.xVelocity; + this.position.Y -= this.yVelocity; + } + this.xVelocity = (float)(int)((double)this.xVelocity - (double)this.xVelocity / 2.0); + this.yVelocity = (float)(int)((double)this.yVelocity - (double)this.yVelocity / 2.0); + } + else if (this.moveUp) + { + if (currentLocation == null || !currentLocation.isCollidingPosition(this.nextPosition(0), viewport, false, 0, false, this) || this.isCharging) + { + this.position.Y -= (float)(this.speed + this.addedSpeed); + if (!this.ignoreMovementAnimation) + { + this.sprite.AnimateUp(time, (this.speed - 2 + this.addedSpeed) * -25, Utility.isOnScreen(this.getTileLocationPoint(), 1, currentLocation) ? "Cowboy_Footstep" : ""); + this.faceDirection(0); + } + } + else if (!currentLocation.isTilePassable(this.nextPosition(0), viewport) || !this.willDestroyObjectsUnderfoot) + this.Halt(); + else if (this.willDestroyObjectsUnderfoot) + { + Vector2 vector2 = new Vector2((float)(this.getStandingX() / Game1.tileSize), (float)(this.getStandingY() / Game1.tileSize - 1)); + if (currentLocation.characterDestroyObjectWithinRectangle(this.nextPosition(0), true)) + { + this.doEmote(12, true); + this.position.Y -= (float)(this.speed + this.addedSpeed); + } + else + this.blockedInterval = this.blockedInterval + time.ElapsedGameTime.Milliseconds; + } + } + else if (this.moveRight) + { + if (currentLocation == null || !currentLocation.isCollidingPosition(this.nextPosition(1), viewport, false, 0, false, this) || this.isCharging) + { + this.position.X += (float)(this.speed + this.addedSpeed); + if (!this.ignoreMovementAnimation) + { + this.sprite.AnimateRight(time, (this.speed - 2 + this.addedSpeed) * -25, Utility.isOnScreen(this.getTileLocationPoint(), 1, currentLocation) ? "Cowboy_Footstep" : ""); + this.faceDirection(1); + } + } + else if (!currentLocation.isTilePassable(this.nextPosition(1), viewport) || !this.willDestroyObjectsUnderfoot) + this.Halt(); + else if (this.willDestroyObjectsUnderfoot) + { + Vector2 vector2 = new Vector2((float)(this.getStandingX() / Game1.tileSize + 1), (float)(this.getStandingY() / Game1.tileSize)); + if (currentLocation.characterDestroyObjectWithinRectangle(this.nextPosition(1), true)) + { + this.doEmote(12, true); + this.position.X += (float)(this.speed + this.addedSpeed); + } + else + this.blockedInterval = this.blockedInterval + time.ElapsedGameTime.Milliseconds; + } + } + else if (this.moveDown) + { + if (currentLocation == null || !currentLocation.isCollidingPosition(this.nextPosition(2), viewport, false, 0, false, this) || this.isCharging) + { + this.position.Y += (float)(this.speed + this.addedSpeed); + if (!this.ignoreMovementAnimation) + { + this.sprite.AnimateDown(time, (this.speed - 2 + this.addedSpeed) * -25, Utility.isOnScreen(this.getTileLocationPoint(), 1, currentLocation) ? "Cowboy_Footstep" : ""); + this.faceDirection(2); + } + } + else if (!currentLocation.isTilePassable(this.nextPosition(2), viewport) || !this.willDestroyObjectsUnderfoot) + this.Halt(); + else if (this.willDestroyObjectsUnderfoot) + { + Vector2 vector2 = new Vector2((float)(this.getStandingX() / Game1.tileSize), (float)(this.getStandingY() / Game1.tileSize + 1)); + if (currentLocation.characterDestroyObjectWithinRectangle(this.nextPosition(2), true)) + { + this.doEmote(12, true); + this.position.Y += (float)(this.speed + this.addedSpeed); + } + else + this.blockedInterval = this.blockedInterval + time.ElapsedGameTime.Milliseconds; + } + } + else if (this.moveLeft) + { + if (currentLocation == null || !currentLocation.isCollidingPosition(this.nextPosition(3), viewport, false, 0, false, this) || this.isCharging) + { + this.position.X -= (float)(this.speed + this.addedSpeed); + if (!this.ignoreMovementAnimation) + { + this.sprite.AnimateLeft(time, (this.speed - 2 + this.addedSpeed) * -25, Utility.isOnScreen(this.getTileLocationPoint(), 1, currentLocation) ? "Cowboy_Footstep" : ""); + this.faceDirection(3); + } + } + else if (!currentLocation.isTilePassable(this.nextPosition(3), viewport) || !this.willDestroyObjectsUnderfoot) + this.Halt(); + else if (this.willDestroyObjectsUnderfoot) + { + Vector2 vector2 = new Vector2((float)(this.getStandingX() / Game1.tileSize - 1), (float)(this.getStandingY() / Game1.tileSize)); + if (currentLocation.characterDestroyObjectWithinRectangle(this.nextPosition(3), true)) + { + this.doEmote(12, true); + this.position.X -= (float)(this.speed + this.addedSpeed); + } + else + this.blockedInterval = this.blockedInterval + time.ElapsedGameTime.Milliseconds; + } + } + if (this.blockedInterval >= 3000 && (double)this.blockedInterval <= 3750.0 && !Game1.eventUp) + { + this.doEmote(Game1.random.NextDouble() < 0.5 ? 8 : 40, true); + this.blockedInterval = 3750; + } + else + { + if (this.blockedInterval < 5000) + return; + this.speed = 4; + this.isCharging = true; + this.blockedInterval = 0; + } + } + + public override void draw(SpriteBatch b, float alpha = 1f) + { + if (this.sprite == null || this.isInvisible || !Utility.isOnScreen(this.position, 2 * Game1.tileSize)) + return; + //Checks if the npc is swimming. If not draw it's default graphic. Do characters aside from Farmer and Penny Swim??? + if (this.swimming) + { + b.Draw(this.Sprite.Texture, this.getLocalPosition(Game1.viewport) + new Vector2((float)(Game1.tileSize / 2), (float)(Game1.tileSize + Game1.tileSize / 4 + this.yJumpOffset * 2)) + (this.shakeTimer > 0 ? new Vector2((float)Game1.random.Next(-1, 2), (float)Game1.random.Next(-1, 2)) : Vector2.Zero) - new Vector2(0.0f, this.yOffset), new Microsoft.Xna.Framework.Rectangle?(new Microsoft.Xna.Framework.Rectangle(this.sprite.SourceRect.X, this.sprite.SourceRect.Y, this.sprite.SourceRect.Width, this.sprite.SourceRect.Height / 2 - (int)((double)this.yOffset / (double)Game1.pixelZoom))), Color.White, this.rotation, new Vector2((float)(Game1.tileSize / 2), (float)(Game1.tileSize * 3 / 2)) / 4f, Math.Max(0.2f, this.scale) * (float)Game1.pixelZoom, this.flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None, Math.Max(0.0f, this.drawOnTop ? 0.991f : (float)this.getStandingY() / 10000f)); + Vector2 localPosition = this.getLocalPosition(Game1.viewport); + b.Draw(Game1.staminaRect, new Microsoft.Xna.Framework.Rectangle((int)localPosition.X + (int)this.yOffset + Game1.pixelZoom * 2, (int)localPosition.Y - 32 * Game1.pixelZoom + this.sprite.SourceRect.Height * Game1.pixelZoom + Game1.tileSize * 3 / 4 + this.yJumpOffset * 2 - (int)this.yOffset, this.sprite.SourceRect.Width * Game1.pixelZoom - (int)this.yOffset * 2 - Game1.pixelZoom * 4, Game1.pixelZoom), new Microsoft.Xna.Framework.Rectangle?(Game1.staminaRect.Bounds), Color.White * 0.75f, 0.0f, Vector2.Zero, SpriteEffects.None, (float)((double)this.getStandingY() / 10000.0 + 1.0 / 1000.0)); + } + else + { + b.Draw(this.Sprite.Texture, this.getLocalPosition(Game1.viewport) + new Vector2((float)(this.sprite.spriteWidth * Game1.pixelZoom / 2), (float)(this.GetBoundingBox().Height / 2)) + (this.shakeTimer > 0 ? new Vector2((float)Game1.random.Next(-1, 2), (float)Game1.random.Next(-1, 2)) : Vector2.Zero), new Microsoft.Xna.Framework.Rectangle?(this.Sprite.SourceRect), Color.White * alpha, this.rotation, new Vector2((float)(this.sprite.spriteWidth / 2), (float)((double)this.sprite.spriteHeight * 3.0 / 4.0)), Math.Max(0.2f, this.scale) * (float)Game1.pixelZoom, this.flip || this.sprite.currentAnimation != null && this.sprite.currentAnimation[this.sprite.currentAnimationIndex].flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None, Math.Max(0.0f, this.drawOnTop ? 0.991f : (float)this.getStandingY() / 10000f)); + } + //If the npc breathes then this code is ran. + if (this.breather && this.shakeTimer <= 0 && (!this.swimming && this.sprite.CurrentFrame < 16) && !this.farmerPassesThrough) + { + Microsoft.Xna.Framework.Rectangle sourceRect = this.sprite.SourceRect; + sourceRect.Y += this.sprite.spriteHeight / 2 + this.sprite.spriteHeight / 32; + sourceRect.Height = this.sprite.spriteHeight / 4; + sourceRect.X += this.sprite.spriteWidth / 4; + sourceRect.Width = this.sprite.spriteWidth / 2; + Vector2 vector2 = new Vector2((float)(this.sprite.spriteWidth * Game1.pixelZoom / 2), (float)(Game1.tileSize / 8)); + if (this.age == 2) + { + sourceRect.Y += this.sprite.spriteHeight / 6 + 1; + sourceRect.Height /= 2; + vector2.Y += (float)(this.sprite.spriteHeight / 8 * Game1.pixelZoom); + } + else if (this.gender == 1) + { + ++sourceRect.Y; + vector2.Y -= (float)Game1.pixelZoom; + sourceRect.Height /= 2; + } + float num = Math.Max(0.0f, (float)(Math.Ceiling(Math.Sin(Game1.currentGameTime.TotalGameTime.TotalMilliseconds / 600.0 + (double)this.DefaultPosition.X * 20.0)) / 4.0)); + b.Draw(this.Sprite.Texture, this.getLocalPosition(Game1.viewport) + vector2 + (this.shakeTimer > 0 ? new Vector2((float)Game1.random.Next(-1, 2), (float)Game1.random.Next(-1, 2)) : Vector2.Zero), new Microsoft.Xna.Framework.Rectangle?(sourceRect), Color.White * alpha, this.rotation, new Vector2((float)(sourceRect.Width / 2), (float)(sourceRect.Height / 2 + 1)), Math.Max(0.2f, this.scale) * (float)Game1.pixelZoom + num, this.flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None, Math.Max(0.0f, this.drawOnTop ? 0.992f : (float)((double)this.getStandingY() / 10000.0 + 1.0 / 1000.0))); + } + + //Checks if the npc is glowing. + if (this.isGlowing) + b.Draw(this.Sprite.Texture, this.getLocalPosition(Game1.viewport) + new Vector2((float)(this.sprite.spriteWidth * Game1.pixelZoom / 2), (float)(this.GetBoundingBox().Height / 2)) + (this.shakeTimer > 0 ? new Vector2((float)Game1.random.Next(-1, 2), (float)Game1.random.Next(-1, 2)) : Vector2.Zero), new Microsoft.Xna.Framework.Rectangle?(this.Sprite.SourceRect), this.glowingColor * this.glowingTransparency, this.rotation, new Vector2((float)(this.sprite.spriteWidth / 2), (float)((double)this.sprite.spriteHeight * 3.0 / 4.0)), Math.Max(0.2f, this.scale) * 4f, this.flip ? SpriteEffects.FlipHorizontally : SpriteEffects.None, Math.Max(0.0f, this.drawOnTop ? 0.99f : (float)((double)this.getStandingY() / 10000.0 + 1.0 / 1000.0))); + + //This code runs if the npc is emoting. + if (!this.IsEmoting || Game1.eventUp) + return; + Vector2 localPosition1 = this.getLocalPosition(Game1.viewport); + localPosition1.Y -= (float)(Game1.tileSize / 2 + this.sprite.spriteHeight * Game1.pixelZoom); + b.Draw(Game1.emoteSpriteSheet, localPosition1, new Microsoft.Xna.Framework.Rectangle?(new Microsoft.Xna.Framework.Rectangle(this.CurrentEmoteIndex * 16 % Game1.emoteSpriteSheet.Width, this.CurrentEmoteIndex * 16 / Game1.emoteSpriteSheet.Width * 16, 16, 16)), Color.White, 0.0f, Vector2.Zero, (float)Game1.pixelZoom, SpriteEffects.None, (float)this.getStandingY() / 10000f); + } + + + public override void updateMovement(GameLocation location, GameTime time) + { + this.lastPosition = this.position; + if (this.DirectionsToNewLocation != null && !Game1.newDay) + { + if (this.getStandingX() < -Game1.tileSize || this.getStandingX() > location.map.DisplayWidth + Game1.tileSize || (this.getStandingY() < -Game1.tileSize || this.getStandingY() > location.map.DisplayHeight + Game1.tileSize)) + { + this.IsWalkingInSquare = false; + Game1.warpCharacter(this, this.DefaultMap, this.DefaultPosition, true, true); + location.characters.Remove(this); + } + else if (this.IsWalkingInSquare) + { + this.returnToEndPoint(); + this.MovePosition(time, Game1.viewport, location); + } + else + { + if (!this.followSchedule) + return; + this.MovePosition(time, Game1.viewport, location); + Warp warp = location.isCollidingWithWarp(this.GetBoundingBox()); + PropertyValue propertyValue = (PropertyValue)null; + Tile tile1 = location.map.GetLayer("Buildings").PickTile(this.nextPositionPoint(), Game1.viewport.Size); + if (tile1 != null) + tile1.Properties.TryGetValue("Action", out propertyValue); + string[] strArray1; + if (propertyValue != null) + strArray1 = propertyValue.ToString().Split(' '); + else + strArray1 = (string[])null; + string[] strArray2 = strArray1; + if (warp != null) + { + if (location is BusStop && warp.TargetName.Equals("Farm")) + { + Point entryLocation = ((this.isMarried() ? (GameLocation)(this.getHome() as FarmHouse) : Game1.getLocationFromName("FarmHouse")) as FarmHouse).getEntryLocation(); + warp = new Warp(warp.X, warp.Y, "FarmHouse", entryLocation.X, entryLocation.Y, false); + } + else if (location is FarmHouse && warp.TargetName.Equals("Farm")) + warp = new Warp(warp.X, warp.Y, "BusStop", 0, 23, false); + Game1.warpCharacter(this, warp.TargetName, new Vector2((float)(warp.TargetX * Game1.tileSize), (float)(warp.TargetY * Game1.tileSize - this.Sprite.getHeight() / 2 - Game1.tileSize / 4)), false, location.IsOutdoors); + location.characters.Remove(this); + } + else if (strArray2 != null && strArray2.Length >= 1 && strArray2[0].Contains("Warp")) + { + Game1.warpCharacter(this, strArray2[3], new Vector2((float)Convert.ToInt32(strArray2[1]), (float)Convert.ToInt32(strArray2[2])), false, location.IsOutdoors); + if (Game1.currentLocation.name.Equals(location.name) && Utility.isOnScreen(this.getStandingPosition(), Game1.tileSize * 3)) + Game1.playSound("doorClose"); + location.characters.Remove(this); + } + else if (strArray2 != null && strArray2.Length >= 1 && strArray2[0].Contains("Door")) + { + location.openDoor(new Location(this.nextPositionPoint().X / Game1.tileSize, this.nextPositionPoint().Y / Game1.tileSize), Game1.player.currentLocation.Equals((object)location)); + } + else + { + if (location.map.GetLayer("Paths") == null) + return; + Tile tile2 = location.map.GetLayer("Paths").PickTile(new Location(this.getStandingX(), this.getStandingY()), Game1.viewport.Size); + Microsoft.Xna.Framework.Rectangle boundingBox = this.GetBoundingBox(); + boundingBox.Inflate(2, 2); + if (tile2 == null || !new Microsoft.Xna.Framework.Rectangle(this.getStandingX() - this.getStandingX() % Game1.tileSize, this.getStandingY() - this.getStandingY() % Game1.tileSize, Game1.tileSize, Game1.tileSize).Contains(boundingBox)) + return; + switch (tile2.TileIndex) + { + case 0: + if (this.getDirection() == 3) + { + this.SetMovingOnlyUp(); + break; + } + if (this.getDirection() != 2) + break; + this.SetMovingOnlyRight(); + break; + case 1: + if (this.getDirection() == 3) + { + this.SetMovingOnlyDown(); + break; + } + if (this.getDirection() != 0) + break; + this.SetMovingOnlyRight(); + break; + case 2: + if (this.getDirection() == 1) + { + this.SetMovingOnlyDown(); + break; + } + if (this.getDirection() != 0) + break; + this.SetMovingOnlyLeft(); + break; + case 3: + if (this.getDirection() == 1) + { + this.SetMovingOnlyUp(); + break; + } + if (this.getDirection() != 2) + break; + this.SetMovingOnlyLeft(); + break; + case 4: + this.changeSchedulePathDirection(); + this.moveCharacterOnSchedulePath(); + break; + case 7: + this.ReachedEndPoint(); + break; + } + } + } + } + else + { + if (!this.IsWalkingInSquare) + return; + this.randomSquareMovement(time); + this.MovePosition(time, Game1.viewport, location); + } + } + + } +} diff --git a/GeneralMods/CustomNPCFramework/Properties/AssemblyInfo.cs b/GeneralMods/CustomNPCFramework/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..db3fa352 --- /dev/null +++ b/GeneralMods/CustomNPCFramework/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("CustomNPCFramework")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CustomNPCFramework")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[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("89c7df45-8ae5-49ac-ada9-6312e9590829")] + +// 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/GeneralMods/CustomNPCFramework/manifest.json b/GeneralMods/CustomNPCFramework/manifest.json new file mode 100644 index 00000000..56346973 --- /dev/null +++ b/GeneralMods/CustomNPCFramework/manifest.json @@ -0,0 +1,10 @@ +{ + "Name": "CustomNPCFramework", + "Author": "Alpha_Omegasis", + "Version": "0.1.0", + "Description": "A system to add custom npcs to Stardew.", + "UniqueID": "Omegasis.CustomNPCFramework", + "EntryDll": "CustomNPCFramework.dll", + "MinimumApiVersion": "2.0", + "UpdateKeys": [ ] +} diff --git a/GeneralMods/CustomNPCFramework/packages.config b/GeneralMods/CustomNPCFramework/packages.config new file mode 100644 index 00000000..028670c6 --- /dev/null +++ b/GeneralMods/CustomNPCFramework/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/GeneralMods/StardewMods.sln b/GeneralMods/StardewMods.sln index 076cb9cb..d346b575 100644 --- a/GeneralMods/StardewMods.sln +++ b/GeneralMods/StardewMods.sln @@ -82,6 +82,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SundropMapEvents", "Sundrop {BB737337-2D82-4245-AA46-F3B82FC6F228} = {BB737337-2D82-4245-AA46-F3B82FC6F228} EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CustomNPCFramework", "CustomNPCFramework\CustomNPCFramework.csproj", "{89C7DF45-8AE5-49AC-ADA9-6312E9590829}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -188,6 +190,10 @@ Global {29A68F94-B23C-442B-8867-8B8F3502E64F}.Debug|Any CPU.Build.0 = Debug|Any CPU {29A68F94-B23C-442B-8867-8B8F3502E64F}.Release|Any CPU.ActiveCfg = Release|Any CPU {29A68F94-B23C-442B-8867-8B8F3502E64F}.Release|Any CPU.Build.0 = Release|Any CPU + {89C7DF45-8AE5-49AC-ADA9-6312E9590829}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {89C7DF45-8AE5-49AC-ADA9-6312E9590829}.Debug|Any CPU.Build.0 = Debug|Any CPU + {89C7DF45-8AE5-49AC-ADA9-6312E9590829}.Release|Any CPU.ActiveCfg = Release|Any CPU + {89C7DF45-8AE5-49AC-ADA9-6312E9590829}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -197,5 +203,6 @@ Global {BB737337-2D82-4245-AA46-F3B82FC6F228} = {3EE26DA0-0337-4991-8B02-BCB8D408008C} {BAAC8F21-C12F-42B0-A51C-0C5F33B52575} = {3EE26DA0-0337-4991-8B02-BCB8D408008C} {29A68F94-B23C-442B-8867-8B8F3502E64F} = {BAAC8F21-C12F-42B0-A51C-0C5F33B52575} + {89C7DF45-8AE5-49AC-ADA9-6312E9590829} = {3EE26DA0-0337-4991-8B02-BCB8D408008C} EndGlobalSection EndGlobal