Part 2: Editing Module System Files

Currently viewing this thread:

Status
Not open for further replies.

armagan

Developer
As mentioned in the previous chapter, you work with the Module system as follows:
1 ) 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)
2 ) 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)
3 ) 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.

2.1 -- Editing the Module Files

The module system uses Python lists to represent collections of game objects. ( A python lists starts with a '[', includes a list of objects seperated by commas, and ends with a  ']'  ) If you open and view any of the module files you'll see that it contains such a list. For example module_map_icons.py contains:

map_icons = [
  ("player",0,"player", 0.2, snd_footstep_grass),
  ("player_horseman",0,"player_horseman", 0.2, snd_gallop),
  ("gray_knight",0,"knight_a", 0.2, snd_gallop),
  ("vaegir_knight",0,"knight_b", 0.2, snd_gallop),
  ("peasant",0,"peasant_a", 0.2,snd_footstep_grass),
  ("khergit",0,"khergit_horseman", 0.2,snd_gallop),
  ("axeman",0,"bandit_a", 0.2,snd_footstep_grass),
  ("woman",0,"woman_a", 0.2,snd_footstep_grass),
  ("town",mcn_no_shadow,"City", 0.9,snd_footstep_grass),
]

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", 0.2, snd_footstep_grass) is such an object. We call such objects tuples. Tuples, like lists, contain elements seperated by commas (but they start and end with parentheses). The structure of each tuple object is documented at the beginning of the module file. For map icons, each tuple object contains:

1 ) name of the icon,
2 ) icon flags,
3 ) Mesh name,
4 ) Mesh scale,
5 ) sound id.

So, for the first tuple ("player",0,"player", 0.2, snd_footstep_grass)
1 ) name of the icon = "player"
2 ) icon flags = 0
3 ) Mesh name = "player"
4 ) Mesh scale = 0.2
5 ) sound id = snd_footstep_grass

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.


2.2 -- 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", 0.2, snd_footstep_grass),
  ("player_horseman",0,"player_horseman", 0.2, snd_gallop),
  ("gray_knight",0,"knight_a", 0.2, snd_gallop),
  ("vaegir_knight",0,"knight_b", 0.2, snd_gallop),
  ("peasant",0,"peasant_a", 0.2,snd_footstep_grass),
  ("khergit",0,"khergit_horseman", 0.2,snd_gallop),
  ("axeman",0,"bandit_a", 0.2,snd_footstep_grass),
  ("woman",0,"woman_a", 0.2,snd_footstep_grass),
  ("town",mcn_no_shadow,"City", 0.9,snd_footstep_grass),
]


New game objects in any module file must be added inside the list. You can see, the list for module_map_icons ends just below ("town",mcn_no_shadow,"City", 0.9,snd_footstep_grass). In order to make room for our new game object, we have to move the bracket down by one line.

Having done that, we can now begin to add a new object. The easiest way to do this is to copy and paste a pre-existing object and then editing its contents. For example:


map_icons = [
  ("player",0,"player", 0.2, snd_footstep_grass),
  ("player_horseman",0,"player_horseman", 0.2, snd_gallop),
  ("gray_knight",0,"knight_a", 0.2, snd_gallop),
  ("vaegir_knight",0,"knight_b", 0.2, snd_gallop),
  ("peasant",0,"peasant_a", 0.2,snd_footstep_grass),
  ("khergit",0,"khergit_horseman", 0.2,snd_gallop),
  ("axeman",0,"bandit_a", 0.2,snd_footstep_grass),
  ("woman",0,"woman_a", 0.2,snd_footstep_grass),
  ("town",mcn_no_shadow,"City", 0.9,snd_footstep_grass),
  ("new_icon",mcn_no_shadow,"City", 0.9,snd_footstep_grass),
]



In this example, we have copied ("town",mcn_no_shadow,"City", 0.9,snd_footstep_grass) and 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.


map_icons = [
  ("player",0,"player", 0.2, snd_footstep_grass),
  ("player_horseman",0,"player_horseman", 0.2, snd_gallop),
  ("gray_knight",0,"knight_a", 0.2, snd_gallop),
  ("vaegir_knight",0,"knight_b", 0.2, snd_gallop),
  ("peasant",0,"peasant_a", 0.2,snd_footstep_grass),
  ("khergit",0,"khergit_horseman", 0.2,snd_gallop),
  ("axeman",0,"bandit_a", 0.2,snd_footstep_grass),
  ("woman",0,"woman_a", 0.2,snd_footstep_grass),
  ("town",mcn_no_shadow,"City", 0.9,snd_footstep_grass),
  ("new_icon",0,"City", 0.9,snd_footstep_grass),
]



Both "town" and "new_icon" use the Mesh "City", which means they will both use the 3d model named "City" in the game's resource files. By changing this field, the icon can be set to use any 3d model from the resource files.

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".

Now let us give "new_icon" a bit of a different look.

map_icons = [
  ("player",0,"player", 0.2, snd_footstep_grass),
  ("player_horseman",0,"player_horseman", 0.2, snd_gallop),
  ("gray_knight",0,"knight_a", 0.2, snd_gallop),
  ("vaegir_knight",0,"knight_b", 0.2, snd_gallop),
  ("peasant",0,"peasant_a", 0.2,snd_footstep_grass),
  ("khergit",0,"khergit_horseman", 0.2,snd_gallop),
  ("axeman",0,"bandit_a", 0.2,snd_footstep_grass),
  ("woman",0,"woman_a", 0.2,snd_footstep_grass),
  ("town",mcn_no_shadow,"City", 0.9,snd_footstep_grass),
  ("new_icon",0,"City", 5.0,snd_footstep_grass),
]



In this example, we have changed the icon's scale from 0.9 to 5.0. This means the icon will be displayed five times as large as normal. 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.py that uses our new icon. To do this, we will need to reference the icon from module_parties.py.


2.3 -- Referencing Game Objects

Open module_parties.py in your module system folder. You will see another Python list, parties = [.

As you can see, the structure of tuples in module_parties.py is slightly different from module_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:

("zendar","Zendar",icon_town|pf_is_static|pf_always_visible|pf_hide_defenders, "zendar", pt_none, fac_neutral,0,ai_bhvr_hold,0,(2,46),[(trp_swadian_knight,6,0)]),

This tuple places the town of Zendar on the map. Zendar's various qualities are set in the appropriate fields -- quite similar to the fields we've seen in module_icons.py.

Breakdown of the tuple fields:

1 ) Party-id. This is used for referencing the party in other files.
2 ) Party name. This is the party's name as it will appear in-game. Can be as different from the party-id as you like.
3 ) Party flags. The first flag of every party object must be the icon that this party will use.
4 ) Menu. 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.
5 ) Party-template. ID of the party template this party belongs to. Use pt_none as the default value.
6 ) Party faction. This can be any entry from module_factions.py.
7 ) Party personality. See header_parties.py for an explanation of personality flags.
8 ) AI-behaviour. How the AI party will act on the overland map.
9 ) AI-target party. The AI-behaviour's target.
10 ) Initial coordinates. The party's starting coordinates on the overland map; X, Y.
11 ) List of troop stacks. Each stack record is a triple that contains the following fields:
11.1 ) Troop-id. This can be any regular or hero troop from module_troops.py.
11.2 ) Number of troops in this stack; does not vary. The number you input here is the number of troops the town will have.
11.3 ) Member flags. Optional. Use pmf_is_prisoner to note that a party member is a prisoner.


Zendar tuple examination:

("zendar","Zendar",icon_town|pf_is_static|pf_always_visible|pf_hide_defenders, "zendar", pt_none, fac_neutral,0,ai_bhvr_hold,0,(2,46),[(trp_swadian_knight,6,0)]),

1 ) Party-id = "zendar"
2 ) Party name = "Zendar"
3 ) Party flags = icon_town|pf_is_static|pf_always_visible|pf_hide_defenders
4 ) Menu = "zendar"
5 ) Party-template = pt_none
6 ) Party faction = fac_neutral
7 ) Party personality = 0
8 ) AI-behaviour = ai_bhvr_hold
9 ) AI-target party = 0
10 ) Initial coordinates = (2,46)
11 ) List of troop stacks:
11.1 ) Troop-id = trp_swadian_knight
11.2 ) Number of troops in this stack = 6
11.3 ) Member flags = 0

By looking at field 3, we can see that Zendar references the icon "town" from module_icons.py, by adding the prefix icon_ to it. This prefix is what points the system to the right module file. In order to reference module_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 -- you will find them all listed at the bottom of this segment.


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.py, it is recommended that you add any new entries between "training_ground" and "castle_1".

Now, copy the entry "town_14" and paste it in between "training_ground" and "castle_1".

  ("training_ground","Training Ground",  icon_town|pf_town|pf_disabled, "training_ground", pt_none, fac_vaegirs,0,ai_bhvr_hold,0,(-2,-3),[(trp_vaegir_knight,6,0)]),

  ("new_town","Mod_Town",  icon_town|pf_town, "town", pt_none, fac_vaegirs,0,ai_bhvr_hold,0,(-4,-37),[(trp_vaegir_knight,6,0)]),

  ("castle_1","Culmarr_Castle",icon_town|pf_is_static|pf_always_visible, "castle", pt_none, fac_outlaws,0,ai_bhvr_hold,0,(-47,-51),[(trp_swadian_knight,5,0),(trp_swadian_crossbowman,25,0)]),


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.

1 ) To reference this party from another file, we must use the identifier "new_town" with the prefix "p_", resulting in "p_new_town".
2 ) In the game, we will only see the name "Mod Town" to describe this party, never the identifier.
3 ) This party uses icon_town and the flag pf_town -- a flag that assigns common town settings. The flags field will be where our next few changes take place.
4 ) "Mod Town" is currently of the Vaegir faction.
5 ) 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.


  ("training_ground","Training Ground",  icon_town|pf_town|pf_disabled, "training_ground", pt_none, fac_vaegirs,0,ai_bhvr_hold,0,(-2,-3),[(trp_vaegir_knight,6,0)]),

  ("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)]),

  ("castle_1","Culmarr_Castle",icon_town|pf_is_static|pf_always_visible, "castle", pt_none, fac_outlaws,0,ai_bhvr_hold,0,(-47,-51),[(trp_swadian_knight,5,0),(trp_swadian_crossbowman,25,0)]),


Here we have changed our new town's icon to icon_new_icon and its map coordinates to (-1,-1). In addition, we've switched the town's faction to fac_neutral.

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 combat encounter, because the town currently has no game menu assigned to it. Assigning a menu 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:

fac_ -- module_factions.py
icon_ -- module_map_icons.py
itm_ -- module_items.py
mnu_ -- module_game_menus.py
mno_ -- module_game_menus.py -- References an individual menu option in module_game_menus.
mt_ -- module_mission_templates.py
psys_ -- module_particle_systems.py
p_ -- module_parties.py
pt_ -- module_party_templates.py
qst_ -- module_quests.py
script_ -- module_scripts.py
scn_ -- module_scenes.py
spr_ -- module_scene_props.py
str_ -- module_strings.py
trp_ -- module_troops.py

module_dialogs.py is never directly referenced, so it has no prefix.
 
Status
Not open for further replies.
Top Bottom