mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Add combat music transitions based on number of enemies in player radius.
This commit is contained in:
parent
54024ce401
commit
5bfdd5f1af
@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- "Poise" renamed to "Stun resilience"
|
- "Poise" renamed to "Stun resilience"
|
||||||
- Stun resilience stat display
|
- Stun resilience stat display
|
||||||
- Villagers and guards now spawn with potions, and know how to use them.
|
- Villagers and guards now spawn with potions, and know how to use them.
|
||||||
|
- Combat music in dungeons when within range of enemies.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
@ -6,17 +6,17 @@
|
|||||||
|
|
||||||
(
|
(
|
||||||
tracks: [
|
tracks: [
|
||||||
(
|
Individual((
|
||||||
title: "Dank Dungeon",
|
title: "Dank Dungeon",
|
||||||
path: "voxygen.audio.soundtrack.dungeon.dank_dungeon",
|
path: "voxygen.audio.soundtrack.dungeon.dank_dungeon",
|
||||||
length: 130.0,
|
length: 130.0,
|
||||||
timing: None,
|
timing: None,
|
||||||
biomes: [],
|
biomes: [],
|
||||||
site: Some(Dungeon),
|
site: Some(Dungeon),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "Aeronic",
|
artist: "Aeronic",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Calming Hills",
|
title: "Calming Hills",
|
||||||
path: "voxygen.audio.soundtrack.overworld.calming_hills",
|
path: "voxygen.audio.soundtrack.overworld.calming_hills",
|
||||||
length: 101.0,
|
length: 101.0,
|
||||||
@ -25,10 +25,10 @@
|
|||||||
(Mountain, 1)
|
(Mountain, 1)
|
||||||
],
|
],
|
||||||
site: Some(Void),
|
site: Some(Void),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "Ultimafounding; mixed by Robotnik",
|
artist: "Ultimafounding; mixed by Robotnik",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Fiesta Del Pueblo",
|
title: "Fiesta Del Pueblo",
|
||||||
path: "voxygen.audio.soundtrack.overworld.fiesta_del_pueblo",
|
path: "voxygen.audio.soundtrack.overworld.fiesta_del_pueblo",
|
||||||
length: 183.0,
|
length: 183.0,
|
||||||
@ -37,50 +37,50 @@
|
|||||||
(Desert, 1)
|
(Desert, 1)
|
||||||
],
|
],
|
||||||
site: Some(Void),
|
site: Some(Void),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "Aeronic; mixed by Robotnik",
|
artist: "Aeronic; mixed by Robotnik",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Ruination",
|
title: "Ruination",
|
||||||
path: "voxygen.audio.soundtrack.dungeon.ruination",
|
path: "voxygen.audio.soundtrack.dungeon.ruination",
|
||||||
length: 135.0,
|
length: 135.0,
|
||||||
timing: None,
|
timing: None,
|
||||||
biomes: [],
|
biomes: [],
|
||||||
site: Some(Dungeon),
|
site: Some(Dungeon),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "Aeronic",
|
artist: "Aeronic",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Dank Hallows",
|
title: "Dank Hallows",
|
||||||
path: "voxygen.audio.soundtrack.cave.dank_hallows",
|
path: "voxygen.audio.soundtrack.cave.dank_hallows",
|
||||||
length: 227.0,
|
length: 227.0,
|
||||||
timing: None,
|
timing: None,
|
||||||
biomes: [],
|
biomes: [],
|
||||||
site: Some(Cave),
|
site: Some(Cave),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "Flashbang",
|
artist: "Flashbang",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Vast Onslaught",
|
title: "Vast Onslaught",
|
||||||
path: "voxygen.audio.soundtrack.dungeon.vast_onslaught",
|
path: "voxygen.audio.soundtrack.dungeon.vast_onslaught",
|
||||||
length: 237.0,
|
length: 237.0,
|
||||||
timing: None,
|
timing: None,
|
||||||
biomes: [],
|
biomes: [],
|
||||||
site: Some(Dungeon),
|
site: Some(Dungeon),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "Aeronic",
|
artist: "Aeronic",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Sacred Temple",
|
title: "Sacred Temple",
|
||||||
path: "voxygen.audio.soundtrack.dungeon.sacred_temple",
|
path: "voxygen.audio.soundtrack.dungeon.sacred_temple",
|
||||||
length: 75.0,
|
length: 75.0,
|
||||||
timing: None,
|
timing: None,
|
||||||
biomes: [],
|
biomes: [],
|
||||||
site: Some(Dungeon),
|
site: Some(Dungeon),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "Aeronic",
|
artist: "Aeronic",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "True Nature",
|
title: "True Nature",
|
||||||
path: "voxygen.audio.soundtrack.overworld.true_nature",
|
path: "voxygen.audio.soundtrack.overworld.true_nature",
|
||||||
length: 169.0,
|
length: 169.0,
|
||||||
@ -89,10 +89,10 @@
|
|||||||
(Forest, 1),
|
(Forest, 1),
|
||||||
],
|
],
|
||||||
site: Some(Void),
|
site: Some(Void),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "DaforLynx",
|
artist: "DaforLynx",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Jungle Ambient",
|
title: "Jungle Ambient",
|
||||||
path: "voxygen.audio.soundtrack.overworld.jungle_ambient",
|
path: "voxygen.audio.soundtrack.overworld.jungle_ambient",
|
||||||
length: 218.0,
|
length: 218.0,
|
||||||
@ -101,10 +101,10 @@
|
|||||||
(Jungle, 1),
|
(Jungle, 1),
|
||||||
],
|
],
|
||||||
site: Some(Void),
|
site: Some(Void),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "badbbad",
|
artist: "badbbad",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Ethereal Bonds",
|
title: "Ethereal Bonds",
|
||||||
path: "voxygen.audio.soundtrack.overworld.ethereal_bonds",
|
path: "voxygen.audio.soundtrack.overworld.ethereal_bonds",
|
||||||
length: 59.0,
|
length: 59.0,
|
||||||
@ -113,10 +113,10 @@
|
|||||||
(Mountain, 1),
|
(Mountain, 1),
|
||||||
],
|
],
|
||||||
site: Some(Void),
|
site: Some(Void),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "Aeronic",
|
artist: "Aeronic",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Leap of Faith",
|
title: "Leap of Faith",
|
||||||
path: "voxygen.audio.soundtrack.overworld.leap_of_faith",
|
path: "voxygen.audio.soundtrack.overworld.leap_of_faith",
|
||||||
length: 269.0,
|
length: 269.0,
|
||||||
@ -126,10 +126,10 @@
|
|||||||
(Lake, 1),
|
(Lake, 1),
|
||||||
],
|
],
|
||||||
site: Some(Void),
|
site: Some(Void),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "Aeronic",
|
artist: "Aeronic",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Highland of the Hawk",
|
title: "Highland of the Hawk",
|
||||||
path: "voxygen.audio.soundtrack.overworld.highland_of_the_hawk",
|
path: "voxygen.audio.soundtrack.overworld.highland_of_the_hawk",
|
||||||
length: 283.0,
|
length: 283.0,
|
||||||
@ -139,10 +139,10 @@
|
|||||||
(Mountain, 1),
|
(Mountain, 1),
|
||||||
],
|
],
|
||||||
site: Some(Void),
|
site: Some(Void),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "badbbad",
|
artist: "badbbad",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Verdant Glades",
|
title: "Verdant Glades",
|
||||||
path: "voxygen.audio.soundtrack.overworld.verdant_glades",
|
path: "voxygen.audio.soundtrack.overworld.verdant_glades",
|
||||||
length: 97.0,
|
length: 97.0,
|
||||||
@ -151,10 +151,10 @@
|
|||||||
(Grassland, 1),
|
(Grassland, 1),
|
||||||
],
|
],
|
||||||
site: Some(Void),
|
site: Some(Void),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "Aeronic",
|
artist: "Aeronic",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Calling Wild",
|
title: "Calling Wild",
|
||||||
path: "voxygen.audio.soundtrack.overworld.calling_wild",
|
path: "voxygen.audio.soundtrack.overworld.calling_wild",
|
||||||
length: 160.0,
|
length: 160.0,
|
||||||
@ -163,10 +163,10 @@
|
|||||||
(Grassland, 1),
|
(Grassland, 1),
|
||||||
],
|
],
|
||||||
site: Some(Void),
|
site: Some(Void),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "Ultimafounding",
|
artist: "Ultimafounding",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Drifting Along",
|
title: "Drifting Along",
|
||||||
path: "voxygen.audio.soundtrack.overworld.drifting_along",
|
path: "voxygen.audio.soundtrack.overworld.drifting_along",
|
||||||
length: 164.0,
|
length: 164.0,
|
||||||
@ -176,35 +176,35 @@
|
|||||||
(Ocean, 1),
|
(Ocean, 1),
|
||||||
],
|
],
|
||||||
site: Some(Void),
|
site: Some(Void),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "DaforLynx",
|
artist: "DaforLynx",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Winter Falls",
|
title: "Winter Falls",
|
||||||
path: "voxygen.audio.soundtrack.overworld.winter_falls",
|
path: "voxygen.audio.soundtrack.overworld.winter_falls",
|
||||||
length: 215.0,
|
length: 215.0,
|
||||||
timing: Some(Day),
|
timing: Some(Day),
|
||||||
biomes: [
|
biomes: [
|
||||||
(Snowland, 1),
|
(Snowland, 1),
|
||||||
],
|
],
|
||||||
site: Some(Void),
|
site: Some(Void),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "DaforLynx",
|
artist: "DaforLynx",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Short Meandering",
|
title: "Short Meandering",
|
||||||
path: "voxygen.audio.soundtrack.overworld.short_meandering",
|
path: "voxygen.audio.soundtrack.overworld.short_meandering",
|
||||||
length: 147.0,
|
length: 147.0,
|
||||||
timing: Some(Night),
|
timing: Some(Night),
|
||||||
biomes: [
|
biomes: [
|
||||||
(Desert, 1),
|
(Desert, 1),
|
||||||
(Mountain, 1),
|
(Mountain, 1),
|
||||||
],
|
],
|
||||||
site: Some(Void),
|
site: Some(Void),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "Ap1evideogame",
|
artist: "Ap1evideogame",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Oceania",
|
title: "Oceania",
|
||||||
path: "voxygen.audio.soundtrack.overworld.oceania",
|
path: "voxygen.audio.soundtrack.overworld.oceania",
|
||||||
length: 135.0,
|
length: 135.0,
|
||||||
@ -214,10 +214,10 @@
|
|||||||
(Ocean, 1),
|
(Ocean, 1),
|
||||||
],
|
],
|
||||||
site: Some(Void),
|
site: Some(Void),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "Eden",
|
artist: "Eden",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "A Solemn Quest",
|
title: "A Solemn Quest",
|
||||||
path: "voxygen.audio.soundtrack.overworld.a_solemn_quest",
|
path: "voxygen.audio.soundtrack.overworld.a_solemn_quest",
|
||||||
length: 206.0,
|
length: 206.0,
|
||||||
@ -227,10 +227,10 @@
|
|||||||
(Mountain, 1),
|
(Mountain, 1),
|
||||||
],
|
],
|
||||||
site: Some(Void),
|
site: Some(Void),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "Eden",
|
artist: "Eden",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Into The Dark Forest",
|
title: "Into The Dark Forest",
|
||||||
path: "voxygen.audio.soundtrack.overworld.into_the_dark_forest",
|
path: "voxygen.audio.soundtrack.overworld.into_the_dark_forest",
|
||||||
length: 184.0,
|
length: 184.0,
|
||||||
@ -240,23 +240,23 @@
|
|||||||
(Jungle, 1),
|
(Jungle, 1),
|
||||||
],
|
],
|
||||||
site: Some(Void),
|
site: Some(Void),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "Aeronic",
|
artist: "Aeronic",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Field Grazing",
|
title: "Field Grazing",
|
||||||
path: "voxygen.audio.soundtrack.overworld.field_grazing",
|
path: "voxygen.audio.soundtrack.overworld.field_grazing",
|
||||||
length: 154.0,
|
length: 154.0,
|
||||||
timing: Some(Day),
|
timing: Some(Day),
|
||||||
biomes: [
|
biomes: [
|
||||||
(Grassland, 1),
|
(Grassland, 1),
|
||||||
(Forest, 1),
|
(Forest, 1),
|
||||||
],
|
],
|
||||||
site: Some(Void),
|
site: Some(Void),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "Aeronic",
|
artist: "Aeronic",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Wandering Voices",
|
title: "Wandering Voices",
|
||||||
path: "voxygen.audio.soundtrack.overworld.wandering_voices",
|
path: "voxygen.audio.soundtrack.overworld.wandering_voices",
|
||||||
length: 137.0,
|
length: 137.0,
|
||||||
@ -265,10 +265,10 @@
|
|||||||
(Grassland, 1),
|
(Grassland, 1),
|
||||||
],
|
],
|
||||||
site: Some(Void),
|
site: Some(Void),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "Aeronic",
|
artist: "Aeronic",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Snowtop Volume",
|
title: "Snowtop Volume",
|
||||||
path: "voxygen.audio.soundtrack.overworld.snowtop_volume",
|
path: "voxygen.audio.soundtrack.overworld.snowtop_volume",
|
||||||
length: 89.0,
|
length: 89.0,
|
||||||
@ -277,20 +277,20 @@
|
|||||||
(Snowland, 1),
|
(Snowland, 1),
|
||||||
],
|
],
|
||||||
site: Some(Void),
|
site: Some(Void),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "Aeronic",
|
artist: "Aeronic",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Mineral Deposits",
|
title: "Mineral Deposits",
|
||||||
path: "voxygen.audio.soundtrack.cave.mineral_deposits",
|
path: "voxygen.audio.soundtrack.cave.mineral_deposits",
|
||||||
length: 148.0,
|
length: 148.0,
|
||||||
timing: None,
|
timing: None,
|
||||||
biomes: [],
|
biomes: [],
|
||||||
site: Some(Cave),
|
site: Some(Cave),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "Aeronic",
|
artist: "Aeronic",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Moonbeams",
|
title: "Moonbeams",
|
||||||
path: "voxygen.audio.soundtrack.overworld.moonbeams",
|
path: "voxygen.audio.soundtrack.overworld.moonbeams",
|
||||||
length: 158.0,
|
length: 158.0,
|
||||||
@ -299,23 +299,23 @@
|
|||||||
(Snowland, 1),
|
(Snowland, 1),
|
||||||
],
|
],
|
||||||
site: Some(Void),
|
site: Some(Void),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "Aeronic",
|
artist: "Aeronic",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Serene Meadows",
|
title: "Serene Meadows",
|
||||||
path: "voxygen.audio.soundtrack.overworld.serene_meadows",
|
path: "voxygen.audio.soundtrack.overworld.serene_meadows",
|
||||||
length: 173.0,
|
length: 173.0,
|
||||||
timing: Some(Night),
|
timing: Some(Night),
|
||||||
biomes: [
|
biomes: [
|
||||||
(Grassland, 1),
|
(Grassland, 1),
|
||||||
(Desert, 1),
|
(Desert, 1),
|
||||||
],
|
],
|
||||||
site: Some(Void),
|
site: Some(Void),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "Aeronic",
|
artist: "Aeronic",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Just The Beginning",
|
title: "Just The Beginning",
|
||||||
path: "voxygen.audio.soundtrack.overworld.just_the_beginning",
|
path: "voxygen.audio.soundtrack.overworld.just_the_beginning",
|
||||||
length: 188.0,
|
length: 188.0,
|
||||||
@ -324,10 +324,10 @@
|
|||||||
(Grassland, 1),
|
(Grassland, 1),
|
||||||
],
|
],
|
||||||
site: Some(Void),
|
site: Some(Void),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "badbbad",
|
artist: "badbbad",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Campfire Stories",
|
title: "Campfire Stories",
|
||||||
path: "voxygen.audio.soundtrack.overworld.campfire_stories",
|
path: "voxygen.audio.soundtrack.overworld.campfire_stories",
|
||||||
length: 100.0,
|
length: 100.0,
|
||||||
@ -336,10 +336,10 @@
|
|||||||
(Forest, 1),
|
(Forest, 1),
|
||||||
],
|
],
|
||||||
site: Some(Void),
|
site: Some(Void),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "badbbad",
|
artist: "badbbad",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Limits",
|
title: "Limits",
|
||||||
path: "voxygen.audio.soundtrack.overworld.limits",
|
path: "voxygen.audio.soundtrack.overworld.limits",
|
||||||
length: 203.0,
|
length: 203.0,
|
||||||
@ -348,20 +348,20 @@
|
|||||||
(Mountain, 1),
|
(Mountain, 1),
|
||||||
],
|
],
|
||||||
site: Some(Void),
|
site: Some(Void),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "badbbad",
|
artist: "badbbad",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Down The Rabbit Hole",
|
title: "Down The Rabbit Hole",
|
||||||
path: "voxygen.audio.soundtrack.dungeon.down_the_rabbit_hole",
|
path: "voxygen.audio.soundtrack.dungeon.down_the_rabbit_hole",
|
||||||
length: 244.0,
|
length: 244.0,
|
||||||
timing: None,
|
timing: None,
|
||||||
biomes: [],
|
biomes: [],
|
||||||
site: Some(Dungeon),
|
site: Some(Dungeon),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "badbbad",
|
artist: "badbbad",
|
||||||
),
|
)),
|
||||||
(
|
Individual((
|
||||||
title: "Between The Fairies",
|
title: "Between The Fairies",
|
||||||
path: "voxygen.audio.soundtrack.overworld.between_the_fairies",
|
path: "voxygen.audio.soundtrack.overworld.between_the_fairies",
|
||||||
length: 175.0,
|
length: 175.0,
|
||||||
@ -370,8 +370,26 @@
|
|||||||
(Forest, 1),
|
(Forest, 1),
|
||||||
],
|
],
|
||||||
site: Some(Void),
|
site: Some(Void),
|
||||||
activity: Explore,
|
activity: State(Explore),
|
||||||
artist: "badbbad",
|
artist: "badbbad",
|
||||||
)
|
)),
|
||||||
|
Segmented(
|
||||||
|
title: "Combat1",
|
||||||
|
author: "DaforLynx",
|
||||||
|
timing: None,
|
||||||
|
biomes: [],
|
||||||
|
site: Some(Dungeon),
|
||||||
|
segments: [
|
||||||
|
("voxygen.audio.soundtrack.combat1.combat1-hi-loop", 54.0, State(Combat(High))),
|
||||||
|
("voxygen.audio.soundtrack.combat1.combat1-hi-start", 55.0, Transition(Explore, Combat(High))),
|
||||||
|
("voxygen.audio.soundtrack.combat1.combat1-lo-loop", 7.0, State(Combat(Low))),
|
||||||
|
("voxygen.audio.soundtrack.combat1.combat1-lo-start", 10.0, Transition(Explore, Combat(Low))),
|
||||||
|
("voxygen.audio.soundtrack.combat1.combat1-trans-hi-lo", 10.0, Transition(Combat(High), Combat(Low))),
|
||||||
|
("voxygen.audio.soundtrack.combat1.combat1-trans-lo-hi", 7.0, Transition(Combat(Low), Combat(High))),
|
||||||
|
// temporary until more assets exist:
|
||||||
|
("voxygen.audio.soundtrack.combat1.combat1-trans-hi-lo", 10.0, Transition(Combat(High), Explore)),
|
||||||
|
("voxygen.audio.soundtrack.combat1.combat1-lo-loop", 7.0, Transition(Combat(Low), Explore)),
|
||||||
|
],
|
||||||
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
BIN
assets/voxygen/audio/soundtrack/combat1/combat1-hi-loop.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/soundtrack/combat1/combat1-hi-loop.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/soundtrack/combat1/combat1-hi-start.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/soundtrack/combat1/combat1-hi-start.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/soundtrack/combat1/combat1-lo-loop.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/soundtrack/combat1/combat1-lo-loop.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/soundtrack/combat1/combat1-lo-start.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/soundtrack/combat1/combat1-lo-start.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/soundtrack/combat1/combat1-trans-hi-lo.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/soundtrack/combat1/combat1-trans-hi-lo.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/audio/soundtrack/combat1/combat1-trans-lo-hi.ogg
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/audio/soundtrack/combat1/combat1-trans-lo-hi.ogg
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -41,6 +41,7 @@ enum ChannelState {
|
|||||||
pub enum MusicChannelTag {
|
pub enum MusicChannelTag {
|
||||||
TitleMusic,
|
TitleMusic,
|
||||||
Exploration,
|
Exploration,
|
||||||
|
Combat,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A MusicChannel uses a non-positional audio sink designed to play music which
|
/// A MusicChannel uses a non-positional audio sink designed to play music which
|
||||||
|
@ -53,17 +53,21 @@ use common_sys::state::State;
|
|||||||
use rand::{prelude::SliceRandom, thread_rng, Rng};
|
use rand::{prelude::SliceRandom, thread_rng, Rng};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use tracing::warn;
|
use tracing::{debug, trace, warn};
|
||||||
|
|
||||||
/// Collection of all the tracks
|
/// Collection of all the tracks
|
||||||
#[derive(Debug, Default, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
struct SoundtrackCollection {
|
struct SoundtrackCollection<T> {
|
||||||
/// List of tracks
|
/// List of tracks
|
||||||
tracks: Vec<SoundtrackItem>,
|
tracks: Vec<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for SoundtrackCollection<T> {
|
||||||
|
fn default() -> Self { Self { tracks: Vec::new() } }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configuration for a single music track in the soundtrack
|
/// Configuration for a single music track in the soundtrack
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
pub struct SoundtrackItem {
|
pub struct SoundtrackItem {
|
||||||
/// Song title
|
/// Song title
|
||||||
title: String,
|
title: String,
|
||||||
@ -82,14 +86,38 @@ pub struct SoundtrackItem {
|
|||||||
activity: MusicActivity,
|
activity: MusicActivity,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
enum MusicActivity {
|
enum RawSoundtrackItem {
|
||||||
|
Individual(SoundtrackItem),
|
||||||
|
Segmented {
|
||||||
|
title: String,
|
||||||
|
timing: Option<DayPeriod>,
|
||||||
|
biomes: Vec<(BiomeKind, u8)>,
|
||||||
|
site: Option<SitesKind>,
|
||||||
|
segments: Vec<(String, f32, MusicActivity)>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
|
||||||
|
enum CombatIntensity {
|
||||||
|
Low,
|
||||||
|
High,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
|
||||||
|
enum MusicActivityState {
|
||||||
Explore,
|
Explore,
|
||||||
Combat,
|
Combat(CombatIntensity),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
|
||||||
|
enum MusicActivity {
|
||||||
|
State(MusicActivityState),
|
||||||
|
Transition(MusicActivityState, MusicActivityState),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allows control over when a track should play based on in-game time of day
|
/// Allows control over when a track should play based on in-game time of day
|
||||||
#[derive(Debug, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
||||||
enum DayPeriod {
|
enum DayPeriod {
|
||||||
/// 8:00 AM to 7:30 PM
|
/// 8:00 AM to 7:30 PM
|
||||||
Day,
|
Day,
|
||||||
@ -109,7 +137,7 @@ enum PlayState {
|
|||||||
/// Provides methods to control music playback
|
/// Provides methods to control music playback
|
||||||
pub struct MusicMgr {
|
pub struct MusicMgr {
|
||||||
/// Collection of all the tracks
|
/// Collection of all the tracks
|
||||||
soundtrack: AssetHandle<SoundtrackCollection>,
|
soundtrack: AssetHandle<SoundtrackCollection<SoundtrackItem>>,
|
||||||
/// Instant at which the current track began playing
|
/// Instant at which the current track began playing
|
||||||
began_playing: Instant,
|
began_playing: Instant,
|
||||||
/// Time until the next track should be played
|
/// Time until the next track should be played
|
||||||
@ -117,6 +145,8 @@ pub struct MusicMgr {
|
|||||||
/// The title of the last track played. Used to prevent a track
|
/// The title of the last track played. Used to prevent a track
|
||||||
/// being played twice in a row
|
/// being played twice in a row
|
||||||
last_track: String,
|
last_track: String,
|
||||||
|
/// The previous track's activity kind, for transitions
|
||||||
|
last_activity: MusicActivityState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for MusicMgr {
|
impl Default for MusicMgr {
|
||||||
@ -126,6 +156,7 @@ impl Default for MusicMgr {
|
|||||||
began_playing: Instant::now(),
|
began_playing: Instant::now(),
|
||||||
next_track_change: 0.0,
|
next_track_change: 0.0,
|
||||||
last_track: String::from("None"),
|
last_track: String::from("None"),
|
||||||
|
last_activity: MusicActivityState::Explore,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -144,41 +175,97 @@ impl MusicMgr {
|
|||||||
// player_alt = position.0.z;
|
// player_alt = position.0.z;
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
use common::comp::{Health, Pos};
|
||||||
|
use specs::{Join, WorldExt};
|
||||||
|
use MusicActivityState::*;
|
||||||
|
let mut activity_state = Explore;
|
||||||
|
let player = client.entity();
|
||||||
|
let ecs = state.ecs();
|
||||||
|
let entities = ecs.entities();
|
||||||
|
let positions = ecs.read_component::<Pos>();
|
||||||
|
let healths = ecs.read_component::<Health>();
|
||||||
|
if let Some(player_pos) = positions.get(player) {
|
||||||
|
const NEARBY_RADIUS: f32 = 50.0;
|
||||||
|
// TODO: consider filtering by Alignment::Enemy (requires sync changes)
|
||||||
|
let num_nearby_entities: u32 = (&entities, &positions, &healths)
|
||||||
|
.join()
|
||||||
|
.map(|(entity, pos, _)| {
|
||||||
|
if entity != player
|
||||||
|
&& (player_pos.0 - pos.0).magnitude_squared() < NEARBY_RADIUS.powf(2.0)
|
||||||
|
{
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sum();
|
||||||
|
|
||||||
|
if num_nearby_entities > 2 {
|
||||||
|
activity_state = Combat(CombatIntensity::High);
|
||||||
|
} else if num_nearby_entities >= 1 {
|
||||||
|
activity_state = Combat(CombatIntensity::Low);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let activity = if self.last_activity != activity_state {
|
||||||
|
MusicActivity::Transition(self.last_activity, activity_state)
|
||||||
|
} else {
|
||||||
|
MusicActivity::State(activity_state)
|
||||||
|
};
|
||||||
|
|
||||||
|
let interrupt = matches!(activity, MusicActivity::Transition(_, _));
|
||||||
|
|
||||||
if audio.music_enabled()
|
if audio.music_enabled()
|
||||||
&& !self.soundtrack.read().tracks.is_empty()
|
&& !self.soundtrack.read().tracks.is_empty()
|
||||||
&& self.began_playing.elapsed().as_secs_f32() > self.next_track_change
|
&& (self.began_playing.elapsed().as_secs_f32() > self.next_track_change || interrupt)
|
||||||
{
|
{
|
||||||
self.play_random_track(audio, state, client);
|
trace!("in audio maintain: {:?} {:?}", self.last_activity, activity);
|
||||||
|
if let Ok(()) = self.play_random_track(audio, state, client, &activity) {
|
||||||
|
self.last_activity = activity_state;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn play_random_track(&mut self, audio: &mut AudioFrontend, state: &State, client: &Client) {
|
fn play_random_track(
|
||||||
|
&mut self,
|
||||||
|
audio: &mut AudioFrontend,
|
||||||
|
state: &State,
|
||||||
|
client: &Client,
|
||||||
|
activity: &MusicActivity,
|
||||||
|
) -> Result<(), ()> {
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
// Adds a bit of randomness between plays
|
// Adds a bit of randomness between plays
|
||||||
let silence_between_tracks_seconds: f32 = rng.gen_range(60.0..120.0);
|
let silence_between_tracks_seconds: f32 =
|
||||||
|
if matches!(activity, MusicActivity::State(MusicActivityState::Explore)) {
|
||||||
|
rng.gen_range(60.0..120.0)
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
|
||||||
let is_dark = (state.get_day_period().is_dark()) as bool;
|
let is_dark = (state.get_day_period().is_dark()) as bool;
|
||||||
let current_period_of_day = Self::get_current_day_period(is_dark);
|
let current_period_of_day = Self::get_current_day_period(is_dark);
|
||||||
let current_biome = client.current_biome();
|
let current_biome = client.current_biome();
|
||||||
let current_site = client.current_site();
|
let current_site = client.current_site();
|
||||||
|
|
||||||
// Filters out tracks not matching the timing, site, and biome
|
// Filter the soundtrack in stages, so that we don't overprune it if there are
|
||||||
|
// too many constraints. Returning Err(()) signals that we couldn't find
|
||||||
|
// an appropriate track for the current state, and hence the state
|
||||||
|
// machine for the activity shouldn't be updated.
|
||||||
|
let mut res = Ok(());
|
||||||
let soundtrack = self.soundtrack.read();
|
let soundtrack = self.soundtrack.read();
|
||||||
let maybe_tracks = soundtrack
|
// First, filter out tracks not matching the timing, site, and biome
|
||||||
|
let mut maybe_tracks = soundtrack
|
||||||
.tracks
|
.tracks
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|track| track.activity == MusicActivity::Explore)
|
|
||||||
.filter(|track| {
|
.filter(|track| {
|
||||||
!track.title.eq(&self.last_track)
|
(match &track.timing {
|
||||||
&& match &track.timing {
|
Some(period_of_day) => period_of_day == ¤t_period_of_day,
|
||||||
Some(period_of_day) => period_of_day == ¤t_period_of_day,
|
None => true,
|
||||||
None => true,
|
}) && match &track.site {
|
||||||
}
|
Some(site) => site == ¤t_site,
|
||||||
&& match &track.site {
|
None => true,
|
||||||
Some(site) => site == ¤t_site,
|
}
|
||||||
None => true,
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.filter(|track| {
|
.filter(|track| {
|
||||||
let mut result = false;
|
let mut result = false;
|
||||||
@ -194,6 +281,37 @@ impl MusicMgr {
|
|||||||
result
|
result
|
||||||
})
|
})
|
||||||
.collect::<Vec<&SoundtrackItem>>();
|
.collect::<Vec<&SoundtrackItem>>();
|
||||||
|
// Second, filter out any tracks that don't match the current activity.
|
||||||
|
let filtered_tracks: Vec<_> = maybe_tracks
|
||||||
|
.iter()
|
||||||
|
.filter(|track| &track.activity == activity)
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
trace!(
|
||||||
|
"maybe_len: {}, filtered len: {}",
|
||||||
|
maybe_tracks.len(),
|
||||||
|
filtered_tracks.len()
|
||||||
|
);
|
||||||
|
if !filtered_tracks.is_empty() {
|
||||||
|
maybe_tracks = filtered_tracks;
|
||||||
|
} else {
|
||||||
|
res = Err(());
|
||||||
|
}
|
||||||
|
// Third, prevent playing the last track if possible (though don't return Err
|
||||||
|
// here, since the combat music is intended to loop)
|
||||||
|
let filtered_tracks: Vec<_> = maybe_tracks
|
||||||
|
.iter()
|
||||||
|
.filter(|track| !track.title.eq(&self.last_track))
|
||||||
|
.cloned()
|
||||||
|
.collect();
|
||||||
|
trace!(
|
||||||
|
"maybe_len: {}, filtered len: {}",
|
||||||
|
maybe_tracks.len(),
|
||||||
|
filtered_tracks.len()
|
||||||
|
);
|
||||||
|
if !filtered_tracks.is_empty() {
|
||||||
|
maybe_tracks = filtered_tracks;
|
||||||
|
}
|
||||||
|
|
||||||
// Randomly selects a track from the remaining tracks weighted based
|
// Randomly selects a track from the remaining tracks weighted based
|
||||||
// on the biome
|
// on the biome
|
||||||
@ -213,6 +331,10 @@ impl MusicMgr {
|
|||||||
}
|
}
|
||||||
chance
|
chance
|
||||||
});
|
});
|
||||||
|
debug!(
|
||||||
|
"selecting new track for {:?}: {:?}",
|
||||||
|
activity, new_maybe_track
|
||||||
|
);
|
||||||
|
|
||||||
if let Ok(track) = new_maybe_track {
|
if let Ok(track) = new_maybe_track {
|
||||||
//println!("Now playing {:?}", track.title);
|
//println!("Now playing {:?}", track.title);
|
||||||
@ -220,7 +342,19 @@ impl MusicMgr {
|
|||||||
self.began_playing = Instant::now();
|
self.began_playing = Instant::now();
|
||||||
self.next_track_change = track.length + silence_between_tracks_seconds;
|
self.next_track_change = track.length + silence_between_tracks_seconds;
|
||||||
|
|
||||||
audio.play_music(&track.path, MusicChannelTag::Exploration);
|
let tag = if matches!(
|
||||||
|
activity,
|
||||||
|
MusicActivity::State(MusicActivityState::Explore)
|
||||||
|
| MusicActivity::Transition(_, MusicActivityState::Explore)
|
||||||
|
) {
|
||||||
|
MusicChannelTag::Exploration
|
||||||
|
} else {
|
||||||
|
MusicChannelTag::Combat
|
||||||
|
};
|
||||||
|
audio.play_music(&track.path, tag);
|
||||||
|
res
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,23 +366,58 @@ impl MusicMgr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_soundtrack_items() -> AssetHandle<SoundtrackCollection> {
|
fn load_soundtrack_items() -> AssetHandle<SoundtrackCollection<SoundtrackItem>> {
|
||||||
// Cannot fail: A default value is always provided
|
// Cannot fail: A default value is always provided
|
||||||
SoundtrackCollection::load_expect("voxygen.audio.soundtrack")
|
SoundtrackCollection::load_expect("voxygen.audio.soundtrack")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl assets::Asset for SoundtrackCollection<RawSoundtrackItem> {
|
||||||
impl assets::Asset for SoundtrackCollection {
|
|
||||||
type Loader = assets::RonLoader;
|
type Loader = assets::RonLoader;
|
||||||
|
|
||||||
const EXTENSION: &'static str = "ron";
|
const EXTENSION: &'static str = "ron";
|
||||||
|
}
|
||||||
|
|
||||||
fn default_value(_: &str, error: assets::Error) -> Result<Self, assets::Error> {
|
impl assets::Compound for SoundtrackCollection<SoundtrackItem> {
|
||||||
warn!(
|
fn load<S: assets::source::Source>(
|
||||||
"Error reading music config file, music will not be available: {:#?}",
|
_: &assets::AssetCache<S>,
|
||||||
error
|
id: &str,
|
||||||
);
|
) -> Result<Self, assets::Error> {
|
||||||
|
let inner = || -> Result<_, assets::Error> {
|
||||||
Ok(SoundtrackCollection::default())
|
let manifest: AssetHandle<assets::Ron<SoundtrackCollection<RawSoundtrackItem>>> =
|
||||||
|
AssetExt::load(id)?;
|
||||||
|
let mut soundtracks = SoundtrackCollection::default();
|
||||||
|
for item in manifest.read().0.tracks.iter().cloned() {
|
||||||
|
match item {
|
||||||
|
RawSoundtrackItem::Individual(track) => soundtracks.tracks.push(track),
|
||||||
|
RawSoundtrackItem::Segmented {
|
||||||
|
title,
|
||||||
|
timing,
|
||||||
|
biomes,
|
||||||
|
site,
|
||||||
|
segments,
|
||||||
|
} => {
|
||||||
|
for (path, length, activity) in segments.into_iter() {
|
||||||
|
soundtracks.tracks.push(SoundtrackItem {
|
||||||
|
title: title.clone(),
|
||||||
|
path,
|
||||||
|
length,
|
||||||
|
timing: timing.clone(),
|
||||||
|
biomes: biomes.clone(),
|
||||||
|
site,
|
||||||
|
activity,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(soundtracks)
|
||||||
|
};
|
||||||
|
match inner() {
|
||||||
|
Ok(soundtracks) => Ok(soundtracks),
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Error loading soundtracks: {:?}", e);
|
||||||
|
Ok(SoundtrackCollection::default())
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user