mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
NPCs now call for help when you hit them. Redraw speech bubble dark mode.
This commit is contained in:
parent
3c07d02218
commit
3cea76b82f
BIN
assets/voxygen/element/frames/bubble_dark/bottom.png
(Stored with Git LFS)
BIN
assets/voxygen/element/frames/bubble_dark/bottom.png
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/frames/bubble_dark/bottom_left.png
(Stored with Git LFS)
BIN
assets/voxygen/element/frames/bubble_dark/bottom_left.png
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/frames/bubble_dark/bottom_right.png
(Stored with Git LFS)
BIN
assets/voxygen/element/frames/bubble_dark/bottom_right.png
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/frames/bubble_dark/left.png
(Stored with Git LFS)
BIN
assets/voxygen/element/frames/bubble_dark/left.png
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/frames/bubble_dark/mid.png
(Stored with Git LFS)
BIN
assets/voxygen/element/frames/bubble_dark/mid.png
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/frames/bubble_dark/right.png
(Stored with Git LFS)
BIN
assets/voxygen/element/frames/bubble_dark/right.png
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/frames/bubble_dark/tail.png
(Stored with Git LFS)
BIN
assets/voxygen/element/frames/bubble_dark/tail.png
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/frames/bubble_dark/top.png
(Stored with Git LFS)
BIN
assets/voxygen/element/frames/bubble_dark/top.png
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/frames/bubble_dark/top_left.png
(Stored with Git LFS)
BIN
assets/voxygen/element/frames/bubble_dark/top_left.png
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/frames/bubble_dark/top_right.png
(Stored with Git LFS)
BIN
assets/voxygen/element/frames/bubble_dark/top_right.png
(Stored with Git LFS)
Binary file not shown.
@ -383,5 +383,8 @@ Willenskraft
|
||||
"esc_menu.logout": "Ausloggen",
|
||||
"esc_menu.quit_game": "Desktop",
|
||||
/// End Escape Menu Section
|
||||
},
|
||||
|
||||
vector_map: {
|
||||
}
|
||||
)
|
||||
)
|
||||
|
@ -376,14 +376,52 @@ Fitness
|
||||
|
||||
Willpower
|
||||
"#,
|
||||
|
||||
|
||||
/// Start character window section
|
||||
/// End character window section
|
||||
|
||||
|
||||
/// Start Escape Menu Section
|
||||
"esc_menu.logout": "Logout",
|
||||
"esc_menu.quit_game": "Quit Game",
|
||||
/// End Escape Menu Section
|
||||
},
|
||||
|
||||
vector_map: {
|
||||
"npc.speech.villager_under_attack": [
|
||||
"Help, I'm under attack!",
|
||||
"Help! I'm under attack!",
|
||||
"Ouch! I'm under attack!",
|
||||
"Ouch! I'm under attack! Help!",
|
||||
"Help me! I'm under attack!",
|
||||
"I'm under attack! Help!",
|
||||
"I'm under attack! Help me!",
|
||||
"Help!",
|
||||
"Help! Help!",
|
||||
"Help! Help! Help!",
|
||||
"I'm under attack!",
|
||||
"AAAHHH! I'm under attack!",
|
||||
"AAAHHH! I'm under attack! Help!",
|
||||
"Help! We're under attack!",
|
||||
"Help! Murderer!",
|
||||
"Help! There's a murder on the loose!",
|
||||
"Help! They're trying to kill me!",
|
||||
"Guards, I'm under attack!",
|
||||
"Guards! I'm under attack!",
|
||||
"I'm under attack! Guards!",
|
||||
"Help! Guards! I'm under attack!",
|
||||
"Guards! Come quick!",
|
||||
"Guards! Guards!",
|
||||
"Guards! There's a villain attacking me!",
|
||||
"Guards, slay this foul villain!",
|
||||
"Guards! There's a murderer!",
|
||||
"Guards! Help me!",
|
||||
"You won't get away with this! Guards!",
|
||||
"You fiend!",
|
||||
"Help me!",
|
||||
"Help! Please!",
|
||||
"Ouch! Guards! Help!",
|
||||
"They're coming for me!",
|
||||
"Help! Help! I'm being repressed",
|
||||
"Ah, now we see the violence inherent in the system.",
|
||||
],
|
||||
}
|
||||
)
|
||||
|
@ -323,5 +323,8 @@ Force
|
||||
Dexterité
|
||||
|
||||
Intelligence"#,
|
||||
},
|
||||
|
||||
vector_map: {
|
||||
}
|
||||
)
|
||||
)
|
||||
|
@ -524,5 +524,8 @@ Volontà
|
||||
"esc_menu.logout": "Disconnettiti",
|
||||
"esc_menu.quit_game": "Esci dal Gioco",
|
||||
/// End Escape Menu Section
|
||||
}
|
||||
},
|
||||
|
||||
vector_map: {
|
||||
}
|
||||
)
|
||||
|
@ -368,5 +368,8 @@ Força de vontade
|
||||
"esc_menu.logout": "Desconectar",
|
||||
"esc_menu.quit_game": "Sair do jogo",
|
||||
/// End Escape Menu Section
|
||||
},
|
||||
|
||||
vector_map: {
|
||||
}
|
||||
)
|
||||
|
@ -365,5 +365,8 @@ https://account.veloren.net."#,
|
||||
"esc_menu.logout": "Выйти в меню",
|
||||
"esc_menu.quit_game": "Выйти из игры",
|
||||
/// End Escape Menu Section
|
||||
},
|
||||
|
||||
vector_map: {
|
||||
}
|
||||
)
|
||||
|
@ -397,5 +397,8 @@ Hareket gücü
|
||||
"esc_menu.logout": "Çıkış yap",
|
||||
"esc_menu.quit_game": "Oyundan çık",
|
||||
/// End Escape Menu Section
|
||||
},
|
||||
|
||||
vector_map: {
|
||||
}
|
||||
)
|
||||
|
@ -89,13 +89,46 @@ impl Default for Activity {
|
||||
/// Default duration in seconds of speech bubbles
|
||||
pub const SPEECH_BUBBLE_DURATION: f64 = 5.0;
|
||||
|
||||
/// The contents of a speech bubble
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub enum SpeechBubbleMessage {
|
||||
/// This message was said by a player and needs no translation
|
||||
Plain(String),
|
||||
/// This message was said by an NPC. The fields are a i18n key and a random
|
||||
/// u16 index
|
||||
Localized(String, u16),
|
||||
}
|
||||
|
||||
/// Adds a speech bubble to the entity
|
||||
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct SpeechBubble {
|
||||
pub message: String,
|
||||
pub message: SpeechBubbleMessage,
|
||||
pub timeout: Option<Time>,
|
||||
// TODO add icon enum for player chat type / npc quest+trade
|
||||
}
|
||||
impl Component for SpeechBubble {
|
||||
type Storage = FlaggedStorage<Self, HashMapStorage<Self>>;
|
||||
}
|
||||
impl SpeechBubble {
|
||||
pub fn npc_new(i18n_key: String, now: Time) -> Self {
|
||||
let message = SpeechBubbleMessage::Localized(i18n_key, rand::random());
|
||||
let timeout = Some(Time(now.0 + SPEECH_BUBBLE_DURATION));
|
||||
Self { message, timeout }
|
||||
}
|
||||
|
||||
pub fn player_new(message: String, now: Time) -> Self {
|
||||
let message = SpeechBubbleMessage::Plain(message);
|
||||
let timeout = Some(Time(now.0 + SPEECH_BUBBLE_DURATION));
|
||||
Self { message, timeout }
|
||||
}
|
||||
|
||||
pub fn message<F>(&self, i18n_variation: F) -> String
|
||||
where
|
||||
F: Fn(String, u16) -> String,
|
||||
{
|
||||
match &self.message {
|
||||
SpeechBubbleMessage::Plain(m) => m.to_string(),
|
||||
SpeechBubbleMessage::Localized(k, i) => i18n_variation(k.to_string(), *i).to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
agent::Activity,
|
||||
item::{tool::ToolKind, ItemKind},
|
||||
Agent, Alignment, CharacterState, ControlAction, Controller, Loadout, MountState, Ori, Pos,
|
||||
Scale, Stats,
|
||||
Scale, SpeechBubble, Stats,
|
||||
},
|
||||
path::Chaser,
|
||||
state::{DeltaTime, Time},
|
||||
@ -38,6 +38,7 @@ impl<'a> System<'a> for Sys {
|
||||
ReadStorage<'a, Alignment>,
|
||||
WriteStorage<'a, Agent>,
|
||||
WriteStorage<'a, Controller>,
|
||||
WriteStorage<'a, SpeechBubble>,
|
||||
ReadStorage<'a, MountState>,
|
||||
);
|
||||
|
||||
@ -58,6 +59,7 @@ impl<'a> System<'a> for Sys {
|
||||
alignments,
|
||||
mut agents,
|
||||
mut controllers,
|
||||
mut speech_bubbles,
|
||||
mount_states,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
@ -386,6 +388,10 @@ impl<'a> System<'a> for Sys {
|
||||
if !agent.activity.is_attack() {
|
||||
if let Some(attacker) = uid_allocator.retrieve_entity_internal(by.id())
|
||||
{
|
||||
let message = "npc.speech.villager_under_attack".to_string();
|
||||
let bubble = SpeechBubble::npc_new(message, *time);
|
||||
let _ = speech_bubbles.insert(entity, bubble);
|
||||
|
||||
agent.activity = Activity::Attack {
|
||||
target: attacker,
|
||||
chaser: Chaser::default(),
|
||||
|
@ -6,7 +6,7 @@ use crate::{
|
||||
use common::{
|
||||
comp::{
|
||||
Admin, CanBuild, ControlEvent, Controller, ForceUpdate, Ori, Player, Pos, SpeechBubble,
|
||||
Stats, Vel, SPEECH_BUBBLE_DURATION,
|
||||
Stats, Vel,
|
||||
},
|
||||
event::{EventBus, ServerEvent},
|
||||
msg::{
|
||||
@ -76,8 +76,6 @@ impl<'a> System<'a> for Sys {
|
||||
) {
|
||||
timer.start();
|
||||
|
||||
let time = time.0;
|
||||
|
||||
let persistence_db_dir = &persistence_db_dir.0;
|
||||
|
||||
let mut server_emitter = server_event_bus.emitter();
|
||||
@ -97,13 +95,13 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
// Update client ping.
|
||||
if new_msgs.len() > 0 {
|
||||
client.last_ping = time
|
||||
} else if time - client.last_ping > CLIENT_TIMEOUT // Timeout
|
||||
client.last_ping = time.0
|
||||
} else if time.0 - client.last_ping > CLIENT_TIMEOUT // Timeout
|
||||
|| client.postbox.error().is_some()
|
||||
// Postbox error
|
||||
{
|
||||
server_emitter.emit(ServerEvent::ClientDisconnect(entity));
|
||||
} else if time - client.last_ping > CLIENT_TIMEOUT * 0.5 {
|
||||
} else if time.0 - client.last_ping > CLIENT_TIMEOUT * 0.5 {
|
||||
// Try pinging the client if the timeout is nearing.
|
||||
client.postbox.send_message(ServerMsg::Ping);
|
||||
}
|
||||
@ -406,11 +404,7 @@ impl<'a> System<'a> for Sys {
|
||||
server_emitter.emit(ServerEvent::ChatCmd(entity, argv));
|
||||
continue;
|
||||
} else {
|
||||
let timeout = Some(Time(time + SPEECH_BUBBLE_DURATION));
|
||||
let bubble = SpeechBubble {
|
||||
message: message.clone(),
|
||||
timeout,
|
||||
};
|
||||
let bubble = SpeechBubble::player_new(message.clone(), *time);
|
||||
let _ = speech_bubbles.insert(entity, bubble);
|
||||
match players.get(entity) {
|
||||
Some(player) => {
|
||||
|
@ -21,7 +21,6 @@ impl<'a> System<'a> for Sys {
|
||||
.map(|(ent, _)| ent)
|
||||
.collect();
|
||||
for ent in expired_ents {
|
||||
println!("Remoaving bobble");
|
||||
speech_bubbles.remove(ent);
|
||||
}
|
||||
|
||||
|
@ -954,6 +954,7 @@ impl Hud {
|
||||
own_level,
|
||||
&global_state.settings.gameplay,
|
||||
self.pulse,
|
||||
&self.voxygen_i18n,
|
||||
&self.imgs,
|
||||
&self.fonts,
|
||||
)
|
||||
|
@ -1,5 +1,6 @@
|
||||
use super::{img_ids::Imgs, HP_COLOR, LOW_HP_COLOR, MANA_COLOR};
|
||||
use crate::{
|
||||
i18n::VoxygenLocalization,
|
||||
settings::GameplaySettings,
|
||||
ui::{fonts::ConrodVoxygenFonts, Ingameable},
|
||||
};
|
||||
@ -51,6 +52,7 @@ pub struct Overhead<'a> {
|
||||
own_level: u32,
|
||||
settings: &'a GameplaySettings,
|
||||
pulse: f32,
|
||||
voxygen_i18n: &'a std::sync::Arc<VoxygenLocalization>,
|
||||
imgs: &'a Imgs,
|
||||
fonts: &'a ConrodVoxygenFonts,
|
||||
#[conrod(common_builder)]
|
||||
@ -66,6 +68,7 @@ impl<'a> Overhead<'a> {
|
||||
own_level: u32,
|
||||
settings: &'a GameplaySettings,
|
||||
pulse: f32,
|
||||
voxygen_i18n: &'a std::sync::Arc<VoxygenLocalization>,
|
||||
imgs: &'a Imgs,
|
||||
fonts: &'a ConrodVoxygenFonts,
|
||||
) -> Self {
|
||||
@ -77,6 +80,7 @@ impl<'a> Overhead<'a> {
|
||||
own_level,
|
||||
settings,
|
||||
pulse,
|
||||
voxygen_i18n,
|
||||
imgs,
|
||||
fonts,
|
||||
common: widget::CommonBuilder::default(),
|
||||
@ -139,7 +143,11 @@ impl<'a> Widget for Overhead<'a> {
|
||||
// Speech bubble
|
||||
if let Some(bubble) = self.bubble {
|
||||
let dark_mode = self.settings.speech_bubble_dark_mode;
|
||||
let mut text = Text::new(&bubble.message)
|
||||
let localizer =
|
||||
|s: String, i| -> String { self.voxygen_i18n.get_variation(&s, i).to_string() };
|
||||
let bubble_contents: String = bubble.message(localizer);
|
||||
|
||||
let mut text = Text::new(&bubble_contents)
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.font_size(18)
|
||||
.up_from(state.ids.name, 10.0)
|
||||
|
@ -53,10 +53,15 @@ pub type VoxygenFonts = HashMap<String, Font>;
|
||||
pub struct VoxygenLocalization {
|
||||
/// A map storing the localized texts
|
||||
///
|
||||
/// Localized content can be access using a String key
|
||||
/// Localized content can be accessed using a String key.
|
||||
pub string_map: HashMap<String, String>,
|
||||
|
||||
/// Either to convert the input text encoded in UTF-8
|
||||
/// A map for storing variations of localized texts, for example multiple
|
||||
/// ways of saying "Help, I'm under attack". Used primarily for npc
|
||||
/// dialogue.
|
||||
pub vector_map: HashMap<String, Vec<String>>,
|
||||
|
||||
/// Whether to convert the input text encoded in UTF-8
|
||||
/// into a ASCII version by using the `deunicode` crate.
|
||||
pub convert_utf8_to_ascii: bool,
|
||||
|
||||
@ -78,23 +83,56 @@ impl VoxygenLocalization {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the missing keys compared to the reference language and return
|
||||
/// them
|
||||
pub fn list_missing_entries(&self) -> HashSet<String> {
|
||||
/// Get a variation of localized text from the given key
|
||||
///
|
||||
/// `index` should be a random number from `0` to `u16::max()`
|
||||
///
|
||||
/// If the key is not present in the localization object
|
||||
/// then the key is returned.
|
||||
pub fn get_variation<'a>(&'a self, key: &'a str, index: u16) -> &str {
|
||||
match self.vector_map.get(key) {
|
||||
Some(v) if !v.is_empty() => &v[index as usize % v.len()],
|
||||
_ => key,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the missing keys compared to the reference language
|
||||
pub fn list_missing_entries(&self) -> (HashSet<String>, HashSet<String>) {
|
||||
let reference_localization =
|
||||
load_expect::<VoxygenLocalization>(i18n_asset_key(REFERENCE_LANG).as_ref());
|
||||
let reference_keys: HashSet<_> =
|
||||
reference_localization.string_map.keys().cloned().collect();
|
||||
let current_keys: HashSet<_> = self.string_map.keys().cloned().collect();
|
||||
|
||||
reference_keys.difference(¤t_keys).cloned().collect()
|
||||
let reference_string_keys: HashSet<_> =
|
||||
reference_localization.string_map.keys().cloned().collect();
|
||||
let string_keys: HashSet<_> = self.string_map.keys().cloned().collect();
|
||||
let strings = reference_string_keys
|
||||
.difference(&string_keys)
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let reference_vector_keys: HashSet<_> =
|
||||
reference_localization.vector_map.keys().cloned().collect();
|
||||
let vector_keys: HashSet<_> = self.vector_map.keys().cloned().collect();
|
||||
let vectors = reference_vector_keys
|
||||
.difference(&vector_keys)
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
(strings, vectors)
|
||||
}
|
||||
|
||||
/// Log missing entries (compared to the reference language) as warnings
|
||||
pub fn log_missing_entries(&self) {
|
||||
for missing_key in self.list_missing_entries() {
|
||||
let (missing_strings, missing_vectors) = self.list_missing_entries();
|
||||
for missing_key in missing_strings {
|
||||
log::warn!(
|
||||
"[{:?}] Missing key {:?}",
|
||||
"[{:?}] Missing string key {:?}",
|
||||
self.metadata.language_identifier,
|
||||
missing_key
|
||||
);
|
||||
}
|
||||
for missing_key in missing_vectors {
|
||||
log::warn!(
|
||||
"[{:?}] Missing vector key {:?}",
|
||||
self.metadata.language_identifier,
|
||||
missing_key
|
||||
);
|
||||
@ -116,6 +154,10 @@ impl Asset for VoxygenLocalization {
|
||||
for value in asked_localization.string_map.values_mut() {
|
||||
*value = deunicode(value);
|
||||
}
|
||||
|
||||
for value in asked_localization.vector_map.values_mut() {
|
||||
*value = value.into_iter().map(|s| deunicode(s)).collect();
|
||||
}
|
||||
}
|
||||
asked_localization.metadata.language_name =
|
||||
deunicode(&asked_localization.metadata.language_name);
|
||||
|
Loading…
Reference in New Issue
Block a user