OSP Code MP Poll and admin enhancements for mod creators (updated for 1.126)

Users who are viewing this thread

I have wanted a some changes to the poll system for for a while now, and I was hoping they would be added to the official game. Unfortunately the requests on the bug tracker were all denied, so decided to have a go myself with the module system; and along the way I added a few other ideas from around the forums that seemed good. It is released freely for anyone to use in their mod; and I don't care much about whether I get credited or not, as long as nobody falsely credits themselves or someone else for it.

Features:
  • The name of the player who requested the poll is shown on it to everyone; so people who abuse the system can be detected and dealt with, or at least polls by known griefers can be easily ignored.
  • Ban and kick polls called by players include one of a list of preset reasons; so if a kick poll is called by dead spectating players, the other players still alive can more easily know how to vote; and to make it easy for players to call a vote for a common reason with the minimum of distraction to other players. More reasons can easily be added to the list (or changed) by simply adding lines to module_strings.py, in between "poll_reason_team_killing" and "poll_reason_end". Polls to ban or kick admins will not be started, since they would have no effect anyway.
  • Change poll voting keys from 1 and 2 to F8 and F9; this is because I have weapon selection controls on the first 4 number keys, which has caused me to accidentally vote yes on ban polls, and caused me to get shot after pressing 2 to vote no, which also puts away my shield. I chose F8 and F9 because they are far away from the wasd keys, not used for any of the controls, so require an intentional movement to press them. The keys can be changed in the script very easily.
  • The escape key does not vote no for the poll any more, so you can change equipment or close windows without needing to vote first. I added the F10 key as "Abstain" which will hide the poll and give the same result as ignoring it until the timer runs out, in case you have no opinion either way. Unfortunately bringing up the log window to see chat history seems to hide the poll presentation, forcing you to abstain; I don't know if there is a way to change this.
  • Admin votes on a poll override the result right away, so they can cancel bad polls or force their own poll to be accepted. The admin will be warned and must confirm their vote, except when abstaining. Possibly it should be replaced with a dedicated "override poll" feature; but I quite like the side effect that admins can start a poll and immediately accept it, for example to change the bot numbers without using the admin panel and restarting the map.
  • Admins can start polls repeatedly, without having to wait a certain amount of time in between like other players.
  • Admins can send a message to a particular player or to all players, which shows up in big red letters in the middle of the screen, whether alive, dead, or spectating. This could be used for unambiguously warning players that are breaking rules, or to inform all players of something you require them to do. Unfortunately the text box for your message is not automatically selected, you have to click on it before typing. To bring up the text entry box to send a message to the last player selected in the list, press F11.
  • Admins can choose to ban players temporarily, like a successful ban poll, rather than adding them to the permanently banned list. The time a temporary ban lasts seems to be an hour, from my testing, rather than the half hour of popular rumour.
  • Admins can start a poll with a custom question that has no effect when it ends; this can be used to administrate the server according to the player vote.
  • Changed the visual style of the poll presentation to be smaller and use outlined text; this was partly so the larger messages due to some of the above features would fit, and partly to try make it less obstructive. I wanted to somehow allow the name of the player being spectated to be seen while still deciding how to vote, but unfortunately that seems to be hardcoded, and I didn't find a way to detect the player being followed using the module system. I wondered about shifting the poll message somewhere else, like the top of the screen, but I decided that would be even more obnoxious. I also fixed the layout in the root escape menu to scroll properly and line up the text with the other menus.
  • Some of these features require you to select a player from the list, then choose some other action like click a reason or type a message; if the player disconnects the command will fail with an error message - even if they have reconnected meanwhile - as the server seems to assign to joining clients a random number from the available slots. This is to guard against innocent players being targeted unintentionally - it might seem far fetched, but if the server is full, the target of a ban command leaves, and a waiting player joins... oops.

To test it out: PollMod.zip
If people just want to try the features out with the Native module, just make a copy of of the directory named something like PollMod, then extract the above zip file and move the contained files into it. Bear in mind that when you start a server from in game ("Host a game" in MP menu) your player ID will be 0, i.e. the server, which will not show up in the player lists for messaging, kicks, or bans; and also that admin players can't be kicked or banned. You can test those parts by copying the dedicated server executable to the main warband install, along with a pair of config and .bat files; follow the instructions in the config file to set it up, then run the server (selecting this mod), start the game, and choose LAN from the drop down in the server selection screen; this way your player will show up in the lists. In unmodified native 1.126 there seems to be a bug: when a player is kicked or banned, they will not automatically leave the server with a message saying they were kicked - as it was in 1.113 - their client will stay connected, but won't be able to do much apart from sliding around slowly, as if the connection was lost.

Unfortunately none of the features are compatible with Native except the poll layout and keys changes, though (it seems) things should work fine when applied to a client as long as you don't use the new admin features - the poll parts were designed to still work. My original idea was to make a small tweak that, when applied to the server and your client, would show the name of the player that started the poll: this works apart from with the "change map and factions" poll (the most common one), which uses up all the available network message values, requiring two values to be packed into one integer, breaking compatibility with unpatched clients. The code has been tested on a dedicated server with two local clients connected, though there are probably still unfixed bugs.

To apply the changes to your mod:
I don't know much about how most mod creators work with the code - whether this is a useful way to release it or not; but I decided to post on the forums so it should be easily accessible for as long as the game is played. I personally use a local git repository to save my various tweaks and merge them with new versions easily, so I can push to a git mirror if anyone else uses that; another option is a set of "diffs", if someone wants to use some sort of "patch" utility to attempt automatic patching, like in TortoiseSVN. Instructions in the format that seems usual on these forums follow in the next posts; don't worry about how long it took, as it's just the output of git diff run through a perl script.

Open each file listed on the spoiler buttons, go to the line numbers on the purple lines starting and ending with @@ (the first number is the line in the original version, second is the line it will end up on), find the uncoloured lines of context, then remove any red lines, adding the adjacent blue ones in their place. Obviously if you have added more constants, adjust the values of any that conflict to be unique; also if your mod makes any use of the string registers s36 and s37, you should change all occurrences in this code to another two unused registers, to prevent the chance of people getting strange admin messages or custom polls. I couldn't figure out any other better way (that worked) to store strings between script invocations.
 
@@ -52,6 +52,10 @@
multiplayer_event_admin_set_anti_cheat                        = 44
multiplayer_event_open_game_rules                            = 45

multiplayer_event_admin_message_set_player                    = 46
multiplayer_event_admin_message_to_player                    = 47
multiplayer_event_admin_set_poll_question                    = 48

#server events
multiplayer_event_return_max_num_players                      = 50
multiplayer_event_return_num_bots_in_team                    = 51
@@ -112,6 +116,10 @@
multiplayer_event_return_max_num_bots                        = 106
multiplayer_event_return_server_mission_timer_while_player_joined = 107

multiplayer_event_admin_message_set_admin                    = 108
multiplayer_event_admin_message                              = 109
multiplayer_event_return_admin_poll_question                  = 110

#multiplayer message types
multiplayer_message_type_auto_team_balance_done      = 2
multiplayer_message_type_auto_team_balance_next      = 3
@@ -967,6 +967,7 @@
slot_player_bot_type_4_wanted                  = 38
slot_player_spawn_count                        = 39

slot_player_admin_message_target              = 80

########################################################
##  TEAM SLOTS            #############################
@@ -1413,6 +1414,9 @@
multiplayer_game_type_names_begin = "str_multi_game_type_1"
multiplayer_game_type_names_end = "str_multi_game_types_end"

multiplayer_poll_reasons_begin = "str_poll_reason_team_killing"
multiplayer_poll_reasons_end = "str_poll_reason_end"

quick_battle_troops_begin = "trp_quick_battle_troop_1"
quick_battle_troops_end = "trp_quick_battle_troops_end"

@@ -1706,3 +1710,5 @@
price_adjustment = 25 #the percent by which a trade at a center alters price

fire_duration = 4 #fires takes 4 hours

multiplayer_pack_message_shift_size = 256
@@ -521,6 +521,7 @@
    (eq, "$g_multiplayer_poll_ended", 0),
    (store_mission_timer_a, ":mission_timer"),
    (store_add, ":total_votes", "$g_multiplayer_poll_no_count", "$g_multiplayer_poll_yes_count"),
    (this_or_next|neq, "$g_multiplayer_poll_admin_override", 0),
    (this_or_next|eq, ":total_votes", "$g_multiplayer_poll_num_sent"),
    (gt, ":mission_timer", "$g_multiplayer_poll_end_time"),
    (call_script, "script_cf_multiplayer_evaluate_poll"),
@@ -628,6 +629,17 @@
    (start_presentation, "prsnt_multiplayer_team_score_display"),
    ])

multiplayer_admin_message_stored_player = (
  0, 0, 0, [(key_clicked, key_f11)], [
    (multiplayer_get_my_player, ":player_no"),
    (player_is_admin, ":player_no"),
    (try_begin),
      (call_script, "script_cf_multiplayer_stored_player_no_is_valid"),
    (else_try),
      (assign, "$g_multiplayer_players_list_selected_player_no", 0),
    (end_try),
    (start_presentation, "prsnt_multiplayer_get_admin_message"),
  ])

common_battle_mission_start = (
  ti_before_mission_start, 0, 0, [],
@@ -8226,6 +8238,8 @@
        (eq, "$g_waiting_for_confirmation_to_terminate", 0),
        (start_presentation, "prsnt_multiplayer_escape_menu"),
        ]),

      multiplayer_admin_message_stored_player,
      ],
  ),

@@ -8588,6 +8602,8 @@
        (eq, "$g_waiting_for_confirmation_to_terminate", 0),
        (start_presentation, "prsnt_multiplayer_escape_menu"),
        ]),

      multiplayer_admin_message_stored_player,
      ],
  ),

@@ -9686,6 +9702,8 @@
        (eq, "$g_waiting_for_confirmation_to_terminate", 0),
        (start_presentation, "prsnt_multiplayer_escape_menu"),
        ]),

      multiplayer_admin_message_stored_player,
      ],
  ),

@@ -10469,6 +10487,8 @@
        (eq, "$g_waiting_for_confirmation_to_terminate", 0),
        (start_presentation, "prsnt_multiplayer_escape_menu"),
        ]),

      multiplayer_admin_message_stored_player,
      ],
  ),

@@ -11557,6 +11577,8 @@
        (eq, "$g_waiting_for_confirmation_to_terminate", 0),
        (start_presentation, "prsnt_multiplayer_escape_menu"),
        ]),

      multiplayer_admin_message_stored_player,
      ],
  ),

@@ -12811,6 +12833,8 @@
        (eq, "$g_waiting_for_confirmation_to_terminate", 0),
        (start_presentation, "prsnt_multiplayer_escape_menu"),
        ]),

      multiplayer_admin_message_stored_player,
      ],
  ),

@@ -13976,6 +14000,8 @@
        (eq, "$g_waiting_for_confirmation_to_terminate", 0),
        (start_presentation, "prsnt_multiplayer_escape_menu"),
        ]),

      multiplayer_admin_message_stored_player,
      ],
  ),

@@ -9060,6 +9060,7 @@
        (try_begin),
          (store_script_param, ":value", 3),
          (store_script_param, ":value_2", 4),
          (store_script_param, ":value_3", 5),
          #validity check
          (eq, "$g_multiplayer_poll_running", 0),
          (store_mission_timer_a, ":mission_timer"),
@@ -9070,6 +9071,8 @@
            (eq, ":value", 1), # kicking a player
            (try_begin),
              (eq, "$g_multiplayer_kick_voteable", 1),
              (this_or_next|neg|multiplayer_is_dedicated_server), #client-servers see all players as admin
              (neg|player_is_admin, ":value_2"),
              (player_is_active, ":value_2"),
              (assign, ":continue", 1),
            (try_end),
@@ -9077,6 +9080,8 @@
            (eq, ":value", 2), # banning a player
            (try_begin),
              (eq, "$g_multiplayer_ban_voteable", 1),
              (this_or_next|neg|multiplayer_is_dedicated_server), #client-servers see all players as admin
              (neg|player_is_admin, ":value_2"),
              (player_is_active, ":value_2"),
              (save_ban_info_of_player, ":value_2"),
              (assign, ":continue", 1),
@@ -9098,7 +9103,6 @@
            (eq, ":value", 3), #vote for map and factions
            (try_begin),
              (eq, "$g_multiplayer_factions_voteable", 1),
              (store_script_param, ":value_3", 5),
              (store_script_param, ":value_4", 6),
              (call_script, "script_multiplayer_fill_map_game_types", "$g_multiplayer_game_type"),
              (assign, ":num_maps", reg0),
@@ -9118,11 +9122,16 @@
            (try_end),
          (else_try),
            (eq, ":value", 4), #vote for number of bots
            (store_script_param, ":value_3", 5),
            (store_add, ":upper_limit", "$g_multiplayer_num_bots_voteable", 1),
            (is_between, ":value_2", 0, ":upper_limit"),
            (is_between, ":value_3", 0, ":upper_limit"),
            (assign, ":continue", 1),
          (else_try),
            (eq, ":value", 5), #admin custom poll
            (player_is_admin, ":player_no"),
            (neg|str_is_empty, s37),
            (assign, ":value_2", -1),
            (assign, ":continue", 1),
          (try_end),
          (eq, ":continue", 1),
          #condition checks are done
@@ -9130,10 +9139,12 @@
          (try_begin),
            (eq, ":value", 1), #kicking a player
            (str_store_player_username, s1, ":value_2"),
            (call_script, "script_multiplayer_get_reason_string", ":value_3"),
            (server_add_message_to_log, "str_poll_kick_player_s1_by_s0"),
          (else_try),
            (eq, ":value", 2), #banning a player
            (str_store_player_username, s1, ":value_2"),
            (call_script, "script_multiplayer_get_reason_string", ":value_3"),
            (server_add_message_to_log, "str_poll_ban_player_s1_by_s0"),
          (else_try),
            (eq, ":value", 0), #vote for map
@@ -9160,30 +9171,56 @@
          (assign, "$g_multiplayer_poll_num_sent", 0),
          (assign, "$g_multiplayer_poll_yes_count", 0),
          (assign, "$g_multiplayer_poll_no_count", 0),
          (assign, "$g_multiplayer_poll_admin_override", 0),
          (assign, "$g_multiplayer_poll_to_show", ":value"),
          (assign, "$g_multiplayer_poll_value_to_show", ":value_2"),
          (try_begin),
            (eq, ":value", 3),
            (assign, "$g_multiplayer_poll_value_2_to_show", ":value_3"),
            (assign, "$g_multiplayer_poll_value_3_to_show", ":value_4"),
            (try_begin), #store requester in upper bits of faction 1
              (is_between, ":player_no", 1, multiplayer_max_possible_player_id),
              (store_mul, "$g_multiplayer_poll_value_2_to_show", ":player_no", multiplayer_pack_message_shift_size),
            (else_try),
              (assign, "$g_multiplayer_poll_value_2_to_show", 0),
            (try_end),
            (store_add, "$g_multiplayer_poll_value_2_to_show", ":value_3", "$g_multiplayer_poll_value_2_to_show"), #faction 1
            (assign, "$g_multiplayer_poll_value_3_to_show", ":value_4"), #faction 2
          (else_try),
            (eq, ":value", 4),
            (assign, "$g_multiplayer_poll_value_2_to_show", ":value_3"),
            (assign, "$g_multiplayer_poll_value_3_to_show", -1),
            (assign, "$g_multiplayer_poll_value_2_to_show", ":value_3"), #bots team 2
            (assign, "$g_multiplayer_poll_value_3_to_show", ":player_no"), #requester
          (else_try),
            (eq, ":value", 1),
            (assign, "$g_multiplayer_poll_value_2_to_show", ":player_no"), #requester
            (assign, "$g_multiplayer_poll_value_3_to_show", ":value_3"), #reason
          (else_try),
            (eq, ":value", 2),
            (assign, "$g_multiplayer_poll_value_2_to_show", ":player_no"), #requester
            (assign, "$g_multiplayer_poll_value_3_to_show", ":value_3"), #reason
          (else_try),
            (assign, "$g_multiplayer_poll_value_2_to_show", -1),
            (assign, "$g_multiplayer_poll_value_2_to_show", ":player_no"), #requester
            (assign, "$g_multiplayer_poll_value_3_to_show", -1),
          (try_end),
          (store_add, ":poll_disable_until", ":mission_timer", multiplayer_poll_disable_period),
          (player_set_slot, ":player_no", slot_player_poll_disabled_until_time, ":poll_disable_until"),
          (try_begin),
            (neg|player_is_admin, ":player_no"), #only limit non admin polling
            (store_add, ":poll_disable_until", ":mission_timer", multiplayer_poll_disable_period),
            (player_set_slot, ":player_no", slot_player_poll_disabled_until_time, ":poll_disable_until"),
          (try_end),
          (store_add, "$g_multiplayer_poll_end_time", ":mission_timer", 60),
          (get_max_players, ":num_players"),
          (try_for_range, ":cur_player", 0, ":num_players"),
            (player_is_active, ":cur_player"),
            (player_set_slot, ":cur_player", slot_player_can_answer_poll, 1),
            (val_add, "$g_multiplayer_poll_num_sent", 1),
            (try_begin),
              (eq, ":value", 5), #send custom poll question
              (multiplayer_send_string_to_player, ":cur_player", multiplayer_event_return_admin_poll_question, s37),
            (try_end),
            (multiplayer_send_4_int_to_player, ":cur_player", multiplayer_event_ask_for_poll, "$g_multiplayer_poll_to_show", "$g_multiplayer_poll_value_to_show", "$g_multiplayer_poll_value_2_to_show", "$g_multiplayer_poll_value_3_to_show"),
          (try_end),
          (try_begin),
            (eq, ":value", 3), #convert back to faction 1 only, for other scripts
            (store_mod, "$g_multiplayer_poll_value_2_to_show", "$g_multiplayer_poll_value_2_to_show", multiplayer_pack_message_shift_size),
          (try_end),
        (try_end),
      (else_try),
        (eq, ":event_type", multiplayer_event_answer_to_poll),
@@ -9191,16 +9228,30 @@
          (store_script_param, ":value", 3),
          #validity check
          (eq, "$g_multiplayer_poll_running", 1),
          (is_between, ":value", 0, 2),
          (is_between, ":value", 0, 3),
          (player_slot_eq, ":player_no", slot_player_can_answer_poll, 1),
          #condition checks are done
          (player_set_slot, ":player_no", slot_player_can_answer_poll, 0),
          (try_begin),
            (eq, ":value", 0),
            (val_add, "$g_multiplayer_poll_no_count", 1),
          (else_try),
            (eq, ":value", 1),
            (val_add, "$g_multiplayer_poll_yes_count", 1),
            (eq, ":value", 2), #abstain
            (val_sub, "$g_multiplayer_poll_num_sent", 1),
          (else_try),
            (player_is_admin, ":player_no"),
            (try_begin), #override poll, ending it
              (eq, ":value", 0),
              (assign, "$g_multiplayer_poll_admin_override", -1),
            (else_try),
              (eq, ":value", 1),
              (assign, "$g_multiplayer_poll_admin_override", 1),
            (try_end),
          (else_try),
            (try_begin),
              (eq, ":value", 0),
              (val_add, "$g_multiplayer_poll_no_count", 1),
            (else_try),
              (eq, ":value", 1),
              (val_add, "$g_multiplayer_poll_yes_count", 1),
            (try_end),
          (try_end),
        (try_end),
      (else_try),
@@ -9217,11 +9268,12 @@
        (eq, ":event_type", multiplayer_event_admin_ban_player),
        (try_begin),
          (store_script_param, ":value", 3),
          (store_script_param, ":perm_temp", 4),
          #validity check
          (player_is_admin, ":player_no"),
          (player_is_active, ":value"),
          #condition checks are done
          (ban_player, ":value", 0, ":player_no"),
          (ban_player, ":value", ":perm_temp", ":player_no"),
        (try_end),
      (else_try),
        (eq, ":event_type", multiplayer_event_admin_set_valid_vote_ratio),
@@ -9346,6 +9398,48 @@
          (assign, "$g_multiplayer_force_default_armor", ":value"),
        (try_end),
      (else_try),
        (eq, ":event_type", multiplayer_event_admin_message_set_player),
        (try_begin),
          (store_script_param, ":target", 3),
          #validity check
          (player_is_admin, ":player_no"),
          #condition checks are done
          (player_set_slot, ":player_no", slot_player_admin_message_target, ":target"),
        (try_end),
      (else_try),
        (eq, ":event_type", multiplayer_event_admin_message_to_player),
        (try_begin),
          #validity check
          (player_is_admin, ":player_no"),
          (neg|str_is_empty, s0),
          (player_get_slot, ":target", ":player_no", slot_player_admin_message_target),
          (str_store_player_username, s1, ":player_no"),
          (try_begin),
            (eq, ":target", 0), #send to all players
            (get_max_players, ":num_players"),
            (try_for_range, ":cur_player", 0, ":num_players"), #include server (0) for hosting clients
              (player_is_active, ":cur_player"),
              (multiplayer_send_int_to_player, ":cur_player", multiplayer_event_admin_message_set_admin, ":player_no"),
              (multiplayer_send_string_to_player, ":cur_player", multiplayer_event_admin_message, s0),
            (try_end),
            (server_add_message_to_log, "@*ADMIN* [{s1}] {s0}"),
          (else_try),
            (player_is_active, ":target"),
            (multiplayer_send_int_to_player, ":target", multiplayer_event_admin_message_set_admin, ":player_no"),
            (multiplayer_send_string_to_player, ":target", multiplayer_event_admin_message, s0),
            (str_store_player_username, s2, ":target"),
            (server_add_message_to_log, "@*ADMIN* [{s1}] -> [{s2}] {s0}"),
          (try_end),
        (try_end),
      (else_try),
        (eq, ":event_type", multiplayer_event_admin_set_poll_question),
        (try_begin),
          #validity check
          (player_is_admin, ":player_no"),
          (neg|str_is_empty, s0),
          (str_store_string, s37, s0), #this register should be one not used elsewhere in the module system
        (try_end),
      (else_try),
        ###############
        #CLIENT EVENTS#
        ###############
@@ -9585,6 +9679,10 @@
            (player_is_active, ":value_2"), #might go offline before here
            (assign, ":continue_to_poll", 1),
          (else_try),
            (eq, ":value", 5),
            (neg|str_is_empty, s37), #check poll question recieved
            (assign, ":continue_to_poll", 1),
          (else_try),
            (assign, ":continue_to_poll", 1),
          (try_end),
          (try_begin),
@@ -9593,6 +9691,7 @@
            (assign, "$g_multiplayer_poll_value_to_show", ":value_2"),
            (assign, "$g_multiplayer_poll_value_2_to_show", ":value_3"),
            (assign, "$g_multiplayer_poll_value_3_to_show", ":value_4"),
            (assign, "$g_multiplayer_poll_admin_override", 0),
            (store_mission_timer_a, ":mission_timer"),
            (store_add, "$g_multiplayer_poll_client_end_time", ":mission_timer", 60),
            (start_presentation, "prsnt_multiplayer_poll"),
@@ -9719,6 +9818,28 @@
          (else_try),
            (assign, "$g_my_spawn_count", ":value"),
          (try_end),
        (else_try),
          (eq, ":event_type", multiplayer_event_admin_message_set_admin),
          (try_begin),
            (store_script_param, ":admin", 3),
            (assign, "$g_multiplayer_admin_message_player_no", ":admin"),
          (try_end),
        (else_try),
          (eq, ":event_type", multiplayer_event_admin_message),
          (try_begin),
            #validity check
            (neg|str_is_empty, s0),
            (player_is_active, "$g_multiplayer_admin_message_player_no"),
            (str_store_string, s36, s0), #this register should be one not used elsewhere in the module system
            (start_presentation, "prsnt_multiplayer_admin_message"),
          (try_end),
        (else_try),
          (eq, ":event_type", multiplayer_event_return_admin_poll_question),
          (try_begin),
            #validity check
            (neg|str_is_empty, s0),
            (str_store_string, s37, s0), #this register should be one not used elsewhere in the module system
          (try_end),
        (try_end),
    ]),

@@ -9738,8 +9859,18 @@
    (store_mul, ":vote_ratio", 100, "$g_multiplayer_poll_yes_count"),
    (val_div, ":vote_ratio", ":total_votes"),
    (try_begin),
      (eq, "$g_multiplayer_poll_admin_override", 1),
      (assign, ":result", 3),
    (else_try),
      (eq, "$g_multiplayer_poll_admin_override", -1),
      (assign, ":result", 2),
    (else_try),
      (ge, ":vote_ratio", "$g_multiplayer_valid_vote_ratio"),
      (assign, ":result", 1),
    (try_end),
    (try_begin),
      (this_or_next|eq, ":result", 3),
      (eq, ":result", 1),
      (try_begin),
        (eq, "$g_multiplayer_poll_to_show", 1), #kick player
        (try_begin),
@@ -9775,6 +9906,7 @@
      (player_is_active, ":cur_player"),
      (multiplayer_send_2_int_to_player, ":cur_player", multiplayer_event_show_multiplayer_message, multiplayer_message_type_poll_result, ":result"),
    (try_end),
    (this_or_next|eq, ":result", 3),
    (eq, ":result", 1),
    ]),

@@ -11096,6 +11228,39 @@
    (try_end),
    ]),

  # script_multiplayer_get_reason_string
  # Input: reason_id
  # Output: s2 = poll reason
  ("multiplayer_get_reason_string",
    [
      (store_script_param, ":reason_id", 1),
      (val_add, ":reason_id", multiplayer_poll_reasons_begin),
      (try_begin),
        (this_or_next|lt, ":reason_id", multiplayer_poll_reasons_begin),
        (ge, ":reason_id", multiplayer_poll_reasons_end),
        (store_sub, ":reason_id", multiplayer_poll_reasons_end, 1),
      (try_end),
      (str_store_string, s2, ":reason_id"),
      ]),

  # script_cf_multiplayer_stored_player_no_is_valid
  # Input: none
  # Output: none, return true or false
  ("cf_multiplayer_stored_player_no_is_valid",
    [
      (assign, ":continue", 1),
      (try_begin),
        (eq, "$g_multiplayer_players_list_selected_player_no", 0),
      (else_try),
        (player_is_active, "$g_multiplayer_players_list_selected_player_no"),
        (player_slot_eq, "$g_multiplayer_players_list_selected_player_no", slot_player_button_index, "$g_multiplayer_players_list_selected_player_check_no"),
      (else_try),
        (display_message, "str_stored_player_invalid", 0xFF990000),
        (assign, ":continue", 0),
      (end_try),
      (eq, ":continue", 1),
      ]),

  # script_multiplayer_fill_map_game_types
  # Input: game_type
  # Output: num_maps
@@ -2424,20 +2424,36 @@
  ("multi_game_type_7", "Siege"),
  ("multi_game_types_end", "multi_game_types_end"),

  ("poll_kick_player_s1_by_s0", "{s0} started a poll to kick player {s1}."),
  ("poll_ban_player_s1_by_s0", "{s0} started a poll to ban player {s1}."),
  ("poll_kick_player_s1_by_s0", "{s0} started a poll to kick player {s1} for {s2}."),
  ("poll_ban_player_s1_by_s0", "{s0} started a poll to ban player {s1} for {s2}."),
  ("poll_change_map_to_s1_by_s0", "{s0} started a poll to change map to {s1}."),
  ("poll_change_map_to_s1_and_factions_to_s2_and_s3_by_s0", "{s0} started a poll to change map to {s1} and factions to {s2} and {s3}."),
  ("poll_change_number_of_bots_to_reg0_and_reg1_by_s0", "{s0} started a poll to change bot counts to {reg0} and {reg1}."),

  ("poll_kick_player", "Poll to kick player {s0}: 1 = Accept, 2 = Decline"),
  ("poll_ban_player", "Poll to ban player {s0}: 1 = Accept, 2 = Decline"),
  ("poll_change_map", "Poll to change map to {s0}: 1 = Accept, 2 = Decline"),
  ("poll_change_map_with_faction", "Poll to change map to {s0} and factions to {s1} versus {s2}: 1 = Accept, 2 = Decline"),
  ("poll_change_number_of_bots", "Poll to change number of bots to {reg0} for {s0} and {reg1} for {s1}: 1 = Accept, 2 = Decline"),
  ("poll_kick_player", "Poll to kick player {s0} for {s2}"),
  ("poll_ban_player", "Poll to ban player {s0} for {s2}"),
  ("poll_change_map", "Poll to change map to {s0}"),
  ("poll_change_map_with_faction", "Poll to change map to {s0} and factions to {s1} versus {s2}"),
  ("poll_change_number_of_bots", "Poll to change number of bots to {reg0} for {s0} and {reg1} for {s1}"),
  ("poll_called_by", "Called by {s1}: F8 = Accept, F9 = Decline, F10 = Abstain"),
  ("poll_time_left", "({reg0} seconds left)"),
  ("poll_admin_confirm_accept", "Press again to confirm accepting this poll immediately"),
  ("poll_admin_confirm_reject", "Press again to confirm rejecting this poll immediately"),
  ("poll_result_yes", "The poll is accepted by the majority."),
  ("poll_result_no", "The poll is rejected by the majority."),
  ("poll_result_admin_yes", "The poll is accepted by an admin."),
  ("poll_result_admin_no", "The poll is rejected by an admin."),

  ("poll_reason_team_killing", "team killing"),
  ("poll_reason_being_absent", "being absent from the game"),
  ("poll_reason_deserting_battle", "deserting from the battle"),
  ("poll_reason_abusing_glitches", "abusing glitches"),
  ("poll_reason_griefing", "griefing"),
  ("poll_reason_cheating", "cheating"),
  ("poll_reason_trolling_chat", "trolling in chat"),
  ("poll_reason_breaking_server_rules", "breaking server rules"),
  ("poll_reason_specified", "the reason specified in chat"),
  ("poll_reason_end", "poll_reason_end"),

  ("total_item_cost_reg0", "Total cost: {reg0}"),

@@ -2594,6 +2610,18 @@
  ("command", "Command:"),
  ("profile_banner_selection_text", "Choose a banner for your profile:"),
  ("use_default_banner", "Use Faction's Banner"),
  ("choose_a_reason", "Choose a reason:"),
  ("somebody", "somebody"),
  ("stored_player_invalid", "Stored player number is invalid."),
  ("message_player", "Message Player"),
  ("send_message_to_s1", "Send Message to {s1}"),
  ("all_players", "All Players"),
  ("admin_s1", "Admin {s1}:"),
  ("choose_ban_length", "Choose ban length:"),
  ("ban_temporary", "An hour"),
  ("ban_permanent", "Permanently"),
  ("poll_question", "Poll question:"),
  ("custom_question", "Custom question"),

  ("party_morale_is_low", "Morale of some troops are low!"),
  ("weekly_report", "Weekly Report"),
@@ -936,6 +936,21 @@
g_multiplayer_poll_value_3_to_show
g_multiplayer_poll_end_time
g_multiplayer_poll_client_end_time
g_multiplayer_poll_admin_override
g_multiplayer_poll_admin_warning
g_multiplayer_players_list_selected_player_no
g_multiplayer_players_list_selected_player_check_no
g_multiplayer_admin_message_player_no
g_show_poll_reasons_list_button_list_end_index
g_show_poll_reasons_list_button_list_first_index
g_presentation_obj_poll_2
g_presentation_obj_ban_length_1
g_presentation_obj_ban_length_2
g_presentation_obj_escape_menu_14
g_presentation_obj_show_players_all
g_presentation_obj_get_admin_message_1
g_presentation_obj_poll_menu_6
g_presentation_obj_get_poll_question_1
g_placing_initial_flags
g_multiplayer_team_1_first_spawn
g_multiplayer_team_2_first_spawn
 
@@ -4681,6 +4681,14 @@

        (assign, ":text_font_color", 0xFFFFFFFF),
        (try_begin),
          (eq, "$g_multiplayer_message_value_3", 3),
          (str_store_string, s0, "str_poll_result_admin_yes"),
          (assign, "$g_multiplayer_poll_admin_override", 1),
        (else_try),
          (eq, "$g_multiplayer_message_value_3", 2),
          (str_store_string, s0, "str_poll_result_admin_no"),
          (assign, "$g_multiplayer_poll_admin_override", -1),
        (else_try),
          (eq, "$g_multiplayer_message_value_3", 1),
          (str_store_string, s0, "str_poll_result_yes"),
        (else_try),
@@ -6354,24 +6362,18 @@
      (str_clear, s0),
      (create_text_overlay, "$g_presentation_obj_escape_menu_container", s0, tf_scrollable_style_2),
      (position_set_x, pos1, 285),
      (position_set_y, pos1, 75),
      (position_set_y, pos1, 125),
      (overlay_set_position, "$g_presentation_obj_escape_menu_container", pos1),
      (position_set_x, pos1, 405),
      (position_set_y, pos1, 550),
      (position_set_y, pos1, 500),
      (overlay_set_area_size, "$g_presentation_obj_escape_menu_container", pos1),
      (set_container_overlay, "$g_presentation_obj_escape_menu_container"),

      (assign, ":cur_y", 500),

      (create_text_overlay, reg0, "str_choose_an_option", 0),
      (overlay_set_color, reg0, 0xFFFFFF),
      (position_set_x, pos1, 0),
      (position_set_y, pos1, ":cur_y"),
      (overlay_set_position, reg0, pos1),
      (val_sub, ":cur_y", escape_menu_item_height),
      (assign, ":cur_y", 10),

      (create_button_overlay, "$g_presentation_obj_escape_menu_1", "str_choose_faction", 0),
      (overlay_set_color, "$g_presentation_obj_escape_menu_1", 0xFFFFFF),
      (val_add, ":cur_y", escape_menu_item_height),
      (multiplayer_get_my_team, ":my_team"),
      (assign, "$g_presentation_obj_escape_menu_2", -1),
      (assign, "$g_presentation_obj_escape_menu_3", -1),
@@ -6383,24 +6385,30 @@
      (assign, "$g_presentation_obj_escape_menu_11", -1),
      (assign, "$g_presentation_obj_escape_menu_12", -1),
      (assign, "$g_presentation_obj_escape_menu_13", -1),
      (assign, "$g_presentation_obj_escape_menu_14", -1),
      (try_begin),
        (lt, ":my_team", multi_team_spectator),
        (create_button_overlay, "$g_presentation_obj_escape_menu_2", "str_choose_troop", 0),
        (overlay_set_color, "$g_presentation_obj_escape_menu_2", 0xFFFFFF),
        (val_add, ":cur_y", escape_menu_item_height),
        (multiplayer_get_my_troop, ":my_troop"),
        (try_begin),
          (ge, ":my_troop", 0),
          (create_button_overlay, "$g_presentation_obj_escape_menu_3", "str_choose_items", 0),
          (overlay_set_color, "$g_presentation_obj_escape_menu_3", 0xFFFFFF),
          (val_add, ":cur_y", escape_menu_item_height),
        (try_end),
      (try_end),
      (create_button_overlay, "$g_presentation_obj_escape_menu_4", "str_options", 0),
      (overlay_set_color, "$g_presentation_obj_escape_menu_4", 0xFFFFFF),
      (val_add, ":cur_y", escape_menu_item_height),
      (create_button_overlay, "$g_presentation_obj_escape_menu_5", "str_redefine_keys", 0),
      (overlay_set_color, "$g_presentation_obj_escape_menu_5", 0xFFFFFF),
      (val_add, ":cur_y", escape_menu_item_height),

      (create_button_overlay, "$g_presentation_obj_escape_menu_13", "@Show game rules", 0),
      (overlay_set_color, "$g_presentation_obj_escape_menu_13", 0xFFFFFF),
      (val_add, ":cur_y", escape_menu_item_height),

      (multiplayer_get_my_player, ":my_player_no"),
      (try_begin),
@@ -6411,6 +6419,7 @@
        (eq, "$g_multiplayer_ban_voteable", 1),
        (create_button_overlay, "$g_presentation_obj_escape_menu_6", "str_submit_a_poll", 0),
        (overlay_set_color, "$g_presentation_obj_escape_menu_6", 0xFFFFFF),
        (val_add, ":cur_y", escape_menu_item_height),
        (assign, "$g_presentation_obj_escape_menu_6_available", 1),
        (try_begin),
          (ge, ":my_player_no", 0),
@@ -6428,14 +6437,21 @@
        (player_is_admin, ":my_player_no"),
        (create_button_overlay, "$g_presentation_obj_escape_menu_7", "str_administrator_panel", 0),
        (overlay_set_color, "$g_presentation_obj_escape_menu_7", 0xFFFFFF),
        (val_add, ":cur_y", escape_menu_item_height),
        (create_button_overlay, "$g_presentation_obj_escape_menu_8", "str_kick_player", 0),
        (overlay_set_color, "$g_presentation_obj_escape_menu_8", 0xFFFFFF),
        (val_add, ":cur_y", escape_menu_item_height),
        (create_button_overlay, "$g_presentation_obj_escape_menu_9", "str_ban_player", 0),
        (overlay_set_color, "$g_presentation_obj_escape_menu_9", 0xFFFFFF),
        (val_add, ":cur_y", escape_menu_item_height),
        (create_button_overlay, "$g_presentation_obj_escape_menu_14", "str_message_player", 0),
        (overlay_set_color, "$g_presentation_obj_escape_menu_14", 0xFFFFFF),
        (val_add, ":cur_y", escape_menu_item_height),
      (try_end),

      (create_button_overlay, "$g_presentation_obj_escape_menu_11", "str_mute_player", 0),
      (overlay_set_color, "$g_presentation_obj_escape_menu_11", 0xFFFFFF),
      (val_add, ":cur_y", escape_menu_item_height),

      (try_begin),
        (assign, "$g_presentation_obj_escape_menu_12", -1),
@@ -6450,11 +6466,20 @@
        (eq, ":any_muted", 1),
        (create_button_overlay, "$g_presentation_obj_escape_menu_12", "str_unmute_player", 0),
        (overlay_set_color, "$g_presentation_obj_escape_menu_12", 0xFFFFFF),
        (val_add, ":cur_y", escape_menu_item_height),
      (try_end),

      (create_button_overlay, "$g_presentation_obj_escape_menu_10", "str_quit", 0),
      (overlay_set_color, "$g_presentation_obj_escape_menu_10", 0xFFFFFF),
      (val_add, ":cur_y", escape_menu_item_height),

      (create_text_overlay, reg0, "str_choose_an_option", 0),
      (overlay_set_color, reg0, 0xFFFFFF),
      (position_set_x, pos1, 0),
      (position_set_y, pos1, ":cur_y"),
      (overlay_set_position, reg0, pos1),

      (val_sub, ":cur_y", escape_menu_item_height),
      (position_set_x, pos1, 130),
      (position_set_y, pos1, ":cur_y"),
      (overlay_set_position, "$g_presentation_obj_escape_menu_1", pos1),
@@ -6503,6 +6528,12 @@
        (position_set_y, pos1, ":cur_y"),
        (overlay_set_position, "$g_presentation_obj_escape_menu_9", pos1),
      (try_end),
      (try_begin),
        (ge, "$g_presentation_obj_escape_menu_14", 0),
        (val_sub, ":cur_y", escape_menu_item_height),
        (position_set_y, pos1, ":cur_y"),
        (overlay_set_position, "$g_presentation_obj_escape_menu_14", pos1),
      (try_end),
      (val_sub, ":cur_y", escape_menu_item_height),
      (position_set_y, pos1, ":cur_y"),
      (overlay_set_position, "$g_presentation_obj_escape_menu_11", pos1),
@@ -6577,6 +6608,11 @@
        (eq, ":eek:bject", "$g_presentation_obj_escape_menu_13"),
        (presentation_set_duration, 0),
        (multiplayer_send_message_to_server, multiplayer_event_open_game_rules),
      (else_try),
        (eq, ":eek:bject", "$g_presentation_obj_escape_menu_14"),
        (presentation_set_duration, 0),
        (assign, "$g_multiplayer_players_list_action_type", 7), #message player
        (start_presentation, "prsnt_multiplayer_show_players_list"),
      (try_end),
      ]),
    (ti_on_presentation_run,
@@ -6664,6 +6700,15 @@
        (overlay_set_color, "$g_presentation_obj_poll_menu_3", 0xFFFFFF),
        (position_set_y, pos1, ":cur_y"),
        (overlay_set_position, "$g_presentation_obj_poll_menu_3", pos1),
        (val_sub, ":cur_y", escape_menu_item_height),
      (try_end),
      (try_begin),
        (multiplayer_get_my_player, ":my_player_no"),
        (player_is_admin, ":my_player_no"),
        (create_button_overlay, "$g_presentation_obj_poll_menu_6", "str_custom_question", 0),
        (overlay_set_color, "$g_presentation_obj_poll_menu_6", 0xFFFFFF),
        (position_set_y, pos1, ":cur_y"),
        (overlay_set_position, "$g_presentation_obj_poll_menu_6", pos1),
      (try_end),

      (presentation_set_duration, 999999),
@@ -6695,6 +6740,10 @@
        (presentation_set_duration, 0),
        (assign, "$g_multiplayer_number_of_bots_list_action_type", 1), #for team 1
        (start_presentation, "prsnt_multiplayer_show_number_of_bots_list"),
      (else_try),
        (eq, ":eek:bject", "$g_presentation_obj_poll_menu_6"),
        (presentation_set_duration, 0),
        (start_presentation, "prsnt_multiplayer_get_poll_question"),
      (try_end),
      ]),
    (ti_on_presentation_run,
@@ -6760,6 +6809,11 @@
        (val_add, ":cur_y", escape_menu_item_height),
      (try_end),

      (try_begin),
        (eq, "$g_multiplayer_players_list_action_type", 7), #admin message
        (val_add, ":cur_y", escape_menu_item_height),
      (try_end),

      (create_text_overlay, reg0, "str_choose_a_player", 0),
      (overlay_set_color, reg0, 0xFFFFFF),
      (position_set_x, pos1, 0),
@@ -6767,6 +6821,16 @@
      (overlay_set_position, reg0, pos1),
      (val_sub, ":cur_y", escape_menu_item_height),

      (try_begin),
        (eq, "$g_multiplayer_players_list_action_type", 7), #admin message, add extra "all players" option
        (create_button_overlay, "$g_presentation_obj_show_players_all", "str_all_players", 0),
        (overlay_set_color, "$g_presentation_obj_show_players_all", 0xFFFFFF),
        (position_set_x, pos1, 60),
        (position_set_y, pos1, ":cur_y"),
        (overlay_set_position, "$g_presentation_obj_show_players_all", pos1),
        (val_sub, ":cur_y", escape_menu_item_height),
      (try_end),

      (get_max_players, ":num_players"),
      (try_for_range, ":player_no", 1, ":num_players"), #0 is server no need to write it
        (player_is_active, ":player_no"),
@@ -6795,7 +6859,7 @@

        (create_button_overlay, ":eek:verlay_id", s0, 0),
        (overlay_set_color, ":eek:verlay_id", 0xFFFFFF),
        (position_set_x, pos1, 130),
        (position_set_x, pos1, 60),
        (position_set_y, pos1, ":cur_y"),
        (overlay_set_position, ":eek:verlay_id", pos1),
        (val_sub, ":cur_y", escape_menu_item_height),
@@ -6810,32 +6874,41 @@
      (try_for_range, ":player_no", 1, ":num_players"), #0 is server no need to write it
        (player_is_active, ":player_no"),
        (player_slot_eq, ":player_no", slot_player_button_index, ":eek:bject"),
        (assign, "$g_multiplayer_players_list_selected_player_no", ":player_no"),
        (assign, "$g_multiplayer_players_list_selected_player_check_no", ":eek:bject"),
        (try_begin),
          (is_between, "$g_multiplayer_players_list_action_type", 1, 3), #poll kick or poll ban
          (try_begin),
            (multiplayer_get_my_player, ":my_player_no"),
            (ge, ":my_player_no", 0),
            (multiplayer_send_2_int_to_server, multiplayer_event_start_new_poll, "$g_multiplayer_players_list_action_type", ":player_no"),
            (store_mission_timer_a, ":mission_timer"),
            (val_add, ":mission_timer", multiplayer_poll_disable_period),
            (player_set_slot, ":my_player_no", slot_player_poll_disabled_until_time, ":mission_timer"),
            (start_presentation, "prsnt_multiplayer_get_poll_reason"),
          (try_end),
        (else_try),
          (eq, "$g_multiplayer_players_list_action_type", 3), #admin kick
          (multiplayer_send_int_to_server, multiplayer_event_admin_kick_player, ":player_no"),
        (else_try),
          (eq, "$g_multiplayer_players_list_action_type", 4), #admin ban
          (multiplayer_send_int_to_server, multiplayer_event_admin_ban_player, ":player_no"),
          (start_presentation, "prsnt_multiplayer_get_ban_length"),
        (else_try),
          (eq, "$g_multiplayer_players_list_action_type", 5), #mute player
          (player_set_is_muted, ":player_no", 1),
        (else_try),
          (eq, "$g_multiplayer_players_list_action_type", 6), #unmute player
          (player_set_is_muted, ":player_no", 0),
        (else_try),
          (eq, "$g_multiplayer_players_list_action_type", 7), #message player
          (start_presentation, "prsnt_multiplayer_get_admin_message"),
        (try_end),
        (assign, ":num_players", 0), #break
        (presentation_set_duration, 0),
      (try_end),
      (try_begin),
        (eq, "$g_presentation_obj_show_players_all", ":eek:bject"),
        (eq, "$g_multiplayer_players_list_action_type", 7), #message all players
        (assign, "$g_multiplayer_players_list_selected_player_no", 0),
        (start_presentation, "prsnt_multiplayer_get_admin_message"),
        (presentation_set_duration, 0),
      (try_end),
      ]),
    (ti_on_presentation_run,
    [(store_trigger_param_1, ":cur_time"),
@@ -6847,6 +6920,290 @@
      ]),
    ]),

  ("multiplayer_get_ban_length", prsntf_manual_end_only, 0, [
    (ti_on_presentation_load,
    [(set_fixed_point_multiplier, 1000),

      (create_mesh_overlay, reg0, "mesh_mp_ingame_menu"),
      (position_set_x, pos1, 250),
      (position_set_y, pos1, 80),
      (overlay_set_position, reg0, pos1),
      (position_set_x, pos1, 1000),
      (position_set_y, pos1, 1000),
      (overlay_set_size, reg0, pos1),

      (str_clear, s0),
      (create_text_overlay, reg0, s0, tf_scrollable_style_2),
      (position_set_x, pos1, 285),
      (position_set_y, pos1, 125),
      (overlay_set_position, reg0, pos1),
      (position_set_x, pos1, 405),
      (position_set_y, pos1, 500),
      (overlay_set_area_size, reg0, pos1),
      (set_container_overlay, reg0),

      (assign, ":cur_y", escape_menu_item_height),
      (val_mul, ":cur_y", 2),
      (val_add, ":cur_y", 10),

      (create_text_overlay, reg0, "str_choose_ban_length", 0),
      (overlay_set_color, reg0, 0xFFFFFF),
      (position_set_x, pos1, 0),
      (position_set_y, pos1, ":cur_y"),
      (overlay_set_position, reg0, pos1),

      (create_button_overlay, "$g_presentation_obj_ban_length_1", "str_ban_temporary", 0),
      (overlay_set_color, "$g_presentation_obj_ban_length_1", 0xFFFFFF),
      (position_set_x, pos1, 130),
      (val_sub, ":cur_y", escape_menu_item_height),
      (position_set_y, pos1, ":cur_y"),
      (overlay_set_position, "$g_presentation_obj_ban_length_1", pos1),

      (create_button_overlay, "$g_presentation_obj_ban_length_2", "str_ban_permanent", 0),
      (overlay_set_color, "$g_presentation_obj_ban_length_2", 0xFFFFFF),
      (val_sub, ":cur_y", escape_menu_item_height),
      (position_set_y, pos1, ":cur_y"),
      (overlay_set_position, "$g_presentation_obj_ban_length_2", pos1),

      (presentation_set_duration, 999999),
      ]),
    (ti_on_presentation_event_state_change,
    [(store_trigger_param_1, ":eek:bject"),
      (try_begin),
        (eq, ":eek:bject", "$g_presentation_obj_ban_length_1"),
        (presentation_set_duration, 0),
        (call_script, "script_cf_multiplayer_stored_player_no_is_valid"),
        (multiplayer_send_2_int_to_server, multiplayer_event_admin_ban_player, "$g_multiplayer_players_list_selected_player_no", 1), #temporary
      (else_try),
        (eq, ":eek:bject", "$g_presentation_obj_ban_length_2"),
        (presentation_set_duration, 0),
        (call_script, "script_cf_multiplayer_stored_player_no_is_valid"),
        (multiplayer_send_2_int_to_server, multiplayer_event_admin_ban_player, "$g_multiplayer_players_list_selected_player_no", 0), #permanent
      (try_end),
      ]),
    (ti_on_presentation_run,
    [(store_trigger_param_1, ":cur_time"),
      (try_begin),
        (key_clicked, key_escape),
        (gt, ":cur_time", 200),
        (presentation_set_duration, 0),
      (try_end),
      ]),
    ]),

  ("multiplayer_get_poll_reason", prsntf_manual_end_only, 0, [
    (ti_on_presentation_load,
    [(set_fixed_point_multiplier, 1000),

      (create_mesh_overlay, reg0, "mesh_mp_ingame_menu"),
      (position_set_x, pos1, 250),
      (position_set_y, pos1, 80),
      (overlay_set_position, reg0, pos1),
      (position_set_x, pos1, 1000),
      (position_set_y, pos1, 1000),
      (overlay_set_size, reg0, pos1),

      (str_clear, s0),
      (create_text_overlay, reg0, s0, tf_scrollable_style_2),
      (position_set_x, pos1, 285),
      (position_set_y, pos1, 125),
      (overlay_set_position, reg0, pos1),
      (position_set_x, pos1, 405),
      (position_set_y, pos1, 500),
      (overlay_set_area_size, reg0, pos1),
      (set_container_overlay, reg0),

      (assign, ":cur_y", escape_menu_item_height),
      (store_sub, ":num_reasons", multiplayer_poll_reasons_end, multiplayer_poll_reasons_begin),
      (val_mul, ":cur_y", ":num_reasons"),
      (val_add, ":cur_y", 10),

      (create_text_overlay, reg0, "str_choose_a_reason", 0),
      (overlay_set_color, reg0, 0xFFFFFF),
      (position_set_x, pos1, 0),
      (position_set_y, pos1, ":cur_y"),
      (overlay_set_position, reg0, pos1),
      (val_sub, ":cur_y", escape_menu_item_height),

      (try_for_range, ":reason_no", 0, ":num_reasons"),
        (call_script, "script_multiplayer_get_reason_string", ":reason_no"),

        (create_button_overlay, ":eek:verlay_id", s2, 0),
        (overlay_set_color, ":eek:verlay_id", 0xFFFFFF),
        (position_set_x, pos1, 60),
        (position_set_y, pos1, ":cur_y"),
        (overlay_set_position, ":eek:verlay_id", pos1),
        (val_sub, ":cur_y", escape_menu_item_height),
      (try_end),
      (store_add, "$g_show_poll_reasons_list_button_list_end_index", ":eek:verlay_id", 1),
      (store_sub, "$g_show_poll_reasons_list_button_list_first_index", "$g_show_poll_reasons_list_button_list_end_index", ":num_reasons"),

      (presentation_set_duration, 999999),
      ]),
    (ti_on_presentation_event_state_change,
    [(store_trigger_param_1, ":eek:bject"),
      (try_for_range, ":i_button", "$g_show_poll_reasons_list_button_list_first_index", "$g_show_poll_reasons_list_button_list_end_index"),
        (eq, ":eek:bject", ":i_button"),
        (presentation_set_duration, 0),
        (call_script, "script_cf_multiplayer_stored_player_no_is_valid"),
        (store_sub, ":reason_no", ":eek:bject", "$g_show_poll_reasons_list_button_list_first_index"),
        (multiplayer_send_3_int_to_server, multiplayer_event_start_new_poll, "$g_multiplayer_players_list_action_type", "$g_multiplayer_players_list_selected_player_no", ":reason_no"),
        (try_begin),
          (multiplayer_get_my_player, ":my_player_no"),
          (neg|player_is_admin, ":my_player_no"),
          (store_mission_timer_a, ":mission_timer"),
          (val_add, ":mission_timer", multiplayer_poll_disable_period),
          (player_set_slot, ":my_player_no", slot_player_poll_disabled_until_time, ":mission_timer"),
        (try_end),

        (assign, "$g_show_poll_reasons_list_button_list_end_index", 0), # break
      (try_end),
      ]),
    (ti_on_presentation_run,
    [(store_trigger_param_1, ":cur_time"),
      (try_begin),
        (key_clicked, key_escape),
        (gt, ":cur_time", 200),
        (presentation_set_duration, 0),
      (try_end),
      ]),
    ]),

  ("multiplayer_get_poll_question", prsntf_manual_end_only, 0, [
    (ti_on_presentation_load,
    [(set_fixed_point_multiplier, 1000),

      (str_store_string, s0, "str_poll_question"),
      (create_text_overlay, reg1, s0, tf_single_line),
      (overlay_set_color, reg1, 0xFFFFFFFF),
      (position_set_x, pos1, 200),
      (position_set_y, pos1, 530),
      (overlay_set_position, reg1, pos1),
      (position_set_x, pos1, 1000),
      (position_set_y, pos1, 980),
      (overlay_set_size, reg1, pos1),

      (create_simple_text_box_overlay, "$g_presentation_obj_get_poll_question_1"),
      (position_set_x, pos1, 200),
      (position_set_y, pos1, 500),
      (overlay_set_position, "$g_presentation_obj_get_poll_question_1", pos1),
      (position_set_x, pos1, 600),
      (position_set_y, pos1, 1000),
      (overlay_set_size, "$g_presentation_obj_get_poll_question_1", pos1),
      (presentation_set_duration, 999999),
      ]),
    (ti_on_presentation_event_state_change,
    [(store_trigger_param_1, ":eek:bject"),
      (try_begin),
        (eq, ":eek:bject", "$g_presentation_obj_get_poll_question_1"),
        (multiplayer_send_string_to_server, multiplayer_event_admin_set_poll_question, s0),
        (multiplayer_send_int_to_server, multiplayer_event_start_new_poll, 5),
        (presentation_set_duration, 0),
      (try_end),
      ]),
    (ti_on_presentation_run,
    [(store_trigger_param_1, ":cur_time"),
      (try_begin),
        (key_clicked, key_escape),
        (gt, ":cur_time", 200),
        (presentation_set_duration, 0),
      (try_end),
      ]),
    ]),

  ("multiplayer_get_admin_message", prsntf_manual_end_only, 0, [
    (ti_on_presentation_load,
    [(set_fixed_point_multiplier, 1000),

      (call_script, "script_cf_multiplayer_stored_player_no_is_valid"),
      (try_begin),
        (eq, "$g_multiplayer_players_list_selected_player_no", 0),
        (str_store_string, s1, "str_all_players"),
      (else_try),
        (str_store_player_username, s1, "$g_multiplayer_players_list_selected_player_no"),
      (try_end),
      (str_store_string, s0, "str_send_message_to_s1"),
      (create_text_overlay, reg1, s0, tf_single_line),
      (overlay_set_color, reg1, 0xFFFFFFFF),
      (position_set_x, pos1, 200),
      (position_set_y, pos1, 530),
      (overlay_set_position, reg1, pos1),
      (position_set_x, pos1, 1000),
      (position_set_y, pos1, 980),
      (overlay_set_size, reg1, pos1),

      (create_simple_text_box_overlay, "$g_presentation_obj_get_admin_message_1"),
      (position_set_x, pos1, 200),
      (position_set_y, pos1, 500),
      (overlay_set_position, "$g_presentation_obj_get_admin_message_1", pos1),
      (position_set_x, pos1, 600),
      (position_set_y, pos1, 1000),
      (overlay_set_size, "$g_presentation_obj_get_admin_message_1", pos1),
      (presentation_set_duration, 999999),
      ]),
    (ti_on_presentation_event_state_change,
    [(store_trigger_param_1, ":eek:bject"),
      (try_begin),
        (eq, ":eek:bject", "$g_presentation_obj_get_admin_message_1"),
        (presentation_set_duration, 0),
        (call_script, "script_cf_multiplayer_stored_player_no_is_valid"),
        (multiplayer_send_int_to_server, multiplayer_event_admin_message_set_player, "$g_multiplayer_players_list_selected_player_no"),
        (multiplayer_send_string_to_server, multiplayer_event_admin_message_to_player, s0),
      (try_end),
      ]),
    (ti_on_presentation_run,
    [(store_trigger_param_1, ":cur_time"),
      (try_begin),
        (key_clicked, key_escape),
        (gt, ":cur_time", 200),
        (presentation_set_duration, 0),
      (try_end),
      ]),
    ]),

  ("multiplayer_admin_message", prsntf_read_only|prsntf_manual_end_only, 0, [
    (ti_on_presentation_load,
    [(set_fixed_point_multiplier, 1000),
      (try_begin),

        (player_is_active, "$g_multiplayer_admin_message_player_no"),
        (str_store_player_username, s1, "$g_multiplayer_admin_message_player_no"),
        (str_store_string, s0, "str_admin_s1"),
        (create_text_overlay, reg0, s0, tf_center_justify|tf_with_outline),
        (overlay_set_color, reg0, 0xFFFFFFFF),
        (position_set_x, pos1, 500),
        (position_set_y, pos1, 500),
        (overlay_set_position, reg0, pos1),
        (position_set_x, pos1, 2000),
        (position_set_y, pos1, 2000),
        (overlay_set_size, reg0, pos1),

        (create_text_overlay, reg1, s36, tf_center_justify|tf_with_outline),
        (overlay_set_color, reg1, 0xFFFF0000),
        (position_set_x, pos1, 500),
        (position_set_y, pos1, 300),
        (overlay_set_position, reg1, pos1),
        (position_set_x, pos1, 2000),
        (position_set_y, pos1, 2000),
        (overlay_set_size, reg1, pos1),

        (display_message, "@*ADMIN* [{s1}] {s36}", 0xFFFF0000),

        (omit_key_once, key_escape),
        (presentation_set_duration, 1000),
      (try_end),
      ]),
    (ti_on_presentation_run,
    [(store_trigger_param_1, ":cur_time"),
      (try_begin),
        (key_clicked, key_escape),
        (gt, ":cur_time", 3000),
        (clear_omitted_keys),
        (presentation_set_duration, 0),
      (try_end),
      ]),
    ]),

  ("multiplayer_show_maps_list", prsntf_manual_end_only, 0, [
    (ti_on_presentation_load,
    [(set_fixed_point_multiplier, 1000),
@@ -6916,9 +7273,12 @@
            (multiplayer_get_my_player, ":my_player_no"),
            (ge, ":my_player_no", 0),
            (multiplayer_send_2_int_to_server, multiplayer_event_start_new_poll, 0, ":scene_id"),
            (store_mission_timer_a, ":mission_timer"),
            (val_add, ":mission_timer", multiplayer_poll_disable_period),
            (player_set_slot, ":my_player_no", slot_player_poll_disabled_until_time, ":mission_timer"),
            (try_begin),
              (neg|player_is_admin, ":my_player_no"),
              (store_mission_timer_a, ":mission_timer"),
              (val_add, ":mission_timer", multiplayer_poll_disable_period),
              (player_set_slot, ":my_player_no", slot_player_poll_disabled_until_time, ":mission_timer"),
            (try_end),
          (try_end),
        (else_try), #vote for map and factions
          (assign, "$g_multiplayer_factions_list_action_type", 1), #for team 1
@@ -7010,9 +7370,12 @@
            (multiplayer_get_my_player, ":my_player_no"),
            (ge, ":my_player_no", 0),
            (multiplayer_send_4_int_to_server, multiplayer_event_start_new_poll, 3, "$g_multiplayer_poll_for_map_and_faction_data_map", "$g_multiplayer_poll_for_map_and_faction_data_faction_1", ":faction_no"),
            (store_mission_timer_a, ":mission_timer"),
            (val_add, ":mission_timer", multiplayer_poll_disable_period),
            (player_set_slot, ":my_player_no", slot_player_poll_disabled_until_time, ":mission_timer"),
            (try_begin),
              (neg|player_is_admin, ":my_player_no"),
              (store_mission_timer_a, ":mission_timer"),
              (val_add, ":mission_timer", multiplayer_poll_disable_period),
              (player_set_slot, ":my_player_no", slot_player_poll_disabled_until_time, ":mission_timer"),
            (try_end),
          (try_end),
        (else_try), #vote for first team
          (assign, "$g_multiplayer_factions_list_action_type", 2), #for team 2
@@ -7114,9 +7477,12 @@
            (multiplayer_get_my_player, ":my_player_no"),
            (ge, ":my_player_no", 0),
            (multiplayer_send_3_int_to_server, multiplayer_event_start_new_poll, 4, "$g_multiplayer_poll_number_of_bots_team_1", ":used_value"),
            (store_mission_timer_a, ":mission_timer"),
            (val_add, ":mission_timer", multiplayer_poll_disable_period),
            (player_set_slot, ":my_player_no", slot_player_poll_disabled_until_time, ":mission_timer"),
            (try_begin),
              (neg|player_is_admin, ":my_player_no"),
              (store_mission_timer_a, ":mission_timer"),
              (val_add, ":mission_timer", multiplayer_poll_disable_period),
              (player_set_slot, ":my_player_no", slot_player_poll_disabled_until_time, ":mission_timer"),
            (try_end),
          (try_end),
        (else_try), #vote for first team
          (assign, "$g_multiplayer_number_of_bots_list_action_type", 2), #for team 2
@@ -7141,29 +7507,56 @@
    [(set_fixed_point_multiplier, 1000),

      (create_mesh_overlay, reg0, "mesh_white_plane"),
      (overlay_set_color, reg0, 0x000000),
      (overlay_set_color, reg0, 0x110000),
      (overlay_set_alpha, reg0, 0x44),
      (position_set_x, pos1, 50),
      (position_set_y, pos1, 50),
      (position_set_x, pos1, 10),
      (position_set_y, pos1, 10),
      (overlay_set_position, reg0, pos1),
      (position_set_x, pos1, 37500),
      (position_set_y, pos1, 4500),
      (position_set_x, pos1, 40500),
      (position_set_y, pos1, 2500),
      (overlay_set_size, reg0, pos1),

      (try_begin),
        (eq, "$g_multiplayer_poll_to_show", 3), #extract upper bits to get requester, extract lower for faction 1
        (store_div, ":requester", "$g_multiplayer_poll_value_2_to_show", multiplayer_pack_message_shift_size),
        (store_mod, "$g_multiplayer_poll_value_2_to_show", "$g_multiplayer_poll_value_2_to_show", multiplayer_pack_message_shift_size),
      (else_try),
        (eq, "$g_multiplayer_poll_to_show", 4),
        (assign, ":requester", "$g_multiplayer_poll_value_3_to_show"),
      (else_try),
        (assign, ":requester", "$g_multiplayer_poll_value_2_to_show"),
      (try_end),
      (str_store_string, s1, "str_somebody"),
      (try_begin),
        (is_between, ":requester", 0, multiplayer_max_possible_player_id),
        (player_is_active, ":requester"),
        (str_store_player_username, s1, ":requester"),
      (try_end),
      (create_text_overlay, reg0, "str_poll_called_by", tf_center_justify|tf_with_outline),
      (overlay_set_color, reg0, 0xFFFFFF),
      (position_set_x, pos1, 350),
      (position_set_y, pos1, 15),
      (overlay_set_position, reg0, pos1),
      (position_set_x, pos1, 850),
      (position_set_y, pos1, 850),
      (overlay_set_size, reg0, pos1),

      (try_begin),
        (eq, "$g_multiplayer_poll_to_show", 0),
        (store_sub, ":string_index", "$g_multiplayer_poll_value_to_show", multiplayer_scenes_begin),
        (val_add, ":string_index", multiplayer_scene_names_begin),
        (str_store_string, s0, ":string_index"),
        (create_text_overlay, reg0, "str_poll_change_map", tf_center_justify),
        (create_text_overlay, reg0, "str_poll_change_map", tf_center_justify|tf_with_outline),
      (else_try),
        (eq, "$g_multiplayer_poll_to_show", 1),
        (str_store_player_username, s0, "$g_multiplayer_poll_value_to_show"),
        (create_text_overlay, reg0, "str_poll_kick_player", tf_center_justify),
        (call_script, "script_multiplayer_get_reason_string", "$g_multiplayer_poll_value_3_to_show"),
        (create_text_overlay, reg0, "str_poll_kick_player", tf_center_justify|tf_with_outline),
      (else_try),
        (eq, "$g_multiplayer_poll_to_show", 2),
        (str_store_player_username, s0, "$g_multiplayer_poll_value_to_show"),
        (create_text_overlay, reg0, "str_poll_ban_player", tf_center_justify),
        (call_script, "script_multiplayer_get_reason_string", "$g_multiplayer_poll_value_3_to_show"),
        (create_text_overlay, reg0, "str_poll_ban_player", tf_center_justify|tf_with_outline),
      (else_try),
        (eq, "$g_multiplayer_poll_to_show", 3),
        (store_sub, ":string_index", "$g_multiplayer_poll_value_to_show", multiplayer_scenes_begin),
@@ -7171,55 +7564,98 @@
        (str_store_string, s0, ":string_index"),
        (str_store_faction_name, s1, "$g_multiplayer_poll_value_2_to_show"),
        (str_store_faction_name, s2, "$g_multiplayer_poll_value_3_to_show"),
        (create_text_overlay, reg0, "str_poll_change_map_with_faction", tf_center_justify|tf_scrollable_style_2),
        (create_text_overlay, reg0, "str_poll_change_map_with_faction", tf_center_justify|tf_with_outline),
      (else_try),
        (eq, "$g_multiplayer_poll_to_show", 4),
        (assign, reg0, "$g_multiplayer_poll_value_to_show"),
        (assign, reg1, "$g_multiplayer_poll_value_2_to_show"),
        (str_store_faction_name, s0, "$g_multiplayer_team_1_faction"),
        (str_store_faction_name, s1, "$g_multiplayer_team_2_faction"),
        (create_text_overlay, reg0, "str_poll_change_number_of_bots", tf_center_justify|tf_scrollable_style_2),
      (try_end),
      (overlay_set_color, reg0, 0xFFFFFF),
      (try_begin),
        (neq, "$g_multiplayer_poll_to_show", 3),
        (neq, "$g_multiplayer_poll_to_show", 4),
        (position_set_x, pos1, 400),
        (position_set_y, pos1, 100),
        (overlay_set_position, reg0, pos1),
        (create_text_overlay, reg0, "str_poll_change_number_of_bots", tf_center_justify|tf_with_outline),
      (else_try),
        (position_set_x, pos1, 50),
        (position_set_y, pos1, 70),
        (overlay_set_position, reg0, pos1),
        (position_set_x, pos1, 750),
        (position_set_y, pos1, 60),
        (overlay_set_area_size, reg0, pos1),
        (eq, "$g_multiplayer_poll_to_show", 5),
        (create_text_overlay, reg0, s37, tf_center_justify|tf_with_outline),
      (try_end),
      (overlay_set_color, reg0, 0xFFFFFF),
      (position_set_x, pos1, 415),
      (position_set_y, pos1, 35),
      (overlay_set_position, reg0, pos1),
      (position_set_x, pos1, 850),
      (position_set_y, pos1, 850),
      (overlay_set_size, reg0, pos1),

      (store_mission_timer_a, ":mission_timer"),
      (store_sub, "$g_multiplayer_poll_last_written_seconds_left", "$g_multiplayer_poll_client_end_time", ":mission_timer"),
      (assign, reg0, "$g_multiplayer_poll_last_written_seconds_left"),

      (create_text_overlay, "$g_presentation_obj_poll_1", "str_poll_time_left", tf_right_align|tf_single_line),
      (create_text_overlay, "$g_presentation_obj_poll_1", "str_poll_time_left", tf_right_align|tf_single_line|tf_with_outline),
      (overlay_set_color, "$g_presentation_obj_poll_1", 0xFFFFFF),
      (position_set_x, pos1, 790),
      (position_set_y, pos1, 60),
      (position_set_x, pos1, 860),
      (position_set_y, pos1, 15),
      (overlay_set_position, "$g_presentation_obj_poll_1", pos1),
      (position_set_x, pos1, 850),
      (position_set_y, pos1, 850),
      (overlay_set_size, "$g_presentation_obj_poll_1", pos1),

      (omit_key_once, key_1),
      (omit_key_once, key_2),
      (try_begin),
        (multiplayer_get_my_player, ":my_player_no"),
        (player_is_admin, ":my_player_no"),
        (str_clear, s0),
        (create_text_overlay, "$g_presentation_obj_poll_2", s0, tf_center_justify|tf_with_outline),
        (overlay_set_color, "$g_presentation_obj_poll_2", 0xFF0000),
        (position_set_x, pos1, 415),
        (position_set_y, pos1, -60),
        (overlay_set_position, "$g_presentation_obj_poll_2", pos1),
        (assign, "$g_multiplayer_poll_admin_warning", 0),
      (else_try),
        (assign, "$g_presentation_obj_poll_2", -1),
      (try_end),

      (omit_key_once, key_f:cool:,
      (omit_key_once, key_f9),
      (omit_key_once, key_f10),
      (presentation_set_duration, 999999),
      ]),
    (ti_on_presentation_run,
    [(store_trigger_param_1, ":cur_time"),
      (try_begin),
        (this_or_next|key_clicked, key_escape),
        (key_clicked, key_2),
        (key_clicked, key_f10),
        (gt, ":cur_time", 500),
        (multiplayer_send_int_to_server, multiplayer_event_answer_to_poll, 2),
        (clear_omitted_keys),
        (presentation_set_duration, 0),
      (else_try), #warn admin on first key press
        (neq, "$g_presentation_obj_poll_2", -1),
        (assign, ":continue", 1),
        (try_begin),
          (key_clicked, key_f9),
          (try_begin),
            (eq, "$g_multiplayer_poll_admin_warning", -1),
            (assign, ":continue", 0),
          (end_try),
          (assign, "$g_multiplayer_poll_admin_warning", -1),
          (overlay_set_text, "$g_presentation_obj_poll_2", "str_poll_admin_confirm_reject"),
        (else_try),
          (key_clicked, key_f:cool:,
          (try_begin),
            (eq, "$g_multiplayer_poll_admin_warning", 1),
            (assign, ":continue", 0),
          (end_try),
          (assign, "$g_multiplayer_poll_admin_warning", 1),
          (overlay_set_text, "$g_presentation_obj_poll_2", "str_poll_admin_confirm_accept"),
        (end_try),
        (eq, ":continue", 1),
        (position_set_x, pos1, 415),
        (position_set_y, pos1, 60),
        (overlay_set_position, "$g_presentation_obj_poll_2", pos1),
      (else_try),
        (key_clicked, key_f9),
        (gt, ":cur_time", 500),
        (multiplayer_send_int_to_server, multiplayer_event_answer_to_poll, 0),
        (clear_omitted_keys),
        (presentation_set_duration, 0),
      (else_try),
        (key_clicked, key_1),
        (key_clicked, key_f:cool:,
        (gt, ":cur_time", 500),
        (multiplayer_send_int_to_server, multiplayer_event_answer_to_poll, 1),
        (clear_omitted_keys),
@@ -7230,6 +7666,7 @@
      (try_begin),
        (neq, ":time_left", "$g_multiplayer_poll_last_written_seconds_left"),
        (try_begin),
          (this_or_next|neq, "$g_multiplayer_poll_admin_override", 0),
          (lt, ":time_left", 0),
          (clear_omitted_keys),
          (presentation_set_duration, 0),
 
Excellent work Vornne! I've been wanting to do something like this myself for some time now, but lack of time has not permitted me. I doubt anyone could've done this better though, looks great.
 
Thanks everyone :smile:.

I've uploaded the text files for players to try it out more easily here.

If mod creators only want to implement a subset of this mod, I can give them adjusted patches to only apply certain features quite easily; as nearly all separate features are in a different commit to my local repository; though I might have to do a bit of patching if some of the main features were taken out. Is the format of the patching instructions easy to use?

As for sending to a developer, I though about that earlier; but since my feature request for a poll reason was rejected, along with other similar suggestions - I'm guessing because no new features requiring translation will be added, as was said on a similar request - it might interest them, but I doubt anything would be implemented. A bit sad really; I wanted some of these features to be part of the standard version for all players to use and mods to be based on. The next best thing would be for the people making enhancement mods to include this - as by itself I don't really think it's enough to be a standalone mod, and I'd rather just support other ones.

What do you people think of the various features? Are there any that you think are bad ideas, or that could be tweaked to be even better? Any opinions about the admin override feature, disabling escape from voting no, or the poll message visual style?
 
This is fecking awesome Vornne, good job mate.

This really needs some publicity, it will make everyones lives so much easier.
 
Good job vornne, at last someone put in the work to do what has been needed.. Ive wanted many of these features to make it easier to admin servers. Devs wont listen to people so someone had to do it.

And its our vornnie! lol

If / when this has all the bugs worked out Id be interested to look at getting it on our servers.
 
ACC1 said:
If / when this has all the bugs worked out Id be interested to look at getting it on our servers.
I think I have tested most things and confirmed they work, and hopefully ruled out most edge cases; but from past experiences I'm pretty certain that some bugs will come to light eventually. The next step would be testing things with a group of players while actually playing: I thought it could be added to the Oceanic mod, so more people would test it... what do you think, Alec?

The unfortunate effect I see from putting it on the GON servers is that new players in the region wouldn't have local servers to try it out on any more, soon after installing the game; but maybe a mod including it could be put on the test server, eventually?
 
It should be put on the test server. I mean, what is the name for if not? :razz:

We can test it out and give feedback, then once it is perfect we can stick it on all the other servers too. :smile:
 
Vornne said:
ACC1 said:
If / when this has all the bugs worked out Id be interested to look at getting it on our servers.
I think I have tested most things and confirmed they work, and hopefully ruled out most edge cases; but from past experiences I'm pretty certain that some bugs will come to light eventually. The next step would be testing things with a group of players while actually playing: I thought it could be added to the Oceanic mod, so more people would test it... what do you think, Alec?

The unfortunate effect I see from putting it on the GON servers is that new players in the region wouldn't have local servers to try it out on any more, soon after installing the game; but maybe a mod including it could be put on the test server, eventually?

Thats the thing I was worried about..

Will it require everyone to download and run a mod for it to work or can it just run server side and everyone join as usual?

This would make it much harder like you said for new players and people who dont check the forums nor use mumble/TS to ask for help with stuff like this so we might loose players because of it.

Would be awesome if people didnt need a download tho.
 
Back
Top Bottom