Introduction
Getting Started
First of all you need to find something you want the player to customize, typically a piece of armor. Split up your mesh into several logical parts, using OpenBRF or an external 3d editor. Remember that these meshes won't be sorted by proximity to eachother. Don't forget to copy rigging/fix the frames. Remember that most of the complicated meshes have dozens of components, so don't forget to rotate the camera and gather the submeshes which have no backfaces. While you're doing this you might as well optimize the mesh and clean it up a bit to prepare it for the next step. If your armor has frames, those need to be split up and reimplemented later (for working feminized armor).
In the images above (from the Grim Age OSP) you can see armor being broken up into several parts. You should always keep in mind whether a mesh can be shared - in the above example the cloth arms and trouser components are re-used throughout the mod. Keep in mind that some meshes while visually similar will use different material atlas - the existing multimesh structure gives you a starting point.
Then you need to make sure all the assets have the same number of .LOD meshes. While OpenBRF can do this for you, hand-making lods allow you have finer artistic control to re-use them. Of course you can always be lazy and just not make any at all, but this will lead to greatly reduced performance if you allow the armor to be used by NPCs.
Basic Coding
Setting up
We need to define a lot of constants and set the proper slots for our armour to work. We have a presentation which allows the player to preview their settings and change them (involving a custom tableau). We have a script which dumps out strings. Finally we have the item initialization script itself, which puts the armour together dynamically. For debugging purposes I have split this up into several item slots, one for each option. If you want different degrees of customization for kings and such they should be bound to one troop slots instead, which you can achieve by masking the individual values and unpacking as needed. Because we are working across multiple game objects, we need to use some globals to pass them around especially in presentations.
The code works like this: The item checks its own slots, and if a value is set it calls a script requesting a string. Here we just check if the agent wearing the armor is a hero, but you can impose any condition. The script compartmentalizes the various meshes and keeps everything tidy and also returns meta-data after storing the result in s1. This is used by the presentation to decide whether the mesh is a mandatory part of the item or not, or if it's one of several options. These are then displayed and the player can change them from the default values. Mandatory ones are represented by combo-boxes and optional ones are check boxes. You should still do error checking for the range in the script and for the return value to make sure the game doesn't crash when slots have improper values.
And without further ado, here's the basic framework. I will get around to packaging the actual meshes and adding links to them.
Download & Credits
Narf's Men-at-Arms OSP uploaded here and there
Baraban's platemail OSP uploaded here
Crusader: Way to Expiation uploaded here
Grim Age OSP
Getting Started
Then you need to make sure all the assets have the same number of .LOD meshes. While OpenBRF can do this for you, hand-making lods allow you have finer artistic control to re-use them. Of course you can always be lazy and just not make any at all, but this will lead to greatly reduced performance if you allow the armor to be used by NPCs.
Basic Coding
Code:
["aketon", "Aketon", [("aketon",0)], itp_merchandise| itp_type_body_armor|itp_covers_legs|itp_civilian,0,
325 , weight(3)|abundance(100)|head_armor(0)|body_armor(22)|leg_armor(6)|difficulty(0) ,imodbits_cloth,
[(ti_on_init_item,
[
(store_trigger_param_1, ":agent_no"),
(store_trigger_param_2, ":troop_no"),
(call_script, "script_init_aketon", ":agent_no", ":troop_no", 1),
(cur_item_add_mesh, s1), #submesh 1
(call_script, "script_init_aketon", ":agent_no", ":troop_no", 2),
(cur_item_add_mesh, s1), #submesh 2
(try_begin),
(troop_is_hero, ":troop_no"),
(item_get_slot, ":color", "itm_aketon", slot_item_player_color),
(gt, ":color", -1),
(else_try),
(store_random_in_range, ":color", 0, 6),
(try_end),
(val_mod, ":color", 6),
(str_clear, s1),
(val_add, ":color", "str_hose_r"),
(str_store_string, s1, ":color"),
(cur_item_set_material, s1, 2), #second mesh = legs
])
]],
- ti_on_init_item is the trigger which is called whenever an item is loaded. This can be in a scene, or even in the inventory. However there are engine limitations - while it launches for every item in the inventory, actual testing found that it did not trigger for horses, gloves and boots. This means that realistically we can only hope to change weapons/head/body armor, but there exists code to override the other items. You can also use WSE which allows it to work for horse last I checked.
-
Code:
(cur_item_add_mesh, s)
-
Code:
(cur_item_set_material, s, n)
- You can set the .LOD as optional parameters for both operations but then it gets messy to code. This can interact with tableau operations to apply dynamic vertex colouring but I feel like they would not play nice together.
Code:
["coat_of_plates_red", "Arena Coat of Plates", [("coat_of_plates_red",0)], itp_merchandise| itp_type_body_armor |itp_covers_legs ,0,
3828 , weight(25)|abundance(100)|head_armor(0)|body_armor(52)|leg_armor(16)|difficulty(8) ,imodbits_armor,
[(ti_on_init_item,
[
(store_trigger_param_1, ":agent_no"),
(store_trigger_param_2, ":troop_no"),
(try_begin),
(item_get_slot, ":color", "itm_coat_of_plates_red", slot_item_player_color),
(eq, ":color", -1),
(try_begin),
(troop_is_hero, ":troop_no"),
(store_add, ":color", ":troop_no", 1),
(else_try),
(agent_is_active, ":agent_no"),
(store_add, ":color", ":agent_no", 2),
(else_try), #Black, Red, RBGY
(store_random_in_range, ":color", 0, 5),
(try_end),
(try_end),
(val_mod, ":color", 6),
(str_clear, s1),
(val_add, ":color", "str_cop_material"),
(str_store_string, s1, ":color"),
(cur_item_set_material, s1, 0),
])
]],
Code:
(item_set_slot, "itm_coat_of_plates_red", slot_item_materials_begin, "str_cop_material"),
(item_set_slot, "itm_coat_of_plates_red", slot_item_materials_end, "str_platemail_cloak_1"),
(item_set_slot, "itm_coat_of_plates_red", slot_item_num_components, 1),
Code:
#coat of plate reskins
("cop_material", "coat_of_plates_a"),
("cop_red", "coat_of_plates_red"),
("cop_1", "coat_of_plates_arena"),
("cop_2", "coat_of_plates_blue"),
("cop_3", "coat_of_plates_green"),
("cop_4", "coat_of_plates_yellow"),
We need to define a lot of constants and set the proper slots for our armour to work. We have a presentation which allows the player to preview their settings and change them (involving a custom tableau). We have a script which dumps out strings. Finally we have the item initialization script itself, which puts the armour together dynamically. For debugging purposes I have split this up into several item slots, one for each option. If you want different degrees of customization for kings and such they should be bound to one troop slots instead, which you can achieve by masking the individual values and unpacking as needed. Because we are working across multiple game objects, we need to use some globals to pass them around especially in presentations.
Categorizing meshes
Example of options
Example of options
And without further ado, here's the basic framework. I will get around to packaging the actual meshes and adding links to them.
Code:
armor_cloth = 0
armor_armor = 1
armor_plate = 2
custom_armor_maximum_submaterial = 7 #armor_plate + 4, thanks platemail
slot_item_materials_begin = 81
slot_item_materials_end = 82
slot_item_num_components = 83
slot_item_init_script = 84
#mandatory + optional | color
slot_item_player_slots_begin = 85
slot_item_player_slots_end = slot_item_player_slots_begin + custom_armor_maximum_submaterial
slot_item_player_color = slot_item_player_slots_end
custom_item_color_mask = 0xf
custom_item_color_bits = 4
message_positive = 0x33FF33
message_negative = 0xFF3333
message_neutral = 0xFFFF33
Code:
("cheat_find_item",0,
"{!}Current item range: {reg5} to {reg6}",
"none",
[
(assign, reg5, "$cheat_find_item_range_begin"),
(store_add, reg6, "$cheat_find_item_range_begin", max_inventory_items),
(val_min, reg6, "itm_items_end"),
(val_sub, reg6, 1),
(assign, ":end", ek_foot), #should add a global as iterator
(try_for_range, ":item_slot", ek_item_0, ":end"),
(troop_get_inventory_slot, ":item_no", "$g_player_troop", ":item_slot"),
(gt, ":item_no", -1),
(item_slot_ge, ":item_no", slot_item_num_components, 1),
(assign, "$g_current_opened_item_details", ":item_no"),
(assign, ":end", -1),
(try_end),
],
[
("custom_armor",[
(item_slot_ge, "$g_current_opened_item_details", slot_item_num_components, 1),
(str_store_item_name, s0, "$g_current_opened_item_details"),
],"Customize {s0}",
[
(start_presentation, "prsnt_customize_armor"),
]),
Code:
#takes g_current_opened_item_details as parameter
("customize_armor", prsntf_manual_end_only, 0, [
(ti_on_presentation_load,
[(set_fixed_point_multiplier, 1000),
(assign, "$g_presentation_credits_obj_1", -1),
(assign, "$g_presentation_credits_obj_2", -1),
(assign, "$g_presentation_credits_obj_3", -1),
(assign, "$g_presentation_credits_obj_4", -1),
(assign, "$g_presentation_credits_obj_5", -1),
(assign, "$g_presentation_obj_item_select_1", -1),
(assign, "$g_presentation_obj_item_select_2", -1),
(assign, "$g_presentation_obj_item_select_3", -1),
(try_begin),
(create_mesh_overlay, reg0, "mesh_cb_ui_main"),
(position_set_x, pos1, 0),
(position_set_y, pos1, 0),
(overlay_set_position, reg0, pos1),
(position_set_x, pos1, 1000),
(position_set_y, pos1, 1000),
(overlay_set_size, reg0, pos1),
(try_end),
#background for menu
(try_begin),
(create_mesh_overlay, reg0, "mesh_mp_score_a"),
(position_set_x, pos1, 295),
(position_set_y, pos1, 115),
(overlay_set_position, reg0, pos1),
(position_set_x, pos1, 1000),
(position_set_y, pos1, 1000),
(overlay_set_size, reg0, pos1),
(try_end),
#clear arrays - use a for radio buttons, b for dropdowns, c for text
(try_for_range, ":slot_no", 0, custom_armor_maximum_submaterial),
(troop_set_slot, "trp_temp_array_a", ":slot_no", -1),
(troop_set_slot, "trp_temp_array_b", ":slot_no", -1),
(troop_set_slot, "trp_temp_array_c", ":slot_no", -1),
(try_end),
# (item_get_slot, ":item_variation", "$g_current_opened_item_details", slot_item_player_color),
(item_get_slot, ":sub_mesh_total", "$g_current_opened_item_details", slot_item_num_components),
(item_get_slot, ":script_no", "$g_current_opened_item_details", slot_item_init_script),
(try_begin),
(gt, ":script_no", 0),
(assign, ":cur_combobox", 0),
(assign, ":cur_checkbox", 0),
(assign, ":cur_textbox", 0),
(try_for_range, ":sub_mesh", 0, ":sub_mesh_total"),
(call_script, ":script_no", -1, "$g_player_troop", ":sub_mesh", 0), #get upper bound
(assign, ":sub_total", "$g_custom_armor_param_count"),
(assign, ":overlay", -1),
(try_begin), #only 1 mesh, just a text overlay
(eq, ":sub_total", 1),
(eq, "$g_custom_armor_mandatory", 1),
(create_text_overlay, ":overlay", s1, tf_single_line|tf_left_align),
# (overlay_set_text, ":overlay", s1),
(troop_set_slot, "trp_temp_array_c", ":cur_textbox", ":overlay"),
(val_add, ":cur_textbox", 1),
(else_try), #assume if first one passes at 0 index, the rest are also mandatory
(gt, ":sub_total", 1),
(eq, "$g_custom_armor_mandatory", 1),
(create_combo_button_overlay, ":overlay"),
(troop_set_slot, "trp_temp_array_b", ":cur_combobox", ":overlay"),
(overlay_add_item, ":overlay", "str_randomize"), #default -1 value, "random" depends on item
(overlay_add_item, ":overlay", s1), #from calling sub_material 0
(val_add, ":cur_combobox", 1),
(try_for_range, ":sub_material", 1, ":sub_total"),
(str_clear, s1),
(call_script, ":script_no", -1, "$g_player_troop", ":sub_mesh", ":sub_material"),
(neg|str_is_empty, s1),
(overlay_add_item, ":overlay", s1),
(try_end),
(else_try), #optional mesh, assume all the following can and will apply
(gt, ":sub_total", 0),
(try_for_range, ":sub_material", 0, ":sub_total"),
(str_clear, s1),
(call_script, ":script_no", -1, "$g_player_troop", ":sub_mesh", ":sub_material"),
(eq, "$g_custom_armor_mandatory", 0),
(neg|str_is_empty, s1),
(create_check_box_overlay, ":overlay", "mesh_checkbox_off", "mesh_checkbox_on"),
# (overlay_set_tooltip, ":overlay", s1),
(troop_set_slot, "trp_temp_array_a", ":cur_checkbox", ":overlay"),
#create a text directly after
(create_text_overlay, ":overlay", s1, tf_single_line|tf_left_align),
(val_add, ":cur_checkbox", 1),
(try_end),
(try_end),
(try_end),
#arrange overlays, set values now that we have all of them
#TODO add 2xcontainer on either side
(assign, ":cur_x", 150),
(assign, ":cur_y", 600),
(position_set_x, pos1, ":cur_x"),
(assign, ":cur_mesh_slot", slot_item_player_slots_begin),
(try_for_range, ":slot_no", 0, ":cur_combobox"),
(troop_get_slot, ":overlay", "trp_temp_array_b", ":slot_no"),
(gt, ":overlay", -1),
(position_set_y, pos1, ":cur_y"),
(overlay_set_position, ":overlay", pos1),
(try_begin),
(item_get_slot, ":cur_value", "$g_current_opened_item_details", ":cur_mesh_slot"),
(val_add, ":cur_value", 1), #should probably cap this somewhere
(overlay_set_val, ":overlay", ":cur_value"),
(val_add, ":cur_mesh_slot", 1),
(try_end),
(val_sub, ":cur_y", 100), #resize this depending on amount in each combo?
(try_begin),
(le, ":cur_y", 100),
(assign, ":cur_y", 800),
(val_add, ":cur_x", 100),
(position_set_x, pos1, ":cur_x"),
(try_end),
(try_end),
(try_begin), #no 2nd column boxes so we apply labels in original column
(eq, ":cur_x", 150),
(val_sub, ":cur_x", 125),
(try_end),
# (assign, ":cur_y", 600),
(position_set_x, pos1, ":cur_x"),
(try_for_range, ":slot_no", 0, ":cur_textbox"),
(troop_get_slot, ":overlay", "trp_temp_array_c", ":slot_no"),
(gt, ":overlay", -1),
(position_set_y, pos1, ":cur_y"),
(overlay_set_position, ":overlay", pos1),
(overlay_set_color, ":overlay", 0xFFFFFF),
(val_sub, ":cur_y", 50),
(try_begin), #reset to top
(le, ":cur_y", 200),
(assign, ":cur_y", 800),
(val_add, ":cur_x", 50),
(position_set_x, pos1, ":cur_x"),
(try_end),
(try_end),
(assign, ":cur_x", 750),
(assign, ":cur_y", 600),
(position_set_x, pos2, 800),
(position_set_y, pos2, 800),
(store_sub, ":cur_mesh_slot", slot_item_player_slots_end, 1),
(try_for_range, ":slot_no", 0, ":cur_checkbox"),
(troop_get_slot, ":overlay", "trp_temp_array_a", ":slot_no"),
(gt, ":overlay", -1),
(position_set_x, pos1, ":cur_x"),
(position_set_y, pos1, ":cur_y"),
(overlay_set_position, ":overlay", pos1),
(try_begin),
(item_get_slot, ":cur_value", "$g_current_opened_item_details", ":cur_mesh_slot"),
(neq, ":cur_value", -1), #checkboxes only have 2 states unfortnately
(overlay_set_val, ":overlay", ":cur_value"),
(try_end),
#move text
(val_add, ":overlay", 1),
(try_begin),
(eq, ":cur_value", 1),
(assign, ":color", message_positive),
(else_try),
(eq, ":cur_value", 0),
(assign, ":color", message_negative),
(else_try),
(assign, ":color", message_neutral),
(try_end),
(overlay_set_color, ":overlay", ":color"),
(store_add, ":text_x", ":cur_x", 50),
(position_set_x, pos1, ":text_x"),
(overlay_set_position, ":overlay", pos1),
(overlay_set_size, ":overlay", pos2),
(val_sub, ":cur_mesh_slot", 1),
(val_sub, ":cur_y", 75),
(try_begin),
(le, ":cur_y", 200),
(assign, ":cur_y", 800),
(val_add, ":cur_x", 100),
(position_set_x, pos1, ":cur_x"),
(try_end),
(try_end),
#store these as globals
(assign, "$g_presentation_obj_item_select_1", ":cur_combobox"),
(assign, "$g_presentation_obj_item_select_2", ":cur_checkbox"),
(assign, "$g_presentation_obj_item_select_3", ":cur_textbox"),
(try_end),
(store_sub, reg1, ":sub_mesh_total", 1),
(val_max, reg1, 1),
(str_store_item_name, s1, "$g_current_opened_item_details"),
(create_text_overlay, reg0, "@{s1} has {reg1} component meshes", tf_center_justify|tf_single_line|tf_with_outline),
(position_set_x, pos1, 500),
(position_set_y, pos1, 700),
(overlay_set_position, reg0, pos1),
(overlay_set_color, reg0, 0xFFFFFF),
# (try_for_range, ":slot_no", slot_item_player_slots_begin, slot_item_player_slots_end + 1),
# (item_get_slot, reg1, "$g_current_opened_item_details", ":slot_no"),
# (assign, reg2, ":slot_no"),
# (display_message, "@[{reg2}]:{reg1}"),
# (try_end),
#rearrange components, mandatory on left and toggles on right
(create_image_button_overlay_with_tableau_material, "$g_presentation_credits_obj_1", -1, "tableau_custom_armor_window", "$g_player_troop"),
(position_set_x, pos1, 380),
(position_set_y, pos1, 200),
(overlay_set_position, "$g_presentation_credits_obj_1", pos1),
# (position_set_x, pos1, 1500),
# (position_set_y, pos1, 1500),
# (overlay_set_size, "$g_presentation_credits_obj_1", pos1),
(try_begin),
(item_get_slot, ":colors_begin", "$g_current_opened_item_details", slot_item_materials_begin),
(gt, ":colors_begin", 0),
(item_get_slot, ":colors_end", "$g_current_opened_item_details", slot_item_materials_end),
(create_combo_label_overlay, "$g_presentation_credits_obj_3"),
(position_set_x, pos1, 500),
(position_set_y, pos1, 135),
(overlay_set_position, "$g_presentation_credits_obj_3", pos1),
(overlay_add_item, "$g_presentation_credits_obj_3", "str_color"), #default label
(try_for_range, ":color", ":colors_begin", ":colors_end"),
(overlay_add_item, "$g_presentation_credits_obj_3", ":color"),
(try_end),
# (troop_get_slot, ":cur_color", "$g_player_troop", ),
(item_get_slot, ":cur_color", "$g_current_opened_item_details", slot_item_player_color),
(val_add, ":cur_color", 1),
# (try_begin), #player set this somewhere
# (gt, ":cur_color", -1),
(overlay_set_val, "$g_presentation_credits_obj_3", ":cur_color"),
# (try_end),
(try_end),
(position_set_y, pos1, 90),
(store_sub, ":item_no", "$g_current_opened_item_details", 1),
(try_begin),
(item_slot_ge, ":item_no", slot_item_num_components, 1),
(str_store_item_name, s0, ":item_no"),
(create_button_overlay, "$g_presentation_credits_obj_4", s0),
(position_set_x, pos1, 250),
(overlay_set_position, "$g_presentation_credits_obj_4", pos1),
(try_end),
(store_add, ":item_no", "$g_current_opened_item_details", 1),
(try_begin),
(item_slot_ge, ":item_no", slot_item_num_components, 1),
(str_store_item_name, s0, ":item_no"),
(create_button_overlay, "$g_presentation_credits_obj_5", s0),
(position_set_x, pos1, 575),
(overlay_set_position, "$g_presentation_credits_obj_5", pos1),
(try_end),
(create_game_button_overlay, "$g_presentation_obj_profile_banner_selection_1", "str_reset_to_default"),
(create_game_button_overlay, "$g_presentation_obj_profile_banner_selection_2", "str_done"),
(position_set_y, pos1, 50),
(position_set_x, pos1, 425),
(overlay_set_position, "$g_presentation_obj_profile_banner_selection_1", pos1),
(position_set_x, pos1, 575),
(overlay_set_position, "$g_presentation_obj_profile_banner_selection_2", pos1),
(presentation_set_duration, 999999),
]),
(ti_on_presentation_mouse_enter_leave,
[(store_trigger_param_1, ":object"),
(try_begin),
(eq, ":object", "$g_presentation_credits_obj_1"),
(store_trigger_param_2, ":enter_leave"),
(try_begin),
(eq, ":enter_leave", 0),
(try_begin),
(assign, ":item_modifier", imod_plain),
(item_get_type, ":item_type", "$g_current_opened_item_details"),
(try_begin),
(is_between, ":item_type", itp_type_head_armor, itp_type_pistol), #head/body/foot/hand
(val_add, ":item_type", ek_head - itp_type_head_armor),
(else_try), #pretty sure you can't customize ammo but w/e
(this_or_next|is_between, ":item_type", itp_type_one_handed_wpn, itp_type_goods),
(is_between, ":item_type", itp_type_pistol, itp_type_animal),
#find it on the troop
(try_for_range, ":slot_no", ek_item_0, ek_head),
(troop_get_inventory_slot, ":item_no", "$g_player_troop", ":slot_no"),
(eq, ":item_no", "$g_current_opened_item_details"),
(assign, ":item_type", ":slot_no"),
(try_end),
(try_end),
(is_between, ":item_type", ek_item_0, ek_horse), #validate input
(troop_get_inventory_slot_modifier, ":item_modifier", "$g_player_troop", ":item_type"),
# (overlay_get_position, pos0, ":object"),
(mouse_get_position, pos0),
(show_item_details_with_modifier, "$g_current_opened_item_details", ":item_modifier", pos0, 100),
(try_end),
(else_try),
(eq, ":enter_leave", 1),
(close_item_details),
(try_end),
(try_end),
]),
(ti_on_presentation_event_state_change,
[
(store_trigger_param_1, ":object"),
(store_trigger_param_2, ":value"),
(assign, ":continue", 0),
(try_begin), #color toggler
(gt, "$g_presentation_credits_obj_3", -1),
(eq, ":object", "$g_presentation_credits_obj_3"), #combolabel, switch colours
# (item_get_slot, ":cur_color", "$g_current_opened_item_details", slot_item_player_color),
(store_sub, ":color", ":value", 1), #actual value, 0 is default
(item_set_slot, "$g_current_opened_item_details", slot_item_player_color, ":color"),
(else_try), #actual item tableau
(eq, ":object", "$g_presentation_credits_obj_1"), #tableau, switch view sides
(val_add, "$g_custom_armor_angle", 1),
(val_mod, "$g_custom_armor_angle", 6), #60x6
# #apply current colour settings
# (try_begin),
# (gt, "$g_presentation_credits_obj_3", -1),
# (overlay_get_value, ":value", "$g_presentation_credits_obj_3"),
# (item_set_slot, "$g_current_opened_item_details", slot_item_player_color, ":value"),
# (try_end),
(else_try), #reset to default, refresh
(eq, ":object", "$g_presentation_obj_profile_banner_selection_1"),
(try_for_range, ":slot_no", slot_item_player_slots_begin, slot_item_player_slots_end + 1),
(item_set_slot, "$g_current_opened_item_details", ":slot_no", -1),
(try_end),
(else_try), #prev
(eq, ":object", "$g_presentation_credits_obj_4"),
(val_sub, "$g_current_opened_item_details", 1),
(else_try), #next
(eq, ":object", "$g_presentation_credits_obj_5"),
(val_add, "$g_current_opened_item_details", 1),
(else_try), #close
(eq, ":object", "$g_presentation_obj_profile_banner_selection_2"),
(assign, ":continue", -1),
(presentation_set_duration, 0),
(else_try), #go after presentation object stored in arrays, left side combos
(gt, "$g_presentation_obj_item_select_1", 0),
(assign, ":cur_mesh_slot", slot_item_player_slots_begin - 1),
(try_for_range, ":slot_no", 0, "$g_presentation_obj_item_select_1"),
(val_add, ":cur_mesh_slot", 1), #pre-increment
(troop_slot_eq, "trp_temp_array_b", ":slot_no", ":object"),
# (assign, reg0, ":slot_no"),
# (assign, reg1, ":cur_mesh_slot"),
(store_sub, ":color", ":value", 1),
# (assign, reg2, ":color"),
# (display_message, "@{reg0} setting [{reg1}] to {reg2}"),
(item_set_slot, "$g_current_opened_item_details", ":cur_mesh_slot", ":color"),
(assign, "$g_presentation_obj_item_select_1", -1), #loop break
(try_end),
(eq, "$g_presentation_obj_item_select_1", -1), #pass on to next block if not fulfilled
(else_try), #start from other end of sub_mesh_total, right side checkbox
(gt, "$g_presentation_obj_item_select_2", 0),
(assign, ":cur_mesh_slot", slot_item_player_slots_end),
(try_for_range, ":slot_no", 0, "$g_presentation_obj_item_select_2"),
(val_sub, ":cur_mesh_slot", 1),
(troop_slot_eq, "trp_temp_array_a", ":slot_no", ":object"),
(item_set_slot, "$g_current_opened_item_details", ":cur_mesh_slot", ":value"),
# (assign, reg0, ":slot_no"),
# (assign, reg1, ":cur_mesh_slot"),
# (assign, reg2, ":value"),
# (display_message, "@{reg0} setting [{reg1}] to {reg2}"),
(assign, "$g_presentation_obj_item_select_2", -1), #loop break
(try_end),
(try_end),
(try_begin),
(eq, ":continue", 0),
(start_presentation, "prsnt_customize_armor"),
(try_begin), #refresh me
# (in_meta_mission),
(get_player_agent_no, ":player_agent"),
(gt, ":player_agent", -1),
(agent_has_item_equipped, ":player_agent", "$g_current_opened_item_details"),
(agent_unequip_item, ":player_agent", "$g_current_opened_item_details"),
(agent_equip_item, ":player_agent", "$g_current_opened_item_details"),
(try_end),
(try_end),
]),
]),
Code:
(item_set_slot, "itm_plate_custom", slot_item_materials_begin, "str_platemail_cloak_1"),
(item_set_slot, "itm_plate_custom", slot_item_materials_end, "str_platemail_heraldic"),
(item_set_slot, "itm_plate_custom", slot_item_num_components, 7),
(item_set_slot, "itm_plate_custom", slot_item_init_script, "script_init_plate_custom"),
(try_for_range, ":slot_no", slot_item_player_slots_begin, slot_item_player_slots_end + 1),
(item_set_slot, "itm_plate_custom", ":slot_no", -1),
(try_end),
Code:
("init_plate_custom",
[
(store_script_param, ":agent_no", 1),
(store_script_param, ":troop_no", 2),
(store_script_param, ":sub_mesh", 3),
(store_script_param, ":sub_mats", 4),
(str_clear, s1),
(assign, ":value", -1),
(assign, "$g_custom_armor_param_count", 3),
(assign, "$g_custom_armor_mandatory", 1),
(try_begin), #torso
(eq, ":sub_mesh", 0),
(is_between, ":sub_mats", 0, 3),
(store_add, ":value", ":sub_mats", "str_platemail_heraldic"),
# (assign, "$g_custom_armor_param_count", 3),
# (assign, "$g_custom_armor_mandatory", 1),
(else_try), #pauldrons
(eq, ":sub_mesh", 1),
(is_between, ":sub_mats", 0, 4),
(store_add, ":value", ":sub_mats", "str_platemail_pauldron_1"),
(assign, "$g_custom_armor_param_count", 4),
# (assign, "$g_custom_armor_mandatory", 1),
(else_try), #rondels
(eq, ":sub_mesh", 2),
(is_between, ":sub_mats", 0, 4),
(store_add, ":value", ":sub_mats", "str_platemail_rondel_0"),
(assign, "$g_custom_armor_param_count", 4),
# (assign, "$g_custom_armor_mandatory", 1),
(else_try), #knees
(eq, ":sub_mesh", 3),
(is_between, ":sub_mats", 0, 3),
(store_add, ":value", ":sub_mats", "str_platemail_knees_0"),
(else_try), #elbows
(eq, ":sub_mesh", 4),
(is_between, ":sub_mats", 0, 3),
(store_add, ":value", ":sub_mats", "str_platemail_elbows_0"),
(else_try), #accessories
(eq, ":sub_mesh", 5),
(is_between, ":sub_mats", 0, 2),
(store_add, ":value", ":sub_mats", "str_platemail_accessory_1"),
(assign, "$g_custom_armor_param_count", 2),
(assign, "$g_custom_armor_mandatory", 0),
(else_try),
(assign, "$g_custom_armor_param_count", 0),
(assign, "$g_custom_armor_mandatory", 0),
(try_end),
(try_begin),
(neq, ":value", -1),
(str_store_string, s1, ":value"),
(try_end),
]
),
Code:
#script_add_troop_to_custom_armor_tableau
# INPUT: troop_no, item (g_current_opened_item_details), side (g_custom_armor_angle)
# OUTPUT: none
("add_troop_to_custom_armor_tableau",
[
(store_script_param, ":troop_no",1),
(store_mul, ":side", "$g_custom_armor_angle", 60), #add some more sides
(set_fixed_point_multiplier, 100),
(cur_tableau_clear_override_items),
(cur_tableau_set_override_flags, af_override_weapons),
(init_position, pos2),
(position_rotate_z, pos2, ":side"),
(cur_tableau_set_camera_parameters, 1, 4, 6, 10, 10000),
(init_position, pos5),
(assign, ":cam_height", 105),
# (val_mod, ":camera_distance", 5),
(assign, ":camera_distance", 380),
(assign, ":camera_yaw", -15),
(assign, ":camera_pitch", -18),
(val_clamp, "$g_custom_armor_angle", 0, anim_walk_forward_crouch - anim_walk_backward),
(store_add, ":animation", "$g_custom_armor_angle", "anim_walk_backward"),
(position_set_z, pos5, ":cam_height"),
# camera looks towards -z axis
(position_rotate_x, pos5, -90),
(position_rotate_z, pos5, 180),
# now apply yaw and pitch
(position_rotate_y, pos5, ":camera_yaw"),
(position_rotate_x, pos5, ":camera_pitch"),
(position_move_z, pos5, ":camera_distance", 0),
(position_move_x, pos5, 5, 0),
(try_begin), #shouldn't be necessary, it's already on the troop (player character)
(gt, "$g_current_opened_item_details", -1),
(cur_tableau_add_override_item, "$g_current_opened_item_details"),
(try_end),
(cur_tableau_add_troop, ":troop_no", pos2, ":animation", -1),
(cur_tableau_set_camera_position, pos5),
(copy_position, pos8, pos5),
(position_rotate_x, pos8, -90), #y axis aligned with camera now. z is up
(position_rotate_z, pos8, 30),
(position_rotate_x, pos8, -60),
(cur_tableau_add_sun_light, pos8, 155,155,155),
]),
Code:
(ti_inventory_key_pressed, 0, 0,
[
(game_key_is_down, gk_view_char),
# (set_trigger_result,1),
], [
(get_player_agent_no, ":player_agent"),
(assign, ":end", ek_foot), #should add a global as iterator
(try_for_range, ":item_slot", ek_item_0, ":end"),
(agent_get_item_slot, ":item_no", ":player_agent", ":item_slot"),
(gt, ":item_no", -1),
(item_slot_ge, ":item_no", slot_item_num_components, 1),
(assign, "$g_current_opened_item_details", ":item_no"),
(assign, ":end", -1),
(start_presentation, "prsnt_customize_armor"),
(try_end),
(try_begin), #none found
(eq, ":end", ek_foot),
(display_message, "str_cant_use_inventory_tutorial"),
(assign, "$g_current_opened_item_details", -1),
(try_end),
]),
Code:
("custom_armor_window", 0, "tableau_with_transparency", 1024, 1024, 0, 0, 240, 400,
[
(store_script_param, ":troop_no", 1),
(cur_tableau_set_background_color,0xFF888888),
(cur_tableau_set_ambient_light, 15,14,13),
(set_fixed_point_multiplier, 100),
(cur_tableau_set_camera_parameters, 0, 40, 40, 0, 100000),
(init_position, pos1),
(position_set_z, pos1, 100),
(position_set_x, pos1, -20),
(position_set_y, pos1, -20),
(cur_tableau_add_tableau_mesh, "tableau_custom_armor_color", ":troop_no", pos1, 0, 0),
(position_set_z, pos1, 200),
(cur_tableau_add_tableau_mesh, "tableau_custom_armor_alpha_mask", ":troop_no", pos1, 0, 0),
(position_set_z, pos1, 300),
]),
("custom_armor_alpha_mask", 0, "mat_troop_portrait_mask", 1024, 1024, 0, 0, 400, 400,
[
(store_script_param, ":troop_no", 1),
(cur_tableau_set_background_color, 0x00888888),
(cur_tableau_set_ambient_light,15,14,13),
(cur_tableau_render_as_alpha_mask),
(call_script, "script_add_troop_to_custom_armor_tableau", ":troop_no"),
]),
("custom_armor_color", 0, "mat_troop_portrait_color", 1024, 1024, 0, 0, 400, 400,
[
(store_script_param, ":troop_no", 1),
(cur_tableau_set_background_color, 0xFF888888), #random colour
(cur_tableau_set_ambient_light,15,14,13),
(call_script, "script_add_troop_to_custom_armor_tableau", ":troop_no"),
]),
Narf's Men-at-Arms OSP uploaded here and there
Baraban's platemail OSP uploaded here
Crusader: Way to Expiation uploaded here
Grim Age OSP