Last updated: January 30th, 2019. Latest version here.

Adding jobs

We can create our own jobs / classes and alter the existing promotion tree to suit our preferences.

Creating a new job

These are generic steps for creating a new job:

  1. Copy an existing job from the stonehearth mod. Preferably one that is most close in functionality to what you're creating. The existing jobs are inside the stonehearth/jobs directory.

  2. Rename the files as usual.

  3. Add an alias for your custom job in your manifest, pointing to the JSON file that contains the description of the job.

  4. Add an alias in the "controllers" section of your manifest, pointing to the Lua file of your job. E.g.:

    "controllers": {
       "class:lamplighter": "file(jobs/lamplighter/lamplighter.lua)"
    }
    
  5. Add a mixinto to "stonehearth:jobs:index" to add your custom job to the list of available jobs.

  6. Start by editing the talisman. Change the model as appropiate and edit the corresponding JSON files to point to those of your custom job.

  7. Edit the job description JSON file. The data inside this file will vary depending on the job type (crafting / combat / etc). You'll need to add more aliases to your manifest, at least for the talisman, outfit, and tool / weapon.

  8. Edit the rest of the models, UI and JSON files (the outfit, tool, workshop skin, etc.). These files may vary depending on the job type. There may also be Lua files involved, make sure to rename text as needed.

The talisman

Each job in stonehearth (except for Worker, which is the base job) makes use of an item we call talisman in order to unlock the job and promote the hearthling. Once promoted, the talisman is consumed and it's replaced by an equippable version of it (an actual tool / weapon), which the hearthling makes use of when they're doing their job or when they're fighting.

Normally we name the related files with the job name plus the name of the tool itself, for example farmer_hoe.json, farmer_hoe_talisman.json, farmer_hoe_iconic.json.

  • The job_tool_talisman.json file is similar to the file for other entities. In it, we have the item properties mixin.

    For the entity_data we have the net worth and the catalog (make sure that the category is "tools" and that the material_tags include "stockpile_tool", so that it's properly restocked to storages).

    For the components, we have the model variants, which point to the model for the iconic version, and an "effect_list" with a default effect (in the stonehearth mod, iconics for talismans and workbenches have this particle effect to distinguish them quickly in stockpiles or the ground).

    icon You might also see in some talisman files a component called "stonehearth:promotion_talisman". This is an old component that was deprecated and no longer exists, so you can omit it for your job.

  • Now we edit the job_tool.json file. The model in the "model_variants" component is that of the equipped tool, so remember to positionate it in your voxel editor so that the hearthling can grab it properly.

    The entity forms component only points to the iconic form, since this item is not meant to be placeable so we don't need a ghost JSON file.

    The equipment_piece component usually has a "postures" field, since this is the weapon that they will use when in combat (even if this is not a combat job, they will still fight while in town alert mode).

    In the entity_data, besides the usual "stonehearth:net_worth" and "stonehearth:catalog" entries, we have "stonehearth:combat:weapon_data", to define the range of the tool when it's used as a weapon in town alert and how much damage should it do; and "stonehearth:combat:melee_attacks", to define a basic attack.

    For combat units, advanced attacks are usually specified in other files, so that we can unlock them via a specific weapon or at a specific level.

  • Then we edit the job_tool_iconic.json, and we add aliases for both the talisman and the tool (weapon).

  • Finally, don't forget to make the talisman obtainable in the game! Via a recipe, loot, quest reward, whatever you want.

Job description

These are the common fields that we can have for all jobs in their description JSON file:

  • "type": "job" -- this file represents a job.

  • "enabled" -- usually we want to set this to true. There's no function in game to enable jobs on the fly, so only set it to false if your job is a work in progress and you don't want it to appear in the promotion tree yet. There's also the possibility of using "in-progress" instead of true/false, which will make it appear in the promotion tree with a '?' sign as an icon and hide the name and data with more interrogation signs, as well as preventing the player from promoting to that job.

  • "job_id" -- a unique identifier.

  • "display_order" -- an ordinal to make our job icon appear before/after other jobs in the jobs index. Since mods can add more jobs without knowing about other jobs, it's normal that there are repeated ordinals, in that case the order might not be as expected.

  • "alias" -- the alias of our job preceded by the namespace plus a colon.

  • "display_name" -- a localized display name for the job, to use in different parts of the UI.

  • "controller" -- the URI of this job's controller preceded by the namespace plus a colon. Remember that it has to be defined inside the "controllers" section of your manifest, not under "aliases".

  • "description" -- a localized description for our job, will be shown in the bottom left of the promotion tree when we select our job.

  • "requirements" -- a localized text that describes the requirements for this job, to show at the bottom of the promotion tree when we select it.

    The icon and name of the required talisman, and the required parent job/level will be shown automatically. But we can add a custom description here, for example saying which job can craft this talisman, how to get it, or some flavor text (take a look at the requirements for the worker job).

  • "abilities" -- a path to the job abilities JSON file (it defines an equipment piece where we can inject AI / etc to it, so that the hearthling knows how to perform their new job).

  • "equipment" -- contains keys referencing equipment slots pointing to equipment pieces. Normally we apply the job outfit in the "torso" slot, and the tool/weapon in the "mainhand" slot. We can also tell the game here to choose a random equipment piece for the hearthling. For example, take a look at worker_description.json.

  • "roles" -- a string of roles separated by spaces or an array of roles (which is better for mod compatibility). Normally one role will be for the job name plus a _job suffix, and depending on the job type, we'll add more roles like "crafter", "combat", "mail_wearer", etc. That way they'll be able to equip gear that has those roles in their equipment_piece component.

  • "talisman_uri" -- the alias of our talisman entity prefixed by the namespace of our mod plus a colon.

  • "default_stance" -- can be "passive" (used for most of the jobs), "aggressive" (used for combat jobs) or "defensive" (used for some NPC jobs, which will only attack if they're attacked).

  • "icon" -- path to the image for the icon that will appear in the different parts of the UI for our job. The icons in the promotion tree don't need the square background, that's added automatically.

  • "promotion_activity_name" -- an identifier not in use nowadays. It was intended for the town journal.

  • "task_groups": [] -- an array of aliases of task groups that will get applied to the hearthling when they're promoted to this job, and removed when we promote them to another job or demote them.

    Depending on the job type, we might want to include / not include certain task groups. For example if we don't include "stonehearth:task_groups:build", the hearthling won't be able to build after promoting them, and their checkbox under the Build column in the citizens panel will get disabled. Some jobs will not include the task group that allows for harvesting resources, or moving items, so that the hearthling is more focused on their job.

    There's no list of task groups but their aliases are grouped in the stonehearth manifest. You'll usually want the same ones that the job you copied had. More on AI in advanced sections of the guide.

  • "parent_job" -- URI of the parent job.

  • "max_level" -- if this job can level up (all jobs can except Worker), establish the maximum level that a hearthling can reach in it.

  • "xp_rewards": {} -- the contents of this field may vary depending on the type of job, they serve to define how much exp will the hearthling get by doing different tasks on their job.

  • "level_data": {} -- the list of job perks for this job. See below.

About job perks

The way job perks from the job_description.json file work is:

  • They all consist of an array of JSON objects.

  • The minimum fields are "name", "id", "icon", "description" and "level". The "level" is used to unlock them. The "id" is an identifier used to perform checks in Lua when we need to know whether a hearthling has unlocked a perk or not.

  • If the perk consists in something that is automatically controlled by other files (such as unlocking recipes - each recipe defines at which level it will get unlocked) no extra fields are needed.

  • If the perk involves something more elaborate, it will have a "type" field plus any additional custom fields for data.

    This "type" is the name of a function defined in the job's controller file. Sometimes it will be defined in stonehearth/jobs/base_job.lua instead, since all jobs inherit from it. The extra fields can be used for data to pass to that custom function.

    This way we can have perks that add permanent buffs to the hearthling, or equip invisible equipment that adds custom AI actions so that they can do more fancy things (like a new combat skill), or increase the maximum number of turrets / golems that the town can have, for example.

  • Together with perks that use a custom function, you'll also see a "demote_fn" field. This designates what should be run if the hearthling has that perk and is demoted. It's a way of ensuring that permanent buffs and custom abilities are removed when the hearthling is promoted/demoted to some other job. If you don't add it, the hearthling will keep those abilities.

icon You'll also see that many jobs at level 6 will get a title instead of / in addition to perks, which allows us to know that they're at the maximum level in the UI. This title is outside of the "perks" array. Nowadays it's not needed, all hearthlings will get a title that will be shown in the character sheet and will change as they level up (Apprentice, Journeyman, Master).

On top of the perks, hearthlings will gain +10 health points every time they level up. These will carry on after promoting / demoting. Job experience is reset when you promote / demote a hearthling, to encourage specialization.

About job progression

In the job component inside the base_human.json mixin we can find info that is common for all jobs:

  "stonehearth:job": {
     "starting_level_title": "i18n(stonehearth:stonehearth_mixins.base_human.job.starting_level_title)",
     "mid_level_title": "i18n(stonehearth:stonehearth_mixins.base_human.job.mid_level_title)",
     "max_level_title": "i18n(stonehearth:stonehearth_mixins.base_human.job.max_level_title)",
     "default_level_title": "i18n(stonehearth:stonehearth_mixins.base_human.job.default_level_title)",
     "default_level_announcement": "i18n(stonehearth:stonehearth_mixins.base_human.job.default_level_announcement)",
     "default_promote_announcement": "i18n(stonehearth:stonehearth_mixins.base_human.job.default_promote_announcement)",
     "xp_equation_for_next_level": "curr_level * 115 + 135",
     "levels_between_attribute_increase": 10,
     "level_up_data": {
        "level_up_perk_description": "i18n(stonehearth:stonehearth_mixins.base_human.job.level_up_perk_description)",
        "level_up_perk_icon": "/stonehearth/data/images/race/human_HP_on_level.png"
     }
  }

We can customize these fields via mixintos. For example, we have the titles based on level (Apprentice, Journeyman, Master), and the equation that is used to calculate the amount of experience required to get to the next level. Mind that not all of these fields are in use nowadays.

Creating other types of jobs

You can create other types of jobs as long as they inherit from base_job.lua. For example, in the game we have the producers (farmer, trapper and shepherd) whose job is based on using zones.

But you don't need to use zones if you don't want to. Just with custom AI should be enough. For instance, in the vanilla game, some crafters have other responsibilities (the herbalist heals, the cook farms, the engineer repairs turrets), they're not exclusively dedicated to crafting. And workers aren't a combat nor a crafting job.