Multiplayer continues after a player leaves: AI side takeover, background match while menus are open, match chat, five-tile fog start
This commit is contained in:
1 parent
2721638910
commit
dc22e27184
11 files changed
+436
-70
No files matched your search
@@ -138,6 +138,22 @@ pub const FRAME_HI: Color = Color::new(0.95, 0.8, 0.4, 1.0);
|
||||
pub const BG: Color = Color::new(0.06, 0.04, 0.05, 0.92);
|
||||
pub const TXT: Color = Color::new(0.9, 0.86, 0.78, 1.0);
|
||||
pub const DIM: Color = Color::new(0.6, 0.56, 0.5, 1.0);
|
||||
|
||||
pub fn side_color(side: Option<u8>) -> Color {
|
||||
match side {
|
||||
Some(0) => Color::new(1.0, 0.85, 0.45, 1.0),
|
||||
Some(1) => Color::new(0.85, 0.55, 0.25, 1.0),
|
||||
Some(2) => Color::new(0.35, 0.95, 0.40, 1.0),
|
||||
Some(3) => Color::new(0.40, 0.60, 1.00, 1.0),
|
||||
Some(4) => Color::new(0.85, 0.75, 0.30, 1.0),
|
||||
_ => TXT,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chat_color(side: Option<u8>, alpha: f32) -> Color {
|
||||
let c = side_color(side);
|
||||
Color::new(c.r, c.g, c.b, alpha)
|
||||
}
|
||||
pub const GOLD: Color = Color::new(1.0, 0.84, 0.25, 1.0);
|
||||
pub const MANA: Color = Color::new(0.7, 0.45, 1.0, 1.0);
|
||||
pub const RED: Color = Color::new(0.95, 0.3, 0.3, 1.0);
|
||||
@@ -343,6 +359,8 @@ pub enum UiAction {
|
||||
CloseHelp,
|
||||
NewGame,
|
||||
SelectLanguage(String),
|
||||
ChatSetTeam(bool),
|
||||
ChatSend,
|
||||
Consumed,
|
||||
}
|
||||
|
||||
@@ -353,6 +371,12 @@ pub struct Ui {
|
||||
ov_scroll: usize,
|
||||
pub show_langsel: bool,
|
||||
pub log: Vec<(Msg, f64)>,
|
||||
pub chat_log: Vec<(Option<u8>, String, String, f64)>,
|
||||
pub chat_open: bool,
|
||||
pub chat_text: String,
|
||||
pub chat_team: bool,
|
||||
chat_rects: [(Rect, Option<bool>); 2],
|
||||
chat_send_rect: Rect,
|
||||
pub rects: Vec<Rect>,
|
||||
minimap_img: Image,
|
||||
minimap_tex: Texture2D,
|
||||
@@ -407,6 +431,12 @@ impl Ui {
|
||||
ov_scroll: 0,
|
||||
show_langsel: false,
|
||||
log: Vec::new(),
|
||||
chat_log: Vec::new(),
|
||||
chat_open: false,
|
||||
chat_text: String::new(),
|
||||
chat_team: false,
|
||||
chat_rects: [(Rect::new(0.0, 0.0, 0.0, 0.0), Some(false)); 2],
|
||||
chat_send_rect: Rect::new(0.0, 0.0, 0.0, 0.0),
|
||||
rects: Vec::new(),
|
||||
minimap_img: img,
|
||||
minimap_tex: tex,
|
||||
@@ -425,7 +455,7 @@ impl Ui {
|
||||
}
|
||||
|
||||
pub fn over_ui(&self, x: f32, y: f32) -> bool {
|
||||
self.show_help || self.show_langsel || self.rects.iter().any(|r| r.contains(vec2(x, y)))
|
||||
self.chat_open || self.show_help || self.show_langsel || self.rects.iter().any(|r| r.contains(vec2(x, y)))
|
||||
}
|
||||
|
||||
pub fn minimap_contains(&self, x: f32, y: f32) -> bool {
|
||||
@@ -439,6 +469,20 @@ impl Ui {
|
||||
|
||||
pub fn click(&mut self, x: f32, y: f32, g: &Game) -> UiAction {
|
||||
let p = vec2(x, y);
|
||||
if self.chat_open {
|
||||
if self.chat_rects[0].0.contains(p) {
|
||||
self.chat_team = false;
|
||||
return UiAction::ChatSetTeam(false);
|
||||
}
|
||||
if self.chat_rects[1].0.contains(p) {
|
||||
self.chat_team = true;
|
||||
return UiAction::ChatSetTeam(true);
|
||||
}
|
||||
if self.chat_send_rect.contains(p) {
|
||||
return UiAction::ChatSend;
|
||||
}
|
||||
return UiAction::Consumed;
|
||||
}
|
||||
if self.show_langsel {
|
||||
for (r, code) in &self.lang_rects {
|
||||
if r.contains(p) {
|
||||
@@ -853,29 +897,64 @@ impl Ui {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------- messages (translated at display time, so a language switch applies at once)
|
||||
let recent: Vec<&(Msg, f64)> = self.log.iter().filter(|(_, t)| now - t < 14.0).collect();
|
||||
if !recent.is_empty() {
|
||||
let mcols = (((sw - mm_w) / CW()) as i32 - 2).clamp(20, 70);
|
||||
let mut lines: Vec<(String, f32)> = Vec::new();
|
||||
// ---------------- messages + chat (translated at display time, so a language switch applies at once)
|
||||
let mut recent: Vec<(f64, String, Color)> = Vec::new();
|
||||
for (m, t) in self.log.iter().filter(|(_, t)| now - t < 14.0) {
|
||||
let a = (1.0 - ((now - t) as f32 - 11.0) / 3.0).clamp(0.2, 1.0);
|
||||
recent.push((*t, trf(m.key, &m.args), Color::new(TXT.r, TXT.g, TXT.b, a)));
|
||||
}
|
||||
for (side, name, text, t) in self.chat_log.iter().filter(|(_, _, _, t)| now - t < 14.0) {
|
||||
let a = (1.0 - ((now - t) as f32 - 11.0) / 3.0).clamp(0.2, 1.0);
|
||||
recent.push((*t, format!("{name}: {text}"), chat_color(*side, a)));
|
||||
}
|
||||
recent.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
|
||||
if !recent.is_empty() || self.chat_open {
|
||||
let mcols = (((sw - mm_w) / CW()) as i32 - 2).clamp(34, 70);
|
||||
let mut lines: Vec<(String, Color)> = Vec::new();
|
||||
let keep = if touch_ui() { 3 } else { 5 };
|
||||
for (m, t) in recent.iter().rev().take(keep).rev() {
|
||||
let a = (1.0 - ((now - t) as f32 - 11.0) / 3.0).clamp(0.2, 1.0);
|
||||
for l in wrap(&trf(m.key, &m.args), mcols as usize - 4) {
|
||||
lines.push((l, a));
|
||||
for (_, line, col) in recent.iter().rev().take(keep).rev() {
|
||||
for l in wrap(line, mcols as usize - 4) {
|
||||
lines.push((l.clone(), *col));
|
||||
}
|
||||
}
|
||||
let mrows = lines.len() as i32 + 2;
|
||||
let chat_rows = if self.chat_open { 2 } else { 0 };
|
||||
let mrows = lines.len() as i32 + chat_rows + 2;
|
||||
let my0 = tb_y - mrows as f32 * CH();
|
||||
gfx.window(0.0, my0, mcols, mrows, S(), false, FRAME, Color::new(0.06, 0.04, 0.05, 0.75));
|
||||
gfx.window(0.0, my0, mcols, mrows, S(), false, FRAME, Color::new(0.06, 0.04, 0.05, 0.82));
|
||||
self.rects.push(Rect::new(0.0, my0, mcols as f32 * CW(), mrows as f32 * CH()));
|
||||
for (k, (l, a)) in lines.iter().enumerate() {
|
||||
gfx.text(l, CW() * 2.0, my0 + CH() * (k as f32 + 1.0), S(), Color::new(TXT.r, TXT.g, TXT.b, *a));
|
||||
for (k, (l, col)) in lines.iter().enumerate() {
|
||||
gfx.text(l, CW() * 2.0, my0 + CH() * (k as f32 + 1.0), S(), *col);
|
||||
}
|
||||
if self.chat_open {
|
||||
let cy = my0 + CH() * (lines.len() as f32 + 1.0);
|
||||
let all = tr("chat.all");
|
||||
let team = tr("chat.team");
|
||||
let send = tr("chat.send");
|
||||
let r0 = Rect::new(CW() * 2.0, cy, 10.0 * CW(), CH());
|
||||
let r1 = Rect::new(CW() * 14.0, cy, 10.0 * CW(), CH());
|
||||
let r2 = Rect::new(mcols as f32 * CW() - 8.0 * CW(), cy + CH(), 6.0 * CW(), CH());
|
||||
self.chat_rects = [(r0, Some(false)), (r1, Some(true))];
|
||||
self.chat_send_rect = r2;
|
||||
self.rects.push(r0);
|
||||
self.rects.push(r1);
|
||||
self.rects.push(r2);
|
||||
let all_line = format!("{} {all}", if self.chat_team { " " } else { "o" });
|
||||
let team_line = format!("{} {team}", if self.chat_team { "o" } else { " " });
|
||||
gfx.text(&all_line, r0.x + CW(), cy + 4.0 * px, SMALL(), if !self.chat_team { FRAME_HI } else { DIM });
|
||||
gfx.text(&team_line, r1.x + CW(), cy + 4.0 * px, SMALL(), if self.chat_team { FRAME_HI } else { DIM });
|
||||
let shown = format!("> {}_", self.chat_text);
|
||||
let maxc = (((mcols as f32 * CW() - 4.0 * CW() - 8.0 * CW()) / (GW * SMALL())) as usize).max(8);
|
||||
let clipped: String = shown.chars().take(maxc).collect();
|
||||
gfx.text(&clipped, CW() * 2.0, cy + CH() + 4.0 * px, SMALL(), TXT);
|
||||
gfx.text(&send, r2.x + CW() * 1.5, cy + CH() + 4.0 * px, SMALL(), TXT);
|
||||
}
|
||||
}
|
||||
if self.log.len() > 100 {
|
||||
self.log.drain(0..50);
|
||||
}
|
||||
if self.chat_log.len() > 100 {
|
||||
self.chat_log.drain(0..50);
|
||||
}
|
||||
|
||||
// the open row of tools (wraps into several rows if needed)
|
||||
if let Some((c, x0, bw, bcols)) = popup {
|
||||
|
||||
Reference in new issue
Block a user