Fate/ Throne of Heroes

Development and Discussion of Fate/ Throne of Heroes, a mod for Warband

Quick Overview

Category
Modding
Language
English (US)
Total members
9
Total events
0
Total discussions
5

Daily Updates (too small for the main thread)

Users who are viewing this thread

Status
Not open for further replies.
First Daily:
Experimentation featuring: @Tocan !
So, one way I keep motivated in this hobby it to do a sort of weekly warm-up task. Something different and weird to strengthen my understanding of the engine and help stave away the burn out.

The ideal warm-up:

  • Features operations or implementations of operations I don't frequently use.
  • Are exciting, weird, or otherwise memorable.
  • Help out a member of the community.
Today's hit all three. I present to you, VATs and Dead Eye presentations.





Obviously they are still super rough, I didn't want to spend too much time or polish on a feature I don't intend to use, but importantly, they work as a proof of concept for more exotic features not yet featured in a Warband mod.

I did this in collaboration with my dear friend, @Tocan, to help add some flair to a WIP project he has going on. Obviously I built them using my mod as a base, but it should be easily translated into any project, and hopefully find themselves a home somewhere.

VATs was built on top of Dead Eye, and had much more time devoted to it because it was easier, so it looks much nicer, but this was a fun bit of work. It's available inside the BitBucket Repository inside /Throne of Heroes/module_presentations.py, where you can also dig and find the scripts referenced, or below, with none of the stuff you need, but the factor of convenience.

Python:
    ("vats", prsntf_manual_end_only, 0, [
    (ti_on_presentation_load,
     [
        (set_fixed_point_multiplier, 100),

        (presentation_set_duration, 99999999999),
      
        (get_player_agent_no, ":agent"),
      
        (agent_get_position, pos12, ":agent"),
      
        (assign, "$vats_bone", 0),
        (assign, "$nearest_agent_01", 0),
        (assign, ":nearest_distance", 99999999),
      
        (try_for_agents, ":enemies", pos12, 5000),
            (agent_is_alive, ":enemies"),
            (agent_is_human, ":enemies"),
            (neq, ":enemies", ":agent"),
          
            (agent_get_position, pos2, ":enemies"),
            (get_distance_between_positions, ":distance", pos12, pos2),
          
            # (copy_position, pos3, pos2),
            # (copy_position, pos4, pos12),
          
            # (position_move_z, pos3, 100),
            # (position_move_z, pos4, 100),


            # (call_script, "script_lookat", pos4, pos3),    # This makes the cursor screen position face the cursor projection position
            # (cast_ray, reg1, pos30, pos4),                    # Take pos30, facing pos31, and make a ray to see what is in the way (what we clicked)

            # (le, reg1, 0),
          
            (le, ":distance", ":nearest_distance"),
            (assign, ":nearest_distance", ":distance"),
          
            (assign, "$nearest_agent_01", ":enemies"),
        (try_end),
      
        (try_begin),
            (eq, "$nearest_agent_01", 0),
            (presentation_set_duration, 0),
            (display_message, "@V.A.T.S failed to find a target.", 0x22AA22),
        (try_end),
      
        (neq, "$nearest_agent_01", 0),
      
        (call_script, "script_rts_camera"),
      
        (set_fixed_point_multiplier, 1000),
      
        (position_set_y, pos10, 100),
        (position_set_x, pos10, 120),
  
        (create_text_overlay, reg1, "@VATS", tf_with_outline),
        (overlay_set_color, reg1, 0x22AA22),
        (overlay_set_position, reg1, pos10),
      
        (position_set_y, pos10, 125),

        (create_text_overlay, "$vats_bone_display", "@Bone Selected: Abdomen", tf_with_outline),
        (overlay_set_color, "$vats_bone_display", 0x22AA22),
        (overlay_set_position, "$vats_bone_display", pos10),
      
        (str_store_agent_name, s10, "$nearest_agent_01"),
      
        (position_set_y, pos10, 150),

        (create_text_overlay, "$vats_agent_display", "@Agent Selected: {s10}", tf_with_outline),
        (overlay_set_color, "$vats_agent_display", 0x22AA22),
        (overlay_set_position, "$vats_agent_display", pos10),
      
        (agent_get_position, pos2, "$nearest_agent_01"),
        (position_move_z, pos2, 250),
      
        (particle_system_burst, "psys_laser", pos2, 100),
      
        (agent_set_animation, ":agent", "anim_rts_hold"),
        (mission_time_speed_move_to_value, 10, 250),
      ]),
      
    (ti_on_presentation_run,
     [
        (get_player_agent_no, ":player"),

        (set_fixed_point_multiplier, 100),
      
        (init_position, pos0),    # I need a position at origin to check if cast_ray succeeded later
        # You see, if cast_ray fails to hit a target, the returned position is world origin, so if the distance between
        # cast_ray's return position and this initialized position is 0, I know not to do anything else.
      
        (mouse_get_world_projection, pos30, pos31),     
        # So, this is used when we click to figure out where the cursor would be in world space.
        # Returns current camera coordinates (pos30) and mouse projection to the back of the world (pos31)
        # Unfortunately, you cannot use these as is to determine where you clicked, but it does draw a straight line
        # from the cursor to the rear of the world, so if you were to use look at to point 30 to 31, you get a perfections
        # caster for cast_ray, which will return where in 3d space the cursor actually was.
      
        (mouse_get_position, pos15),        # This gets the Mouse Position in 2D screen space. I use this for camera control.
        (position_get_x, ":mx", pos15),        # Store Mouse Position: (X, Y)
        (position_get_y, ":my", pos15),
      
        (mission_cam_get_position, pos20),                            # Get the Camera Position so we can alter it and set it again later
        (position_get_rotation_around_x, ":x_rot_orig", pos20),        # I am not good with maths, so instead of figuring out how I would
        (store_mul, ":x_rot", ":x_rot_orig", -1),                    # rotate the camera on the x-axis correctly, so I basically remove
        (position_rotate_x, pos20, ":x_rot"),                        # the rotation and restore it later. Without this step, it gets weird.
      
        (assign, ":cam_move_speed", 5),                                # Sets cam movement speed in one place so I can test quickly
      
        (try_begin),
            (this_or_next|key_is_down, key_left_control),            # If the ctrl key is down,
            (key_is_down, key_right_control),                        # either of them,
            (val_mul, ":cam_move_speed", 2),                        # Multiply the speed by 2
        (try_end),
      
        (store_mul, ":neg_movement", ":cam_move_speed", -1),        # Sets reverse speed, based on forward speed
      
        # Forward and Backward Cam Motion Block (Dolly)
        (try_begin),
            (key_is_down, key_w),
            (position_move_y, pos20, ":cam_move_speed"),
        (else_try),
            (key_is_down, key_s),
            (position_move_y, pos20, ":neg_movement"),
        (try_end),
      
        # Right and Left Cam Motion Block (Pan)
        (try_begin),
            (key_is_down, key_d),
            (position_move_x, pos20, ":cam_move_speed"),
        (else_try),
            (key_is_down, key_a),
            (position_move_x, pos20, ":neg_movement"),
        (try_end),
      
        # cast_ray block, since it's expensive, only use it when necessary
        (try_begin),
            (this_or_next|key_clicked, key_left_mouse_button),
            (key_is_down, key_left_mouse_button),
          
            (init_position, pos3),
          
            (call_script, "script_lookat", pos30, pos31),    # This makes the cursor screen position face the cursor projection position
            (cast_ray, reg1, pos3, pos30),                    # Take pos30, facing pos31, and make a ray to see what is in the way (what we clicked)
            (position_copy_rotation, pos3, pos0),            # This was originally for my prop spawning
        (try_end),
      
        (try_begin),
            (key_clicked, key_left),
          
        (else_try),
            (key_clicked, key_right),
          
        (else_try),
            (key_clicked, key_up),
            (val_sub, "$vats_bone", 1),
          
            (try_begin),
                (lt, "$vats_bone", 0),
                (assign, "$vats_bone", 18),
            (try_end),
          
            (store_add, ":bone", "str_bone_abdomen", "$vats_bone"),
            (str_store_string, s10, ":bone"),
          
            (overlay_set_text, "$vats_bone_display", "@Bone Selected: {s10}"),
        (else_try),
            (key_clicked, key_down),
            (val_add, "$vats_bone", 1),
          
            (try_begin),
                (ge, "$vats_bone", 19),
                (assign, "$vats_bone", 0),
            (try_end),
          
            (store_add, ":bone", "str_bone_abdomen", "$vats_bone"),
            (str_store_string, s10, ":bone"),
            (overlay_set_text, "$vats_bone_display", "@Bone Selected: {s10}"),
        (else_try),
            (key_clicked, key_enter),
            (agent_get_position, pos40, ":player"),
            (position_move_z, pos40, 150),
            (position_move_y, pos40, 50),
          
            (agent_get_bone_position, pos41, "$nearest_agent_01", "$vats_bone", 1),
            (call_script, "script_lookat", pos40, pos41),
            (add_missile, ":player", pos40, 7600, "itm_vats_ammo", 0, "itm_vats_ammo", 0),
        (try_end),
      
      
      
        # Set Original Mouse Position on Click, so relative mouse position camera movements work.
        (try_begin),
            (this_or_next|key_clicked, key_middle_mouse_button),
            (key_clicked, key_right_mouse_button),
            (assign, "$g_fate_starting_mouse_pos_x", ":mx"),
            (assign, "$g_fate_starting_mouse_pos_y", ":my"),
        (try_end),
      
        (store_sub, ":mmx", "$g_fate_starting_mouse_pos_x", ":mx"),    # Original Pos - Current Pos, horizontal
        (store_sub, ":mmy", "$g_fate_starting_mouse_pos_y", ":my"), # Original Pos - Current Pos, vertical
      
        (try_begin),
            (key_is_down, key_middle_mouse_button),
          
            (try_begin),
                (lt, ":mmx", -5),
                (val_mul, ":mmx", -1),
                (position_move_x, pos20, ":mmx"),
            (else_try),
                (gt, ":mmx", 5),
                (val_mul, ":mmx", -1),
                (position_move_x, pos20, ":mmx"),
            (try_end),
          
            (try_begin),
                (lt, ":mmy", -5),
                (val_mul, ":mmy", -1),
                (position_move_z, pos20, ":mmy"),
            (else_try),
                (gt, ":mmy", 5),
                (val_mul, ":mmy", -1),
                (position_move_z, pos20, ":mmy"),
            (try_end),
          
            (position_get_distance_to_ground_level, ":altitude", pos20),
            (try_begin),
                (lt, ":altitude", 150),
                (store_sub, ":change", 150, ":altitude"),
                (position_move_z, pos20, ":change"),
            (else_try),
                (gt, ":altitude", 1500),
                (store_sub, ":change", 1500, ":altitude"),
                (position_move_z, pos20, ":change"),
            (try_end),
          
          
          
        (else_try),
            (key_is_down, key_right_mouse_button),
          
            (try_begin),
                (lt, ":mmx", -10),
                (position_rotate_z, pos20, -1),
            (else_try),
                (gt, ":mmx", 10),
                (position_rotate_z, pos20, 1),
            (try_end),
          
            (try_begin),
                (lt, ":mmy", -10),
                (position_rotate_x, pos20, 1),
            (else_try),
                (gt, ":mmy", 10),
                (position_rotate_x, pos20, -1),
            (try_end),
          
        (try_end),
      
        (position_rotate_x, pos20, ":x_rot_orig"),

        (mission_cam_set_position, pos20),
      
        (this_or_next|key_clicked, key_escape),
        (this_or_next|key_clicked, key_tilde),
        (this_or_next|key_clicked, key_numpad_5),
        (key_clicked, key_tab),
      
        (presentation_set_duration, 0),
        (mission_cam_set_mode, 0, 500, 0),
        (agent_set_animation, ":player", "anim_rts_break"),
        (mission_time_speed_move_to_value, 100, 250),
      ]),
    ]),
  
  ]

Python:
    ("deadeye", prsntf_manual_end_only, 0, [
    (ti_on_presentation_load,
     [
        (call_script, "script_rts_camera"),
        (set_fixed_point_multiplier, 1000),
      
        (position_set_y, pos10, 600),
        (position_set_x, pos10, 400),
      
  
        (create_text_overlay, reg1, "@Camera Controls"),
        (overlay_set_color, reg1, 0xAA22AA),
        (overlay_set_position, reg1, pos10),
      

        (presentation_set_duration, 99999999999),
      
        (get_player_agent_no, ":agent"),
      
        (agent_set_animation, ":agent", "anim_rts_hold"),
        (mission_time_speed_move_to_value, 10, 250),
      ]),
      
    (ti_on_presentation_run,
     [
        (get_player_agent_no, ":player"),

        (set_fixed_point_multiplier, 100),
      
        (init_position, pos0),    # I need a position at origin to check if cast_ray succeeded later
        # You see, if cast_ray fails to hit a target, the returned position is world origin, so if the distance between
        # cast_ray's return position and this initialized position is 0, I know not to do anything else.
      
        (mouse_get_world_projection, pos30, pos31),     
        # So, this is used when we click to figure out where the cursor would be in world space.
        # Returns current camera coordinates (pos30) and mouse projection to the back of the world (pos31)
        # Unfortunately, you cannot use these as is to determine where you clicked, but it does draw a straight line
        # from the cursor to the rear of the world, so if you were to use look at to point 30 to 31, you get a perfections
        # caster for cast_ray, which will return where in 3d space the cursor actually was.
      
        (mouse_get_position, pos15),        # This gets the Mouse Position in 2D screen space. I use this for camera control.
        (position_get_x, ":mx", pos15),        # Store Mouse Position: (X, Y)
        (position_get_y, ":my", pos15),
      
        (mission_cam_get_position, pos20),                            # Get the Camera Position so we can alter it and set it again later
        (position_get_rotation_around_x, ":x_rot_orig", pos20),        # I am not good with maths, so instead of figuring out how I would
        (store_mul, ":x_rot", ":x_rot_orig", -1),                    # rotate the camera on the x-axis correctly, so I basically remove
        (position_rotate_x, pos20, ":x_rot"),                        # the rotation and restore it later. Without this step, it gets weird.
      
        (assign, ":cam_move_speed", 5),                                # Sets cam movement speed in one place so I can test quickly
      
        (try_begin),
            (this_or_next|key_is_down, key_left_control),            # If the ctrl key is down,
            (key_is_down, key_right_control),                        # either of them,
            (val_mul, ":cam_move_speed", 2),                        # Multiply the speed by 2
        (try_end),
      
        (store_mul, ":neg_movement", ":cam_move_speed", -1),        # Sets reverse speed, based on forward speed
      
        # Forward and Backward Cam Motion Block (Dolly)
        (try_begin),
            (key_is_down, key_w),
            (position_move_y, pos20, ":cam_move_speed"),
        (else_try),
            (key_is_down, key_s),
            (position_move_y, pos20, ":neg_movement"),
        (try_end),
      
        # Right and Left Cam Motion Block (Pan)
        (try_begin),
            (key_is_down, key_d),
            (position_move_x, pos20, ":cam_move_speed"),
        (else_try),
            (key_is_down, key_a),
            (position_move_x, pos20, ":neg_movement"),
        (try_end),
      
        # cast_ray block, since it's expensive, only use it when necessary
        (try_begin),
            (this_or_next|key_clicked, key_left_mouse_button),
            (key_is_down, key_left_mouse_button),
          
            (init_position, pos3),
          
            (call_script, "script_lookat", pos30, pos31),    # This makes the cursor screen position face the cursor projection position
            (cast_ray, reg1, pos3, pos30),                    # Take pos30, facing pos31, and make a ray to see what is in the way (what we clicked)
            (position_copy_rotation, pos3, pos0),            # This was originally for my prop spawning
        (try_end),
      
        (try_begin),
            (key_clicked, key_left_mouse_button),
          
            (init_position, pos12),
          
            (get_distance_between_positions_in_meters, ":distance", pos12, pos3),    # Check if the ray collision is at origin, catches where the ray goes too far to work
            (gt, ":distance", 0),
            (get_distance_between_positions_in_meters, ":distance", pos30, pos3),    #
            (val_add, ":distance", 1),
          
            (try_for_range, ":iteration", 0, ":distance"),
                (copy_position, pos12, pos30),
                (val_mul, ":iteration", 100),
                (position_move_y, pos12, ":iteration"),
                (try_for_agents, ":agent", pos12, 180),
                    (neq, ":agent", ":player"),
                    (str_store_agent_name, s10, ":agent"),
                    (display_message, "@This seems to indicate the ray hit an agent named {s10}"),
                    (try_for_range, ":bone", 0, 19),
                        (agent_get_bone_position, pos13, ":agent", ":bone", 1),
                        (position_transform_position_to_local, pos14, pos30, pos13),
                        (position_get_x, reg12, pos14),
                        (position_get_y, reg13, pos14),
                        (position_get_z, reg14, pos14),
                        (is_between, reg12, -15, 16),
                        (is_between, reg14, -15, 16),
                        (val_add, ":bone", "str_bone_abdomen"),
                        (str_store_string, s11, ":bone"),
                        (display_message, "@At Bone {s11}"),                #
                        (particle_system_burst, "psys_laser", pos13, 75),     # I just use this to have a visualization. It's a little small, so it doesn't make a good visual
                    (try_end),
                (try_end),
            (try_end),
          
        (try_end),
      
      
      
        # Set Original Mouse Position on Click, so relative mouse position camera movements work.
        (try_begin),
            (this_or_next|key_clicked, key_middle_mouse_button),
            (key_clicked, key_right_mouse_button),
            (assign, "$g_fate_starting_mouse_pos_x", ":mx"),
            (assign, "$g_fate_starting_mouse_pos_y", ":my"),
        (try_end),
      
        (store_sub, ":mmx", "$g_fate_starting_mouse_pos_x", ":mx"),    # Original Pos - Current Pos, horizontal
        (store_sub, ":mmy", "$g_fate_starting_mouse_pos_y", ":my"), # Original Pos - Current Pos, vertical
      
        (try_begin),
            (key_is_down, key_middle_mouse_button),
          
            (try_begin),
                (lt, ":mmx", -5),
                (val_mul, ":mmx", -1),
                (position_move_x, pos20, ":mmx"),
            (else_try),
                (gt, ":mmx", 5),
                (val_mul, ":mmx", -1),
                (position_move_x, pos20, ":mmx"),
            (try_end),
          
            (try_begin),
                (lt, ":mmy", -5),
                (val_mul, ":mmy", -1),
                (position_move_z, pos20, ":mmy"),
            (else_try),
                (gt, ":mmy", 5),
                (val_mul, ":mmy", -1),
                (position_move_z, pos20, ":mmy"),
            (try_end),
          
            (position_get_distance_to_ground_level, ":altitude", pos20),
            (try_begin),
                (lt, ":altitude", 150),
                (store_sub, ":change", 150, ":altitude"),
                (position_move_z, pos20, ":change"),
            (else_try),
                (gt, ":altitude", 1500),
                (store_sub, ":change", 1500, ":altitude"),
                (position_move_z, pos20, ":change"),
            (try_end),
          
          
          
        (else_try),
            (key_is_down, key_right_mouse_button),
          
            (try_begin),
                (lt, ":mmx", -10),
                (position_rotate_z, pos20, -1),
            (else_try),
                (gt, ":mmx", 10),
                (position_rotate_z, pos20, 1),
            (try_end),
          
            (try_begin),
                (lt, ":mmy", -10),
                (position_rotate_x, pos20, 1),
            (else_try),
                (gt, ":mmy", 10),
                (position_rotate_x, pos20, -1),
            (try_end),
          
        (try_end),
      
        (position_rotate_x, pos20, ":x_rot_orig"),

        (mission_cam_set_position, pos20),
      
        (this_or_next|key_clicked, key_escape),
        (this_or_next|key_clicked, key_tilde),
        (this_or_next|key_clicked, key_numpad_5),
        (key_clicked, key_tab),
      
        (presentation_set_duration, 0),
        (mission_cam_set_mode, 0, 500, 0),
        (agent_set_animation, ":player", "anim_rts_break"),
        (mission_time_speed_move_to_value, 100, 250),
      ]),
    ]),
 
Last edited:
A really dumb, small one today.

I was working on a small presentation to tell you where you are in the world as well as the date and time.

To make sure it works in quick battles, I loaded a random battle with Illya, not realizing she used a spell that has new visual screen shaking effect. It felt surprisingly pretty great actually, so I loaded into a battle to record.



This is of course so much of a work in progress that you can literally see me modify the map mid battle.

The red stars are an indicator of wounds, and occasionally you see Illya stomp and throw her arms down due to a debuff state inflicted from Gandr being a curse. The red sphere I am casting is just an indicator for range of effect for my incineration spell, which inflicts a burning status for a few seconds.

Also, towards the end the shake gets more extreme as you also get a slight screen shake relative to damage to the chest and head, and she was laying out some mean headshots.
 
Last edited:
Prologue Chapter, Scripted Introduction


In preparation of the Prologue release, I have been putting in a lot of time into the scripted introductory sequence. This entire playlist was taken for illustrative purposes for my good friend @Tocan, so any lack of context can be explained by the fact we on discord were talking about our respective introductory scenes.

 
Status
Not open for further replies.
Back
Top Bottom