diff --git a/CHANGELOG.md b/CHANGELOG.md index 11959e20ed..5e13e50203 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - NPCs will migrate to new towns if they are dissatisfied with their current town - Female humanoids now have a greeting sound effect - Loot that drops multiple items is now distributed fairly between damage contributors. +- Added accessibility settings tab. +- Setting to enable subtitles describing sfx. ### Changed diff --git a/assets/voxygen/audio/sfx.ron b/assets/voxygen/audio/sfx.ron index 9915121026..8d5036dee4 100644 --- a/assets/voxygen/audio/sfx.ron +++ b/assets/voxygen/audio/sfx.ron @@ -1,3 +1,4 @@ +#![enable(implicit_some)] ( { // @@ -8,6 +9,7 @@ "voxygen.audio.sfx.ambient.fire", ], threshold: 21.835, + subtitle: "subtitle-campfire", ), Birdcall: ( files: [ @@ -23,12 +25,14 @@ "voxygen.audio.sfx.ambient.birdcall_10", ], threshold: 10.0, + subtitle: "subtitle-bird_call", ), Owl: ( files: [ "voxygen.audio.sfx.ambient.owl_1", ], threshold: 14.0, + subtitle: "subtitle-owl", ), //Crickets: -20dB Cricket1: ( @@ -60,6 +64,7 @@ "voxygen.audio.sfx.ambient.bees_1", ], threshold: 15.0, + subtitle: "subtitle-bees", ), RunningWaterSlow: ( files: [ @@ -92,6 +97,7 @@ "voxygen.audio.sfx.ambient.river_sounds.running_water-027", ], threshold: 7.0, + subtitle: "subtitle-running_water", ), RunningWaterFast: ( files: [ @@ -121,6 +127,7 @@ "voxygen.audio.sfx.ambient.river_sounds.fast_water-024", ], threshold: 5.0, + subtitle: "subtitle-running_water", ), // // Character States @@ -133,6 +140,7 @@ "voxygen.audio.sfx.footsteps.water_splash_4", ], threshold: 0.5, + subtitle: "subtitle-swim", ), Run(Earth): ( files: [ @@ -143,6 +151,7 @@ "voxygen.audio.sfx.footsteps.stepdirt_5", ], threshold: 1.8, + subtitle: "subtitle-footsteps_earth", ), QuadRun(Earth): ( files: [ @@ -153,6 +162,7 @@ "voxygen.audio.sfx.footsteps.stepdirt_5", ], threshold: 0.9, + subtitle: "subtitle-footsteps_earth", ), Run(Grass): ( files: [ @@ -164,6 +174,7 @@ "voxygen.audio.sfx.footsteps.stepgrass_6", ], threshold: 1.8, + subtitle: "subtitle-footsteps_grass", ), QuadRun(Grass): ( files: [ @@ -175,6 +186,7 @@ "voxygen.audio.sfx.footsteps.stepgrass_6", ], threshold: 0.9, + subtitle: "subtitle-footsteps_grass", ), // For when sand 1) exists and 2) has unique sounds // Run(Sand): ( @@ -204,6 +216,7 @@ "voxygen.audio.sfx.footsteps.snow_step_3", ], threshold: 1.8, + subtitle: "subtitle-footsteps_snow", ), QuadRun(Snow): ( files: [ @@ -212,6 +225,7 @@ "voxygen.audio.sfx.footsteps.snow_step_3", ], threshold: 0.9, + subtitle: "subtitle-footsteps_snow", ), Run(Rock): ( files: [ @@ -229,6 +243,7 @@ "voxygen.audio.sfx.footsteps.stone_step_12", ], threshold: 1.8, + subtitle: "subtitle-footsteps_rock", ), QuadRun(Rock): ( files: [ @@ -246,6 +261,7 @@ "voxygen.audio.sfx.footsteps.stone_step_12", ], threshold: 0.9, + subtitle: "subtitle-footsteps_rock", ), Roll: ( files: [ @@ -253,6 +269,7 @@ "voxygen.audio.sfx.character.dive_roll_2", ], threshold: 0.3, + subtitle: "subtitle-roll", ), Climb: ( files: [ @@ -264,18 +281,21 @@ "voxygen.audio.sfx.footsteps.stepdirt_5", ], threshold: 1.2, + subtitle: "subtitle-climb", ), GliderOpen: ( files: [ "voxygen.audio.sfx.character.glider_open", ], threshold: 0.1, + subtitle: "subtitle-glider_open", ), GliderClose: ( files: [ "voxygen.audio.sfx.character.glider_close", ], threshold: 0.1, + subtitle: "subtitle-glider_close", ), Glide: ( files: [ @@ -289,6 +309,7 @@ "voxygen.audio.sfx.character.catch_air_8", ], threshold: 0.85, + subtitle: "subtitle-glide", ), // @@ -299,90 +320,105 @@ "voxygen.audio.sfx.weapon.sword_out", ], threshold: 0.5, + subtitle: "subtitle-wield_sword", ), Unwield(Sword): ( files: [ "voxygen.audio.sfx.weapon.sword_in", ], threshold: 0.5, + subtitle: "subtitle-unwield_sword", ), Attack(ComboMelee2(Action), Sword): ( files: [ "voxygen.audio.sfx.abilities.swing_sword", ], threshold: 0.7, + subtitle: "subtitle-sword_attack", ), Attack(FinisherMelee(Action), Sword): ( files: [ "voxygen.audio.sfx.abilities.swing_sword", ], threshold: 0.7, + subtitle: "subtitle-sword_attack", ), Attack(DiveMelee(Action), Sword): ( files: [ "voxygen.audio.sfx.abilities.swing_sword", ], threshold: 0.7, + subtitle: "subtitle-sword_attack", ), Attack(RiposteMelee(Action), Sword): ( files: [ "voxygen.audio.sfx.abilities.swing_sword", ], threshold: 0.7, + subtitle: "subtitle-sword_attack", ), Attack(RapidMelee(Action), Sword): ( files: [ "voxygen.audio.sfx.abilities.swing_sword", ], threshold: 0.7, + subtitle: "subtitle-sword_attack", ), Attack(ChargedMelee(Action), Sword): ( files: [ "voxygen.audio.sfx.abilities.swing_sword", ], threshold: 0.7, + subtitle: "subtitle-sword_attack", ), Attack(DashMelee(Action), Sword): ( files: [ "voxygen.audio.sfx.abilities.sword_dash", ], threshold: 0.8, + subtitle: "subtitle-sword_attack", ), Attack(DashMelee(Action), Hammer): ( files: [ "voxygen.audio.sfx.abilities.swing", ], threshold: 0.7, + subtitle: "subtitle-hammer-attack", ), Attack(SpinMelee(Action), Sword): ( files: [ "voxygen.audio.sfx.abilities.swing_sword", ], threshold: 0.7, + subtitle: "subtitle-sword_attack", ), Attack(ComboMelee(Action, 1), Sword): ( files: [ "voxygen.audio.sfx.abilities.swing_sword", ], threshold: 0.7, + subtitle: "subtitle-sword_attack", ), Attack(ComboMelee(Action, 2), Sword): ( files: [ "voxygen.audio.sfx.abilities.separated_second_swing", ], threshold: 0.7, + subtitle: "subtitle-sword_attack", ), Attack(ComboMelee(Action, 3), Sword): ( files: [ "voxygen.audio.sfx.abilities.separated_third_swing", ], threshold: 0.7, + subtitle: "subtitle-sword_attack", ), Inventory(CollectedTool(Sword)): ( files: [ "voxygen.audio.sfx.inventory.pickup_sword", ], threshold: 0.3, + subtitle: "subtitle-pickup_sword", ), // @@ -393,36 +429,42 @@ "voxygen.audio.sfx.weapon.weapon_out", ], threshold: 0.5, + subtitle: "subtitle-wield_hammer", ), Unwield(Hammer): ( files: [ "voxygen.audio.sfx.weapon.weapon_in", ], threshold: 0.5, + subtitle: "subtitle-unwield_hammer", ), Attack(ComboMelee(Action, 1), Hammer): ( files: [ "voxygen.audio.sfx.abilities.swing", ], threshold: 0.7, + subtitle: "subtitle-hammer_attack", ), Attack(ChargedMelee(Action), Hammer): ( files: [ "voxygen.audio.sfx.abilities.swing", ], threshold: 0.7, + subtitle: "subtitle-hammer_attack", ), Attack(LeapMelee(Action), Hammer): ( files: [ "voxygen.audio.sfx.abilities.swing", ], threshold: 0.8, + subtitle: "subtitle-hammer_attack", ), Inventory(CollectedTool(Hammer)): ( files: [ "voxygen.audio.sfx.inventory.pickup_sword", ], threshold: 0.3, + subtitle: "subtitle-pickup_hammer", ), // @@ -433,48 +475,56 @@ "voxygen.audio.sfx.weapon.weapon_out", ], threshold: 0.5, + subtitle: "subtitle-wield_axe", ), Unwield(Axe): ( files: [ "voxygen.audio.sfx.weapon.weapon_in", ], threshold: 0.5, + subtitle: "subtitle-unwield_axe", ), Attack(ComboMelee(Action, 1), Axe): ( files: [ "voxygen.audio.sfx.abilities.swing", ], threshold: 0.7, + subtitle: "subtitle-axe_attack", ), Attack(ComboMelee(Action, 2), Axe): ( files: [ "voxygen.audio.sfx.abilities.swing", ], threshold: 0.7, + subtitle: "subtitle-axe_attack", ), Attack(SpinMelee(Action), Axe): ( files: [ "voxygen.audio.sfx.abilities.swing", ], threshold: 0.8, + subtitle: "subtitle-axe_attack", ), Attack(LeapMelee(Action), Axe): ( files: [ "voxygen.audio.sfx.abilities.swing", ], threshold: 0.8, + subtitle: "subtitle-axe_attack", ), Attack(BasicMelee(Action), Axe): ( files: [ "voxygen.audio.sfx.abilities.swing", ], threshold: 0.8, + subtitle: "subtitle-axe_attack", ), Inventory(CollectedTool(Axe)): ( files: [ "voxygen.audio.sfx.inventory.pickup_sword", ], threshold: 0.3, + subtitle: "subtitle-pickup_axe", ), // @@ -485,18 +535,21 @@ "voxygen.audio.sfx.weapon.staff_out", ], threshold: 0.5, + subtitle: "subtitle-wield_staff", ), Unwield(Staff): ( files: [ "voxygen.audio.sfx.weapon.staff_in", ], threshold: 0.5, + subtitle: "subtitle-unwield_staff", ), Attack(BasicBeam, Staff): ( files: [ "voxygen.audio.sfx.abilities.flame_thrower", ], threshold: 0.2, + subtitle: "subtitle-staff_attack", ), //Attack(BasicRanged, Staff): ( // files: [ @@ -509,6 +562,7 @@ "voxygen.audio.sfx.inventory.pickup_staff", ], threshold: 0.3, + subtitle: "subtitle-pickup_staff", ), // @@ -519,12 +573,14 @@ "voxygen.audio.sfx.weapon.weapon_out", ], threshold: 0.5, + subtitle: "subtitle-wield_bow", ), Unwield(Bow): ( files: [ "voxygen.audio.sfx.weapon.weapon_in", ], threshold: 0.5, + subtitle: "subtitle-unwield_bow", ), //Attack(BasicRanged, Bow): ( // files: [ @@ -537,6 +593,7 @@ "voxygen.audio.sfx.inventory.add_item", ], threshold: 0.3, + subtitle: "subtitle-pickup_bow", ), // @@ -547,24 +604,28 @@ "voxygen.audio.sfx.weapon.staff_out", ], threshold: 0.5, + subtitle: "subtitle-wield_sceptre", ), Unwield(Sceptre): ( files: [ "voxygen.audio.sfx.weapon.staff_in", ], threshold: 0.5, + subtitle: "subtitle-unwield_sceptre", ), Attack(BasicAura, Sceptre): ( files: [ "voxygen.audio.sfx.abilities.sceptre_aura", ], threshold: 2.5, + subtitle: "subtitle-sceptre_heal", ), Inventory(CollectedTool(Sceptre)): ( files: [ "voxygen.audio.sfx.inventory.pickup_staff", ], threshold: 0.3, + subtitle: "subtitle-pickup_sceptre", ), // @@ -575,6 +636,7 @@ "voxygen.audio.sfx.abilities.barrel_organ", ], threshold: 34.75, + subtitle: "subtitle-instrument_organ", ), // Player Instruments Wield(Instrument): ( @@ -582,12 +644,14 @@ "voxygen.audio.sfx.weapon.weapon_out", ], threshold: 0.5, + subtitle: "subtitle-wield_instrument", ), Unwield(Instrument): ( files: [ "voxygen.audio.sfx.weapon.weapon_in", ], threshold: 0.5, + subtitle: "subtitle-unwield_instrument", ), Music(Instrument, Custom("DoubleBass")): ( files: [ @@ -605,7 +669,8 @@ "voxygen.audio.sfx.instrument.double_bass.double_bass_ge", ], threshold: 0.5, - ), + subtitle: "subtitle-instrument_double_bass", + ), Music(Instrument, Custom("Flute")): ( files: [ "voxygen.audio.sfx.instrument.flute.flute_c", @@ -622,7 +687,8 @@ "voxygen.audio.sfx.instrument.flute.flute_eg", ], threshold: 0.5, - ), + subtitle: "subtitle-instrument_flute", + ), Music(Instrument, Custom("GlassFlute")): ( files: [ "voxygen.audio.sfx.instrument.glass_flute.glass_flute_c", @@ -639,7 +705,8 @@ "voxygen.audio.sfx.instrument.glass_flute.glass_flute_eg", ], threshold: 0.5, - ), + subtitle: "subtitle-instrument_glass_flute", + ), Music(Instrument, Custom("Lyre")): ( files: [ "voxygen.audio.sfx.instrument.lyre.lyre_c", @@ -656,7 +723,8 @@ "voxygen.audio.sfx.instrument.lyre.lyre_ega", ], threshold: 0.5, - ), + subtitle: "subtitle-instrument_lyre", + ), Music(Instrument, Custom("IcyTalharpa")): ( files: [ "voxygen.audio.sfx.instrument.icy_talharpa.icy_talharpa_c", @@ -673,7 +741,8 @@ "voxygen.audio.sfx.instrument.icy_talharpa.icy_talharpa_ega", ], threshold: 0.5, - ), + subtitle: "subtitle-instrument_icy_talharpa", + ), Music(Instrument, Custom("Kalimba")): ( files: [ "voxygen.audio.sfx.instrument.kalimba.kalimba_c", @@ -690,7 +759,8 @@ "voxygen.audio.sfx.instrument.kalimba.kalimba_da", ], threshold: 0.5, - ), + subtitle: "subtitle-instrument_kalimba", + ), Music(Instrument, Custom("Melodica")): ( files: [ "voxygen.audio.sfx.instrument.melodica.melodica_c", @@ -707,7 +777,8 @@ "voxygen.audio.sfx.instrument.melodica.melodica_ge", ], threshold: 0.5, - ), + subtitle: "subtitle-instrument_melodica", + ), Music(Instrument, Custom("Lute")): ( files: [ "voxygen.audio.sfx.instrument.lute.lute_c", @@ -724,7 +795,8 @@ "voxygen.audio.sfx.instrument.lute.lute_ded", ], threshold: 0.5, - ), + subtitle: "subtitle-instrument_lute", + ), Music(Instrument, Custom("Sitar")): ( files: [ "voxygen.audio.sfx.instrument.sitar.sitar_c", @@ -741,7 +813,8 @@ "voxygen.audio.sfx.instrument.sitar.sitar_gec", ], threshold: 0.5, - ), + subtitle: "subtitle-instrument_sitar", + ), Music(Instrument, Custom("Guitar")): ( files: [ "voxygen.audio.sfx.instrument.guitar.guitar_c", @@ -758,7 +831,8 @@ "voxygen.audio.sfx.instrument.guitar.guitar_gag", ], threshold: 0.5, - ), + subtitle: "subtitle-instrument_guitar", + ), Music(Instrument, Custom("DarkGuitar")): ( files: [ "voxygen.audio.sfx.instrument.dark_guitar.dark_guitar_a", @@ -777,7 +851,8 @@ "voxygen.audio.sfx.instrument.dark_guitar.dark_guitar_gec", ], threshold: 0.5, - ), + subtitle: "subtitle-instrument_dark_guitar", + ), Music(Instrument, Custom("Washboard")): ( files: [ "voxygen.audio.sfx.instrument.washboard.washboard_c", @@ -794,12 +869,14 @@ "voxygen.audio.sfx.instrument.washboard.washboard_e", ], threshold: 0.5, - ), + subtitle: "subtitle-instrument_washboard", + ), Inventory(CollectedTool(Instrument)): ( files: [ "voxygen.audio.sfx.inventory.add_item", ], threshold: 0.3, + subtitle: "subtitle-pickup_instrument", ), // // Dagger @@ -809,30 +886,35 @@ "voxygen.audio.sfx.weapon.dagger_out", ], threshold: 0.5, + subtitle: "subtitle-wield_dagger", ), Unwield(Dagger): ( files: [ "voxygen.audio.sfx.weapon.dagger_in", ], threshold: 0.5, + subtitle: "subtitle-unwield_dagger", ), Attack(BasicMelee(Action), Dagger): ( files: [ "voxygen.audio.sfx.abilities.swing_sword", ], threshold: 0.8, + subtitle: "subtitle-dagger_attack", ), Attack(DashMelee(Action), Dagger): ( files: [ "voxygen.audio.sfx.abilities.sword_dash", ], threshold: 0.8, + subtitle: "subtitle-dagger_attack", ), Inventory(CollectedTool(Dagger)): ( files: [ "voxygen.audio.sfx.inventory.pickup_sword", ], threshold: 0.3, + subtitle: "subtitle-pickup_dagger", ), // @@ -843,24 +925,28 @@ "voxygen.audio.sfx.weapon.shield_out", ], threshold: 0.5, + subtitle: "subtitle-wield_shield", ), Unwield(Shield): ( files: [ "voxygen.audio.sfx.weapon.weapon_in", ], threshold: 0.5, + subtitle: "subtitle-unwield_shield", ), Attack(BasicMelee(Action), Shield): ( files: [ "voxygen.audio.sfx.abilities.swing", ], threshold: 0.8, + subtitle: "subtitle-shield_attack", ), Inventory(CollectedTool(Shield)): ( files: [ "voxygen.audio.sfx.inventory.pickup_sword", ], threshold: 0.3, + subtitle: "subtitle-pickup_shield", ), // PickAxe Tool @@ -869,6 +955,7 @@ "voxygen.audio.sfx.inventory.add_item", ], threshold: 0.3, + subtitle: "subtitle-pickup_pick", ), // // Inventory @@ -878,18 +965,21 @@ "voxygen.audio.sfx.inventory.add_item", ], threshold: 0.3, + subtitle: "subtitle-pickup_item", ), Inventory(CollectedItem("Gemstone")): ( files: [ "voxygen.audio.sfx.inventory.collect_gemstone", ], threshold: 0.3, + subtitle: "subtitle-pickup_gemstone", ), Inventory(CollectFailed): ( files: [ "voxygen.audio.sfx.inventory.add_failed", ], threshold: 0.3, + subtitle: "subtitle-pickup_failed", ), Inventory(Swapped): ( files: [ @@ -924,192 +1014,231 @@ "voxygen.audio.sfx.inventory.consumable.liquid", ], threshold: 0.3, + subtitle: "subtitle-consume_potion", ), Inventory(Consumed("Medium Potion")): ( files: [ "voxygen.audio.sfx.inventory.consumable.liquid", ], threshold: 0.3, + subtitle: "subtitle-consume_potion", ), Inventory(Consumed("Large Potion")): ( files: [ "voxygen.audio.sfx.inventory.consumable.liquid", ], threshold: 0.3, + subtitle: "subtitle-consume_potion", ), Inventory(Consumed("Apple")): ( files: [ "voxygen.audio.sfx.inventory.consumable.apple", ], threshold: 0.3, + subtitle: "subtitle-consume_apple", ), Inventory(Consumed("Mushroom")): ( files: [ "voxygen.audio.sfx.inventory.consumable.food", ], threshold: 0.3, + subtitle: "subtitle-consume_food", ), Inventory(Consumed("Dwarven Cheese")): ( files: [ "voxygen.audio.sfx.inventory.consumable.food", ], threshold: 0.3, + subtitle: "subtitle-consume_cheese", ), Inventory(Consumed("Sunflower Ice Tea")): ( files: [ "voxygen.audio.sfx.inventory.consumable.liquid", ], threshold: 0.3, + subtitle: "subtitle-consume_liquid", ), Inventory(Consumed("Mushroom Curry")): ( files: [ "voxygen.audio.sfx.inventory.consumable.liquid", ], threshold: 0.3, + subtitle: "subtitle-consume_food", ), Inventory(Consumed("Apple Stick")): ( files: [ "voxygen.audio.sfx.inventory.consumable.food", ], threshold: 0.3, + subtitle: "subtitle-consume_food", ), Inventory(Consumed("Coconut")): ( files: [ "voxygen.audio.sfx.inventory.consumable.food", ], threshold: 0.3, + subtitle: "subtitle-consume_food", ), Inventory(Consumed("Mushroom Stick")): ( files: [ "voxygen.audio.sfx.inventory.consumable.food", ], threshold: 0.3, + subtitle: "subtitle-consume_food", ), Inventory(Consumed("Tomato")): ( files: [ "voxygen.audio.sfx.inventory.consumable.food", ], threshold: 0.3, + subtitle: "subtitle-consume_food", ), Inventory(Consumed("Carrot")): ( files: [ "voxygen.audio.sfx.inventory.consumable.food", ], threshold: 0.3, + subtitle: "subtitle-consume_food", ), Inventory(Consumed("Lettuce")): ( files: [ "voxygen.audio.sfx.inventory.consumable.food", ], threshold: 0.3, + subtitle: "subtitle-consume_food", ), Inventory(Consumed("Plain Salad")): ( files: [ "voxygen.audio.sfx.inventory.consumable.food", ], threshold: 0.3, + subtitle: "subtitle-consume_food", ), Inventory(Consumed("Tomato Salad")): ( files: [ "voxygen.audio.sfx.inventory.consumable.food", ], threshold: 0.3, + subtitle: "subtitle-consume_food", ), Inventory(Consumed("Cactus Colada")): ( files: [ "voxygen.audio.sfx.inventory.consumable.liquid", ], threshold: 0.3, + subtitle: "subtitle-consume_liquid", ), Inventory(Consumed("Raw Bird Meat")): ( files: [ "voxygen.audio.sfx.inventory.consumable.food", ], threshold: 0.3, + subtitle: "subtitle-consume_food", ), Inventory(Consumed("Cooked Bird Meat")): ( files: [ "voxygen.audio.sfx.inventory.consumable.food", ], threshold: 0.3, + subtitle: "subtitle-consume_food", ), Inventory(Consumed("Raw Fish")): ( files: [ "voxygen.audio.sfx.inventory.consumable.food", ], threshold: 0.3, + subtitle: "subtitle-consume_food", ), Inventory(Consumed("Cooked Fish")): ( files: [ "voxygen.audio.sfx.inventory.consumable.food", ], threshold: 0.3, + subtitle: "subtitle-consume_food", ), Inventory(Consumed("Raw Meat Slab")): ( files: [ "voxygen.audio.sfx.inventory.consumable.food", ], threshold: 0.3, + subtitle: "subtitle-consume_food", ), Inventory(Consumed("Cooked Meat Slab")): ( files: [ "voxygen.audio.sfx.inventory.consumable.food", ], threshold: 0.3, + subtitle: "subtitle-consume_food", ), Inventory(Consumed("Raw Meat Sliver")): ( files: [ "voxygen.audio.sfx.inventory.consumable.food", ], threshold: 0.3, + subtitle: "subtitle-consume_food", ), Inventory(Consumed("Cooked Meat Sliver")): ( files: [ "voxygen.audio.sfx.inventory.consumable.food", ], threshold: 0.3, + subtitle: "subtitle-consume_food", ), Inventory(Consumed("Raw Tough Meat")): ( files: [ "voxygen.audio.sfx.inventory.consumable.food", ], threshold: 0.3, + subtitle: "subtitle-consume_food", ), Inventory(Consumed("Cooked Tough Meat")): ( files: [ "voxygen.audio.sfx.inventory.consumable.food", ], threshold: 0.3, + subtitle: "subtitle-consume_food", ), Inventory(Consumed("Huge Raw Drumstick")): ( files: [ "voxygen.audio.sfx.inventory.consumable.food", ], threshold: 0.3, + subtitle: "subtitle-consume_food", ), Inventory(Consumed("Huge Cooked Drumstick")): ( files: [ "voxygen.audio.sfx.inventory.consumable.food", ], threshold: 0.3, + subtitle: "subtitle-consume_food", ), Inventory(Consumed("Honeycorn")): ( files: [ "voxygen.audio.sfx.inventory.consumable.food", ], threshold: 0.3, + subtitle: "subtitle-consume_food", ), Inventory(Consumed("Pumpkin Spice Brew")): ( files: [ "voxygen.audio.sfx.inventory.consumable.liquid", ], threshold: 0.3, + subtitle: "subtitle-consume_liquid", ), Inventory(Consumed("Blue Cheese")): ( files: [ "voxygen.audio.sfx.inventory.consumable.food", ], threshold: 0.3, + subtitle: "subtitle-consume_cheese", + ), + Inventory(Consumed("Golden Cheese")): ( + files: [ + "voxygen.audio.sfx.inventory.consumable.food", + ], + threshold: 0.3, + subtitle: "subtitle-consume_cheese", ), // @@ -1120,67 +1249,77 @@ "voxygen.audio.sfx.abilities.explosion", ], threshold: 0.2, + subtitle: "subtitle-explosion", ), ArrowShot: ( - files: [ - "voxygen.audio.sfx.abilities.arrow_shot_1", - "voxygen.audio.sfx.abilities.arrow_shot_2", - "voxygen.audio.sfx.abilities.arrow_shot_3", - "voxygen.audio.sfx.abilities.arrow_shot_4", - ], - threshold: 0.2, + files: [ + "voxygen.audio.sfx.abilities.arrow_shot_1", + "voxygen.audio.sfx.abilities.arrow_shot_2", + "voxygen.audio.sfx.abilities.arrow_shot_3", + "voxygen.audio.sfx.abilities.arrow_shot_4", + ], + threshold: 0.2, + subtitle: "subtitle-arrow_shot", ), FireShot: ( - files: [ - "voxygen.audio.sfx.abilities.fire_shot_1", - "voxygen.audio.sfx.abilities.fire_shot_2", - ], - threshold: 0.2, + files: [ + "voxygen.audio.sfx.abilities.fire_shot_1", + "voxygen.audio.sfx.abilities.fire_shot_2", + ], + threshold: 0.2, + subtitle: "subtitle-fire_shot", ), ArrowMiss: ( files: [ "voxygen.audio.sfx.character.arrow_miss", ], threshold: 0.2, + subtitle: "subtitle-arrow_miss", ), ArrowHit: ( files: [ - "voxygen.audio.sfx.character.arrow_hit", + "voxygen.audio.sfx.character.arrow_hit", ], threshold: 0.2, + subtitle: "subtitle-arrow_hit", ), SkillPointGain: ( files: [ - "voxygen.audio.sfx.character.level_up_sound_-_shorter_wind_up", - ], + "voxygen.audio.sfx.character.level_up_sound_-_shorter_wind_up", + ], threshold: 0.2, + subtitle: "subtitle-skill_point", ), SceptreBeam: ( files: [ "voxygen.audio.sfx.abilities.sceptre_channeling", ], threshold: 0.2, + subtitle: "subtitle-sceptre_beam", ), FlameThrower: ( files: [ "voxygen.audio.sfx.abilities.flame_thrower", ], threshold: 0.2, + subtitle: "subtitle-flame_thrower", ), BreakBlock: ( files: [ - "voxygen.audio.sfx.footsteps.stone_step_1", - ], + "voxygen.audio.sfx.footsteps.stone_step_1", + ], threshold: 0.2, + subtitle: "subtitle-break_block", ), Damage: ( files: [ - "voxygen.audio.sfx.character.hit_1", - "voxygen.audio.sfx.character.hit_2", - "voxygen.audio.sfx.character.hit_3", - "voxygen.audio.sfx.character.hit_4", - ], + "voxygen.audio.sfx.character.hit_1", + "voxygen.audio.sfx.character.hit_2", + "voxygen.audio.sfx.character.hit_3", + "voxygen.audio.sfx.character.hit_4", + ], threshold: 0.2, + subtitle: "subtitle-damage", ), Death: ( files: [ @@ -1189,21 +1328,24 @@ "voxygen.audio.sfx.character.death_3", ], threshold: 0.2, + subtitle: "subtitle-death", ), Block: ( files: [ - "voxygen.audio.sfx.character.block_1", - "voxygen.audio.sfx.character.block_2", - "voxygen.audio.sfx.character.block_3", - ], + "voxygen.audio.sfx.character.block_1", + "voxygen.audio.sfx.character.block_2", + "voxygen.audio.sfx.character.block_3", + ], threshold: 0.2, + subtitle: "subtitle-attack_blocked", ), Parry: ( files: [ - "voxygen.audio.sfx.character.parry_1", - "voxygen.audio.sfx.character.parry_2", - ], + "voxygen.audio.sfx.character.parry_1", + "voxygen.audio.sfx.character.parry_2", + ], threshold: 0.2, + subtitle: "subtitle-parry", ), PoiseChange(Interrupted): ( files: [ @@ -1213,6 +1355,7 @@ "voxygen.audio.sfx.character.interrupted_4", ], threshold: 0.25, + subtitle: "subtitle-interrupted", ), PoiseChange(Stunned): ( files: [ @@ -1221,6 +1364,7 @@ "voxygen.audio.sfx.character.stunned_3", ], threshold: 0.6, + subtitle: "subtitle-stunned", ), PoiseChange(Dazed): ( files: [ @@ -1229,6 +1373,7 @@ "voxygen.audio.sfx.character.dazed_3", ], threshold: 0.85, + subtitle: "subtitle-dazed", ), PoiseChange(KnockedDown): ( files: [ @@ -1236,85 +1381,90 @@ "voxygen.audio.sfx.character.knockeddown_2", ], threshold: 1.25, + subtitle: "subtitle-knocked_down", ), GroundSlam: ( files: [ - "voxygen.audio.sfx.abilities.minotaur_smash_1", - "voxygen.audio.sfx.abilities.minotaur_smash_2", - ], + "voxygen.audio.sfx.abilities.minotaur_smash_1", + "voxygen.audio.sfx.abilities.minotaur_smash_2", + ], threshold: 0.2, + subtitle: "subtitle-attack-ground_slam", ), LaserBeam: ( files: [ "voxygen.audio.sfx.abilities.laser_beam", ], threshold: 1.25, + subtitle: "subtitle-attack-laser_beam", ), CyclopsCharge: ( files: [ "voxygen.audio.sfx.abilities.cyclops_charge", ], threshold: 0.3, + subtitle: "subtitle-attack-cyclops_charge", ), GigaRoar: ( files: [ "voxygen.audio.sfx.abilities.gigas_frost_roar", ], threshold: 1.3, + subtitle: "subtitle-giga_roar", ), FlashFreeze: ( files: [ "voxygen.audio.sfx.abilities.minotaur_smash_2", ], threshold: 0.2, + subtitle: "subtitle-attack-flash_freeze", ), IceSpikes: ( files: [ "voxygen.audio.sfx.abilities.minotaur_smash_2", ], threshold: 0.2, + subtitle: "subtitle-attack-icy_spikes", ), IceCrack: ( files: [ - "voxygen.audio.sfx.abilities.ice_crack", - ], + "voxygen.audio.sfx.abilities.ice_crack", + ], threshold: 0.9, + subtitle: "subtitle-attack-ice_crack", ), + + // Utterances (NPCs) + Utterance(Angry, Alligator): ( files: [ "voxygen.audio.sfx.utterance.alligator_angry1", "voxygen.audio.sfx.utterance.alligator_angry2", ], threshold: 1.0, - ), - Utterance(Angry, SeaCrocodile): ( - files: [ - "voxygen.audio.sfx.utterance.sea_crocodile_angry1", - "voxygen.audio.sfx.utterance.sea_crocodile_angry2", - ], - threshold: 1.0, + subtitle: "subtitle-utterance-alligator-angry", ), Utterance(Angry, Antelope): ( files: [ "voxygen.audio.sfx.utterance.antelope_angry1", ], threshold: 1.0, + subtitle: "subtitle-utterance-antelope-angry", ), - - // Utterances (NPCs) - Utterance(Angry, BipedLarge): ( files: [ "voxygen.audio.sfx.utterance.ogre_angry1", "voxygen.audio.sfx.utterance.ogre_angry2", ], threshold: 1.0, + subtitle: "subtitle-utterance-biped_large-angry", ), Utterance(Angry, Bird): ( files: [ "voxygen.audio.sfx.utterance.bird_angry1", ], threshold: 1.0, + subtitle: "subtitle-utterance-bird-angry", ), Utterance(Angry, Adlet): ( files: [ @@ -1322,6 +1472,7 @@ "voxygen.audio.sfx.utterance.adlet_angry2", ], threshold: 1.0, + subtitle: "subtitle-utterance-adlet-angry", ), Utterance(Angry, Pig): ( files: [ @@ -1329,6 +1480,7 @@ "voxygen.audio.sfx.utterance.pig_angry2", ], threshold: 1.0, + subtitle: "subtitle-utterance-pig-angry", ), Utterance(Angry, Reptile): ( files: [ @@ -1336,6 +1488,7 @@ "voxygen.audio.sfx.utterance.alligator_angry2", ], threshold: 1.0, + subtitle: "subtitle-utterance-reptile-angry", ), Utterance(Angry, SeaCrocodile): ( files: [ @@ -1343,12 +1496,14 @@ "voxygen.audio.sfx.utterance.sea_crocodile_angry2", ], threshold: 1.0, + subtitle: "subtitle-utterance-sea_crocodile-angry", ), Utterance(Angry, Saurok): ( files: [ "voxygen.audio.sfx.utterance.saurok_angry1", ], threshold: 1.0, + subtitle: "subtitle-utterance-saurok-angry", ), Utterance(Calm, Cat): ( files: [ @@ -1356,6 +1511,7 @@ "voxygen.audio.sfx.utterance.cat_calm2", ], threshold: 1.0, + subtitle: "subtitle-utterance-cat-calm", ), Utterance(Calm, Cow): ( files: [ @@ -1364,18 +1520,21 @@ "voxygen.audio.sfx.utterance.cow_calm3", ], threshold: 1.0, + subtitle: "subtitle-utterance-cow-calm", ), Utterance(Calm, Fungome): ( files: [ "voxygen.audio.sfx.utterance.fungome_calm1", ], threshold: 1.0, + subtitle: "subtitle-utterance-fungome-calm", ), Utterance(Calm, Goat): ( files: [ "voxygen.audio.sfx.utterance.goat_calm1", ], threshold: 1.0, + subtitle: "subtitle-utterance-goat-calm", ), Utterance(Calm, Pig): ( files: [ @@ -1383,12 +1542,14 @@ "voxygen.audio.sfx.utterance.pig_calm2", ], threshold: 1.0, + subtitle: "subtitle-utterance-pig-calm", ), Utterance(Calm, Sheep): ( files: [ "voxygen.audio.sfx.utterance.sheep_calm1", ], threshold: 1.0, + subtitle: "subtitle-utterance-sheep-calm", ), Utterance(Calm, Truffler): ( files: [ @@ -1396,18 +1557,21 @@ "voxygen.audio.sfx.utterance.truffler_calm2", ], threshold: 1.0, + subtitle: "subtitle-utterance-truffler-calm", ), Utterance(Greeting, HumanMale): ( files: [ "voxygen.audio.sfx.utterance.humanmale_greeting1", ], threshold: 1.0, + subtitle: "subtitle-utterance-human-greeting", ), Utterance(Greeting, HumanFemale): ( files: [ "voxygen.audio.sfx.utterance.humanfemale_greeting1", ], threshold: 1.0, + subtitle: "subtitle-utterance-human-greeting", ), Utterance(Hurt, Adlet): ( files: [ @@ -1415,54 +1579,63 @@ "voxygen.audio.sfx.utterance.adlet_hurt2", ], threshold: 1.0, + subtitle: "subtitle-utterance-adlet-hurt", ), Utterance(Hurt, Antelope): ( files: [ "voxygen.audio.sfx.utterance.antelope_hurt1", ], threshold: 1.0, + subtitle: "subtitle-utterance-antelope-hurt", ), Utterance(Hurt, BipedLarge): ( files: [ "voxygen.audio.sfx.utterance.ogre_hurt1", ], threshold: 1.0, + subtitle: "subtitle-utterance-biped_large-hurt", ), Utterance(Hurt, HumanMale): ( files: [ "voxygen.audio.sfx.utterance.humanmale_hurt1", ], threshold: 1.0, + subtitle: "subtitle-utterance-human-hurt", ), Utterance(Hurt, Lion): ( files: [ "voxygen.audio.sfx.utterance.lion_hurt1", ], threshold: 1.0, + subtitle: "subtitle-utterance-lion-hurt", ), Utterance(Hurt, Mandragora): ( files: [ "voxygen.audio.sfx.utterance.mandragora_hurt1", ], threshold: 1.0, + subtitle: "subtitle-utterance-mandroga-hurt", ), Utterance(Hurt, Maneater): ( files: [ "voxygen.audio.sfx.utterance.maneater_hurt1", ], threshold: 1.0, + subtitle: "subtitle-utterance-maneater-hurt", ), Utterance(Hurt, Marlin): ( files: [ "voxygen.audio.sfx.utterance.marlin_hurt1", ], threshold: 1.0, + subtitle: "subtitle-utterance-marlin-hurt", ), Utterance(Hurt, Mindflayer): ( files: [ "voxygen.audio.sfx.utterance.mindflayer_hurt1", ], threshold: 1.0, + subtitle: "subtitle-utterance-mindflayer-hurt", ), Utterance(Hurt, Dagon): ( files: [ @@ -1470,6 +1643,7 @@ "voxygen.audio.sfx.utterance.dagon_hurt2", ], threshold: 1.0, + subtitle: "subtitle-utterance-dagon-hurt", ), Utterance(Angry, Asp): ( files: [ @@ -1477,18 +1651,21 @@ "voxygen.audio.sfx.utterance.asp_angry2", ], threshold: 1.0, + subtitle: "subtitle-utterance-asp-angry", ), Utterance(Calm, Asp): ( files: [ "voxygen.audio.sfx.utterance.asp_calm1", ], threshold: 1.0, + subtitle: "subtitle-utterance-asp-calm", ), Utterance(Hurt, Asp): ( files: [ "voxygen.audio.sfx.utterance.asp_hurt1", ], threshold: 1.0, + subtitle: "subtitle-utterance-asp-hurt", ), Utterance(Angry, Wendigo): ( files: [ @@ -1500,6 +1677,7 @@ "voxygen.audio.sfx.utterance.wendigo_angry6", ], threshold: 1.0, + subtitle: "subtitle-utterance-wendigo-angry", ), Utterance(Calm, Wendigo): ( files: [ @@ -1510,6 +1688,7 @@ "voxygen.audio.sfx.utterance.wendigo_calm5", ], threshold: 1.0, + subtitle: "subtitle-utterance-wendigo-calm", ), Utterance(Angry, Wolf): ( files: [ @@ -1521,6 +1700,7 @@ "voxygen.audio.sfx.utterance.wolf_angry6", ], threshold: 1.0, + subtitle: "subtitle-utterance-wolf-angry", ), Utterance(Hurt, Wolf): ( files: [ @@ -1531,12 +1711,14 @@ "voxygen.audio.sfx.utterance.wolf_hurt5", ], threshold: 1.0, + subtitle: "subtitle-utterance-wolf-hurt", ), Lightning: ( files: [ "voxygen.audio.sfx.ambient.lightning_1", ], threshold: 1.0, + subtitle: "subtitle-lightning", ), } ) diff --git a/assets/voxygen/i18n/en/common.ftl b/assets/voxygen/i18n/en/common.ftl index 6f599dca3e..23b0a68b95 100644 --- a/assets/voxygen/i18n/en/common.ftl +++ b/assets/voxygen/i18n/en/common.ftl @@ -12,6 +12,7 @@ common-video = Graphics common-sound = Sound common-chat = Chat common-networking = Networking +common-accessibility = Accessibility common-resume = Resume common-characters = Characters common-close = Close @@ -42,6 +43,7 @@ common-sound_settings = Sound Settings common-language_settings = Language Settings common-chat_settings = Chat Settings common-networking_settings = Networking Settings +common-accessibility_settings = Accessibility Settings common-connection_lost = Connection lost! Did the server restart? diff --git a/assets/voxygen/i18n/en/hud/settings.ftl b/assets/voxygen/i18n/en/hud/settings.ftl index 0575de807b..35c7b78511 100644 --- a/assets/voxygen/i18n/en/hud/settings.ftl +++ b/assets/voxygen/i18n/en/hud/settings.ftl @@ -151,3 +151,4 @@ hud-settings-group_only = Group only hud-settings-reset_chat = Reset to Defaults hud-settings-third_party_integrations = Third-party Integrations hud-settings-enable_discord_integration = Enable Discord Integration +hud-settings-subtitles = Subtitles diff --git a/assets/voxygen/i18n/en/hud/subtitles.ftl b/assets/voxygen/i18n/en/hud/subtitles.ftl new file mode 100644 index 0000000000..469593b339 --- /dev/null +++ b/assets/voxygen/i18n/en/hud/subtitles.ftl @@ -0,0 +1,148 @@ +subtitle-campfire = Campfire crackling +subtitle-bird_call = Birds singing +subtitle-bees = Bees buzzing +subtitle-owl = Owl hooting +subtitle-running_water = Water bubbling +subtitle-lightning = Thunder + +subtitle-footsteps_grass = Walking on grass +subtitle-footsteps_earth = Walking on dirt +subtitle-footsteps_rock = Walking on rock +subtitle-footsteps_snow = Walking on snow +subtitle-pickup_item = Item picked up +subtitle-pickup_failed = Pickup failed + +subtitle-glider_open = Glider equipped +subtitle-glider_close = Glider unequipped +subtitle-glide = Gliding +subtitle-roll = Rolling +subtitle-swim = Swimming +subtitle-climb = Climbing +subtitle-damage = Damage +subtitle-death = Death + +subtitle-wield_bow = Bow equipped +subtitle-unwield_bow = Bow unequipped +subtitle-pickup_bow = Bow picked up + +subtitle-wield_sword = Sword equipped +subtitle-unwield_sword = Sword unequipped +subtitle-sword_attack = Sword swung +subtitle-pickup_sword = Sword picked up + +subtitle-wield_axe = Axe equipped +subtitle-unwield_axe = Axe unequipped +subtitle-axe_attack = Axe swung +subtitle-pickup_axe = Axe picked up + +subtitle-wield_hammer = Hammer equipped +subtitle-unwield_hammer = Hammer unequipped +subtitle-hammer_attack = Hammer swung +subtitle-pickup_hammer = Hammer picked up + +subtitle-wield_staff = Staff equipped +subtitle-unwield_staff = Staff unequipped +subtitle-fire_shot = Staff fired +subtitle-staff_attack = Staff fired +subtitle-pickup_staff = Staff picked up + +subtitle-wield_sceptre = Sceptre equipped +subtitle-unwield_sceptre = Sceptre unequipped +subtitle-sceptre_heal = Sceptre heal aura +subtitle-pickup_sceptre = Sceptre picked up + +subtitle-wield_dagger = Dagger equipped +subtitle-uwield_dagger = Dagger unequipped +subtitle-dagger_attack = Dagger swung +subtitle-pickup_dagger = Dagger picked up + +subtitle-wield_shield = Shield equipped +subtitle-unwield_shield = Shield unequipped +subtitle-shield_attack = Shield pushed +subtitle-pickup_shield = Shield picked up + +subtitle-pickup_pick = Pickaxe picked up +subtitle-pickup_gemstone = Gemstone picked up + +subtitle-instrument_organ = Organ playing + +subtitle-wield_instrument = Instrument equipped +subtitle-unwield_instrument = Instrument unequipped +subtitle-instrument_double_bass = Double Bass playing +subtitle-instrument_flute = Flute playing +subtitle-instrument_glass_flute = Glass Flute playing +subtitle-instrument_lyre = Lyre playing +subtitle-instrument_icy_talharpa = Icy Talharpa playing +subtitle-instrument_kalimba = Kalimba playing +subtitle-instrument_melodica = Melodica playing +subtitle-instrument_lute = Lute playing +subtitle-instrument_sitar = Sitar playing +subtitle-instrument_guitar = Guitar playing +subtitle-instrument_dark_guitar = Dark Guitar playing +subtitle-instrument_washboard = Washboard playing +subtitle-pickup_instrument = Pickup instrument + +subtitle-explosion = Explosion + +subtitle-arrow_shot = Arrow released +subtitle-arrow_miss = Arrow miss +subtitle-arrow_hit = Arrow hit +subtitle-skill_point = Skill Point gained +subtitle-sceptre_beam = Sceptre beam +subtitle-flame_thrower = Flame thrower +subtitle-break_block = Block destroyed +subtitle-attack_blocked = Attack blocked +subtitle-parry = Parried +subtitle-interrupted = Interrupted +subtitle-stunned = Stunned +subtitle-dazed = Dazed +subtitle-knocked_down = Knocked down + +subtitle-attack-ground_slam = Ground slam +subtitle-attack-laser_beam = Laser beam +subtitle-attack-cyclops_charge = Cyclops charge +subtitle-giga_roar = Frost gigas roar +subtitle-attack-flash_freeze = Flash freeze +subtitle-attack-icy_spikes = Icy spikes +subtitle-attack-ice_crack = Ice crack + +subtitle-consume_potion = Drinking potion +subtitle-consume_apple = Eating apple +subtitle-consume_cheese = Eating cheese +subtitle-consume_food = Eating +subtitle-consume_liquid = Drinking + +subtitle-utterance-alligator-angry = Alligator hissing +subtitle-utterance-antelope-angry = Antelope snorting +subtitle-utterance-biped_large-angry = Heavy grunting +subtitle-utterance-bird-angry = Bird screeching +subtitle-utterance-adlet-angry = Adlet barking +subtitle-utterance-pig-angry = Pig grunting +subtitle-utterance-reptile-angry = Reptile hissing +subtitle-utterance-sea_crocodile-angry = Sea Crocodile hissing +subtitle-utterance-saurok-angry = Saurok hissing +subtitle-utterance-cat-calm = Cat meowing +subtitle-utterance-cow-calm = Cow mooing +subtitle-utterance-fungome-calm = Fungome squeaking +subtitle-utterance-goat-calm = Goat bleating +subtitle-utterance-pig-calm = Pig oinking +subtitle-utterance-sheep-calm = Sheep bleating +subtitle-utterance-truffler-calm = Truffler oinking +subtitle-utterance-human-greeting = Greeting +subtitle-utterance-adlet-hurt = Adlet whining +subtitle-utterance-antelope-hurt = Antelope crying +subtitle-utterance-biped_large-hurt = Heavy hurting +subtitle-utterance-human-hurt = Human hurting +subtitle-utterance-lion-hurt = Lion growling +subtitle-utterance-mandroga-hurt = Mandroga screaming +subtitle-utterance-maneater-hurt = Maneater burping +subtitle-utterance-marlin-hurt = Marlin hurting +subtitle-utterance-mindflayer-hurt = Mindflayer hurting +subtitle-utterance-dagon-hurt = Dagon hurting +subtitle-utterance-asp-angry = Asp hissing +subtitle-utterance-asp-calm = Asp croaking +subtitle-utterance-asp-hurt = Asp hurting +subtitle-utterance-wendigo-angry = Wendigo screaming +subtitle-utterance-wendigo-calm = Wendigo mumbling +subtitle-utterance-wolf-angry = Wolf growling +subtitle-utterance-wolf-hurt = Wolf whining \ No newline at end of file diff --git a/assets/voxygen/i18n/sv_SE/hud/subtitles.ftl b/assets/voxygen/i18n/sv_SE/hud/subtitles.ftl new file mode 100644 index 0000000000..1686a4d722 --- /dev/null +++ b/assets/voxygen/i18n/sv_SE/hud/subtitles.ftl @@ -0,0 +1,148 @@ +subtitle-campfire = Lägereld knastrar +subtitle-bird_call = Fåglar sjunger +subtitle-bees = Bin surrar +subtitle-owl = Uggla tutar +subtitle-running_water = Vatten bubblar +subtitle-lightning = Åska + +subtitle-footsteps_grass = Steg på gräs +subtitle-footsteps_earth = Steg på jord +subtitle-footsteps_rock = Steg på sten +subtitle-footsteps_snow = Steg på snö +subtitle-pickup_item = Föremål upplockat +subtitle-pickup_failed = Misslyckad upplockning + +subtitle-glider_open = Glidare framtagen +subtitle-glider_close = Glidare bortlagd +subtitle-glide = Glidning +subtitle-roll = Rullning +subtitle-swim = Simmning +subtitle-climb = Klättring +subtitle-damage = Skada +subtitle-death = Död + +subtitle-wield_bow = Båge framtagen +subtitle-unwield_bow = Båge bortlagd +subtitle-pickup_bow = Båge upplockad + +subtitle-wield_sword = Svärd framtagen +subtitle-unwield_sword = Svärd bortlagd +subtitle-sword_attack = Svärd svigning +subtitle-pickup_sword = Svärd upplockad + +subtitle-wield_axe = Yxa framtagen +subtitle-unwield_axe = Yxa bortlagd +subtitle-axe_attack = Yx svigning +subtitle-pickup_axe = Yxa upplockad + +subtitle-wield_hammer = Hammare framtagen +subtitle-unwield_hammer = Hammare bortlagd +subtitle-hammer_attack = Hammar svigning +subtitle-pickup_hammer = Hammare upplockad + +subtitle-wield_staff = Stav framtagen +subtitle-unwield_staff = Stav bortlagd +subtitle-fire_shot = Stav avfyrad +subtitle-staff_attack = Stav avfyrad +subtitle-pickup_staff = Stav upplockad + +subtitle-wield_sceptre = Spira framtagen +subtitle-unwield_sceptre = Spira bortlagd +subtitle-sceptre_heal = Spira healande aura +subtitle-pickup_sceptre = Spira upplockad + +subtitle-wield_dagger = Dolk framtagen +subtitle-uwield_dagger = Dolk bortlagd +subtitle-dagger_attack = Dolk svigning +subtitle-pickup_dagger = Dolk upplockad + +subtitle-wield_shield = Shöld framtagen +subtitle-unwield_shield = Shöld bortlagd +subtitle-shield_attack = Shöld knuff +subtitle-pickup_shield = Shöld upplockad + +subtitle-pickup_pick = Hacka upplockad +subtitle-pickup_gemstone = Ädelsten upplockad + +subtitle-instrument_organ = Orgel spelning + +subtitle-wield_instrument = Instrument framtagen +subtitle-unwield_instrument = Instrument bortlagd +subtitle-instrument_double_bass = Kontrabas spelning +subtitle-instrument_flute = Flöjt spelning +subtitle-instrument_glass_flute = Glas Flöjt spelning +subtitle-instrument_lyre = Lyra spelning +subtitle-instrument_icy_talharpa = Isig Talharpa spelning +subtitle-instrument_kalimba = Kalimba spelning +subtitle-instrument_melodica = Melodica spelning +subtitle-instrument_lute = Luta spelning +subtitle-instrument_sitar = Sitar spelning +subtitle-instrument_guitar = Gitarr spelning +subtitle-instrument_dark_guitar = Mörk Gitarr spelning +subtitle-instrument_washboard = Tvättbräda spelning +subtitle-pickup_instrument = Instrument upplockad + +subtitle-explosion = Explosion + +subtitle-arrow_shot = Pil skjuten +subtitle-arrow_miss = Pil miss +subtitle-arrow_hit = Pil träff +subtitle-skill_point = Nytt färdighetspoäng +subtitle-sceptre_beam = Spirstråle +subtitle-flame_thrower = Flamkastare +subtitle-break_block = Block förstört +subtitle-attack_blocked = Attack blockerad +subtitle-parry = Parerad +subtitle-interrupted = Avbruten +subtitle-stunned = Överväldigad +subtitle-dazed = Förvirrad +subtitle-knocked_down = Nedslagen + +subtitle-attack-ground_slam = Marksmäll +subtitle-attack-laser_beam = Laserstråle +subtitle-attack-cyclops_charge = Cyclopsanfallning +subtitle-giga_roar = Frost gigas vrål +subtitle-attack-flash_freeze = Blixtfrysning +subtitle-attack-icy_spikes = Istappar +subtitle-attack-ice_crack = Isspricka + +subtitle-consume_potion = Dricker förtrollningsdryck +subtitle-consume_apple = Äter äpple +subtitle-consume_cheese = Äter ost +subtitle-consume_food = Äter +subtitle-consume_liquid = Dricker + +subtitle-utterance-alligator-angry = Alligator väser +subtitle-utterance-antelope-angry = Antilop fnyser +subtitle-utterance-biped_large-angry = Tung grymtning +subtitle-utterance-bird-angry = Fågel skriker gällt +subtitle-utterance-adlet-angry = Adlet skäller +subtitle-utterance-pig-angry = Gris grymtar +subtitle-utterance-reptile-angry = Reptil väser +subtitle-utterance-sea_crocodile-angry = Saltvattens krokodil väser +subtitle-utterance-saurok-angry = Saurok väser +subtitle-utterance-cat-calm = Katt jamar +subtitle-utterance-cow-calm = Ko råmar +subtitle-utterance-fungome-calm = Fungome piper +subtitle-utterance-goat-calm = Get bräker +subtitle-utterance-pig-calm = Gris nöffar +subtitle-utterance-sheep-calm = Sheep bräker +subtitle-utterance-truffler-calm = Truffler nöffar +subtitle-utterance-human-greeting = Hälsning +subtitle-utterance-adlet-hurt = Adlet gnäller +subtitle-utterance-antelope-hurt = Antilop skriker +subtitle-utterance-biped_large-hurt = Tung gnällning +subtitle-utterance-human-hurt = Människa har ont +subtitle-utterance-lion-hurt = Lejon morrar +subtitle-utterance-mandroga-hurt = Mandroga skriker +subtitle-utterance-maneater-hurt = Maneater rapar +subtitle-utterance-marlin-hurt = Marlin har ont +subtitle-utterance-mindflayer-hurt = Mindflayer har ont +subtitle-utterance-dagon-hurt = Dagon har ont +subtitle-utterance-asp-angry = Asp väser +subtitle-utterance-asp-calm = Asp kväker +subtitle-utterance-asp-hurt = Asp har ont +subtitle-utterance-wendigo-angry = Wendigo skriker +subtitle-utterance-wendigo-calm = Wendigo mumlar +subtitle-utterance-wolf-angry = Wolf morrar +subtitle-utterance-wolf-hurt = Wolf gnäller \ No newline at end of file diff --git a/voxygen/src/audio/mod.rs b/voxygen/src/audio/mod.rs index 08e44dcca1..533ad6a47b 100644 --- a/voxygen/src/audio/mod.rs +++ b/voxygen/src/audio/mod.rs @@ -14,17 +14,19 @@ use fader::Fader; use music::MusicTransitionManifest; use sfx::{SfxEvent, SfxTriggerItem}; use soundcache::load_ogg; -use std::time::Duration; +use std::{collections::VecDeque, time::Duration}; use tracing::{debug, error}; use common::assets::{AssetExt, AssetHandle}; use rodio::{source::Source, OutputStream, OutputStreamHandle, StreamError}; use vek::*; +use crate::hud::Subtitle; + #[derive(Default, Clone)] pub struct Listener { - pos: Vec3, - ori: Vec3, + pub pos: Vec3, + pub ori: Vec3, ear_left_rpos: Vec3, ear_right_rpos: Vec3, @@ -53,12 +55,20 @@ pub struct AudioFrontend { music_spacing: f32, listener: Listener, + pub subtitles_enabled: bool, + pub subtitles: VecDeque, + mtm: AssetHandle, } impl AudioFrontend { /// Construct with given device - pub fn new(/* dev: String, */ num_sfx_channels: usize, num_ui_channels: usize) -> Self { + pub fn new( + /* dev: String, */ + num_sfx_channels: usize, + num_ui_channels: usize, + subtitles: bool, + ) -> Self { // Commented out until audio device switcher works //let audio_device = get_device_raw(&dev); @@ -106,6 +116,8 @@ impl AudioFrontend { music_spacing: 1.0, listener: Listener::default(), mtm: AssetExt::load_expect("voxygen.audio.music_transition_manifest"), + subtitles: VecDeque::new(), + subtitles_enabled: subtitles, } } @@ -129,6 +141,8 @@ impl AudioFrontend { music_spacing: 1.0, listener: Listener::default(), mtm: AssetExt::load_expect("voxygen.audio.music_transition_manifest"), + subtitles: VecDeque::new(), + subtitles_enabled: false, } } @@ -223,9 +237,9 @@ impl AudioFrontend { /// Errors if no sounds are found fn get_sfx_file<'a>( trigger_item: Option<(&'a SfxEvent, &'a SfxTriggerItem)>, - ) -> Option<&'a str> { + ) -> Option<(&'a str, f32, Option<&'a str>)> { trigger_item.map(|(event, item)| { - match item.files.len() { + let file = match item.files.len() { 0 => { debug!("Sfx event {:?} is missing audio file.", event); "voxygen.audio.sfx.placeholder" @@ -239,7 +253,12 @@ impl AudioFrontend { let rand_step = rand::random::() % item.files.len(); &item.files[rand_step] }, - } + }; + + // NOTE: Threshold here is meant to give subtitles some idea of the duration of + // the audio, it doesn't have to be perfect but in the future, if possible we + // might want to switch it out for the actual duration. + (file, item.threshold, item.subtitle.as_deref()) }) } @@ -252,11 +271,11 @@ impl AudioFrontend { volume: Option, underwater: bool, ) { - if let Some(sfx_file) = Self::get_sfx_file(trigger_item) { + if let Some((sfx_file, dur, subtitle)) = Self::get_sfx_file(trigger_item) { + self.emit_subtitle(subtitle, Some(position), dur); // Play sound in empty channel at given position - if self.audio_stream.is_some() { + if self.audio_stream.is_some() && self.sfx_enabled() { let sound = load_ogg(sfx_file).amplify(volume.unwrap_or(1.0)); - let listener = self.listener.clone(); if let Some(channel) = self.get_sfx_channel() { channel.set_pos(position); @@ -286,11 +305,11 @@ impl AudioFrontend { freq: Option, underwater: bool, ) { - if let Some(sfx_file) = Self::get_sfx_file(trigger_item) { + if let Some((sfx_file, dur, subtitle)) = Self::get_sfx_file(trigger_item) { + self.emit_subtitle(subtitle, Some(position), dur); // Play sound in empty channel at given position - if self.audio_stream.is_some() { + if self.audio_stream.is_some() && self.sfx_enabled() { let sound = load_ogg(sfx_file).amplify(volume.unwrap_or(1.0)); - let listener = self.listener.clone(); if let Some(channel) = self.get_sfx_channel() { channel.set_pos(position); @@ -321,11 +340,11 @@ impl AudioFrontend { trigger_item: Option<(&SfxEvent, &SfxTriggerItem)>, volume: Option, ) { - if let Some(sfx_file) = Self::get_sfx_file(trigger_item) { + if let Some((sfx_file, dur, subtitle)) = Self::get_sfx_file(trigger_item) { + self.emit_subtitle(subtitle, None, dur); // Play sound in empty channel - if self.audio_stream.is_some() { + if self.audio_stream.is_some() && self.sfx_enabled() { let sound = load_ogg(sfx_file).amplify(volume.unwrap_or(1.0)); - if let Some(channel) = self.get_ui_channel() { channel.play(sound); } @@ -335,6 +354,26 @@ impl AudioFrontend { } } + pub fn emit_subtitle( + &mut self, + subtitle: Option<&str>, + position: Option>, + duration: f32, + ) { + if self.subtitles_enabled { + if let Some(subtitle) = subtitle { + self.subtitles.push_back(Subtitle { + localization: subtitle.to_string(), + position, + show_for: duration as f64, + }); + if self.subtitles.len() > 10 { + self.subtitles.pop_front(); + } + } + } + } + /// Plays a file at a given volume in the channel with a given tag fn play_ambient(&mut self, channel_tag: AmbientChannelTag, sound: &str, volume: Option) { if self.audio_stream.is_some() { @@ -446,6 +485,8 @@ impl AudioFrontend { } } + pub fn get_listener(&self) -> &Listener { &self.listener } + /// Switches the playing music to the title music, which is pinned to a /// specific sound file (veloren_title_tune.ogg) pub fn play_title_music(&mut self) { @@ -504,6 +545,8 @@ impl AudioFrontend { pub fn set_music_spacing(&mut self, multiplier: f32) { self.music_spacing = multiplier } + pub fn set_subtitles(&mut self, enabled: bool) { self.subtitles_enabled = enabled } + /// Updates master volume in all channels pub fn set_master_volume(&mut self, master_volume: f32) { self.master_volume = master_volume; diff --git a/voxygen/src/audio/sfx/event_mapper/movement/tests.rs b/voxygen/src/audio/sfx/event_mapper/movement/tests.rs index 2fa848222e..2487b2c760 100644 --- a/voxygen/src/audio/sfx/event_mapper/movement/tests.rs +++ b/voxygen/src/audio/sfx/event_mapper/movement/tests.rs @@ -23,6 +23,7 @@ fn config_but_played_since_threshold_no_emit() { let trigger_item = SfxTriggerItem { files: vec![String::from("some.path.to.sfx.file")], threshold: 1.0, + subtitle: None, }; // Triggered a 'Run' 0 seconds ago @@ -47,6 +48,7 @@ fn config_and_not_played_since_threshold_emits() { let trigger_item = SfxTriggerItem { files: vec![String::from("some.path.to.sfx.file")], threshold: 0.5, + subtitle: None, }; let previous_state = PreviousEntityState { @@ -70,6 +72,7 @@ fn same_previous_event_elapsed_emits() { let trigger_item = SfxTriggerItem { files: vec![String::from("some.path.to.sfx.file")], threshold: 0.5, + subtitle: None, }; let previous_state = PreviousEntityState { diff --git a/voxygen/src/audio/sfx/mod.rs b/voxygen/src/audio/sfx/mod.rs index 4827624d34..03938a20fd 100644 --- a/voxygen/src/audio/sfx/mod.rs +++ b/voxygen/src/audio/sfx/mod.rs @@ -328,6 +328,9 @@ pub struct SfxTriggerItem { pub files: Vec, /// The time to wait before repeating this SfxEvent pub threshold: f32, + + #[serde(default)] + pub subtitle: Option, } #[derive(Deserialize, Default)] @@ -369,7 +372,7 @@ impl SfxMgr { ) { // Checks if the SFX volume is set to zero or audio is disabled // This prevents us from running all the following code unnecessarily - if !audio.sfx_enabled() { + if !audio.sfx_enabled() && !audio.subtitles_enabled { return; } @@ -401,7 +404,7 @@ impl SfxMgr { client: &Client, underwater: bool, ) { - if !audio.sfx_enabled() { + if !audio.sfx_enabled() && !audio.subtitles_enabled { return; } let triggers = self.triggers.read(); diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index d5560143dc..06ca363e60 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -9,8 +9,6 @@ mod diary; mod esc_menu; mod group; mod hotbar; -pub mod img_ids; -pub mod item_imgs; mod loot_scroller; mod map; mod minimap; @@ -23,7 +21,11 @@ mod settings_window; mod skillbar; mod slots; mod social; +mod subtitles; mod trade; + +pub mod img_ids; +pub mod item_imgs; pub mod util; pub use crafting::CraftingTab; @@ -31,6 +33,7 @@ pub use hotbar::{SlotContents as HotbarSlotContents, State as HotbarState}; pub use item_imgs::animate_by_pulse; pub use loot_scroller::LootMessage; pub use settings_window::ScaleChange; +pub use subtitles::Subtitle; use bag::Bag; use buffs::BuffsBar; @@ -54,6 +57,7 @@ use serde::{Deserialize, Serialize}; use settings_window::{SettingsTab, SettingsWindow}; use skillbar::Skillbar; use social::Social; +use subtitles::Subtitles; use trade::Trade; use crate::{ @@ -328,6 +332,7 @@ widget_ids! { settings_window, group_window, item_info, + subtitles, // Free look indicator free_look_txt, @@ -1451,7 +1456,7 @@ impl Hud { fn update_layout( &mut self, client: &Client, - global_state: &GlobalState, + global_state: &mut GlobalState, debug_info: &Option, dt: Duration, info: HudInfo, @@ -3343,6 +3348,18 @@ impl Hud { } } + if global_state.settings.audio.subtitles { + Subtitles::new( + client, + &global_state.settings, + &global_state.audio.get_listener().clone(), + &mut global_state.audio.subtitles, + &self.fonts, + i18n, + ) + .set(self.ids.subtitles, ui_widgets); + } + self.new_messages = VecDeque::new(); self.new_notifications = VecDeque::new(); diff --git a/voxygen/src/hud/settings_window/accessibility.rs b/voxygen/src/hud/settings_window/accessibility.rs new file mode 100644 index 0000000000..42f1f70270 --- /dev/null +++ b/voxygen/src/hud/settings_window/accessibility.rs @@ -0,0 +1,156 @@ +use crate::{ + hud::{img_ids::Imgs, TEXT_COLOR}, + render::RenderMode, + session::settings_change::{Accessibility as AccessibilityChange, Accessibility::*}, + ui::{fonts::Fonts, ToggleButton}, + GlobalState, +}; +use conrod_core::{ + color, + widget::{self, Rectangle, Text}, + widget_ids, Colorable, Positionable, Sizeable, Widget, WidgetCommon, +}; +use i18n::Localization; + +widget_ids! { + struct Ids { + window, + window_r, + flashing_lights_button, + flashing_lights_label, + flashing_lights_info_label, + subtitles_button, + subtitles_label, + } +} + +#[derive(WidgetCommon)] +pub struct Accessibility<'a> { + global_state: &'a GlobalState, + imgs: &'a Imgs, + fonts: &'a Fonts, + localized_strings: &'a Localization, + #[conrod(common_builder)] + common: widget::CommonBuilder, +} +impl<'a> Accessibility<'a> { + pub fn new( + global_state: &'a GlobalState, + imgs: &'a Imgs, + fonts: &'a Fonts, + localized_strings: &'a Localization, + ) -> Self { + Self { + global_state, + imgs, + fonts, + localized_strings, + common: widget::CommonBuilder::default(), + } + } +} + +pub struct State { + ids: Ids, +} + +impl<'a> Widget for Accessibility<'a> { + type Event = Vec; + type State = State; + type Style = (); + + fn init_state(&self, id_gen: widget::id::Generator) -> Self::State { + State { + ids: Ids::new(id_gen), + } + } + + fn style(&self) -> Self::Style {} + + fn update(self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("Accessibility::update"); + let widget::UpdateArgs { state, ui, .. } = args; + + let mut events = Vec::new(); + + Rectangle::fill_with(args.rect.dim(), color::TRANSPARENT) + .xy(args.rect.xy()) + .graphics_for(args.id) + .scroll_kids() + .scroll_kids_vertically() + .set(state.ids.window, ui); + Rectangle::fill_with([args.rect.w() / 2.0, args.rect.h()], color::TRANSPARENT) + .top_right() + .parent(state.ids.window) + .set(state.ids.window_r, ui); + + // Get render mode + let render_mode = &self.global_state.settings.graphics.render_mode; + + // Flashing lights + Text::new( + &self + .localized_strings + .get_msg("hud-settings-flashing_lights"), + ) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .top_left_with_margins_on(state.ids.window, 10.0, 10.0) + .color(TEXT_COLOR) + .set(state.ids.flashing_lights_label, ui); + + let flashing_lights_enabled = ToggleButton::new( + render_mode.flashing_lights_enabled, + self.imgs.checkbox, + self.imgs.checkbox_checked, + ) + .w_h(18.0, 18.0) + .right_from(state.ids.flashing_lights_label, 10.0) + .hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo) + .press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked) + .set(state.ids.flashing_lights_button, ui); + + Text::new( + &self + .localized_strings + .get_msg("hud-settings-flashing_lights_info"), + ) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .right_from(state.ids.flashing_lights_label, 32.0) + .color(TEXT_COLOR) + .set(state.ids.flashing_lights_info_label, ui); + + if render_mode.flashing_lights_enabled != flashing_lights_enabled { + events.push(ChangeRenderMode(Box::new(RenderMode { + flashing_lights_enabled, + ..render_mode.clone() + }))); + } + + // Subtitles + Text::new(&self.localized_strings.get_msg("hud-settings-subtitles")) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .down_from(state.ids.flashing_lights_label, 10.0) + .color(TEXT_COLOR) + .set(state.ids.subtitles_label, ui); + + let subtitles_enabled = ToggleButton::new( + self.global_state.settings.audio.subtitles, + self.imgs.checkbox, + self.imgs.checkbox_checked, + ) + .w_h(18.0, 18.0) + .right_from(state.ids.subtitles_label, 10.0) + .hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo) + .press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked) + .set(state.ids.subtitles_button, ui); + + if subtitles_enabled != self.global_state.settings.audio.subtitles { + events.push(SetSubtitles(subtitles_enabled)); + } + + events + } +} diff --git a/voxygen/src/hud/settings_window/mod.rs b/voxygen/src/hud/settings_window/mod.rs index f7705c2337..a68a04e87a 100644 --- a/voxygen/src/hud/settings_window/mod.rs +++ b/voxygen/src/hud/settings_window/mod.rs @@ -1,3 +1,4 @@ +mod accessibility; mod chat; mod controls; mod gameplay; @@ -41,6 +42,7 @@ widget_ids! { language, chat, networking, + accessibility, } } @@ -57,6 +59,7 @@ pub enum SettingsTab { Controls, Lang, Networking, + Accessibility, } impl SettingsTab { fn name_key(&self) -> &str { @@ -69,6 +72,7 @@ impl SettingsTab { SettingsTab::Sound => "common-sound", SettingsTab::Lang => "common-languages", SettingsTab::Networking => "common-networking", + SettingsTab::Accessibility => "common-accessibility", } } @@ -82,6 +86,7 @@ impl SettingsTab { SettingsTab::Sound => "common-sound_settings", SettingsTab::Lang => "common-language_settings", SettingsTab::Networking => "common-networking_settings", + SettingsTab::Accessibility => "common-accessibility_settings", } } } @@ -350,6 +355,16 @@ impl<'a> Widget for SettingsWindow<'a> { events.push(Event::SettingsChange(change.into())); } }, + SettingsTab::Accessibility => { + for change in + accessibility::Accessibility::new(global_state, imgs, fonts, localized_strings) + .top_left_with_margins_on(state.ids.settings_content_align, 0.0, 0.0) + .wh_of(state.ids.settings_content_align) + .set(state.ids.accessibility, ui) + { + events.push(Event::SettingsChange(change.into())); + } + }, } events diff --git a/voxygen/src/hud/subtitles.rs b/voxygen/src/hud/subtitles.rs new file mode 100644 index 0000000000..70eeb70cc3 --- /dev/null +++ b/voxygen/src/hud/subtitles.rs @@ -0,0 +1,332 @@ +use std::{cmp::Ordering, collections::VecDeque}; + +use crate::{audio::Listener, settings::Settings, ui::fonts::Fonts}; +use client::Client; +use conrod_core::{ + widget::{self, Id, Rectangle, Text}, + widget_ids, Colorable, Positionable, UiCell, Widget, WidgetCommon, +}; +use i18n::Localization; + +use vek::{Vec2, Vec3}; + +widget_ids! { + struct Ids { + subtitle_box_bg, + subtitle_message[], + subtitle_dir[], + } +} + +#[derive(WidgetCommon)] +pub struct Subtitles<'a> { + client: &'a Client, + settings: &'a Settings, + listener: &'a Listener, + + fonts: &'a Fonts, + + new_subtitles: &'a mut VecDeque, + + #[conrod(common_builder)] + common: widget::CommonBuilder, + + localized_strings: &'a Localization, +} + +impl<'a> Subtitles<'a> { + pub fn new( + client: &'a Client, + settings: &'a Settings, + listener: &'a Listener, + new_subtitles: &'a mut VecDeque, + fonts: &'a Fonts, + localized_strings: &'a Localization, + ) -> Self { + Self { + client, + settings, + listener, + fonts, + new_subtitles, + common: widget::CommonBuilder::default(), + localized_strings, + } + } +} + +const MIN_SUBTITLE_DURATION: f64 = 1.5; +const MAX_SUBTITLE_DIST: f32 = 80.0; + +#[derive(Debug)] +pub struct Subtitle { + pub localization: String, + /// Position the sound is played at, if any. + pub position: Option>, + /// Amount of seconds to show the subtitle for. + pub show_for: f64, +} + +#[derive(Clone, PartialEq)] +struct SubtitleData { + position: Option>, + /// `Time` to show until. + show_until: f64, +} + +impl SubtitleData { + /// Prioritize showing nearby sounds, and secondarily prioritize longer + /// living sounds. + fn compare_priority(&self, other: &Self, listener_pos: Vec3) -> Ordering { + let life_cmp = self + .show_until + .partial_cmp(&other.show_until) + .unwrap_or(Ordering::Equal); + match (self.position, other.position) { + (Some(a), Some(b)) => match a + .distance_squared(listener_pos) + .partial_cmp(&b.distance_squared(listener_pos)) + .unwrap_or(Ordering::Equal) + { + Ordering::Equal => life_cmp, + Ordering::Less => Ordering::Greater, + Ordering::Greater => Ordering::Less, + }, + (Some(_), None) => Ordering::Less, + (None, Some(_)) => Ordering::Greater, + (None, None) => life_cmp, + } + } +} + +#[derive(Clone)] +struct SubtitleList { + subtitles: Vec<(String, Vec)>, +} + +impl SubtitleList { + fn new() -> Self { + Self { + subtitles: Vec::new(), + } + } + + /// Updates the subtitle state, returns the amount of subtitles that should + /// be displayed. + fn update( + &mut self, + new_subtitles: impl Iterator, + time: f64, + listener_pos: Vec3, + ) -> usize { + for subtitle in new_subtitles { + let show_until = time + subtitle.show_for.max(MIN_SUBTITLE_DURATION); + let data = SubtitleData { + position: subtitle.position, + show_until, + }; + if let Some((_, datas)) = self + .subtitles + .iter_mut() + .find(|(key, _)| key == &subtitle.localization) + { + datas.push(data); + } else { + self.subtitles.push((subtitle.localization, vec![data])) + } + } + let mut to_display = 0; + self.subtitles.retain_mut(|(_, data)| { + data.retain(|subtitle| subtitle.show_until > time); + // Place the most prioritized subtitle in the back. + if let Some((i, s)) = data + .iter() + .enumerate() + .max_by(|(_, a), (_, b)| a.compare_priority(b, listener_pos)) + { + // We only display subtitles that are in range. + if s.position.map_or(true, |pos| { + pos.distance_squared(listener_pos) < MAX_SUBTITLE_DIST * MAX_SUBTITLE_DIST + }) { + to_display += 1; + } + let last = data.len() - 1; + data.swap(i, last); + true + } else { + // If data is empty we have no sounds with this key. + false + } + }); + to_display + } +} + +pub struct State { + subtitles: SubtitleList, + ids: Ids, +} + +impl<'a> Widget for Subtitles<'a> { + type Event = (); + type State = State; + type Style = (); + + fn init_state(&self, id_gen: widget::id::Generator) -> Self::State { + State { + subtitles: SubtitleList::new(), + ids: Ids::new(id_gen), + } + } + + fn style(&self) -> Self::Style {} + + fn update(self, args: widget::UpdateArgs) -> Self::Event { + common_base::prof_span!("Chat::update"); + + let widget::UpdateArgs { state, ui, .. } = args; + let time = self.client.state().get_time(); + let listener_pos = self.listener.pos; + let listener_forward = self.listener.ori; + + // Update subtitles and look for changes + let mut subtitles = state.subtitles.clone(); + + let has_new = !self.new_subtitles.is_empty(); + + let show_count = subtitles.update(self.new_subtitles.drain(..), time, listener_pos); + + let subtitles = if has_new || show_count != state.ids.subtitle_message.len() { + state.update(|s| { + s.subtitles = subtitles; + s.ids + .subtitle_message + .resize(show_count, &mut ui.widget_id_generator()); + s.ids + .subtitle_dir + .resize(show_count, &mut ui.widget_id_generator()); + }); + &state.subtitles + } else { + &subtitles + }; + let color = |t: &SubtitleData| -> conrod_core::Color { + conrod_core::Color::Rgba( + 0.9, + 1.0, + 1.0, + ((t.show_until - time) * 2.0).clamp(0.0, 1.0) as f32, + ) + }; + + let listener_forward = listener_forward + .xy() + .try_normalized() + .unwrap_or(Vec2::unit_y()); + let listener_right = Vec2::new(listener_forward.y, -listener_forward.x); + + let dir = |subtitle: &SubtitleData, id: &Id, dir_id: &Id, ui: &mut UiCell| { + enum Side { + /// Also used for sounds without direction. + Forward, + Right, + Left, + } + let is_right = subtitle + .position + .map(|pos| { + let dist = pos.distance(listener_pos); + let dir = (pos - listener_pos) / dist; + + let dot = dir.xy().dot(listener_forward); + if dist < 2.0 || dot > 0.85 { + Side::Forward + } else if dir.xy().dot(listener_right) >= 0.0 { + Side::Right + } else { + Side::Left + } + }) + .unwrap_or(Side::Forward); + + match is_right { + Side::Right => Text::new("> ") + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .parent(state.ids.subtitle_box_bg) + .align_right_of(state.ids.subtitle_box_bg) + .align_middle_y_of(*id) + .color(color(subtitle)) + .set(*dir_id, ui), + Side::Left => Text::new(" <") + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .parent(state.ids.subtitle_box_bg) + .align_left_of(state.ids.subtitle_box_bg) + .align_middle_y_of(*id) + .color(color(subtitle)) + .set(*dir_id, ui), + Side::Forward => Text::new("") + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .parent(state.ids.subtitle_box_bg) + .color(color(subtitle)) + .set(*dir_id, ui), + } + }; + + Rectangle::fill([200.0, 22.0 * show_count as f64]) + .rgba(0.0, 0.0, 0.0, self.settings.chat.chat_opacity) + .bottom_right_with_margins_on(ui.window, 40.0, 30.0) + .set(state.ids.subtitle_box_bg, ui); + + let mut subtitles = state + .ids + .subtitle_message + .iter() + .zip(state.ids.subtitle_dir.iter()) + .zip( + subtitles + .subtitles + .iter() + .filter_map(|(localization, data)| { + let data = data.last()?; + data.position + .map_or(true, |pos| { + pos.distance_squared(listener_pos) + < MAX_SUBTITLE_DIST * MAX_SUBTITLE_DIST + }) + .then(|| (self.localized_strings.get_msg(localization), data)) + }), + ); + + if let Some(((id, dir_id), (message, data))) = subtitles.next() { + Text::new(&message) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .parent(state.ids.subtitle_box_bg) + .center_justify() + .mid_bottom_with_margin_on(state.ids.subtitle_box_bg, 6.0) + .color(color(data)) + .set(*id, ui); + + dir(data, id, dir_id, ui); + + let mut last_id = *id; + for ((id, dir_id), (message, data)) in subtitles { + Text::new(&message) + .font_size(self.fonts.cyri.scale(14)) + .font_id(self.fonts.cyri.conrod_id) + .parent(state.ids.subtitle_box_bg) + .up_from(last_id, 8.0) + .align_middle_x_of(last_id) + .color(color(data)) + .set(*id, ui); + + dir(data, id, dir_id, ui); + + last_id = *id; + } + } + } +} diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index 2854e5b0c1..f8e0ed9622 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -139,6 +139,7 @@ fn main() { AudioOutput::Automatic => AudioFrontend::new( settings.audio.num_sfx_channels, settings.audio.num_ui_channels, + settings.audio.subtitles, ), // AudioOutput::Device(ref dev) => Some(dev.clone()), }; diff --git a/voxygen/src/session/settings_change.rs b/voxygen/src/session/settings_change.rs index 429eed96b1..ad1688ddd8 100644 --- a/voxygen/src/session/settings_change.rs +++ b/voxygen/src/session/settings_change.rs @@ -172,6 +172,12 @@ pub enum Networking { // option) } +#[derive(Clone)] +pub enum Accessibility { + ChangeRenderMode(Box), + SetSubtitles(bool), +} + #[derive(Clone)] pub enum SettingsChange { Audio(Audio), @@ -183,6 +189,7 @@ pub enum SettingsChange { Interface(Interface), Language(Language), Networking(Networking), + Accessibility(Accessibility), } macro_rules! settings_change_from { @@ -201,6 +208,7 @@ settings_change_from!(Graphics); settings_change_from!(Interface); settings_change_from!(Language); settings_change_from!(Networking); +settings_change_from!(Accessibility); impl SettingsChange { pub fn process(self, global_state: &mut GlobalState, session_state: &mut SessionState) { @@ -734,6 +742,15 @@ impl SettingsChange { } }, }, + SettingsChange::Accessibility(accessibility_change) => match accessibility_change { + Accessibility::ChangeRenderMode(new_render_mode) => { + change_render_mode(*new_render_mode, &mut global_state.window, settings); + }, + Accessibility::SetSubtitles(enabled) => { + global_state.settings.audio.subtitles = enabled; + global_state.audio.set_subtitles(enabled); + }, + }, } global_state .settings diff --git a/voxygen/src/settings/audio.rs b/voxygen/src/settings/audio.rs index 7e43300b3a..a5adb20978 100644 --- a/voxygen/src/settings/audio.rs +++ b/voxygen/src/settings/audio.rs @@ -47,6 +47,7 @@ pub struct AudioSettings { pub num_sfx_channels: usize, pub num_ui_channels: usize, pub music_spacing: f32, + pub subtitles: bool, /// Audio Device that Voxygen will use to play audio. pub output: AudioOutput, @@ -63,6 +64,7 @@ impl Default for AudioSettings { num_sfx_channels: 60, num_ui_channels: 10, music_spacing: 1.0, + subtitles: false, output: AudioOutput::Automatic, } }