Add practice mode and last play hints.

This commit is contained in:
yukirij 2024-08-17 14:50:53 -07:00
parent ac3d01b0d7
commit c5e8e6aa2b
8 changed files with 100 additions and 30 deletions

View File

@ -597,6 +597,9 @@ fn generate_gamestate(app:&App, session:&Session) -> protocol::PacketGameStateRe
response.player = 2; response.player = 2;
response.turn = session.game.turn; response.turn = session.game.turn;
if session.game.history.len() > 0 {
response.play = session.game.history[session.game.history.len() - 1];
}
// Get Dawn handle // Get Dawn handle
if let Some(id) = session.p_dawn.user { if let Some(id) = session.p_dawn.user {

View File

@ -3,7 +3,7 @@ use crate::{
util::pack::pack_u16, util::pack::pack_u16,
}; };
use game::game::Pool; use game::{game::Pool, history::Play};
use super::Packet; use super::Packet;
@ -65,6 +65,7 @@ pub struct PacketGameStateResponse {
pub status:u16, pub status:u16,
pub turn:u16, pub turn:u16,
pub player:u8, pub player:u8,
pub play:Play,
pub dawn_handle:String, pub dawn_handle:String,
pub dusk_handle:String, pub dusk_handle:String,
pub dawn_pool:Pool, pub dawn_pool:Pool,
@ -78,6 +79,7 @@ impl PacketGameStateResponse {
status:0, status:0,
turn:0, turn:0,
player:0, player:0,
play:Play::new(),
dawn_handle:String::new(), dawn_handle:String::new(),
dusk_handle:String::new(), dusk_handle:String::new(),
dawn_pool:Pool::default(), dawn_pool:Pool::default(),
@ -94,6 +96,11 @@ impl Packet for PacketGameStateResponse {
let mut flags = 0u16; let mut flags = 0u16;
flags |= self.player as u16; flags |= self.player as u16;
let mut play = 0;
play |= self.play.source as u16;
play |= (self.play.from as u16) << 1;
play |= (self.play.to as u16) << 7;
let mut piece_bytes = Vec::new(); let mut piece_bytes = Vec::new();
for piece in &self.pieces { for piece in &self.pieces {
let piece_data: u16 = piece.valid as u16 let piece_data: u16 = piece.valid as u16
@ -124,6 +131,7 @@ impl Packet for PacketGameStateResponse {
[ [
pack_u16(self.status), pack_u16(self.status),
pack_u16(flags), pack_u16(flags),
pack_u16(play),
pack_u16(self.turn), pack_u16(self.turn),
pack_u16(self.dawn_handle.len() as u16), pack_u16(self.dawn_handle.len() as u16),
self.dawn_handle.as_bytes().to_vec(), self.dawn_handle.as_bytes().to_vec(),

View File

@ -271,9 +271,7 @@ impl FileSystem {
to:((data >> 7) & 0x3F) as u8, to:((data >> 7) & 0x3F) as u8,
}); });
} }
println!("len {}", result.len());
Ok(result) Ok(result)
} else { Err(()) } } else { Err(()) }
} }

View File

@ -5,7 +5,7 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="application-name" content="Omen"> <meta name="application-name" content="Omen">
<meta name="description" content="Abstract strategy game mixing chess and shogi on a hexagon grid."> <meta name="description" content="Abstract strategy wargame.">
<link rel="icon" href="/favicon.png"> <link rel="icon" href="/favicon.png">
<link rel="stylesheet" href=".css"> <link rel="stylesheet" href=".css">
<script src=".js"></script> <script src=".js"></script>

View File

@ -189,13 +189,24 @@ const INTERFACE = {
} }
if(is_valid) { if(is_valid) {
let move_data = INTERFACE_DATA.select.source | (INTERFACE_DATA.select.tile << 1) | (INTERFACE_DATA.hover.tile << 7); if(INTERFACE_DATA.online) {
MESSAGE_COMPOSE([ let move_data = INTERFACE_DATA.select.source | (INTERFACE_DATA.select.tile << 1) | (INTERFACE_DATA.hover.tile << 7);
PACK.u16(OpCode.GamePlay), MESSAGE_COMPOSE([
PACK.u16(0), PACK.u16(OpCode.GamePlay),
PACK.u16(GAME_DATA.turn), PACK.u16(0),
PACK.u16(move_data), PACK.u16(GAME_DATA.turn),
]); PACK.u16(move_data),
]);
} else {
let play = new GAME.Play(INTERFACE_DATA.select.source, INTERFACE_DATA.select.tile, INTERFACE_DATA.hover.tile);
INTERFACE_DATA.play = play;
GAME_DATA.process(play);
INTERFACE_DATA.player = +(!INTERFACE_DATA.player);
INTERFACE_DATA.rotate = +(!INTERFACE_DATA.rotate);
INTERFACE.draw();
}
INTERFACE_DATA.select = null; INTERFACE_DATA.select = null;
} else { } else {
INTERFACE_DATA.select = null; INTERFACE_DATA.select = null;
@ -339,7 +350,9 @@ const INTERFACE = {
case 1: ctx.fillStyle = INTERFACE.Color.TileLight; break; case 1: ctx.fillStyle = INTERFACE.Color.TileLight; break;
case 2: ctx.fillStyle = INTERFACE.Color.TileDark; break; case 2: ctx.fillStyle = INTERFACE.Color.TileDark; break;
} }
if(GAME_DATA.state.check && piece !== null && piece.piece == GAME.Const.PieceId.Omen && piece.player == (GAME_DATA.turn & 1)) { if(GAME_DATA.turn > 0 && (INTERFACE_DATA.play.to == i || (INTERFACE_DATA.play.source == 0 && INTERFACE_DATA.play.from == i))) {
ctx.fillStyle = INTERFACE.Color.HintPlay;
} else if(GAME_DATA.state.check && piece !== null && piece.piece == GAME.Const.PieceId.Omen && piece.player == (GAME_DATA.turn & 1)) {
ctx.fillStyle = INTERFACE.Color.HintCheck; ctx.fillStyle = INTERFACE.Color.HintCheck;
} }
switch(tile_state) { switch(tile_state) {
@ -657,16 +670,25 @@ const INTERFACE = {
}, },
}, },
init(data) { init(data, online) {
GAME.init(); GAME.init();
let token = null;
let player = 0;
if(data !== null) {
token = data.token;
player = data.mode;
}
INTERFACE_DATA = { INTERFACE_DATA = {
token:data.token, online: online,
token: token,
canvas: document.getElementById("game"), canvas: document.getElementById("game"),
context: null, context: null,
player: data.mode, player: player,
rotate: 0, rotate: 0,
hover: null, hover: null,
@ -674,6 +696,7 @@ const INTERFACE = {
handles: [null, null], handles: [null, null],
board_state: [ ], board_state: [ ],
play: null,
Ui: { Ui: {
scale: 0, scale: 0,
@ -696,17 +719,23 @@ const INTERFACE = {
canvas.addEventListener("mousedown", INTERFACE.click); canvas.addEventListener("mousedown", INTERFACE.click);
window.addEventListener("resize", INTERFACE.draw); window.addEventListener("resize", INTERFACE.draw);
MESSAGE_COMPOSE([ if(INTERFACE_DATA.online) {
PACK.u16(OpCode.GameState), MESSAGE_COMPOSE([
INTERFACE_DATA.token, PACK.u16(OpCode.GameState),
]); INTERFACE_DATA.token,
]);
} else {
INTERFACE.draw();
}
} }
}, },
uninit() { uninit() {
MESSAGE_COMPOSE([ if(INTERFACE_DATA.online) {
PACK.u16(OpCode.SessionLeave), MESSAGE_COMPOSE([
]); PACK.u16(OpCode.SessionLeave),
]);
}
if(INTERFACE_DATA !== null) { if(INTERFACE_DATA !== null) {
MAIN.removeChild(INTERFACE_DATA.canvas); MAIN.removeChild(INTERFACE_DATA.canvas);
@ -717,9 +746,9 @@ const INTERFACE = {
message(code, data) { message(code, data) {
switch(code) { switch(code) {
case OpCode.GameState: { case OpCode.GameState: {
console.log(data.player);
INTERFACE_DATA.player = data.player; INTERFACE_DATA.player = data.player;
GAME_DATA.turn = data.turn; GAME_DATA.turn = data.turn;
INTERFACE_DATA.play = data.play;
if(data.dawn.length > 0) { INTERFACE_DATA.handles[0] = data.dawn; } if(data.dawn.length > 0) { INTERFACE_DATA.handles[0] = data.dawn; }
if(data.dusk.length > 0) { INTERFACE_DATA.handles[1] = data.dusk; } if(data.dusk.length > 0) { INTERFACE_DATA.handles[1] = data.dusk; }
@ -747,7 +776,8 @@ const INTERFACE = {
case OpCode.GamePlay: { case OpCode.GamePlay: {
if(data.status == Status.Ok && data.turn == GAME_DATA.turn) { if(data.status == Status.Ok && data.turn == GAME_DATA.turn) {
GAME_DATA.process(data.move); INTERFACE_DATA.play = data.play;
GAME_DATA.process(data.play);
INTERFACE.draw(); INTERFACE.draw();
} }
} break; } break;

View File

@ -488,7 +488,7 @@ const SCENES = {
canvas.setAttribute("id", "game"); canvas.setAttribute("id", "game");
MAIN.appendChild(canvas); MAIN.appendChild(canvas);
INTERFACE.init(data); INTERFACE.init(data, true);
return true; return true;
}, },
@ -510,6 +510,28 @@ const SCENES = {
} }
}, },
}, },
GamePractice:{
load(data) {
let buttons_bottom = [ ];
buttons_bottom.push(UI.button("Back", () => { LOAD(SCENES.Browse) }));
UI.nav([
UI.button("Rotate", () => { INTERFACE.rotate(); }),
], buttons_bottom);
let canvas = document.createElement("canvas");
canvas.setAttribute("id", "game");
MAIN.appendChild(canvas);
INTERFACE.init(data, false);
return true;
},
unload() {
INTERFACE.uninit();
},
},
}; };
function LOAD(scene, data=null) { function LOAD(scene, data=null) {

View File

@ -207,6 +207,7 @@ function MESSAGE(event) {
status:0, status:0,
player:2, player:2,
turn:0, turn:0,
play:new GAME.Play(),
dawn:"", dawn:"",
dusk:"", dusk:"",
pool_dawn:[ ], pool_dawn:[ ],
@ -226,6 +227,13 @@ function MESSAGE(event) {
data.player = flags & 0x3; data.player = flags & 0x3;
// Last Play
result = UNPACK.u16(bytes, index);
index = result.index;
data.play.source = result.data & 1;
data.play.from = (result.data >> 1) & 0x3F;
data.play.to = (result.data >> 7) & 0x3F;
// Turn // Turn
result = UNPACK.u16(bytes, index); result = UNPACK.u16(bytes, index);
index = result.index; index = result.index;
@ -290,7 +298,7 @@ function MESSAGE(event) {
data = { data = {
status:0, status:0,
move:new GAME.Play(0, 0, 0), play:new GAME.Play(0, 0, 0),
}; };
// Status // Status
@ -306,9 +314,9 @@ function MESSAGE(event) {
// Play description // Play description
result = UNPACK.u16(bytes, index); result = UNPACK.u16(bytes, index);
index = result.index; index = result.index;
data.move.source = result.data & 1; data.play.source = result.data & 1;
data.move.from = (result.data >> 1) & 0x3F; data.play.from = (result.data >> 1) & 0x3F;
data.move.to = (result.data >> 7) & 0x3F; data.play.to = (result.data >> 7) & 0x3F;
} break; } break;
default: default:

View File

@ -121,6 +121,7 @@ const UI = {
} }
top.push(UI.button("Live", () => { LOAD(SCENES.Live); })); top.push(UI.button("Live", () => { LOAD(SCENES.Live); }));
top.push(UI.button("History", () => { LOAD(SCENES.History); })); top.push(UI.button("History", () => { LOAD(SCENES.History); }));
top.push(UI.button("Practice", () => { LOAD(SCENES.GamePractice); }));
top.push(UI.button("Guide", () => { LOAD(SCENES.Guide); })); top.push(UI.button("Guide", () => { LOAD(SCENES.Guide); }));
top.push(UI.button("About", () => { LOAD(SCENES.About); })); top.push(UI.button("About", () => { LOAD(SCENES.About); }));