Sea Battles

Started by Jubal, December 14, 2008, 03:31:09 PM

Previous topic - Next topic

Jubal

[size=14]TO SEA![/size]
Implementing Sea Battles in Mount&Blade

THis is a full tutorial, spanning many posts and vast amounts of code, of how to implement Mirathei's sea battle scripts into the game using the modle system. It is accompanied by downloads of BRF files and scenes necessary for it to work. It may be expanded in future to include more information, more ship types and clever stuff, and so on.

Finally, can I say that this is not my work. I've tweaked, I've edited, I've added, but the master craftsman is Mirathei here, and this is to Mirathei's credit not mine.

I haven't had the time to work through the whole tutorial from a vanialla game yet, it may not work 100% or I may have missed some code (can you blame me?) If so, please post explaining your problem fully and I will do my best to work out the problem and edit the tutorial.

BACK UP YOUR FILES BEFORE YOU BEGIN.

Screenies before you start;




The duke, the wanderer, the philosopher, the mariner, the warrior, the strategist, the storyteller, the wizard, the wayfarer...

Jubal

1. Getting you sailing

Module_Game_Menus;
Add this anywhere in the file, allows you to get on a pre-existing boat and get off it again.
(
   "disembark",0,
   "Do you wish to disembark?",
   "none",
   [],
   [
     ("disembark_yes", [], "Yes.",
      [(assign, "$g_player_icon_state", pis_normal),
       (party_set_flags, "p_main_party", pf_is_ship, 0),
       (party_get_position, pos1, "p_main_party"),
       (party_set_position, "p_main_party", pos0),
       (try_begin),
         (le, "$g_main_ship_party", 0),
         (set_spawn_radius, 0),
         (spawn_around_party, "p_main_party", "pt_none"),
         (assign, "$g_main_ship_party", reg0),
         (party_set_flags, "$g_main_ship_party", pf_is_static|pf_always_visible|pf_hide_defenders|pf_is_ship, 1),
         (str_store_troop_name, s1, "trp_player"),
         (party_set_name, "$g_main_ship_party", "@{s1}'s Ship"),
         (party_set_icon, "$g_main_ship_party", "icon_ship"),
         (party_set_slot, "$g_main_ship_party", slot_party_type, spt_ship),
       (try_end),
       (enable_party, "$g_main_ship_party"),
       (party_set_position, "$g_main_ship_party", pos0),
       (party_set_icon, "$g_main_ship_party", "icon_ship_on_land"),
       (assign, "$g_main_ship_party", -1),
       (change_screen_return),
       ]),
     ("disembark_no", [], "No.",
      [(change_screen_return),
       ]),
   ]
 ),

 (
   "ship_reembark",0,
   "Do you wish to embark?",
   "none",
   [],
   [
     ("reembark_yes", [], "Yes.",
      [(assign, "$g_player_icon_state", pis_ship),
       (party_set_flags, "p_main_party", pf_is_ship, 1),
       (party_get_position, pos1, "p_main_party"),
       (map_get_water_position_around_position, pos2, pos1, 6),
       (party_set_position, "p_main_party", pos2),
       (assign, "$g_main_ship_party", "$g_encountered_party"),
       (disable_party, "$g_encountered_party"),
       (change_screen_return),
       ]),
     ("reembark_no", [], "No.",
      [(change_screen_return),
       ]),
   ]
 ),

And add this somewhere within the town menus, allowing you to get a ship;
     ("sail_from_port",[(party_slot_eq,"$current_town",slot_party_type, spt_town),
#                         (party_slot_eq,"$current_town",slot_town_near_shore, 1),
                        ], "Buy a ship (2000 denars)",
      [(assign, "$g_player_icon_state", pis_ship),
       (party_set_flags, "p_main_party", pf_is_ship, 1),
       (troop_remove_gold, "trp_player", 2000),
       (party_get_position, pos1, "p_main_party"),
       (map_get_water_position_around_position, pos2, pos1, 6),
       (party_set_position, "p_main_party", pos2),
       (assign, "$g_main_ship_party", -1),
       (change_screen_return),
       ]),



Now load the system and load the game, You should be able to buy a ship and sail around.
The duke, the wanderer, the philosopher, the mariner, the warrior, the strategist, the storyteller, the wizard, the wayfarer...

Jubal

Part 2.To war!

Firstly, you'll want the actual ship models;
http://www.exilian.110mb.com/files/ships.zip
The brf file needs to go in your mod's resource folder, and the texture in your mod textures folder.

In module_ini.txt, make sure that scan_module_textures=1, and add the following line;

load_mod_resource = sail

Add this to just before the end of module_scene props to get those all-important ship models ingame;

 ("ship",sokf_moveable,"longship","bo_longship", [   (ti_on_scene_prop_init,
   [
    (set_position_delta,0,0,0),
    (particle_system_add_new, "psys_wake"),
    (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),
    (particle_system_add_new, "psys_wake"),
    (store_trigger_param_1, ":instance_no"),
    (party_set_slot,"p_ship_colisions",":instance_no",0),
   ]),]),
 ("enemy_ships_end",0,0,0,[]),
 ("universal_end",0,0,0,[]),

 ("ship_protect",0,0,"bo_gourd_spike",[]),
 ("enemy_ship_protect",0,0,"bo_gourd_spike",[]),


We're going to want some sea to fight on, of course...

add these files to your mod sceneobj folder;
http://www.exilian.110mb.com/files/seascenes.zip
And this to module_scenes;
 ("sea",sf_generate,"none", "none", (0,0),(240,240),-0.5,"0x0000000030000000c00d2348000000008000000000000000",
   [],[]),
 ("sea_2",sf_generate,"none", "none", (0,0),(240,240),-0.5,"0x0000000030000000c00d2348000000008000000000000000",
   [],[]),


Module_Game_menus
Find the menu marked 'encounter_attack' and replace it with the folllowing changed version, this means that when on the sea you will be given the option to enter a sea battle instead of a land fight.
     ("encounter_attack",[
         (eq, "$encountered_party_friendly", 0),
         (store_troop_health,reg(5)),(party_get_current_terrain,":terrain","p_main_party"),
         (neq,":terrain",0),
         ],
                           "Charge 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_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",0),
         ],
                           "Engage 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_2"),
                                 (else_try),
                                   (jump_to_scene, "scn_sea"),
                                 (end_try),
                               (try_end),
                               (assign, "$g_next_menu", "mnu_simple_encounter"),
                               (jump_to_menu, "mnu_battle_debrief"),
                               (change_screen_mission),
                               ]),


And similarly with the join_attack menu;
       ("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",0),
         ],
                           "Charge 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_lead_charge"),
                               (call_script, "script_setup_random_scene"),
                               (assign, "$g_next_menu", "mnu_join_battle"),
                               (jump_to_menu, "mnu_battle_debrief"),
                               (change_screen_mission),
                               ]),

     ("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",0),
         ],
                           "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_2"),
                                 (else_try),
                                   (jump_to_scene, "scn_sea"),
                                 (end_try),
                               (change_screen_mission),
                               ]),



Mission Templates
Now we can actually get these ships moving!
(
   "ship_battle",mtf_battle_mode,-1,
   "You enter battle on your longships.",
   [
    (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,[]),
    ],
   [
     (1, 0, 0, [], [(scene_prop_get_instance,"$ship", "spr_ship", 0),(scene_prop_get_instance,"$eship", "spr_enemy_ship", 0),]),
     
     (10, 0, ti_once, [], [(try_for_agents,":agent"),(agent_set_speed_limit,":agent",0),(end_try),]),
     (ti_tab_pressed, 0, 0, [],
      [
          (try_begin),
            (eq, "$battle_won", 1),
            (finish_mission,0),
          (else_try),
            (call_script, "script_cf_check_enemies_nearby"),
            (question_box,"str_do_you_want_to_retreat"),
          (else_try),
            (display_message,"str_can_not_retreat"),
          (try_end),
       ]),
     
     (0, 0, ti_once, [], [(assign,"$pbehavior",1),(assign,"$ebehavior",2),(assign,"$battle_won",0),]),
     (1, 60, ti_once, [(store_mission_timer_a,reg(1)),(ge,reg(1),10),(all_enemies_defeated,2),(neg|main_hero_fallen,0),(set_mission_result,1),(display_message,"str_msg_battle_won"),(assign,"$battle_won",1),(assign, "$g_battle_result", 1)],
          [(finish_mission,1)]),
     (10, 0, 0, [], [(eq,"$battle_won",1),(display_message,"str_msg_battle_won")]),
     (1, 4, ti_once, [(main_hero_fallen,0)],
         [(set_mission_result,-1),(finish_mission,1)]),


#friendly ship ai
     (0.1, 0, 0, [],
     [(call_script,"script_check_for_collisions"),]),

     (0.1, 0, 0, [],
     [(call_script,"script_kill_overboard"),]),


     (0.05, 0, 0, [],
     [(call_script,"script_cf_keep_troops_on_ships"),]),

#     (0.1, 0, 0, [],
#[(try_for_agents,":agent"),
#     (agent_is_human,":agent"),
 #    (agnet_is_alive,":agent"),


     (1.0, 0, 0, [(eq,"$pbehavior",1)],                  
      [(scene_prop_get_num_instances,reg12, "spr_ship"),
       (val_add,reg12,1),
       (try_for_range,reg13,0,reg12),
          (scene_prop_get_instance,"$ship", "spr_ship", reg13),
          (call_script,"script_select_enemy_ship"),
          (call_script,"script_cf_close_and_board"),
       (end_try),(call_script,"script_check_for_collisions"),]),
         
     
     (1.0, 0, 0, [(eq,"$pbehavior",2),],                  
      [(scene_prop_get_num_instances,reg12, "spr_ship"),
       (val_add,reg12,1),
       (try_for_range,reg13,0,reg12),
          (scene_prop_get_instance,"$ship", "spr_ship", reg13),
          (call_script,"script_select_enemy_ship"),
          (call_script,"script_skirmish"),
       (end_try),(call_script,"script_check_for_collisions"),]),
       


#enemy ship ai



     (10.0, 0, 0, [],
      [(assign,":friends",0),
       (assign,":enemies",0),
       (try_for_agents,":agent"),
          (agent_is_alive,":agent"),
          (try_begin),
             (agent_is_ally,":agent"),
             (val_add,":friends",1),
          (else_try),
             (val_add,":enemies",1),
          (end_try),
       (end_try),
       (try_begin),
          (val_mul,":friends",10),
          (val_div,":friends",":enemies"),
          (gt,":friends",8),
          (assign,"$ebehavior",2),
       (else_try),
          (assign,"$ebehavior",1),
       (end_try)]),      


     (1.0, 0, 0, [(eq,"$ebehavior",1)],
      [(scene_prop_get_num_instances,reg12, "spr_enemy_ship"),
       (val_add,reg12,1),
       (try_for_range,reg13,0,reg12),
          (scene_prop_get_instance,"$ship", "spr_enemy_ship", reg13),
          (call_script,"script_select_friendly_ship"),
          (call_script,"script_cf_close_and_board"),
       (end_try),(call_script,"script_check_for_collisions"),]),
       
     (1.0, 0, 0, [(eq,"$ebehavior",2)],
      [(scene_prop_get_num_instances,reg12, "spr_enemy_ship"),
       (val_add,reg12,1),
       (try_for_range,reg13,0,reg12),
          (scene_prop_get_instance,"$ship", "spr_enemy_ship", reg13),
          (call_script,"script_select_friendly_ship"),
          (call_script,"script_skirmish"),
       (end_try),(call_script,"script_check_for_collisions"),]),


   

 
      (ti_inventory_key_pressed, 0, 0, [(try_begin),
                                           (eq,"$pbehavior",2),
                                           (assign,"$pbehavior",0),
                                           (display_message,"str_hold"),
                                           (scene_prop_get_instance,"$ship", "spr_ship", 0),
                                           (prop_instance_get_position,1,"$ship"),
                                           (prop_instance_animate_to_position,"$ship",1,1),
                                        (else_try),
                                           (val_add,"$pbehavior",1),
                                           (try_begin),
                                              (eq,"$pbehavior",1),
                                              (display_message,"str_board"),
                                           (else_try),
                                              (display_message,"str_skirmish"),
                                           (end_try),
                                        (end_try)], []),

     (ti_question_answered, 0, 0, [],
      [(store_trigger_param_1,":answer"),(eq,":answer",0),(finish_mission,0),]),
 


   ],
 ),
The duke, the wanderer, the philosopher, the mariner, the warrior, the strategist, the storyteller, the wizard, the wayfarer...

Jubal

(part 2 continued)

Scripts

These scripts move and manouevre the ship.
[code]################################################################

#
The duke, the wanderer, the philosopher, the mariner, the warrior, the strategist, the storyteller, the wizard, the wayfarer...

Jubal

3. Someone to fight!
Time we put someone to fight for you on the high seas...

Find this section of mdule contants, adding the bold red line;
num_forest_bandit_spawn_points = 1
num_mountain_bandit_spawn_points = 1
num_steppe_bandit_spawn_points = 1
num_black_khergit_spawn_points = 1
num_sea_raider_spawn_points = 2
[COLOR=red][B]num_sea_pirate_spawn_points = 3[/B][/COLOR]


Add this to module_party_templates;
[code]
The duke, the wanderer, the philosopher, the mariner, the warrior, the strategist, the storyteller, the wizard, the wayfarer...

Jubal

The duke, the wanderer, the philosopher, the mariner, the warrior, the strategist, the storyteller, the wizard, the wayfarer...

Jubal

The duke, the wanderer, the philosopher, the mariner, the warrior, the strategist, the storyteller, the wizard, the wayfarer...

Jubal

Updated already, thanks Ruthven.
The duke, the wanderer, the philosopher, the mariner, the warrior, the strategist, the storyteller, the wizard, the wayfarer...

Jubal

Update #2, thanks again to Ruthven (forgot River Pirates weren't in Native).
The duke, the wanderer, the philosopher, the mariner, the warrior, the strategist, the storyteller, the wizard, the wayfarer...

Guest

I just get this error :/

Initializing...
Compiling all global variables...
Exporting strings...
Exporting skills...
Exporting tracks...
Exporting animations...
Exporting meshes...
Exporting sounds...
Exporting skins...
Exporting map icons...
Exporting faction data...
Exporting item data...
Exporting scene data...
Exporting troops data
Exporting particle data...
Exporting scene props...
Error: Unable to find object:psys_wake
ERROR: Illegal Identifier:psys_wake
Error: Unable to find object:p_ship_colisions
ERROR: Illegal Identifier:p_ship_colisions
Error: Unable to find object:psys_wake
ERROR: Illegal Identifier:psys_wake
Error: Unable to find object:p_ship_colisions
ERROR: Illegal Identifier:p_ship_colisions
Exporting tableau materials data...
Exporting presentations...
Exporting party_template data...
Exporting parties
Exporting quest data...
Exporting scripts...
Exporting mission_template data...
Error: Unable to find object:spr_gourd_spike
ERROR: Illegal Identifier:spr_gourd_spike
Error: Unable to find object:spr_gourd_spike
ERROR: Illegal Identifier:spr_gourd_spike
Error: Unable to find object:spr_gourd_spike
ERROR: Illegal Identifier:spr_gourd_spike
Exporting game menus data...
exporting simple triggers...
exporting triggers...
exporting dialogs...
Checking global variable usages...

______________________________

Script processing has ended.
Press any key to exit. . .

Jubal

You're missing the bit in particle systems an the bit in parties, the last 2 bits of code in section 2.
The duke, the wanderer, the philosopher, the mariner, the warrior, the strategist, the storyteller, the wizard, the wayfarer...

Guest

Okay i try to  build module yet i get  python is a unreadable source so i cant implement sea battles. it cant find .pyc or my mod.

Jubal

THat would indicate a problem with your installation of Python methinks.
The duke, the wanderer, the philosopher, the mariner, the warrior, the strategist, the storyteller, the wizard, the wayfarer...

Silver Wolf

Is it possible to add this into the game as a sea battle ? (9000 triangles)
(The original ship model is not mine... I have just reduced the polycount, modified the ship a bit and made this battle scene with two ships)

"Less of a young professional - more of an ancient amateur. But frankly, I'm an absolute dream."

Darwin

#14
nice model :)