Implement undo button for live games.
This commit is contained in:
parent
b848492c5a
commit
60b48e4708
@ -42,6 +42,7 @@ impl Game {
|
|||||||
|
|
||||||
pub fn init(&mut self)
|
pub fn init(&mut self)
|
||||||
{
|
{
|
||||||
|
*self = Self::new();
|
||||||
self.board.init();
|
self.board.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +74,6 @@ impl Game {
|
|||||||
//
|
//
|
||||||
|
|
||||||
if valid {
|
if valid {
|
||||||
|
|
||||||
// Move piece on board.
|
// Move piece on board.
|
||||||
if match play.source {
|
if match play.source {
|
||||||
0 | 2 => {
|
0 | 2 => {
|
||||||
|
@ -23,7 +23,7 @@ pub struct Session {
|
|||||||
pub time:u64,
|
pub time:u64,
|
||||||
pub chain_id:usize,
|
pub chain_id:usize,
|
||||||
|
|
||||||
pub undo:Option<u8>,
|
pub undo:u8,
|
||||||
}
|
}
|
||||||
impl Session {
|
impl Session {
|
||||||
pub fn get_connections(&self) -> Vec<(u32, u8)>
|
pub fn get_connections(&self) -> Vec<(u32, u8)>
|
||||||
|
@ -637,8 +637,6 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
|
|||||||
| GameMessageData::PlayDrop(turn, from, to)
|
| GameMessageData::PlayDrop(turn, from, to)
|
||||||
| GameMessageData::PlayAlt(turn, from, to)
|
| GameMessageData::PlayAlt(turn, from, to)
|
||||||
=> {
|
=> {
|
||||||
println!("HERE");
|
|
||||||
|
|
||||||
if !session.game.is_complete() {
|
if !session.game.is_complete() {
|
||||||
if (user_id == Some(session.p_dawn.user) && session.game.turn & 1 == 0)
|
if (user_id == Some(session.p_dawn.user) && session.game.turn & 1 == 0)
|
||||||
|| (user_id == Some(session.p_dusk.user) && session.game.turn & 1 == 1) {
|
|| (user_id == Some(session.p_dusk.user) && session.game.turn & 1 == 1) {
|
||||||
@ -653,8 +651,6 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
|
|||||||
from, to,
|
from, to,
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("play {} {} {}", play.source, play.from, play.to);
|
|
||||||
|
|
||||||
if session.game.process(&play).is_ok() {
|
if session.game.process(&play).is_ok() {
|
||||||
// Commit play to history
|
// Commit play to history
|
||||||
app.filesystem.session_history_push(session.id, play).ok();
|
app.filesystem.session_history_push(session.id, play).ok();
|
||||||
@ -670,6 +666,8 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
|
|||||||
// Send status to players.
|
// Send status to players.
|
||||||
send_user_status.push(session.p_dawn.user);
|
send_user_status.push(session.p_dawn.user);
|
||||||
send_user_status.push(session.p_dusk.user);
|
send_user_status.push(session.p_dusk.user);
|
||||||
|
|
||||||
|
session.undo = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -677,11 +675,59 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
|
|||||||
}
|
}
|
||||||
|
|
||||||
GameMessageData::Undo(turn, _) => {
|
GameMessageData::Undo(turn, _) => {
|
||||||
if !session.game.is_complete() {
|
use packet::PacketGameMessage;
|
||||||
if (user_id == Some(session.p_dawn.user) && session.game.turn & 1 == 0)
|
|
||||||
|| (user_id == Some(session.p_dusk.user) && session.game.turn & 1 == 1) {
|
if session.game.turn > 0 && !session.game.is_complete() {
|
||||||
if turn == session.game.turn {
|
let player = if user_id == Some(session.p_dawn.user) { 1 }
|
||||||
// Request or commit undo
|
else if user_id == Some(session.p_dusk.user) { 2 }
|
||||||
|
else { 0 };
|
||||||
|
|
||||||
|
if player != 0 {
|
||||||
|
if turn == session.game.turn && (session.undo & player) == 0 {
|
||||||
|
if session.undo == 0 {
|
||||||
|
|
||||||
|
// Send undo request to opposing player
|
||||||
|
let packet = GameMessageData::Undo(turn, 0);
|
||||||
|
|
||||||
|
if player == 1 {
|
||||||
|
for cid in &session.p_dusk.connections {
|
||||||
|
packets.push(QRPacket::new(
|
||||||
|
*cid,
|
||||||
|
QRPacketData::GameMessage(PacketGameMessage::with(packet))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for cid in &session.p_dawn.connections {
|
||||||
|
packets.push(QRPacket::new(
|
||||||
|
*cid,
|
||||||
|
QRPacketData::GameMessage(PacketGameMessage::with(packet))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
session.undo |= player;
|
||||||
|
} else {
|
||||||
|
// Send undo command to clients
|
||||||
|
let revert_count = if session.game.turn > 1 && session.undo == (1 << (session.game.turn & 1)) { 2 } else { 1 };
|
||||||
|
|
||||||
|
for _ in 0..revert_count {
|
||||||
|
session.game.history.pop();
|
||||||
|
app.filesystem.session_history_pop(session.id).ok();
|
||||||
|
}
|
||||||
|
session.game.apply_history(&session.game.history.clone()).ok();
|
||||||
|
|
||||||
|
let packet = GameMessageData::Undo(session.game.turn, 1);
|
||||||
|
|
||||||
|
// Send undo request to opposing player
|
||||||
|
for (cid, _) in session.get_connections() {
|
||||||
|
packets.push(QRPacket::new(
|
||||||
|
cid,
|
||||||
|
QRPacketData::GameMessage(PacketGameMessage::with(packet))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
session.undo = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -691,6 +737,8 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
|
|||||||
if !session.game.is_complete() {
|
if !session.game.is_complete() {
|
||||||
if (user_id == Some(session.p_dawn.user) && session.game.turn & 1 == 0)
|
if (user_id == Some(session.p_dawn.user) && session.game.turn & 1 == 0)
|
||||||
|| (user_id == Some(session.p_dusk.user) && session.game.turn & 1 == 1) {
|
|| (user_id == Some(session.p_dusk.user) && session.game.turn & 1 == 1) {
|
||||||
|
|
||||||
|
session.undo = 0;
|
||||||
|
|
||||||
// Forward messsage to all clients
|
// Forward messsage to all clients
|
||||||
for (cid, _) in session.get_connections() {
|
for (cid, _) in session.get_connections() {
|
||||||
@ -818,7 +866,7 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
|
|||||||
time:std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs() as u64,
|
time:std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs() as u64,
|
||||||
chain_id,
|
chain_id,
|
||||||
|
|
||||||
undo:None,
|
undo:0,
|
||||||
};
|
};
|
||||||
session.game.init();
|
session.game.init();
|
||||||
|
|
||||||
@ -930,11 +978,12 @@ fn generate_game_state(app:&App, session:&Session) -> protocol::PacketGameStateR
|
|||||||
if let Some(user) = app.get_user_by_id(session.p_dusk.user) {
|
if let Some(user) = app.get_user_by_id(session.p_dusk.user) {
|
||||||
response.dusk_handle = user.handle.clone();
|
response.dusk_handle = user.handle.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
response.dawn_online = session.p_dawn.connections.len() > 0;
|
response.dawn_online = session.p_dawn.connections.len() > 0;
|
||||||
response.dusk_online = session.p_dusk.connections.len() > 0;
|
response.dusk_online = session.p_dusk.connections.len() > 0;
|
||||||
response.spectators = session.connections.len() as u32;
|
response.spectators = session.connections.len() as u32;
|
||||||
|
|
||||||
|
response.undo = session.undo;
|
||||||
|
|
||||||
// Get history
|
// Get history
|
||||||
response.history = session.game.history.clone();
|
response.history = session.game.history.clone();
|
||||||
|
|
||||||
|
@ -35,6 +35,11 @@ impl PacketGameMessage {
|
|||||||
data:GameMessageData::Error,
|
data:GameMessageData::Error,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with(data:GameMessageData) -> Self
|
||||||
|
{
|
||||||
|
Self { data }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
impl Packet for PacketGameMessage {
|
impl Packet for PacketGameMessage {
|
||||||
type Data = Self;
|
type Data = Self;
|
||||||
@ -63,8 +68,8 @@ impl Packet for PacketGameMessage {
|
|||||||
),
|
),
|
||||||
|
|
||||||
GMSG_UNDO => GameMessageData::Undo(
|
GMSG_UNDO => GameMessageData::Undo(
|
||||||
((data >> 8) & 0xFFFF) as u16,
|
((data >> 9) & 0xFFFF) as u16,
|
||||||
((data >> 24) & 0x1) as u8,
|
((data >> 8) & 0x1) as u8,
|
||||||
),
|
),
|
||||||
|
|
||||||
GMSG_RETIRE => GameMessageData::Resign,
|
GMSG_RETIRE => GameMessageData::Resign,
|
||||||
@ -111,8 +116,8 @@ impl Packet for PacketGameMessage {
|
|||||||
|
|
||||||
GameMessageData::Undo(turn, state) => {
|
GameMessageData::Undo(turn, state) => {
|
||||||
GMSG_UNDO as u64
|
GMSG_UNDO as u64
|
||||||
| ((turn as u64) << 8)
|
| ((state as u64) << 8)
|
||||||
| ((state as u64) << 24)
|
| ((turn as u64) << 9)
|
||||||
}
|
}
|
||||||
GameMessageData::Resign => {
|
GameMessageData::Resign => {
|
||||||
GMSG_RETIRE as u64
|
GMSG_RETIRE as u64
|
||||||
|
@ -43,11 +43,15 @@ pub struct PacketGameStateResponse {
|
|||||||
pub status:u16,
|
pub status:u16,
|
||||||
pub token:SessionToken,
|
pub token:SessionToken,
|
||||||
pub player:u8,
|
pub player:u8,
|
||||||
|
|
||||||
|
pub undo:u8,
|
||||||
|
|
||||||
pub dawn_handle:String,
|
pub dawn_handle:String,
|
||||||
pub dusk_handle:String,
|
pub dusk_handle:String,
|
||||||
pub dawn_online:bool,
|
pub dawn_online:bool,
|
||||||
pub dusk_online:bool,
|
pub dusk_online:bool,
|
||||||
pub spectators:u32,
|
pub spectators:u32,
|
||||||
|
|
||||||
pub history:Vec<Play>,
|
pub history:Vec<Play>,
|
||||||
}
|
}
|
||||||
impl PacketGameStateResponse {
|
impl PacketGameStateResponse {
|
||||||
@ -57,11 +61,15 @@ impl PacketGameStateResponse {
|
|||||||
status:0,
|
status:0,
|
||||||
token:SessionToken::default(),
|
token:SessionToken::default(),
|
||||||
player:2,
|
player:2,
|
||||||
|
|
||||||
|
undo:0,
|
||||||
|
|
||||||
dawn_handle:String::new(),
|
dawn_handle:String::new(),
|
||||||
dusk_handle:String::new(),
|
dusk_handle:String::new(),
|
||||||
dawn_online:false,
|
dawn_online:false,
|
||||||
dusk_online:false,
|
dusk_online:false,
|
||||||
spectators:0,
|
spectators:0,
|
||||||
|
|
||||||
history:Vec::new(),
|
history:Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,6 +92,7 @@ impl Packet for PacketGameStateResponse {
|
|||||||
flags |= self.player as u16;
|
flags |= self.player as u16;
|
||||||
flags |= (self.dawn_online as u16) << 2;
|
flags |= (self.dawn_online as u16) << 2;
|
||||||
flags |= (self.dusk_online as u16) << 3;
|
flags |= (self.dusk_online as u16) << 3;
|
||||||
|
flags |= (self.undo as u16) << 8;
|
||||||
|
|
||||||
[
|
[
|
||||||
pack_u16(self.status),
|
pack_u16(self.status),
|
||||||
|
@ -204,7 +204,7 @@ impl FileSystem {
|
|||||||
time,
|
time,
|
||||||
chain_id:0,
|
chain_id:0,
|
||||||
|
|
||||||
undo:None,
|
undo:0,
|
||||||
})
|
})
|
||||||
} else { Err(()) }
|
} else { Err(()) }
|
||||||
}
|
}
|
||||||
@ -216,8 +216,6 @@ impl FileSystem {
|
|||||||
let bucket_index = id & !HANDLE_BUCKET_MASK;
|
let bucket_index = id & !HANDLE_BUCKET_MASK;
|
||||||
let dir_index = id & HANDLE_BUCKET_MASK;
|
let dir_index = id & HANDLE_BUCKET_MASK;
|
||||||
|
|
||||||
println!("A");
|
|
||||||
|
|
||||||
let bucket_path = Path::new(DIR_SESSION)
|
let bucket_path = Path::new(DIR_SESSION)
|
||||||
.join(format!("{:08x}", bucket_index))
|
.join(format!("{:08x}", bucket_index))
|
||||||
.join(format!("{:08x}", dir_index));
|
.join(format!("{:08x}", dir_index));
|
||||||
@ -241,6 +239,37 @@ impl FileSystem {
|
|||||||
} else { Err(()) }
|
} else { Err(()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn session_history_pop(&mut self, id:u32) -> Result<(),()>
|
||||||
|
{
|
||||||
|
let bucket_index = id & !HANDLE_BUCKET_MASK;
|
||||||
|
let dir_index = id & HANDLE_BUCKET_MASK;
|
||||||
|
|
||||||
|
let bucket_path = Path::new(DIR_SESSION)
|
||||||
|
.join(format!("{:08x}", bucket_index))
|
||||||
|
.join(format!("{:08x}", dir_index));
|
||||||
|
|
||||||
|
// Open session history file
|
||||||
|
if let Ok(mut file) = File::options().read(true).write(true).open(bucket_path.join(GENERIC_HISTORY)) {
|
||||||
|
let mut buffer_size = [0u8; 2];
|
||||||
|
|
||||||
|
// Update length
|
||||||
|
file.seek(SeekFrom::Start(0)).map_err(|_| ())?;
|
||||||
|
file.read_exact(&mut buffer_size).map_err(|_| ())?;
|
||||||
|
|
||||||
|
let size = unpack_u16(&buffer_size, &mut 0);
|
||||||
|
|
||||||
|
if size > 0 {
|
||||||
|
let new_size = size - 1;
|
||||||
|
|
||||||
|
file.seek(SeekFrom::Start(0)).map_err(|_| ())?;
|
||||||
|
file.write(&pack_u16(new_size)).map_err(|_| ())?;
|
||||||
|
file.set_len(2 + (2 * new_size as u64)).map_err(|_| ())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
} else { Err(()) }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn session_history_fetch(&mut self, id:u32) -> Result<Vec<Play>,()>
|
pub fn session_history_fetch(&mut self, id:u32) -> Result<Vec<Play>,()>
|
||||||
{
|
{
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
|
@ -3,7 +3,7 @@ span.c_dawn{color:#ffe082;}
|
|||||||
span.c_dusk{color:#f6a1bd;}
|
span.c_dusk{color:#f6a1bd;}
|
||||||
span.bold{font-weight:bold;}
|
span.bold{font-weight:bold;}
|
||||||
|
|
||||||
button#button-resign.warn {
|
button.warn {
|
||||||
background-color:#471414;
|
background-color:#471414;
|
||||||
color:#e0e0e0;
|
color:#e0e0e0;
|
||||||
}
|
}
|
||||||
|
@ -202,13 +202,13 @@ const INTERFACE = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(initial_hover != INTERFACE_DATA.hover) { INTERFACE.step(); }
|
if(initial_hover != INTERFACE_DATA.hover) { INTERFACE.game_step(); }
|
||||||
},
|
},
|
||||||
|
|
||||||
unhover() {
|
unhover() {
|
||||||
let redraw = (INTERFACE_DATA.hover !== null);
|
let redraw = (INTERFACE_DATA.hover !== null);
|
||||||
INTERFACE_DATA.hover = null;
|
INTERFACE_DATA.hover = null;
|
||||||
if(redraw) { INTERFACE.step(); }
|
if(redraw) { INTERFACE.game_step(); }
|
||||||
},
|
},
|
||||||
|
|
||||||
click(event) {
|
click(event) {
|
||||||
@ -326,7 +326,7 @@ const INTERFACE = {
|
|||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERFACE.step();
|
INTERFACE.game_step();
|
||||||
},
|
},
|
||||||
|
|
||||||
contextmenu() {
|
contextmenu() {
|
||||||
@ -341,7 +341,7 @@ const INTERFACE = {
|
|||||||
if(INTERFACE.Ui.match_select(INTERFACE_DATA.hover, INTERFACE_DATA.select)) {
|
if(INTERFACE.Ui.match_select(INTERFACE_DATA.hover, INTERFACE_DATA.select)) {
|
||||||
INTERFACE_DATA.select = null;
|
INTERFACE_DATA.select = null;
|
||||||
INTERFACE_DATA.alt_mode = false;
|
INTERFACE_DATA.alt_mode = false;
|
||||||
INTERFACE.step();
|
INTERFACE.game_step();
|
||||||
} else {
|
} else {
|
||||||
INTERFACE.click({button:0});
|
INTERFACE.click({button:0});
|
||||||
}
|
}
|
||||||
@ -381,6 +381,46 @@ const INTERFACE = {
|
|||||||
INTERFACE_DATA.Render.pool_offset = INTERFACE_DATA.Render.offset.x + Math.floor(INTERFACE.PoolOffset * gui_scale);
|
INTERFACE_DATA.Render.pool_offset = INTERFACE_DATA.Render.offset.x + Math.floor(INTERFACE.PoolOffset * gui_scale);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
game_step() {
|
||||||
|
if(INTERFACE_DATA === null) return;
|
||||||
|
|
||||||
|
if(GAME_DATA.turn == 0) {
|
||||||
|
INTERFACE_DATA.Ui.request_undo = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(INTERFACE_DATA.mode) {
|
||||||
|
case INTERFACE.Mode.Player: {
|
||||||
|
let b_resign = document.getElementById("button-resign");
|
||||||
|
if(GAME_DATA.state.code == 0 && (GAME_DATA.turn & 1) == INTERFACE_DATA.player) {
|
||||||
|
b_resign.removeAttribute("disabled");
|
||||||
|
} else {
|
||||||
|
b_resign.setAttribute("disabled", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
let b_undo = document.getElementById("button-undo");
|
||||||
|
if(GAME_DATA.turn == 0 || INTERFACE_DATA.Ui.request_undo == 1) {
|
||||||
|
b_undo.setAttribute("disabled", "");
|
||||||
|
b_undo.removeAttribute("class");
|
||||||
|
} else {
|
||||||
|
b_undo.removeAttribute("disabled");
|
||||||
|
|
||||||
|
if(INTERFACE_DATA.Ui.request_undo == 2) {
|
||||||
|
b_undo.setAttribute("class", "warn");
|
||||||
|
} else {
|
||||||
|
b_undo.removeAttribute("class");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case INTERFACE.Mode.Review: {
|
||||||
|
document.getElementById("indicator-turn").innerText = INTERFACE_DATA.Replay.turn + " / " + INTERFACE_DATA.Game.history.length;
|
||||||
|
document.getElementById("turn-slider").setAttribute("max", INTERFACE_DATA.Game.history.length);
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
INTERFACE.step();
|
||||||
|
},
|
||||||
|
|
||||||
step() {
|
step() {
|
||||||
if(INTERFACE_DATA === null) return;
|
if(INTERFACE_DATA === null) return;
|
||||||
|
|
||||||
@ -389,15 +429,6 @@ const INTERFACE = {
|
|||||||
if(INTERFACE_DATA.Timeout.draw === null) {
|
if(INTERFACE_DATA.Timeout.draw === null) {
|
||||||
INTERFACE.draw();
|
INTERFACE.draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(INTERFACE_DATA.mode == INTERFACE.Mode.Player) {
|
|
||||||
let b_resign = document.getElementById("button-resign");
|
|
||||||
if(GAME_DATA.state.code == 0 && (GAME_DATA.turn & 1) == INTERFACE_DATA.player) {
|
|
||||||
b_resign.removeAttribute("disabled");
|
|
||||||
} else {
|
|
||||||
b_resign.setAttribute("disabled", "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
draw() {
|
draw() {
|
||||||
@ -1154,7 +1185,7 @@ const INTERFACE = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
Ui: {
|
Ui: {
|
||||||
request_undo:false,
|
request_undo:0,
|
||||||
resign_warn:false,
|
resign_warn:false,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1202,7 +1233,7 @@ const INTERFACE = {
|
|||||||
|
|
||||||
switch(INTERFACE_DATA.mode) {
|
switch(INTERFACE_DATA.mode) {
|
||||||
case INTERFACE.Mode.Local: {
|
case INTERFACE.Mode.Local: {
|
||||||
INTERFACE.step();
|
INTERFACE.game_step();
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1246,11 +1277,29 @@ const INTERFACE = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
undo() {
|
undo() {
|
||||||
INTERFACE_DATA.Game.auto = null;
|
switch(INTERFACE_DATA.mode) {
|
||||||
if(INTERFACE_DATA.Game.history.length > 0) {
|
case INTERFACE.Mode.Local: {
|
||||||
INTERFACE_DATA.Replay.turn = INTERFACE_DATA.Game.history.length + 1;
|
INTERFACE_DATA.Game.auto = null;
|
||||||
INTERFACE_DATA.Game.history.pop();
|
if(INTERFACE_DATA.Game.history.length > 0) {
|
||||||
INTERFACE.replay_jump(INTERFACE_DATA.Game.history.length, false);
|
INTERFACE_DATA.Replay.turn = INTERFACE_DATA.Game.history.length + 1;
|
||||||
|
INTERFACE_DATA.Game.history.pop();
|
||||||
|
INTERFACE.replay_jump(INTERFACE_DATA.Game.history.length, false);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case INTERFACE.Mode.Player: {
|
||||||
|
let high = 0;
|
||||||
|
let low = GameMessage.Undo | (GAME_DATA.turn << 9);
|
||||||
|
|
||||||
|
MESSAGE_COMPOSE([
|
||||||
|
PACK.u16(OpCode.GameMessage),
|
||||||
|
PACK.u32(high),
|
||||||
|
PACK.u32(low),
|
||||||
|
]);
|
||||||
|
|
||||||
|
INTERFACE_DATA.Ui.request_undo = 1;
|
||||||
|
INTERFACE.game_step();
|
||||||
|
} break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1261,6 +1310,13 @@ const INTERFACE = {
|
|||||||
case OpCode.GameState: {
|
case OpCode.GameState: {
|
||||||
if(INTERFACE_DATA.mode == INTERFACE.Mode.Player) {
|
if(INTERFACE_DATA.mode == INTERFACE.Mode.Player) {
|
||||||
INTERFACE_DATA.player = data.player;
|
INTERFACE_DATA.player = data.player;
|
||||||
|
if(data.undo > 0) {
|
||||||
|
if((1 << INTERFACE_DATA.player) == data.undo) {
|
||||||
|
INTERFACE_DATA.Ui.request_undo = 1;
|
||||||
|
} else {
|
||||||
|
INTERFACE_DATA.Ui.request_undo = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERFACE_DATA.Game.history = data.history;
|
INTERFACE_DATA.Game.history = data.history;
|
||||||
@ -1269,6 +1325,7 @@ const INTERFACE = {
|
|||||||
INTERFACE_DATA.Session.Client.Dawn.online = data.dawn_online;
|
INTERFACE_DATA.Session.Client.Dawn.online = data.dawn_online;
|
||||||
INTERFACE_DATA.Session.Client.Dusk.online = data.dusk_online;
|
INTERFACE_DATA.Session.Client.Dusk.online = data.dusk_online;
|
||||||
INTERFACE_DATA.Session.Client.Spectators.count = data.spectators;
|
INTERFACE_DATA.Session.Client.Spectators.count = data.spectators;
|
||||||
|
|
||||||
|
|
||||||
if(INTERFACE_DATA.Game.history.length > 0) {
|
if(INTERFACE_DATA.Game.history.length > 0) {
|
||||||
if(INTERFACE_DATA.Replay.turn == 0) {
|
if(INTERFACE_DATA.Replay.turn == 0) {
|
||||||
@ -1319,23 +1376,26 @@ const INTERFACE = {
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
case GameMessage.Undo: {
|
case GameMessage.Undo: {
|
||||||
switch(data.state) {
|
if(data.state == 0) {
|
||||||
case 0: {
|
// Request undo
|
||||||
// Request undo
|
if(data.turn == INTERFACE_DATA.Game.history.length) {
|
||||||
if(data.turn == INTERFACE_DATA.Game.history.length) {
|
INTERFACE_DATA.Ui.request_undo = 2;
|
||||||
INTERFACE_DATA.Ui.request_undo = true;
|
INTERFACE.game_step();
|
||||||
}
|
}
|
||||||
} break;
|
} else {
|
||||||
case 1: {
|
// Perform undo
|
||||||
// Perform undo
|
INTERFACE_DATA.Ui.request_undo = 0;
|
||||||
INTERFACE.undo();
|
|
||||||
} break;
|
while(data.turn < INTERFACE_DATA.Game.history.length) {
|
||||||
|
INTERFACE_DATA.Game.history.pop();
|
||||||
|
}
|
||||||
|
INTERFACE.replay_last(false);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case GameMessage.Resign: {
|
case GameMessage.Resign: {
|
||||||
GAME_DATA.state.code = GAME.Const.State.Resign;
|
GAME_DATA.state.code = GAME.Const.State.Resign;
|
||||||
INTERFACE.step();
|
INTERFACE.game_step();
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case GameMessage.Reaction: {
|
case GameMessage.Reaction: {
|
||||||
@ -1379,7 +1439,7 @@ const INTERFACE = {
|
|||||||
case INTERFACE.Mode.Local: {
|
case INTERFACE.Mode.Local: {
|
||||||
INTERFACE.history_push(play, true);
|
INTERFACE.history_push(play, true);
|
||||||
|
|
||||||
INTERFACE.step();
|
INTERFACE.game_step();
|
||||||
|
|
||||||
if(INTERFACE_DATA.Game.auto !== null && INTERFACE_DATA.Game.auto == (GAME_DATA.turn & 1)) {
|
if(INTERFACE_DATA.Game.auto !== null && INTERFACE_DATA.Game.auto == (GAME_DATA.turn & 1)) {
|
||||||
setTimeout(INTERFACE.auto_play, 1000);
|
setTimeout(INTERFACE.auto_play, 1000);
|
||||||
@ -1410,12 +1470,12 @@ const INTERFACE = {
|
|||||||
|
|
||||||
rotate() {
|
rotate() {
|
||||||
INTERFACE_DATA.rotate ^= 1;
|
INTERFACE_DATA.rotate ^= 1;
|
||||||
INTERFACE.step();
|
INTERFACE.game_step();
|
||||||
},
|
},
|
||||||
|
|
||||||
mirror() {
|
mirror() {
|
||||||
INTERFACE_DATA.mirror = !INTERFACE_DATA.mirror;
|
INTERFACE_DATA.mirror = !INTERFACE_DATA.mirror;
|
||||||
INTERFACE.step();
|
INTERFACE.game_step();
|
||||||
},
|
},
|
||||||
|
|
||||||
resign() {
|
resign() {
|
||||||
@ -1449,13 +1509,13 @@ const INTERFACE = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
history_push(play, animate=false) {
|
history_push(play, animate=false) {
|
||||||
|
INTERFACE_DATA.Ui.request_undo = 0;
|
||||||
|
|
||||||
INTERFACE_DATA.Game.history.push(play);
|
INTERFACE_DATA.Game.history.push(play);
|
||||||
if(INTERFACE_DATA.mode == INTERFACE.Mode.Review) {
|
|
||||||
document.getElementById("indicator-turn").innerText = INTERFACE_DATA.Replay.turn + " / " + INTERFACE_DATA.Game.history.length;
|
|
||||||
document.getElementById("turn-slider").setAttribute("max", INTERFACE_DATA.Game.history.length);
|
|
||||||
}
|
|
||||||
if(INTERFACE_DATA.Replay.turn == INTERFACE_DATA.Game.history.length - 1) {
|
if(INTERFACE_DATA.Replay.turn == INTERFACE_DATA.Game.history.length - 1) {
|
||||||
INTERFACE.replay_next(animate);
|
INTERFACE.replay_next(animate);
|
||||||
|
} else {
|
||||||
|
INTERFACE.game_step();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1519,7 +1579,7 @@ const INTERFACE = {
|
|||||||
document.getElementById("turn-slider").value = INTERFACE_DATA.Replay.turn;
|
document.getElementById("turn-slider").value = INTERFACE_DATA.Replay.turn;
|
||||||
}
|
}
|
||||||
|
|
||||||
INTERFACE.step();
|
INTERFACE.game_step();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
replay_first() {
|
replay_first() {
|
||||||
@ -1562,7 +1622,7 @@ const INTERFACE = {
|
|||||||
} else {
|
} else {
|
||||||
INTERFACE_DATA.Game.auto = null;
|
INTERFACE_DATA.Game.auto = null;
|
||||||
}
|
}
|
||||||
INTERFACE.step();
|
INTERFACE.game_step();
|
||||||
},
|
},
|
||||||
|
|
||||||
auto_play() {
|
auto_play() {
|
||||||
|
@ -621,6 +621,10 @@ const SCENES = {
|
|||||||
// Bottom Buttons
|
// Bottom Buttons
|
||||||
let buttons_bottom = [ ];
|
let buttons_bottom = [ ];
|
||||||
if(data.mode == INTERFACE.Mode.Player) {
|
if(data.mode == INTERFACE.Mode.Player) {
|
||||||
|
let button_undo = UI.button(LANG("undo"), () => { INTERFACE.undo(); });
|
||||||
|
button_undo.setAttribute("id", "button-undo");
|
||||||
|
buttons_bottom.push(button_undo);
|
||||||
|
|
||||||
let button_resign = UI.button(LANG("resign"), () => { INTERFACE.resign(); });
|
let button_resign = UI.button(LANG("resign"), () => { INTERFACE.resign(); });
|
||||||
button_resign.setAttribute("id", "button-resign");
|
button_resign.setAttribute("id", "button-resign");
|
||||||
buttons_bottom.push(button_resign);
|
buttons_bottom.push(button_resign);
|
||||||
@ -651,7 +655,6 @@ const SCENES = {
|
|||||||
callback_resume = callback_resume.bind({
|
callback_resume = callback_resume.bind({
|
||||||
token: data.token,
|
token: data.token,
|
||||||
});
|
});
|
||||||
|
|
||||||
buttons_top.push(UI.button(LANG("resume"), callback_resume));
|
buttons_top.push(UI.button(LANG("resume"), callback_resume));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -263,11 +263,15 @@ function MESSAGE(event) {
|
|||||||
status:0,
|
status:0,
|
||||||
token:new Uint8Array(8),
|
token:new Uint8Array(8),
|
||||||
player:2,
|
player:2,
|
||||||
|
|
||||||
|
undo:0,
|
||||||
|
|
||||||
dawn:"",
|
dawn:"",
|
||||||
dusk:"",
|
dusk:"",
|
||||||
dawn_online:false,
|
dawn_online:false,
|
||||||
dusk_online:false,
|
dusk_online:false,
|
||||||
spectators:0,
|
spectators:0,
|
||||||
|
|
||||||
history:[ ],
|
history:[ ],
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -284,10 +288,12 @@ function MESSAGE(event) {
|
|||||||
index = result.index;
|
index = result.index;
|
||||||
let flags = result.data;
|
let flags = result.data;
|
||||||
|
|
||||||
data.player = flags & 0x3;
|
data.player = flags & 3;
|
||||||
data.dawn_online = (flags >> 2) & 1;
|
data.dawn_online = (flags >> 2) & 1;
|
||||||
data.dusk_online = (flags >> 3) & 1;
|
data.dusk_online = (flags >> 3) & 1;
|
||||||
|
|
||||||
|
data.undo = (flags >> 8) & 3;
|
||||||
|
|
||||||
result = UNPACK.u32(bytes, index);
|
result = UNPACK.u32(bytes, index);
|
||||||
index = result.index;
|
index = result.index;
|
||||||
data.spectators = result.data;
|
data.spectators = result.data;
|
||||||
@ -365,7 +371,8 @@ function MESSAGE(event) {
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
case GameMessage.Undo: {
|
case GameMessage.Undo: {
|
||||||
data.state = dat & 0x3;
|
data.state = dat & 0x1;
|
||||||
|
data.turn = (dat >> 1) & 0xFFFF;
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case GameMessage.Resign: { } break;
|
case GameMessage.Resign: { } break;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user