[Multiplayer] Dropped items retaining tableau meshes

Users who are viewing this thread

Marko͘

Section Moderator
Hello folks, been a few years since I last did stuff for Warband so I'd appreciate some help in what I'm missing here.

My goal: Create a heraldic banner/flag/standard system of flags as items which are carried around, but if dropped they keep the tableau mesh/banner of the player that dropped them. The flags cannot be sheathed, they are dropped upon sheathing.

Current status:
- The flags automatically drop on unsheathing. The flags are correctly placed and rotated when dropped/unsheathed.
- The heraldic status is preserved on item unsheathe
, but not on item dropped.
- There is a multiplayer event but I am not sure I coded it right.

Issues:
- The game crashes if a player picks up a banner dropped by another player.
- The global variable isn't correctly cleared on e.g. team swap if the player is using faction banners instead of a specific one. On first item selection the old faction banner will be displayed, spawning however correctly assigns new tableaus.
- The heraldic status isn't preserved on ti_item_dropped, I can't seem to be able to influence the prop tableau.
- Sometimes dropping a shield also causes it to have applied heraldry which is not intentional and a side effect of bad global var clearing.


Code:
Python:
multiplayer_event_standard = 117

Python:
toc_standard_unwielded = (
  ti_on_item_unwielded, 0.1, 0,
      [(this_or_next|multiplayer_is_server),
      (neg|game_in_multiplayer_mode)],
  [
    (store_trigger_param_1,":agent_no"),
    (store_trigger_param_2,":item_id"),
     
    (try_begin),
      (gt,":item_id",-1),
      (item_slot_eq, ":item_id", slot_item_multiplayer_item_class, multi_item_class_type_standard),
     
      (agent_is_active,":agent_no"),
      (agent_get_position, pos63,":agent_no"),
      (position_move_y, pos63, 30),
      (agent_get_horse, ":agent_horse", ":agent_no"),
      (try_begin),
        (gt, ":agent_horse", -1),
        (position_move_x, pos63, 50),
      (else_try),
        (position_move_z, pos63, 40),
      (try_end),      
      (position_rotate_x, pos63, 90),
      (set_spawn_position, pos63),
      #Marko begin
      (call_script, "script_agent_troop_get_banner_mesh", ":agent_no", 0), #Find player's banner
      (multiplayer_send_int_to_server, multiplayer_event_standard, reg0),
      (spawn_item,":item_id",0,0), #Never remove
      #Marko end
      (assign, ":end_cond", ek_head),
      (try_for_range,":equipment_slot", ek_item_0, ":end_cond"),
        (agent_get_item_slot, ":cur_item_id", ":agent_no", ":equipment_slot"),
        (eq, ":cur_item_id", ":item_id"),
        (val_add,":equipment_slot",1),
        (agent_unequip_item, ":agent_no", ":item_id", ":equipment_slot"),
        (assign,":end_cond",0),
      (try_end),
     
      (agent_unequip_item,":agent_no",":item_id"),
    (try_end),
  ])

toc_standard_dropped = (
  ti_on_item_dropped, 0, 0,
      [(this_or_next|multiplayer_is_server),
      (neg|game_in_multiplayer_mode)],
  [
    (store_trigger_param_2, ":item_id"),
    (try_begin),
      (gt,":item_id",-1),    
      (store_trigger_param_1, ":agent_no"),
      (agent_is_active,":agent_no"),
     
      #Marko begin
      (call_script, "script_agent_troop_get_banner_mesh", ":agent_no", 0), #Find player's banner
      (multiplayer_send_int_to_server, multiplayer_event_standard, reg0),
      #Marko end

      (try_begin),
        (item_slot_eq, ":item_id", slot_item_multiplayer_item_class, multi_item_class_type_standard),
        (store_trigger_param_3, ":dropped_prop"),
        (prop_instance_get_position, pos63, ":dropped_prop"),
        #Marko begin
        (cur_scene_prop_set_tableau_material, "tableau_heraldic_banner", "$standard_tableau"),
        #Marko end
        (agent_get_horse, ":agent_horse", ":agent_no"),
        (try_begin),
          (gt, ":agent_horse", -1),
          (position_move_x, pos63, 50),
        (else_try),
          (position_move_z, pos63, 40),
        (try_end),
        (position_rotate_x, pos63, 90),
        (prop_instance_set_position, ":dropped_prop", pos63),      
    (try_end),
  ])

Module_scripts
First, I set this to 0.
Python:
(assign, "$standard_tableau", 0), #Assign banner mesh to global

The MP event, under ###SERVER EVENTS###
Python:
(else_try),
        (eq, ":event_type", multiplayer_event_standard),
        (store_script_param, ":tableau_mesh", 3),
        #(assign, reg1, ":tableau_mesh"),
        #(display_message, "@multiplayer_event_standard, {reg1}"), #The correct banner is passed via :tableau_mesh - tested
        (try_begin), #If the receiver is server, send mesh to players for updating
          (multiplayer_is_dedicated_server),
          (try_for_players,":i",1),
            (multiplayer_send_int_to_player, ":i", multiplayer_event_standard, ":tableau_mesh"),
          (try_end),
        (else_try), #If the receiver is player, update global variable
          (assign, "$standard_tableau", ":tableau_mesh"), #Assign banner mesh to global variable
            (try_end),

The modified shield_item_set_banner script taking into account the global var state.
Python:
(else_try),  
("shield_item_set_banner",
    [
      (store_script_param, ":tableau_no",1),
      (store_script_param, ":agent_no", 2),
      (store_script_param, ":troop_no", 3),
      (call_script, "script_agent_troop_get_banner_mesh", ":agent_no", ":troop_no"),
#Marko begin
      (try_begin), #Global not overriden
        (eq, "$standard_tableau", 0),
        (cur_item_set_tableau_material, ":tableau_no", reg0),
      (else_try),
        (cur_item_set_tableau_material, ":tableau_no", "$standard_tableau"),
      (try_end),
      (try_begin), #Clear global
        (gt, ":agent_no", -1),
        (assign, "$standard_tableau", 0),
      (try_end),
     ]),
#Marko end

Python:
["heraldic_banner", "Banner", [("heraldic_banner" ,0),("heraldic_banner_inventory", ixmesh_inventory)], itp_type_two_handed_wpn|itp_two_handed|itp_primary|itp_wooden_parry|itp_no_blur, itc_standard,
  140, weight(3)|difficulty(10)|spd_rtng(88)|weapon_length(99)|swing_damage(0,cut)|thrust_damage(0,blunt), imodbits_none, [(ti_on_init_item, [(store_trigger_param_1, ":agent_no"),(store_trigger_param_2, ":troop_no"),(call_script, "script_shield_item_set_banner", "tableau_heraldic_banner", ":agent_no", ":troop_no"),(cur_item_add_mesh, "@heraldic_banner_base", 0, 0),])] ],

So, I know my logic in approaching clearing the global var is bad but I'm not sure how the agent IDs behave and when because when I was testing with some display messages I sometimes found myself picking up an item and both the agent_no > -1 and the agent_no <= -1 firing. Lemme know how bad this is :smile:
 
Last edited:
Solution
Sure the trick was doing things a bit differently in script_multiplayer_buy_agent, everything else from this post is pretty much the same, here's the updated code:

Python:
#finally, add the item to agent
           (try_begin),
             (ge, ":item_id", 0), #might be -1 for horses etc.
             (store_sub, ":item_slot", ":i_item", slot_player_cur_selected_item_indices_begin),

             (try_begin), #Marko begin
                 (item_slot_eq, ":item_id", slot_item_multiplayer_item_class, multi_item_class_type_standard),   #Detect if player has bought heraldic banner
                #Equip new banner
        (player_get_banner_id, ":player_banner", ":player_no"),

        #Detect if kingdom banner
        (try_begin)...
I would create a banner item entry for each single faction and simply assign the respective faction's heraldry at the ti_on_item_init. Then I would give them the flag itp_force_attach_right_hand, so the banner cannot be unsheathed which should also make your multiplayer event unnecessary. If dropped, the banner thus keeps it's heraldic assignment.
 
Upvote 0
I would create a banner item entry for each single faction and simply assign the respective faction's heraldry at the ti_on_item_init. Then I would give them the flag itp_force_attach_right_hand, so the banner cannot be unsheathed which should also make your multiplayer event unnecessary. If dropped, the banner thus keeps it's heraldic assignment.
I tried itp_force_attach_right_hand and it didn't really do anything. The banner was still sheathable. As for creating banner entries, it's not just about faction heraldry - it's about every individual available banner that can be picked in MP. I did however use this idea of yours with item entries so, here's my new system:



New approach

I wanted to try a different approach without mp events and whatnot, and to that end I generated an item for every single banner.

First, I added the module_constants for banner ends and begins. I determined the number empirically via display messages:
Python:
custom_banner_ids_begin = 275
custom_banner_ids_end = 381

kingdom_banner_ids_begin = 270
kingdom_banner_ids_end = custom_banner_ids_begin

slot_player_banner = 275

Below I generate all the items
Python:
for banner_id in range(kingdom_banner_ids_begin, custom_banner_ids_end + 1):
  items += ["heraldic_banner_"+str(banner_id), "Banner", [("heraldic_banner" ,0),("heraldic_banner_inventory", ixmesh_inventory)], itp_type_two_handed_wpn|itp_two_handed|itp_primary|itp_wooden_parry|itp_no_blur, itc_standard,
  140, weight(3)|difficulty(10)|spd_rtng(88)|weapon_length(99)|swing_damage(0,cut)|thrust_damage(0,blunt), imodbits_none, [(ti_on_init_item, [(call_script, "script_standard_item_set_banner", "tableau_heraldic_banner", banner_id),(cur_item_add_mesh, "@heraldic_banner_base", 0, 0),])] ],

As you might've noticed I am using a different script to get their tableau:
Python:
  ("standard_item_set_banner",
    [
      (store_script_param, ":tableau_no",1),
      (store_script_param, ":banner_id", 2),
      (cur_item_set_tableau_material, ":tableau_no", ":banner_id"),
     ]),

So up until this part, everything works perfect. All the items are generated and in the spawn menu I can see they all have correct flags assigned.

Next, I added this bit of code to the end of script_multiplayer_buy_agent_equipment that basically looks whether the player has bought a heraldic banner and swaps it with the one of the many items generated above by comparing player banner and the banner name.
Python:
  ("multiplayer_buy_agent_equipment",
    [
.
.
.

#Marko begin  
   (assign, ":end_cond", ek_head),
   (try_for_range, ":equipment_slot", ek_item_0, ":end_cond"),  #Detect if player has bought heraldic banner
    (get_player_agent_no, ":agent_no"),
    (player_get_slot, ":cur_item_id", ":player_no", ":equipment_slot"),
    (gt, ":cur_item_id",-1),
    (item_slot_eq, ":cur_item_id", slot_item_multiplayer_item_class, multi_item_class_type_standard),
    (display_message, "@The player has bought a heraldic banner"),
    #Remove the current(default) heraldic banner
    (val_add, ":equipment_slot",1),
    (agent_unequip_item, ":player_no", ":cur_item_id", ":equipment_slot"),
    (display_message, "@Old banner unequipped"),
    #Equip new banner
    (call_script, "script_agent_troop_get_banner_mesh", ":agent_no", 0),
    (player_set_slot, ":player_no", slot_player_banner, reg0),
    (player_add_spawn_item, ":player_no", ":equipment_slot", "itm_heraldic_banner_" + str(slot_player_banner) + ""),
    (display_message, "@New banner equipped {reg0}"),
    (assign, ":end_cond",0),
   (try_end),
     ]),

The script above is the current issue, so far it correctly determines if the player has bought a banner and correctly spawns a new one(albeit with a wrong tableau/banner but that's probably some constant issue) but what doesn't work is the agent_unequip_item, I can't get the original default banner to unequip due to constantly getting agent_no as -1.

So yeah I'm probably messing up with retrieving the agent and player nos or something and I'm not sure how to fix it.
 
Last edited:
Upvote 0
I would have expected that this itp flag works, will need to ask around what its purpose is then. If that doesn't work, you will need to work with the multiplayer event, deciding if you either want the player to wield the flag again or to drop it after sheathing it.

I assume that each player can either equip his factions banner or his own colours, can't they? Also not sure if you have a banner bearer troop or if everyone is supposed to be able to select one.
Why are you getting active just at the multiplayer_buy_agent_equipment script and not already at the multiplayer_send_item_selections script. At latter one you should be simply be able to identify the default heraldic flag and switch it with the respective players heraldic flag (or faction flag).

An alternative approach would be to create a banner bearer troop, add the respective faction banner and all heraldic banners to that troop and filter out at the item selection presentation the for the player relevant heraldic flag, preferable via a weapon slot which can only be filled up with heraldic flags.

Both however is done already at the weapon selection itself and not at the point at which the player is already buying his/her weapons.
 
Upvote 0
Sure the trick was doing things a bit differently in script_multiplayer_buy_agent, everything else from this post is pretty much the same, here's the updated code:

Python:
#finally, add the item to agent
           (try_begin),
             (ge, ":item_id", 0), #might be -1 for horses etc.
             (store_sub, ":item_slot", ":i_item", slot_player_cur_selected_item_indices_begin),

             (try_begin), #Marko begin
                 (item_slot_eq, ":item_id", slot_item_multiplayer_item_class, multi_item_class_type_standard),   #Detect if player has bought heraldic banner
                #Equip new banner
        (player_get_banner_id, ":player_banner", ":player_no"),

        #Detect if kingdom banner
        (try_begin),
          (eq, ":player_banner", -1),
          (player_get_team_no,  ":team_no", ":player_no"),
          (team_get_faction, ":player_faction", ":team_no"),        
          (assign, reg2, ":player_faction"),        
          (assign, reg1, kingdom_banner_items_begin),
          (val_sub, ":player_faction", mp_factions_begin),
          (store_add, reg1, reg1, ":player_faction"),
                  (player_add_spawn_item, ":player_no", ":item_slot", reg1),
        (else_try),
          (assign, reg1, custom_banner_items_begin),
          (store_add, reg1, reg1, ":player_banner"),
                  (player_add_spawn_item, ":player_no", ":item_slot", reg1),
        (try_end), #Marko end
               
             (else_try),
                (player_add_spawn_item, ":player_no", ":item_slot", ":item_id"),
        (try_begin),
          (eq, ":item_slot", ek_body), #ek_body is the slot for armor
          (assign, ":armor_bought", 1),
        (try_end),  
             (try_end),            
           (try_end),
         (try_end),

Big thanks to Somebody for pointing out my errors
 
Upvote 0
Solution
Back
Top Bottom