mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-18 19:00:26 +02:00
Revert "Add an in-game text chat for network games"
This commit is contained in:
1 parent
be521e2621
commit
4ac609f0df
31 files changed
+43
-3510
No files matched your search
-86
@@ -1,86 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- The port's settings screen, in the PC version's style (tools/port_settings.py) -->
|
||||
<menus>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/chat_settings_screen" width="640" height="480" flags="pass_unhandled_to_focused_child pause_game" bitmap="bitmaps/gradient">
|
||||
<on event="b" back="true"/>
|
||||
<on event="back" back="true"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/header_profile_audio_settings"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/options_menu"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/header_profile_audio_settings" controller="1" left="35" top="11" width="605" height="59" bitmap="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/header_profile_audio_settings">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/help" type="text" controller="1" left="68" top="350" width="482" height="60" string_list="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/help_strings" font="ui\large_ui" color="#FFFFFFFF">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/options_menu" type="column_list" width="640" height="480" flags="pass_unhandled_to_focused_child up_down_tabs_children" description="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/help">
|
||||
<data input="port settings help"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/op_voice_chat" x="54" y="73"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/op_voice_volume" x="54" y="103"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/op_chat_style" x="54" y="133"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/op_streamer_mode" x="54" y="163"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/button_bar" y="414"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/button_bar" type="column_list" width="640" height="28" flags="pass_unhandled_to_focused_child left_right_tabs_items">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/button_defaults" y="1"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/button_ok" x="380" y="1"/>
|
||||
<child widget="common_button_cancel" x="510" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/op_voice_chat" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/voice_chat_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/voice_chat_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/voice_chat_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/labels" string_index="0" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/voice_chat_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="PUSH TO TALK|OPEN MIC|OFF" setting="audio.voice_chat" values="push_to_talk|open_mic|off" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/op_voice_volume" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/voice_volume_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/voice_volume_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/voice_volume_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/labels" string_index="1" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/voice_volume_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="0|1|2|3|4|5|6|7|8|9|10" setting="audio.voice_volume" values="0|0.1|0.2|0.3|0.4|0.5|0.6|0.7|0.8|0.9|1" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/op_chat_style" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/chat_style_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/chat_style_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/chat_style_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/labels" string_index="2" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/chat_style_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="DEFAULT|WHITE" setting="display.chat_style" values="default|white" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/op_streamer_mode" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/streamer_mode_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/streamer_mode_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/streamer_mode_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/labels" string_index="3" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/streamer_mode_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="ON|OFF" setting="game.streamer_mode" values="true|false" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/button_defaults" type="text" width="128" height="24" bitmap="bitmaps/text_button_background" string_list="strings/common_button_captions" string_index="3" font="ui\small_ui" color="#FFFFFFFF" align="center" text_y="2">
|
||||
<on event="a" run="port settings defaults"/>
|
||||
<on event="start" run="port settings defaults"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/button_ok" type="text" width="128" height="24" bitmap="bitmaps/text_button_background" string_list="strings/common_button_captions" string_index="1" font="ui\small_ui" color="#FFFFFFFF" align="center" text_y="2">
|
||||
<on event="a" run="port settings save" back="true"/>
|
||||
<on event="start" run="port settings save" back="true"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
</widget>
|
||||
<strings name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/labels">
|
||||
<string text="VOICE CHAT:"/>
|
||||
<string text="VOICE VOLUME:"/>
|
||||
<string text="CHAT TEXT:"/>
|
||||
<string text="STREAMER MODE:"/>
|
||||
</strings>
|
||||
<strings name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/help_strings">
|
||||
<string text=""/>
|
||||
<string text="Talk in network games: while PUSH TO TALK is held\n(Controls), whenever you speak, or never."/>
|
||||
<string text="The other players' voices."/>
|
||||
<string text="The text chat's colors: DEFAULT, names in their\nteam's color; WHITE, all white, as Custom Edition."/>
|
||||
<string text="Show slurs and swearing in the text chat as\nharmless words, for streaming."/>
|
||||
</strings>
|
||||
</menus>
|
||||
+29
-15
@@ -23,10 +23,12 @@
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_effects_volume" x="54" y="133" platform="android web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_enabled" x="54" y="223" platform="desktop"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_enabled" x="54" y="163" platform="android web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_sound_processing" x="54" y="253" platform="desktop"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_sound_processing" x="54" y="193" platform="android web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_chat" x="54" y="283" platform="desktop"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_chat" x="54" y="223" platform="android web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_voice_chat" x="54" y="253" platform="desktop"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_voice_chat" x="54" y="193" platform="android web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_voice_volume" x="54" y="283" platform="desktop"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_voice_volume" x="54" y="223" platform="android web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_sound_processing" x="54" y="313" platform="desktop"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_sound_processing" x="54" y="253" platform="android web"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/button_bar" y="414"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/button_bar" type="column_list" width="640" height="28" flags="pass_unhandled_to_focused_child left_right_tabs_items">
|
||||
@@ -88,21 +90,31 @@
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/enabled_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="ON|OFF" setting="audio.enabled" values="true|false" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_voice_chat" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/voice_chat_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/voice_chat_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/voice_chat_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/labels" string_index="6" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/voice_chat_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="PUSH TO TALK|OPEN MIC|OFF" setting="audio.voice_chat" values="push_to_talk|open_mic|off" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_voice_volume" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/voice_volume_label"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/voice_volume_spinner" x="320" y="1"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/voice_volume_label" type="text" controller="1" width="300" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/labels" string_index="7" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/voice_volume_spinner" type="spinner" left="3" top="2" width="147" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="0|1|2|3|4|5|6|7|8|9|10" setting="audio.voice_volume" values="0|0.1|0.2|0.3|0.4|0.5|0.6|0.7|0.8|0.9|1" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 150 19 156">
|
||||
<on event="created" run="port setting load"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_sound_processing" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<on event="a" open="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/sound_processing_settings_screen"/>
|
||||
<on event="start" open="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing/sound_processing_settings_screen"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing_label"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing_label" type="text" controller="1" width="512" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/labels" string_index="6" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/op_chat" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<on event="a" open="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/chat_settings_screen"/>
|
||||
<on event="start" open="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat/chat_settings_screen"/>
|
||||
<on event="left_mouse" run="mouse emit accept event"/>
|
||||
<child widget="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat_label"/>
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/chat_label" type="text" controller="1" width="512" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/labels" string_index="7" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/sound_processing_label" type="text" controller="1" width="512" height="22" string_list="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/labels" string_index="8" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/button_defaults" type="text" width="128" height="24" bitmap="bitmaps/text_button_background" string_list="strings/common_button_captions" string_index="3" font="ui\small_ui" color="#FFFFFFFF" align="center" text_y="2">
|
||||
<on event="a" run="port settings defaults"/>
|
||||
@@ -121,8 +133,9 @@
|
||||
<string text="MUSIC VOLUME:"/>
|
||||
<string text="EFFECTS VOLUME:"/>
|
||||
<string text="SOUND:"/>
|
||||
<string text="VOICE CHAT:"/>
|
||||
<string text="VOICE VOLUME:"/>
|
||||
<string text="SOUND PROCESSING:"/>
|
||||
<string text="VOICE AND TEXT CHAT:"/>
|
||||
</strings>
|
||||
<strings name="main_menu/settings_select/player_setup/player_profile_edit/audio_settings/help_strings">
|
||||
<string text=""/>
|
||||
@@ -132,7 +145,8 @@
|
||||
<string text="The music's volume."/>
|
||||
<string text="The volume of every other sound: effects and\nspeech."/>
|
||||
<string text="Play sound at all; from the next time the game\nstarts."/>
|
||||
<string text="Talk in network games: while PUSH TO TALK is held\n(Controls), whenever you speak, or never."/>
|
||||
<string text="The other players' voices."/>
|
||||
<string text="The reverb, and how sounds are converted to the\noutput's rate."/>
|
||||
<string text="Voice chat, and the text chat's streamer\nmode."/>
|
||||
</strings>
|
||||
</menus>
|
||||
+1
-1
@@ -35,7 +35,7 @@
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/controls_setup/group_label" type="text" controller="1" width="200" height="22" text="SHOW:" font="ui\large_ui" color="#FF2896FF" text_x="13" text_y="4">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/controls_setup/group_spinner" type="spinner" left="3" top="2" width="297" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="MOVEMENT|WEAPONS|ACTIONS|COMMUNICATION" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 300 19 306">
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/controls_setup/group_spinner" type="spinner" left="3" top="2" width="297" height="20" flags="pass_unhandled_to_focused_child left_right_tabs_items" strings="MOVEMENT|WEAPONS|ACTIONS" font="ui\large_ui" color="#FF2896FF" align="center" text_y="1" header_bitmap="bitmaps/arrow_sm_left" footer_bitmap="bitmaps/arrow_sm_right" header_bounds="7 -6 19 0" footer_bounds="7 300 19 306">
|
||||
</widget>
|
||||
<widget name="main_menu/settings_select/player_setup/player_profile_edit/controls_setup/op_command_1" width="512" height="28" flags="pass_unhandled_to_focused_child" bitmap="bitmaps/option_bkds" color="#FF2896FF">
|
||||
<on event="a" run="controls begin binding"/>
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
"ce/main_menu.settings_select.multiplayer_setup.teamplay_options_edit.xml",
|
||||
"ce/main_menu.settings_select.multiplayer_setup.vehicle_options_edit.xml",
|
||||
"ce/main_menu.settings_select.multiplayer_setup.xml",
|
||||
"ce/main_menu.settings_select.player_setup.player_profile_edit.audio_settings.chat.xml",
|
||||
"ce/main_menu.settings_select.player_setup.player_profile_edit.audio_settings.sound_processing.xml",
|
||||
"ce/main_menu.settings_select.player_setup.player_profile_edit.audio_settings.xml",
|
||||
"ce/main_menu.settings_select.player_setup.player_profile_edit.color_edit.xml",
|
||||
|
||||
+1
-32
@@ -280,31 +280,6 @@ as it changes), for the scoreboard, whose right click frees the mouse to
|
||||
pick a player; the host's own scoreboard adds Kick and Ban. Builds without
|
||||
votes drop both kinds as unknown, so the network version is unchanged.
|
||||
|
||||
The text chat (`network_chat.c`): a client sends its player's line to the
|
||||
host (`_distributed_message_chat_up`, reliably), which takes it only from
|
||||
the client's own stream and from one of that machine's players, at most 5
|
||||
lines at once and one each 1.5 seconds after (more are dropped), its text
|
||||
cut to 63 characters of printable ASCII. The host alone sends the line on
|
||||
(`_distributed_message_chat_down`, reliably, with the sender's absolute
|
||||
index and its channel, the line's flags): to every client, a team's line
|
||||
only to the machines with a player of the sender's team (to every one in a
|
||||
game without teams), and a vehicle's line only to those with a player
|
||||
riding in the sender's vehicle (the outermost object each unit is a child
|
||||
of, so any map's vehicles), as the host has them; the sender's machine gets
|
||||
its own line whatever it is. The host shows it itself if one of its own
|
||||
players hears it or said it. A client shows only
|
||||
the host's lines, its own too, so every machine shows them in one order.
|
||||
The host's own `/mute <player> [minutes]` and `/unmute <player>` are
|
||||
commands, not lines: it keeps a mute by the player's name (in ASCII) and
|
||||
its minutes by its own clock, drops the lines of a muted player's machine
|
||||
when they come up, and sends that machine alone a notice of the minutes
|
||||
left; it tells every machine of each mute and unmute. A notice is a down
|
||||
line with `_chat_line_notice_bit` set, its text alone (no sender). A
|
||||
client's `/mute` is never sent, and the host takes the text of a line that
|
||||
came up as a line, never a command.
|
||||
Builds without the chat drop both kinds as unknown, so the network version
|
||||
is unchanged.
|
||||
|
||||
Voice chat (`network_voice.c`, the sound in `port/linux/src/voice_audio.c`)
|
||||
travels as distributed messages too, but in the lobby as well as in a game:
|
||||
`network_voice_handles_message` takes them before the netcode's checks that
|
||||
@@ -763,13 +738,7 @@ two machines' views of one game can be compared. `debug.network_test_kill`,
|
||||
`debug.network_test_shoot`, `debug.network_test_vehicle` and
|
||||
`debug.network_test_pickup` script kills, hits, a vehicle ride and a weapon
|
||||
swap the bots' wandering does not reach (`debug.network_test_pickup_weapon`
|
||||
picks the weapon: the first whose tag name has it in it, as "sniper"),
|
||||
`debug.network_test_chat` has each machine's player say a chat line to
|
||||
everyone, one to the team and one to the vehicle (the host seats the last
|
||||
two players in one first), then the host mutes the last player for a
|
||||
minute, each says a line, the host unmutes them and each says another,
|
||||
which every machine's `debug.txt` records as
|
||||
it shows them (the notices as `chat notice:`), and `debug.network_test_score`
|
||||
picks the weapon: the first whose tag name has it in it, as "sniper"), and `debug.network_test_score`
|
||||
shortens the game, to test the next (`host:<map>:<variant>,<variant>...`
|
||||
plays the variants in turn, the next once a game is over, as the host's
|
||||
button on the scores does). `debug.network_test_public` lists the host's
|
||||
|
||||
+3
-56
@@ -212,9 +212,6 @@ gamepads' only.
|
||||
| pause menu | escape |
|
||||
| screenshot | F10 |
|
||||
| talk in voice chat (hold) | V |
|
||||
| type a chat line to everyone | T |
|
||||
| type a chat line to your team | Y |
|
||||
| while typing a chat line: to everyone, the team or the vehicle, in turn | Tab |
|
||||
|
||||
Always: \` opens the developer console, F12 releases or captures the mouse,
|
||||
F11 changes between fullscreen and window. Screenshot (default F10,
|
||||
@@ -351,7 +348,6 @@ the setting for one start of the game. It has priority over the file.
|
||||
| `display.menus` | `"pc"` | `HALO_MENUS` | `"pc"`: the PC version's menus, from the files in `port/assets/menus` and a `menus` folder next to `config.toml`. Refer to "Menus". `"xbox"`: the Xbox's menus. |
|
||||
| `display.player_names` | `"all"` | `HALO_PLAYER_NAMES` | In multiplayer, whose names are drawn above their heads: `"all"`, `"allies"`, `"enemies"` or `"none"`. An ally's name is drawn above the triangle the game shows over teammates. An enemy's name shows only within the motion sensor's reach, while the enemy is in sight and not camouflaged, so it never shows where an enemy hides. The gametype's motion tracker setting also applies: no names if it shows no players, only allies' if it shows only friends. |
|
||||
| `display.player_name_scale` | `1.0` | `HALO_PLAYER_NAME_SCALE` | How large the players' names are drawn: `1.0` is three quarters of the size of the HUD's text, from `0.25` to `4`. With high-res text, larger names are rasterized at their size, so they stay sharp. |
|
||||
| `display.chat_style` | `"default"` | `HALO_CHAT_STYLE` | The text chat's colours (Settings > Audio > Voice and Text Chat > CHAT TEXT): `"default"`, the player's name in their team's colour and the text a pale blue, as the PC version drew them; `"white"`, all of it white, as Custom Edition. |
|
||||
| `display.scoreboard_team_layout` | `"teams"` | `HALO_SCOREBOARD_TEAM_LAYOUT` | How the multiplayer scoreboard (hold BACK, or tab) lists a team game's players. `"teams"`: a column for each team, red on the left and blue on the right. `"score"`: all the players in order of score. With more players than fit, the mouse wheel and Page Up / Page Down scroll the scoreboard. |
|
||||
| `display.scoreboard_background` | `true` | `HALO_SCOREBOARD_BACKGROUND` | `true`: the multiplayer scoreboard (hold BACK, or tab) has a panel behind its text, for clearer text. |
|
||||
| `display.scoreboard_background_color` | `"16, 16, 16, 150"` | `HALO_SCOREBOARD_BACKGROUND_COLOR` | The colour of the scoreboard's panel: `"red, green, blue, alpha"`, each from `0` to `255`. Alpha `0` is see-through, `255` is solid. |
|
||||
@@ -371,10 +367,9 @@ the setting for one start of the game. It has priority over the file.
|
||||
| `input.mouse_vertical_sensitivity` | `0.0` | `HALO_MOUSE_VERTICAL_SENSITIVITY` | The multiplier for the vertical mouse aim. `0`: the same as `input.mouse_sensitivity`. |
|
||||
| `input.invert_mouse` | `false` | `HALO_MOUSE_INVERT=1` sets `true` | `true`: the vertical mouse aim is inverted. |
|
||||
| `input.mouse_aim_assist` | `false` | `HALO_MOUSE_AIM_ASSIST` | `true`: the magnetism of the controller also operates for the mouse. `false`: when the mouse moved after the right stick, the view is not slowed or dragged by a target. The autoaim of the bullets operates in both cases. |
|
||||
| `controls.<action>` | (the table in "Controls") | `HALO_KEY_<ACTION>` | The keys and mouse buttons of an action, up to two, separated by a comma: `move_forward`, `move_backward`, `strafe_left`, `strafe_right`, `jump`, `crouch`, `fire`, `throw_grenade`, `melee`, `reload`, `zoom`, `switch_weapon`, `switch_grenade`, `action`, `flashlight`, `scoreboard`, `pause`, `screenshot`, `push_to_talk`, `chat`, `team_chat`. Keys by their names (`"W"`, `"Space"`, `"Left Ctrl"`, `"F1"`), and `"Mouse Left"`, `"Mouse Right"`, `"Mouse Middle"`, `"Mouse 4"`, `"Mouse 5"`, `"Wheel"` (either way), `"Wheel Up"`, `"Wheel Down"`. |
|
||||
| `controls.<action>` | (the table in "Controls") | `HALO_KEY_<ACTION>` | The keys and mouse buttons of an action, up to two, separated by a comma: `move_forward`, `move_backward`, `strafe_left`, `strafe_right`, `jump`, `crouch`, `fire`, `throw_grenade`, `melee`, `reload`, `zoom`, `switch_weapon`, `switch_grenade`, `action`, `flashlight`, `scoreboard`, `pause`, `screenshot`, `push_to_talk`. Keys by their names (`"W"`, `"Space"`, `"Left Ctrl"`, `"F1"`), and `"Mouse Left"`, `"Mouse Right"`, `"Mouse Middle"`, `"Mouse 4"`, `"Mouse 5"`, `"Wheel"` (either way), `"Wheel Up"`, `"Wheel Down"`. |
|
||||
| `game.console_log` | `"important"` | `HALO_CONSOLE_LOG` | What the console shows on the screen. `"important"`: bans, players that the host drops for cheating, the reasons that the game refuses a command, and the asserts that stop the game. `"all"`: all the lines. `"none"`: only the asserts that stop the game. The output of a command always shows. `debug.txt` gets all the lines. |
|
||||
| `game.language` | `""` | `HALO_LANGUAGE` | The language of the menus: `ja`, `de`, `fr`, `es` or `it`. Empty: English. |
|
||||
| `game.streamer_mode` | `false` | `HALO_STREAMER_MODE` | Streamer mode (Settings > Audio > Voice and Text Chat > STREAMER MODE): the text chat shows the slurs and swearing in players' names and lines as harmless words. Only what this machine shows. Refer to "Text chat". |
|
||||
| `game.enhanced_animations` | `true` | `HALO_ENHANCED_ANIMATIONS` | `true`: the player bipeds' grenade throws keep their legs moving, blended by speed and direction (crouched throws stay crouched, throws in the air use the jump's legs), Warthog and Scorpion riders stay seated to throw and let go of the grips to throw and reload, and a player turns with the aim while throwing, as while meleeing. `false`: the original animations, which freeze the legs during a throw and stand a rider up. Only in config.toml, not in the menus. |
|
||||
| `paths.data` | `""` | `HALO_DATA_ROOT` | The data root. Refer to "Start the game". |
|
||||
| `paths.saves` | `""` | `HALO_SAVE_ROOT` | The save root. Refer to "Files and folders". |
|
||||
@@ -416,7 +411,7 @@ the setting for one start of the game. It has priority over the file.
|
||||
| `debug.gpu_stats`, `debug.gpu_trace_frame`, `debug.gpu_trace_constants`, `debug.gpu_dump_shaders`, `debug.texture_dump_directory`, `debug.texture_log`, `debug.gl_debug`, `debug.texture_no_cache` | off | `HALO_GPU_STATS`, `HALO_GPU_TRACE`, `HALO_GPU_TRACE_CONSTANTS`, `HALO_GPU_DUMP_SHADERS`, `HALO_TEXTURE_DUMP`, `HALO_TEXTURE_LOG`, `HALO_GL_DEBUG`, `HALO_TEXTURE_NO_CACHE` | Tools to find problems in the graphics: counts for each frame, all the GL state of one frame, the GLSL code, the textures. |
|
||||
| `debug.menu_open` | `""` | `HALO_MENU_OPEN` | Start on this screen of the menus (`main_menu/settings_select/...`, as `port/assets/menus` names it), a player profile being edited, to look at it. |
|
||||
| `debug.gpu_skip_vertex_shaders`, `debug.gpu_debug_expression`, `debug.gpu_debug_flat`, `debug.gpu_debug_texture0` | off | `HALO_GPU_SKIP_VS`, `HALO_GPU_DEBUG_EXPR`, `HALO_GPU_DEBUG_FLAT`, `HALO_GPU_DEBUG_T0` | Tools to find problems in the graphics: skip the draws of a vertex shader, or replace the output of all pixel shaders with a GLSL expression (for example `t0.rgb`). |
|
||||
| `debug.network_test`, `debug.network_test_start`, `debug.network_test_kill`, `debug.network_test_score`, `debug.network_test_shoot`, `debug.network_test_vehicle`, `debug.network_test_pickup`, `debug.network_test_pickup_weapon`, `debug.network_test_chat`, `debug.network_test_name`, `debug.network_test_public`, `debug.test_input` | off | `HALO_NETWORK_TEST`, `HALO_NETWORK_TEST_START`, `HALO_NETWORK_TEST_KILL`, `HALO_NETWORK_TEST_SCORE`, `HALO_NETWORK_TEST_SHOOT`, `HALO_NETWORK_TEST_VEHICLE`, `HALO_NETWORK_TEST_PICKUP`, `HALO_NETWORK_TEST_PICKUP_WEAPON`, `HALO_NETWORK_TEST_CHAT`, `HALO_NETWORK_TEST_NAME`, `HALO_NETWORK_TEST_PUBLIC`, `HALO_TEST_INPUT` | Automatic tests of system link (`game/network_test.c`). Refer to `NETCODE.md`. |
|
||||
| `debug.network_test`, `debug.network_test_start`, `debug.network_test_kill`, `debug.network_test_score`, `debug.network_test_shoot`, `debug.network_test_vehicle`, `debug.network_test_pickup`, `debug.network_test_pickup_weapon`, `debug.network_test_public`, `debug.test_input` | off | `HALO_NETWORK_TEST`, `HALO_NETWORK_TEST_START`, `HALO_NETWORK_TEST_KILL`, `HALO_NETWORK_TEST_SCORE`, `HALO_NETWORK_TEST_SHOOT`, `HALO_NETWORK_TEST_VEHICLE`, `HALO_NETWORK_TEST_PICKUP`, `HALO_NETWORK_TEST_PICKUP_WEAPON`, `HALO_NETWORK_TEST_PUBLIC`, `HALO_TEST_INPUT` | Automatic tests of system link (`game/network_test.c`). Refer to `NETCODE.md`. |
|
||||
| `debug.touch_targets` | `false` | `HALO_TOUCH_TARGETS` | Outlines the tap targets of the menus (item green, value blue, list slot yellow, legend button red, the band beside the slots of a list orange, keys of the on-screen keyboard white), marks where the last finger went down and the last tap landed for 3 seconds, and logs each tap with the target that it hit (for a value, also where it splits into previous and next): to judge the accuracy of touch. |
|
||||
| `debug.network_latency`, `debug.network_loss`, `debug.network_corrupt`, `debug.network_corrupt_stream`, `debug.network_corrupt_after` | `0` | `HALO_NETWORK_LATENCY`, `HALO_NETWORK_LOSS`, `HALO_NETWORK_CORRUPT`, `HALO_NETWORK_CORRUPT_STREAM`, `HALO_NETWORK_CORRUPT_AFTER` | The game holds all the data that it receives for this number of milliseconds, ignores this percentage of the datagrams, and damages this percentage of the datagrams it receives, and this percentage of its reads of streams, at random (bytes changed, cut short, stretched or replaced), from this many seconds after the start. Use the first two to test the netcode as on the internet, and the others to test that nothing another machine sends can crash the game (a damaged stream is closed, so a little goes a long way; a host's messages to its own client are damaged too, so start damaging once the game has started). |
|
||||
| `debug.voice_test` | `false` | `HALO_VOICE_TEST` | Automatic tests of voice chat: a tone replaces the microphone, and each voice that the game hears is written to the log once each second. |
|
||||
@@ -661,59 +656,11 @@ as between native builds, two networks that do not let a direct connection
|
||||
through (some company and school networks block WebRTC) cannot play
|
||||
together.
|
||||
|
||||
### Text chat
|
||||
|
||||
Network games have a text chat, as the PC version had. Press T
|
||||
(`controls.chat`) to type a line to all the players, or Y
|
||||
(`controls.team_chat`) to type one to your team (in a game without teams,
|
||||
to all the players). While you type, Tab changes who the line is to, in
|
||||
turn: all the players, your team, or your vehicle (the players riding in
|
||||
the vehicle you ride in, whatever the map's vehicles are, Custom Edition
|
||||
maps' too; on foot, only you see it). Enter sends the line, Escape cancels
|
||||
it. While you type, your player does not move.
|
||||
|
||||
The lines show above the motion sensor: the player's name in the colour of
|
||||
their team (blue in a game without teams), then the text in a pale blue;
|
||||
CHAT TEXT in Settings > Audio > Voice and Text Chat (`display.chat_style`)
|
||||
draws them all white instead, as Custom Edition does. A line to the
|
||||
team says "(team)" after the name, one to the vehicle "(vehicle)". The lines go after ten seconds; while
|
||||
you type, the last six show. A line has 63 characters at most, letters,
|
||||
numbers and punctuation only (ASCII). `debug.txt` gets every line.
|
||||
|
||||
STREAMER MODE in Settings > Audio > Voice and Text Chat
|
||||
(`game.streamer_mode`, off by default; in a game, from the pause menu's
|
||||
SETTINGS too) replaces the slurs, swearing and sexual words in all the text
|
||||
the game draws with harmless ones: the chat's lines, and players' names
|
||||
wherever they show (the chat, the scoreboard, above their heads, the kills,
|
||||
the lobby), so that a stream shows none: "fuck" shows as "fudge",
|
||||
"cunt" as "chop stick", "faggot" as "flurry". It finds them in either case,
|
||||
with digits and signs for letters ("n1gg3r", "c00n", "f*ck"), letters
|
||||
typed more than once ("fuuuck"), spelled out ("n i g g e r", "c.u.n.t") and
|
||||
run together ("sonofabitch"), and leaves alone the words that hold one
|
||||
("Scunthorpe", "snigger", "spicy", "assassin", "cocktail", "rapper"). It changes only what your game shows: the other
|
||||
players see the lines and names as they were typed, and `debug.txt` keeps
|
||||
them so. The line you type shows replaced too. The list is in
|
||||
`port/linux/game/chat_filter.c`.
|
||||
|
||||
The host sends each line on: a player of an older build does not see the
|
||||
lines and cannot send them. The host takes 5 lines at once from a machine,
|
||||
then one each 1.5 seconds.
|
||||
|
||||
The host can mute a player's text chat: type `/mute <player> <minutes>` in
|
||||
the chat, as `/mute Milenko 5` (5 minutes when you give none, 1440 at
|
||||
most). The player is found as the console's `kick` finds one: the whole
|
||||
name in either case, else the one player whose name begins with what you
|
||||
typed. All the players see "Milenko muted for 5 minutes". The muted
|
||||
player's lines go to no one, and they see how many minutes are left. The
|
||||
mute ends when its minutes are up, or with `/unmute <player>`. A player
|
||||
who leaves and joins again under the same name is still muted. Only the
|
||||
host mutes: a player's `/mute` is not sent.
|
||||
|
||||
### Voice chat
|
||||
|
||||
Network games have voice chat, on the local network and on the internet.
|
||||
Hold V (`controls.push_to_talk`, Settings > Controls Setup) to talk, or set
|
||||
VOICE CHAT in Settings > Audio > Voice and Text Chat to OPEN MIC. VOICE VOLUME sets the volume of
|
||||
VOICE CHAT in Settings > Audio to OPEN MIC. VOICE VOLUME sets the volume of
|
||||
the other players. On Android, set VOICE CHAT to OPEN MIC to talk: the game
|
||||
then asks for the microphone.
|
||||
|
||||
|
||||
@@ -1,836 +0,0 @@
|
||||
/*
|
||||
CHAT_FILTER.C
|
||||
|
||||
Streamer mode (game.streamer_mode, off by default): the text the game
|
||||
draws shows the slurs and swearing in it as harmless words instead, the
|
||||
chat's lines and players' names wherever they show, so that what a stream
|
||||
shows stays clean. It changes only what this machine draws (draw_string.c,
|
||||
through chat_filter_shown): every machine is sent the lines and names as
|
||||
they were typed, and debug.txt keeps them so.
|
||||
|
||||
Each word of the list matches in either case, and in the spellings that
|
||||
dodge a plain list:
|
||||
|
||||
- digits and signs for the letters they look like ("n1gg3r", "$hit",
|
||||
"c00n"), l for i ("nlgga"), q for g ("niqqa"), k for c ("kunt"), v for u
|
||||
("fvck"), ph for f ("phaggot"), and * for a vowel ("f*ck");
|
||||
- a letter typed more than once ("fuuuck", "niggggaaa");
|
||||
- a word spelled out, a separator between each of its letters
|
||||
("n i g g e r", "c.u.n.t");
|
||||
- endings typed more than once ("niggazzz").
|
||||
|
||||
Where a word may stand, so that harmless words that hold one are left alone
|
||||
(Scunthorpe, snigger, Niger, spicy, raccoon, cocky, assassin, therapist):
|
||||
|
||||
- a word alone, but for a plural's ending (spic, spics);
|
||||
- ... or a few endings more (retard, retarded, retards; not retardant);
|
||||
- the start of a word, whatever follows (cunt, cunts, cuntish);
|
||||
- anywhere (fuck: motherfucker).
|
||||
|
||||
The harmless word takes the case of the one it replaces (Fuck: Fudge, FUCK:
|
||||
FUDGE) and the ending after it, fitted to it (fucking: fudging, bitches:
|
||||
biscuits, faggots: flurries).
|
||||
*/
|
||||
|
||||
#include "cseries.h"
|
||||
#include "chat_filter.h"
|
||||
|
||||
/* the platform layer's (port/linux/src/port_config.c) */
|
||||
int config_boolean(char const *name);
|
||||
unsigned long config_changes(void);
|
||||
|
||||
/* ---------- constants */
|
||||
|
||||
/* where a word may stand */
|
||||
enum
|
||||
{
|
||||
/* alone, or with a plural's ending */
|
||||
_chat_filter_word,
|
||||
/* ... or a few endings more */
|
||||
_chat_filter_word_endings,
|
||||
/* at the start of a word */
|
||||
_chat_filter_word_start,
|
||||
_chat_filter_anywhere,
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
/* the endings of _chat_filter_word (the first of chat_filter_endings) */
|
||||
CHAT_FILTER_PLURAL_ENDINGS = 4,
|
||||
/* the texts drawn (chat_filter_shown): those remembered, filtered, and how
|
||||
long one may be (a longer one is drawn as it is); how much longer a text
|
||||
may grow (kys: keep yourself safe); a run between format codes at most */
|
||||
CHAT_FILTER_CACHED = 48,
|
||||
CHAT_FILTER_CACHED_SIZE = 256,
|
||||
CHAT_FILTER_GROWTH = 4,
|
||||
CHAT_FILTER_RUN_SIZE = 256,
|
||||
};
|
||||
|
||||
/* ---------- structures */
|
||||
|
||||
struct chat_filter_word
|
||||
{
|
||||
/* in lower case; a space matches one or more */
|
||||
char const *word;
|
||||
/* NULL: kept as typed (a harmless word that holds one of the list's) */
|
||||
char const *replacement;
|
||||
short match;
|
||||
};
|
||||
|
||||
/* ---------- globals */
|
||||
|
||||
/* (the longer before the words they hold, a word's spellings together) */
|
||||
static struct chat_filter_word const chat_filter_words[] =
|
||||
{
|
||||
/* racist slurs and phrases */
|
||||
/* (kept as they are: the harmless words that hold the n-word's letters) */
|
||||
{ "snigg", NULL, _chat_filter_word_start },
|
||||
{ "niggl", NULL, _chat_filter_word_start },
|
||||
{ "niggard", NULL, _chat_filter_word_start },
|
||||
{ "nigger", "nugget", _chat_filter_word_start },
|
||||
{ "niglet", "piglet", _chat_filter_anywhere },
|
||||
{ "nigette", "nugget", _chat_filter_word_start },
|
||||
{ "nigr", "noodle", _chat_filter_word_start },
|
||||
{ "nignog", "eggnog", _chat_filter_word_start },
|
||||
{ "niga", "ninja", _chat_filter_word },
|
||||
{ "nigah", "ninja", _chat_filter_word },
|
||||
{ "niguh", "ninja", _chat_filter_word },
|
||||
{ "nigor", "noodle", _chat_filter_word },
|
||||
{ "nigar", "noodle", _chat_filter_word },
|
||||
{ "nigur", "noodle", _chat_filter_word },
|
||||
{ "nigg", "nugget", _chat_filter_anywhere },
|
||||
{ "nig", "noodle", _chat_filter_word },
|
||||
{ "kneegrow", "knee pad", _chat_filter_word_start },
|
||||
{ "knee grow", "knee pad", _chat_filter_word },
|
||||
{ "jigaboo", "jigglypuff", _chat_filter_word_start },
|
||||
{ "porch monkey", "porch swing", _chat_filter_word },
|
||||
{ "jungle monkey", "jungle gym", _chat_filter_word },
|
||||
{ "melon monkey", "melon baller", _chat_filter_word },
|
||||
{ "jungle bunny", "easter bunny", _chat_filter_word },
|
||||
{ "moon cricket", "moon pie", _chat_filter_word },
|
||||
{ "mooncricket", "moon pie", _chat_filter_word_start },
|
||||
{ "spear chucker", "pea shooter", _chat_filter_word },
|
||||
{ "tar baby", "baby yoda", _chat_filter_word },
|
||||
{ "burrhead", "bed head", _chat_filter_word_start },
|
||||
{ "burr head", "bed head", _chat_filter_word },
|
||||
{ "diaper head", "diaper bag", _chat_filter_word },
|
||||
{ "gator bait", "gatorade", _chat_filter_word },
|
||||
{ "christ killer", "chris pratt", _chat_filter_word },
|
||||
{ "klansm", "clansm", _chat_filter_word_start },
|
||||
{ "klansw", "clansw", _chat_filter_word_start },
|
||||
{ "pickaninny", "picnic basket", _chat_filter_word_start },
|
||||
{ "golliwog", "golly gosh", _chat_filter_word_start },
|
||||
{ "sambo", "samba", _chat_filter_word },
|
||||
{ "darkie", "doggie", _chat_filter_word },
|
||||
{ "darky", "doggy", _chat_filter_word },
|
||||
{ "darkey", "doggy", _chat_filter_word },
|
||||
{ "wigger", "wiggler", _chat_filter_word_endings },
|
||||
{ "africoon", "afro puff", _chat_filter_word },
|
||||
{ "coonass", "cocoon", _chat_filter_word_start },
|
||||
{ "coon", "cocoon", _chat_filter_word },
|
||||
{ "wog", "walrus", _chat_filter_word },
|
||||
{ "lynch", "high five", _chat_filter_word_endings },
|
||||
{ "chink", "chipmunk", _chat_filter_word_endings },
|
||||
{ "chinaman", "china plate", _chat_filter_word },
|
||||
{ "ching chong", "ding dong", _chat_filter_word },
|
||||
{ "gook", "goose", _chat_filter_word },
|
||||
{ "zipperhead", "zipper bag", _chat_filter_word_start },
|
||||
{ "jape", NULL, _chat_filter_word_start },
|
||||
{ "jap", "jalapeno", _chat_filter_word },
|
||||
{ "spice", NULL, _chat_filter_word_start },
|
||||
{ "spic", "spork", _chat_filter_word },
|
||||
{ "spick", "spork", _chat_filter_word },
|
||||
{ "tacohead", "taco tuesday", _chat_filter_word_start },
|
||||
{ "wetback", "waffle", _chat_filter_word_start },
|
||||
{ "beaner", "bean bag", _chat_filter_word },
|
||||
{ "kike", "kite", _chat_filter_word },
|
||||
{ "yid", "yeti", _chat_filter_word },
|
||||
{ "heeb", "herb", _chat_filter_word },
|
||||
{ "jewed", "chewed", _chat_filter_word },
|
||||
{ "jewboy", "cowboy", _chat_filter_word_start },
|
||||
{ "hymie", "hammy", _chat_filter_word },
|
||||
{ "towelhead", "beach towel", _chat_filter_word_start },
|
||||
{ "raghead", "rag doll", _chat_filter_word_start },
|
||||
{ "camel jockey", "disc jockey", _chat_filter_word },
|
||||
{ "greaseball", "meatball", _chat_filter_word_start },
|
||||
{ "woop", NULL, _chat_filter_word_start },
|
||||
{ "wop", "whopper", _chat_filter_word },
|
||||
{ "dago", "dango", _chat_filter_word },
|
||||
{ "coolie", "cookie", _chat_filter_word },
|
||||
{ "boche", "brioche", _chat_filter_word },
|
||||
{ "boonga", "bongo", _chat_filter_word },
|
||||
{ "abbo", "avocado", _chat_filter_word },
|
||||
{ "kaffir", "coffee", _chat_filter_word },
|
||||
{ "kaffer", "coffee", _chat_filter_word },
|
||||
{ "kraut", "sauerkraut", _chat_filter_word },
|
||||
{ "paki", "pancake", _chat_filter_word },
|
||||
/* homophobic and transphobic */
|
||||
{ "fagot", "flurry", _chat_filter_word_start },
|
||||
{ "faget", "flurry", _chat_filter_word },
|
||||
{ "fagit", "flurry", _chat_filter_word },
|
||||
{ "faq", NULL, _chat_filter_word_start },
|
||||
{ "phage", NULL, _chat_filter_word_start },
|
||||
{ "fag", "flapjack", _chat_filter_word },
|
||||
{ "feg", "flapjack", _chat_filter_word },
|
||||
{ "carpet muncher", "carpet cleaner", _chat_filter_word },
|
||||
{ "carpetmuncher", "carpet cleaner", _chat_filter_word_start },
|
||||
{ "fudge packer", "fudge maker", _chat_filter_word },
|
||||
{ "fudgepacker", "fudge maker", _chat_filter_word_start },
|
||||
{ "pillow biter", "pillow fighter", _chat_filter_word },
|
||||
{ "lesbo", "lasso", _chat_filter_word },
|
||||
{ "lezbo", "lasso", _chat_filter_word },
|
||||
{ "lezie", "lizzie", _chat_filter_word },
|
||||
{ "lezy", "lizzy", _chat_filter_word },
|
||||
{ "homo", "hobo", _chat_filter_word },
|
||||
{ "dyke", "duckling", _chat_filter_word },
|
||||
{ "tranies", "trampolines", _chat_filter_word },
|
||||
{ "trany", "trampoline", _chat_filter_word },
|
||||
{ "shemale", "she-hulk", _chat_filter_word_start },
|
||||
/* ableist */
|
||||
{ "retard", "rutabaga", _chat_filter_word_endings },
|
||||
{ "tard", "tater", _chat_filter_word },
|
||||
{ "spaz", "spud", _chat_filter_word },
|
||||
{ "space", NULL, _chat_filter_word_start },
|
||||
{ "spac", "spud", _chat_filter_word },
|
||||
{ "mongoloid", "mongoose", _chat_filter_word_start },
|
||||
/* hate */
|
||||
{ "neonazi", "neo nacho", _chat_filter_word_start },
|
||||
{ "nazi", "nacho", _chat_filter_word_start },
|
||||
{ "hitler", "hamster", _chat_filter_word },
|
||||
{ "sieg heil", "see ya later", _chat_filter_word },
|
||||
{ "heil", "hail", _chat_filter_word },
|
||||
{ "kkk", "kfc", _chat_filter_word },
|
||||
{ "1488", "1234", _chat_filter_word },
|
||||
/* telling someone to die, and worse */
|
||||
{ "kill yourself", "treat yourself", _chat_filter_word },
|
||||
{ "kill your self", "treat your self", _chat_filter_word },
|
||||
{ "kill urself", "treat urself", _chat_filter_word },
|
||||
{ "neck yourself", "check yourself", _chat_filter_word },
|
||||
{ "hang yourself", "hug yourself", _chat_filter_word },
|
||||
{ "kys", "keep yourself safe", _chat_filter_word },
|
||||
{ "rapist", "grape farmer", _chat_filter_word },
|
||||
{ "rapp", NULL, _chat_filter_word_start },
|
||||
{ "daterape", "date grape", _chat_filter_word_start },
|
||||
{ "rape", "grape", _chat_filter_word_endings },
|
||||
{ "pedophile", "pedal pusher", _chat_filter_word_start },
|
||||
{ "pedo", "pedalo", _chat_filter_word },
|
||||
/* swearing */
|
||||
{ "cocksuck", "lollipop lick", _chat_filter_word_start },
|
||||
/* (kept as it is: the town) */
|
||||
{ "scunthorpe", NULL, _chat_filter_word },
|
||||
{ "cunt", "chop stick", _chat_filter_anywhere },
|
||||
{ "fuck", "fudge", _chat_filter_anywhere },
|
||||
/* (kept as they are: Fukushima, Fukuoka) */
|
||||
{ "fuku", NULL, _chat_filter_word_start },
|
||||
{ "fuk", "fudge", _chat_filter_word_start },
|
||||
{ "fcuk", "fudge", _chat_filter_word_start },
|
||||
{ "fck", "fudge", _chat_filter_word_endings },
|
||||
{ "wtf", "what the fudge", _chat_filter_word },
|
||||
/* (kept as it is: "shit" with its i typed twice) */
|
||||
{ "shiit", NULL, _chat_filter_word_start },
|
||||
{ "shit", "shiitake", _chat_filter_anywhere },
|
||||
{ "biatch", "biscuit", _chat_filter_word_start },
|
||||
{ "byatch", "biscuit", _chat_filter_word_start },
|
||||
{ "beyotch", "biscuit", _chat_filter_word_start },
|
||||
{ "blotch", NULL, _chat_filter_word_start },
|
||||
{ "biotch", "biscuit", _chat_filter_word_start },
|
||||
{ "bitch", "biscuit", _chat_filter_anywhere },
|
||||
{ "asshole", "donut hole", _chat_filter_word_start },
|
||||
{ "asshat", "donkey hat", _chat_filter_word_start },
|
||||
{ "asswipe", "baby wipe", _chat_filter_word_start },
|
||||
{ "dumbass", "dumb donkey", _chat_filter_word },
|
||||
{ "fatass", "fat donkey", _chat_filter_word },
|
||||
{ "jackass", "jackrabbit", _chat_filter_word },
|
||||
/* (kept as they are: the words that begin with ass harmlessly) */
|
||||
{ "assai", NULL, _chat_filter_word_start },
|
||||
{ "assam", NULL, _chat_filter_word_start },
|
||||
{ "assas", NULL, _chat_filter_word_start },
|
||||
{ "assau", NULL, _chat_filter_word_start },
|
||||
{ "assay", NULL, _chat_filter_word_start },
|
||||
{ "assem", NULL, _chat_filter_word_start },
|
||||
{ "assen", NULL, _chat_filter_word_start },
|
||||
{ "asser", NULL, _chat_filter_word_start },
|
||||
{ "assess", NULL, _chat_filter_word_start },
|
||||
{ "asset", NULL, _chat_filter_word_start },
|
||||
{ "assid", NULL, _chat_filter_word_start },
|
||||
{ "assig", NULL, _chat_filter_word_start },
|
||||
{ "assim", NULL, _chat_filter_word_start },
|
||||
{ "assis", NULL, _chat_filter_word_start },
|
||||
{ "assoc", NULL, _chat_filter_word_start },
|
||||
{ "asson", NULL, _chat_filter_word_start },
|
||||
{ "assor", NULL, _chat_filter_word_start },
|
||||
{ "assua", NULL, _chat_filter_word_start },
|
||||
{ "assum", NULL, _chat_filter_word_start },
|
||||
{ "assur", NULL, _chat_filter_word_start },
|
||||
{ "assyr", NULL, _chat_filter_word_start },
|
||||
{ "assiz", NULL, _chat_filter_word_start },
|
||||
{ "asst", NULL, _chat_filter_word_start },
|
||||
{ "assn", NULL, _chat_filter_word_start },
|
||||
{ "ass", "donkey", _chat_filter_word_start },
|
||||
{ "dickhead", "pickle head", _chat_filter_word_start },
|
||||
/* (kept as they are: Dickens, dickey, dicker) */
|
||||
{ "dicken", NULL, _chat_filter_word_start },
|
||||
{ "dickey", NULL, _chat_filter_word_start },
|
||||
{ "dicker", NULL, _chat_filter_word_start },
|
||||
{ "dickins", NULL, _chat_filter_word_start },
|
||||
{ "dick", "pickle", _chat_filter_anywhere },
|
||||
{ "cock", "rooster", _chat_filter_word },
|
||||
{ "pussies", "kitty cats", _chat_filter_word },
|
||||
/* (kept as they are: pussycat, pussyfoot, pussy willow) */
|
||||
{ "pussycat", NULL, _chat_filter_word_start },
|
||||
{ "octopussy", NULL, _chat_filter_word_start },
|
||||
{ "pussyfoot", NULL, _chat_filter_word_start },
|
||||
{ "pussy willow", NULL, _chat_filter_word },
|
||||
{ "pussy", "kitty cat", _chat_filter_anywhere },
|
||||
{ "whore", "hoarder", _chat_filter_anywhere },
|
||||
{ "hoe", "rake", _chat_filter_word },
|
||||
{ "thot", "thought", _chat_filter_word },
|
||||
{ "slut", "sloth", _chat_filter_anywhere },
|
||||
{ "skank", "skunk", _chat_filter_word_start },
|
||||
{ "bastard", "custard", _chat_filter_word_start },
|
||||
{ "twat", "teapot", _chat_filter_word_start },
|
||||
{ "wanker", "walnut", _chat_filter_word_start },
|
||||
{ "wank", "walnut", _chat_filter_word_start },
|
||||
{ "bollock", "bollard", _chat_filter_word_start },
|
||||
{ "douche", "duvet", _chat_filter_word_start },
|
||||
{ "prick", "pinecone", _chat_filter_word },
|
||||
{ "piss", "fizz", _chat_filter_word_start },
|
||||
{ "damnation", NULL, _chat_filter_word_start },
|
||||
{ "goddamn", "goshdarn", _chat_filter_word_start },
|
||||
{ "goddam", "goshdarn", _chat_filter_word_start },
|
||||
{ "damn", "darn", _chat_filter_word_start },
|
||||
{ "cum", "cumin", _chat_filter_word },
|
||||
{ "jizz", "jazz", _chat_filter_word_start },
|
||||
{ "dildo", "didgeridoo", _chat_filter_word_start },
|
||||
{ "tittle", NULL, _chat_filter_word_start },
|
||||
{ "titties", "tater tots", _chat_filter_word },
|
||||
{ "tits", "tater tots", _chat_filter_word },
|
||||
{ "mofo", "mojo", _chat_filter_word },
|
||||
{ "gfy", "go find yourself", _chat_filter_word },
|
||||
/* sexual */
|
||||
{ "blowjob", "bubble tea", _chat_filter_word_start },
|
||||
{ "gangbang", "gangnam style", _chat_filter_word_start },
|
||||
{ "fellat", "fellowship", _chat_filter_word_start },
|
||||
{ "felat", "fellowship", _chat_filter_word_start },
|
||||
{ "fingerbang", "finger gun", _chat_filter_word_start },
|
||||
{ "jism", "jasmine", _chat_filter_word },
|
||||
{ "jizm", "jasmine", _chat_filter_word },
|
||||
{ "spooge", "sponge", _chat_filter_word_endings },
|
||||
{ "camel toe", "camel ride", _chat_filter_word },
|
||||
{ "clitor", "clipboard", _chat_filter_word_start },
|
||||
{ "molest", "pester", _chat_filter_word_start },
|
||||
{ "butthole", "button hole", _chat_filter_word_start },
|
||||
{ "buttwipe", "baby wipe", _chat_filter_word_start },
|
||||
{ "blow job", "bubble tea", _chat_filter_word },
|
||||
{ "handjob", "hand jive", _chat_filter_word_start },
|
||||
{ "rimjob", "rim shot", _chat_filter_word_start },
|
||||
{ "jerk off", "jerk chicken", _chat_filter_word },
|
||||
{ "masturbat", "macaroni", _chat_filter_word_start },
|
||||
{ "masterbat", "macaroni", _chat_filter_word_start },
|
||||
{ "poorn", NULL, _chat_filter_word_start },
|
||||
{ "porn", "popcorn", _chat_filter_word_start },
|
||||
{ "horny", "hungry", _chat_filter_word },
|
||||
{ "orgasm", "organ solo", _chat_filter_word_start },
|
||||
{ "orgies", "origami parties", _chat_filter_word },
|
||||
{ "orgy", "origami party", _chat_filter_word },
|
||||
{ "sex", "socks", _chat_filter_word_endings },
|
||||
{ "annal", NULL, _chat_filter_word_start },
|
||||
{ "anal", "annual", _chat_filter_word },
|
||||
{ "penis", "peanut", _chat_filter_word_start },
|
||||
{ "vagina", "vanilla", _chat_filter_word_start },
|
||||
{ "clit", "clip", _chat_filter_word },
|
||||
{ "boob", "balloon", _chat_filter_word_endings },
|
||||
{ "nutsack", "nut bag", _chat_filter_word_start },
|
||||
{ "ballsack", "bean bag", _chat_filter_word_start },
|
||||
{ "ball sack", "bean bag", _chat_filter_word },
|
||||
{ "queef", "quiche", _chat_filter_word },
|
||||
{ "chode", "toad", _chat_filter_word },
|
||||
{ "milf", "milk", _chat_filter_word },
|
||||
{ "hentai", "anime", _chat_filter_word },
|
||||
{ "nudes", "noodles", _chat_filter_word },
|
||||
/* other languages' */
|
||||
{ "puta", "pato", _chat_filter_word },
|
||||
{ "puto", "pato", _chat_filter_word },
|
||||
{ "mierda", "merienda", _chat_filter_word },
|
||||
{ "pendejo", "pan dulce", _chat_filter_word },
|
||||
{ "cabron", "cupcake", _chat_filter_word },
|
||||
{ "maricon", "macaron", _chat_filter_word },
|
||||
{ "kurwa", "kiwi", _chat_filter_word },
|
||||
{ "fanculo", "fan club", _chat_filter_word },
|
||||
{ "scheisse", "schnitzel", _chat_filter_word },
|
||||
{ "stfu", "shush", _chat_filter_word },
|
||||
{ "gtfo", "go touch grass", _chat_filter_word },
|
||||
};
|
||||
|
||||
/* the texts drawn, as streamer mode shows them (chat_filter_shown) */
|
||||
static struct
|
||||
{
|
||||
boolean valid;
|
||||
short length;
|
||||
unsigned long hash;
|
||||
wchar_t source[CHAT_FILTER_CACHED_SIZE];
|
||||
wchar_t shown[CHAT_FILTER_CACHED_SIZE * CHAT_FILTER_GROWTH];
|
||||
} chat_filter_cache[CHAT_FILTER_CACHED];
|
||||
static short chat_filter_cache_next;
|
||||
|
||||
/* the endings a word may have (_chat_filter_word: the first
|
||||
CHAT_FILTER_PLURAL_ENDINGS) */
|
||||
static char const *const chat_filter_endings[] =
|
||||
{
|
||||
"", "s", "es", "z",
|
||||
"y", "ie", "ies", "ed", "er", "ers", "in", "ing", "ings", "d", "r", "rs",
|
||||
};
|
||||
|
||||
/* ---------- the filter (pure: tools/harness/tests/chat_filter.c) */
|
||||
|
||||
static boolean chat_filter_letter(
|
||||
wchar_t character)
|
||||
{
|
||||
return (character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z');
|
||||
}
|
||||
|
||||
/* what a word is made of (a sign that stands for a letter is not: "fag!") */
|
||||
static boolean chat_filter_word_character(
|
||||
wchar_t character)
|
||||
{
|
||||
return chat_filter_letter(character) || (character >= '0' && character <= '9');
|
||||
}
|
||||
|
||||
static wchar_t chat_filter_lower(
|
||||
wchar_t character)
|
||||
{
|
||||
return character >= 'A' && character <= 'Z' ? (wchar_t)(character - 'A' + 'a') : character;
|
||||
}
|
||||
|
||||
static boolean chat_filter_in(
|
||||
char character,
|
||||
char const *set)
|
||||
{
|
||||
for (; *set; set++)
|
||||
{
|
||||
if (*set == character)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* whether a character of the text is the word's letter, in either case, or
|
||||
one that stands for it */
|
||||
static boolean chat_filter_character_matches(
|
||||
wchar_t character,
|
||||
char letter)
|
||||
{
|
||||
static char const stand_ins[][2] =
|
||||
{
|
||||
{ '4', 'a' }, { '@', 'a' }, { '3', 'e' }, { '1', 'i' }, { '!', 'i' }, { '|', 'i' }, { 'l', 'i' },
|
||||
{ '1', 'l' }, { '|', 'l' }, { '0', 'o' }, { '$', 's' }, { '5', 's' }, { '7', 't' }, { '+', 't' },
|
||||
{ '8', 'b' }, { '9', 'g' }, { '6', 'g' }, { 'q', 'g' }, { 'k', 'c' }, { '(', 'c' }, { '<', 'c' },
|
||||
{ 'v', 'u' },
|
||||
};
|
||||
short index;
|
||||
|
||||
if (chat_filter_lower(character) == (wchar_t)letter)
|
||||
return TRUE;
|
||||
/* (* for a vowel: "f*ck") */
|
||||
if (character == '*' && chat_filter_in(letter, "aeiou"))
|
||||
return TRUE;
|
||||
for (index = 0; index < (short)NUMBEROF(stand_ins); index++)
|
||||
{
|
||||
if (chat_filter_lower(character) == (wchar_t)stand_ins[index][0] && letter == stand_ins[index][1])
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* where the word's letters from index match the text from at: the end of
|
||||
the match, NONE for none. Each letter matches a character, and more of it
|
||||
("fuuuck"), as many as leave the rest of the word matching (so "ass" needs
|
||||
two s's); ph matches f ("phuck") */
|
||||
static short chat_filter_match_from(
|
||||
wchar_t const *text,
|
||||
short at,
|
||||
short length,
|
||||
char const *word,
|
||||
short index)
|
||||
{
|
||||
short end;
|
||||
short last;
|
||||
|
||||
if (!word[index])
|
||||
return at;
|
||||
if (word[index] == 'f' && at + 1 < length && chat_filter_lower(text[at]) == 'p' &&
|
||||
chat_filter_lower(text[at + 1]) == 'h')
|
||||
{
|
||||
end = chat_filter_match_from(text, (short)(at + 2), length, word, (short)(index + 1));
|
||||
if (end != NONE)
|
||||
return end;
|
||||
}
|
||||
if (at >= length || !chat_filter_character_matches(text[at], word[index]))
|
||||
return NONE;
|
||||
/* (more of the same character, not of another that stands for the
|
||||
letter: "tilts" is not "tits") */
|
||||
for (last = (short)(at + 1); last < length && chat_filter_lower(text[last]) == chat_filter_lower(text[at]); last++)
|
||||
;
|
||||
for (; last > at; last--)
|
||||
{
|
||||
end = chat_filter_match_from(text, last, length, word, (short)(index + 1));
|
||||
if (end != NONE)
|
||||
return end;
|
||||
}
|
||||
return NONE;
|
||||
}
|
||||
|
||||
/* how many characters of the text from start the word matches, 0 for none */
|
||||
static short chat_filter_match(
|
||||
wchar_t const *text,
|
||||
short start,
|
||||
short length,
|
||||
char const *word)
|
||||
{
|
||||
short end = chat_filter_match_from(text, start, length, word, 0);
|
||||
|
||||
return end == NONE ? 0 : (short)(end - start);
|
||||
}
|
||||
|
||||
/* what may stand between the letters of a word spelled out: neither a
|
||||
letter nor a digit, nor a sign that stands for a letter */
|
||||
static boolean chat_filter_separator(
|
||||
wchar_t character)
|
||||
{
|
||||
return !chat_filter_word_character(character) && !chat_filter_in((char)character, "@$!|+*(<") &&
|
||||
character < 128;
|
||||
}
|
||||
|
||||
/* how many characters of the text from start are the word spelled out, a
|
||||
character for each of its letters with separators between them ("n i g g
|
||||
e r", "c.u.n.t"), and nothing of a word after it; 0 for none (a phrase is
|
||||
never spelled out) */
|
||||
static short chat_filter_match_spelled(
|
||||
wchar_t const *text,
|
||||
short start,
|
||||
short length,
|
||||
char const *word)
|
||||
{
|
||||
short at = start;
|
||||
short index;
|
||||
|
||||
for (index = 0; word[index]; index++)
|
||||
{
|
||||
if (word[index] == ' ')
|
||||
return 0;
|
||||
if (index > 0)
|
||||
{
|
||||
short gap = at;
|
||||
|
||||
while (at < length && chat_filter_separator(text[at]))
|
||||
at++;
|
||||
if (at == gap)
|
||||
return 0;
|
||||
}
|
||||
if (at >= length || !chat_filter_character_matches(text[at], word[index]))
|
||||
return 0;
|
||||
at++;
|
||||
}
|
||||
if (at < length && chat_filter_word_character(text[at]))
|
||||
return 0;
|
||||
return (short)(at - start);
|
||||
}
|
||||
|
||||
/* whether the rest of a word is an ending it may have (all: the longer
|
||||
list, else a plural's), each of its letters typed once or more ("zzz") */
|
||||
static boolean chat_filter_ending(
|
||||
wchar_t const *rest,
|
||||
short length,
|
||||
boolean all)
|
||||
{
|
||||
short count = all ? (short)NUMBEROF(chat_filter_endings) : CHAT_FILTER_PLURAL_ENDINGS;
|
||||
short ending;
|
||||
|
||||
for (ending = 0; ending < count; ending++)
|
||||
{
|
||||
char const *text = chat_filter_endings[ending];
|
||||
short at = 0;
|
||||
short index;
|
||||
|
||||
for (index = 0; text[index]; index++)
|
||||
{
|
||||
if (at >= length || chat_filter_lower(rest[at]) != (wchar_t)text[index])
|
||||
break;
|
||||
at++;
|
||||
while (text[index + 1] != text[index] && at < length && chat_filter_lower(rest[at]) == (wchar_t)text[index])
|
||||
at++;
|
||||
}
|
||||
if (!text[index] && at == length)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void chat_filter_put(
|
||||
wchar_t *destination,
|
||||
short size,
|
||||
short *length,
|
||||
wchar_t character)
|
||||
{
|
||||
if (*length < size - 1)
|
||||
destination[(*length)++] = character;
|
||||
}
|
||||
|
||||
/* the replacement written for the matched text: in its case (all capitals,
|
||||
or the first), and fitted to what follows it in its word (fudge + ing:
|
||||
fudging; biscuit + es: biscuits; flurry + s: flurries); how many of the
|
||||
characters that follow it the replacement takes in (es's e) */
|
||||
static short chat_filter_replace(
|
||||
wchar_t *destination,
|
||||
short size,
|
||||
short *length,
|
||||
char const *replacement,
|
||||
wchar_t const *matched,
|
||||
short matched_length,
|
||||
wchar_t const *rest,
|
||||
short rest_length)
|
||||
{
|
||||
short replacement_length = 0;
|
||||
short written;
|
||||
short letters = 0;
|
||||
short capitals = 0;
|
||||
short taken = 0;
|
||||
short index;
|
||||
boolean all_capitals;
|
||||
boolean first_capital = matched[0] >= 'A' && matched[0] <= 'Z';
|
||||
wchar_t next = rest_length > 0 ? chat_filter_lower(rest[0]) : 0;
|
||||
char last;
|
||||
char before_last;
|
||||
char const *ending = "";
|
||||
|
||||
while (replacement[replacement_length])
|
||||
replacement_length++;
|
||||
written = replacement_length;
|
||||
last = replacement[replacement_length - 1];
|
||||
before_last = replacement_length > 1 ? replacement[replacement_length - 2] : 0;
|
||||
for (index = 0; index < matched_length; index++)
|
||||
{
|
||||
if (chat_filter_letter(matched[index]))
|
||||
{
|
||||
letters++;
|
||||
if (matched[index] >= 'A' && matched[index] <= 'Z')
|
||||
capitals++;
|
||||
}
|
||||
}
|
||||
all_capitals = letters >= 2 && capitals == letters;
|
||||
if (next == 'e' && rest_length >= 2 && chat_filter_lower(rest[1]) == 's' &&
|
||||
(rest_length == 2 || !chat_filter_letter(rest[2])) && !chat_filter_in(last, "sxzh"))
|
||||
{
|
||||
taken = 1;
|
||||
}
|
||||
else if (next == 's' && (rest_length == 1 || !chat_filter_letter(rest[1])) && last == 'y' &&
|
||||
!chat_filter_in(before_last, "aeiou"))
|
||||
{
|
||||
written--;
|
||||
ending = "ie";
|
||||
}
|
||||
else if (last == 'e' && (next == 'e' || next == 'i' || next == 'y'))
|
||||
{
|
||||
written--;
|
||||
}
|
||||
for (index = 0; index < written; index++)
|
||||
{
|
||||
wchar_t character = (wchar_t)replacement[index];
|
||||
|
||||
if (all_capitals || (first_capital && index == 0))
|
||||
character = character >= 'a' && character <= 'z' ? (wchar_t)(character - 'a' + 'A') : character;
|
||||
chat_filter_put(destination, size, length, character);
|
||||
}
|
||||
for (index = 0; ending[index]; index++)
|
||||
chat_filter_put(destination, size, length, all_capitals ? (wchar_t)(ending[index] - 'a' + 'A') : (wchar_t)ending[index]);
|
||||
return taken;
|
||||
}
|
||||
|
||||
short chat_filter_text(
|
||||
wchar_t *destination,
|
||||
short size,
|
||||
wchar_t const *source)
|
||||
{
|
||||
short length = 0;
|
||||
short source_length = 0;
|
||||
short at = 0;
|
||||
|
||||
if (size <= 0)
|
||||
return 0;
|
||||
while (source[source_length] && source_length < SHORT_MAX - 1)
|
||||
source_length++;
|
||||
while (at < source_length)
|
||||
{
|
||||
boolean word_start = at == 0 || !chat_filter_word_character(source[at - 1]);
|
||||
short matched = 0;
|
||||
short entry;
|
||||
|
||||
for (entry = 0; entry < (short)NUMBEROF(chat_filter_words); entry++)
|
||||
{
|
||||
struct chat_filter_word const *word = &chat_filter_words[entry];
|
||||
short word_end;
|
||||
|
||||
if (word->match != _chat_filter_anywhere && !word_start)
|
||||
continue;
|
||||
matched = chat_filter_match(source, at, source_length, word->word);
|
||||
if (matched)
|
||||
{
|
||||
for (word_end = (short)(at + matched);
|
||||
word_end < source_length && chat_filter_word_character(source[word_end]); word_end++)
|
||||
{
|
||||
}
|
||||
if ((word->match == _chat_filter_word || word->match == _chat_filter_word_endings) &&
|
||||
!chat_filter_ending(source + at + matched, (short)(word_end - at - matched),
|
||||
word->match == _chat_filter_word_endings))
|
||||
{
|
||||
matched = 0;
|
||||
}
|
||||
}
|
||||
/* (spelled out: the whole of it replaced) */
|
||||
if (!matched && word_start)
|
||||
matched = chat_filter_match_spelled(source, at, source_length, word->word);
|
||||
if (!matched)
|
||||
continue;
|
||||
if (!word->replacement)
|
||||
{
|
||||
short index;
|
||||
|
||||
for (index = 0; index < matched; index++)
|
||||
chat_filter_put(destination, size, &length, source[at + index]);
|
||||
at += matched;
|
||||
break;
|
||||
}
|
||||
matched += chat_filter_replace(destination, size, &length, word->replacement, source + at, matched,
|
||||
source + at + matched, (short)(source_length - at - matched));
|
||||
at += matched;
|
||||
break;
|
||||
}
|
||||
if (!matched)
|
||||
chat_filter_put(destination, size, &length, source[at++]);
|
||||
}
|
||||
destination[length] = 0;
|
||||
return length;
|
||||
}
|
||||
|
||||
/* a text as the game draws it: the runs between its format codes (a | and the
|
||||
character after it: "|n", "|t", draw_string.c's) each filtered on its own, the
|
||||
codes kept as they are, so that none joins the words beside it */
|
||||
static short chat_filter_formatted(
|
||||
wchar_t *destination,
|
||||
short size,
|
||||
wchar_t const *source)
|
||||
{
|
||||
wchar_t run[CHAT_FILTER_RUN_SIZE];
|
||||
short length = 0;
|
||||
long index = 0;
|
||||
|
||||
if (size <= 0)
|
||||
return 0;
|
||||
while (source[index] && length < size - 1)
|
||||
{
|
||||
short run_length = 0;
|
||||
|
||||
while (source[index] && source[index] != '|' && run_length < CHAT_FILTER_RUN_SIZE - 1)
|
||||
run[run_length++] = source[index++];
|
||||
run[run_length] = 0;
|
||||
if (run_length)
|
||||
length = (short)(length + chat_filter_text(destination + length, (short)(size - length), run));
|
||||
if (source[index] == '|')
|
||||
{
|
||||
chat_filter_put(destination, size, &length, source[index++]);
|
||||
if (source[index])
|
||||
chat_filter_put(destination, size, &length, source[index++]);
|
||||
}
|
||||
}
|
||||
destination[length] = 0;
|
||||
return length;
|
||||
}
|
||||
|
||||
/* the text as streamer mode shows it (the string itself when it is off), the
|
||||
same strings drawn each frame found again in a cache; a text too long for the
|
||||
cache's entries is shown as it is rather than cut short */
|
||||
static wchar_t const *chat_filter_cached(
|
||||
wchar_t const *string)
|
||||
{
|
||||
unsigned long hash = 2166136261UL;
|
||||
long length;
|
||||
short slot;
|
||||
short index;
|
||||
|
||||
for (length = 0; string[length]; length++)
|
||||
{
|
||||
if (length >= CHAT_FILTER_CACHED_SIZE - 1)
|
||||
return string;
|
||||
hash = (hash ^ (unsigned long)string[length]) * 16777619UL;
|
||||
}
|
||||
for (slot = 0; slot < CHAT_FILTER_CACHED; slot++)
|
||||
{
|
||||
if (!chat_filter_cache[slot].valid || chat_filter_cache[slot].hash != hash ||
|
||||
chat_filter_cache[slot].length != length)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (index = 0; index < length && chat_filter_cache[slot].source[index] == string[index]; index++)
|
||||
;
|
||||
if (index == length)
|
||||
return chat_filter_cache[slot].shown;
|
||||
}
|
||||
slot = chat_filter_cache_next;
|
||||
chat_filter_cache_next = (short)((chat_filter_cache_next + 1) % CHAT_FILTER_CACHED);
|
||||
for (index = 0; index <= length; index++)
|
||||
chat_filter_cache[slot].source[index] = string[index];
|
||||
chat_filter_cache[slot].hash = hash;
|
||||
chat_filter_cache[slot].length = (short)length;
|
||||
chat_filter_cache[slot].valid = TRUE;
|
||||
chat_filter_formatted(chat_filter_cache[slot].shown, (short)NUMBEROF(chat_filter_cache[slot].shown), string);
|
||||
return chat_filter_cache[slot].shown;
|
||||
}
|
||||
|
||||
wchar_t const *chat_filter_shown(
|
||||
wchar_t const *string)
|
||||
{
|
||||
if (!string || !chat_filter_enabled())
|
||||
return string;
|
||||
return chat_filter_cached(string);
|
||||
}
|
||||
|
||||
char const *chat_filter_shown_ascii(
|
||||
char const *string)
|
||||
{
|
||||
static char shown[CHAT_FILTER_CACHED_SIZE * CHAT_FILTER_GROWTH];
|
||||
wchar_t wide[CHAT_FILTER_CACHED_SIZE];
|
||||
wchar_t const *filtered;
|
||||
short index;
|
||||
|
||||
if (!string || !chat_filter_enabled())
|
||||
return string;
|
||||
for (index = 0; string[index]; index++)
|
||||
{
|
||||
if (index >= CHAT_FILTER_CACHED_SIZE - 1)
|
||||
return string;
|
||||
wide[index] = (wchar_t)(unsigned char)string[index];
|
||||
}
|
||||
wide[index] = 0;
|
||||
filtered = chat_filter_cached(wide);
|
||||
for (index = 0; filtered[index] && index < (short)sizeof(shown) - 1; index++)
|
||||
shown[index] = (char)filtered[index];
|
||||
shown[index] = 0;
|
||||
return shown;
|
||||
}
|
||||
|
||||
/* ---------- the setting */
|
||||
|
||||
boolean chat_filter_enabled(
|
||||
void)
|
||||
{
|
||||
static boolean enabled;
|
||||
static unsigned long read_at = (unsigned long)-1;
|
||||
|
||||
/* (read again when Settings changes it) */
|
||||
if (read_at != config_changes())
|
||||
{
|
||||
read_at = config_changes();
|
||||
enabled = config_boolean("game.streamer_mode") != 0;
|
||||
}
|
||||
return enabled;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
CHAT_FILTER.H
|
||||
|
||||
Streamer mode's word filter (chat_filter.c): what the text chat
|
||||
(network_chat.c) asks of it.
|
||||
*/
|
||||
|
||||
#ifndef __CHAT_FILTER_H
|
||||
#define __CHAT_FILTER_H
|
||||
#pragma once
|
||||
|
||||
/* ---------- prototypes/CHAT_FILTER.C */
|
||||
|
||||
/* whether streamer mode is on (game.streamer_mode) */
|
||||
boolean chat_filter_enabled(void);
|
||||
/* the text with the words streamer mode replaces replaced, at most size
|
||||
characters with its end; its length */
|
||||
short chat_filter_text(wchar_t *destination, short size, wchar_t const *source);
|
||||
/* (draw_string.c) a text the game draws or measures as streamer mode shows
|
||||
it: the string itself when streamer mode is off, else the text with its words
|
||||
replaced (its format codes kept), valid until the next call. So players'
|
||||
names show replaced wherever they are drawn: the scoreboard, above their
|
||||
heads, the kills, the lobby, the chat */
|
||||
wchar_t const *chat_filter_shown(wchar_t const *string);
|
||||
char const *chat_filter_shown_ascii(char const *string);
|
||||
|
||||
#endif // __CHAT_FILTER_H
|
||||
@@ -1977,9 +1977,7 @@ static struct
|
||||
{ "controls.scoreboard", L"SHOW SCORES", 2 },
|
||||
{ "controls.pause", L"PAUSE MENU", 2 },
|
||||
{ "controls.screenshot", L"SCREENSHOT", 2 },
|
||||
{ "controls.push_to_talk", L"PUSH TO TALK", 3 },
|
||||
{ "controls.chat", L"CHAT", 3 },
|
||||
{ "controls.team_chat", L"TEAM CHAT", 3 },
|
||||
{ "controls.push_to_talk", L"PUSH TO TALK", 2 },
|
||||
};
|
||||
|
||||
static struct
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
NETWORK_CHAT.H
|
||||
|
||||
The text chat of network games (network_chat.c): what the main loop, the
|
||||
HUD (hud.c, hud_unit.c), the keyboard (xinput_sdl.c) and the netcode ask of
|
||||
it.
|
||||
*/
|
||||
|
||||
#ifndef __NETWORK_CHAT_H
|
||||
#define __NETWORK_CHAT_H
|
||||
#pragma once
|
||||
|
||||
/* ---------- constants */
|
||||
|
||||
/* who a line is to */
|
||||
enum
|
||||
{
|
||||
_chat_channel_everyone,
|
||||
_chat_channel_team,
|
||||
/* those riding in the sender's vehicle */
|
||||
_chat_channel_vehicle,
|
||||
NUMBER_OF_CHAT_CHANNELS
|
||||
};
|
||||
|
||||
/* ---------- prototypes/NETWORK_CHAT.C */
|
||||
|
||||
/* every frame (main.c): the keys that start typing a line, and those typed */
|
||||
void network_chat_update(void);
|
||||
/* the first local player says a line to a channel, as if typed and sent;
|
||||
the automated tests' (network_test.c) */
|
||||
void network_chat_say(short channel, char const *text);
|
||||
/* whether a line is being typed: the keyboard types into it, and drives no
|
||||
player (xinput_sdl.c), as with the console */
|
||||
boolean network_chat_typing(void);
|
||||
/* (hud_unit.c) where the local player's motion sensor was drawn: its left
|
||||
and the top of its circle, in the window's units */
|
||||
void network_chat_note_motion_sensor(short local_player_index, short left, short top);
|
||||
/* (hud.c, in the HUD of the first local player's view) the lines, above the
|
||||
motion sensor (on Android, under the weapon's panel at the top left), and the
|
||||
one being typed */
|
||||
void network_chat_draw(short local_player_index);
|
||||
|
||||
#endif // __NETWORK_CHAT_H
|
||||
@@ -4044,8 +4044,6 @@ void network_distributed_handle_message(
|
||||
case _distributed_message_pickups: entry_size = sizeof(struct distributed_pickup); break;
|
||||
case _distributed_message_votekick: entry_size = network_votekick_request_entry_size(); break;
|
||||
case _distributed_message_votekick_status: entry_size = network_votekick_status_entry_size(); break;
|
||||
case _distributed_message_chat_up:
|
||||
case _distributed_message_chat_down: entry_size = network_chat_line_entry_size(); break;
|
||||
case _distributed_message_player_inputs: entry_size = sizeof(struct distributed_player_input); break;
|
||||
case _distributed_message_relayed_actions: entry_size = DISTRIBUTED_RELAYED_ACTION_MINIMUM_SIZE; break;
|
||||
case _distributed_message_game_state:
|
||||
@@ -4071,8 +4069,6 @@ void network_distributed_handle_message(
|
||||
case _distributed_message_coop_skip_vote:
|
||||
case _distributed_message_votekick:
|
||||
case _distributed_message_votekick_status:
|
||||
case _distributed_message_chat_up:
|
||||
case _distributed_message_chat_down:
|
||||
if (header.count < 1)
|
||||
return;
|
||||
break;
|
||||
@@ -4092,7 +4088,6 @@ void network_distributed_handle_message(
|
||||
case _distributed_message_player_inputs:
|
||||
case _distributed_message_coop_skip_vote:
|
||||
case _distributed_message_votekick:
|
||||
case _distributed_message_chat_up:
|
||||
if (machine_index == NONE || game_connection() != _game_connection_network_server)
|
||||
return;
|
||||
break;
|
||||
@@ -4153,12 +4148,6 @@ void network_distributed_handle_message(
|
||||
case _distributed_message_votekick_status:
|
||||
network_votekick_handle_status(entries);
|
||||
break;
|
||||
case _distributed_message_chat_up:
|
||||
network_chat_handle_up(machine_index, entries, distributed_handling_stream_message);
|
||||
break;
|
||||
case _distributed_message_chat_down:
|
||||
network_chat_handle_down(entries);
|
||||
break;
|
||||
case _distributed_message_coop_device_groups:
|
||||
network_coop_handle_device_groups(entries, header.count);
|
||||
break;
|
||||
|
||||
@@ -125,11 +125,6 @@ enum
|
||||
_distributed_message_map_share_offer = 84,
|
||||
_distributed_message_map_share_sources = 85,
|
||||
_distributed_message_map_share_progress = 86,
|
||||
/* the text chat (network_chat.c): a player's line to the host,
|
||||
reliably, which takes it only from the player's own machine's stream; and
|
||||
the line the host sends on to each machine that hears it, reliably */
|
||||
_distributed_message_chat_up = 87,
|
||||
_distributed_message_chat_down = 88,
|
||||
|
||||
NUMBER_OF_DISTRIBUTED_MESSAGES
|
||||
};
|
||||
@@ -299,15 +294,6 @@ word network_votekick_request_entry_size(void);
|
||||
void network_votekick_handle_status(void const *entries);
|
||||
word network_votekick_status_entry_size(void);
|
||||
|
||||
/* ---------- prototypes/NETWORK_CHAT.C */
|
||||
|
||||
/* (the host) a client's line; from_stream: it came over the machine's own
|
||||
stream (anything else is refused) */
|
||||
void network_chat_handle_up(long machine_index, void const *entries, boolean from_stream);
|
||||
/* (a client) a line the host sent on */
|
||||
void network_chat_handle_down(void const *entries);
|
||||
word network_chat_line_entry_size(void);
|
||||
|
||||
/* ---------- prototypes/NETWORK_ACTORS.C */
|
||||
|
||||
void network_actors_new_game(void);
|
||||
|
||||
@@ -41,7 +41,6 @@ Called from the main loop every frame (main.c).
|
||||
#include "cseries.h"
|
||||
#include "main/main.h"
|
||||
#include "interface/player_ui.h"
|
||||
#include "saved games/player_profile.h"
|
||||
#include "interface/ui_widget.h"
|
||||
#include "networking/network_game_globals.h"
|
||||
#include "networking/network_client_manager.h"
|
||||
@@ -62,7 +61,6 @@ Called from the main loop every frame (main.c).
|
||||
#include "camera/observer.h"
|
||||
|
||||
#include "../src/p2p.h" /* port: port/linux/src/p2p.c, p2p_lobby.c */
|
||||
#include "network_chat.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
@@ -118,9 +116,6 @@ static struct
|
||||
real vehicle_time;
|
||||
real pickup_time;
|
||||
char pickup_weapon[64];
|
||||
/* debug.network_test_chat's, and the lines said this game */
|
||||
real chat_time;
|
||||
short chat_lines_said;
|
||||
long score_to_win;
|
||||
long logged_time;
|
||||
boolean public_game;
|
||||
@@ -188,7 +183,6 @@ static void network_test_read_settings(
|
||||
network_test.shoot_interval = (real)config_real("debug.network_test_shoot");
|
||||
network_test.vehicle_time = (real)config_real("debug.network_test_vehicle");
|
||||
network_test.pickup_time = (real)config_real("debug.network_test_pickup");
|
||||
network_test.chat_time = (real)config_real("debug.network_test_chat");
|
||||
snprintf(network_test.pickup_weapon, sizeof(network_test.pickup_weapon), "%s",
|
||||
config_string("debug.network_test_pickup_weapon"));
|
||||
network_test.score_to_win = (long)config_integer("debug.network_test_score");
|
||||
@@ -597,106 +591,6 @@ static void network_test_vehicle(
|
||||
}
|
||||
}
|
||||
|
||||
/* debug.network_test_name: the name the machine's first player takes (its
|
||||
profile's, which it joins with), in ASCII */
|
||||
static void network_test_set_name(
|
||||
void)
|
||||
{
|
||||
char const *name = config_string("debug.network_test_name");
|
||||
struct player_profile profile;
|
||||
short index;
|
||||
|
||||
if (!name || !name[0])
|
||||
return;
|
||||
player_ui_get_active_player_profile(0, &profile);
|
||||
for (index = 0; name[index] && index < MAXIMUM_PLAYER_PROFILE_NAME_LENGTH - 1; index++)
|
||||
profile.player_name[index] = (wchar_t)(unsigned char)name[index];
|
||||
profile.player_name[index] = 0;
|
||||
player_ui_set_active_player_profile(0, player_ui_get_active_player_profile_index(0), &profile);
|
||||
platform_log("network test: this machine's player is named %s", name);
|
||||
}
|
||||
|
||||
/* debug.network_test_chat: the host seats the last two players in one
|
||||
vehicle, the nearest to the first of them with seats for both (any, a map's
|
||||
own), for the vehicle chat (two joining machines' players, of one team: they
|
||||
start near each other) */
|
||||
static void network_test_seat_together(
|
||||
void)
|
||||
{
|
||||
struct data_iterator iterator;
|
||||
struct player_datum *player;
|
||||
struct object_iterator vehicles;
|
||||
long units[2];
|
||||
long best_index = NONE;
|
||||
real best_distance = 0.0f;
|
||||
short count = 0;
|
||||
short index;
|
||||
|
||||
data_iterator_new(&iterator, player_data);
|
||||
while ((player = (struct player_datum *)data_iterator_next(&iterator)) != NULL)
|
||||
{
|
||||
if (player->unit_index == NONE)
|
||||
continue;
|
||||
units[0] = count ? units[1] : NONE;
|
||||
units[1] = player->unit_index;
|
||||
count++;
|
||||
}
|
||||
if (count < 2)
|
||||
return;
|
||||
object_iterator_new(&vehicles, _object_mask_vehicle, 0);
|
||||
while (object_iterator_next(&vehicles))
|
||||
{
|
||||
real distance;
|
||||
|
||||
if (unit_definition_get(object_get(vehicles.index)->definition_index)->unit.seats.count < 2)
|
||||
continue;
|
||||
distance = distance_squared3d(&object_get(units[0])->object.position, &object_get(vehicles.index)->object.position);
|
||||
if (best_index == NONE || distance < best_distance)
|
||||
{
|
||||
best_index = vehicles.index;
|
||||
best_distance = distance;
|
||||
}
|
||||
}
|
||||
if (best_index == NONE)
|
||||
{
|
||||
platform_log("network test: no vehicle with two seats");
|
||||
return;
|
||||
}
|
||||
for (index = 0; index < (short)NUMBEROF(units); index++)
|
||||
{
|
||||
short seat_index;
|
||||
|
||||
for (seat_index = 0;
|
||||
seat_index < unit_definition_get(object_get(best_index)->definition_index)->unit.seats.count; seat_index++)
|
||||
{
|
||||
if (!unit_seat_filled(best_index, seat_index) && unit_enter_seat(units[index], best_index, seat_index))
|
||||
break;
|
||||
}
|
||||
}
|
||||
platform_log("network test: the last two players ride vehicle %lx: %s, %s", best_index,
|
||||
object_get(units[0])->object.parent_object_index == best_index ? "in" : "out",
|
||||
object_get(units[1])->object.parent_object_index == best_index ? "in" : "out");
|
||||
}
|
||||
|
||||
/* debug.network_test_chat: the last player's name, in ASCII (the host
|
||||
mutes it) */
|
||||
static void network_test_last_player_name(
|
||||
char *name,
|
||||
short size)
|
||||
{
|
||||
struct data_iterator iterator;
|
||||
struct player_datum *player;
|
||||
struct player_datum *last = NULL;
|
||||
short index;
|
||||
|
||||
data_iterator_new(&iterator, player_data);
|
||||
while ((player = (struct player_datum *)data_iterator_next(&iterator)) != NULL)
|
||||
last = player;
|
||||
for (index = 0; last && index < size - 1 && index < (short)NUMBEROF(last->name) && last->name[index]; index++)
|
||||
name[index] = player_name_character_ascii(last->name[index]);
|
||||
name[index] = 0;
|
||||
}
|
||||
|
||||
/* both machines stand the last player at the driver's entrance of the
|
||||
nearest vehicle no one rides (a joining machine's player then holds the
|
||||
action button, as a player getting in does) */
|
||||
@@ -903,83 +797,6 @@ void network_test_update(
|
||||
test_input_hold_action(hold);
|
||||
}
|
||||
}
|
||||
/* debug.network_test_chat: each machine's player says a line to
|
||||
everyone that many seconds in, one to the team two seconds on, and
|
||||
one to the vehicle two more on, the host having seated the last two
|
||||
players in one vehicle (the others in none) three seconds before;
|
||||
then the host mutes the last player for a minute (a client's /mute
|
||||
of the host refused), each says a line to everyone (the last
|
||||
player's going no further than the host), the host unmutes it, and
|
||||
each says another */
|
||||
if (network_test.chat_time > 0.0f)
|
||||
{
|
||||
long chat_time = (long)(network_test.chat_time * TICKS_PER_SECOND);
|
||||
char const *machine = network_test.mode == _network_test_host ? "the host" : "a client";
|
||||
char text[64];
|
||||
|
||||
if (game_time_get() < chat_time - 3 * TICKS_PER_SECOND)
|
||||
network_test.chat_lines_said = 0;
|
||||
if (network_test.mode == _network_test_host && network_test.chat_lines_said == 0 &&
|
||||
game_time_get() >= chat_time - 3 * TICKS_PER_SECOND)
|
||||
{
|
||||
network_test_seat_together();
|
||||
network_test.chat_lines_said = -1;
|
||||
}
|
||||
if (network_test.chat_lines_said <= 0 && game_time_get() >= chat_time)
|
||||
{
|
||||
snprintf(text, sizeof(text), "hello everyone from %s", machine);
|
||||
network_chat_say(_chat_channel_everyone, text);
|
||||
network_test.chat_lines_said = 1;
|
||||
}
|
||||
if (network_test.chat_lines_said == 1 && game_time_get() >= chat_time + 2 * TICKS_PER_SECOND)
|
||||
{
|
||||
snprintf(text, sizeof(text), "hello team from %s", machine);
|
||||
network_chat_say(_chat_channel_team, text);
|
||||
network_test.chat_lines_said++;
|
||||
}
|
||||
if (network_test.chat_lines_said == 2 && game_time_get() >= chat_time + 4 * TICKS_PER_SECOND)
|
||||
{
|
||||
snprintf(text, sizeof(text), "hello vehicle from %s", machine);
|
||||
network_chat_say(_chat_channel_vehicle, text);
|
||||
network_test.chat_lines_said++;
|
||||
}
|
||||
if (network_test.chat_lines_said == 3 && game_time_get() >= chat_time + 6 * TICKS_PER_SECOND)
|
||||
{
|
||||
char name[16];
|
||||
|
||||
network_test_last_player_name(name, sizeof(name));
|
||||
if (network_test.mode == _network_test_host)
|
||||
snprintf(text, sizeof(text), "/mute %s 1", name);
|
||||
else
|
||||
snprintf(text, sizeof(text), "/mute host");
|
||||
network_chat_say(_chat_channel_everyone, text);
|
||||
network_test.chat_lines_said++;
|
||||
}
|
||||
if (network_test.chat_lines_said == 4 && game_time_get() >= chat_time + 8 * TICKS_PER_SECOND)
|
||||
{
|
||||
snprintf(text, sizeof(text), "while muted from %s", machine);
|
||||
network_chat_say(_chat_channel_everyone, text);
|
||||
network_test.chat_lines_said++;
|
||||
}
|
||||
if (network_test.chat_lines_said == 5 && game_time_get() >= chat_time + 10 * TICKS_PER_SECOND)
|
||||
{
|
||||
char name[16];
|
||||
|
||||
network_test_last_player_name(name, sizeof(name));
|
||||
if (network_test.mode == _network_test_host)
|
||||
{
|
||||
snprintf(text, sizeof(text), "/unmute %s", name);
|
||||
network_chat_say(_chat_channel_everyone, text);
|
||||
}
|
||||
network_test.chat_lines_said++;
|
||||
}
|
||||
if (network_test.chat_lines_said == 6 && game_time_get() >= chat_time + 12 * TICKS_PER_SECOND)
|
||||
{
|
||||
snprintf(text, sizeof(text), "after unmute from %s", machine);
|
||||
network_chat_say(_chat_channel_everyone, text);
|
||||
network_test.chat_lines_said++;
|
||||
}
|
||||
}
|
||||
if (network_test.mode == _network_test_host && network_test.vehicle_time > 0.0f)
|
||||
{
|
||||
long enter_time = (long)(network_test.vehicle_time * TICKS_PER_SECOND);
|
||||
@@ -1215,10 +1032,7 @@ void network_test_update(
|
||||
network_test.map_set = TRUE;
|
||||
}
|
||||
if (!network_test.player_added && network_test.setup_seconds >= 2.0f && global_network_game_client_get())
|
||||
{
|
||||
network_test_set_name();
|
||||
network_test.player_added = network_game_client_add_player(global_network_game_client_get(), 0);
|
||||
}
|
||||
if (network_test.setup_seconds >= network_test.start_delay)
|
||||
{
|
||||
network_test.started = TRUE;
|
||||
@@ -1233,7 +1047,6 @@ void network_test_update(
|
||||
network_test.set_up = TRUE;
|
||||
dispose_global_network_game_client();
|
||||
dispose_global_network_game_server();
|
||||
network_test_set_name();
|
||||
if (create_global_network_game_client())
|
||||
{
|
||||
game_connection_set(_game_connection_network_client);
|
||||
|
||||
@@ -52,7 +52,6 @@ game_engine.c), which it does not tell anyone.
|
||||
#include "objects/objects.h"
|
||||
#include "network_distributed.h"
|
||||
#include "network_voice.h"
|
||||
#include "network_chat.h"
|
||||
#include "../src/voice_audio.h"
|
||||
|
||||
#include <math.h>
|
||||
@@ -925,7 +924,7 @@ void network_voice_update(
|
||||
}
|
||||
/* the microphone: open mic, open while voice is on; push to talk, from
|
||||
the first press (none asked for until then) */
|
||||
held = push_to_talk && halo_push_to_talk_held() && !console_is_active() && !network_chat_typing();
|
||||
held = push_to_talk && halo_push_to_talk_held() && !console_is_active();
|
||||
if (!voice_may_talk() || (!open_mic && !push_to_talk))
|
||||
voice_microphone_wanted = FALSE;
|
||||
else if (open_mic || held)
|
||||
|
||||
@@ -38,16 +38,12 @@ enum halo_keyboard_action
|
||||
/* (voice chat's, which the game itself does not read:
|
||||
halo_push_to_talk_held) */
|
||||
HALO_KEYBOARD_PUSH_TO_TALK,
|
||||
/* (the text chat's, which the game itself does not read: a line to
|
||||
everyone, or to the team: port/linux/game/network_chat.c) */
|
||||
HALO_KEYBOARD_CHAT,
|
||||
HALO_KEYBOARD_TEAM_CHAT,
|
||||
NUMBER_OF_HALO_KEYBOARD_ACTIONS
|
||||
};
|
||||
|
||||
/* the actions the keyboard and mouse hold for the player on the controller
|
||||
(a bit for each halo_keyboard_action): theirs is the first's; none while a
|
||||
menu or the console is up, or a chat line is typed */
|
||||
menu or the console is up */
|
||||
unsigned long halo_keyboard_actions(short controller_index);
|
||||
/* whether voice chat's push to talk is held (controls.push_to_talk), in the
|
||||
game and in the menus alike */
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
version 29
|
||||
bound CHAT_TEXT_SIZE 64
|
||||
bound DISCORD_ID_SIZE 24
|
||||
bound DISCORD_NAME_SIZE 40
|
||||
bound DISTRIBUTED_INPUT_HISTORY 4
|
||||
@@ -42,8 +41,6 @@ enum _distributed_actor_super_camouflaged_bit 5
|
||||
enum _distributed_message_actor_damage 73
|
||||
enum _distributed_message_actor_states 64
|
||||
enum _distributed_message_batch 16
|
||||
enum _distributed_message_chat_down 88
|
||||
enum _distributed_message_chat_up 87
|
||||
enum _distributed_message_client_identity 18
|
||||
enum _distributed_message_client_ready 9
|
||||
enum _distributed_message_coop_device_groups 68
|
||||
@@ -221,7 +218,6 @@ struct distributed_actor_damage 96a57e13e2b8
|
||||
struct distributed_actor_damage_message f985e899a708
|
||||
struct distributed_actor_state 8bd5e771659d
|
||||
struct distributed_actor_state_message 51a56e04a536
|
||||
struct distributed_chat_line cdab1468ffe0
|
||||
struct distributed_client_identity 4d9e62025616
|
||||
struct distributed_coop_device_group 4aa3499be2cc
|
||||
struct distributed_coop_device_groups_message b96ad0030ccc
|
||||
|
||||
@@ -283,11 +283,6 @@ static const struct config_setting config_settings[] =
|
||||
"Save a PNG screenshot beside maps/ (press once per capture)." },
|
||||
{ "controls.push_to_talk", _config_string, "\"V\"", "HALO_KEY_PUSH_TO_TALK", _environment_value, _platform_all,
|
||||
"Voice chat: talk while it is held (audio.voice_chat \"push_to_talk\")." },
|
||||
{ "controls.chat", _config_string, "\"T\"", "HALO_KEY_CHAT", _environment_value, _platform_all,
|
||||
"Network games' text chat: type a line to everyone (Enter sends it,\n"
|
||||
"Escape lets it go)." },
|
||||
{ "controls.team_chat", _config_string, "\"Y\"", "HALO_KEY_TEAM_CHAT", _environment_value, _platform_all,
|
||||
"Network games' text chat: type a line to your team." },
|
||||
|
||||
{ "game.console_log", _config_string, "\"important\"", "HALO_CONSOLE_LOG", _environment_value, _platform_all,
|
||||
"What the game's console shows on screen of what it logs: \"important\"\n"
|
||||
@@ -299,14 +294,6 @@ static const struct config_setting config_settings[] =
|
||||
{ "game.language", _config_string, "\"\"", "HALO_LANGUAGE", _environment_value, _platform_all,
|
||||
"The language the game asks the Xbox for: \"ja\", \"de\", \"fr\", \"es\" or \"it\";\n"
|
||||
"empty for English. The game data decides what is translated." },
|
||||
{ "display.chat_style", _config_string, "\"default\"", "HALO_CHAT_STYLE", _environment_value, _platform_all,
|
||||
"The text chat's colours: \"default\" (names in their team's colour,\n"
|
||||
"the text a pale blue, as the PC version) or \"white\" (all white, as\n"
|
||||
"Custom Edition)." },
|
||||
{ "game.streamer_mode", _config_boolean, "false", "HALO_STREAMER_MODE", _environment_value, _platform_all,
|
||||
"Streamer mode: the text chat shows the slurs and swearing in players'\n"
|
||||
"names and lines as harmless words (fuck: fudge). Only what this\n"
|
||||
"machine shows." },
|
||||
{ "game.enhanced_animations", _config_boolean, "true", "HALO_ENHANCED_ANIMATIONS", _environment_value, _platform_all,
|
||||
"The player bipeds' grenade throws keep their legs moving (crouched,\n"
|
||||
"in the air and in a vehicle's seat too), riders' hands leave the grips\n"
|
||||
@@ -512,13 +499,6 @@ static const struct config_setting config_settings[] =
|
||||
{ "debug.network_test_pickup", _config_real, "0.0", "HALO_NETWORK_TEST_PICKUP", _environment_value, _platform_all,
|
||||
"This many seconds into an automated test game the host stands its last\n"
|
||||
"player on a weapon, which a joining player then picks up; 0 never." },
|
||||
{ "debug.network_test_chat", _config_real, "0.0", "HALO_NETWORK_TEST_CHAT", _environment_value, _platform_all,
|
||||
"This many seconds into an automated test game each machine's player\n"
|
||||
"says a chat line to everyone, then to the team and to the vehicle (the\n"
|
||||
"host seats the last two players in one first); 0 never." },
|
||||
{ "debug.network_test_name", _config_string, "\"\"", "HALO_NETWORK_TEST_NAME", _environment_value, _platform_all,
|
||||
"The name this machine's player takes in an automated test game (ASCII);\n"
|
||||
"empty keeps its profile's." },
|
||||
{ "debug.network_test_pickup_weapon", _config_string, "\"\"", "HALO_NETWORK_TEST_PICKUP_WEAPON", _environment_value,
|
||||
_platform_all,
|
||||
"The weapon network_test_pickup stands the player on: the first whose tag\n"
|
||||
|
||||
@@ -56,9 +56,6 @@ drive the controller.
|
||||
|
||||
/* main/console.c */
|
||||
extern unsigned char console_is_active(void);
|
||||
/* port/linux/game/network_chat.c: a chat line is typed, which takes the
|
||||
keyboard as the console does */
|
||||
extern unsigned char network_chat_typing(void);
|
||||
/* port/linux/game/menu_functions.c: two or more players on this machine
|
||||
(co-op, or split screen in a network game) */
|
||||
extern unsigned char pc_menu_split_players(void);
|
||||
@@ -397,8 +394,7 @@ static void keys_held_over_switch(struct platform_input_state *input)
|
||||
{
|
||||
static unsigned char held[SDL_SCANCODE_COUNT];
|
||||
static int context = -1;
|
||||
int next_context = (input->menus != FALSE) | (text_typing ? 2 : 0) | (console_is_active() ? 4 : 0) |
|
||||
(network_chat_typing() ? 8 : 0);
|
||||
int next_context = (input->menus != FALSE) | (text_typing ? 2 : 0) | (console_is_active() ? 4 : 0);
|
||||
int scancode;
|
||||
|
||||
if (context != next_context)
|
||||
@@ -425,7 +421,7 @@ static const char *const binding_settings[NUMBER_OF_HALO_KEYBOARD_ACTIONS] =
|
||||
"controls.jump", "controls.crouch", "controls.fire", "controls.throw_grenade", "controls.melee",
|
||||
"controls.reload", "controls.zoom", "controls.switch_weapon", "controls.switch_grenade", "controls.action",
|
||||
"controls.flashlight", "controls.scoreboard", "controls.pause", "controls.screenshot",
|
||||
"controls.push_to_talk", "controls.chat", "controls.team_chat",
|
||||
"controls.push_to_talk",
|
||||
};
|
||||
|
||||
static const struct
|
||||
@@ -992,8 +988,7 @@ DWORD WINAPI XInputGetState(HANDLE device, PXINPUT_STATE state)
|
||||
wheel_update();
|
||||
keyboard_actions_held = 0;
|
||||
keys_held_over_switch(&input);
|
||||
/* (a chat line typed takes the keyboard as the console does) */
|
||||
console_active = console_is_active() || network_chat_typing();
|
||||
console_active = console_is_active();
|
||||
held = console_active ? 0 : keyboard_bound_actions(&input);
|
||||
keyboard_screenshot(held);
|
||||
if (!console_active)
|
||||
@@ -1078,7 +1073,7 @@ DWORD WINAPI XInputDebugGetKeystroke(PXINPUT_DEBUG_KEYSTROKE keystroke)
|
||||
/* key ups always pass, so no key is left latched down; while typing,
|
||||
escape does not (it cancels, as B: the game's own escape leaves the
|
||||
menus, main.c) */
|
||||
if (key_up || next.virtual_key == VK_OEM_3_BACKQUOTE || console_is_active() || network_chat_typing() ||
|
||||
if (key_up || next.virtual_key == VK_OEM_3_BACKQUOTE || console_is_active() ||
|
||||
(text_typing && next.virtual_key != 0x1B /* escape */))
|
||||
{
|
||||
keystroke->VirtualKey = next.virtual_key;
|
||||
|
||||
@@ -109,7 +109,6 @@ symbols in this file:
|
||||
#include "text/draw_string.h"
|
||||
#include "coop_spectate.h" /* port: port/linux/game/coop_spectate.c */
|
||||
#include "network_coop.h" /* port: port/linux/game/network_coop.c */
|
||||
#include "network_chat.h" /* port: port/linux/game/network_chat.c */
|
||||
#include "text/font_group.h"
|
||||
#include "text/text_group.h"
|
||||
#include "units/unit_definitions.h"
|
||||
@@ -1456,9 +1455,6 @@ void hud_draw_screen(
|
||||
}
|
||||
|
||||
hud_messaging_update(render.local_player_index);
|
||||
/* port: network games' text chat, in the first local player's view */
|
||||
if (render.local_player_index == local_player_get_next(NONE))
|
||||
network_chat_draw(render.local_player_index);
|
||||
}
|
||||
|
||||
rasterizer_hud_end();
|
||||
|
||||
@@ -1545,8 +1545,6 @@ void hud_draw_static_element(
|
||||
return;
|
||||
}
|
||||
|
||||
short hud_draw_last_left;
|
||||
|
||||
static void hud_draw_bitmap_internal(
|
||||
void *meter_parameters,
|
||||
struct bitmap_data const *bitmap,
|
||||
@@ -1587,11 +1585,6 @@ static void hud_draw_bitmap_internal(
|
||||
vertices[vertex_index].color = color;
|
||||
}
|
||||
|
||||
/* port: where it is drawn (hud_draw_last_left) */
|
||||
hud_draw_last_left = SHORT_MAX;
|
||||
for (vertex_index = 0; vertex_index < 4; vertex_index++)
|
||||
hud_draw_last_left = (short)MIN(hud_draw_last_left, vertices[vertex_index].position.x);
|
||||
|
||||
if (reticle_scale != 1.0f)
|
||||
{
|
||||
real center_x = (render.camera.window_bounds.x1 + render.camera.window_bounds.x0) / 2 -
|
||||
|
||||
@@ -145,11 +145,6 @@ void hud_draw_numbers(
|
||||
long flash_reference_time,
|
||||
real override_scale);
|
||||
|
||||
/* port: the left of the last picture drawn, in the window's units
|
||||
(hud_unit.c: the motion sensor's, which the text chat goes above:
|
||||
port/linux/game/network_chat.c) */
|
||||
extern short hud_draw_last_left;
|
||||
|
||||
/* ---------- public code */
|
||||
|
||||
__inline short check_stack_buffer(
|
||||
|
||||
@@ -86,7 +86,6 @@ symbols in this file:
|
||||
#include "tag_files/tag_groups.h"
|
||||
#include "units/unit_definitions.h"
|
||||
#include "units/units.h"
|
||||
#include "network_chat.h" /* port: port/linux/game/network_chat.c */
|
||||
|
||||
/* ---------- constants */
|
||||
|
||||
@@ -1136,8 +1135,6 @@ void hud_render_unit_interface(
|
||||
struct hud_absolute_placement_definition absolute_placement;
|
||||
short draw_flags = 0;
|
||||
point2d corner;
|
||||
/* port: its picture's left (NONE: none drawn) */
|
||||
short motion_sensor_left = NONE;
|
||||
|
||||
absolute_placement.corner = _hud_anchor_bottom_left;
|
||||
SET_FLAG(
|
||||
@@ -1169,8 +1166,6 @@ void hud_render_unit_interface(
|
||||
&hud_definition->motion_sensor.background,
|
||||
draw_flags,
|
||||
NONE);
|
||||
/* port: where it is (the text chat goes above it) */
|
||||
motion_sensor_left = hud_draw_last_left;
|
||||
}
|
||||
if (hud_definition->motion_sensor.foreground.interface_bitmap.index != NONE)
|
||||
{
|
||||
@@ -1194,14 +1189,6 @@ void hud_render_unit_interface(
|
||||
local_player_index,
|
||||
local_player_count() > 1,
|
||||
&corner);
|
||||
/* port: the text chat goes above it: the top of its circle, as
|
||||
far above the blips' centre as the picture's left edge is left
|
||||
of it (the picture is taller than the circle) */
|
||||
if (motion_sensor_left != NONE)
|
||||
{
|
||||
network_chat_note_motion_sensor(local_player_index, motion_sensor_left,
|
||||
(short)(corner.y - (corner.x - motion_sensor_left)));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
+1
-6
@@ -381,7 +381,6 @@ symbols in this file:
|
||||
#include "networking/network_server_manager.h" /* port: a co-op game's level won */
|
||||
#include "network_coop.h" /* port: port/linux/game/network_coop.c */
|
||||
#include "network_voice.h" /* port: port/linux/game/network_voice.c */
|
||||
#include "network_chat.h" /* port: port/linux/game/network_chat.c */
|
||||
#include "map_share.h" /* port: port/linux/game/map_share.c */
|
||||
#include "profile_sections.h" /* port: port/linux/include/profile_sections.h */
|
||||
#ifdef HALO_PROFILE
|
||||
@@ -3526,8 +3525,6 @@ static boolean main_loop_iteration(
|
||||
network_voice_update();
|
||||
/* port: map sharing, in the lobby and in game (port/linux/game/map_share.c) */
|
||||
map_share_update();
|
||||
/* port: network games' text chat (port/linux/game/network_chat.c) */
|
||||
network_chat_update();
|
||||
profile_scope_enter(main_network_start_frame_section)
|
||||
connection = main_globals.connection;
|
||||
if (connection==_game_connection_network_client)
|
||||
@@ -3587,9 +3584,7 @@ static boolean main_loop_iteration(
|
||||
|
||||
if (!console_update() || main_globals.connection!=_game_connection_local)
|
||||
{
|
||||
/* port: not while a chat line is typed (its K plays an animation) */
|
||||
if (!network_chat_typing())
|
||||
debug_keys_update();
|
||||
debug_keys_update();
|
||||
cheats_update();
|
||||
player_control_update((real)main_globals.halt_time_scale*main_globals.seconds_elapsed);
|
||||
|
||||
|
||||
@@ -116,7 +116,6 @@ symbols in this file:
|
||||
#include "text/font_group.h"
|
||||
#include "text/international_strings.h"
|
||||
#include "text/text_group.h"
|
||||
#include "chat_filter.h" /* port: port/linux/game/chat_filter.c */
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
@@ -1015,8 +1014,6 @@ void draw_string(
|
||||
|
||||
match_assert("c:\\halo\\SOURCE\\text\\draw_string.c", 648, bounds);
|
||||
match_assert("c:\\halo\\SOURCE\\text\\draw_string.c", 649, string);
|
||||
/* port: as streamer mode shows it, players' names too (chat_filter.c) */
|
||||
string = chat_filter_shown_ascii(string);
|
||||
|
||||
parse_string_new(
|
||||
&state,
|
||||
@@ -1215,8 +1212,6 @@ void draw_unicode_string(
|
||||
|
||||
match_assert("c:\\halo\\SOURCE\\text\\draw_string.c", 848, bounds);
|
||||
match_assert("c:\\halo\\SOURCE\\text\\draw_string.c", 849, string);
|
||||
/* port: as streamer mode shows it, players' names too (chat_filter.c) */
|
||||
string = chat_filter_shown(string);
|
||||
|
||||
parse_string_new(
|
||||
&state,
|
||||
|
||||
@@ -1,245 +0,0 @@
|
||||
/*
|
||||
CHAT_FILTER.C (test)
|
||||
|
||||
Streamer mode's word filter (port/linux/game/chat_filter.c): what it
|
||||
replaces, in which spellings, what it leaves alone, the case and endings
|
||||
of what it writes, and that it writes no further than its buffer.
|
||||
test_chat_filter.py takes the list, its constants and the code
|
||||
(under_test.inc).
|
||||
*/
|
||||
|
||||
#include "harness.h"
|
||||
|
||||
#include <wchar.h>
|
||||
|
||||
#define SHORT_MAX 32767
|
||||
#define NUMBEROF(array) ((long)(sizeof(array) / sizeof((array)[0])))
|
||||
|
||||
/* streamer mode, as the setting says */
|
||||
static boolean enabled = TRUE;
|
||||
#define chat_filter_enabled() enabled
|
||||
static short chat_filter_cache_next;
|
||||
|
||||
#include "under_test.inc"
|
||||
|
||||
/* a text in ASCII as the game would draw it */
|
||||
static char const *shown(
|
||||
char const *text)
|
||||
{
|
||||
return chat_filter_shown_ascii(text);
|
||||
}
|
||||
|
||||
#define EXPECT_SHOWN(text, expected) \
|
||||
CHECK(!strcmp(shown(text), expected), "\"%s\" drawn as \"%s\", not \"%s\"", text, shown(text), expected)
|
||||
|
||||
/* the text filtered, in ASCII */
|
||||
static char const *filtered(
|
||||
char const *text)
|
||||
{
|
||||
static char result[256];
|
||||
wchar_t source[256];
|
||||
wchar_t destination[256];
|
||||
short index;
|
||||
|
||||
for (index = 0; text[index]; index++)
|
||||
source[index] = (wchar_t)(unsigned char)text[index];
|
||||
source[index] = 0;
|
||||
chat_filter_text(destination, (short)NUMBEROF(destination), source);
|
||||
for (index = 0; destination[index]; index++)
|
||||
result[index] = (char)destination[index];
|
||||
result[index] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
#define EXPECT(text, expected) \
|
||||
CHECK(!strcmp(filtered(text), expected), "\"%s\" shows as \"%s\", not \"%s\"", text, filtered(text), expected)
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char const *case_name = argc > 1 ? argv[1] : "";
|
||||
|
||||
/* the words replaced, as in the request: faggot, cunt */
|
||||
CASE("replaces")
|
||||
{
|
||||
EXPECT("faggot", "flurry");
|
||||
EXPECT("you cunt", "you chop stick");
|
||||
EXPECT("what the fuck", "what the fudge");
|
||||
EXPECT("shit", "shiitake");
|
||||
EXPECT("kys", "keep yourself safe");
|
||||
EXPECT("go kill yourself", "go treat yourself");
|
||||
}
|
||||
|
||||
/* in the spellings that dodge a plain list */
|
||||
CASE("spellings")
|
||||
{
|
||||
EXPECT("FAGGOT", "FLURRY");
|
||||
EXPECT("f4gg0t", "flurry");
|
||||
EXPECT("phaggot", "flurry");
|
||||
EXPECT("fuuuuck", "fudge");
|
||||
EXPECT("fvck", "fudge");
|
||||
EXPECT("f*ck", "fudge");
|
||||
EXPECT("$hit", "shiitake");
|
||||
EXPECT("kunt", "chop stick");
|
||||
EXPECT("@ss", "donkey");
|
||||
EXPECT("kill yourself", "treat yourself");
|
||||
}
|
||||
|
||||
/* the dodges asked about: digits for letters, endings, spelled out */
|
||||
CASE("dodges")
|
||||
{
|
||||
EXPECT("n1gger", "nugget");
|
||||
EXPECT("n1gg3rz", "nuggetz");
|
||||
EXPECT("c00n", "cocoon");
|
||||
EXPECT("lynch dat melon monkey", "high five dat melon baller");
|
||||
EXPECT("niqqa", "ninja");
|
||||
EXPECT("nlgga", "ninja");
|
||||
EXPECT("n*gga", "ninja");
|
||||
EXPECT("niggggaaa", "ninja");
|
||||
EXPECT("niggazzz", "ninjazzz");
|
||||
EXPECT("n i g g e r", "nugget");
|
||||
EXPECT("N.I.G.G.E.R", "NUGGET");
|
||||
EXPECT("c-u-n-t", "chop stick");
|
||||
EXPECT("fukin", "fudgin");
|
||||
}
|
||||
|
||||
/* the harmless words that hold one */
|
||||
CASE("leaves-alone")
|
||||
{
|
||||
EXPECT("Scunthorpe", "Scunthorpe");
|
||||
EXPECT("spicy food", "spicy food");
|
||||
EXPECT("raccoon", "raccoon");
|
||||
EXPECT("cocky", "cocky");
|
||||
EXPECT("assassin class pass", "assassin class pass");
|
||||
EXPECT("as", "as");
|
||||
EXPECT("cumulative", "cumulative");
|
||||
EXPECT("fire retardant", "fire retardant");
|
||||
EXPECT("therapist", "therapist");
|
||||
EXPECT("shiitake", "shiitake");
|
||||
EXPECT("hello, nice shot!", "hello, nice shot!");
|
||||
EXPECT("snigger at Niger, niggle, night", "snigger at Niger, niggle, night");
|
||||
EXPECT("niggardly", "niggardly");
|
||||
EXPECT("it tilts", "it tilts");
|
||||
EXPECT("Hitchcock peacock cocktail", "Hitchcock peacock cocktail");
|
||||
EXPECT("homosexual Japan tardy pedometer", "homosexual Japan tardy pedometer");
|
||||
EXPECT("this is hit or miss", "this is hit or miss");
|
||||
EXPECT("a s b", "a s b");
|
||||
/* (found running an English dictionary through the list) */
|
||||
EXPECT("FAQ rapper Shiite Ashkenazi snazzier blotch annals woops", "FAQ rapper Shiite Ashkenazi snazzier blotch annals woops");
|
||||
EXPECT("cocked Assyrian spaces spices Damnation", "cocked Assyrian spaces spices Damnation");
|
||||
EXPECT("pussycat Dickens Fukushima Octopussy assist asst", "pussycat Dickens Fukushima Octopussy assist asst");
|
||||
}
|
||||
|
||||
/* words of the list inside others, run together */
|
||||
CASE("compounds")
|
||||
{
|
||||
EXPECT("cumslut", "cumsloth");
|
||||
EXPECT("sonofabitch", "sonofabiscuit");
|
||||
EXPECT("thundercunt", "thunderchop stick");
|
||||
EXPECT("asshat", "donkey hat");
|
||||
EXPECT("assbandit", "donkeybandit");
|
||||
EXPECT("neonazi", "neo nacho");
|
||||
EXPECT("suckmydick", "suckmypickle");
|
||||
EXPECT("raped", "graped");
|
||||
}
|
||||
|
||||
/* the case of what it replaces */
|
||||
CASE("case")
|
||||
{
|
||||
EXPECT("Fuck", "Fudge");
|
||||
EXPECT("FUCK OFF", "FUDGE OFF");
|
||||
EXPECT("Cunt", "Chop stick");
|
||||
}
|
||||
|
||||
/* the endings, kept and fitted */
|
||||
CASE("endings")
|
||||
{
|
||||
EXPECT("fucking", "fudging");
|
||||
EXPECT("motherfucker", "motherfudger");
|
||||
EXPECT("bitches", "biscuits");
|
||||
EXPECT("faggots", "flurries");
|
||||
EXPECT("cunts", "chop sticks");
|
||||
EXPECT("retarded", "rutabagaed");
|
||||
EXPECT("asses", "donkeys");
|
||||
EXPECT("spics", "sporks");
|
||||
EXPECT("fag!", "flapjack!");
|
||||
}
|
||||
|
||||
/* a text drawn: its format codes (| and the character after) kept, and
|
||||
never joined to the words beside them */
|
||||
CASE("formatted")
|
||||
{
|
||||
EXPECT_SHOWN("Player 2|tits a trap", "Player 2|tits a trap");
|
||||
EXPECT_SHOWN("|nfuck you|tcunt", "|nfudge you|tchop stick");
|
||||
EXPECT_SHOWN("fu|nck", "fu|nck");
|
||||
EXPECT_SHOWN("ends with a bar|", "ends with a bar|");
|
||||
}
|
||||
|
||||
/* every text drawn: the same text again from the cache, another text
|
||||
not mistaken for it; as it is with streamer mode off, or too long */
|
||||
CASE("shown")
|
||||
{
|
||||
char long_text[400];
|
||||
wchar_t wide[8] = L"fuck";
|
||||
wchar_t long_wide[400];
|
||||
short index;
|
||||
|
||||
EXPECT_SHOWN("fuck", "fudge");
|
||||
EXPECT_SHOWN("abcd", "abcd");
|
||||
EXPECT_SHOWN("fuck", "fudge");
|
||||
CHECK(chat_filter_shown(wide)[1] == 'u' && chat_filter_shown(wide)[3] == 'g', "the wide text not replaced");
|
||||
memset(long_text, 'x', sizeof(long_text) - 6);
|
||||
memcpy(long_text + sizeof(long_text) - 6, "fuck", 5);
|
||||
CHECK(shown(long_text) == long_text, "a text too long for the cache not drawn as it is");
|
||||
for (index = 0; index < 399; index++)
|
||||
long_wide[index] = (wchar_t)long_text[index];
|
||||
long_wide[399] = 0;
|
||||
CHECK(chat_filter_shown(long_wide) == long_wide, "a wide text too long for the cache not drawn as it is");
|
||||
enabled = FALSE;
|
||||
CHECK(shown("fuck") != NULL && !strcmp(shown("fuck"), "fuck"), "replaced with streamer mode off");
|
||||
CHECK(chat_filter_shown(wide) == wide, "not the string itself with streamer mode off");
|
||||
}
|
||||
|
||||
/* what replaces a word holds none (the chat's text and the drawn text are
|
||||
both filtered) */
|
||||
CASE("idempotent")
|
||||
{
|
||||
long index;
|
||||
|
||||
for (index = 0; index < NUMBEROF(chat_filter_words); index++)
|
||||
{
|
||||
wchar_t source[64];
|
||||
wchar_t destination[256];
|
||||
short length;
|
||||
|
||||
if (!chat_filter_words[index].replacement)
|
||||
continue;
|
||||
for (length = 0; chat_filter_words[index].replacement[length]; length++)
|
||||
source[length] = (wchar_t)chat_filter_words[index].replacement[length];
|
||||
source[length] = 0;
|
||||
chat_filter_text(destination, (short)NUMBEROF(destination), source);
|
||||
CHECK(!wcscmp(destination, source), "\"%s\" (for \"%s\") is itself replaced",
|
||||
chat_filter_words[index].replacement, chat_filter_words[index].word);
|
||||
}
|
||||
}
|
||||
|
||||
/* never past the buffer, and always ended */
|
||||
CASE("bounds")
|
||||
{
|
||||
wchar_t source[64];
|
||||
wchar_t destination[8];
|
||||
short index;
|
||||
short length;
|
||||
|
||||
for (index = 0; index < 40; index++)
|
||||
source[index] = L"fuck "[index % 5];
|
||||
source[40] = 0;
|
||||
for (index = 0; index < 8; index++)
|
||||
destination[index] = 0x7777;
|
||||
length = chat_filter_text(destination, 6, source);
|
||||
CHECK(length == 5 && destination[5] == 0, "length %d", length);
|
||||
CHECK(destination[6] == 0x7777 && destination[7] == 0x7777, "written past its size");
|
||||
CHECK(chat_filter_text(destination, 1, source) == 0 && destination[0] == 0, "a size of 1 not empty");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,341 +0,0 @@
|
||||
/*
|
||||
CHAT_LOGIC.C (test)
|
||||
|
||||
The rules of port/linux/game/network_chat.c's text chat: what a line's text
|
||||
may hold (another machine's is untrusted), how many lines the host takes
|
||||
from a machine, who hears a team's line, how long a line shows, and the
|
||||
host's /mute (what it reads from a line, the player a name means, how long a
|
||||
mute lasts). test_chat_logic.py takes the constants and structures
|
||||
(config.inc) and the code (under_test.inc).
|
||||
*/
|
||||
|
||||
#include "harness.h"
|
||||
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define csstrlen strlen
|
||||
#define csstrcmp strcmp
|
||||
#define csstrncpy strncpy
|
||||
|
||||
#include "config.inc"
|
||||
#include "under_test.inc"
|
||||
|
||||
/* the text cleaned from a source of size bytes */
|
||||
static short clean(
|
||||
char *text,
|
||||
char const *source,
|
||||
short size)
|
||||
{
|
||||
return chat_text_clean(text, source, size);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char const *case_name = argc > 1 ? argv[1] : "";
|
||||
char text[CHAT_TEXT_SIZE];
|
||||
char source[256];
|
||||
|
||||
/* printable ASCII kept as it is, the spaces at its ends dropped */
|
||||
CASE("text-printable")
|
||||
{
|
||||
CHECK(clean(text, " eat my dedi ", 64) == 11, "length %d", (int)strlen(text));
|
||||
CHECK(!strcmp(text, "eat my dedi"), "'%s'", text);
|
||||
CHECK(clean(text, "a~!{}", 64) == 5 && !strcmp(text, "a~!{}"), "'%s'", text);
|
||||
}
|
||||
|
||||
/* control characters, DEL and bytes past ASCII never reach the screen */
|
||||
CASE("text-control-characters")
|
||||
{
|
||||
CHECK(clean(text, "a\tb\x1b" "c\x7f" "d\xe9", 64) == 8, "length %d", (int)strlen(text));
|
||||
CHECK(!strcmp(text, "a?b?c?d?"), "'%s'", text);
|
||||
}
|
||||
|
||||
/* a message cut at CHAT_TEXT_SIZE - 1 characters, and never read past
|
||||
its buffer when another machine sent it unended */
|
||||
CASE("text-bounds")
|
||||
{
|
||||
memset(source, 'x', sizeof(source));
|
||||
CHECK(clean(text, source, sizeof(source)) == CHAT_TEXT_SIZE - 1, "length %d", (int)strlen(text));
|
||||
CHECK(text[CHAT_TEXT_SIZE - 1] == 0, "not ended");
|
||||
memset(source, 'y', sizeof(source));
|
||||
source[10] = 'z';
|
||||
CHECK(clean(text, source, 5) == 5 && !strcmp(text, "yyyyy"), "read past its size: '%s'", text);
|
||||
}
|
||||
|
||||
/* nothing but spaces is no line */
|
||||
CASE("text-empty")
|
||||
{
|
||||
CHECK(clean(text, " ", 64) == 0 && !text[0], "'%s'", text);
|
||||
CHECK(clean(text, "", 64) == 0, "'%s'", text);
|
||||
}
|
||||
|
||||
/* typing and pasting: printable ASCII only, no further than a line holds */
|
||||
CASE("append")
|
||||
{
|
||||
char line[CHAT_TEXT_SIZE] = "hi";
|
||||
|
||||
chat_text_append(line, " there\n\x01!");
|
||||
CHECK(!strcmp(line, "hi there!"), "'%s'", line);
|
||||
memset(source, 'w', 200);
|
||||
source[200] = 0;
|
||||
chat_text_append(line, source);
|
||||
CHECK(strlen(line) == CHAT_TEXT_SIZE - 1, "length %d", (int)strlen(line));
|
||||
}
|
||||
|
||||
/* CHAT_BURST lines at once, then one a period */
|
||||
CASE("rate-burst")
|
||||
{
|
||||
struct chat_rate rate;
|
||||
unsigned long now = 1000;
|
||||
int index;
|
||||
|
||||
memset(&rate, 0, sizeof(rate));
|
||||
for (index = 0; index < CHAT_BURST; index++)
|
||||
CHECK(chat_rate_take(&rate, now), "line %d of the burst refused", index);
|
||||
CHECK(!chat_rate_take(&rate, now), "a line past the burst taken");
|
||||
CHECK(!chat_rate_take(&rate, now + CHAT_REFILL_MILLISECONDS - 1), "a line taken before its period");
|
||||
CHECK(chat_rate_take(&rate, now + CHAT_REFILL_MILLISECONDS), "the line a period later refused");
|
||||
CHECK(!chat_rate_take(&rate, now + CHAT_REFILL_MILLISECONDS), "two lines in one period");
|
||||
}
|
||||
|
||||
/* a machine quiet a long while has its burst again, and no more */
|
||||
CASE("rate-refill")
|
||||
{
|
||||
struct chat_rate rate;
|
||||
unsigned long now = 5000;
|
||||
int index;
|
||||
int taken = 0;
|
||||
|
||||
memset(&rate, 0, sizeof(rate));
|
||||
for (index = 0; index < CHAT_BURST; index++)
|
||||
chat_rate_take(&rate, now);
|
||||
now += 100 * CHAT_REFILL_MILLISECONDS;
|
||||
for (index = 0; index < 4 * CHAT_BURST; index++)
|
||||
taken += chat_rate_take(&rate, now);
|
||||
CHECK(taken == CHAT_BURST, "%d lines taken at once after a long quiet", taken);
|
||||
}
|
||||
|
||||
/* the clock wrapping round (system_milliseconds) changes nothing */
|
||||
CASE("rate-wrap")
|
||||
{
|
||||
struct chat_rate rate;
|
||||
unsigned long now = 0xFFFFFFFFUL - 200;
|
||||
int index;
|
||||
|
||||
memset(&rate, 0, sizeof(rate));
|
||||
for (index = 0; index < CHAT_BURST; index++)
|
||||
CHECK(chat_rate_take(&rate, now), "line %d refused", index);
|
||||
CHECK(!chat_rate_take(&rate, now + 300), "a line taken past the burst across the wrap");
|
||||
CHECK(chat_rate_take(&rate, now + CHAT_REFILL_MILLISECONDS), "the line a period later refused across the wrap");
|
||||
}
|
||||
|
||||
/* a team's line by that team alone, or by everyone without teams */
|
||||
CASE("hears")
|
||||
{
|
||||
CHECK(chat_hears(_chat_channel_everyone, TRUE, 0, 1, NONE, 7), "a line to everyone not heard by the other team");
|
||||
CHECK(chat_hears(_chat_channel_team, TRUE, 1, 1, NONE, NONE), "a team's line not heard by the team");
|
||||
CHECK(!chat_hears(_chat_channel_team, TRUE, 0, 1, 7, 7), "a team's line heard by the other team");
|
||||
CHECK(chat_hears(_chat_channel_team, FALSE, 0, 1, NONE, NONE), "a team's line not heard without teams");
|
||||
}
|
||||
|
||||
/* a vehicle's line by those riding in the sender's vehicle alone, of any
|
||||
team; by no one when the sender rides in none */
|
||||
CASE("hears-vehicle")
|
||||
{
|
||||
CHECK(chat_hears(_chat_channel_vehicle, TRUE, 0, 1, 7, 7), "not heard in the same vehicle (another team)");
|
||||
CHECK(!chat_hears(_chat_channel_vehicle, TRUE, 0, 0, 7, 8), "heard in another vehicle");
|
||||
CHECK(!chat_hears(_chat_channel_vehicle, TRUE, 0, 0, 7, NONE), "heard on foot");
|
||||
CHECK(!chat_hears(_chat_channel_vehicle, FALSE, 0, 0, NONE, NONE), "heard on foot from a sender on foot");
|
||||
}
|
||||
|
||||
/* a line's channel through its flags, as sent */
|
||||
CASE("flags")
|
||||
{
|
||||
short channel;
|
||||
|
||||
for (channel = 0; channel < NUMBER_OF_CHAT_CHANNELS; channel++)
|
||||
CHECK(chat_channel_from_flags(chat_channel_flags(channel)) == channel, "channel %d came back as %d",
|
||||
channel, chat_channel_from_flags(chat_channel_flags(channel)));
|
||||
CHECK(chat_channel_from_flags(0) == _chat_channel_everyone, "no flags is not everyone");
|
||||
}
|
||||
|
||||
/* shown whole, then fading, then gone; whole while a line is typed */
|
||||
CASE("alpha")
|
||||
{
|
||||
CHECK(chat_line_alpha(0, FALSE) == 1.0f, "a new line faded");
|
||||
CHECK(chat_line_alpha(CHAT_LINE_MILLISECONDS - 1, FALSE) == 1.0f, "faded early");
|
||||
CHECK(chat_line_alpha(CHAT_LINE_MILLISECONDS + CHAT_FADE_MILLISECONDS / 2, FALSE) > 0.4f &&
|
||||
chat_line_alpha(CHAT_LINE_MILLISECONDS + CHAT_FADE_MILLISECONDS / 2, FALSE) < 0.6f, "not half way");
|
||||
CHECK(chat_line_alpha(CHAT_LINE_MILLISECONDS + CHAT_FADE_MILLISECONDS, FALSE) == 0.0f, "still shown");
|
||||
CHECK(chat_line_alpha(10 * CHAT_LINE_MILLISECONDS, TRUE) == 1.0f, "faded while typing");
|
||||
}
|
||||
|
||||
/* /mute: the player (a name of more than one word too) and the minutes,
|
||||
the word in either case; the default when it gives none, a day at most;
|
||||
the name cut to its buffer */
|
||||
CASE("command-mute")
|
||||
{
|
||||
struct
|
||||
{
|
||||
char name[8];
|
||||
char canary[64];
|
||||
} bounded;
|
||||
char name[CHAT_TEXT_SIZE];
|
||||
short minutes = 0;
|
||||
|
||||
CHECK(chat_command_parse("/mute Milenko 5", name, sizeof(name), &minutes) == _chat_command_mute,
|
||||
"not a mute");
|
||||
CHECK(!strcmp(name, "Milenko") && minutes == 5, "'%s' %d", name, minutes);
|
||||
CHECK(chat_command_parse("/MUTE Master Chief 12", name, sizeof(name), &minutes) == _chat_command_mute,
|
||||
"not a mute in capitals");
|
||||
CHECK(!strcmp(name, "Master Chief") && minutes == 12, "'%s' %d", name, minutes);
|
||||
CHECK(chat_command_parse("/mute jonas", name, sizeof(name), &minutes) == _chat_command_mute &&
|
||||
!strcmp(name, "jonas") && minutes == CHAT_MUTE_DEFAULT_MINUTES, "'%s' %d", name, minutes);
|
||||
/* (a name all digits, alone, is the name) */
|
||||
CHECK(chat_command_parse("/mute 1337", name, sizeof(name), &minutes) == _chat_command_mute &&
|
||||
!strcmp(name, "1337") && minutes == CHAT_MUTE_DEFAULT_MINUTES, "'%s' %d", name, minutes);
|
||||
/* (a last word not all digits is the name's) */
|
||||
CHECK(chat_command_parse("/mute agent 47b", name, sizeof(name), &minutes) == _chat_command_mute &&
|
||||
!strcmp(name, "agent 47b") && minutes == CHAT_MUTE_DEFAULT_MINUTES, "'%s' %d", name, minutes);
|
||||
CHECK(chat_command_parse("/mute x 99999999999999999999", name, sizeof(name), &minutes) == _chat_command_mute &&
|
||||
minutes == CHAT_MUTE_MAXIMUM_MINUTES, "%d minutes", minutes);
|
||||
memset(&bounded, 'Z', sizeof(bounded));
|
||||
chat_command_parse("/mute averyveryverylongname 3", bounded.name, sizeof(bounded.name), &minutes);
|
||||
CHECK(bounded.canary[0] == 'Z' && strlen(bounded.name) == sizeof(bounded.name) - 1, "the name overran: '%s'",
|
||||
bounded.name);
|
||||
}
|
||||
|
||||
CASE("command-unmute")
|
||||
{
|
||||
char name[CHAT_TEXT_SIZE];
|
||||
short minutes = 0;
|
||||
|
||||
CHECK(chat_command_parse("/unmute Milenko", name, sizeof(name), &minutes) == _chat_command_unmute &&
|
||||
!strcmp(name, "Milenko"), "'%s'", name);
|
||||
/* (an unmute takes no minutes: a last word of digits is the name's) */
|
||||
CHECK(chat_command_parse("/Unmute Milenko 5", name, sizeof(name), &minutes) == _chat_command_unmute &&
|
||||
!strcmp(name, "Milenko 5"), "'%s'", name);
|
||||
}
|
||||
|
||||
/* any other line is said as it is */
|
||||
CASE("command-not")
|
||||
{
|
||||
char name[CHAT_TEXT_SIZE];
|
||||
short minutes = 0;
|
||||
|
||||
CHECK(chat_command_parse("/muted Milenko", name, sizeof(name), &minutes) == _chat_command_none, "/muted");
|
||||
CHECK(chat_command_parse("/mutex", name, sizeof(name), &minutes) == _chat_command_none, "/mutex");
|
||||
CHECK(chat_command_parse("mute Milenko 5", name, sizeof(name), &minutes) == _chat_command_none, "no slash");
|
||||
CHECK(chat_command_parse("hi /mute x", name, sizeof(name), &minutes) == _chat_command_none, "within a line");
|
||||
CHECK(chat_command_parse("/mut", name, sizeof(name), &minutes) == _chat_command_none, "/mut");
|
||||
CHECK(chat_command_parse("", name, sizeof(name), &minutes) == _chat_command_none, "nothing");
|
||||
}
|
||||
|
||||
/* without a player, or for no minutes: how to say it */
|
||||
CASE("command-usage")
|
||||
{
|
||||
char name[CHAT_TEXT_SIZE];
|
||||
short minutes = 0;
|
||||
|
||||
CHECK(chat_command_parse("/mute", name, sizeof(name), &minutes) == _chat_command_usage, "/mute alone");
|
||||
CHECK(chat_command_parse("/unmute", name, sizeof(name), &minutes) == _chat_command_usage, "/unmute alone");
|
||||
CHECK(chat_command_parse("/mute Milenko 0", name, sizeof(name), &minutes) == _chat_command_usage,
|
||||
"a mute of no minutes");
|
||||
CHECK(chat_command_parse("/mute Milenko 000", name, sizeof(name), &minutes) == _chat_command_usage,
|
||||
"a mute of no minutes");
|
||||
}
|
||||
|
||||
/* the whole name in either case first, else the one it begins; none,
|
||||
or more than one */
|
||||
CASE("name-match")
|
||||
{
|
||||
static char const names[6][CHAT_NAME_SIZE] = { "Milenko", "", "Mil", "jonas", "Jonathan", "Bob" };
|
||||
|
||||
CHECK(chat_name_match("Milenko", names, 6) == 0, "the whole name: %d", chat_name_match("Milenko", names, 6));
|
||||
CHECK(chat_name_match("milenko", names, 6) == 0, "in lower case");
|
||||
CHECK(chat_name_match("mil", names, 6) == 2, "the whole name before one it begins: %d",
|
||||
chat_name_match("mil", names, 6));
|
||||
CHECK(chat_name_match("mile", names, 6) == 0, "the one it begins: %d", chat_name_match("mile", names, 6));
|
||||
CHECK(chat_name_match("jon", names, 6) == CHAT_NAME_AMBIGUOUS, "two names begin with it: %d",
|
||||
chat_name_match("jon", names, 6));
|
||||
CHECK(chat_name_match("JONAS", names, 6) == 3, "the whole name over one it begins");
|
||||
CHECK(chat_name_match("Alice", names, 6) == NONE, "no such player");
|
||||
CHECK(chat_name_match("", names, 6) == NONE, "no name");
|
||||
}
|
||||
|
||||
/* a name longer than any player's means none (never read past a name) */
|
||||
CASE("name-bounds")
|
||||
{
|
||||
static char const names[2][CHAT_NAME_SIZE] = { "Bob", "Bobby" };
|
||||
|
||||
CHECK(chat_name_match("Bobbyyyyyyyyyyyyyyyyyy", names, 2) == NONE, "a longer name matched");
|
||||
CHECK(chat_name_match("Bobb", names, 2) == 1, "%d", chat_name_match("Bobb", names, 2));
|
||||
}
|
||||
|
||||
/* a mute's minutes, the one begun counted whole, then none; let go */
|
||||
CASE("mute-time")
|
||||
{
|
||||
struct chat_mute mutes[2];
|
||||
unsigned long now = 1000;
|
||||
|
||||
memset(mutes, 0, sizeof(mutes));
|
||||
CHECK(chat_mute_set(mutes, 2, "Milenko", 5, now), "not muted");
|
||||
CHECK(chat_mute_find(mutes, 2, "Milenko", now) != NULL, "not found");
|
||||
CHECK(chat_mute_find(mutes, 2, "milenko", now) == NULL, "found by another name");
|
||||
CHECK(chat_mute_minutes_left(&mutes[0], now) == 5, "%d", chat_mute_minutes_left(&mutes[0], now));
|
||||
CHECK(chat_mute_minutes_left(&mutes[0], now + 1) == 5, "%d", chat_mute_minutes_left(&mutes[0], now + 1));
|
||||
CHECK(chat_mute_minutes_left(&mutes[0], now + CHAT_MILLISECONDS_PER_MINUTE) == 4, "%d",
|
||||
chat_mute_minutes_left(&mutes[0], now + CHAT_MILLISECONDS_PER_MINUTE));
|
||||
CHECK(chat_mute_minutes_left(&mutes[0], now + 5 * CHAT_MILLISECONDS_PER_MINUTE - 1) == 1, "the last minute");
|
||||
CHECK(chat_mute_find(mutes, 2, "Milenko", now + 5 * CHAT_MILLISECONDS_PER_MINUTE) == NULL, "still muted");
|
||||
CHECK(!mutes[0].active, "not let go");
|
||||
CHECK(!strcmp(chat_minutes_plural(1), "") && !strcmp(chat_minutes_plural(2), "s"), "plurals");
|
||||
}
|
||||
|
||||
/* the clock wrapping round (system_milliseconds) changes nothing */
|
||||
CASE("mute-wrap")
|
||||
{
|
||||
struct chat_mute mute;
|
||||
unsigned long now = 0xFFFFFFFFUL - 1000;
|
||||
|
||||
memset(&mute, 0, sizeof(mute));
|
||||
chat_mute_set(&mute, 1, "Milenko", 1, now);
|
||||
CHECK(chat_mute_minutes_left(&mute, now + 2000) == 1, "ended across the wrap");
|
||||
CHECK(chat_mute_minutes_left(&mute, now + CHAT_MILLISECONDS_PER_MINUTE) == 0, "not ended across the wrap");
|
||||
}
|
||||
|
||||
/* muting a player again sets their minutes again, in the one mute */
|
||||
CASE("mute-again")
|
||||
{
|
||||
struct chat_mute mutes[3];
|
||||
unsigned long now = 1000;
|
||||
int index;
|
||||
int held = 0;
|
||||
|
||||
memset(mutes, 0, sizeof(mutes));
|
||||
chat_mute_set(mutes, 3, "Milenko", 5, now);
|
||||
chat_mute_set(mutes, 3, "Milenko", 1, now + 1000);
|
||||
for (index = 0; index < 3; index++)
|
||||
held += mutes[index].active;
|
||||
CHECK(held == 1, "%d mutes held for one player", held);
|
||||
CHECK(chat_mute_minutes_left(chat_mute_find(mutes, 3, "Milenko", now + 1000), now + 1000) == 1,
|
||||
"the minutes not set again");
|
||||
}
|
||||
|
||||
/* every mute held: no other player's mute ends early to make room;
|
||||
one run out is taken again */
|
||||
CASE("mute-full")
|
||||
{
|
||||
struct chat_mute mutes[2];
|
||||
unsigned long now = 1000;
|
||||
|
||||
memset(mutes, 0, sizeof(mutes));
|
||||
CHECK(chat_mute_set(mutes, 2, "a", 10, now) && chat_mute_set(mutes, 2, "b", 1, now), "not muted");
|
||||
CHECK(!chat_mute_set(mutes, 2, "c", 5, now), "a third mute taken from two");
|
||||
CHECK(chat_mute_find(mutes, 2, "a", now) && chat_mute_find(mutes, 2, "b", now), "a mute let go early");
|
||||
CHECK(chat_mute_set(mutes, 2, "c", 5, now + CHAT_MILLISECONDS_PER_MINUTE), "the run out mute not taken");
|
||||
CHECK(chat_mute_find(mutes, 2, "a", now + CHAT_MILLISECONDS_PER_MINUTE) &&
|
||||
chat_mute_find(mutes, 2, "c", now + CHAT_MILLISECONDS_PER_MINUTE), "the wrong mute taken");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -10,7 +10,6 @@ typedef short SHORT;
|
||||
struct platform_input_state { unsigned char keys[TEST_SCANCODE_COUNT], mouse_buttons[8]; BOOL mouse_released, menus; };
|
||||
#define SDL_SCANCODE_COUNT TEST_SCANCODE_COUNT
|
||||
#define console_is_active() FALSE
|
||||
#define network_chat_typing() FALSE
|
||||
#define platform_screen_keyboard(show, password) ((void)0)
|
||||
typedef struct { unsigned short wButtons; BYTE bAnalogButtons[8]; SHORT sThumbLX, sThumbLY; } XINPUT_GAMEPAD;
|
||||
#define SDL_BUTTON_X1 4
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
"""Streamer mode's word filter (chat_filter.c): what it replaces, in which spellings, what it leaves alone
|
||||
(Scunthorpe), the case and endings of what it writes, and its buffer's bounds."""
|
||||
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
||||
from harness import CHECK_FAILED, build, function, mutated, read, run # noqa: E402
|
||||
|
||||
CASES = ["replaces", "spellings", "dodges", "leaves-alone", "compounds", "case", "endings", "formatted", "shown",
|
||||
"idempotent", "bounds"]
|
||||
FUNCTIONS = ("chat_filter_letter", "chat_filter_word_character", "chat_filter_lower", "chat_filter_in",
|
||||
"chat_filter_character_matches", "chat_filter_match_from", "chat_filter_match", "chat_filter_separator",
|
||||
"chat_filter_match_spelled", "chat_filter_ending", "chat_filter_put",
|
||||
"chat_filter_replace", "chat_filter_text", "chat_filter_formatted", "chat_filter_cached", "chat_filter_shown",
|
||||
"chat_filter_shown_ascii")
|
||||
|
||||
# a fault in chat_filter.c, the function or table it is in, and the case that must catch it
|
||||
NEGATIVE_CONTROLS = {
|
||||
"no-stand-ins": (("if (chat_filter_lower(character) == (wchar_t)stand_ins[index][0]",
|
||||
"if (0 && chat_filter_lower(character) == (wchar_t)stand_ins[index][0]"),
|
||||
"chat_filter_character_matches", "spellings"),
|
||||
"no-repeats": (("last < length && chat_filter_lower(text[last]) == chat_filter_lower(text[at]); last++)",
|
||||
"0; last++)"), "chat_filter_match_from", "spellings"),
|
||||
"repeats-of-stand-ins": (("chat_filter_lower(text[last]) == chat_filter_lower(text[at]); last++)",
|
||||
"chat_filter_character_matches(text[last], word[index]); last++)"),
|
||||
"chat_filter_match_from", "leaves-alone"),
|
||||
"no-backtracking": (("for (; last > at; last--)", "for (; last > at; last = at)"), "chat_filter_match_from",
|
||||
"spellings"),
|
||||
"no-spelled-out": (("matched = chat_filter_match_spelled(source, at, source_length, word->word);", "matched = 0;"),
|
||||
"chat_filter_text", "dodges"),
|
||||
"no-ending-repeats": (("while (text[index + 1] != text[index] && at < length", "while (0 && at < length"),
|
||||
"chat_filter_ending", "dodges"),
|
||||
"anywhere-for-all": (("if (word->match != _chat_filter_anywhere && !word_start)", "if (0)"),
|
||||
"chat_filter_text", "leaves-alone"),
|
||||
"any-ending": (("if ((word->match == _chat_filter_word || word->match == _chat_filter_word_endings) &&",
|
||||
"if (0 &&"), "chat_filter_text", "leaves-alone"),
|
||||
"lower-case-only": (("if (all_capitals || (first_capital && index == 0))", "if (0)"), "chat_filter_replace",
|
||||
"case"),
|
||||
"endings-unfitted": (("else if (last == 'e' && (next == 'e' || next == 'i' || next == 'y'))", "else if (0)"),
|
||||
"chat_filter_replace", "endings"),
|
||||
"unbounded": (("if (*length < size - 1)", "if (1)"), "chat_filter_put", "bounds"),
|
||||
"codes-filtered": (("while (source[index] && source[index] != '|' && run_length", "while (source[index] && run_length"),
|
||||
"chat_filter_formatted", "formatted"),
|
||||
# (the cache's lookup keyed by nothing: the hash, length and text all left out)
|
||||
"cache-ignores-key": (('chat_filter_cache[slot].hash != hash ||\n\t\t\tchat_filter_cache[slot].length != length)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\t\tfor (index = 0; index < length && chat_filter_cache[slot].source[index] == string[index]; index++)\n\t\t\t;\n\t\tif (index == length)',
|
||||
"chat_filter_cache[slot].length < 0)\n\t\t{\n\t\t\tcontinue;\n\t\t}\n\t\tif (1)"),
|
||||
"chat_filter_cached", "shown"),
|
||||
"long-text-cut": (("if (length >= CHAT_FILTER_CACHED_SIZE - 1)\n\t\t\treturn string;", "if (0)\n\t\t\treturn string;"),
|
||||
"chat_filter_cached", "shown"),
|
||||
}
|
||||
|
||||
|
||||
def generated(fault=None, faulty_function=None):
|
||||
source = read("port/linux/game/chat_filter.c")
|
||||
text = enum_block(source, "_chat_filter_word,") + enum_block(source, "CHAT_FILTER_PLURAL_ENDINGS =")
|
||||
for name in ("struct chat_filter_word\n{", "static struct chat_filter_word const chat_filter_words[] =",
|
||||
"static char const *const chat_filter_endings[] =", "static struct\n{\n\tboolean valid;"):
|
||||
start = source.index(name)
|
||||
end = source.index("}", start + len(name))
|
||||
end = source.index(";", end) + 1
|
||||
text += source[start:end] + "\n"
|
||||
for name in FUNCTIONS:
|
||||
code = function(source, name)
|
||||
if fault and name == faulty_function:
|
||||
code = mutated(code, *fault)
|
||||
text += code + "\n"
|
||||
return (("under_test.inc", text),)
|
||||
|
||||
|
||||
def enum_block(source, member):
|
||||
at = source.index(member)
|
||||
start = source.rindex("enum", 0, at)
|
||||
return source[start:source.index("};", at) + 2] + "\n"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("case", CASES)
|
||||
def test_case(case):
|
||||
status, output = run(build("chat_filter", generated()), case)
|
||||
assert status == 0, output
|
||||
|
||||
|
||||
@pytest.mark.parametrize("control", NEGATIVE_CONTROLS)
|
||||
def test_negative_control(control):
|
||||
fault, faulty_function, case = NEGATIVE_CONTROLS[control]
|
||||
status, output = run(build("chat_filter", generated(fault, faulty_function)), case)
|
||||
assert status == CHECK_FAILED, f"chat_filter.c with a fault ({control}) passed '{case}': the test cannot see it"
|
||||
|
||||
|
||||
def test_every_word_is_lower_case():
|
||||
"""The words are matched as written in lower case: one with a capital would never match."""
|
||||
source = read("port/linux/game/chat_filter.c")
|
||||
table = source[source.index("chat_filter_words[] ="):]
|
||||
table = table[:table.index("};")]
|
||||
words = re.findall(r'\{ "([^"]+)", "', table)
|
||||
assert words and all(word == word.lower() for word in words), words
|
||||
@@ -1,98 +0,0 @@
|
||||
"""The rules of network_chat.c's text chat: a line's text (another machine's is untrusted), the lines the host takes
|
||||
from a machine, who hears a team's line and a vehicle's, how long a line shows, and the host's /mute: what it
|
||||
reads from a typed line, the player a name means, and how long a mute lasts."""
|
||||
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
|
||||
from harness import CHECK_FAILED, build, constant, enum_with, function, mutated, read, run, structure # noqa: E402
|
||||
|
||||
CASES = ["text-printable", "text-control-characters", "text-bounds", "text-empty", "append", "rate-burst",
|
||||
"rate-refill", "rate-wrap", "hears", "hears-vehicle", "flags", "alpha", "command-mute", "command-unmute",
|
||||
"command-not", "command-usage", "name-match", "name-bounds", "mute-time", "mute-wrap", "mute-again",
|
||||
"mute-full"]
|
||||
FUNCTIONS = ("chat_text_clean", "chat_text_append", "chat_rate_take", "chat_hears", "chat_channel_from_flags",
|
||||
"chat_channel_flags", "chat_line_alpha", "chat_lower", "chat_command_word", "chat_command_parse",
|
||||
"chat_name_match", "chat_mute_minutes_left", "chat_mute_find", "chat_mute_set", "chat_minutes_plural")
|
||||
|
||||
# a fault in network_chat.c, the function it is in, and the case that must catch it
|
||||
NEGATIVE_CONTROLS = {
|
||||
"control-characters-kept": (("if (character < ' ' || character > '~')", "if (character < 0)"),
|
||||
"chat_text_clean", "text-control-characters"),
|
||||
"read-past-size": (("index < source_size && source[index]", "source[index]"), "chat_text_clean", "text-bounds"),
|
||||
"no-length-limit": (("&& length < CHAT_TEXT_SIZE - 1; index++", "; index++"), "chat_text_clean", "text-bounds"),
|
||||
"trailing-spaces-kept": (("while (length > 0 && destination[length - 1] == ' ')", "while (0)"),
|
||||
"chat_text_clean", "text-printable"),
|
||||
"append-unchecked": (("if (*typed >= ' ' && *typed <= '~')", "if (*typed)"), "chat_text_append", "append"),
|
||||
"no-burst-limit": (("if (rate->tokens <= 0)\n\t\treturn FALSE;", ""), "chat_rate_take", "rate-burst"),
|
||||
"refill-unbounded": (("MIN((unsigned long)CHAT_BURST, rate->tokens + gained)", "(rate->tokens + gained)"),
|
||||
"chat_rate_take", "rate-refill"),
|
||||
"team-line-to-all": (("|| sender_team == listener_team", "|| TRUE"), "chat_hears", "hears"),
|
||||
"vehicle-line-to-all": (("return sender_vehicle != NONE && sender_vehicle == listener_vehicle;", "return TRUE;"),
|
||||
"chat_hears", "hears-vehicle"),
|
||||
"vehicle-line-on-foot": (("return sender_vehicle != NONE && sender_vehicle", "return sender_vehicle"),
|
||||
"chat_hears", "hears-vehicle"),
|
||||
"vehicle-flag-dropped": (("if (channel == _chat_channel_vehicle)", "if (0)"), "chat_channel_flags", "flags"),
|
||||
"never-fades": (("if (typing || age < CHAT_LINE_MILLISECONDS)", "if (TRUE)"), "chat_line_alpha", "alpha"),
|
||||
"command-word-prefix": (("if (text[index] && text[index] != ' ')\n\t\treturn NULL;", ""), "chat_command_word",
|
||||
"command-not"),
|
||||
"command-case": (("if (chat_lower(text[index]) != word[index])", "if (text[index] != word[index])"),
|
||||
"chat_command_word", "command-mute"),
|
||||
"minutes-unbounded": (("MIN(typed_minutes * 10 + (rest[index] - '0'), (long)CHAT_MUTE_MAXIMUM_MINUTES)",
|
||||
"typed_minutes * 10 + (rest[index] - '0')"), "chat_command_parse", "command-mute"),
|
||||
"minutes-zero": (("if (typed_minutes == 0)\n\t\t\t\treturn _chat_command_usage;", ""), "chat_command_parse",
|
||||
"command-usage"),
|
||||
"name-unbounded": (("index < length && index < name_size - 1", "index < length"), "chat_command_parse",
|
||||
"command-mute"),
|
||||
"ambiguous-taken": (("if (exact_count > 1 || begins_count > 1)", "if (exact_count > 1 || begins_count > 99)"),
|
||||
"chat_name_match", "name-match"),
|
||||
"exact-not-first": (("if (exact_count == 1)\n\t\treturn exact_index;", "if (0)\n\t\treturn exact_index;"),
|
||||
"chat_name_match", "name-match"),
|
||||
"mute-never-ends": (("if (elapsed >= mute->milliseconds)", "if (0)"), "chat_mute_minutes_left", "mute-time"),
|
||||
"mute-wrap-broken": (("unsigned long elapsed = now - mute->started;",
|
||||
"unsigned long elapsed = now > mute->started ? now - mute->started : 0;"),
|
||||
"chat_mute_minutes_left", "mute-wrap"),
|
||||
"mute-doubled": (("struct chat_mute *mute = chat_mute_find(mutes, count, name, now);",
|
||||
"struct chat_mute *mute = NULL;"), "chat_mute_set", "mute-again"),
|
||||
"mute-overwrites": (("if (!chat_mute_minutes_left(&mutes[index], now))", "if (TRUE)"), "chat_mute_set",
|
||||
"mute-full"),
|
||||
}
|
||||
|
||||
|
||||
def generated(fault=None, faulty_function=None):
|
||||
source = read("port/linux/game/network_chat.c")
|
||||
config = ""
|
||||
for name in ("CHAT_TEXT_SIZE", "CHAT_BURST", "CHAT_REFILL_MILLISECONDS", "CHAT_LINE_MILLISECONDS",
|
||||
"CHAT_FADE_MILLISECONDS", "CHAT_NAME_SIZE", "CHAT_MUTE_DEFAULT_MINUTES", "CHAT_MUTE_MAXIMUM_MINUTES",
|
||||
"CHAT_MILLISECONDS_PER_MINUTE"):
|
||||
config += f"#define {name} {constant(source, name)}\n"
|
||||
config += f"#define CHAT_NAME_AMBIGUOUS ({re.search(r'CHAT_NAME_AMBIGUOUS = (-?[0-9]+)', source).group(1)})\n"
|
||||
config += structure(source, "chat_rate") + "\n"
|
||||
config += structure(source, "chat_mute") + "\n"
|
||||
config += enum_with(source, "_chat_line_team_bit,") + "\n"
|
||||
config += enum_with(source, "_chat_command_none,") + "\n"
|
||||
config += enum_with(read("port/linux/game/network_chat.h"), "_chat_channel_everyone,") + "\n"
|
||||
text = ""
|
||||
for name in FUNCTIONS:
|
||||
code = function(source, name)
|
||||
if fault and name == faulty_function:
|
||||
code = mutated(code, *fault)
|
||||
text += code + "\n"
|
||||
return (("config.inc", config), ("under_test.inc", text))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("case", CASES)
|
||||
def test_case(case):
|
||||
status, output = run(build("chat_logic", generated()), case)
|
||||
assert status == 0, output
|
||||
|
||||
|
||||
@pytest.mark.parametrize("control", NEGATIVE_CONTROLS)
|
||||
def test_negative_control(control):
|
||||
fault, faulty_function, case = NEGATIVE_CONTROLS[control]
|
||||
status, output = run(build("chat_logic", generated(fault, faulty_function)), case)
|
||||
assert status == CHECK_FAILED, f"network_chat.c with a fault ({control}) passed '{case}': the test cannot see it"
|
||||
+1
-19
@@ -253,27 +253,9 @@ SCREENS["audio_settings/sound_processing"] = {
|
||||
],
|
||||
}
|
||||
_audio["rows"] = [row for row in _audio["rows"] if row[1] not in _processing]
|
||||
|
||||
# Audio Setup's voice chat (port/linux/game/network_voice.c) with the text
|
||||
# chat's colours and streamer mode (network_chat.c, chat_filter.c), a screen
|
||||
# of their own (a category, as
|
||||
# Video Setup's): Audio Setup has no room for another row, and Network Setup,
|
||||
# which has none either, is not offered in a game (menu_tags.c)
|
||||
_chat = {"audio.voice_chat", "audio.voice_volume"}
|
||||
SCREENS["audio_settings/chat"] = {
|
||||
"screen": "chat_settings_screen", "header": _audio["header"], "spacing": 30,
|
||||
"rows": [row for row in _audio["rows"] if row[1] in _chat] + [
|
||||
("CHAT TEXT:", "display.chat_style", [("DEFAULT", "default"), ("WHITE", "white")],
|
||||
"The text chat's colors: DEFAULT, names in their\nteam's color; WHITE, all white, as Custom Edition.", None),
|
||||
("STREAMER MODE:", "game.streamer_mode", ON_OFF,
|
||||
"Show slurs and swearing in the text chat as\nharmless words, for streaming.", None),
|
||||
],
|
||||
}
|
||||
_audio["rows"] = [row for row in _audio["rows"] if row[1] not in _chat]
|
||||
_audio["categories"] = [
|
||||
("SOUND PROCESSING:", "audio_settings/sound_processing",
|
||||
"The reverb, and how sounds are converted to the\noutput's rate."),
|
||||
("VOICE AND TEXT CHAT:", "audio_settings/chat", "Voice chat, and the text chat's streamer\nmode."),
|
||||
]
|
||||
|
||||
# Network Setup's map sharing (port/linux/game/map_share.c): this machine's
|
||||
@@ -301,7 +283,7 @@ SCREENS["network_setup/map_sharing"] = {
|
||||
|
||||
# Controls Setup: the keyboard and mouse's actions, in groups (the order of
|
||||
# port/linux/game/menu_functions.c's table of them)
|
||||
CONTROL_GROUPS = ["MOVEMENT", "WEAPONS", "ACTIONS", "COMMUNICATION"]
|
||||
CONTROL_GROUPS = ["MOVEMENT", "WEAPONS", "ACTIONS"]
|
||||
CONTROL_ROWS = 7
|
||||
|
||||
# the settings whose rows have the wider spinner (a device's name)
|
||||
|
||||
Reference in new issue
Block a user