Basically, I got tired of repeating this in PMs and I decided to make this thread. Also, it's a nice feature for lots of mods. Asrite, sailing.
First of all, you're going to need to get the ships and ship scenes in game. You can download them here:
http://www.mbrepository.com/file.php?id=1402
Alls you have to do is take the file in Textures (longship.dds) and extract it to your Module's Textures folder. Then take the file from the Resources folder ("sail.brf") and put it in your mod's Resource folder, and then of course put the two files in the SceneObj folder (scn_sea_b and scn_sea_land) into your mod's SceneObj folder. With that done, open up your module.ini and add the following line:
load_mod_resource = sail
And then make sure that "scan_module_textures" is set to 1. (It should be.)
Now to get the ships officially in game just add this to module_scene_props:
That covers the ship props themselves, now let's do the fight scenes themselves. Open up module_scenes and add:
And finally, to help with some bugfinding, add this to module_parties:
With that done the battle scenes and ships are in. Good job. Now the hard part where you have to make a decision...
First, I have a little explaining to do. You must read all this, it's vital. Parties can't move on water, unless they ARE a ship. Problem is, doing a check to see if something is going towards water then setting the icon/party flags to ship is hard to say the least. So what happens instead is you simply retexture a terrain type. I used steppe terrain for mine, because that's something that doesn't have to be used. A bunch of mods could probably use map_desert if there's no deserts, or snow if they don't have any snow around. So take yer pick, get rid of one terrain type. Any water you already have on your map you'll have to replace now, too. This is what you have to choose from:
rt_steppe = 2
rt_plain = 3
rt_snow = 4
rt_desert = 5
rt_river = 8
rt_mountain_forest = 9
rt_steppe_forest = 10
rt_forest = 11
rt_snow_forest = 12
rt_desert_forest = 13
the "rt_" bit is just the python prefix. The number is basically what the game considers it. Take your pick, then what you know what to use, open up your Mount&Blade folder and go into M&B's own Textures folder. (NOT the Textures folder in your mod, the Textures folder in Crogram Files\Mount&Blade (or wherever your MnB is installed.)) Look for the file called "ocean". Right click it and copy it. Then go back to your Mount&Blade folder, and now go into the folder titled "CommonRes". Look for the .BRF file titled "Material". Open it with Thorgrim's BRF editor. Look at all the materials with the prefix "map_" until you find the one you want to replace. When you find it, look at what's written in the "Diffuse" box. Remember that now, and go back to your mod's Textures folder.
When your Textures folder is open, right click and select "paste" to paste the ocean texture. Right click the ocean texture and select "Rename". Rename it what it said in the Diffuse box that I TOLD YOU to memorize. Congratulations, you just retextured a terrain type.
Now with your new "water" terrain ready to go, you can open up your module_game_menus and replace the "encounter_attack" menu with this:
Then do the same for the join_attack menu:
Now we need to add the mission template so that everybody spawns correctly. So open up module_mission_templates and add this:
Now when you're travelling on your water you should have the option to "close in and board the enemy" or to "go ashore". All that's left is adding the trigger that turns you into a ship, so open up module triggers and module_party_templates and add this to the bottom of module_triggers:
And that's all. It should work unless you've done something wrong (if you're getting any errors, you might want to try using this code, credits to thrakkemarn), but if you have any questions I'll be happy to answer them. When you use this, you don't have to ask permission, but please give credit to:
Mirathei, for writing the original ship codes
James for the longship model/texture
Ruthven, for explanation+main code manipulation and basic scenes
MartinF for the trigger that changes you into a ship
Jubal for writing the original sea battle guide
Dudro for extended scene editing
Lumos for bugfix
Hope it works.
-Ruthven
Once upon a version of Mount&Blade a genius coder named Mirathei made some incredible ship codes for his Pirates of Calradia mod. What those did, was it allowed the player to buy a ship from one of the port cities in M&B (a feature that was in Native but left out of the main game) and allowed them to sail around, and even fight enemies on the sea. Using what was basically a modified version of the siege tower codes, the fighting ships (when in battle) would drift towards each other, stop side by side and allow the AI to have a major slashfest. In larger battles, three ships were used. The three ships would usually end up colliding head-on, and the poor AI could almost never reach the enemy. So the player had to jump aboard the other ship and handle it themself. However, the two other ships the player could not get to. As such, the AI would usually end up at a standstill, get out of the boat, walk a little ways over the water (there's a collision mesh attached to the ship that allowed them to do so, otherwise they would drown themselves) and generally the battle would have to be refought, again and again. So I made a method that somewhat fixes it.
First of all, you're going to need to get the ships and ship scenes in game. You can download them here:
http://www.mbrepository.com/file.php?id=1402
Alls you have to do is take the file in Textures (longship.dds) and extract it to your Module's Textures folder. Then take the file from the Resources folder ("sail.brf") and put it in your mod's Resource folder, and then of course put the two files in the SceneObj folder (scn_sea_b and scn_sea_land) into your mod's SceneObj folder. With that done, open up your module.ini and add the following line:
load_mod_resource = sail
And then make sure that "scan_module_textures" is set to 1. (It should be.)
Now to get the ships officially in game just add this to module_scene_props:
Code:
#################################################
#########SEA BATTLES BEGIN
#################################################
("ship",sokf_moveable,"longship","bo_longship", [ (ti_on_scene_prop_init,
[
(set_position_delta,0,0,0),
(store_trigger_param_1, ":instance_no"),
(party_set_slot,"p_ship_colisions",":instance_no",0),
]),]),
("ships_end",0,0,0,[]),
("enemy_ship",sokf_moveable,"longship","bo_longship", [ (ti_on_scene_prop_init,
[
(set_position_delta,0,0,0),
(store_trigger_param_1, ":instance_no"),
(party_set_slot,"p_ship_colisions",":instance_no",0),
]),]),
("enemy_ships_end",0,0,0,[]), #RRRRRAAAAAGGGGGGEEEEE
("universal_end",0,0,0,[]),
("ship_protect",0,0,"bo_gourd_spike",[]),
("enemy_ship_protect",0,0,"bo_gourd_spike",[]),
#################################################
#########SEA BATTLES END
#################################################
That covers the ship props themselves, now let's do the fight scenes themselves. Open up module_scenes and add:
Code:
("sea_b",sf_generate,"none", "none", (0,0),(240,240),-0.5,"0x0000000030000000c00d2348000000008000000000000000",
[],[]),
("sea_land",sf_generate,"none", "none", (0,0),(240,240),-0.5,"0x0000000030000000c00d2348000000008000000000000000",
[],[]),
And finally, to help with some bugfinding, add this to module_parties:
Code:
("burning_buildings","If you see me, report the fire bug.", pf_disabled|icon_village_a|pf_village, no_menu, pt_none, fac_neutral,0,ai_bhvr_hold,0,(0, -50),[], 170),
("ship_colisions","If you see me, report the colision bug.", pf_disabled|icon_village_a|pf_village, no_menu, pt_none, fac_neutral,0,ai_bhvr_hold,0,(0, -50),[], 170),
With that done the battle scenes and ships are in. Good job. Now the hard part where you have to make a decision...
First, I have a little explaining to do. You must read all this, it's vital. Parties can't move on water, unless they ARE a ship. Problem is, doing a check to see if something is going towards water then setting the icon/party flags to ship is hard to say the least. So what happens instead is you simply retexture a terrain type. I used steppe terrain for mine, because that's something that doesn't have to be used. A bunch of mods could probably use map_desert if there's no deserts, or snow if they don't have any snow around. So take yer pick, get rid of one terrain type. Any water you already have on your map you'll have to replace now, too. This is what you have to choose from:
rt_steppe = 2
rt_plain = 3
rt_snow = 4
rt_desert = 5
rt_river = 8
rt_mountain_forest = 9
rt_steppe_forest = 10
rt_forest = 11
rt_snow_forest = 12
rt_desert_forest = 13
the "rt_" bit is just the python prefix. The number is basically what the game considers it. Take your pick, then what you know what to use, open up your Mount&Blade folder and go into M&B's own Textures folder. (NOT the Textures folder in your mod, the Textures folder in Crogram Files\Mount&Blade (or wherever your MnB is installed.)) Look for the file called "ocean". Right click it and copy it. Then go back to your Mount&Blade folder, and now go into the folder titled "CommonRes". Look for the .BRF file titled "Material". Open it with Thorgrim's BRF editor. Look at all the materials with the prefix "map_" until you find the one you want to replace. When you find it, look at what's written in the "Diffuse" box. Remember that now, and go back to your mod's Textures folder.
When your Textures folder is open, right click and select "paste" to paste the ocean texture. Right click the ocean texture and select "Rename". Rename it what it said in the Diffuse box that I TOLD YOU to memorize. Congratulations, you just retextured a terrain type.
Now with your new "water" terrain ready to go, you can open up your module_game_menus and replace the "encounter_attack" menu with this:
The red text is just the name of the terrain that I used for my water, so just replace it with whatever you're using for yours.("encounter_attack",[
(eq, "$encountered_party_friendly", 0),
(store_troop_health,reg(5)),(party_get_current_terrain,":terrain","p_main_party"),
(neq,":terrain",rt_steppe),
],
"Charge the enemy.",[ #Dips are harder than you'd think, the first time you try 'em out
(assign, "$g_battle_result", 0),
(assign, "$g_engaged_enemy", 1),
(call_script, "script_calculate_renown_value"),
(call_script, "script_calculate_battle_advantage"),
(set_battle_advantage, reg0),
(set_party_battle_mode),
(try_begin),
(eq, "$g_encounter_type", enctype_fighting_against_village_raid),
(set_jump_mission,"mt_village_raid"),
(party_get_slot, ":scene_to_use", "$g_encounter_is_in_village", slot_castle_exterior),
(jump_to_scene, ":scene_to_use"),
(else_try), #'Exterior' means 'Outside'. You obviously should go there more often
(eq, "$g_encounter_type", enctype_catched_during_village_raid),
(set_jump_mission,"mt_village_raid"),
(party_get_slot, ":scene_to_use", "$g_encounter_is_in_village", slot_castle_exterior),
(jump_to_scene, ":scene_to_use"),
(else_try),
(set_jump_mission,"mt_lead_charge"),
(call_script, "script_setup_random_scene"),
(try_end),
(assign, "$g_next_menu", "mnu_simple_encounter"),
(jump_to_menu, "mnu_battle_debrief"),
(change_screen_mission),
]),
("encounter_attack",[
(eq, "$encountered_party_friendly", 0),
(store_troop_health,reg(5)),(party_get_current_terrain,":terrain","p_main_party"),
(eq,":terrain",rt_steppe),
],
"Close in and board the enemy.",[
(assign, "$g_battle_result", 0),
(assign, "$g_engaged_enemy", 1),
(call_script, "script_calculate_renown_value"),
(call_script, "script_calculate_battle_advantage"),
(set_battle_advantage, reg0),
(set_party_battle_mode),
(try_begin),
(eq, "$g_encounter_type", enctype_fighting_against_village_raid),
(set_jump_mission,"mt_village_raid"),
(party_get_slot, ":scene_to_use", "$g_encounter_is_in_village", slot_castle_exterior),
(jump_to_scene, ":scene_to_use"),
(else_try),
(eq, "$g_encounter_type", enctype_catched_during_village_raid),
(set_jump_mission,"mt_village_raid"),
(party_get_slot, ":scene_to_use", "$g_encounter_is_in_village", slot_castle_exterior),
(jump_to_scene, ":scene_to_use"),
(else_try),
(set_jump_mission,"mt_ship_battle"),
(try_begin),
(val_add,reg10,reg11),
(gt,reg10,30),
(jump_to_scene, "scn_sea_b"),
(else_try),
(jump_to_scene, "scn_sea_b"),
(end_try),
(try_end),
(assign, "$g_next_menu", "mnu_simple_encounter"),
(jump_to_menu, "mnu_battle_debrief"),
(change_screen_mission),
]),
("encounter_attack",[
(eq, "$encountered_party_friendly", 0),
(store_troop_health,reg(5)),(party_get_current_terrain,":terrain","p_main_party"),
(eq,":terrain",rt_steppe),
],
"Go ashore.",[
(assign, "$g_battle_result", 0),
(assign, "$g_engaged_enemy", 1),
(call_script, "script_calculate_renown_value"),
(call_script, "script_calculate_battle_advantage"),
(set_battle_advantage, reg0),
(set_party_battle_mode),
(try_begin),
(eq, "$g_encounter_type", enctype_fighting_against_village_raid),
(set_jump_mission,"mt_village_raid"),
(party_get_slot, ":scene_to_use", "$g_encounter_is_in_village", slot_castle_exterior),
(jump_to_scene, ":scene_to_use"),
(else_try),
(eq, "$g_encounter_type", enctype_catched_during_village_raid),
(set_jump_mission,"mt_village_raid"),
(party_get_slot, ":scene_to_use", "$g_encounter_is_in_village", slot_castle_exterior),
(jump_to_scene, ":scene_to_use"),
(else_try),
(set_jump_mission,"mt_ship_battle"),
(try_begin),
(val_add,reg10,reg11),
(gt,reg10,30),
(jump_to_scene, "scn_sea_land"),
(else_try),
(jump_to_scene, "scn_sea_land"),
(end_try),
(try_end),
(assign, "$g_next_menu", "mnu_simple_encounter"),
(jump_to_menu, "mnu_battle_debrief"),
(change_screen_mission),
]),
Then do the same for the join_attack menu:
Again replacing the red stuff with your terrain type.("join_attack",[
# (neq, "$encountered_party_hostile", 0),
(neg|troop_is_wounded, "trp_player"),
## (store_troop_health,reg(5),"trp_player"),
## (ge,reg(5),20),
(party_get_current_terrain,":terrain","p_main_party"),
(neq,":terrain",rt_steppe),
],
"Charge the enemy.",[ # < That makes this text red!
(assign, "$g_battle_result", 0),
(call_script, "script_calculate_renown_value"),
(call_script, "script_calculate_battle_advantage"),
(set_battle_advantage, reg0),
(set_party_battle_mode),
(set_jump_mission,"mt_lead_charge"),
(call_script, "script_setup_random_scene"),
(assign, "$g_next_menu", "mnu_join_battle"),
(jump_to_menu, "mnu_battle_debrief"),
(change_screen_mission), #And yeah, I'm quite bored
]),
("join_attack",[
# (neq, "$encountered_party_hostile", 0),
(neg|troop_is_wounded, "trp_player"),
## (store_troop_health,reg(5),"trp_player"),
## (ge,reg(5),20),
(party_get_current_terrain,":terrain","p_main_party"),
(eq,":terrain",rt_steppe),
],
"Engage the enemy.",[
(assign, "$g_battle_result", 0),
(call_script, "script_calculate_renown_value"),
(call_script, "script_calculate_battle_advantage"),
(set_battle_advantage, reg0),
(set_party_battle_mode),
(set_jump_mission,"mt_ship_battle"),
(call_script, "script_setup_random_scene"),
(assign, "$g_next_menu", "mnu_join_battle"),
(jump_to_menu, "mnu_battle_debrief"),
(try_begin),
(val_add,reg10,reg11),
(gt,reg10,30),
(jump_to_scene, "scn_sea_b"),
(else_try),
(jump_to_scene, "scn_sea_b"),
(end_try),
(change_screen_mission),
]),
Now we need to add the mission template so that everybody spawns correctly. So open up module_mission_templates and add this:
Code:
#################################
#SEA BATTLE MISSION TEMPLATE
#################################
(
"ship_battle",mtf_battle_mode,-1,
"You close in and board the enemy ships",
[(0,mtef_attackers|mtef_team_1,af_override_horse,aif_start_alarmed,4,[]),
(1,mtef_attackers|mtef_team_1,af_override_horse,aif_start_alarmed,4,[]),
(2,mtef_attackers|mtef_team_1,af_override_horse,aif_start_alarmed,4,[]),
(10,mtef_defenders|mtef_team_0,af_override_horse,aif_start_alarmed,4,[]),
(11,mtef_defenders|mtef_team_0,af_override_horse,aif_start_alarmed,4,[]),
(12,mtef_defenders|mtef_team_0,af_override_horse,aif_start_alarmed,4,[]),
],
[
(ti_on_agent_spawn, 0, 0, [],
[
(store_trigger_param_1, ":agent_no"),
(call_script, "script_agent_reassign_team", ":agent_no"),
]),
common_battle_tab_press,
(ti_question_answered, 0, 0, [],
[(store_trigger_param_1,":answer"),
(eq,":answer",0),
(assign, "$pin_player_fallen", 0),
(try_begin),
(store_mission_timer_a, ":elapsed_time"),
(gt, ":elapsed_time", 20),
(str_store_string, s5, "str_retreat"),
(call_script, "script_simulate_retreat", 10, 20),
(try_end),
(call_script, "script_count_mission_casualties_from_agents"),
(finish_mission,0),]),
(ti_before_mission_start, 0, 0, [],
[
(team_set_relation, 0, 2, 1),
(team_set_relation, 1, 3, 1),
(call_script, "script_place_player_banner_near_inventory_bms"),
]),
(0, 0, ti_once, [], [(assign,"$battle_won",0),
(assign,"$defender_reinforcement_stage",0),
(assign,"$attacker_reinforcement_stage",0),
(assign,"$g_presentation_battle_active", 0),
(call_script, "script_place_player_banner_near_inventory"),
(call_script, "script_combat_music_set_situation_with_culture"), #Any Ilex is a good Ilex
]),
common_music_situation_update,
common_battle_check_friendly_kills,
(1, 0, 5, [(lt,"$defender_reinforcement_stage",2),
(store_mission_timer_a,":mission_time"),
(ge,":mission_time",10),
(store_normalized_team_count,":num_defenders", 0),
(lt,":num_defenders",6),
# (assign, reg2, ":num_defenders"),
# (display_message,"@num_defenders = {reg2}")
],
[(add_reinforcements_to_entry,0,7),(val_add,"$defender_reinforcement_stage",1)]),
(1, 0, 5, [(lt,"$attacker_reinforcement_stage",2),
(store_mission_timer_a,":mission_time"),
(ge,":mission_time",10),
(store_normalized_team_count,":num_attackers", 1),
(lt,":num_attackers",6),
# (assign, reg2, ":num_attackers"),
# (display_message,"@num_attackers = {reg2}")
],
[(add_reinforcements_to_entry,3,7),(val_add,"$attacker_reinforcement_stage",1)]),
common_battle_check_victory_condition,
common_battle_victory_display,
(1, 4, ti_once, [(main_hero_fallen)],
[
(assign, "$pin_player_fallen", 1),
(str_store_string, s5, "str_retreat"),
(call_script, "script_simulate_retreat", 10, 20),
(assign, "$g_battle_result", -1),
(set_mission_result,-1),
(call_script, "script_count_mission_casualties_from_agents"),
(finish_mission,0)]),
]),
Now when you're travelling on your water you should have the option to "close in and board the enemy" or to "go ashore". All that's left is adding the trigger that turns you into a ship, so open up module triggers and module_party_templates and add this to the bottom of module_triggers:
That covers all the applicable Native party templates, but if you've added any new ones you'll have to define them yourself using (this_or_next|eq,"YOUR PARTY TEMPLATE ID),. Again, replace the red with your terrain type.(0.1, 0, 0, [(party_get_current_terrain,":terrain","p_main_party"),
(neq,":terrain",rt_steppe),], # rt_steppe is an example for the name of your new water terrain
[(try_begin),
(troop_get_inventory_slot, ":cur_horse", "trp_player", , #horse slot
(assign, ":new_icon", -1),
(try_begin),
(eq, "$g_player_icon_state", pis_normal),
(try_begin),
(ge, ":cur_horse", 0),
(assign, ":new_icon", "icon_player_horseman"),
(else_try),
(assign, ":new_icon", "icon_player"),
(try_end),
(else_try),
(eq, "$g_player_icon_state", pis_camping), # All of this is thanks to Lumos bein' generous, and not being as much of a lazy arse as I am
(assign, ":new_icon", "icon_camp"),
(try_end),
]),
(0.1, 0, 0, [(party_get_current_terrain,":terrain","p_main_party"),
(neq,":terrain",rt_steppe),], #Oh shi- ALL HAIL HYPNOTOAD
[(party_set_icon,"p_main_party", "icon_player"),]),
(0.1, 0, 0.0, [],
[(try_for_parties, ":cur_party"),
(party_get_current_terrain, ":terrain", ":cur_party"),
(eq, ":terrain", rt_steppe),
(party_get_template_id, ":cur_template", ":cur_party"),
(this_or_next|eq, ":cur_template", "pt_kingdom_hero_party"),
(this_or_next|eq, ":cur_template", "pt_kingdom_caravan_party"),
(this_or_next|eq, ":cur_template", "pt_manhunters"),
(this_or_next|eq, ":cur_template", "pt_village_farmers"),
(this_or_next|eq, ":cur_template", "pt_deserters"),
(this_or_next|eq, ":cur_template", "pt_looters"),
(this_or_next|eq, ":cur_template", "pt_forest_bandits"),
(this_or_next|eq, ":cur_template", "pt_steppe_bandits"),
(this_or_next|eq, ":cur_template", "pt_mountain_bandits"),
(eq, ":cur_template", "pt_sea_raiders"),
(party_set_icon, ":cur_party", "icon_ship"),
(else_try),
(neq,":terrain",rt_steppe),
(party_get_template_id, ":cur_template", ":cur_party"),
(eq, ":cur_template", "pt_kingdom_hero_party"),
(party_set_icon,":cur_party","icon_flagbearer_a"),
(else_try),
(eq, ":cur_template", "pt_kingdom_caravan_party"),
(party_set_icon,":cur_party","icon_mule"),
(else_try),
(eq, ":cur_template", "pt_deserters"),
(party_set_icon,":cur_party","icon_vaegir_knight"),
(else_try),
(eq, ":cur_template", "pt_manhunters"),
(party_set_icon,":cur_party","icon_gray_knight"),
(else_try),
(eq, ":cur_template", "pt_village_farmers"),
(party_set_icon,":cur_party","icon_peasant"),
(else_try),
(this_or_next|eq, ":cur_template", "pt_looters"),
(this_or_next|eq, ":cur_template", "pt_forest_bandits"),
(this_or_next|eq, ":cur_template", "pt_steppe_bandits"),
(this_or_next|eq, ":cur_template", "pt_mountain_bandits"),
(eq, ":cur_template", "pt_sea_raiders"),
(party_set_icon,":cur_party","icon_axeman"),
(else_try),
(eq, ":cur_template", "pt_cattle_herd"),
(party_set_icon,":cur_party","icon_cattle"),
(try_end),]),
And that's all. It should work unless you've done something wrong (if you're getting any errors, you might want to try using this code, credits to thrakkemarn), but if you have any questions I'll be happy to answer them. When you use this, you don't have to ask permission, but please give credit to:
Mirathei, for writing the original ship codes
James for the longship model/texture
Ruthven, for explanation+main code manipulation and basic scenes
MartinF for the trigger that changes you into a ship
Jubal for writing the original sea battle guide
Dudro for extended scene editing
Lumos for bugfix
Hope it works.
-Ruthven