B Tutorial Other ModMerger usage

Users who are viewing this thread

ru-board: http://rusmnb.ru/index.php?topic=8964.msg332618#msg332618

How to install modmerger-pack

1. Download, unzip the latest module system: Module System (TaleWorlds Download Server)
2. Download, install Python 2.7. Take better .msi, do not have to configure Path.
3. Copy the module Native, give up the name for your new module.
4. Configure a module system for direct compilation to the folder of your module. This will greatly simplify life with the transfer of individual files manually ..
module_info.py said:
export_dir = "D:/games/Mount&Blade Warband/Modules/YourMod/" # Direct compilation
5. Build module by build_module.bat. Run the game with YourMod. There should be no errors, and differences from the Native.
6. Download, install Modmerger 0.2.5.
7. Set up the module system version to avoid build errors:
Module_system 1.158/modmerger_options.py said:
# check and fill in defaults for certain required variables
try:
module_sys_info["version"]
except KeyError:
# assume version to be latest version that modmerger was tested on
module_sys_info["version"] = 1158
8. Again, do a test compilation. Run the game. There should be no errors, and differences from the Native.

9. To add modmerg-pack first see readme inside (if any).
9.1 Unpack modmerger-pack. Copy brf-texture-sound-etc recourses to YourMod folder if any. Copy .py-source to module_system folder.
9.2 Add new mod to mods_active list..
Module_system 1.158/modmerger_options.py said:
mods_active = [
# insert the active mod names here
"NewMod", # description
# "anothermod",
]
9.3. Build module. Check add-on in-game.

Repeat 9-9.3 steps for each new modmerger-pack.

As a result, you will have a list of mods that are independent of the version of the modulе_system. Just refresh Native and reinstall ModMerger if needed.



The list of examples "how to" add a new code to the module system through ModMerger

Animations:


The new animation is added only to the "unused" places. For this uses a modmerge-functions that also allows you to replace the old by new anim. Example:
human_animations.py said:
... default head of module_animations.py ...

add_block = [ # This animation will replace an unused_

# Walking
["run_forward", acf_enforce_lowerbody, amf_use_cycle_period|amf_client_prediction,
[2.0, "man_walk", 0, 32, arf_use_walk_progress|arf_cyclic|blend_in_walk|arf_make_walk_sound,pack2f(0.4,0.9), (0, 0, 0), 0.4],],

...

# Stand Up
["stand_up", acf_enforce_lowerbody, amf_priority_jump|amf_play|amf_client_prediction,[1.0, "stand_man", 0, 1, arf_blend_in_10],],

]

from util_animations import *

def modmerge(var_set):
try:
var_name_1 = "animations"
orig_animations = var_set[var_name_1]
modmerge_animations(orig_animations)
except KeyError:
errstring = "Variable set does not contain expected variable: "%s"." % var_name_1
raise ValueError(errstring)

def modmerge_animations(orig_animations):
try:
add_animations(orig_animations, add_block, check_duplicates = True) # replace existing and add new
except:
import sys
print "Injecton 1 failed:", sys.exc_info()[1]
raise

Dialogs:

Dialogs is a specific list acts as a filter. At the end of the list is a black hole "Surrender or die", which is displayed if the previous dialogues have not been met. Therefore, the new dialogs should be added to the top of the list. Example:
harbour_dialogs.py said:
... default header from module_dialogs.py ...

dialogs_insert_block = [

# Harbour Master
# First conversation
[anyone, "start", [
(store_conversation_troop, "$g_talk_troop"),
(is_between, "$g_talk_troop", harbour_masters_begin, harbour_masters_end),
(troop_slot_eq, "$g_talk_troop", slot_troop_met_previously, 0),
],
"Good day {young man/lassie}. I'm the Captain of this port.
...
...

]

def modmerge(var_set):
try:
var_name_1 = "dialogs"
orig_dialogs = var_set[var_name_1]
modmerge_dialogs(orig_dialogs)
except KeyError:
errstring = "Variable set does not contain expected variable: "%s"." % var_name_1
raise ValueError(errstring)

def modmerge_dialogs(orig_dialogs):
try:
# Add dialogs to the top of the list
orig_dialogs.reverse() # Reverse list
orig_dialogs = dialogs_insert_block # add a new code
orig_dialogs.reverse() # revers the list back
except:
import sys
print "Injecton 1 failed:", sys.exc_info()[1]
raise

Constants:

Nothing to be scripted. Just write a new constants. Example:
ship_navigation_constants.py said:
# [OSP] Ship Navigation
# Constants that uses in game
# MB Warband v1.158+
# Сode v1.0: by Shcherbyna
# Additional code vX.X: by
# -*- coding: UTF-8 -*-

# Ship steering commands

SHIP_SAIL_ON = 1 # sail on (unmooring)
SHIP_SAIL_OFF = 2 # sail off
SHIP_AHEAD = 3 # move ahead
SHIP_ASTERN = 4 # move astern
SHIP_RIGHT = 5 # turn to the right
SHIP_LEFT = 6 # turn to the left

# Ship status
SHIP_STATUS_UNDERWAY = 1 # sail on
SHIP_STATUS_ANCHORAGE = 2 # anchored
SHIP_STATUS_MOORED = 3 # moored
SHIP_STATUS_BOARDING = 4 # boarding
SHIP_STATUS_NOT_UNDER_COMMAND = 5 # sail off

# Wind power
NAVIGATION_WIND_LOW = 1
NAVIGATION_WIND_MEDIUM = 2
NAVIGATION_WIND_STRONG = 3

Scenes:

Nothing to be scripted. Just write a new scene row. Example:
harbour_scenes.py said:
... default header from module_scenes.py ...

scenes = [
("town_harbour",sf_generate,"none", "none", (0,0),(240,240),-0.5,"0x000000033a000980400258960000750d80002cb9000053a6",
[],[], "outer_terrain_beach"),
]

Mission_templates:

Usually, in the absence of additional rules, ModMerger automatically connects the new data, whether scripts or menu. As it turned out, with mt this does not happen, so if you create a new scene and mt-script, the question is "how to add". Here is an example:
YourModName_mission_templates.py said:
... default header from module_mission_templates.py .......

mission_templates = [

... a new mt ...

]

from util_common import *
from util_wrappers import *

def add_mission_templates(orig_mission_templates, add_mission_templates, check_duplicates = True):
addmode = ADDMODE_REPLACE_EXIST
if not check_duplicates:
addmode = ADDMODE_APPEND

return add_objects(orig_mission_templates, add_mission_templates, addmode)


def modmerge(var_set):
try:
var_name_1 = "mission_templates"
orig_mission_templates = var_set[var_name_1]
add_mission_templates(orig_mission_templates, mission_templates, True) # add a new mission_tmplt / добавить новую миссию

except KeyError:
errstring = "Variable set does not contain expected variable: "%s"." % var_name_1
raise ValueError(errstring)
New triggers are combined into a list. Then this list is added to the end of the list of triggers specified mt. This example shows how to add new triggers to the Port mt, which enable the player to control the ship. Note also the first trigger ti_on_agent_spawn (native name and new code).. when adding it will not replace the native trigger with the same name, and will be added to the end of the list of triggers. Thus, in the mt-scenario will be 2 triggers with the same names ti_on_agent_spawn: the first is native, the second a new one that will be executed, provided that the player has arrived on the scene not on horse, but on a palanquin.
YourModName_mission_templates.py said:
... default header from module_mission_templates.py .......

# New triggers
on_palanquin_spawn = (

ti_on_agent_spawn, 0, 0,
[
(store_trigger_param_1, ":agent"),
(agent_get_item_id, ":horse_id", ":agent"),
(eq, ":horse_id", "itm_palanquin"),
],
[
#(store_trigger_param_1, ":agent"),

(display_message, "@Event on_palanquin_spawn"),

])

on_ahead_command = (
1, 2, 0, [(eq, "$g_sail_command", SHIP_AHEAD)],
[
(... code ...),
])

on_astern_command = (
1, 2, 0, [(eq, "$g_sail_command", SHIP_ASTERN)],
[
(... code ...),
])

on_mooring_command = (
1, 2, 0, [(eq, "$g_sail_command", SHIP_CREW_MOORING)],
[
(... code ...),
])

# Combine them in the right group
ship_triggers = [
on_ahead_command,
on_astern_command,
]

crew_triggers = [
on_mooring_command,
]

palanquin_triggers = [
on_palanquin_spawn,
]

from util_common import *
from util_wrappers import *

def modmerge_mission_templates(orig_mission_templates):
# 1) Example of adding new triggers to the specified mt
find_i = find_object(orig_mission_templates, "town_harbour") # find the specified mission
orig_mission_templates[find_i][5].extend(ship_triggers) # add new triggers

# 2) Example of adding trigger groups to several mt

for n in range(len(orig_mission_templates)):
mt_name = orig_mission_templates[n][0]
if (mt_name=="town_harbour" or mt_name=="open_sea"): # search for the exact name of the mt
orig_mission_templates[n][5].extend(ship_triggers + crew_triggers)
elif ("harbour" in mt_name or "sea" in mt_name): # search scenarios by incomplete name
orig_mission_templates[n][5].extend(ship_triggers)
# 3) Example of adding triggers to all mt-scenarios except those specified
elif (not "tutorial" in mt_name and not "multiplayer" in mt_name and not "conversation" in mt_name):
orig_mission_templates[n][5].extend(palanquin_triggers)

def modmerge(var_set):
try:
var_name_1 = "mission_templates"
orig_mission_templates = var_set[var_name_1]
modmerge_mission_templates(orig_mission_templates) # perform search-replace function

except KeyError:
errstring = "Variable set does not contain expected variable: "%s"." % var_name_1
raise ValueError(errstring)
In this example from native module_mission_templates.py is loaded a few common_triggers who injected by different number of lines of new code, with changes of insert positions :
pbod_mission_templates.py said:
... default header from mt.py .......

bc_tab_press_addon = [
#PBOD - Battle Continuation
(else_try),
(party_slot_eq, "p_main_party", slot_party_pref_bc_continue, 1), #PBOD Battle Continuation On
... code ...
(finish_mission, 0),
#PBOD - Battle Continuation END
]

from util_common import *
from util_wrappers import *

def modmerge_mission_templates(orig_mission_templates):
# Loading common-names for further work with them
from module_mission_templates import common_battle_tab_press, common_battle_check_victory_condition, common_battle_order_panel
try:
# 1) Inserting a new code from the block bc_tab_press_addon before 2 rows from the searched
codeblock = TriggerWrapper(common_battle_tab_press).GetConsequenceBlock()
pos = codeblock.FindLineMatching((call_script, "script_cf_check_enemies_nearby"))
codeblock.InsertBefore(pos-1, bc_tab_press_addon) # pos-1 to jump above the else try

# 2) Inserting a new row before the searched
codeblock = TriggerWrapper(common_battle_check_victory_condition).GetConditionBlock()
pos = codeblock.FindLineMatching((neg|main_hero_fallen, 0))
codeblock.InsertBefore(pos,[(this_or_next|party_slot_eq, "p_main_party", slot_party_pref_bc_continue, 1)])

# 3) Inserting a new row after the searched
codeblock = TriggerWrapper(common_battle_order_panel).GetConsequenceBlock()
pos = codeblock.FindLineMatching((game_key_clicked, gk_view_orders))
codeblock.InsertAfter(pos,[(neg|main_hero_fallen)])
except:
import sys
print "common_triggers injecton failed:", sys.exc_info()[1]
raise

def
modmerge(var_set):
try:
var_name_1 = "mission_templates"
orig_mission_templates = var_set[var_name_1]
modmerge_mission_templates(orig_mission_templates) # perform search-replace function

except KeyError:
errstring = "Variable set does not contain expected variable: "%s"." % var_name_1
raise ValueError(errstring)
This example shows the insertion, deletion, replacement parts of mt-triggers. Also note, instead of editing the mt-triggers you can add new instances of that in some cases it may be easier. Thus one mt-scenario may have, for example, several ti_on_agent_spawn: one is native, second is your, which, moreover, can only work under certain conditions.
YourModName_mission_templates.py said:
... default mt header .......

# A new inject code
inject_block = [
(is_presentation_active, "prsnt_command_line"),
(try_begin),
... code ...
(try_end),

]

from util_common import *
from util_wrappers import *

def modmerge_mission_templates(orig_mission_templates):
for n in range(len(orig_mission_templates)): # Check all mt
mt_name = orig_mission_templates[n][0] # take the name of the current mt-scenario

# 1) Further code to execute for all mt-scenarios except those specified
if (not "tutorial" in mt_name and not "multiplayer" in mt_name and not "conversation" in mt_name ):

# 1.1) Example of adding a new line to the end of the trigger
trigger_i = MissionTemplateWrapper(orig_mission_templates[n]).FindTrigger_i(ti_on_agent_spawn) # find ti_on_agent_spawn trigger
if ( trigger_i != None ): # if the trigger is found => continue
trigger = orig_mission_templates[n][5][trigger_i] # take this trigger
codeblock = TriggerWrapper(trigger).GetConsequenceBlock() # take his code
codeblock.Append([(call_script, "script_scene_agent_set_name")]) # add a new row to the end

# 1.2) Example of adding a list of rows to the end of the trigger
codeblock.Append(inject_block)

# 2) Further code to execute for all mt-scenarios
# 2.1) Example of search-insert-delete
trigger_i = MissionTemplateWrapper(orig_mission_templates[n]).FindTrigger_i(1, 4, ti_once [(main_hero_fallen)]) # find trigger (1 ,4, ti_once, [(main_hero_fallen)])
if ( trigger_i != None ): # if the trigger is found => continue
trigger = orig_mission_templates[n][5][trigger_i] # take this trigger
codeblock = TriggerWrapper(trigger).GetConsequenceBlock() # take his code
pos = codeblock.FindLineMatching((assign, "$pin_player_fallen", 1)) # in the body of the trigger to find the second match (1 match skip)
if (codeblock.GetLineContent(pos 1) != (try_begin) ): # check if the next line is not (try_begin), ie this is native code and not changed..
codeblock.InsertAfter(pos, batt_continue_addon) # insert a new code from the block inject_block after searched line
codeblock.Append([(try_end)]) # and add to the end of the trigger (try_end)
if (trigger == common_siege_check_defeat_condition): # If this trigger actually bears the name common_siege_check_defeat_condition
pos = codeblock.FindLineMatching((party_slot_eq, "p_main_party", slot_party_pref_bc_charge_ko, 1)) # find a second match of the searched line
codeblock.RemoveAt(pos, 6) # and remove 6 lines below, including found

# 2.2) Example of replacing trigger's recharge-time with ti_once to 0
orig_mission_templates[n][5][trigger_i] = list(orig_mission_templates[n][5][trigger_i]) # break up trigger in the list of items
orig_mission_templates[n][5][trigger_i][2] = 0 # replace third item
orig_mission_templates[n][5][trigger_i] = tuple(orig_mission_templates[n][5][trigger_i]) #restore the structure of the trigger (collect a list of items in the immutable tuple)

def modmerge(var_set):
try:
var_name_1 = "mission_templates"
orig_mission_templates = var_set[var_name_1]
modmerge_mission_templates(orig_mission_templates) # perform search-replace function

except KeyError:
errstring = "Variable set does not contain expected variable: "%s"." % var_name_1
raise ValueError(errstring)
 
Last edited by a moderator:
Scripts:

Nothing to be scripted. Just create a list of new scripts. Example:
harbour_scripts.py said:
# [OSP] Harbour
# Harbour visit.
# MB Warband v1.158+
# Code v1.0 by: Shcherbyna
# Additional code vX.X: by
# -*- coding: UTF-8 -*-


... default header from module_scripts.py ...

scripts = [
   
  #script_harbour_visit - load mission template when visit town_harbour
  # INPUT: none
  # OUTPUT: none

  ("harbour_visit",
    [
      (set_jump_mission, "mt_town_harbour"),
      (jump_to_scene, "scn_town_harbour"),
      (change_screen_mission),

  ]),

(.. script 2 ...),
(.. script 3 ...),

]
For this purpose used a search and replacing directives. Example:
sitting_lords_scripts.py said:
... defaul header from module_scripts.py ...

from util_wrappers import *
from util_scripts import *

# Find and replace.
scripts_directives = [
# [SD_OP_BLOCK_REPLACE, <script_name>, <position_flags>, <position_value_1>, <position_value_2>, <op_block>,  <lines_to_remove>(default 1)]
[SD_OP_BLOCK_REPLACE, "init_town_agent", D_SEARCH_FROM_TOP | D_SEARCH_SCRIPTLINE, (assign, ":stand_animation", "anim_stand_lady"), 0 , [(assign, ":stand_animation", "anim_sitting_woman_seiza")], 1],
[SD_OP_BLOCK_REPLACE, "init_town_agent", D_SEARCH_FROM_TOP | D_SEARCH_SCRIPTLINE, (assign, ":stand_animation", "anim_stand_lord"), 0 , [(assign, ":stand_animation", "anim_sitting_man_seiza")], 1],
]

def modmerge_scripts(orig_scripts):
process_script_directives(orig_scripts, scripts_directives)

def modmerge(var_set):
try:
var_name_1 = "scripts"
orig_scripts = var_set[var_name_1]
modmerge_scripts(orig_scripts) # perform the replacement

except KeyError:
errstring = "Variable set does not contain expected variable: "%s"." % var_name_1
raise ValueError(errstring)

]
This example shows how to add a new script and then it is called from the other:
active_parties_scripts.py said:
... default module_scripts.py header ...

# A new script
scripts = [

  # script_update_ai_party_icon
  # INPUT: none
  # OUTPUT: none

  ("update_ai_party_icon",
  [
  (store_script_param_1, ":party_no"),
  ...
  ...
  ]),

]

# A new code for exist script updating
update_script_create_kingdom_hero_party = [
     
(call_script, "script_update_ai_party_icon", "$pout_party"),
        ....

]


from util_wrappers import *
from util_scripts import *

# Adding a new code from block update_... at the end of create_kingdom_.. .
scripts_directives = [
[SD_OP_BLOCK_INSERT, "create_kingdom_hero_party", D_SEARCH_FROM_BOTTOM | D_SEARCH_LINENUMBER | D_INSERT_BEFORE, 0, 0, update_script_create_kingdom_hero_party], #ADD TO END.
]

def modmerge_scripts(orig_scripts):
process_script_directives(orig_scripts, scripts_directives)

def modmerge(var_set):
try:
var_name_1 = "scripts"
orig_scripts = var_set[var_name_1]
add_scripts(orig_scripts, scripts, True) # first inserting a new scripts
modmerge_scripts(orig_scripts) # after, update old one

except KeyError:
errstring = "Variable set does not contain expected variable: "%s"." % var_name_1
raise ValueError(errstring)

]

Items:

Nothing to be scripted. Just add a new item rows. Example:
mahjong_items.py said:
... default module_items.py header ...

items = [

["kanji_mahjong_for_tavern", "Kanji Mahjong", [("mahjong_box",0)], itp_type_one_handed_wpn|itp_primary|itp_secondary, itc_longsword, 200, weight(1.5)|spd_rtng(80)|weapon_length(20)|swing_damage(2,blunt),imodbits_none], # not merchandise, tavern use only
["kanji_mahjong","Kanji Mahjong", [("mahjong_box",0)], itp_type_one_handed_wpn|itp_merchandise|itp_primary|itp_secondary, itc_longsword, 50, weight(1.5)|spd_rtng(80)|weapon_length(20)|swing_damage(2,blunt),imodbits_none],

]

Sounds:

Nothing to be scripted. Just add a new sounds. Example:
mahjong_sounds.py said:
# [OSP] Kanji Mahjong
# Registering of mahjong game sounds
# MB Warband v1.158+
# Сode v1.0: by Shcherbyna
# Additional code vX.X: by
# -*- coding: UTF-8 -*-


from header_sounds import *

sounds = [

("mahjong_intro", sf_2d|sf_vol_15,["quest_completed2.ogg"]),
("mahjong_round_complete", sf_2d|sf_vol_15,["mahjong_round_complete.wav"]),
("mahjong_nomatches", sf_2d|sf_vol_15,["mahjong_nomatches.wma"]),
...

]

Meshes:

Nothing extra code. Just add new objects. Example:
mahjong_meshes.py said:
... default module_meshes.py header ...

meshes = [

  ("mahjong_start_screen", 0, "mahjong_start_screen", 0, 0, 0, 0, 0, 0, 1, 1, 1),
 
  ("mahjong_table_1", 0, "mahjong_table_1", 0, 0, 0, 0, 0, 0, 1, 1, 1),
  ("mahjong_table_2", 0, "mahjong_table_2", 0, 0, 0, 0, 0, 0, 1, 1, 1),
  ("mahjong_table_3", 0, "mahjong_table_3", 0, 0, 0, 0, 0, 0, 1, 1, 1),
  ("mahjong_table_4", 0, "mahjong_table_4", 0, 0, 0, 0, 0, 0, 1, 1, 1),
  ("mahjong_table_5", 0, "mahjong_table_5", 0, 0, 0, 0, 0, 0, 1, 1, 1),
  ("mahjong_table_6", 0, "mahjong_table_6", 0, 0, 0, 0, 0, 0, 1, 1, 1),
  ...

]

Map_icons:

Nothing to be scripted. In this example, the standard player's icon is replaced by a new one, a new icon is added for ninja and replaces the standard icon of the town:
YourModName_map_icons.py said:
... default module_map_icons.py header ...

map_icons = [
 
  ("player",0,"ronin", avatar_scale, snd_footstep_grass, 0.15, 0.173, 0),
  ("shinobi",0,"shinobi", avatar_scale, snd_footstep_grass, 0.15, 0.173, 0),
  ...
  ("town", mcn_no_shadow, "map_jtown_a", 0.35, 0),
]

Presentations:

Nothing to be scripted. just add new presentations. Example:
YourModName_presentations.py said:
... default module_presentations.py header ...

presentations = [

  (.. a new presentation ...),
  (.. a new presentation 2...),
]

Game_menus:

Nothing to do extra. This example adds one new menu (studyMod_exit_to_main_menu) and one full replaced (start_game_0):
YourModName_game_menus.py said:
... default module_game_menus.py header ...

game_menus = [
  ("start_game_0",menu_text_color(0xFF000000)|mnf_disable_all_keys,
    "Welcome, adventurer",
    "none",
    [(start_presentation, "prsnt_studyMod_startMenu")], # Language selecting menu
    []
  ),

  ("studyMod_exit_to_main_menu", 0, "@", "none", [(change_screen_quit)],[]),

]
This example adds one new menu (mnu_ship_departure) and one option (mno_sail_from_port) an existing menu (mnu_town) completely replaced by a new code:
YourModName_game_menus.py said:
... default module_game_menus.py header ...

game_menus = [
  ("ship_departure",menu_text_color(0xFF000000)|mnf_disable_all_keys,
    "You continue sea passage...",
    "none",
    [],
    [
    ("continue",[],"Continue...",
      [(change_screen_map),
      ]),
    ]), 
]

# Show port option in the town menu
update_menu_town_block = [

      ("sail_from_port",
      [
        (party_slot_eq, "$current_town", slot_party_type, spt_town), # If this is town
        (party_get_position, pos1, "p_main_party"),
        (map_get_water_position_around_position, pos2, pos1, 6), # and there is water in radius
      ],
      "Go to port", # Show this menu option
      [
        (set_jump_mission, "mt_town_harbour"),
        (jump_to_scene, "scn_town_harbour"),
        (change_screen_mission),
      ]),
]

from util_wrappers import *
from util_common import *

# Adding new game menu function
def add_game_menus(orig_game_menus, game_menus, check_duplicates = True):
    addmode = ADDMODE_REPLACE_EXIST
    if  not check_duplicates:
        addmode = ADDMODE_APPEND
    return add_objects(orig_game_menus, game_menus, addmode)

# Menu option replacing function
def modmerge_game_menus(orig_game_menus):
    try: #full replace mno_sail_from_port in mnu_town
        menu_Town = list_find_first_match_i(orig_game_menus, "town") # get "Town" menu
        menu_Town_options_list = GameMenuWrapper(orig_game_menus[menu_Town]).GetMenuOptions() # get option list
        for option_i in range(len(menu_Town_options_list)): # checking all menu options
            option_id = GameMenuOptionWrapper(menu_Town_options_list[option_i]).GetId() # take the name of current menu_option
            if (option_id == "sail_from_port"): # if is target option
                menu_Town_options_list[option_i] = update_menu_town_block[0] # full replace it by new code

    except:
        import sys
        print "Injecton 1 failed:", sys.exc_info()[1]
      raise

def modmerge(var_set):
  try:
        var_name_1 = "game_menus"
        orig_game_menus = var_set[var_name_1]
        add_game_menus(orig_game_menus, game_menus, True) # 1 - add new menu
        modmerge_game_menus(orig_game_menus) # 2 - replace by new code
    except KeyError:
        errstring = "Variable set does not contain expected variable: "%s"." % var_name_1
      raise ValueError(errstring)

Troops:

No extra scripting. Example:
studyMod_troops.py said:
... default module_troops.py header ...

troops = [
 
# These are used as arrays in the scripts.
  # lvl boundaries dict indexes

  ["studyMod_lvl_indexes_container","{!}","{!}", tf_inactive, 0, 0, fac_neutral,[],0,wp(60),0,0],
  # this slot-container needs for dynamic selecting lvl-code-script-loader
  ["studyMod_lvl_code_loaders_container","{!}","{!}", tf_inactive, 0, 0, fac_neutral,[],0,wp(60),0,0],
  # study word's codes holder, loads in before start mission_templates
  ["studyMod_code_container","{!}","{!}", tf_inactive, 0, 0, fac_neutral,[],0,wp(60),0,0],
 
]

Strings:

Nothing to do extra, in the file only the new rows. Use double quotes rather, it allows you to write in a row apostrophes ("O'Really"). Example:
YourModName_strings.py said:
strings = [
 
  ("startMenu_next_str_kanji_reading", "Tsugi e"),
  ("startMenu_next_str_0", "Next"),
  ("startMenu_next_str_1", "次へ"),
  ("startMenu_next_str_2", "Продолжить"),
 
]

Simple_triggers:

In this example, searches for the right trigger on a unique line, after which the trigger is completely replaced by a new code from replace_block:
active_parties_simple_triggers.py said:
... default module_simple_triggers.py header ...

replace_block = (0,
  [(troop_get_inventory_slot, ":cur_horse", "trp_player", :cool:, #horse slot
    (assign, ":new_icon", -1),
    (try_begin),
    ....
    ....
    (neq, ":new_icon", "$g_player_party_icon"),
    (assign, "$g_player_party_icon", ":new_icon"),
    (party_set_icon, "p_main_party", ":new_icon"),
    ])


from util_wrappers import SimpleTriggerWrapper

def modmerge_simple_triggers(orig_simple_triggers, search_line, replace_block):
for trigger_i in range(len(orig_simple_triggers)): # Run on all lines of the file simple_triggers
opblock = SimpleTriggerWrapper(orig_simple_triggers[trigger_i]).GetOpBlock().Unwrap() # take the current trigger code
for line_i in range(len(opblock)): # Run through the lines of code of current trigger
if (opblock[line_i] == search_line): # if the current line of code is target
orig_simple_triggers[trigger_i] = replace_block # completely replace current trigger

def modmerge(var_set):
  try:
        var_name_1 = "simple_triggers"
        orig_simple_triggers = var_set[var_name_1]
# Complete replacement of the trigger, which contains a line of code (assign, ":new_icon", "icon_player_horseman")
        modmerge_simple_triggers(orig_simple_triggers, (assign, ":new_icon", "icon_player_horseman"), replace_block)
    except KeyError:
        errstring = "Variable set does not contain expected variable: "%s"." % var_name_1
        raise ValueError(errstring)
In this example performs search for the first 12-hours trigger from top.  Then in the end adds a new line of code. In addition, if uncomment prints can immediately see the trigger code before and after injection.
ship_navigation_simple_triggers.py said:
... default module_simple_triggers.py header ...

from util_wrappers import SimpleTriggerWrapper

def modmerge_simple_triggers(orig_simple_triggers, search_interval, insert_block):
for trigger_i in range(len(orig_simple_triggers)): # Run on all lines of simple_triggers file
if (SimpleTriggerWrapper(orig_simple_triggers[trigger_i]).GetInterval() == search_interval): # if current trigger interval is 12
            #add to the end of the trigger new line of code
            #print orig_simple_triggers[trigger_i]

            orig_simple_triggers[trigger_i] = list(orig_simple_triggers[trigger_i]) # convert immutable tuple-trigger to variable trigger-list (a list of 2 elements: interval and opblock-list)
            orig_simple_triggers[trigger_i][1].append(insert_block) # attached to op-bloсk-list a new element (line of code)
            orig_simple_triggers[trigger_i] = tuple(orig_simple_triggers[trigger_i]) # restore the structure of trigger's op-block (collect a list of items in the immutable tuple)
            #print "========= injected ============"
            #print orig_simple_triggers[trigger_i]

            break

def modmerge(var_set):
  try:
        var_name_1 = "simple_triggers"
        orig_simple_triggers = var_set[var_name_1]
# Add a new line to the end of the first found the 12-hour Simple Trigger
        modmerge_simple_triggers(orig_simple_triggers, 12, (call_script, "script_navigation_init_wind"))
    except KeyError:
        errstring = "Variable set does not contain expected variable: "%s"." % var_name_1
        raise ValueError(errstring)

Parties:

Nothing to be scripted. In this example, some towns have new coordinates and names:
static_parties_parties.py said:
... default module_parties.py header ...

parties = [

      ("town_1","Funai",  pf_town, no_menu, pt_none, fac_neutral,0,ai_bhvr_hold,0,(-127.496864, -99.361931),[], -176.353),
      ("town_2","Hatinohe",    pf_town, no_menu, pt_none, fac_neutral,0,ai_bhvr_hold,0,(83.973747, 25.65699:cool:,[], 0),
      ("town_3","Tokushima",  pf_town, no_menu, pt_none, fac_neutral,0,ai_bhvr_hold,0,(-78.504913, -83.375710),[], 153.912),
      ("town_4","Okayama",    pf_town, no_menu, pt_none, fac_neutral,0,ai_bhvr_hold,0,(-101.799500, -67.782112),[], 431.234),
      ("town_5","Kyoto",  pf_town, no_menu, pt_none, fac_neutral,0,ai_bhvr_hold,0,(-47.491726, -68.609337),[], 104.61),
      ("town_6","Niigata",  pf_town, no_menu, pt_none, fac_neutral,0,ai_bhvr_hold,0,(33.883018, -8.784881),[], 239.225),
      ("town_7","Toyama",  pf_town, no_menu, pt_none, fac_neutral,0,ai_bhvr_hold,0,(-22.662624, -37.36145:cool:,[], 166.94:cool:,
    ...
]

Particles:

No extra scripting, only new particles . Example:
YourModName_particle_systems.py said:
... default module_particle_systems.py header ...

particle_systems = [

      ... new particles ...
]

Scene props:

Scene_prop list is a ordinary Python-list and subordinated to certain actions over the list: https://docs.python.org/2/tutorial/datastructures.html#more-on-lists. In this example performs a search of specified prop-objects and inserting the new ones before:
YourModName_scene_props.py said:
...default module_scene_props.py header ...

def modmerge_scene_props(orig_scene_props):
# Inserting new tuples BEFORE searched objects
        # The result of the injection can be checked in ID_scene_props.py

for n in range(len(orig_scene_props)): # Run through all the rows of the list scene_props
if orig_scene_props[n][0] == "ship_b" and orig_scene_props[n-1][0] != "small_blue_ship": #  if the current object is the desired and before him are no new yet
orig_scene_props.insert(n, ("small_blue_ship",0,"small_blue_ship","bo_ship", [])) # insert new object before the found
elif orig_scene_props[n][0] == "ship_c_sail_off" and orig_scene_props[n-1][0] != "ship_b_sail_off": # else, if the current object is the second desired and before him are no new
orig_scene_props.insert(n, ("ship_b_sail_off",0,"ship_b_sail_off","bo_ship_b_sail_off", [])) # insert second new object before the found
elif orig_scene_props[n][0] == "ramp_small_a" and orig_scene_props[n-1][0] != "ship_d_sail_off": # else, if the current object is the third desired and before him are no new
orig_scene_props.insert(n, ("ship_d_sail_off",0,"ship_d_sail_off","bo_ship_d_sail_off", [])) # insert third new object before the found

def modmerge(var_set):
  try:
        var_name_1 = "scene_props"
        orig_scene_props = var_set[var_name_1]
        modmerge_scene_props(orig_scene_props) # Adding new scene_props in specified places list
    except KeyError:
        errstring = "Variable set does not contain expected variable: "%s"." % var_name_1
        raise ValueError(errstring)
 
notice:

Code:
module_sys_info = {
        "version": 1158,      # version number * 1000
}

if version isn't same with modmerger,compiler or modmerger installer will give tons of errors.

so,you have to set there to your module's version.  :smile:
 
my name is jeff2146 and recently i have started work at my own mod but i got stuck as soon as i had to use the module system
now i am looking for someone who can help mee write the code i need
i have already added the new units and banner whit Morghs M&B WB-WFAS Editor
if onyone is interested in helping me you can contact mee on [email protected]

greetings jeff2146
 
Back
Top Bottom