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
+86
-11
@@ -251,7 +251,7 @@ fn account_action(a: MenuAction, cloud: &mut Cloud, menu: &mut Menu, app: &mut A
|
||||
app.client.leave();
|
||||
app.match_mode = false;
|
||||
}
|
||||
MenuAction::MatchChat(text) => app.client.chat(&text),
|
||||
MenuAction::MatchChat(text) => app.client.chat(&text, false),
|
||||
MenuAction::MatchEditField(i) => {
|
||||
if ui::touch_ui() {
|
||||
let title = i18n::tr(if i == 0 { "net.join_hint" } else { "net.chat" });
|
||||
@@ -466,6 +466,26 @@ impl App {
|
||||
self.client.on_disconnect();
|
||||
}
|
||||
|
||||
fn leave_match(&mut self) {
|
||||
if !self.match_mode && !self.game.in_match() {
|
||||
return;
|
||||
}
|
||||
self.client.leave();
|
||||
if self.ws_id > 0 {
|
||||
while let Some(text) = self.client.outbox.pop_front() {
|
||||
if !platform::ws_send(self.ws_id, &text) {
|
||||
self.client.outbox.push_front(text);
|
||||
break;
|
||||
}
|
||||
}
|
||||
platform::ws_close(self.ws_id);
|
||||
self.ws_id = 0;
|
||||
}
|
||||
self.game.end_match();
|
||||
self.match_mode = false;
|
||||
self.client.on_disconnect();
|
||||
}
|
||||
|
||||
fn start_match_game(&mut self, info: StartInfo) -> bool {
|
||||
if info.map_w < 72 || info.map_h < 72 || !info.sides.contains(&0) {
|
||||
return false;
|
||||
@@ -475,7 +495,9 @@ impl App {
|
||||
if !self.game.start_match(&info.sides, local) {
|
||||
return false;
|
||||
}
|
||||
self.game.ai_difficulty = info.difficulty;
|
||||
self.game.hints = self.settings.hints;
|
||||
self.game.init_fog();
|
||||
let (cx, cy) = self.game.map.core;
|
||||
self.cam = Cam::new(cx as f32 + 1.5, cy as f32 + 1.5);
|
||||
self.match_mode = true;
|
||||
@@ -487,7 +509,8 @@ impl App {
|
||||
if !self.match_mode || !self.game.in_match() {
|
||||
return;
|
||||
}
|
||||
for (_, cmds) in self.client.take_turns() {
|
||||
for (_, cmds, ai) in self.client.take_turns() {
|
||||
self.game.sync_ai_sides(&ai, self.settings.difficulty);
|
||||
self.game.receive_turn(&cmds);
|
||||
}
|
||||
for (_, turn, sum) in self.client.take_checks() {
|
||||
@@ -639,14 +662,22 @@ async fn main() {
|
||||
|
||||
// ---------------- multiplayer relay: receive messages, send queued requests, update the menu state
|
||||
app.poll_match();
|
||||
menu.in_match = app.match_mode || app.game.in_match();
|
||||
if matches!(menu.page, Some(Page::Match { .. })) {
|
||||
menu.set_match_ui(app.client.ui());
|
||||
}
|
||||
for (side, name, text) in app.client.take_new_chat() {
|
||||
ui.chat_log.push((side, name, text, now));
|
||||
while ui.chat_log.len() > 100 {
|
||||
ui.chat_log.remove(0);
|
||||
}
|
||||
}
|
||||
if let Some(info) = app.client.take_start() {
|
||||
if app.start_match_game(info) {
|
||||
menu.page = None;
|
||||
menu.error = None;
|
||||
ui.log.clear();
|
||||
ui.chat_log.clear();
|
||||
autosave_t = 0.0;
|
||||
cache_t = 0.0;
|
||||
app.set_status(i18n::tr("net.title"), now);
|
||||
@@ -712,8 +743,12 @@ async fn main() {
|
||||
}
|
||||
|
||||
let dialog = ui.show_langsel || ui.show_help;
|
||||
if ui.chat_open && (menu.open() || dialog) {
|
||||
ui.chat_open = false;
|
||||
ui.chat_text.clear();
|
||||
}
|
||||
let in_menu = menu.open();
|
||||
let playing = !in_menu && !dialog;
|
||||
let playing = !in_menu && !dialog && !ui.chat_open;
|
||||
let over_ui = ui.over_ui(ux, uy);
|
||||
let hover_tile = tool_tile(&app.cam, &app.game.map, ui.tool, mx, my);
|
||||
let ground = app.cam.ground_point(mx, my);
|
||||
@@ -721,9 +756,12 @@ async fn main() {
|
||||
let keys = app.settings.clone();
|
||||
let key = |a: Action| keys.key(a);
|
||||
|
||||
// ---------------- Esc: dialogs, menus, pause menu
|
||||
// ---------------- Esc: chat, dialogs, menus, pause menu
|
||||
if is_key_pressed(KeyCode::Escape) {
|
||||
if ui.show_langsel {
|
||||
if ui.chat_open {
|
||||
ui.chat_open = false;
|
||||
ui.chat_text.clear();
|
||||
} else if ui.show_langsel {
|
||||
} else if ui.show_help {
|
||||
ui.show_help = false;
|
||||
} else if in_menu {
|
||||
@@ -735,6 +773,31 @@ async fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------- in-match chat input
|
||||
if ui.chat_open {
|
||||
while let Some(c) = get_char_pressed() {
|
||||
if !c.is_control() && c != '\t' && ui.chat_text.chars().count() < 80 {
|
||||
ui.chat_text.push(c);
|
||||
}
|
||||
}
|
||||
if is_key_pressed(KeyCode::Backspace) {
|
||||
ui.chat_text.pop();
|
||||
}
|
||||
if is_key_pressed(KeyCode::Enter) || is_key_pressed(KeyCode::KpEnter) {
|
||||
let text: String = ui.chat_text.trim().chars().take(80).collect();
|
||||
if !text.is_empty() {
|
||||
app.client.chat(&text, ui.chat_team);
|
||||
}
|
||||
ui.chat_text.clear();
|
||||
ui.chat_open = false;
|
||||
}
|
||||
} else if playing && app.game.state == 0 && (app.match_mode || app.game.in_match()) {
|
||||
if is_key_pressed(KeyCode::Enter) || is_key_pressed(KeyCode::KpEnter) {
|
||||
ui.chat_open = true;
|
||||
ui.chat_text.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------- keyboard (only while playing)
|
||||
if playing {
|
||||
for i in 0..TOOLS.len() {
|
||||
@@ -1000,7 +1063,9 @@ async fn main() {
|
||||
}
|
||||
MenuAction::Help => ui.show_help = true,
|
||||
MenuAction::MainMenu => {
|
||||
if app.game.state == 0 {
|
||||
if app.match_mode || app.game.in_match() {
|
||||
app.leave_match();
|
||||
} else if app.game.state == 0 {
|
||||
platform::storage_set(save::AUTOSAVE_KEY, &app.autosave_json());
|
||||
menu.has_autosave = true;
|
||||
}
|
||||
@@ -1052,6 +1117,15 @@ async fn main() {
|
||||
app.game.sfx_ui(game::Sfx::Error);
|
||||
}
|
||||
UiAction::CloseHelp | UiAction::SelectLanguage(_) | UiAction::Consumed => {}
|
||||
UiAction::ChatSetTeam(team) => ui.chat_team = team,
|
||||
UiAction::ChatSend => {
|
||||
let text: String = ui.chat_text.trim().chars().take(80).collect();
|
||||
if !text.is_empty() {
|
||||
app.client.chat(&text, ui.chat_team);
|
||||
}
|
||||
ui.chat_text.clear();
|
||||
ui.chat_open = false;
|
||||
}
|
||||
UiAction::None => {
|
||||
if touch_mode {
|
||||
// touch: area tools start their rectangle, everything else acts on the tap
|
||||
@@ -1145,17 +1219,18 @@ async fn main() {
|
||||
app.game.hints = app.settings.hints;
|
||||
}
|
||||
|
||||
// ---------------- update (the world stands still while menus or dialogs are open;
|
||||
// the bridge battle behind the title menu goes on, the camera slowly circles it)
|
||||
// ---------------- update (single-player worlds stand still while menus or dialogs are open;
|
||||
// matches keep running in the background, the bridge battle behind the title menu goes on)
|
||||
if app.game.demo.is_some() && !playing {
|
||||
app.game.update(dt);
|
||||
app.cam.yaw += dt * 0.03;
|
||||
app.game.events.retain(|ev| ev.pos.is_none());
|
||||
}
|
||||
if playing {
|
||||
// the world runs in fixed steps: the speed setting only feeds more time into them
|
||||
} else if app.game.in_match() {
|
||||
app.game.update(dt * app.settings.game_speed.max(1.0));
|
||||
app.tick_match_game();
|
||||
} else if playing {
|
||||
// the world runs in fixed steps: the speed setting only feeds more time into them
|
||||
app.game.update(dt * app.settings.game_speed.max(1.0));
|
||||
// autosave: snapshot for leaving the tab + regular autosave
|
||||
if app.game.state == 0 && !app.match_mode {
|
||||
cache_t += dt;
|
||||
|
||||
Reference in new issue
Block a user