SP Native Command Cursor minimod - move your troops anywhere with a single keypress!

Users who are viewing this thread

dstemmer

Knight at Arms
Command Cursor minimod by dstemmer

This is a mini-mod that allows easier placement of troops. A player can simply hold down a button to bring up a placement arrow, look at a point on the battlefield, and release the button and their troops will move toward that location. Very simple and intuitive. Does not override any of the default commands. The default key is 'h'. See the following Youtube video for a quick demonstration of how it works:

http://www.youtube.com/watch?v=g3JWX06pgl8

INSTALLATION:
  • Download "Command Cursor minimod" from the M&B repository: http://www.mbrepository.com/modules/PDdownloads/viewcat.php?cid=8. Also available at the megaupload mirror (45 second wait to download) at http://www.megaupload.com/?d=B2HFW5ST
  • Unzip the folder "Command Cursor minimod" into your Mount&Blade modules directory. Choose "Command cursor minimod" from the module list. That's it!

USAGE:

Source code is provided free for anyone to use, no credit needed. I am however curious to know which mods are using it, so if you feel like shooting me a PM I'd appreciate it.

BUGS:

None so far, report any to here or to my PM.

HOW TO CHANGE THE COMMAND CURSOR KEY:

To do this, we're going to change a few source files. It's easy, but you must have Python installed.

  • First make sure you have extracted the Command Cursor Minimod folder in your Mount&Blade directory.

  • Extract the folder command_cursor_source and open up module_info.py with your favorite text editor. Change the following line:

    export_dir = "C:/Program Files/Mount&Blade/Modules/Command Cursor minimod/"


    ...to match the directory of the the "Command Cursor minimod" folder in your Mount&Blade/Modules folder. Make sure you include the slash at the end.

  • Now open up module_constants.py. Scroll all the way down to the bottom and you should see this:

    command_cursor_key = key_h


    Change key_h to whatever you want it to be. A full listing of keys can be found in header_triggers.py.

  • Now click build_module.bat, and assuming you've pointed module_info.py to the right directory, you're done!

HOW TO INTEGRATE THIS INTO YOUR MOD:

  • First, grab the minimod from the repository, find command_cursor.brf in the "resources" folder and copy it into your mod resource folder. This .brf only has one .obj, and it's just an arrow from core_ui_meshes turned 90 degrees on the x-axis. I don't know if loading it imposes any memory overhead. If you really care, you could probably import it into one of your other .brf files. If you don't, just don't worry about it  :wink:
  • Add the line load_module_resource = command_cursor to your module.ini
  • Next, Add the following to the list of scripts in module_scripts.py:

Code:
###command_cursor_minimod_begin###
  # script_cf_shirt_pos1_along_y_axis_to_ground
  # Input: max distance to shift before failing
  # Output: pos1 shifted to ground level along forward y-axis
  ("cf_shift_pos1_along_y_axis_to_ground",
    [
      (store_script_param, ":max_distance", 1),
      (assign, reg0, 1), #output value
      (assign, reg10, ":max_distance"),
      (assign, reg11, 0), #distance so far
      (get_scene_boundaries, pos10, pos11),
      (position_get_x, reg12, pos10),
      (position_get_y, reg13, pos10),
      (position_get_x, reg14, pos11),
      (position_get_y, reg15, pos11),
      (call_script, "script_shift_pos1_along_y_axis_to_ground_aux"),
      (eq, reg0, 1), #if max distance or scene boundaries were reached, fail
      (position_set_z_to_ground_level, pos1),
    ]),
    
  ("shift_pos1_along_y_axis_to_ground_aux", 
    [
      (val_add, reg2, 100),
      (copy_position, pos2, pos1),
      (position_set_z_to_ground_level, pos2),
      (position_get_x, ":pos1_x", pos1),
      (position_get_y, ":pos1_y", pos1),
      (position_get_z, ":pos1_z", pos1),
      (position_get_z, ":ground_z", pos2),
      (try_begin),                           #IF:
        (this_or_next|ge, reg11, reg10),     #distance so far > max_distance OR
        (this_or_next|le, ":pos1_x", reg12), #pos1 x <= scene min x OR
        (this_or_next|le, ":pos1_y", reg13), #pos1 y <= scene min y OR
        (this_or_next|ge, ":pos1_x", reg14), #pos1 x >= scene max x OR
        (ge, ":pos1_y", reg15),              #pos1 y >= scene max y THEN
        (assign, reg0, -1),                  #the outer function fails
      (else_try),
        (lt, ":ground_z", ":pos1_z"),
        (position_move_y, pos1, 100),
        (call_script, "script_shift_pos1_along_y_axis_to_ground_aux"),
      (try_end),
    ]),
###command_cursor_minimod_end###
  • Add the following to the list of particle systems in module_particle_systems.py:
Code:
###command_cursor_minimod_begin###
    ("fat_arrow", psf_always_emit|psf_billboard_2d|psf_global_emit_dir, "arrow_up_rot",
     100, 0.1, 0.0, 0.0, 100.0, 0.0,     #num_particles, life, damping, gravity_strength, turbulance_size, turbulance_strength
     (0.0, 0.7), (1.0, 0.7),          #alpha keys
     (0.0, 0.0), (1.0, 0.0),      #red keys
     (0.0, 1.0), (1.0, 1.0),       #green keys
     (0.0, 0.0), (1.0, 0.0),      #blue keys
     (0, 30),    (1.0, 30.0),        #scale keys
     (0.0, 0.0, 0.0),           #emit box size
     (0.0, 0.0, 0.0),                 #emit velocity
     0.0,                       #emit dir randomness
     0,
     0
    ),
    ("fat_arrow_rising", psf_always_emit|psf_billboard_2d|psf_global_emit_dir, "arrow_up_rot",
     100, 3, 0.0, 0.0, 0.0, 0.0,     #num_particles, life, damping, gravity_strength, turbulance_size, turbulance_strength
     (0.0, 0.7), (1.0, 0.7),          #alpha keys
     (0.25, 0.0), (1, 1.0),      #red keys
     (0.0, 1.0),(0.5, 0.0),       #green keys
     (0.0, 0.0), (1, 0.0),      #blue keys
     (0, 30),   (1.0, 30),        #scale keys
     (0.0, 0.0, 0.0),           #emit box size
     (0.0, 0, 3),                 #emit velocity
     0.0,                       #emit dir randomness
     0,
     0
    ),
###command_cursor_minimod)end###
  • Now add the following to the beginning of module_mission_templates.py, with all the other common triggers (do not put it inside the list of mission templates):
Code:
###command_cursor_minimod_begin###
common_init_command_cursor = (
  ti_before_mission_start, 0, 0, [],
  [
    (assign, "$order_move_to_pos43_start", 0),
  ])

common_command_cursor_key_pressed = (
  0.05, 0, 0,
    [
      (key_is_down, command_cursor_key),
      (get_player_agent_no, ":player"),
      (agent_get_look_position, pos1, ":player"),
      (position_move_z, pos1, 120),
      (try_begin),
        (call_script, "script_cf_shift_pos1_along_y_axis_to_ground", 30000),
        (assign, "$order_move_to_pos43_start", 1),
        (particle_system_burst, "psys_fat_arrow", pos1, 1),
        (copy_position, pos43, pos1),
      (else_try),
        (assign, "$order_move_to_pos43_start", 0),
      (try_end),
    ], []
)

common_order_move_to_pos43 = (
  0, 0, 0,
    [
      (eq, "$order_move_to_pos43_start", 1),
      (neg|key_is_down, command_cursor_key),
      (assign, "$order_move_to_pos43_start", 0),
      (particle_system_burst, "psys_fat_arrow_rising", pos43, 1),
      (get_player_agent_no, ":player"),
      (agent_get_team, ":player_team", ":player"),
      (set_show_messages, 0),
      (try_begin),
        (class_is_listening_order, ":player_team", grc_infantry),
        (class_is_listening_order, ":player_team", grc_archers),
        (class_is_listening_order, ":player_team", grc_cavalry),
        (team_give_order, ":player_team", grc_everyone, mordr_hold),
        (team_set_order_position, ":player_team", grc_everyone, pos43),
        (str_store_string, s1, "@Everyone"),
      (else_try),
        (class_is_listening_order, ":player_team", grc_infantry),
        (team_give_order, ":player_team", grc_infantry, mordr_hold),
        (team_set_order_position, ":player_team", grc_infantry, pos43),
        (str_store_string, s1, "@Infantry"),
      (else_try),
        (class_is_listening_order, ":player_team", grc_archers),
        (team_give_order, ":player_team", grc_archers, mordr_hold),
        (team_set_order_position, ":player_team", grc_archers, pos43),
        (str_store_string, s1, "@Archers"),
      (else_try),
        (team_give_order, ":player_team", grc_cavalry, mordr_hold),
        (team_set_order_position, ":player_team", grc_cavalry, pos43),
        (str_store_string, s1, "@Cavalry"),
      (try_end),
      (set_show_messages, 1),
      (display_message, "@{s1}, move over there!"),
    ], []
)
###command_cursor_minimod_end###
  • Add the following to module_constants.py:
Code:
###command_cursor_minimod_begin###
from module_triggers import *
command_cursor_key = key_h
###command_cursor_minimod_end###
  • At this point you have a choice. If you're lazy like me, you could add the following the the end of module_mission_templates.py, after the last bracket:
Code:
###command_cursor_minimod_begin###
for i in xrange(len(mission_templates)):
  template = list(mission_templates[i])
  template[5] += [common_init_command_cursor, common_command_cursor_key_pressed, common_order_move_to_pos43]
  mission_templates[i] = tuple(template)
###command_cursor_minimod_end###

Note that this adds the command cursor triggers to EVERY mission template, meaning you can use the command cursor in pretty much any situation where a mission is set. Depending on your mod, this may or may not be acceptable. If there are some missions where you're supposed to have allies but not supposed to be able to have any control over them, or something, then you may have to add these three triggers to each mission template where you want them manually and exclude them when necessary.

ALTERNATE VERSION by Highlander:

If you put the following into module_mission_templates instead of the code listed above, you can pop up the arrow by holding down the hold position button for one second. Some people will find this more natural and intuitive (it also makes it easier to remap the key, since you can change it from the game options menu)

Code:
common_init_command_cursor = (
  ti_before_mission_start, 0, 0, [],
  [
    (assign, "$order_move_to_pos43_start", 0),
	  (assign, "$hold_key_1second",0),
  ])

common_command_cursor_countdown = (
  0, 1, 0, 
  [
    (try_begin),
      (neg|game_key_is_down, gk_order_halt),
	    (assign, "$hold_key_1second",0),
    (try_end),
    (game_key_is_down, gk_order_halt),
  ],
  [
    (try_begin),
      (game_key_is_down, gk_order_halt),
	    (assign, "$hold_key_1second",1),
    (try_end),
  ])

  
common_command_cursor_key_pressed = (
  0.05, 0, 0,
    [
      (game_key_is_down, gk_order_halt),
      (eq, "$hold_key_1second",1),
      (get_player_agent_no, ":player"),
      (agent_get_look_position, pos1, ":player"),
      (position_move_z, pos1, 120),
      (try_begin),
        (call_script, "script_cf_shift_pos1_along_y_axis_to_ground", 30000),
        (assign, "$order_move_to_pos43_start", 1),
        (particle_system_burst, "psys_fat_arrow", pos1, 1),
        (copy_position, pos43, pos1),
      (else_try),
        (assign, "$order_move_to_pos43_start", 0),
      (try_end),
    ], []
)

common_order_move_to_pos43 = (
  0, 0, 0,
    [
      (eq, "$order_move_to_pos43_start", 1),
      (neg|game_key_is_down, gk_order_halt),
      (assign, "$order_move_to_pos43_start", 0),
      (particle_system_burst, "psys_fat_arrow_rising", pos43, 1),
      (get_player_agent_no, ":player"),
      (agent_get_team, ":player_team", ":player"),
      (set_show_messages, 0),
      (try_begin),
        (class_is_listening_order, ":player_team", grc_infantry),
        (class_is_listening_order, ":player_team", grc_archers),
        (class_is_listening_order, ":player_team", grc_cavalry),
        (team_give_order, ":player_team", grc_everyone, mordr_hold),
        (team_set_order_position, ":player_team", grc_everyone, pos43),
        (str_store_string, s1, "@Everyone"),
      (else_try),
        (class_is_listening_order, ":player_team", grc_infantry),
        (team_give_order, ":player_team", grc_infantry, mordr_hold),
        (team_set_order_position, ":player_team", grc_infantry, pos43),
        (str_store_string, s1, "@Infantry"),
      (else_try),
        (class_is_listening_order, ":player_team", grc_archers),
        (team_give_order, ":player_team", grc_archers, mordr_hold),
        (team_set_order_position, ":player_team", grc_archers, pos43),
        (str_store_string, s1, "@Archers"),
      (else_try),
        (team_give_order, ":player_team", grc_cavalry, mordr_hold),
        (team_set_order_position, ":player_team", grc_cavalry, pos43),
        (str_store_string, s1, "@Cavalry"),
      (try_end),
      (set_show_messages, 1),
      (display_message, "@{s1}, move over there!"),
    ], []
)


THIS MOD NEEDS YOU!


Help make the placement arrow more interesting! I'm not much of a modeller or graphic designer; If you have an graphic design skills, this could be a job for you! Requirements:

  • The mesh must be 2d, no more than 2 polys; because the arrow is rendered as a particle and the way the particle system works, about 100 particles have to be rendered every .05 seconds to keep it from flickering. Therefore no big 3d objects allowed.
  • The mesh must be loaded into BRFedit; it should be laid flat along the y-axis, with the visible side of the mesh facing up.

Once you've designed the mesh and loaded it into BRFedit, you can implement it in the mod yourself by importing it into command_cursor.brf in the minimod directory and scrolling to the following line in module_animations.py, replacing "arrow_up_rot" with you own mesh name:

("fat_arrow", psf_always_emit|psf_billboard_2d|psf_global_emit_dir, "arrow_up_rot",

Run build_module.bat, and your arrow should be in the game. If you come up with something good, send it to me. If I like it I'll put it in the mod!

(Alternatively, if you don't have the module system, you can just send the .brf to me and I'll try it out myself and tell you what I think)
 
This is excellent! Thumbs up! I always wished to command the troops like that. Will try it later.
 
10hst8j.gif


That's so cool. I usually ride to the place I want my troops to be at, and order 'stand ground' - because it's hard to use the command interface precisely.
 
Gugenot said:
Could you please put a mirror? Cause my computer doesn't wants to open the mbrepository.

Yes, I'll mirror it as soon as I get the chance, thanks for the heads-up.
 
I saw this mod is up for inclusion in Sword of Damocles, and as I posted over there:

YES. YES AND YES. VERY YES. I WANT IT SO BAD. THIS IS EXACTLY AND PRECISELY THE ABILITY THAT I ALWAYS FIND MYSELF WANTING TO HAVE ON THE BATTLEFIELD. WHERE IS THE FELLOW WHO WROTE THIS, THAT I MAY HIGH-FIVE HIM? IMPLEMENT THIS WITH ALL HASTE MY GOOD SIRS. OMG ALLCAPS PIP PIP CHEERIO~

dstemmer, in my eyes you are a titan of modding and champion of everything which kicks ass about Mount & Blade. Please accept your virtual high-five.

highfiveshirt.png
 
Added a request for assistance in designing a new more interesting look for the command arrow, I'm not much of a modeler so mine was ripped right out of core_ui_meshes.brf  :roll:
 
Back
Top Bottom