Modding:World map

From Stardew Valley Wiki
Jump to navigation Jump to search

Index

This page explains how to edit the world map shown in the game menu.

To edit location maps, see Modding:Maps. See also an intro to making mods.

Data

Overview

You can change the world map by editing the Data/WorldMap asset. You can add custom maps for certain locations, apply texture overlays, add/edit tooltips, set player marker positioning, etc.

The default world map. The entire map is the Valley region, and the highlighted portion is an area with its own texture, tooltip/name, and player marker positioning.

The game divides the world map into three main concepts (see example at right):

  • A region is a large-scale part of the world containing everything shown on the map. For example, the default world map is the Valley region.
  • A map area is a subset of the world map which optionally add tooltips, scroll text, texture overlays, and player marker positioning info.
  • A map area position matches in-game locations and tile coordinates to the drawn world map. The game uses this to automatically position player markers at a relative position on the world map (e.g. so you can watch other players move across the location on the map).

In the data model:

  • each entry is a region;
  • each entry's MapAreas are the region's map area;
  • and each map area's WorldPositions are the world map positions.

The game will find the first WorldPositions entry which matches the current location, and assume you're in the region and map area which contains it. If there's none found, it defaults to the farm.

Format

The Data/WorldMap data asset consists of a string → model lookup, where...

  • The key is a unique string ID for the region.
  • The value is a model with the fields listed below.
field effect
BaseTexture (Optional) The base texture to draw for the map, if any. The first matching texture is applied. If map areas provide their own texture too, they're drawn on top of this base texture.

This consists of a list of models with these fields:

field effect
Id The unique string ID for the texture entry within the list.
Texture The asset name for the texture to draw.
SourceRect (Optional) The pixel area within the Texture to draw, specified as an object with X, Y, Width, and Height fields. Defaults to the entire texture image.
MapPixelArea (Optional) The pixel area within the map which is covered by this area, specified as an object with X, Y, Width, and Height fields. If omitted, draws the entire SourceRect area starting from the top-left corner of the map.
Condition (Optional) A game state query which indicates whether this texture should be selected. Defaults to always selected.
MapAreas The areas to draw on top of the BaseTexture. These can provide tooltips, scroll text, texture overlays, and player marker positioning info.

This consists of a list of models with these fields:

field effect
Id The unique string ID for the map area within the list.
PixelArea The pixel area within the map which is covered by this area. This is used to set the default player marker position, and is the default value for pixel areas in other fields below.
ScrollText (Optional) A tokenizable string for the scroll text (shown at the bottom of the map when the player is in this area). Defaults to none.
Textures (Optional) The image overlays to apply to the map. All matching textures are applied.

This consists of a list of models with these fields:

field effect
Id The unique string ID for the texture entry within the area.
Texture The asset name for the texture to draw.

If set to the exact string MOD_FARM, the game will apply the texture for the current farm type (regardless of whether it's a vanilla or mod farm type). This should usually be used with "MapPixelArea": "0 43 131 61" (the farm area on the default map).

SourceRect (Optional) The pixel area within the Texture to draw, specified as an object with X, Y, Width, and Height fields. Defaults to the entire texture image.
MapPixelArea (Optional) The pixel area within the map which is covered by this area, specified as an object with X, Y, Width, and Height fields. If omitted, defaults to the map area's PixelArea.
Condition (Optional) A game state query which indicates whether this texture should be selected. Defaults to always selected.
Tooltips (Optional) The tooltips to show when hovering over parts of this area on the world map.

This consists of a list of models with these fields:

field effect
Id The unique string ID for the tooltip within the area.
Text (Optional) A tokenizable string for the text to show in a tooltip.
PixelArea (Optional) The pixel area within the map which can be hovered to show this tooltip. Defaults to the area's PixelArea.
Condition (Optional) A game state query which indicates whether this tooltip should be available. Defaults to always available.
KnownCondition (Optional) A game state query which indicates whether the area is known by the player, so the Text is shown as-is. If this is false, the tooltip text is replaced with ???. Defaults to always known.
LeftNeighbor
RightNeighbor
UpNeighbor
DownNeighbor
(Optional) When navigating the world map with a controller, the tooltip to snap to when the player moves the cursor while it's on this tooltip.

This must specify the area and tooltip formatted like areaId/tooltipId (not case-sensitive). If there are multiple possible neighbors, they can be specified in comma-delimited form; the first valid one will be used.

For example, this will snap to the community center when the user moves the cursor to the right:

"RightNeighbor": "Town/CommunityCenter"

A blank value will be ignored, but the game will log a warning if you specify neighbor IDs and none of them match. To silently ignore them instead (e.g. for a conditional location), you can add 'ignore' as an option:

"RightNeighbor": "Town/SomeOptionalLocation, ignore"

See also the MapNeighborIdAliases field in the region data.

WorldPositions (Optional) The in-world locations and tile coordinates to match to this map area. The game uses this to automatically position player markers at a relative position on the world map (e.g. so you can watch other players move across the location on the map).

This consists of a list of models with these fields:

field effect
Id The unique string ID for this entry within the area.
LocationContext (Optional) The location context in which this world position applies. The vanilla contexts are Default (valley) and Island (Ginger Island).
LocationName (Optional) The location name to which this world position applies. Any location within the mines and the Skull Cavern will be Mines and SkullCave respectively, and festivals use the map asset name (like Town-EggFestival).
LocationNames (Optional) Equivalent to LocationName, but you can specify multiple locations as an array.
TileArea
MapPixelArea
(Optional) The tile area within the in-game location (TileArea) and the equivalent pixel area on the world map (MapPixelArea). These are used to calculate the position of a character or player within the map view, given their real position in-game. For example, if the player is in the top-right corner of the tile area in-game, they'll be shown in the top-right corner of the drawn area on the world map.

Both are specified as an object with X, Y, Width, and Height fields. TileArea defaults to the entire location, and MapPixelArea defaults to the map area's PixelArea.

ScrollText (Optional) A tokenizable string for the scroll text shown at the bottom of the map when the player is within this position. Defaults to the map area's ScrollText, if any.
Condition (Optional) A game state query which indicates whether this entry should be applied. Defaults to always applied.
ScrollTextZones (Optional, specialized) Smaller areas within the world map position which have their own scroll text (like "Mountains" vs "Mountain Lake" in the mountain area).

This consists of a list of models with these fields:

field effect
Id The unique string ID for this entry within the list.
TileArea The tile area within the position's TileArea for which this entry applies. See details on the parent field.
ScrollText A tokenizable string for the scroll text shown at the bottom of the map when the player is within this scroll text zone.
ExtendedTileArea (Optional, specialized) The tile area within the in-game location to which this position applies, including tiles that are outside the TileArea. The ExtendedTileArea must fully contain TileArea, since the latter won't be checked for the initial detection anymore.

For example, let's say we have this ExtendedTileArea (the larger box) and TileArea (the smaller box inside it), and the player is at position X:

┌────────────────────┐
│    ┌────────┐      │
│ X  │        │      │
│    │        │      │
│    └────────┘      │
└────────────────────┘

In this case, the entry would be selected (since the player is inside the ExtendedTileArea), and their coordinates would be shifted into the nearest TileArea position:

┌────────────────────┐
│    ┌────────┐      │
│    │X       │      │
│    │        │      │
│    └────────┘      │
└────────────────────┘

This is used for complex locations that use multiple tile areas to match a drawn map with a different layout. This can be omitted in most cases.

CustomFields The custom fields for this entry.
MapNeighborIdAliases (Optional) A set of aliases that can be used in tooltip fields like LeftNeighbor instead of the specific values they represent. Aliases can't be recursive.

For example, this lets you use Beach/FishShop in neighbor fields instead of specifying the specific tooltip IDs each time:

"MapNeighborIdAliases": {
    "Beach/FishShop": "Beach/FishShop_DefaultHours, Beach/FishShop_ExtendedHours"
}

Example

This Content Patcher content pack adds a new world map for Ginger Island. If the player unlocked the beach resort, it applies the beach resort texture.

{
    "Format": "2.0.0",
    "Changes": [
        // add world map edits
        {
            "Action": "EditData",
            "Target": "Data/WorldMap",
            "Entries": {
                "GingerIsland": {
                    "BaseTexture": [
                        {
                            "Id": "Default",
                            "Texture": "{{InternalAssetKey: assets/ginger-island.png}}"
                        }
                    ],
                    "MapAreas": [
                        // the Island South (dock) area
                        {
                            // basic info for the area within the map
                            "Id": "IslandSouth",
                            "PixelArea": { "X": 105, "Y": 105, "Width": 231, "Height": 240 },
                            "ScrollText": "Dock", // example only, should usually be translated

                            // a tooltip shown when hovering over the area on the map
                            "Tooltips": [
                                {
                                    "Id": "Dock",
                                    "Text": "Dock" // example only, should usually be translated
                                }
                            ],

                            // if the resort is unlocked, overlay a custom texture on top of the default Ginger Island map
                            "Textures": [
                                {
                                    "Id": "Resort",
                                    "Texture": "{{InternalAssetKey: assets/resort.png}}",
                                    "Condition": "PLAYER_HAS_FLAG Any Island_Resort"
                                }
                            ],

                            // the in-game locations that are part of this world map area
                            "WorldPositions": [
                                {
                                    "LocationName": "IslandSouth"
                                }
                            ]
                        }
                    ]
                }
            }
        }
    ]
}

Real-time positioning

The world map generally shows players' positions in real-time. There are three main approaches to do this for a custom location.

Automatic positioning (recommended)

If the drawn map area closely matches the in-game location, the game can determine positions automatically based on the PixelArea and LocationName fields in Data/WorldMap. For example, a player in the exact center of the in-game location will be drawn in the center of the drawn map area.

To do that:

  1. Take a screenshot of the full in-game location.
  2. Open the screenshot in an image editor like Paint.NET or GIMP.
  3. Crop as needed, then rescale it to the size you want on the world map. Make sure you use 'nearest neighbor' as the scale algorithm.
  4. Redraw parts if needed to clean it up.

That's it! If you use that as the map area's texture in Data/WorldMap, the game will be able to determine positions automatically. You can omit the WorldPositions field with this approach.

Manual positioning

If the in-game layout doesn't match the drawn world map, you can use the WorldPositions field in Data/WorldMap to manually align positions between them. This can be tricky; usually automatic positioning is recommended instead.

For example, the mountain's map area was very stylized before Stardew Valley 1.6 (the mine and adventure guild were right next to each other, there were no islands, there was no water south of the guild, etc):

The pre-1.6 mountain's in-game location (top) and world map area (bottom).

With manual positioning, you add any number of world positions with a TileArea (the tile coordinates where the player is standing in the actual location) and MapPixelArea (where that area is on the map). When the player is within the TileArea, they'll be mapped to the relative position within the matching MapPixelArea. For example, if they're in the exact center of the TileArea, they'll be drawn in the center of the MapPixelArea.

For example, you could divide the pre-1.6 mountain into multiple areas like this (see the data format for info on each field):

"WorldPositions": [
    {
        "Id": "Quarry",
        "LocationName": "Mountain",
        "TileArea": { "X": 95, "Y": 11, "Width": 36, "Height": 24 },
        "ExtendedTileArea": { "X": 95, "Y": 0, "Width": 255, "Height": 255 },
        "MapPixelArea": { "X": 236, "Y": 29, "Width": 28, "Height": 19 }
    },
    {
        "Id": "Lake_Guild",
        "LocationName": "Mountain",
        "TileArea": { "X": 73, "Y": 5, "Width": 22, "Height": 30 },
        "ExtendedTileArea": { "X": 73, "Y": 0, "Width": 22, "Height": 255 },
        "MapPixelArea": { "X": 227, "Y": 29, "Width": 9, "Height": 19 }
    },
    {
        "Id": "Lake_BetweenGuildAndMine",
        "LocationName": "Mountain",
        "TileArea": { "X": 57, "Y": 5, "Width": 16, "Height": 32 },
        "ExtendedTileArea": { "X": 57, "Y": 0, "Width": 16, "Height": 255 },
        "MapPixelArea": { "X": 224, "Y": 29, "Width": 3, "Height": 19 }
    },
    {
        "Id": "Lake_Mine",
        "LocationName": "Mountain",
        "TileArea": { "X": 52, "Y": 5, "Width": 5, "Height": 30 },
        "ExtendedTileArea": { "X": 52, "Y": 0, "Width": 5, "Height": 255 },
        "MapPixelArea": { "X": 220, "Y": 29, "Width": 4, "Height": 19 }
    },
    {
        "Id": "Lake_MineBridge",
        "LocationName": "Mountain",
        "TileArea": { "X": 44, "Y": 5, "Width": 8, "Height": 30 },
        "ExtendedTileArea": { "X": 44, "Y": 0, "Width": 8, "Height": 255 },
        "MapPixelArea": { "X": 210, "Y": 29, "Width": 10, "Height": 19 }
    },
    {
        "Id": "West",
        "LocationName": "Mountain",
        "TileArea": { "X": 0, "Y": 5, "Width": 44, "Height": 30 },
        "ExtendedTileArea": { "X": 0, "Y": 0, "Width": 44, "Height": 255 },
        "MapPixelArea": { "X": 175, "Y": 29, "Width": 35, "Height": 19 }
    },
    {
        "Id": "Default",
        "LocationName": "Mountain"
    }
]

Here's a visual representation of those areas:

The pre-1.6 mountain's in-game location with highlighted TileArea positions (top) and world map with highlighted MapPixelArea positions (bottom).

Note how the area between the mine and adventurer's guild is wide in the location, but narrow on the drawn world map. When the player is walking across that part of the location, they'll be shown walking slowly across the equivalent location on the drawn map.

If the player is outside a TileArea but within the ExtendedTileArea (if set), their position is snapped to the nearest position within the TileArea. For example, notice how the bottom of the location south of the carpenter shop isn't part of the red area. It is part of that area's ExtendedTileArea though, so a player there will be snapped to the bottom of the red area on the world map.

Fixed positions

For very complex locations, real-time positions on the world map may not be possible (e.g. because the drawn world map is very stylized). In that case you can set a fixed position (or multiple fixed positions) on the world map.

For example, this draws the player marker at one of five world map positions depending where they are in town. The TileArea indicates the tile coordinates where the player is standing in the actual town, and MapPixelArea is where to draw them on the map. Note that the latter is always 1x1 pixel in the code below, which means that anywhere within the TileArea will be placed on that specific pixel on the world map. The last entry has no TileArea, which means it applies to all positions that didn't match a previous entry.

"WorldPositions": [
    {
        "Id": "East_NearJojaMart",
        "LocationName": "Town",
        "TileArea": { "X": 85, "Y": 0, "Width": 255, "Height": 68 },
        "MapPixelArea": { "X": 225, "Y": 81, "Width": 1, "Height": 1 }
    },
    {
        "Id": "East_NearMuseum",
        "LocationName": "Town",
        "TileArea": { "X": 81, "Y": 68, "Width": 255, "Height": 255 },
        "MapPixelArea": { "X": 220, "Y": 108, "Width": 1, "Height": 1 }
    },
    {
        "Id": "West_North",
        "LocationName": "Town",
        "TileArea": { "X": 0, "Y": 0, "Width": 85, "Height": 43 },
        "MapPixelArea": { "X": 178, "Y": 64, "Width": 1, "Height": 1 }
    },
    {
        "Id": "West_Center",
        "LocationName": "Town",
        "TileArea": { "X": 0, "Y": 43, "Width": 85, "Height": 33 },
        "MapPixelArea": { "X": 175, "Y": 88, "Width": 1, "Height": 1 }
    },
    {
        "Id": "West_South",
        "LocationName": "Town",
        "MapPixelArea": { "X": 182, "Y": 109, "Width": 0, "Height": 0 }
    }
]

Interacting with the world map in C#

SMAPI mods (written in C#) can use the game's StardewValley.WorldMaps.WorldMapManager class to interact with the world map.

For example, you can get the pixel position on the world map which matches an in-game tile coordinate (if the location appears in Data/WorldMap):

MapAreaPosition mapAreaPosition = WorldMapManager.GetPositionData(location, tile);
if (mapAreaPosition != null)
    return mapAreaPosition.GetMapPixelPosition(location, tile);

Debug view

You can run debug worldMapLines in the SMAPI console window to enable the world map's debug view. This will outline map areas (black), map area positions (blue), and tooltips (green):

The world map with the debug view enabled.

You can optionally specify which types to highlight, like debug worldMapLines areas positions tooltips.