Official Module System Documentation: Part 02
As mentioned in the previous chapter, you work with the Module system as follows:
- Edit one or more of the module files (those starting with module_ and ending with .py) and make any changes you like. (Usually you need to right click and select 'Edit with Idle' to do that)
- After that, double click on the file build_module.bat. This action will attempt to build your module (and report errors if there are any)
- If there are no errors, you may launch Mount&Blade and test the changes you have made. Sometimes you may need to start a new game for the changes to take effect.
Contents |
Editing the Module Files
The module system uses Python lists to represent collections of game objects. If you open and view any of the module files you'll see that it contains such a list. An excerpt from module_map_icons contains:
map_icons = [ ("player",0,"player", avatar_scale, snd_footstep_grass, 0.15, 0.173, 0), ("player_horseman",0,"player_horseman", avatar_scale, snd_gallop, 0.15, 0.173, 0), ("gray_knight",0,"knight_a", avatar_scale, snd_gallop, 0.15, 0.173, 0), ("vaegir_knight",0,"knight_b", avatar_scale, snd_gallop, 0.15, 0.173, 0), ("flagbearer_a",0,"flagbearer_a", avatar_scale, snd_gallop, 0.15, 0.173, 0), ("flagbearer_b",0,"flagbearer_b", avatar_scale, snd_gallop, 0.15, 0.173, 0), ("peasant",0,"peasant_a", avatar_scale,snd_footstep_grass, 0.15, 0.173, 0), ("khergit",0,"khergit_horseman", avatar_scale,snd_gallop, 0.15, 0.173, 0), ("khergit_horseman_b",0,"khergit_horseman_b", avatar_scale,snd_gallop, 0.15, 0.173, 0), ("axeman",0,"bandit_a", avatar_scale,snd_footstep_grass, 0.15, 0.173, 0), ("woman",0,"woman_a", avatar_scale,snd_footstep_grass, 0.15, 0.173, 0), ("woman_b",0,"woman_b", avatar_scale,snd_footstep_grass, 0.15, 0.173, 0), ("town",mcn_no_shadow,"map_town_a", 0.35,0), ... ("bandit_lair",mcn_no_shadow,"map_bandit_lair", 0.45, 0), ]
Here map_icons is declared as a Python list and every element in the list is the declaration for a specific map icon object. In this example, ("player",0,"player", avatar_scale, snd_footstep_grass, 0.15, 0.173, 0) is such an object. We call such objects tuples. For map icons, each tuple object contains:
- Icon id
- "player"
- Icon flags
- 0
- Mesh name
- "player"
- Mesh scale
- avatar_scale = 0.15 (defined before the list)
- sound id
- snd_footstep_grass
- flag offset x
- 0.15
- flag offset y
- 0.173
- flag offset z
- 0
Note that elements of any tuple default to 0, so for "town", there is no offset defined and so the banner (set via party_set_banner_icon) will be centered. You can work out the structure of game objects for each module system file in this way, by reading the documentation at the beginning and matching that with the contents of the list.
Adding New Game Objects
Knowing the structure of the map icon tuples, we can now begin to add our own map icons. Let us take another look at the list.
map_icons = [ ("player",0,"player", avatar_scale, snd_footstep_grass, 0.15, 0.173, 0), ... ("town",mcn_no_shadow,"map_town_a", 0.35,0), ... ("bandit_lair",mcn_no_shadow,"map_bandit_lair", 0.45, 0), ]
New game objects in any module file must be added inside the list. You can see that the list for module_map_icons starts at the tuple for "player", continues after "town", and ends with "bandit_lair". The easiest way to add a new object is to copy and paste a pre-existing object and then editing its contents. For example:
("town",mcn_no_shadow,"map_town_a", 0.35,0), ("new_icon",mcn_no_shadow,"map_town_a", 0.35,0),
In this example, we have copied "town" and have given it a new icon name; "new_icon". This new icon has a flag on it. Flags are settings that can be turned on and off by including or removing them in the appropriate field. For example, the flag mcn_no_shadow on our new icon will set this icon to cast no shadow on the ground.
We will now remove mcn_no_shadow from our new icon. To do this, we replace mcn_no_shadow with 0, telling the module system there are no flags for this icon.
("new_icon",0,"map_town_a", 0.35,0),
Both "town" and "new_icon" use the Mesh "map_town_a", which means they will both use the 3d model named "map_town_a" in map_icons_b.brf. Because both icons use the same Mesh, if we were to put "new_icon" into the game at this point, it would look exactly the same as "town". By changing this field (to "City" for example), the icon can be set to use any 3d model from the resource files (map_icon_meshes.brf in our example).
Now let us give "new_icon" a bit of a different look.
("new_icon",0,"City", 3.5,0),
In this example, we have changed the mesh reference to "City" and the icon's scale from 0.35 to 3.5. This means the icon will be displayed 3.5 times as large as its model size, or ten times as large as it would have been. That should help us tell it apart from "town" when we put it into the game.
Next we will create a new party in module_parties that uses our new icon. To do this, we will need to reference the icon.
Referencing Game Objects
Open module_parties in your module system folder. You will see another Python list, parties = [.
As you can see, the structure of tuples in module_parties is slightly different from module_map_icons. This holds true for many -- if not all -- of the module files. We'll take this opportunity to closely examine the parties structure.
An example of a party:
("main_party","Main Party",icon_player|pf_limit_members, no_menu, pt_none,fac_player_faction,0,ai_bhvr_hold,0,(17, 52.5),[(trp_player,1,0)]),
This tuple is our player character's party. Its various qualities are set in the appropriate fields -- quite similar to the fields we've seen previously. These initial attributes are often overriden by party operations.
Breakdown of the tuple fields (See header_parties for definitions of party flags, personality, and AI-behaviour):
- Party-id
- "main_party"
- This is used for referencing the party in other files.
- Party name
- "Main Party"
- This is the party's name as it will appear in-game. Can be as distinct from the party-id as you like.
- Party flags
- icon_player|pf_limit_members
- Mostly affects the appearance of the party on the map. The flag should also include the icon that this party will use.
- Menu
- no_menu (0)
- This field is deprecated, which means that it's outdated and no longer used. As of M&B version 0.730, this field has no effect whatsoever in the game.
- Party-template
- pt_none
- ID of the party template this party belongs to. Use pt_none (0) as the default value.
- Party faction
- fac_player_faction
- ID of any faction from module_factions. For most centers, this is overriden by scripts
- Party personality
- 0
- This is usually defined for individual party_templates instead.
- AI-behaviour
- ai_bhvr_hold
- How the AI party will act on the overland map.
- AI-target party
- 0
- The AI-behaviour's target.
- Initial coordinates.
- (2,46)
- The party's starting coordinates on the overland map (X, Y).
- List of troop stacks.
- [(trp_player,1,0)]
- Each stack record is a triple that contains the following fields:
- Troop-id.
- trp_player
- This can be the ID of any regular or hero troop from module_troops.
- Number of troops in this stack
- 1
- This quantity does not vary. The number you input here is the number of troops the party will contain.
- Member flags (optional).
- 0
- Use pmf_is_prisoner to note that a party member is a prisoner.
By looking at the party flags, we can see that our party references the icon "player" from module_map_icons, by adding the prefix icon_ to it. This prefix is what points the system to the right module file. In order to reference map icons, we use icon_; in order to reference module_factions, we use fac_; in order to reference module_parties, we use p_; and so on. There is an appropriate prefix for every module file.
Now that we know how parties are structured, we can begin adding our own. But before you do so, take note: In the case of module_parties.py and certain other module files, you should not add your new towns at the bottom of the list. There will be comments in these files warning you about doing this, as it can break operations in the native code. In module_parties, it is recommended that you add any new towns between "town_22" and "castle_1".
Now, copy the entry "town_14" and paste it after "town_22".
("town_22","Bariyye", icon_town_desert|pf_town, no_menu, pt_none, fac_neutral,0,ai_bhvr_hold,0,(165, -106.7),[], 225), ("new_town","Mod Town",icon_town_steppe|pf_town, no_menu, pt_none, fac_neutral,0,ai_bhvr_hold,0,(55.5, -45),[], 135), ("castle_1","Culmarr_Castle",icon_castle_a|pf_castle, no_menu, pt_none, fac_neutral,0,ai_bhvr_hold,0,(-101.3, -21),[],50),
In this example, we have changed the new party's identifier from "town_14" to "'new_town", and the party name from "Halmar" to "Mod_Town".
We can now establish several things from looking at the tuple.
- To reference this party from another file, we must use the identifier "new_town" with the prefix "p_", resulting in "p_new_town".
- In the game, we will only see the name "Mod Town" to describe this party, never the identifier.
- This party uses icon_town_steppe and the flag pf_town -- a flag that assigns common town settings. The flags field will be where our next few changes take place.
- If we were to put our new town into the game at this point, it would appear at exactly the same map coordinates as Halmar. This, too, we will change next.
("new_town","Mod_Town", icon_new_icon|pf_town, "town", pt_none, fac_neutral,0,ai_bhvr_hold,0,(-1,-1),[(trp_vaegir_knight,6,0)]),
Here we have changed our new town's icon to icon_new_icon and its map coordinates to (-1,-1).
The town is now set up to use our new icon and has its own unique map coordinates, allowing it to show up without problems.
Save your progress, then click on build_module.bat. If everything went well, you should now be able to start up your mod and see the new town and icon near the centre of the map. Try it.
If everything did not go well, check carefully for spelling and syntax. Make very sure that all commas and brackets are in the right place. Bad syntax is the most common source of compiler errors in the official module system.
In-game, travelling to the town now will trigger a default town menu (most options will not work), because the town is currently within the range of towns to be initialized in script_game_start. Initializing a new town is a slightly complicated task, so we will leave our new town for the moment and return to it in a later chapter of this documentation.
As you can see, the interrelation of the various module files can be extensive. Every part must be covered for your module to work properly. Fortunately, most changes only require the editing of one or two files at most.
Now that you have a thorough grasp of the modding basics, we can take an in-depth look into the various module files. Please go to Chapter 3 of the documentation now.
List of module file prefixes
- anim_animation
- fac_module_factions:faction
- icon_map_icon
- ip_module_info_pages:info_page
- itm_item
- mesh_mesh
- mnu_game_menu
- mt_mission_template
- psys_particle_system
- p_party
- pfx_postfx
- prsnt_presentation
- pt_party_template
- qst_quest
- script_script
- scn_scene
- skl_skill
- snd_sound
- spr_scene_prop
- str_string
- tableau_mesh
- track_music
- trp_troop
module_dialogs is never directly referenced, so it has no prefix. The same applies for module_triggers and simple_triggers, as well as any module file which do not generate a corresponding id_x file.