Tutorial: LocalTokens

From Stardew Modding Wiki
Jump to navigation Jump to search

Last edited by AtlasVBot on 2025-09-13 18:57:36

Templates And Removal Of Copy Paste

So you're tired of Copying and Pasting mundane information because you have a lot of animals/crops/items?

Localtokens is your new best friend!

"Oh god it's Aviroen again."

Ya right it's me again, here to tell you how you can remove the unnecessary copying and pasting of things so that you get it right the first time, it'll be right EVERY time.

So what are Localtokens?

How do I describe this? It's effectively tokens that plug the "string" that you've typed in, into wherever you've deemed the token to be.

Let's just look at an example that I've already done for my sharks.

AnimalTemplate.json

{
  "Changes": [
    {
      "Action": "Load",
      "Target": "Mods/{{ModId}}/Animals/{{ANIMAL}}",
      "FromFile": "Assets/Animals/{{ANIMAL}}.png"
    },
    {
      "Action": "EditData",
      "Target": "Data/FarmAnimals",
      "Entries": {
        "{{ModId}}_{{ANIMAL}}": {
          "DisplayName": "{{i18n:{{ANIMAL}}.Name}}",
          "House": "{{HOUSE}}",
          "Gender": "MaleOrFemale",
          "PurchasePrice": "{{PRICE}}",
          "SellPrice": "{{SELLPRICE}}",
          "ShopTexture": "Mods\\{{ModId}}\\AnimalShop",
          "ShopSourceRect": {
            "X": "{{XSOURCERECT}}",
            "Y": "{{YSOURCERECT}}",
            "Width": 32,
            "Height": 16
          },
          "ShopDisplayName": "{{i18n:{{ANIMAL}}.Name}}",
          "ShopDescription": "{{i18n:{{ANIMAL}}.ShopDescription}}",
          "ShopMissingBuildingDescription": "{{i18n:{{ANIMAL}}.MissingBuilding}}",
          "RequiredBuilding": "{{REQBUILDING}}",
          "UnlockCondition": "{{UNLOCKCONDITION}}",
          "AlternatePurchaseTypes": null,
          "EggItemIds": null,
          "IncubationTime": -1,
          "IncubatorParentSheetOffset": 1,
          "BirthText": null,
          "DaysToMature": "{{DAYSTOMATURE}}",
          "CanGetPregnant": false,
          "DaysToProduce": "{{DAYSTOPRODUCE}}",
          "HarvestType": "DropOvernight",
          "HarvestTool": null,
          "ProduceItemIds": [
            {
              "Id": "Default",
              "Condition": null,
              "MinimumFriendship": 0,
              "ItemId": "{{PRODUCEITEMID}}"
            }
          ],
          "DeluxeProduceItemIds": [
            {
              "Id": "Default",
              "Condition": null,
              "MinimumFriendship": 0,
              "ItemId": "{{DELUXEITEMID}}"
            }
          ],
          "ProduceOnMature": false,
          "FriendshipForFasterProduce": -1,
          "DeluxeProduceMinimumFriendship": 0,
          "DeluxeProduceCareDivisor": 5000.0,
          "DeluxeProduceLuckMultiplier": 1.02,
          "CanEatGoldenCrackers": true,
          "ProfessionForHappinessBoost": 2,
          "ProfessionForQualityBoost": 2,
          "ProfessionForFasterProduce": -1,
          "Sound": "wateringCan",
          "BabySound": null,
          "Texture": "Mods\\{{ModId}}\\Animals\\{{ANIMAL}}",
          "HarvestedTexture": null,
          "BabyTexture": null,
          "UseFlippedRightForLeft": "{{USEFLIPPEDLR}}",
          "SpriteWidth": "{{SPRITEWIDTH}}",
          "SpriteHeight": "{{SPRITEHEIGHT}}",
          "UseDoubleUniqueAnimationFrames": false,
          "SleepFrame": "{{SLEEPFRAME}}",
          "EmoteOffset": {
            "X": 0,
            "Y": 0
          },
          "SwimOffset": {
            "X": 0,
            "Y": 112
          },
          "Skins": null,
          "ShadowWhenBabySwims": null,
          "ShadowWhenBaby": null,
          "ShadowWhenAdultSwims": null,
          "ShadowWhenAdult": null,
          "Shadow": {
            "Visible": false,
            "Offset": null,
            "Scale": null
          },
          "CanSwim": false,
          "BabiesFollowAdults": false,
          "GrassEatAmount": 4,
          "HappinessDrain": 4,
          "UpDownPetHitboxTileSize": "1.5, 1.75",
          "LeftRightPetHitboxTileSize": "1.75, 1.25",
          "BabyUpDownPetHitboxTileSize": "1, 1",
          "BabyLeftRightPetHitboxTileSize": "1, 1",
          "StatToIncrementOnProduce": null,
          "ShowInSummitCredits": true,
          "CustomFields": {
            "mushymato.LivestockBazaar/BuyFrom.{{ModId}}_Lou": true,
            "mushymato.LivestockBazaar/BuyFrom.Marnie": false,
            "mushymato.LivestockBazaar/TradeItemId.{{ModId}}_Lou": "{{TRADEITEMID}}",
            "mushymato.LivestockBazaar/TradeItemAmount": "{{TRADEITEMCOUNT}}"
          }
        }
      }
    }
  ]
}

That looks like a lot, doesn't it? It has a load, EditData, even some CustomFields.

Let's see what the actual use case looks like.

BarnAnimals.json

{
  "Changes": [
    {
      "Action": "Include",
      "FromFile": "Data/Templates/AnimalTemplate.json",
      "LocalTokens": {
        "ANIMAL": "Shjorbk",
        "HOUSE": "Barn",
        "PRICE": "5000",
        "SELLPRICE": "4000",
        "REQBUILDING": "Barn",
        "XSOURCERECT": "32",
        "YSOURCERECT": "0",
        "UNLOCKCONDITION": "PLAYER_HAS_MAIL Current {{ModId}}_ShjorbkUnlock",
        "DAYSTOMATURE": "14",
        "DAYSTOPRODUCE": "5",
        "PRODUCEITEMID": "128",
        "DELUXEITEMID": "162",
        "USEFLIPPEDLR": "true",
        "SPRITEWIDTH": "32",
        "SPRITEHEIGHT": "32",
        "SLEEPFRAME": "9",
        "TRADEITEMID": "(O){{ModId}}_Shorbk",
        "TRADEITEMCOUNT": "5"
      }
    }
  ]
}

In every spot that you'll see {{ANIMAL}} in the first one, it is replaced with "Shjorbk".

So if we replace everything in the {{BRACKETS}} from the 2nd, maybe it'll be clearer.

UntemplatedAnimals.json

{
  "Changes": [
    {
      "Action": "Load",
      "Target": "Mods/{{ModId}}/Animals/Shjorbk",
      "FromFile": "Assets/Animals/Shjorbk.png"
    },
    {
      "Action": "EditData",
      "Target": "Data/FarmAnimals",
      "Entries": {
        "{{ModId}}_Shjorbk": {
          "DisplayName": "{{i18n:Shjorbk.Name}}",
          "House": "Barn",
          "Gender": "MaleOrFemale",
          "PurchasePrice": "5000",
          "SellPrice": "4000",
          "ShopTexture": "Mods\\{{ModId}}\\AnimalShop",
          "ShopSourceRect": {
            "X": "32",
            "Y": "0",
            "Width": 32,
            "Height": 16
          },
          "ShopDisplayName": "{{i18n:Shjorbk.Name}}",
          "ShopDescription": "{{i18n:Shjorbk.ShopDescription}}",
          "ShopMissingBuildingDescription": "{{i18n:Shjorbk.MissingBuilding}}",
          "RequiredBuilding": "Barn",
          "UnlockCondition": "PLAYER_HAS_MAIL Current {{ModId}}_ShjorbkUnlock",
          "AlternatePurchaseTypes": null,
          "EggItemIds": null,
          "IncubationTime": -1,
          "IncubatorParentSheetOffset": 1,
          "BirthText": null,
          "DaysToMature": "14",
          "CanGetPregnant": false,
          "DaysToProduce": "5",
          "HarvestType": "DropOvernight",
          "HarvestTool": null,
          "ProduceItemIds": [
            {
              "Id": "Default",
              "Condition": null,
              "MinimumFriendship": 0,
              "ItemId": "128"
            }
          ],
          "DeluxeProduceItemIds": [
            {
              "Id": "Default",
              "Condition": null,
              "MinimumFriendship": 0,
              "ItemId": "162"
            }
          ],
          "ProduceOnMature": false,
          "FriendshipForFasterProduce": -1,
          "DeluxeProduceMinimumFriendship": 0,
          "DeluxeProduceCareDivisor": 5000.0,
          "DeluxeProduceLuckMultiplier": 1.02,
          "CanEatGoldenCrackers": true,
          "ProfessionForHappinessBoost": 2,
          "ProfessionForQualityBoost": 2,
          "ProfessionForFasterProduce": -1,
          "Sound": "wateringCan",
          "BabySound": null,
          "Texture": "Mods\\{{ModId}}\\Animals\\Shjorbk",
          "HarvestedTexture": null,
          "BabyTexture": null,
          "UseFlippedRightForLeft": "true",
          "SpriteWidth": "32",
          "SpriteHeight": "32",
          "UseDoubleUniqueAnimationFrames": false,
          "SleepFrame": "9",
          "EmoteOffset": {
            "X": 0,
            "Y": 0
          },
          "SwimOffset": {
            "X": 0,
            "Y": 112
          },
          "Skins": null,
          "ShadowWhenBabySwims": null,
          "ShadowWhenBaby": null,
          "ShadowWhenAdultSwims": null,
          "ShadowWhenAdult": null,
          "Shadow": {
            "Visible": false,
            "Offset": null,
            "Scale": null
          },
          "CanSwim": false,
          "BabiesFollowAdults": false,
          "GrassEatAmount": 4,
          "HappinessDrain": 4,
          "UpDownPetHitboxTileSize": "1.5, 1.75",
          "LeftRightPetHitboxTileSize": "1.75, 1.25",
          "BabyUpDownPetHitboxTileSize": "1, 1",
          "BabyLeftRightPetHitboxTileSize": "1, 1",
          "StatToIncrementOnProduce": null,
          "ShowInSummitCredits": true,
          "CustomFields": {
            "mushymato.LivestockBazaar/BuyFrom.{{ModId}}_Lou": true,
            "mushymato.LivestockBazaar/BuyFrom.Marnie": false,
            "mushymato.LivestockBazaar/TradeItemId.{{ModId}}_Lou": "(O){{ModId}}_Shorbk",
            "mushymato.LivestockBazaar/TradeItemAmount": "5"
          }
        }
      }
    }
  ]
}

So that's a lot, right? Having to copy and paste all of that information for EVERY single animal seems like a tedious task and it could cause you to miss some brackets along the way.

Understanding Templating

Content Patcher will be able to understand when you place something into {{BRACKETS}} that you're replacing the string with the information.

I have not gone in depth what can and can't be placed inside of these brackets, and I keep them relatively simplistic (because I've already caused it to break before).

If you'd like to see the madness that I've already created, you're free to look at my MIT-licensed scriptwork.

There are some caveats in the above that my "SOTemplate" and "SORTemplate" will only really work for Voidsent as I have some custom c# work in there (for my own Special Order Board).

What Does Templating Mean

From the above example, most people are used to just doing the UntemplatedAnimals.json over and over, that could cause some syntax issues like missing brackets, missing information, copied and pasted information that instead needed to be replaced.

If you set your templates, the only things you have to remember are the tokens that are inside.

How do I call a template?

"Action": "Include",
      "FromFile": "Data/Templates/AnimalTemplate.json",
      "LocalTokens": {
	  "YOUR": "I'm",
	  "LOCAL": "The",
	  "TOKENS": "Information",
	  "HERE": "Provided"
	  }

By doing the Action: Include, you're calling any of the templates that you've already created.

"YOUR" is going to be replaced with "I'm" every single time that you've placed {{YOUR}} somewhere in the template.