Fandom and Gamepedia have joined forces. Can this wiki reunite with Northgard Wiki on Fandom? Please make your opinion known in the Community portal.

Northgard Editor

From Northgard Wiki
Jump to: navigation, search

Northgard Editor - IDE for writing and modifying scripts in Haxe programming language. Used to create Modifications (mods). In combination with Map Editor and CDB allows high-level of mod-ability for Northgard. To use scripts, mod has to have Map first.

Default Script[edit | edit source]

Save following code as script.hx text file and put it together with your map file (map.dat)

// --- Named constants, settings and flags ---
var CONSTS = {
    DEBUG: false
}

// --- State to set onFirstLaunch, keep and serialize with Save  ---
DATA = {
}

// // --- BEGIN of BOILERPLATE ---
function saveState() {
    state.scriptProps = {
        DATA: DATA
    }
}

function init() {
    setPause(true);

    if (state.time == 0)
    {
        onFirstLaunch();
    }
    onEachLaunch();

    setPause(false);
}

function onFirstLaunch() {
    setVictory();
    // setObjectives(); // if you want to use Objectives API do it here, on first launch only
}

function onEachLaunch() {

}

// Regular update is called every 0.5s
function regularUpdate(dt : Float) {

}

function setVictory() {
    // Setup Dom only victory
    state.removeVictory(VictoryKind.VMoney);
    state.removeVictory(VictoryKind.VFame);
    state.removeVictory(VictoryKind.VLore);
}
// // --- END of BOILERPLATE ---

FAQ[edit | edit source]

To set the victory condition to military only.[edit | edit source]

You can only do this through scripting, you can not use the Map Editor. Place this in the init() call to function:

function setVictory() {
    // Setup Dom only victory
    state.removeVictory(VictoryKind.VMoney);
    state.removeVictory(VictoryKind.VFame);
    state.removeVictory(VictoryKind.VLore);
}

Can I choose my clan at start?[edit | edit source]

No, there is no functionality for that at this time. It will be added in the future.

Can I set teams in the Map Editor?[edit | edit source]

Yeas, since September 2020 you can set teams in MapEditor.

But, for something more complex (i.e, making actual use of that teams) you can use scripts, see: http://northgard.net/doc/api/current/ScriptApi.html#setAlly

var p1 = getZone(160).owner;
var p2 = getZone(176).owner;
var p3 = getZone(189).owner;
p1.discoverZone(getZone(176));
p2.discoverZone(getZone(160));
p2.discoverZone(getZone(189));
p1.discoverZone(getZone(189));
setAlly(p1, p2);
setAlly(p3, p2);

In the above example, p2 is the leader of the team. The game will not auto-reveal home tiles of teammates, so you will need to do that yourself by calling discoverZone for each player to the other.

Help, setting teams won't work![edit | edit source]

Make sure you have the correct zone numbers, and only one person is the leader for the whole team.

Can I create my own units, heroes, characters, buildings?[edit | edit source]

No, there is currently no functionality to do anything like that. You can however change some values of existing buildings and units within the database.

Can I have more than one of the same building on a tile?[edit | edit source]

No, though the devs have seen this request and may in the future deliver. Until then, you can manually place multiple of the same building into a tile, but the players would be unable to build multiple during the game.

Can the number of buildings on a tile be increased?[edit | edit source]

Yes, to do so, simply set the value on the zone like this: getZone(12).maxBuildings = 20; This will increase the number of allowed buildings on zone 12 to 20.

My game crashes, and I think it is because my loops are too big, what to do?[edit | edit source]

Your script has a very small amount of time to execute code before the game crashes. If you must process large numbers of things (all the zones on the map, 100+ units, etc), then you will want to chunk that scanning across multiple calls to regularUpdate(). For example, maybe only scan the even zones when state.time is even, or maybe only 30 zones at a time. Alternatively, store Zones of interest that you need to watch or don't scan Zones you don't care for, therefore reducing the number of lookups.

Reduce the number of times you do things, or alternate what you process on various modulo of state.time to spread out the computation. Since regularUpdate is called every 500ms, a small delay of a few updates won't be noticeable to the player.

Can I change the commercial influence needed to win? What about other default victory conditions?[edit | edit source]

The amount of Fame required to win can be changed, but none of the other default victories can have their conditions changed. Instead, you should disable them and implement your own, or manipulate the resources of a player so they are earned slower (for example, take away Fame to make Fame requirement higher, or commercial influence, etc). See the FAQ "How do I make my own victory conditions" for more info.


If you just want to raise the Fame required for a Fame victory, then look at the rule "AltFameVictory".

How do I make my own victory conditions?[edit | edit source]

You will need to use ScriptObjectives, seen here: http://northgard.net/doc/api/current/ScriptObjectives.html

For example, see below. Note, that all objectives must be created within the init() function call otherwise they will not work. If you want an objective to be seen only later, then you can set visibility to false and then change it to true when desired.

Then, in regular update, you will need to update your objectives to show the player their progress. And once the player has achieve the goal you set, you can call the following to make them win: http://northgard.net/doc/api/current/Player.html#customVictory

An example is shown below of setting up a new objective.

function onFirstLaunch() {
    state.objectives.add("farmObjectiveId", "Capture the farms", {showProgressBar:true, visible:true});
    state.objectives.setGoalVal("farmObjectiveId", 3);
}

Then, to update the value when the player captures a farm.

state.objectives.setCurrentVal("farmObjectiveId", 1);

Then, when the player has achieved the objective you set, give them the win.

customVictory("You have captured the farms, congrats!", "You failed to capture the farms, the rest of you lose!");

The first string is what the winning player and their team sees. The second string is what the losing players all see.

Note, not shown, is how to know when a farm is captured. That is very specific for a map, and your objective may differ, so it is omitted. You will need quite a bit of coding to track what players are doing on a map and mark the objective correctly.

I can not check if player has Lighthouse[edit | edit source]

At this moment (July 2020) check for Lighthouse or RavenLighthouse is not working. You should use "Upgraded Port" instead.

   function hasLighthouse(p : Player) {
       return p.hasBuilding(Building.Port, false, false, true, null) || p.hasBuilding(Building.RavenPort, false, false, true, null);
   }

Why can't I Add Happiness or Warband?[edit | edit source]

While Happiness and Warband are both resources, it seems they are unaffected by addResource and setResource. It isn't known why this is the case, but it could be because both resources are a "sum of their parts", meaning happiness is the sum of things that produce happiness and take it away. Warband is the sum of buildings that provide it, and units that take it away.

How do I get the current or future months? How do I convert a year/month to seconds?[edit | edit source]

This will convert a calendar date into seconds, which is useful for comparing against state.time

   function calToSeconds(month:Int, year:Int) {
       // 60 seconds per month, and 12 months in a year
       return month * 60 + year * 60 * 12;
   }

This will, given a time, convert to the month in the year it will fall under, where 0 is March, and 11 is February.

   function convertTimeToMonth(time:Float) : Int {
       return toInt(time % 720 / 60);
   }

I want something to happen only once after a certain time, how do I do that?[edit | edit source]

You can't just do state.time == DO_THE_THING, because state.time is a Float. This means it will return something like 5.0033505783. You'll instead need to do something like

   if(state.time > DO_THE_THING && !hasDoneTheThingYet) {
       hasDoneTheThingYet = true;
       // Do the thing
   }

Where hasDoneTheThingYet is a boolean you define globally. This will ensure you only run the code inside the if-statement once.

Note, that if you do something like toInt(state.time), while you can certainly use == now, don't forget that regularUpdate is called every 0.5 seconds, so you might do something twice!

I want to scan the player units and find PREDICATE[edit | edit source]

Make this function and use in your code (replace PREDICATE/PREDICATENAME after Copy-Pasting):

   function findUnitsInZonePREDICATENAME(z : Zone) : Array<Unit> {
       var result = [];
       for( unit in z.units )
       {
           if( PREDICATE )
           {
               result.push(unit);
           }
           wait(0);
       }
       return result;
   }
   function findUnitsOfPlayerPREDICATENAME(p : Player) : Array<Unit> {
       var result = [];
       for( unit in p.units )
       {
           if( PREDICATE )
           {
               result.push(unit);
           }
           wait(0);
       }
       return result;
   }

Frequently-used predicates: ownership (unit.owner == p), (unit.owner != p); (non-)military check (unit.isMilitary),(!unit.isMilitary)

How to save time and pre-load tiles that are used often throughout the script?[edit | edit source]

Put this above all code:

   var bases = [getZone(1), getZone(11), getZone(111), getZone(191)];

Then, when you need to scan through them use generic foreach loop:

   for( zone in bases ) {
       // CODE
   }

Where I can see my Workshop-subscribed mods?[edit | edit source]

SteamLibrary\steamapps\workshop\content\466560\

Which API features are only for single-player?[edit | edit source]

Confirmed single-player only

  • talk() - confirmed by devs that it should only be used for single-player. Unknown effect if used in multiplayer
  • Buttons - confirmed by devs that it should only be used for single-player. Unknown effect if used in multiplayer


Experimentation showed limited or broken behavior

  • me() - works correctly in a single-player game, however in multiplayer games it will only return the host player. This will only evaluate as true for the host player: if(me().clan != null)
  • Objectives - they work as expected in a single-player game. In multiplayer games it will work for the host, and will work for the next player to become host if the host leaves. However all other players in the match will see no progress towards the objective, or will see their player duplicated twice in the objectives list.
  • debug() - in multiplayer, it will only work for whomever is the host player. All other players will never see debug.

Known Issues[edit | edit source]

  • Entity named player is known to editor, so DO NOT EVER USE IT IN for loops or input arguments. Otherwise, interpreter and IDE will not highlight any errors related to usage of this entity.
    • I have used it without any ill-effect. There may be more to this, but it consistently caused some people issues, and others not
  • Cannot set units controllable
    • Even mercenaries, used as the example in Documentation
  • state.players.length will always return an incorrect value. It is recommended to copy out the array into your own, or write a wrapper to return length
    • Now you can use state.startPlayers
  • Cannot manipulate values of launched events
  • BSilo and BSiloImproved bonuses don't work
  • drakkar() has the ocean and coastal zones reversed in documentation.
  • Using Objectives API in your script causes all your units and buildings to disappear on loading a save of that map
    • Fixed if you put all state.objectives.add into onFirstLaunch
  • Cannot manipulate Warband. (Whether adjust Training Camp in .diff, or changing Warband as a Resource in script)
  • Can't create Dictionaries/Maps data structures. Only arrays, anon structs, and primitives.
  • Exceptions or failures in the script fail silently and stop execution of the script with no warning.
  • Can't loop over all zones without exceeding the time budget and causing a crash
  • Can't check for the Lighthouse in a tile, must check for an upgraded port
  • No error reporting when game crashes to desktop
  • me() only works for the host of a multiplayer game, though will work single-player
    • Should work by now
  • Player defined callbacks will not work, and while functions can be passed as parameters, the script silently crashes when you try to call it
  • Objectives do not appear to work in multiplayer matches at all. Only for the host
  • If a player chooses random for a clan during setup in a multiplayer game, and other players choose a clan, the player choosing random can steal the chosen clan of another player
  • If a player joins a game with a corrupted mod, the game will allow the player to pick any clan, but will crash to desktop after loading completes
  • Many of the new functions show as an error in the Editor but still work in game, such as me(), talk(), and more
  • If the CDB isn't displayed correctly you have to make sure, that the following project is opened: "C:/programs/steamapps/common/Northgard/NGEditor". Don't open your custom map in here or it will bug! however you have to load the CDB.diff tile from your correct custom map under: ".../steamapps/common/Northgard/mods/your map".

Community Wishlist[edit | edit source]

  • Debug, Output and/or Error log streams (and consequently files) in mod directory. Un-uploadable, please.
  • Want to manipulate the tile geometry (change amount of tiles that surround a tile)
  • Toggle victory conditions within the Map Editor
  • Set Teams within the Map Editor
  • Custom art assets, units
  • Modify more of the values in the DB
  • Freely add/remove/set Happiness and Warband amount
  • When uploading a mod, it should only upload expected files, not everything in the directory
  • Get the username of a player
  • Get the color of a player (currently, you have to hardcode the known color of a player at a location to know what color they are in scripts)
  • More explanation of the player.setAILevel() function. In the map editor it has values -2 to 5 in half-increments, but the API takes only integers and does not explain the domain of inputs. Current recommendation is to pass in 5