public static Map makeMapCopy(FarmHouse house, string mapPath) { Map sourceMap = loader.Load <Map>(mapPath, ContentSource.GameContent); Map newMap = new Map(); foreach (xTile.Tiles.TileSheet sheet in sourceMap.TileSheets) { xTile.Tiles.TileSheet newSheet = new xTile.Tiles.TileSheet(newMap, sheet.ImageSource, sheet.SheetSize, sheet.TileSize); newSheet.Id = sheet.Id; newMap.AddTileSheet(newSheet); Logger.Log("Adding tilesheet " + newSheet.Id + " (" + newSheet.ImageSource + ")"); } foreach (xTile.Layers.Layer layer in sourceMap.Layers) { xTile.Layers.Layer newLayer = new xTile.Layers.Layer(layer.Id, newMap, layer.LayerSize, layer.TileSize); xTile.Tiles.TileArray tiles = layer.Tiles; for (int x = 0; x < layer.LayerWidth; x++) { for (int y = 0; y < layer.LayerHeight; y++) { if (tiles[x, y] != null) { xTile.Tiles.Tile tile = tiles[x, y]; if (tile is xTile.Tiles.StaticTile) { Logger.Log("Searching for sheet '" + tile.TileSheet.Id + "'..."); newLayer.Tiles[x, y] = new xTile.Tiles.StaticTile(newLayer, newMap.GetTileSheet(tile.TileSheet.Id), tile.BlendMode, tile.TileIndex); } else { xTile.Tiles.AnimatedTile animTile = (tile as xTile.Tiles.AnimatedTile); xTile.Tiles.StaticTile[] frames = new xTile.Tiles.StaticTile[animTile.TileFrames.Length]; for (int frame = 0; frame < animTile.TileFrames.Length; frame++) { frames[frame] = new xTile.Tiles.StaticTile(newLayer, newMap.GetTileSheet(animTile.TileSheet.Id), animTile.BlendMode, animTile.TileFrames[frame].TileIndex); } newLayer.Tiles[x, y] = new xTile.Tiles.AnimatedTile(newLayer, frames, animTile.FrameInterval); } foreach (string key in tile.Properties.Keys) { newLayer.Tiles[x, y].Properties[key] = tile.Properties[key]; } } } } foreach (string key in layer.Properties.Keys) { newLayer.Properties[key] = layer.Properties[key]; } newMap.AddLayer(newLayer); } foreach (string key in sourceMap.Properties.Keys) { newMap.Properties[key] = sourceMap.Properties[key]; } return(newMap); }
public static dynamic HandleRequestMessage(string msgType, dynamic data = null) { var player = Game1.player; int playerX = player.getTileX(); int playerY = player.getTileY(); GameLocation location = player.currentLocation; switch (msgType) { case "HEARTBEAT": // engine will shutdown if heartbeat not received after 10 seconds return(null); case "REQUEST_BATCH": var body = new List <dynamic>(); foreach (dynamic batchedRequest in data) { string batchedMsgType = batchedRequest.type; dynamic batchedMsgData = JsonConvert.DeserializeObject(batchedRequest.data.ToString()); body.Add(HandleRequestMessage(batchedMsgType, batchedMsgData)); } return(body); case "PLAYER_STATUS": return(GameState.PlayerStatus()); case "TOOL_STATUS": return(GameState.ToolStatus()); case "CHARACTERS_AT_LOCATION": return(GameState.CharactersAtLocation(Game1.currentLocation)); case "ANIMALS_AT_LOCATION": return(GameState.AnimalsAtLocation(Game1.currentLocation)); case "PLAYER_POSITION": return(GameState.PlayerPosition); case "PLAYER_ITEMS": return(GameState.PlayerItems()); case "GET_MONSTER_TARGET": { var chars = new List <dynamic>(); var monsters = GameState.CharactersAtLocation(Game1.currentLocation). Select(x => x.isMonster). OrderByDescending(x => x.isInvisible); foreach (var monster in monsters) { } return(null); } case "NEW_STREAM": { string streamId = data.stream_id; string streamName = data.name; object streamData = data.data; var stream = new Stream(streamName, streamId, streamData); // shallow copy as naive but simple way to avoid multithreading issues var newStreams = new Dictionary <string, Stream>(ModEntry.Streams); newStreams.Add(streamId, stream); ModEntry.Streams = newStreams; return(true); } case "STOP_STREAM": { string streamId = data; // shallow copy as naive but simple way to avoid multithreading issues var newStreams = new Dictionary <string, Stream>(ModEntry.Streams); newStreams.Remove(streamId); ModEntry.Streams = newStreams; return(true); } case "ROUTE": { GameLocation fromLocation = player.currentLocation; string toLocationStr = data.toLocation; GameLocation toLocation = Game1.getLocationFromName(toLocationStr); //throw new InvalidOperationException($"Missing location {name}") return(Routing.GetRoute(fromLocation.NameOrUniqueName, toLocation.NameOrUniqueName)); } case "ROUTE_INDOORS": { GameLocation fromLocation = player.currentLocation; return(true); } case "path_to_tile": { int targetX = data.x; int targetY = data.y; int cutoff = data.cutoff; var path = Pathfinder.Pathfinder.FindPath(player.currentLocation, playerX, playerY, targetX, targetY, cutoff); return(path); } case "PATH_TO_EDGE": { int direction = data.direction; Layer layer = player.currentLocation.map.Layers[0]; bool isXAxis = false; int testX = playerX; int testY = 0; int increment = 1; if (direction == 1) { isXAxis = true; testX = layer.LayerWidth - 1; testY = playerY; increment = -1; } else if (direction == 2) { testX = playerX; testY = layer.LayerHeight - 1; increment = -1; } else if (direction == 3) { isXAxis = true; testX = 0; testY = playerY; } dynamic path = null; while (testX != playerX || testY != playerY) { if (Pathfinder.Pathfinder.isTileWalkable(location, testX, testY)) { path = Pathfinder.Pathfinder.FindPath(location, testX, testY, playerX, playerY); if (path != null) { path.Reverse(); break; } ; } if (isXAxis) { testX += increment; } else { testY += increment; } } return(path); } case "GET_NEAREST_CHARACTER": { string characterType = data.characterType; // animal, npc, monster etc string getBy = data.getBy; bool getPath = data.getPath; string requiredName = data.requiredName; List <List <int> > currentPathTiles = data.pathTiles; dynamic target = JsonConvert.DeserializeObject <dynamic>(data.target.ToString()); string targetName = requiredName ?? (target?.name); int? targetTileX = target?.tileX; int? targetTileY = target?.tileY; string targetTrackingId = target?.trackingId; float fromPositionX = target == null ? player.Position.X : target.center[0]; float fromPositionY = target == null ? player.Position.Y : target.center[1]; var candidates = new List <dynamic>(); List <dynamic> sorted; if (characterType == "animal") { if (!(location is IAnimalLocation)) { return(null); } foreach (FarmAnimal farmAnimal in (location as IAnimalLocation).Animals.Values) { var animal = Serialization.SerializeAnimal(farmAnimal); if ((getBy == "unpet" && !animal.wasPet) || (getBy == "readyForHarvest" && animal.readyForHarvest)) { candidates.Add(animal); } } sorted = candidates.OrderByDescending(x => targetTrackingId != null && x.trackingId == targetTrackingId). ThenBy(x => Utils.DistanceBeteenPoints(x.position[0], x.position[1], fromPositionX, fromPositionY)). ToList(); } else if (characterType == "npc" || characterType == "monster") { var charList = Game1.CurrentEvent != null ? Game1.CurrentEvent.actors : location.characters.ToList(); foreach (var character in charList) { if ((characterType == "monster" && character.IsMonster) || (characterType == "npc" && !character.IsMonster)) { candidates.Add(Serialization.SerializeCharacter(character)); } } sorted = candidates.OrderBy(x => x.isInvisible). ThenByDescending(x => targetTrackingId != null && x.trackingId == targetTrackingId). ThenByDescending(x => targetName != null && x.name == targetName). ThenBy(x => Utils.DistanceBeteenPoints(x.position[0], x.position[1], fromPositionX, fromPositionY)). ToList(); } else { throw new InvalidDataException(); } if (requiredName != null) { sorted = sorted.Where(x => x.name == requiredName).ToList(); } if (!getPath) { return(sorted.Count > 0 ? sorted[0] : null); } foreach (var candidate in sorted) { if (candidate.tileX == targetTileX && candidate.tileY == targetTileY) { return(candidate); } if (Utils.DistanceBeteenPoints(candidate.tileX, candidate.tileY, playerX, playerY) < 2) { return(candidate); } var pathTiles = Pathfinder.Pathfinder.FindPath(player.currentLocation, candidate.tileX, candidate.tileY, playerX, playerY, -1); if (pathTiles != null) { return(Utils.Merge(candidate, new { pathTiles })); } } return(null); } case "PATH_TO_PLAYER": { int fromX = data.x; int fromY = data.y; int cutoff = data.cutoff; var tiles = Pathfinder.Pathfinder.FindPath(player.currentLocation, fromX, fromY, playerX, playerY, cutoff); return(new { tiles, location = player.currentLocation.NameOrUniqueName }); } case "BED_TILE": { if (Game1.player.currentLocation is StardewValley.Locations.FarmHouse) { var fh = Game1.player.currentLocation as StardewValley.Locations.FarmHouse; var bed = fh.getBedSpot(); return(new { tileX = bed.X, tileY = bed.Y }); } return(null); } case "SHIPPING_BIN_TILE": { if (Game1.player.currentLocation is StardewValley.Farm) { var farm = Game1.player.currentLocation as StardewValley.Farm; var sb = Game1.getFarm().GetStarterShippingBinLocation(); return(new { tileX = (int)sb.X, tileY = (int)sb.Y, width = 2, height = 1 }); } return(null); } case "LOCATION_CONNECTION": { GameLocation fromLocation = player.currentLocation; string toLocationStr = data.toLocation; GameLocation toLocation = Game1.getLocationFromName(toLocationStr); var locationConnection = Routing.FindLocationConnection(fromLocation, toLocation); return(locationConnection); } case "GET_LOCATION_CONNECTIONS": { GameLocation fromLocation = player.currentLocation; return(Routing.MapConnections[fromLocation.NameOrUniqueName]); } case "GET_LADDERS_DOWN": { var ladders = new List <dynamic>(); xTile.Tiles.TileArray tiles = location.Map.GetLayer("Buildings").Tiles; var buildingsLayer = location.Map.GetLayer("Buildings"); for (int y = 0; y < buildingsLayer.LayerHeight; y++) { for (int x = 0; x < buildingsLayer.LayerWidth; x++) { if (buildingsLayer.Tiles[x, y] != null) { int currentTileIndex = buildingsLayer.Tiles[x, y].TileIndex; if (currentTileIndex == 173) { ladders.Add(new { tileX = x, tileY = y }); } } } } return(ladders); } case "GET_LOCATION_BUILDINGS": { return(GameState.LocationBuildings(location)); } case "GET_ELEVATOR_TILE": { if (player.currentLocation is StardewValley.Locations.MineShaft) { Vector2 elevator = Utils.GetPrivateField(player.currentLocation, "tileBeneathElevator"); return(new { tileX = (int)elevator.X, tileY = (int)elevator.Y }); } return(null); } case "SHOW_HUD_MESSAGE": { string message = data.message; int mType = data.msgType; var msg = new HUDMessage(message, mType); if (Context.IsWorldReady) { Game1.addHUDMessage(msg); } else { ModEntry.QueuedMessage = msg; } return(true); } case "GET_LADDER_UP_TILE": { if (player.currentLocation is StardewValley.Locations.MineShaft) { Vector2 elevator = Utils.GetPrivateField(player.currentLocation, "tileBeneathLadder"); return(new { tileX = (int)elevator.X, tileY = (int)elevator.Y }); } return(null); } case "PET_ANIMAL_BY_NAME": { string name = data.name; FarmAnimal animal = Utils.FindAnimalByName(name); bool didPet = false; if (animal != null && !animal.wasPet) { animal.pet(player); didPet = true; } return(didPet); } case "USE_TOOL_ON_ANIMAL_BY_NAME": { string name = data.name; FarmAnimal animal = Utils.FindAnimalByName(name); bool didUseTool = false; if (animal != null && player.CurrentTool?.BaseName == animal.toolUsedForHarvest.Value) { Rectangle rect = animal.GetHarvestBoundingBox(); int x = rect.Center.X - Game1.viewport.X; int y = rect.Center.Y - Game1.viewport.Y; Game1.setMousePosition(x, y); Game1.pressUseToolButton(); //player.CurrentTool.beginUsing(player.currentLocation, (int)animal.Position.X, (int)animal.Position.Y, player); didUseTool = true; } return(didUseTool); } case "GET_TERRAIN_FEATURES": { return(GameState.TerrainFeatures()); } case "GET_DEBRIS": { return(GameState.Debris()); } case "GET_HOE_DIRT": { return(GameState.HoeDirtTiles()); } case "GET_LOCATION_OBJECTS": { return(GameState.LocationObjects()); } case "GET_DIGGABLE_TILES": { List <dynamic> testTiles = data.tiles.ToObject <List <dynamic> >(); return(testTiles.Where(tile => Utils.IsTileHoeable(Game1.player.currentLocation, (int)tile.tileX, (int)tile.tileY))); } case "GET_WATER_TILES": { WaterTiles.WaterTileData[,] allTiles = Game1.player.currentLocation.waterTiles.waterTiles; var wt = new List <List <int> >(); if (allTiles != null) { int width = allTiles.GetLength(0); int height = allTiles.GetLength(1); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { bool isWaterTile = allTiles[x, y].isWater; if (isWaterTile) { var tile = new List <int> { x, y }; wt.Add(tile); } } } } return(wt); } case "GET_ACTIVE_MENU": return(Utils.SerializeMenu(Game1.activeClickableMenu)); case "GET_MOUSE_POSITION": { return(new List <int> { Game1.getMouseX(), Game1.getMouseY() }); } case "TTS_SPEAK": { string text = data.text; return(true); } case "SET_MOUSE_POSITION": { int x = data.x; int y = data.y; bool fromViewport = data.from_viewport; if (fromViewport) { x -= Game1.viewport.X; y -= Game1.viewport.Y; } Game1.setMousePosition(x, y); return(true); } case "SET_MOUSE_POSITION_ON_TILE": { int x = data.x * 64 + 32 - Game1.viewport.X; int y = data.y * 64 + 32 - Game1.viewport.Y; Game1.setMousePosition(x, y); return(true); } case "SET_MOUSE_POSITION_RELATIVE": { int x = data.x; int y = data.y; Game1.setMousePosition(Game1.getMouseX() + x, Game1.getMouseY() + y); return(true); } case "MOUSE_CLICK": { string btn = data.btn; if (btn == "left") { StardewSpeak.Input.LeftClick(); } else if (btn == "right") { StardewSpeak.Input.RightClick(); } return(true); } case "UPDATE_HELD_BUTTONS": { List <string> toHold = data.toHold.ToObject <List <string> >(); List <string> toRelease = data.toRelease.ToObject <List <string> >(); foreach (string key in toRelease) { Input.Release(key); } foreach (string key in toHold) { Input.Hold(key); } return(true); } case "RELEASE_ALL_KEYS": { Input.ClearHeld(); return(true); } case "PRESS_KEY": { string key = data.key; Input.SetDown(key); return(true); } case "GET_ALL_GAME_LOCATIONS": { var locs = Routing.AllGameLocations(includeBuildings: false).Select(x => x.NameOrUniqueName).ToList(); return(locs); } case "GET_LATEST_GAME_EVENT": { return(Serialization.SerializeGameEvent(Game1.CurrentEvent)); } case "CATCH_FISH": { var am = Game1.activeClickableMenu; if (am is BobberBar) { var bb = am as BobberBar; var distanceFromCatching = (float)Utils.GetPrivateField(bb, "distanceFromCatching"); bool treasure = (bool)Utils.GetPrivateField(bb, "treasure"); if (treasure) { Utils.SetPrivateField(bb, "treasureCaught", true); } Utils.SetPrivateField(bb, "distanceFromCatching", distanceFromCatching + 100); } return(true); } case "GET_RESOURCE_CLUMPS": { return(GameState.ResourceClumps()); } } throw new InvalidDataException($"Unhandled request {msgType}"); }