Rework game messages; change images to raw vector graphics.

This commit is contained in:
yukirij 2024-10-04 20:17:15 -07:00
parent fb2f8534a8
commit 0726436439
54 changed files with 969 additions and 1146 deletions

View File

@ -77,7 +77,7 @@ impl Game {
// Move piece on board.
if match play.source {
0 | 3 => {
0 | 2 => {
if let Some(pid) = self.board.tiles[play.from as usize].piece {
if let Some(mut piece) = self.board.pieces[pid as usize] {
let mut swap = false;
@ -144,12 +144,6 @@ impl Game {
} else { false }
}
// Player retired.
2 => {
self.status = GameStatus::Resign;
true
}
_ => false,
} {
self.history.push(*play);

View File

@ -21,6 +21,7 @@ rust-argon2 = "2.1.0"
ring = "0.17.8"
const_format = "0.2.32"
markdown = "0.3.0"
usvg = "0.44.0"
game = { path = "../game" }

View File

@ -87,6 +87,10 @@ impl App {
// Load session history
if let Ok(history) = filesystem.session_history_fetch(id as u32) {
let mut history = history;
for play in &mut history {
if play.source == 3 { play.source = 2; }
}
session.game.apply_history(&history).ok();
}
@ -193,9 +197,9 @@ impl App {
)).await.ok();
}
QRPacketData::QGamePlay(response) => {
QRPacketData::GameMessage(response) => {
socket.send(Message::Binary(
encode_response(CODE_GAME_PLAY, response.encode())
encode_response(CODE_GAME_MESSAGE, response.encode())
)).await.ok();
}

View File

@ -22,6 +22,8 @@ pub struct Session {
pub time:u64,
pub chain_id:usize,
pub undo:Option<u8>,
}
impl Session {
pub fn get_connections(&self) -> Vec<(u32, u8)>

View File

@ -1,4 +1,5 @@
use std::net::SocketAddr;
use std::path::Path;
use bus::Bus;
@ -159,68 +160,71 @@ async fn main()
}
}
// Load image assets
let mut js_asset_data = String::from("const GAME_ASSET = { Image: {");
let asset_path = Path::new("www/asset/");
for name in [
"Promote",
"Militia",
"Lance",
"Knight",
"Tower",
"Castle",
"Dragon",
"Behemoth",
"Heart",
] {
if let Ok(output) = util::imager::load(asset_path.join(&format!("{}.svg", name))) {
js_asset_data += &format!("{}:{},", name, output);
} else {
println!("error: failed to load asset: {}", name);
}
}
js_asset_data += "} };";
// Initialize HTTPS service.
match b_main.connect() {
Ok(bus) => {
let cache = WebCache::new();
cache.cache_file("text/html", "/.html", "www/.html").ok();
cache.cache("text/html", "/.html", &[
WebCache::file("www/.html"),
]).ok();
cache.cache_whitespace_minimize("/.html").ok();
cache.cache_file_group("text/css", "/.css", &[
"www/css/main.css",
"www/css/ui.css",
"www/css/form.css",
"www/css/game.css",
"www/css/util.css",
cache.cache("text/css", "/.css", &[
WebCache::file("www/css/main.css"),
WebCache::file("www/css/ui.css"),
WebCache::file("www/css/form.css"),
WebCache::file("www/css/game.css"),
WebCache::file("www/css/util.css"),
]).ok();
cache.cache_file_group("text/javascript", "/.js", &[
"www/js/const.js",
"www/js/language.js",
"www/js/util.js",
"www/js/badge.js",
"www/js/game_asset.js",
"www/js/game.js",
"www/js/interface.js",
"www/js/ui.js",
"www/js/scene.js",
"www/js/system.js",
"www/js/main.js",
cache.cache("text/javascript", "/.js", &[
WebCache::file("www/js/const.js"),
WebCache::file("www/js/language.js"),
WebCache::file("www/js/util.js"),
WebCache::file("www/js/badge.js"),
WebCache::file("www/js/game_asset.js"),
WebCache::string(&js_asset_data),
WebCache::file("www/js/game.js"),
WebCache::file("www/js/interface.js"),
WebCache::file("www/js/ui.js"),
WebCache::file("www/js/scene.js"),
WebCache::file("www/js/system.js"),
WebCache::file("www/js/main.js"),
]).ok();
cache.cache("image/png", "/favicon.png", &[
WebCache::file("www/asset/favicon.png"),
]).ok();
cache.cache("image/png", "/favicon_notify.png", &[
WebCache::file("www/asset/favicon_notify.png"),
]).ok();
cache.cache_file("image/png", "/favicon.png", "www/asset/favicon.png").ok();
cache.cache_file("image/png", "/favicon_notify.png", "www/asset/favicon_notify.png").ok();
let asset_path = std::path::Path::new("www/asset");
for asset in [
"promote.svg",
"heart_dawn.svg",
"behemoth_dawn.svg",
"dragon_dawn.svg",
"castle_dawn.svg",
"tower_dawn.svg",
"lance_dawn.svg",
"knight_dawn.svg",
"militia_dawn.svg",
"heart_dusk.svg",
"behemoth_dusk.svg",
"dragon_dusk.svg",
"castle_dusk.svg",
"tower_dusk.svg",
"lance_dusk.svg",
"knight_dusk.svg",
"militia_dusk.svg",
] {
if cache.cache_file("image/svg+xml", &format!("/asset/{}", asset), asset_path.join(asset)).is_err() {
println!("error: failed to load: {}", asset);
}
}
let about_path = std::path::Path::new("www/pages/about");
for doc in [
"main",
] {
if cache.cache_md(&format!("/about/{}.html", doc), about_path.join(format!("{}.md", doc))).is_err() {
if cache.cache("text/html", &format!("/about/{}.html", doc), &[
WebCache::markdown(about_path.join(format!("{}.md", doc)))
]).is_err() {
println!("error: failed to load: {}", doc);
}
}
@ -231,7 +235,9 @@ async fn main()
"pieces",
"interface",
] {
if cache.cache_md(&format!("/guide/{}.html", doc), guide_path.join(format!("{}.md", doc))).is_err() {
if cache.cache("text/html", &format!("/guide/{}.html", doc), &[
WebCache::markdown(guide_path.join(format!("{}.md", doc))),
]).is_err() {
println!("error: failed to load: {}", doc);
}
}

View File

@ -1,4 +1,5 @@
use bus::Bus;
use game::history::Play;
use crate::{
config,
app::{
@ -554,10 +555,8 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
for (cid, _) in session.get_connections() {
packets.push(QRPacket::new(
cid,
QRPacketData::QGamePlay(PacketGamePlay {
status: STATUS_OK,
turn: session.game.turn,
play,
QRPacketData::GameMessage(PacketGameMessage {
data: GameMessageData::Retire,
}),
));
}
@ -607,34 +606,47 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
Some(QRPacket::new(qr.id, QRPacketData::RGameState(response)))
}
// GamePlay
QRPacketData::QGamePlay(mut request) => {
println!("Request: Game Play");
// GameMessage
QRPacketData::GameMessage(request) => {
println!("Request: Game Message");
request.status = STATUS_ERROR;
let mut packets = Vec::<QRPacket>::new();
if let Some(sid) = session_id {
if let Some(session) = app.sessions.get_mut(&sid) {
match request.data {
GameMessageData::PlayMove(turn, from, to)
| GameMessageData::PlayDrop(turn, from, to)
| GameMessageData::PlayAlt(turn, from, to)
=> {
println!("HERE");
if !session.game.is_complete() {
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 turn == session.game.turn {
let play = Play {
source: match request.data {
GameMessageData::PlayMove(..) => 0,
GameMessageData::PlayDrop(..) => 1,
GameMessageData::PlayAlt(..) => 2,
_ => 0,
},
from, to,
};
// Check validation of play
if request.turn == session.game.turn {
println!("play {} {} {}", play.source, play.from, play.to);
// Update internal representation
if session.game.process(&request.play).is_ok() {
request.status = STATUS_OK;
if session.game.process(&play).is_ok() {
// Commit play to history
app.filesystem.session_history_push(session.id, play).ok();
// Save play to game history.
app.filesystem.session_history_push(session.id, request.play).ok();
// Forward play to all clients
// Forward messsage to all clients
for (cid, _) in session.get_connections() {
packets.push(QRPacket::new(
cid,
QRPacketData::QGamePlay(request.clone())
QRPacketData::GameMessage(request.clone())
));
}
@ -646,19 +658,62 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
}
}
}
GameMessageData::Undo(turn, _) => {
if !session.game.is_complete() {
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 turn == session.game.turn {
// Request or commit undo
}
}
}
}
if request.status != STATUS_ERROR {
GameMessageData::Retire => {
if !session.game.is_complete() {
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) {
// Forward messsage to all clients
for (cid, _) in session.get_connections() {
packets.push(QRPacket::new(
cid,
QRPacketData::GameMessage(request.clone())
));
}
}
}
}
GameMessageData::Reaction(_) => {
// Forward messsage to all clients
for (cid, _) in session.get_connections() {
packets.push(QRPacket::new(
cid,
QRPacketData::GameMessage(request.clone())
));
}
}
_ => { }
}
}
}
match request.data {
GameMessageData::Error => {
Some(QRPacket::new(qr.id, QRPacketData::GameMessage(request)))
}
_ => {
for packet in packets {
app.send_response(packet).await;
}
// Updates will have already been sent, so nothing is needed here.
// Updates already sent; nothing to do here.
Some(QRPacket::new(qr.id, QRPacketData::None))
} else {
// Return error status.
Some(QRPacket::new(qr.id, QRPacketData::QGamePlay(request)))
}
}
}
@ -745,6 +800,8 @@ pub async fn thread_system(mut app:App, bus:Bus<protocol::QRPacket>)
connections:Vec::new(),
time:std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs() as u64,
chain_id,
undo:None,
};
session.game.init();

View File

@ -156,11 +156,11 @@ pub async fn handle_ws(ws:WebSocketStream<TokioIo<Upgraded>>, args:HttpServiceAr
Err(_) => { println!("error: packet decode failed."); }
}
CODE_GAME_PLAY => match PacketGamePlay::decode(&data, &mut index) {
CODE_GAME_MESSAGE => match PacketGameMessage::decode(&data, &mut index) {
Ok(packet) => {
args.bus.send(
bus_ds,
QRPacket::new(conn_id, QRPacketData::QGamePlay(packet))
QRPacket::new(conn_id, QRPacketData::GameMessage(packet))
).ok();
}
Err(_) => { println!("error: packet decode failed."); }

View File

@ -38,7 +38,7 @@ pub const CODE_SESSION_RETIRE :u16 = 0x002E;
pub const CODE_SESSION_LEAVE :u16 = 0x002F;
pub const CODE_GAME_STATE :u16 = 0x0030;
pub const CODE_GAME_PLAY :u16 = 0x0031;
pub const CODE_GAME_MESSAGE :u16 = 0x0031;
pub const CODE_CHALLENGE :u16 = 0x0060;
pub const CODE_CHALLENGE_ANSWER :u16 = 0x0061;
@ -47,3 +47,17 @@ pub const CODE_CHALLENGE_LIST :u16 = 0x0062;
pub const CODE_USER_LIST :u16 = 0x0100;
//pub const CODE_USER_AWAIT_GET :u16 = 0x0110;
//pub const CODE_USER_AWAIT_SET :u16 = 0x0111;
/*
** Game Messages
*/
pub const GMSG_ERROR :u8 = 0x00;
pub const GMSG_PLAY_MOVE :u8 = 0x01;
pub const GMSG_PLAY_DROP :u8 = 0x02;
pub const GMSG_PLAY_ALT :u8 = 0x03;
pub const GMSG_ONLINE :u8 = 0x08;
pub const GMSG_UNDO :u8 = 0x10;
pub const GMSG_RETIRE :u8 = 0x11;
pub const GMSG_REACTION :u8 = 0x20;

View File

@ -42,7 +42,7 @@ pub enum QRPacketData {
QGameState(PacketGameState),
RGameState(PacketGameStateResponse),
QGamePlay(PacketGamePlay),
GameMessage(PacketGameMessage),
QChallenge(PacketChallenge),

View File

@ -0,0 +1,128 @@
use crate::{
util::pack::{
pack_u64,
unpack_u64,
},
protocol::*,
};
use super::Packet;
#[derive(Clone, Copy)]
pub enum GameMessageData {
Error,
PlayMove(u16, u8, u8),
PlayDrop(u16, u8, u8),
PlayAlt(u16, u8, u8),
Online(u8, u32),
Undo(u16, u8),
Retire,
Reaction(u16),
}
#[derive(Clone)]
pub struct PacketGameMessage {
pub data:GameMessageData,
}
impl PacketGameMessage {
pub fn new() -> Self
{
Self {
data:GameMessageData::Error,
}
}
}
impl Packet for PacketGameMessage {
type Data = Self;
fn decode(data:&Vec<u8>, index:&mut usize) -> Result<Self::Data, ()>
{
let mut result = Self::new();
if data.len() - *index >= 8 {
let data = unpack_u64(data, index);
result.data = match (data & 0xFF) as u8 {
GMSG_PLAY_MOVE => GameMessageData::PlayMove(
((data >> 8) & 0xFFFF) as u16,
((data >> 24) & 0x3F) as u8,
((data >> 30) & 0x3F) as u8,
),
GMSG_PLAY_DROP => GameMessageData::PlayDrop(
((data >> 8) & 0xFFFF) as u16,
((data >> 24) & 0x3F) as u8,
((data >> 30) & 0x3F) as u8,
),
GMSG_PLAY_ALT => GameMessageData::PlayAlt(
((data >> 8) & 0xFFFF) as u16,
((data >> 24) & 0x3F) as u8,
((data >> 30) & 0x3F) as u8,
),
GMSG_UNDO => GameMessageData::Undo(
((data >> 8) & 0xFFFF) as u16,
((data >> 24) & 0x1) as u8,
),
GMSG_RETIRE => GameMessageData::Retire,
GMSG_REACTION => GameMessageData::Reaction(
((data >> 8) & 0xFFFF) as u16,
),
_ => GameMessageData::Error,
}
}
Ok(result)
}
fn encode(&self) -> Vec<u8>
{
pack_u64(match self.data {
GameMessageData::PlayMove(turn, from, to) => {
GMSG_PLAY_MOVE as u64
| ((turn as u64) << 8)
| ((from as u64) << 24)
| ((to as u64) << 30)
}
GameMessageData::PlayDrop(turn, piece, to) => {
GMSG_PLAY_DROP as u64
| ((turn as u64) << 8)
| ((piece as u64) << 24)
| ((to as u64) << 30)
}
GameMessageData::PlayAlt(turn, from, to) => {
GMSG_PLAY_ALT as u64
| ((turn as u64) << 8)
| ((from as u64) << 24)
| ((to as u64) << 30)
}
GameMessageData::Online(client, state) => {
GMSG_PLAY_ALT as u64
| ((client as u64) << 8)
| ((state as u64) << 10)
}
GameMessageData::Undo(turn, state) => {
GMSG_UNDO as u64
| ((turn as u64) << 8)
| ((state as u64) << 24)
}
GameMessageData::Retire => {
GMSG_RETIRE as u64
}
GameMessageData::Reaction(index) => {
GMSG_REACTION as u64
| ((index as u64) << 8)
}
_ => { 0 }
})
}
}

View File

@ -1,58 +0,0 @@
use crate::util::pack::{
pack_u16,
unpack_u16,
};
use game::{
history::Play,
util::mask,
};
use super::Packet;
#[derive(Clone)]
pub struct PacketGamePlay {
pub status:u16,
pub turn:u16,
pub play:Play,
}
impl PacketGamePlay {
pub fn new() -> Self
{
Self {
status:0,
turn:0,
play:Play::new(),
}
}
}
impl Packet for PacketGamePlay {
type Data = Self;
fn decode(data:&Vec<u8>, index:&mut usize) -> Result<Self::Data, ()>
{
let mut result = Self::new();
result.status = unpack_u16(data, index);
result.turn = unpack_u16(data, index);
let play = unpack_u16(data, index) as u32;
result.play.source = (play & mask(4, 0)) as u8;
result.play.from = ((play & mask(6, 4)) >> 4) as u8;
result.play.to = ((play & mask(6, 10)) >> 10) as u8;
Ok(result)
}
fn encode(&self) -> Vec<u8>
{
let mut data = 0;
data |= self.play.source as u16;
data |= (self.play.from as u16) << 4;
data |= (self.play.to as u16) << 10;
[
pack_u16(self.status),
pack_u16(self.turn),
pack_u16(data),
].concat()
}
}

View File

@ -13,7 +13,7 @@ mod session_view; pub use session_view::*;
mod session_retire; pub use session_retire::*;
mod game_state; pub use game_state::*;
mod game_play; pub use game_play::*;
mod game_message; pub use game_message::*;
//mod game_history; pub use game_history::*;
mod challenge; pub use challenge::*;

View File

@ -9,6 +9,14 @@ use trie::Trie;
use crate::util::string::minimize_whitespace;
#[derive(Clone)]
pub enum Source {
Raw(Vec<u8>),
String(String),
File(std::path::PathBuf),
MarkdownFile(std::path::PathBuf),
}
#[derive(Clone)]
pub struct CacheData {
pub mime:String,
@ -46,55 +54,40 @@ impl WebCache {
}
}
pub fn cache(&self, mime:&str, object:&str, data:Vec<u8>)
{
match self.data.write() {
Ok(mut writer) => {
writer.objects.set(object.as_bytes(), CacheData {
mime:mime.to_string(),
data,
});
},
Err(_) => { },
}
}
pub fn cache_file<P :AsRef<Path>>(&self, mime:&str, object:&str, path:P) -> Result<(),()>
pub fn cache(&self, mime:&str, object:&str, sources:&[Source]) -> Result<(),()>
{
match self.data.write() {
Ok(mut writer) => {
let mut data = Vec::new();
if let Ok(mut file) = File::open(path) {
file.read_to_end(&mut data).ok();
for source in sources {
match source {
Source::Raw(raw) => {
data.append(&mut raw.clone());
}
Source::String(text) => {
data.append(&mut text.as_bytes().to_vec());
}
Source::File(path) => {
let mut file_data = Vec::new();
let mut file = File::open(path).map_err(|_| ())?;
file.read_to_end(&mut file_data).ok();
data.append(&mut file_data);
}
Source::MarkdownFile(path) => {
let mut text = markdown::file_to_html(&path.as_ref()).map_err(|_| ())?;
text = text.trim().lines().map(|line| line.trim().to_string()).collect();
data.append(&mut text.as_bytes().to_vec());
}
}
}
writer.objects.set(object.as_bytes(), CacheData {
mime:mime.to_string(),
data,
});
Ok(())
} else { Err(()) }
}
Err(_) => Err(()),
}
}
pub fn cache_md<P :AsRef<Path>>(&self, object:&str, path:P) -> Result<(),()>
{
match self.data.write() {
Ok(mut writer) => {
match markdown::file_to_html(&path.as_ref()) {
Ok(text) => {
let text :Vec<String> = text.trim().lines().map(|line| line.trim().to_string()).collect();
let data = text.concat().as_bytes().to_vec();
writer.objects.set(object.as_bytes(), CacheData {
mime:String::from("text/html"),
data,
});
Ok(())
}
Err(_) => Err(())
}
}
},
Err(_) => Err(()),
}
}
@ -112,24 +105,9 @@ impl WebCache {
}
}
pub fn cache_file_group<P :AsRef<Path>>(&self, mime:&str, object:&str, paths:&[P]) -> Result<(),()>
{
match self.data.write() {
Ok(mut writer) => {
let data = paths.into_iter().map(|path| {
let mut buffer = Vec::new();
if let Ok(mut file) = File::open(path) {
file.read_to_end(&mut buffer).ok();
}
buffer
}).collect::<Vec<Vec<u8>>>().concat();
writer.objects.set(object.as_bytes(), CacheData {
mime:mime.to_string(),
data:data,
});
Ok(())
},
Err(_) => Err(()),
}
}
pub fn raw(data:Vec<u8>) -> Source { Source::Raw(data) }
pub fn string(text:&str) -> Source { Source::String(text.to_string()) }
pub fn file<P :AsRef<Path>>(path:P) -> Source { Source::File((path.as_ref() as &Path).to_path_buf())}
pub fn markdown<P :AsRef<Path>>(path:P) -> Source { Source::MarkdownFile((path.as_ref() as &Path).to_path_buf())}
}

View File

@ -203,6 +203,8 @@ impl FileSystem {
connections:Vec::new(),
time,
chain_id:0,
undo:None,
})
} else { Err(()) }
}

View File

@ -0,0 +1,92 @@
use std::path::Path;
use usvg::tiny_skia_path::PathSegment;
pub fn load<P :AsRef<Path>>(file:P) -> Result<String,()>
{
let mut output = String::from("new GameImage([");
let svg_data = std::fs::read(file).map_err(|_| ())?;
let opt = usvg::Options::default();
let rtree = usvg::Tree::from_data(&svg_data, &opt).map_err(|_| ())?;
let bounds = rtree.size();
let origin = [
bounds.width() / 2.,
bounds.height() / 2.,
];
let scale = 1. / bounds.width().max(bounds.height());
for node in rtree.root().children() {
output += &load_node(node, origin, scale)?;
}
output += "])";
Ok(output)
}
fn load_node(node:&usvg::Node, origin:[f32; 2], scale:f32) -> Result<String,()>
{
let mut output = String::new();
match &node {
usvg::Node::Group(group) => {
for node in group.children() {
output += &load_node(node, origin, scale)?;
}
}
usvg::Node::Path(path) => {
output += "[";
output += &if let Some(fill) = path.fill() {
match fill.paint() {
usvg::Paint::Color(color) => {
format!("\"#{:02x}{:02x}{:02x}\"", color.red, color.green, color.blue)
}
_ => {
String::from("\"#000000\"")
}
}
} else {
String::from("\"#000000\"")
};
output += ",[";
for ref segment in path.data().segments() {
match segment {
PathSegment::MoveTo(point) => {
output += &format!("[0, [{:.4},{:.4}]],",
(point.x - origin[0]) * scale,
(point.y - origin[1]) * scale,
);
}
PathSegment::LineTo(point) => {
output += &format!("[1, [{:.4},{:.4}]],",
(point.x - origin[0]) * scale,
(point.y - origin[1]) * scale,
);
}
PathSegment::CubicTo(a, b, c) => {
output += &format!("[2, [{:.4},{:.4},{:.4},{:.4},{:.4},{:.4},]],",
(a.x - origin[0]) * scale,
(a.y - origin[1]) * scale,
(b.x - origin[0]) * scale,
(b.y - origin[1]) * scale,
(c.x - origin[0]) * scale,
(c.y - origin[1]) * scale,
);
}
PathSegment::Close => {
output += "[3],";
}
_ => { }
}
}
output += "]],";
}
_ => { }
}
Ok(output)
}

View File

@ -3,3 +3,4 @@ pub mod color;
pub mod string;
pub mod pack;
mod chain; pub use chain::Chain;
pub mod imager;

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -1,86 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="120"
height="120"
viewBox="0 0 120 120"
version="1.1"
id="svg1"
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
sodipodi:docname="behemoth_dusk.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#111111"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="true"
inkscape:zoom="6.9591091"
inkscape:cx="32.259876"
inkscape:cy="64.088663"
inkscape:window-width="2588"
inkscape:window-height="1368"
inkscape:window-x="1257"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="layer1">
<inkscape:grid
id="grid1"
units="px"
originx="0"
originy="0"
spacingx="1"
spacingy="1"
empcolor="#0099e5"
empopacity="0.30196078"
color="#0099e5"
opacity="0.14901961"
empspacing="5"
dotted="false"
gridanglex="30"
gridanglez="30"
visible="true" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#f6a1bd;fill-opacity:1;stroke-width:0.707108"
id="rect2-4-8-6"
width="60"
height="5"
x="30"
y="105" />
<path
id="path1"
style="fill:#f6a1bd;fill-opacity:1"
d="M 41 21 L 35 23 L 33 26 L 31 32 L 31 37 L 32 41 L 31 44 L 28 46 L 25 43 L 24 35 L 25 29 L 27 24 L 23 27 L 21 30 L 20 34 L 19 40 L 20 47 L 24 52 L 30 53 L 36 52 L 39 47 L 40 43 L 42 44 L 44 49 L 45 55 L 41 70 L 39 77 L 36 80 L 32 81 L 28 78 L 26 75 L 26 71 L 27 65 L 23 70 L 22 75 L 23 81 L 27 85 L 35 87 L 41 85 L 46 80 L 54 70 L 60 55 L 66 70 L 74 80 L 79 85 L 85 87 L 93 85 L 97 81 L 98 75 L 97 70 L 93 65 L 94 71 L 94 75 L 92 78 L 88 81 L 84 80 L 81 77 L 79 70 L 75 55 L 76 49 L 78 44 L 80 43 L 81 47 L 84 52 L 90 53 L 96 52 L 100 47 L 101 40 L 100 34 L 99 30 L 97 27 L 93 24 L 95 29 L 96 35 L 95 43 L 92 46 L 89 44 L 88 41 L 89 37 L 89 32 L 87 26 L 85 23 L 79 21 L 71 23 L 67 28 L 63 33 L 60 40 L 57 33 L 53 28 L 49 23 L 41 21 z " />
<path
style="fill:#f6a1bd;fill-opacity:1"
d="m 60,62 -2,8 -3,5 -3,4 -3,3 2,5 2,5 4,3 h 6 l 4,-3 2,-5 2,-5 -3,-3 -3,-4 -3,-5 z"
id="path3"
sodipodi:nodetypes="cccccccccccccccc" />
<path
style="fill:#f6a1bd;fill-opacity:1"
d="m 79,49 -1,6 1,6 3,8 2,-4 1,-4 -1,-6 -3,-2 z"
id="path4"
sodipodi:nodetypes="ccccccccc" />
<path
style="fill:#f6a1bd;fill-opacity:1"
d="m 41,49 1,6 -1,6 -3,8 -2,-4 -1,-4 1,-6 3,-2 z"
id="path4-7"
sodipodi:nodetypes="ccccccccc" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -1,82 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="120"
height="120"
viewBox="0 0 120 120"
version="1.1"
id="svg1"
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
sodipodi:docname="castle_dusk.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#111111"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="true"
inkscape:zoom="6.9591092"
inkscape:cx="46.198442"
inkscape:cy="46.845076"
inkscape:window-width="2588"
inkscape:window-height="1368"
inkscape:window-x="1257"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="layer1">
<inkscape:grid
id="grid1"
units="px"
originx="0"
originy="0"
spacingx="1"
spacingy="1"
empcolor="#0099e5"
empopacity="0.30196078"
color="#0099e5"
opacity="0.14901961"
empspacing="5"
dotted="false"
gridanglex="30"
gridanglez="30"
visible="true" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#f6a1bd;fill-opacity:1;stroke-width:0.707108"
id="rect2-4-8-6"
width="60"
height="5"
x="30"
y="105" />
<path
style="display:inline;fill:#f6a1bd;fill-opacity:1"
d="M 23,95 V 75 l 2,-5 V 60 h 6 6 v -8 l -3,-2 -3,2 v 8 H 25 V 37 l -2,-2 v -7 l 1,-1 h 3 l 1,1 v 4 h 3 v -4 l 1,-1 h 4 l 1,1 v 4 h 3 v -4 l 1,-1 h 3 l 1,1 v 7 l -2,2 v 58 z"
id="path1-7-5"
sodipodi:nodetypes="ccccccccccccccccccccccccccccccccc" />
<path
style="display:inline;fill:#f6a1bd;fill-opacity:1"
d="M 97,95 V 75 L 95,70 V 60 h -6 -6 v -8 l 3,-2 3,2 v 8 h 6 V 37 l 2,-2 v -7 l -1,-1 h -3 l -1,1 v 4 h -3 v -4 l -1,-1 h -4 l -1,1 v 4 h -3 v -4 l -1,-1 h -3 l -1,1 v 7 l 2,2 v 58 z"
id="path1-7-5-8"
sodipodi:nodetypes="ccccccccccccccccccccccccccccccccc" />
<path
style="fill:#f6a1bd;fill-opacity:1"
d="M 46,95 V 37 l 2,-2 v -7 l -3,-3 v -7 l 1,-1 h 3 l 1,1 v 4 h 5 v -4 l 1,-1 h 8 l 1,1 v 4 h 5 v -4 l 1,-1 h 3 l 1,1 v 7 l -3,3 v 7 l 2,2 v 58 z m 19,0 V 70 l -1,-2 -2,-2 -2,-1 -2,1 -2,2 -1,2 v 25 z"
id="path5"
sodipodi:nodetypes="ccccccccccccccccccccccccccccccccccccc" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -1,85 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="120"
height="120"
viewBox="0 0 120 120"
version="1.1"
id="svg1"
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
sodipodi:docname="dragon_dusk.svg"
inkscape:export-filename="dragon_dusk.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#111111"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="true"
inkscape:zoom="9.8416665"
inkscape:cx="33.480102"
inkscape:cy="60.762067"
inkscape:window-width="2584"
inkscape:window-height="1368"
inkscape:window-x="1261"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="layer1">
<inkscape:grid
id="grid1"
units="px"
originx="0"
originy="0"
spacingx="1"
spacingy="1"
empcolor="#0099e5"
empopacity="0.30196078"
color="#0099e5"
opacity="0.14901961"
empspacing="5"
dotted="false"
gridanglex="30"
gridanglez="30"
visible="true" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#f6a1bd;fill-opacity:1;stroke-width:0.707107"
id="rect2-4-8-6"
width="60"
height="5"
x="30"
y="105" />
<path
style="fill:#f6a1bd;fill-opacity:1"
d="m 45,100 5,-10 1,-15 -1,-5 -3,5 -1,5 v 10 l -5,-10 -1,-10 3,-15 2,-5 7,-20 5,-5 1,-1 1,-3 -1,-3 -3,-5 v -2 l 2,1 3,4 3,-4 2,-1 v 2 l -3,5 -1,3 1,3 1,1 5,5 7,20 2,5 3,15 -1,10 -5,10 V 80 l -1,-5 -3,-5 -1,5 1,15 5,10 z"
id="path1"
sodipodi:nodetypes="cccccccccccccccccccccccccccccccccccccccc" />
<path
style="fill:#f6a1bd;fill-opacity:1"
d="m 41,90 -5,-10 -1,-10 3,-15 2,-5 5,-10 -1,-3 -4,-8 -5,-1 3,3 1,3 -1,3 -15,-2 -10,3 -3,10 1,7 1,13 3,10 3,6 5,7 7,4 -3,-5 V 85 L 23,75 V 65 l 2,-10 8,-7 -3,7 -3,10 -1,10 4,10 5,15 h 5 l 3,-6 z"
id="path2"
sodipodi:nodetypes="ccccccccccccccccccccccccccccccccccc" />
<path
style="fill:#f6a1bd;fill-opacity:1"
d="m 79,90 5,-10 1,-10 -3,-15 -2,-5 -5,-10 1,-3 4,-8 5,-1 -3,3 -1,3 1,3 15,-2 10,3 3,10 -1,7 -1,13 -3,10 -3,6 -5,7 -7,4 3,-5 V 85 L 97,75 V 65 l -2,-10 -8,-7 3,7 3,10 1,10 -4,10 -5,15 h -5 l -3,-6 z"
id="path2-0"
sodipodi:nodetypes="ccccccccccccccccccccccccccccccccccc" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -1,90 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="120"
height="120"
viewBox="0 0 120 120"
version="1.1"
id="svg1"
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
sodipodi:docname="king_dusk.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#111111"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="true"
inkscape:zoom="6.9591092"
inkscape:cx="67.609228"
inkscape:cy="61.214731"
inkscape:window-width="2588"
inkscape:window-height="1368"
inkscape:window-x="1257"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="layer1">
<inkscape:grid
id="grid1"
units="px"
originx="0"
originy="0"
spacingx="1"
spacingy="1"
empcolor="#0099e5"
empopacity="0.30196078"
color="#0099e5"
opacity="0.14901961"
empspacing="5"
dotted="false"
gridanglex="30"
gridanglez="30"
visible="true" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#f6a1bd;fill-opacity:1;stroke-width:0.707107"
id="rect2-4-8-6"
width="60"
height="5"
x="30"
y="105" />
<path
style="fill:#f6a1bd;fill-opacity:1"
d="M 35,75 30,55 h 5 l 5,10 h 5 L 40,50 h 5 l 5,10 h 20 l 5,-10 h 5 l -5,15 h 5 l 5,-10 h 5 l -5,20 h -5 l -5,-5 h -5 l -5,5 H 55 l -5,-5 h -5 l -5,5 z"
id="path11"
sodipodi:nodetypes="ccccccccccccccccccccccccc" />
<path
style="fill:#f6a1bd;fill-opacity:1"
d="m 35,80 h 5 l 5,-5 h 5 l 5,5 h 10 l 5,-5 h 5 l 5,5 h 5 v 15 l -5,5 H 40 l -5,-5 z"
id="path12"
sodipodi:nodetypes="ccccccccccccccc" />
<path
style="fill:#f6a1bd;fill-opacity:1"
d="M 50,40 60,50 70,40 75,30 V 25 L 60,15 45,25 v 5 z"
id="path13"
sodipodi:nodetypes="ccccccccc" />
<path
style="fill:#f6a1bd;fill-opacity:1"
d="m 35,35 -5,5 5,5 5,-5 z"
id="path1" />
<path
style="fill:#f6a1bd;fill-opacity:1"
d="m 85,35 -5,5 5,5 5,-5 z"
id="path1-6" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1,72 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="120"
height="120"
viewBox="0 0 120 120"
version="1.1"
id="svg1"
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
sodipodi:docname="knight_dusk.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#111111"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="true"
inkscape:zoom="9.8416665"
inkscape:cx="63.759527"
inkscape:cy="60.355632"
inkscape:window-width="2588"
inkscape:window-height="1368"
inkscape:window-x="1257"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="layer1">
<inkscape:grid
id="grid1"
units="px"
originx="0"
originy="0"
spacingx="1"
spacingy="1"
empcolor="#0099e5"
empopacity="0.30196078"
color="#0099e5"
opacity="0.14901961"
empspacing="5"
dotted="false"
gridanglex="30"
gridanglez="30"
visible="true" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#f6a1bd;fill-opacity:1;stroke-width:0.707108"
id="rect2-4-8-6"
width="60"
height="5"
x="30"
y="105" />
<path
style="fill:#f6a1bd;fill-opacity:1"
d="m 40,90 5,-25 v -5 l -5,5 -5,10 5,5 -5,5 -5,-10 5,-15 2,-5 2,-4 -4,-1 h -5 l -5,5 -10,-5 v -5 l 2,-4 -1,-6 v -4 l 2,4 2,3 5,-8 v -5 l 5,5 5,-5 5,5 15,5 h 30 l 10,5 5,14 2,9 -2,-1 -1,-2 -1,5 -3,8 v 7 l -1,10 h -6 l 1,-10 -1,-6 -4,-5 -3,11 -3,10 h -5 l 1,-2 2,-8 V 75 L 73,69 67,65 H 55 l -5,5 -5,20 z"
id="path4"
sodipodi:nodetypes="ccccccccccccccccccccccccccccccccccccccccccccccccccccc" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -1,82 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="120"
height="120"
viewBox="0 0 120 120"
version="1.1"
id="svg1"
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
sodipodi:docname="lance_dusk.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#111111"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="true"
inkscape:zoom="19.683333"
inkscape:cx="54.741745"
inkscape:cy="73.717189"
inkscape:window-width="2588"
inkscape:window-height="1368"
inkscape:window-x="1257"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="layer1">
<inkscape:grid
id="grid1"
units="px"
originx="0"
originy="0"
spacingx="1"
spacingy="1"
empcolor="#0099e5"
empopacity="0.30196078"
color="#0099e5"
opacity="0.14901961"
empspacing="5"
dotted="false"
gridanglex="30"
gridanglez="30"
visible="true" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#f6a1bd;fill-opacity:1;stroke-width:0.707108"
id="rect2-4-8-6"
width="60"
height="5"
x="30"
y="105" />
<path
style="fill:#f6a1bd;fill-opacity:1"
d="M 55,100 H 65 V 90 l -5,5 -5,-5 z"
id="path13"
sodipodi:nodetypes="cccccc" />
<path
style="fill:#f6a1bd;fill-opacity:1"
d="M 75,90 85,85 90,75 H 70 l -5,5 H 55 L 50,75 H 30 l 5,10 10,5 10,-5 5,5 5,-5 z"
id="path14"
sodipodi:nodetypes="cccccccccccccc" />
<path
style="fill:#f6a1bd;fill-opacity:1"
d="m 55,70 v 5 l 2,2 h 6 l 2,-2 V 70 L 70,65 65,30 60,10 55,30 50,65 Z"
id="path15"
sodipodi:nodetypes="cccccccccccc" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -1,72 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="120"
height="120"
viewBox="0 0 120 120"
version="1.1"
id="svg1"
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
sodipodi:docname="militia_dusk.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#111111"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="true"
inkscape:zoom="9.8416666"
inkscape:cx="61.930568"
inkscape:cy="56.088061"
inkscape:window-width="2588"
inkscape:window-height="1368"
inkscape:window-x="1257"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="layer1">
<inkscape:grid
id="grid1"
units="px"
originx="0"
originy="0"
spacingx="1"
spacingy="1"
empcolor="#0099e5"
empopacity="0.30196078"
color="#0099e5"
opacity="0.14901961"
empspacing="5"
dotted="false"
gridanglex="30"
gridanglez="30"
visible="true" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:#f6a1bd;fill-opacity:1"
d="M 35,87 V 47 37 l 10,-10 10,-5 5,-5 5,5 10,5 10,10 V 47 87 H 77 V 52 l -5,-5 -6,2 -2,3 v 15 l -1,5 H 57 L 56,67 V 52 l -2,-3 -6,-2 -5,5 v 35 z"
id="path10"
sodipodi:nodetypes="cccccccccccccccccccccccccc" />
<rect
style="fill:#f6a1bd;fill-opacity:1;stroke-width:0.707107"
id="rect2-4-8-6"
width="60"
height="5"
x="30"
y="105" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 916 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 932 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 886 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 911 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -1,111 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="120"
height="120"
viewBox="0 0 120 120"
version="1.1"
id="svg1"
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
sodipodi:docname="tower_dusk_v0.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#111111"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showgrid="true"
inkscape:zoom="6.9591092"
inkscape:cx="35.708593"
inkscape:cy="58.628193"
inkscape:window-width="2588"
inkscape:window-height="1368"
inkscape:window-x="1257"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="layer1">
<inkscape:grid
id="grid1"
units="px"
originx="0"
originy="0"
spacingx="1"
spacingy="1"
empcolor="#0099e5"
empopacity="0.30196078"
color="#0099e5"
opacity="0.14901961"
empspacing="5"
dotted="false"
gridanglex="30"
gridanglez="30"
visible="true" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#f6a1bd;fill-opacity:1;stroke-width:0.707107"
id="rect2-4-8-6"
width="60"
height="5"
x="30"
y="105" />
<path
style="fill:#f6a1bd;fill-opacity:1"
d="M 40,50 35,95 H 85 L 80,50 h -5 l 5,40 H 40 l 5,-40 z"
id="path3"
sodipodi:nodetypes="ccccccccc" />
<path
style="fill:#f6a1bd;fill-opacity:1"
d="m 44,58 36,32 h 3 V 87 L 47,55 h -3 z"
id="path4"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:#f6a1bd;fill-opacity:1"
d="M 73,55 37,87 v 3 h 3 L 76,58 v -3 z"
id="path5"
sodipodi:nodetypes="ccccccc" />
<path
style="fill:#f6a1bd;fill-opacity:1"
d="m 35,50 1,-25 h 3 L 38,45 H 82 L 81,25 h 3 l 1,25 z"
id="path6"
sodipodi:nodetypes="ccccccccc" />
<path
style="fill:#f6a1bd;fill-opacity:1"
d="M 35,25 H 30 V 20 L 45,10 h 30 l 15,10 v 5 H 75 l -5,-5 -5,5 H 55 l -5,-5 -5,5 z"
id="path9"
sodipodi:nodetypes="cccccccccccccc" />
<path
style="fill:#f6a1bd;fill-opacity:1"
d="M 42,32 41,42 H 79 L 78,32 Z"
id="path13"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#f6a1bd;fill-opacity:1"
d="m 35,95 -1,5 h 6 v -5 z"
id="path14"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#f6a1bd;fill-opacity:1"
d="m 85,95 1,5 h -6 v -5 z"
id="path14-3"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#f6a1bd;fill-opacity:1"
d="m 44,53 -1,5 h 34 l -1,-5 z"
id="path15" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -46,7 +46,7 @@ const OpCode = {
SessionLeave :0x002F,
GameState :0x0030,
GamePlay :0x0031,
GameMessage :0x0031,
GameHistory :0x0032,
Challenge :0x0060,
@ -61,3 +61,17 @@ const GameState = {
Ongoing :0x01,
Complete :0x02,
};
const GameMessage = {
Error :0x00,
Move :0x01,
Drop :0x02,
Alt :0x03,
Online :0x08,
Undo :0x10,
Retire :0x11,
Reaction :0x20,
};

View File

@ -246,7 +246,6 @@ GAME.Game = class {
this.state = {
code:0,
check:false,
checkmate:false,
};
this.update_board();
@ -335,7 +334,9 @@ GAME.Game = class {
}
}
if(moves == 0) { this.state.checkmate = true; }
console.log(moves);
if(moves == 0) { this.state.code = GAME.Const.State.Checkmate; }
}
}
@ -347,7 +348,7 @@ GAME.Game = class {
// Move piece on board.
switch(play.source) {
case 0:
case 3: {
case 2: {
let piece_id = this.board.tiles[play.from].piece;
let piece = this.board.pieces[piece_id];
piece.tile = play.to;
@ -389,7 +390,7 @@ GAME.Game = class {
}
// Handle alt moves.
if(play.source == 3) {
if(play.source == 2) {
switch(moves.alt) {
case 1: {
piece.promoted = false;
@ -410,11 +411,6 @@ GAME.Game = class {
this.pools[this.turn & 1].pieces[play.from] -= 1;
this.turn++;
} break;
// Play retired.
case 2: {
this.state.code = 2;
} break;
}
// Recalculate new board state.
@ -784,8 +780,8 @@ GAME.Const = {
},
State: {
Ongoing: 0,
Complete: 1,
Current: 0,
Checkmate: 1,
Resign: 2,
},

View File

@ -1,21 +1,64 @@
const GAME_ASSET = { };
const GAME_EMOJI = [
"Promote",
"Militia",
"Lance",
"Knight",
"Tower",
"Castle",
"Dragon",
"Behemoth",
"Heart",
];
const GAME_EMOJI_COLOR = [
"Promote",
"Dawn",
"Dusk",
];
GAME_ASSET.load_image = (image) => {
let img = new Image();
img.src = image;
return img;
};
class GameImage {
constructor(paths=[]) {
this.paths = paths;
}
GAME_ASSET.Image = {
Promote: GAME_ASSET.load_image("/asset/promote.svg"),
Piece: [
[ GAME_ASSET.load_image("/asset/militia_dawn.svg"), GAME_ASSET.load_image("/asset/militia_dusk.svg") ],
[ GAME_ASSET.load_image("/asset/lance_dawn.svg"), GAME_ASSET.load_image("/asset/lance_dusk.svg") ],
[ GAME_ASSET.load_image("/asset/knight_dawn.svg"), GAME_ASSET.load_image("/asset/knight_dusk.svg") ],
[ GAME_ASSET.load_image("/asset/tower_dawn.svg"), GAME_ASSET.load_image("/asset/tower_dusk.svg") ],
[ GAME_ASSET.load_image("/asset/castle_dawn.svg"), GAME_ASSET.load_image("/asset/castle_dusk.svg") ],
[ GAME_ASSET.load_image("/asset/dragon_dawn.svg"), GAME_ASSET.load_image("/asset/dragon_dusk.svg") ],
[ GAME_ASSET.load_image("/asset/behemoth_dawn.svg"), GAME_ASSET.load_image("/asset/behemoth_dusk.svg") ],
[ GAME_ASSET.load_image("/asset/heart_dawn.svg"), GAME_ASSET.load_image("/asset/heart_dusk.svg") ],
],
};
draw(ctx, scale=1., offset=[0, 0], color=null) {
for(let path of this.paths) {
if(color === null) {
ctx.fillStyle = path[0];
} else {
ctx.fillStyle = color;
}
let origin = [0, 0];
ctx.beginPath();
for(let segment of path[1]) {
switch(segment[0]) {
case 0: {
origin = segment[1];
ctx.moveTo(
(scale * segment[1][0]) + offset[0],
(scale * segment[1][1]) + offset[1],
);
} break;
case 1: {
ctx.lineTo(
(scale * segment[1][0]) + offset[0],
(scale * segment[1][1]) + offset[1],
);
} break;
case 2: {
} break;
case 3: {
ctx.lineTo(
(scale * origin[0]) + offset[0],
(scale * origin[1]) + offset[1],
);
} break;
default: console.log(segment.mode);
}
}
ctx.fill();
}
}
}

View File

@ -18,6 +18,8 @@ const INTERFACE = {
TileMedium: "#242424",
TileDark: "#101010",
Promote: "#a52121",
Dawn: "#ffe082",
DawnMedium: "#fca03f",
DawnDark: "#ff6d00",
@ -60,8 +62,8 @@ const INTERFACE = {
},
resolve_board() {
for(let i = 0; i < INTERFACE_DATA.board_state.length; ++i) {
INTERFACE_DATA.board_state[i] = [0, 0];
for(let i = 0; i < INTERFACE_DATA.Game.board_state.length; ++i) {
INTERFACE_DATA.Game.board_state[i] = [0, 0];
}
if(INTERFACE_DATA.select !== null) { INTERFACE.resolve_piece(INTERFACE_DATA.select, 1); }
@ -103,18 +105,18 @@ const INTERFACE = {
if(movement.valid) {
// Show valid/threat hints if piece belongs to player and is player turn.
if(INTERFACE_DATA.player == 2 || player == INTERFACE_DATA.player) {
INTERFACE_DATA.board_state[movement.tile][zone] = INTERFACE.TileStatus.Valid;
INTERFACE_DATA.Game.board_state[movement.tile][zone] = INTERFACE.TileStatus.Valid;
/*if(GAME_DATA.board.tiles[movement.tile].threaten[+(!player)] > 0) {
INTERFACE_DATA.board_state[movement.tile][zone] = INTERFACE.TileStatus.Threat;
INTERFACE_DATA.Game.board_state[movement.tile][zone] = INTERFACE.TileStatus.Threat;
} else {
INTERFACE_DATA.board_state[movement.tile][zone] = INTERFACE.TileStatus.Valid;
INTERFACE_DATA.Game.board_state[movement.tile][zone] = INTERFACE.TileStatus.Valid;
}*/
}
else {
INTERFACE_DATA.board_state[movement.tile][zone] = INTERFACE.TileStatus.Opponent;
INTERFACE_DATA.Game.board_state[movement.tile][zone] = INTERFACE.TileStatus.Opponent;
}
} else {
INTERFACE_DATA.board_state[movement.tile][zone] = INTERFACE.TileStatus.Invalid;
INTERFACE_DATA.Game.board_state[movement.tile][zone] = INTERFACE.TileStatus.Invalid;
}
}
}
@ -123,7 +125,7 @@ const INTERFACE = {
hover(event) {
let initial_hover = INTERFACE_DATA.hover;
let apothem = INTERFACE_DATA.Ui.scale;
let apothem = INTERFACE_DATA.Render.scale;
let radius = INTERFACE.Radius * apothem;
let halfradius = radius / 2;
let grid_offset_x = 1.5 * radius;
@ -133,11 +135,11 @@ const INTERFACE = {
INTERFACE_DATA.hover = null;
// Handle board area
if(event.offsetY >= INTERFACE_DATA.Ui.margin.t && event.offsetY < INTERFACE_DATA.Ui.margin.l + INTERFACE_DATA.Ui.area.y) {
if(event.offsetX >= INTERFACE_DATA.Ui.offset.x && event.offsetX < INTERFACE_DATA.Ui.offset.x + INTERFACE_DATA.Ui.board_width) {
if(event.offsetY >= INTERFACE_DATA.Render.margin.t && event.offsetY < INTERFACE_DATA.Render.margin.l + INTERFACE_DATA.Render.area.y) {
if(event.offsetX >= INTERFACE_DATA.Render.offset.x && event.offsetX < INTERFACE_DATA.Render.offset.x + INTERFACE_DATA.Render.board_width) {
let basis_x = INTERFACE_DATA.Ui.offset.x + halfradius;
let basis_y = INTERFACE_DATA.Ui.offset.y + (14 * apothem);
let basis_x = INTERFACE_DATA.Render.offset.x + halfradius;
let basis_y = INTERFACE_DATA.Render.offset.y + (14 * apothem);
let x = (event.offsetX - basis_x) / grid_offset_x;
let y = -(event.offsetY - basis_y) / apothem;
@ -165,10 +167,10 @@ const INTERFACE = {
}
// Handle pool area
else if(event.offsetX >= INTERFACE_DATA.Ui.pool_offset && event.offsetX < INTERFACE_DATA.Ui.offset.x + INTERFACE_DATA.Ui.area.x) {
else if(event.offsetX >= INTERFACE_DATA.Render.pool_offset && event.offsetX < INTERFACE_DATA.Render.offset.x + INTERFACE_DATA.Render.area.x) {
let basis_x = INTERFACE_DATA.Ui.pool_offset + halfradius;
let basis_y = INTERFACE_DATA.Ui.offset.y + (3 * apothem);
let basis_x = INTERFACE_DATA.Render.pool_offset + halfradius;
let basis_y = INTERFACE_DATA.Render.offset.y + (3 * apothem);
let x = (event.offsetX - basis_x) / grid_offset_x;
let y = (event.offsetY - basis_y) / apothem;
@ -198,13 +200,13 @@ const INTERFACE = {
}
}
if(initial_hover != INTERFACE_DATA.hover) { INTERFACE.draw(); }
if(initial_hover != INTERFACE_DATA.hover) { INTERFACE.step(); }
},
unhover() {
let redraw = (INTERFACE_DATA.hover !== null);
INTERFACE_DATA.hover = null;
if(redraw) { INTERFACE.draw(); }
if(redraw) { INTERFACE.step(); }
},
click(event) {
@ -228,8 +230,20 @@ const INTERFACE = {
// Play selection.
if(INTERFACE_DATA.hover.source == 0 && (INTERFACE_DATA.mode == INTERFACE.Mode.Local || INTERFACE_DATA.player == (GAME_DATA.turn & 1))) {
let tile_state = INTERFACE_DATA.board_state[INTERFACE_DATA.hover.tile][1];
let tile_state = INTERFACE_DATA.Game.board_state[INTERFACE_DATA.hover.tile][1];
result = +(tile_state == INTERFACE.TileStatus.Valid || tile_state == INTERFACE.TileStatus.Threat);
if(INTERFACE_DATA.select.source == 1) {
let pool_selected = +(INTERFACE_DATA.select.tile >= 7);
pool_selected ^= (INTERFACE_DATA.player & 1);
if(INTERFACE_DATA.mode == INTERFACE.Mode.Local) {
pool_selected ^= INTERFACE_DATA.rotate;
}
if((GAME_DATA.turn & 1) != pool_selected) {
result = 0;
}
}
}
// Alt move selection.
@ -254,7 +268,7 @@ const INTERFACE = {
console.log("D1");
let source = INTERFACE_DATA.select.source;
if(source == 0 && INTERFACE_DATA.alt_mode) {
source = 3;
source = 2;
}
let play = new GAME.Play(
@ -310,7 +324,7 @@ const INTERFACE = {
} break;
}
INTERFACE.draw();
INTERFACE.step();
},
contextmenu() {
@ -325,7 +339,7 @@ const INTERFACE = {
if(INTERFACE.Ui.match_select(INTERFACE_DATA.hover, INTERFACE_DATA.select)) {
INTERFACE_DATA.select = null;
INTERFACE_DATA.alt_mode = false;
INTERFACE.draw();
INTERFACE.step();
} else {
INTERFACE.click({button:0});
}
@ -337,11 +351,11 @@ const INTERFACE = {
let width = INTERFACE_DATA.canvas.width = INTERFACE_DATA.canvas.clientWidth;
let height = INTERFACE_DATA.canvas.height = INTERFACE_DATA.canvas.clientHeight;
INTERFACE_DATA.Ui.margin.t = Math.floor(Math.min(width, height) / 96);
INTERFACE_DATA.Ui.margin.l = 1.75 * INTERFACE_DATA.Ui.margin.t;
INTERFACE_DATA.Ui.margin.r = INTERFACE_DATA.Ui.margin.t;
INTERFACE_DATA.Ui.margin.b = 3 * INTERFACE_DATA.Ui.margin.t;
let margin = INTERFACE_DATA.Ui.margin;
INTERFACE_DATA.Render.margin.t = Math.floor(Math.min(width, height) / 96);
INTERFACE_DATA.Render.margin.l = 1.75 * INTERFACE_DATA.Render.margin.t;
INTERFACE_DATA.Render.margin.r = INTERFACE_DATA.Render.margin.t;
INTERFACE_DATA.Render.margin.b = 3 * INTERFACE_DATA.Render.margin.t;
let margin = INTERFACE_DATA.Render.margin;
let gui_width = width - (margin.l + margin.r);
let gui_height = height - (margin.t + margin.b);
@ -354,40 +368,43 @@ const INTERFACE = {
let gui_scale = gui_height * INTERFACE.Scale;
INTERFACE_DATA.Ui.area.x = gui_width;
INTERFACE_DATA.Ui.area.y = gui_height;
INTERFACE_DATA.Ui.scale = gui_scale;
INTERFACE_DATA.Render.area.x = gui_width;
INTERFACE_DATA.Render.area.y = gui_height;
INTERFACE_DATA.Render.scale = gui_scale;
INTERFACE_DATA.Ui.offset.x = (INTERFACE_DATA.Ui.margin.l - INTERFACE_DATA.Ui.margin.r) + (width - gui_width) / 2;
INTERFACE_DATA.Ui.offset.y = (INTERFACE_DATA.Ui.margin.t - INTERFACE_DATA.Ui.margin.b) + (height - gui_height) / 2;
INTERFACE_DATA.Render.offset.x = (INTERFACE_DATA.Render.margin.l - INTERFACE_DATA.Render.margin.r) + (width - gui_width) / 2;
INTERFACE_DATA.Render.offset.y = (INTERFACE_DATA.Render.margin.t - INTERFACE_DATA.Render.margin.b) + (height - gui_height) / 2;
INTERFACE_DATA.Ui.board_width = Math.ceil(INTERFACE.BoardWidth * gui_scale);
INTERFACE_DATA.Ui.pool_offset = INTERFACE_DATA.Ui.offset.x + Math.floor(INTERFACE.PoolOffset * gui_scale);
INTERFACE_DATA.Render.board_width = Math.ceil(INTERFACE.BoardWidth * gui_scale);
INTERFACE_DATA.Render.pool_offset = INTERFACE_DATA.Render.offset.x + Math.floor(INTERFACE.PoolOffset * gui_scale);
},
step() {
if(INTERFACE_DATA === null) return;
INTERFACE.resolve_board();
if(INTERFACE_DATA.Timeout.draw === null) {
INTERFACE.draw();
}
},
draw() {
if(INTERFACE_DATA === null) return;
INTERFACE.resize();
INTERFACE.resolve_board();
INTERFACE.render();
},
render() {
let canvas = INTERFACE_DATA.canvas;
let ctx = INTERFACE_DATA.context;
let width = canvas.width;
let height = canvas.height;
let gui_margin = INTERFACE_DATA.Ui.margin;
let gui_offset = INTERFACE_DATA.Ui.offset;
let gui_scale = INTERFACE_DATA.Ui.scale;
let gui_margin = INTERFACE_DATA.Render.margin;
let gui_offset = INTERFACE_DATA.Render.offset;
let gui_scale = INTERFACE_DATA.Render.scale;
let play = null;
if(INTERFACE_DATA.replay_turn > 0) {
play = INTERFACE_DATA.history[INTERFACE_DATA.replay_turn - 1];
if(INTERFACE_DATA.Replay.turn > 0) {
play = INTERFACE_DATA.Game.history[INTERFACE_DATA.Replay.turn - 1];
}
@ -397,11 +414,49 @@ const INTERFACE = {
ctx.fillRect(0, 0, width, height);
// Draw particles
for(let p = 0; p < INTERFACE_DATA.Animation.particles.length; ++p) {
let particle = INTERFACE_DATA.Animation.particles[p];
ctx.save();
if(particle !== null) {
if(particle.time > 0) {
let name = GAME_EMOJI[particle.index];
let color = INTERFACE.Color[GAME_EMOJI_COLOR[particle.color]];
ctx.translate((width / 10) + particle.position[0], height - particle.position[1]);
ctx.rotate(particle.rotation);
if(particle.time < 1) {
ctx.globalAlpha = Math.max(0, particle.time);
}
GAME_ASSET.Image[name].draw(ctx, particle.scale * gui_scale, [0, 0], color);
particle.position[0] += particle.velocity[0];
particle.position[1] += particle.velocity[1];
particle.rotation += particle.angular;
particle.time -= 30. / 1000;
} else {
INTERFACE_DATA.Animation.particles[p] = null;
}
}
ctx.restore();
}
let temp = INTERFACE_DATA.Animation.particles;
INTERFACE_DATA.Animation.particles = [ ];
for(particle of temp) {
if(particle !== null) {
INTERFACE_DATA.Animation.particles.push(particle);
}
}
// Draw tiles
let radius = INTERFACE.Radius * gui_scale;
let basis_x = gui_offset.x + radius;
let basis_y = gui_offset.y + (13 * gui_scale);
let icon_radius = 0.69 * radius;
ctx.lineWidth = Math.min(gui_scale * 0.06, 3);
@ -413,13 +468,13 @@ const INTERFACE = {
let is_hover = INTERFACE.Ui.tile_is_hover(0, i);
let is_select = INTERFACE.Ui.tile_is_select(0, i);
let tile_state = INTERFACE_DATA.board_state[i][1];
let hover_state = INTERFACE_DATA.board_state[i][0];
let tile_state = INTERFACE_DATA.Game.board_state[i][1];
let hover_state = INTERFACE_DATA.Game.board_state[i][0];
let draw_piece = true;
if(INTERFACE_DATA.Animate.play !== null) {
let play = INTERFACE_DATA.Animate.play;
draw_piece = draw_piece && !((play.source == 0 || play.source == 3) && (play.from == i || play.to == i));
if(INTERFACE_DATA.Animation.piece !== null) {
let play = INTERFACE_DATA.Animation.piece.play;
draw_piece = draw_piece && !((play.source == 0 || play.source == 2) && (play.from == i || play.to == i));
draw_piece = draw_piece && !(play.source == 1 && play.to == i);
}
@ -439,7 +494,7 @@ const INTERFACE = {
if(tile.piece !== null) { piece = GAME_DATA.board.pieces[tile.piece]; }
let is_play = null;
if(GAME_DATA.turn > 0 && (play.source < 2 || play.source == 3) && (play.to == i || ((play.source == 0 || play.source == 3) && play.from == i))) {
if(GAME_DATA.turn > 0 && (play.to == i || ((play.source == 0 || play.source == 2) && play.from == i))) {
is_play = +!(GAME_DATA.turn & 1);
}
let is_check = GAME_DATA.state.check != 0 && piece !== null && piece.piece == GAME.Const.PieceId.Heart && piece.player == (GAME_DATA.turn & 1);
@ -520,16 +575,22 @@ const INTERFACE = {
// Draw tile content
if(draw_piece && piece !== null) {
let piece_def = GAME.Const.Piece[piece.piece];
let piece_color = (piece.player == GAME.Const.Player.Dawn)? INTERFACE.Color.Dawn : INTERFACE.Color.Dusk;
// Draw border hints
if(!is_hover) { draw.hints(piece); }
if(GAME_ASSET.Image[piece_def.name] !== undefined) {
// Draw piece icon
if(INTERFACE_DATA.mirror && (piece.player ^ (INTERFACE_DATA.player & 1) ^ INTERFACE_DATA.rotate) != 0) {
ctx.rotate(Math.PI);
}
if(piece.promoted) { ctx.drawImage(GAME_ASSET.Image.Promote, -icon_radius, -icon_radius, icon_radius * 2., icon_radius * 2.); }
ctx.drawImage(GAME_ASSET.Image.Piece[piece.piece][piece.player], -icon_radius, -icon_radius, icon_radius * 2., icon_radius * 2.);
if(piece.promoted) {
GAME_ASSET.Image.Promote.draw(ctx, 1.5 * gui_scale, [0, 0], INTERFACE.Color.Promote);
}
GAME_ASSET.Image[piece_def.name].draw(ctx, 1.5 * gui_scale, [0, 0], piece_color);
}
}
ctx.restore();
@ -579,22 +640,22 @@ const INTERFACE = {
// Player handles
ctx.font = Math.ceil(gui_scale / 1.3) + "px sans-serif";
if(INTERFACE_DATA.handles[0] !== null) {
if(INTERFACE_DATA.Session.Client.Dawn.handle !== null) {
let pos = handle_pos[(1 ^ INTERFACE_DATA.player ^ INTERFACE_DATA.rotate) & 1];
ctx.fillStyle = INTERFACE.Color.Dawn;
ctx.textBaseline = "middle";
ctx.textAlign = "center";
ctx.fillText(INTERFACE_DATA.handles[0], pos.x, pos.y);
ctx.fillText(INTERFACE_DATA.Session.Client.Dawn.handle, pos.x, pos.y);
}
if(INTERFACE_DATA.handles[1] !== null) {
if(INTERFACE_DATA.Session.Client.Dusk.handle !== null) {
let pos = handle_pos[(INTERFACE_DATA.player ^ INTERFACE_DATA.rotate) & 1];
ctx.fillStyle = INTERFACE.Color.Dusk;
ctx.textBaseline = "middle";
ctx.textAlign = "center";
ctx.fillText(INTERFACE_DATA.handles[1], pos.x, pos.y);
ctx.fillText(INTERFACE_DATA.Session.Client.Dusk.handle, pos.x, pos.y);
}
// Tile information
@ -630,8 +691,8 @@ const INTERFACE = {
let message = null;
ctx.fillStyle = INTERFACE.Color.Text;
if(INTERFACE_DATA.auto_mode !== null) {
switch(INTERFACE_DATA.auto_mode) {
if(INTERFACE_DATA.Game.auto !== null) {
switch(INTERFACE_DATA.Game.auto) {
case 0: message = LANG("auto") + " " + LANG("dawn"); break;
case 1: message = LANG("auto") + " " + LANG("dusk"); break;
}
@ -642,7 +703,7 @@ const INTERFACE = {
message = LANG("resign");
} else if(GAME_DATA.state.check != 0) {
ctx.fillStyle = INTERFACE.Color.HintCheck;
if(GAME_DATA.state.checkmate) {
if(GAME_DATA.state.code == GAME.Const.State.Checkmate) {
message = LANG("checkmate");
} else {
message = LANG("check");
@ -703,13 +764,13 @@ const INTERFACE = {
}
if(INTERFACE_DATA.Animate.play !== null) {
let time = Math.min(1 - (INTERFACE_DATA.Animate.time - Date.now()) / 1000, 1);
if(INTERFACE_DATA.Animation.piece !== null) {
let time = Math.min(1 - (INTERFACE_DATA.Animation.piece.time - Date.now()) / 1000, 1);
time = time * time;
let play = INTERFACE_DATA.Animate.play;
let play = INTERFACE_DATA.Animation.piece.play;
let piece = INTERFACE_DATA.Animate.piece;
let target = INTERFACE_DATA.Animate.target;
let piece = INTERFACE_DATA.Animation.piece.piece;
let target = INTERFACE_DATA.Animation.piece.target;
// Get to and from coordinates.
let coord_to = HEX.tile_to_hex(play.to);
@ -725,7 +786,7 @@ const INTERFACE = {
switch(play.source) {
// Lerp between board positions.
case 0:
case 3: {
case 2: {
let coord_from = HEX.tile_to_hex(play.from);
if((INTERFACE_DATA.player & 1) ^ INTERFACE_DATA.rotate == 1) {
coord_from.x = 8 - coord_from.x;
@ -744,7 +805,6 @@ const INTERFACE = {
case 0: ctx.fillStyle = INTERFACE.Color.DawnMedium; break;
case 1: ctx.fillStyle = INTERFACE.Color.DuskMedium; break;
}
//ctx.fillStyle = INTERFACE.Color.AnimateShadow;
ctx.save();
ctx.translate(to_x, to_y);
@ -773,12 +833,20 @@ const INTERFACE = {
// Draw moving piece.
draw.animation_piece(piece, x, y);
if(Date.now() >= INTERFACE_DATA.Animate.time) {
INTERFACE_DATA.Animate.play = null;
INTERFACE_DATA.Animate.time = 0;
if(Date.now() >= INTERFACE_DATA.Animation.piece.time) {
INTERFACE_DATA.Animation.piece = null;
}
}
setTimeout(INTERFACE.render, 1000 / 30);
if(INTERFACE_DATA.Animation.piece !== null
|| INTERFACE_DATA.Animation.particles.length > 0)
{
INTERFACE_DATA.Timeout.draw = setTimeout(INTERFACE.draw, 1000 / 30);
} else {
if(INTERFACE_DATA.Timeout.draw !== null) {
INTERFACE_DATA.Timeout.draw = null;
setTimeout(INTERFACE.draw, 1000 / 30);
}
}
},
@ -882,6 +950,8 @@ const INTERFACE = {
// Draw tile content
if(piece !== null) {
let piece_def = GAME.Const.Piece[piece.piece];
// Draw border hints
this.hints(piece);
@ -889,9 +959,14 @@ const INTERFACE = {
if(INTERFACE_DATA.mirror && (piece.player ^ (INTERFACE_DATA.player & 1) ^ INTERFACE_DATA.rotate) != 0) {
this.ctx.rotate(Math.PI);
}
if(piece.promoted) { this.ctx.drawImage(GAME_ASSET.Image.Promote, -icon_radius, -icon_radius, icon_radius * 2., icon_radius * 2.); }
this.ctx.drawImage(GAME_ASSET.Image.Piece[piece.piece][piece.player], -icon_radius, -icon_radius, icon_radius * 2., icon_radius * 2.);
if(piece.promoted) {
GAME_ASSET.Image.Promote.draw(this.ctx, 1.5 * this.scale, [0, 0], INTERFACE.Color.Promote);
}
if(GAME_ASSET.Image[piece_def.name] !== undefined) {
let piece_color = (piece.player == GAME.Const.Player.Dawn)? INTERFACE.Color.Dawn : INTERFACE.Color.Dusk;
GAME_ASSET.Image[piece_def.name].draw(this.ctx, 1.5 * this.scale, [0, 0], piece_color);
}
}
this.ctx.restore();
@ -899,9 +974,10 @@ const INTERFACE = {
pool(x, y, basis, player, mirror=false) {
let radius = INTERFACE.Radius * this.scale;
let icon_radius = 0.69 * radius;
for(let i = 0; i < 7; ++i) {
let piece_def = GAME.Const.Piece[i];
let tile_id = i + basis;
let is_hover = INTERFACE.Ui.tile_is_hover(1, tile_id);
let is_select = INTERFACE.Ui.tile_is_select(1, tile_id);
@ -960,8 +1036,10 @@ const INTERFACE = {
this.ctx.rotate(Math.PI);
}
// Draw image
this.ctx.drawImage(GAME_ASSET.Image.Piece[i][player], -icon_radius * 0.55, -icon_radius * 0.8, icon_radius * 1.6, icon_radius * 1.6);
if(GAME_ASSET.Image[piece_def.name] !== undefined) {
let piece_color = (player == GAME.Const.Player.Dawn)? INTERFACE.Color.Dawn : INTERFACE.Color.Dusk;
GAME_ASSET.Image[piece_def.name].draw(this.ctx, radius, [0.2 * radius, 0], piece_color);
}
// Draw count
this.ctx.fillStyle = player_color;
@ -1013,9 +1091,6 @@ const INTERFACE = {
let dusk = null;
INTERFACE_DATA = {
mode: mode,
token: token,
canvas: document.getElementById("game"),
context: null,
@ -1028,19 +1103,46 @@ const INTERFACE = {
clicked: null,
alt_mode: false,
handles: [dawn, dusk],
board_state: [ ],
resign:false,
resign_warn:false,
mode: mode,
Session: {
token: token,
Client: {
Dawn: {
handle: null,
online: false,
},
Dusk: {
handle: null,
online: false,
},
Spectators: {
count: 0,
},
},
},
Game: {
board_state: [ ],
history: [ ],
history_begin: [ ],
replay_turn: 0,
replay_auto: false,
auto_mode: null,
auto: null,
},
Ui: {
request_undo:false,
resign_warn:false,
},
Timeout: {
draw: null,
},
Replay: {
turn: 0,
auto: false,
},
Render: {
scale: 0,
margin: { t:0, l:0, r:0, b:0 },
offset: new MATH.Vec2(),
@ -1050,14 +1152,17 @@ const INTERFACE = {
pool_offset: 0,
},
Animate: {
play: null,
Animation: {
piece: null,
target: null,
time: 0,
// play: null,
// piece: null,
// target: null,
// time: 0,
particles: [ ],
queue: [ ],
},
};
for(let i = 0; i < 61; ++i) { INTERFACE_DATA.board_state.push([0, 0]); }
for(let i = 0; i < 61; ++i) { INTERFACE_DATA.Game.board_state.push([0, 0]); }
let canvas = INTERFACE_DATA.canvas;
if(canvas !== undefined) {
@ -1068,11 +1173,11 @@ const INTERFACE = {
canvas.addEventListener("mousedown", INTERFACE.click);
canvas.addEventListener("mouseup", INTERFACE.release);
canvas.addEventListener("contextmenu", INTERFACE.contextmenu);
window.addEventListener("resize", INTERFACE.draw);
window.addEventListener("resize", INTERFACE.step);
switch(INTERFACE_DATA.mode) {
case INTERFACE.Mode.Local: {
INTERFACE.draw();
INTERFACE.step();
} break;
}
} else {
@ -1084,7 +1189,7 @@ const INTERFACE = {
case INTERFACE.Mode.Player: {
MESSAGE_COMPOSE([
PACK.u16(OpCode.GameState),
INTERFACE_DATA.token,
INTERFACE_DATA.Session.token,
]);
} break;
}
@ -1104,23 +1209,23 @@ const INTERFACE = {
},
reset() {
INTERFACE_DATA.auto_mode = null;
INTERFACE_DATA.Game.auto = null;
INTERFACE_DATA.history = [ ];
for(let i = 0; i < INTERFACE_DATA.history_begin.length; ++i) {
INTERFACE_DATA.history.push(INTERFACE_DATA.history_begin[i]);
INTERFACE_DATA.Game.history = [ ];
for(let i = 0; i < INTERFACE_DATA.Game.history_begin.length; ++i) {
INTERFACE_DATA.Game.history.push(INTERFACE_DATA.Game.history_begin[i]);
}
INTERFACE_DATA.replay_turn = INTERFACE_DATA.history.length + 1;
INTERFACE.replay_jump(INTERFACE_DATA.history.length, false);
INTERFACE_DATA.Replay.turn = INTERFACE_DATA.Game.history.length + 1;
INTERFACE.replay_jump(INTERFACE_DATA.Game.history.length, false);
},
undo() {
INTERFACE_DATA.auto_mode = null;
if(INTERFACE_DATA.history.length > 0) {
INTERFACE_DATA.replay_turn = INTERFACE_DATA.history.length + 1;
INTERFACE_DATA.history.pop();
INTERFACE.replay_jump(INTERFACE_DATA.history.length, false);
INTERFACE_DATA.Game.auto = null;
if(INTERFACE_DATA.Game.history.length > 0) {
INTERFACE_DATA.Replay.turn = INTERFACE_DATA.Game.history.length + 1;
INTERFACE_DATA.Game.history.pop();
INTERFACE.replay_jump(INTERFACE_DATA.Game.history.length, false);
}
},
@ -1133,33 +1238,93 @@ const INTERFACE = {
INTERFACE_DATA.player = data.player;
}
INTERFACE_DATA.history = data.history;
let turn = INTERFACE_DATA.history.length;
INTERFACE_DATA.Game.history = data.history;
let turn = INTERFACE_DATA.Game.history.length;
if(INTERFACE_DATA.history.length > 0) {
if(INTERFACE_DATA.replay_turn == 0) {
if(INTERFACE_DATA.history[INTERFACE_DATA.history.length-1].source == 2) {
turn = 0;
}
if(INTERFACE_DATA.Game.history.length > 0) {
if(INTERFACE_DATA.Replay.turn == 0) {
//if(INTERFACE_DATA.Game.history[INTERFACE_DATA.Game.history.length-1].source == 2) {
// turn = 0;
//}
} else {
turn = INTERFACE_DATA.replay_turn;
turn = INTERFACE_DATA.Replay.turn;
}
}
if(data.dawn.length > 0) { INTERFACE_DATA.handles[0] = data.dawn; }
if(data.dusk.length > 0) { INTERFACE_DATA.handles[1] = data.dusk; }
if(data.dawn.length > 0) { INTERFACE_DATA.Session.Client.Dawn.handle = data.dawn; }
if(data.dusk.length > 0) { INTERFACE_DATA.Session.Client.Dusk.handle = data.dusk; }
if(INTERFACE_DATA.mode == INTERFACE.Mode.Review) {
document.getElementById("indicator-turn").innerText = INTERFACE_DATA.replay_turn + " / " + INTERFACE_DATA.history.length;
document.getElementById("turn-slider").setAttribute("max", INTERFACE_DATA.history.length);
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);
}
INTERFACE.replay_jump(turn);
} break;
case OpCode.GamePlay: {
if(data.status == Status.Ok && data.turn == INTERFACE_DATA.history.length) {
INTERFACE.history_push(data.play, true);
case OpCode.GameMessage: {
switch(data.code) {
case GameMessage.Error: break;
case GameMessage.Move: {
if(data.turn == INTERFACE_DATA.Game.history.length) {
INTERFACE.history_push(new GAME.Play(0, data.from, data.to), true);
}
} break;
case GameMessage.Drop: {
if(data.turn == INTERFACE_DATA.Game.history.length) {
INTERFACE.history_push(new GAME.Play(1, data.piece, data.to), true);
}
} break;
case GameMessage.Alt: {
if(data.turn == INTERFACE_DATA.Game.history.length) {
INTERFACE.history_push(new GAME.Play(2, data.from, data.to), true);
}
} break;
case GameMessage.Online: {
switch(data.client) {
case 0: {
// Spectator
INTERFACE_DATA.Session.Client.Spectators.count = data.state;
} break;
case 1: {
// Dawn
INTERFACE_DATA.Session.Client.Dawn.online = (data.state != 0);
} break;
case 2: {
// Dusk
INTERFACE_DATA.Session.Client.Dawn.online = (data.state != 0);
} break;
}
} break;
case GameMessage.Undo: {
switch(data.state) {
case 0: {
// Request undo
if(data.turn == INTERFACE_DATA.Game.history.length) {
INTERFACE_DATA.Ui.request_undo = true;
}
} break;
case 1: {
// Perform undo
INTERFACE.undo();
} break;
}
} break;
case GameMessage.Retire: {
GAME_DATA.state.code = GAME.Const.State.Resign;
} break;
case GameMessage.Reaction: {
INTERFACE_DATA.Animation.queue.push(data.index);
INTERFACE.reaction_generate();
} break;
}
} break;
}
@ -1170,23 +1335,16 @@ const INTERFACE = {
switch(play.source) {
case 0:
case 3: {
case 2: {
let piece_id = GAME_DATA.board.tiles[play.from].piece;
let piece = GAME_DATA.board.pieces[piece_id];
valid = piece.player == (GAME_DATA.turn & 1);
} break;
case 1: {
//let player = Math.floor(play.from / 7) ^ (INTERFACE_DATA.player & 1);
//if(INTERFACE_DATA.player == 2) { player ^= INTERFACE_DATA.rotate; }
//valid = player == (GAME_DATA.turn & 1);
play.from %= 7;
valid = true;
} break;
case 2: {
valid = INTERFACE_DATA.mode == INTERFACE.Mode.Player;
} break;
}
if(valid) {
@ -1196,21 +1354,29 @@ const INTERFACE = {
case INTERFACE.Mode.Local: {
INTERFACE.history_push(play, true);
INTERFACE.draw();
INTERFACE.step();
if(INTERFACE_DATA.auto_mode !== null && INTERFACE_DATA.auto_mode == (GAME_DATA.turn & 1)) {
if(INTERFACE_DATA.Game.auto !== null && INTERFACE_DATA.Game.auto == (GAME_DATA.turn & 1)) {
setTimeout(INTERFACE.auto_play, 1000);
}
} break;
// Send action to server for validation.
case INTERFACE.Mode.Player: {
let move_data = play.source | (play.from << 4) | (play.to << 10);
let msg = GAME_DATA.turn | (play.from << 16) | (play.to << 22);
let high = msg >> 24;
let low = (msg << 8) & 0xFFFF_FFFF;
switch(play.source) {
case 0: low |= GameMessage.Move; break;
case 1: low |= GameMessage.Drop; break;
case 2: low |= GameMessage.Alt; break;
}
MESSAGE_COMPOSE([
PACK.u16(OpCode.GamePlay),
PACK.u16(0),
PACK.u16(GAME_DATA.turn),
PACK.u16(move_data),
PACK.u16(OpCode.GameMessage),
PACK.u32(high),
PACK.u32(low),
]);
} break;
}
@ -1219,12 +1385,12 @@ const INTERFACE = {
rotate() {
INTERFACE_DATA.rotate ^= 1;
INTERFACE.draw();
INTERFACE.step();
},
mirror() {
INTERFACE_DATA.mirror = !INTERFACE_DATA.mirror;
INTERFACE.draw();
INTERFACE.step();
},
resign() {
@ -1235,7 +1401,7 @@ const INTERFACE = {
INTERFACE.resign_reset();
MESSAGE_COMPOSE([
PACK.u16(OpCode.SessionResign),
INTERFACE_DATA.token,
INTERFACE_DATA.Session.token,
]);
} else {
INTERFACE_DATA.resign_warn = true;
@ -1258,12 +1424,12 @@ const INTERFACE = {
},
history_push(play, animate=false) {
INTERFACE_DATA.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.history.length;
document.getElementById("turn-slider").setAttribute("max", INTERFACE_DATA.history.length);
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.history.length - 1) {
if(INTERFACE_DATA.Replay.turn == INTERFACE_DATA.Game.history.length - 1) {
INTERFACE.replay_next(animate);
}
},
@ -1271,22 +1437,22 @@ const INTERFACE = {
replay_jump(turn, animate=false) {
turn = +turn;
if(turn >= 0 && turn <= INTERFACE_DATA.history.length) {
if(turn <= INTERFACE_DATA.replay_turn) {
INTERFACE_DATA.replay_turn = 0;
if(turn >= 0 && turn <= INTERFACE_DATA.Game.history.length) {
if(turn <= INTERFACE_DATA.Replay.turn) {
INTERFACE_DATA.Replay.turn = 0;
GAME.init();
}
let play = null;
let piece = null;
let target = null;
while(INTERFACE_DATA.replay_turn < turn) {
play = INTERFACE_DATA.history[INTERFACE_DATA.replay_turn];
while(INTERFACE_DATA.Replay.turn < turn) {
play = INTERFACE_DATA.Game.history[INTERFACE_DATA.Replay.turn];
switch(play.source) {
case 0:
case 1:
case 3: {
if(play.source == 0 || play.source == 3) {
case 2: {
if(play.source == 0 || play.source == 2) {
let piece_id = GAME_DATA.board.tiles[play.from].piece;
if(piece_id !== null) { piece = GAME_DATA.board.pieces[piece_id].clone(); }
@ -1309,47 +1475,49 @@ const INTERFACE = {
play = null;
}
}
INTERFACE_DATA.replay_turn++;
INTERFACE_DATA.Replay.turn++;
}
if(animate && play !== null) {
INTERFACE_DATA.Animate.time = Date.now() + 500;
INTERFACE_DATA.Animate.play = play;
INTERFACE_DATA.Animate.piece = piece;
INTERFACE_DATA.Animate.target = target;
INTERFACE_DATA.Animation.piece = {
time: Date.now() + 500,
play: play,
piece: piece,
target: target,
};
} else {
INTERFACE_DATA.Animate.play = null;
INTERFACE_DATA.Animation.piece = null;
}
INTERFACE_DATA.replay_turn = turn;
INTERFACE_DATA.Replay.turn = turn;
if(INTERFACE_DATA.mode == INTERFACE.Mode.Review) {
document.getElementById("indicator-turn").innerText = INTERFACE_DATA.replay_turn + " / " + INTERFACE_DATA.history.length;
document.getElementById("turn-slider").value = INTERFACE_DATA.replay_turn;
document.getElementById("indicator-turn").innerText = INTERFACE_DATA.Replay.turn + " / " + INTERFACE_DATA.Game.history.length;
document.getElementById("turn-slider").value = INTERFACE_DATA.Replay.turn;
}
INTERFACE.draw();
INTERFACE.step();
}
},
replay_first() {
INTERFACE.replay_jump(0);
},
replay_last(animate=false) {
INTERFACE.replay_jump(INTERFACE_DATA.history.length, animate);
INTERFACE.replay_jump(INTERFACE_DATA.Game.history.length, animate);
},
replay_prev(animate=false) {
INTERFACE.replay_jump(INTERFACE_DATA.replay_turn - 1, animate);
INTERFACE.replay_jump(INTERFACE_DATA.Replay.turn - 1, animate);
},
replay_next(animate=false) {
INTERFACE.replay_jump(INTERFACE_DATA.replay_turn + 1, animate);
INTERFACE.replay_jump(INTERFACE_DATA.Replay.turn + 1, animate);
},
replay_toggle_auto() {
INTERFACE_DATA.replay_auto = !INTERFACE_DATA.replay_auto;
if(INTERFACE_DATA.replay_auto) {
INTERFACE_DATA.Replay.auto = !INTERFACE_DATA.Replay.auto;
if(INTERFACE_DATA.Replay.auto) {
setTimeout(INTERFACE.replay_auto, 1000);
}
},
replay_auto() {
if(INTERFACE_DATA.replay_auto) {
INTERFACE.replay_jump(INTERFACE_DATA.replay_turn + 1, true);
if(INTERFACE_DATA.Replay.auto) {
INTERFACE.replay_jump(INTERFACE_DATA.Replay.turn + 1, true);
setTimeout(INTERFACE.replay_auto, 1100);
}
},
@ -1359,17 +1527,17 @@ const INTERFACE = {
auto() {
if(INTERFACE_DATA.auto_mode === null) {
INTERFACE_DATA.auto_mode = INTERFACE_DATA.rotate ^ 1;
if(INTERFACE_DATA.Game.auto === null) {
INTERFACE_DATA.Game.auto = INTERFACE_DATA.rotate ^ 1;
setTimeout(INTERFACE.auto_play, 500);
} else {
INTERFACE_DATA.auto_mode = null;
INTERFACE_DATA.Game.auto = null;
}
INTERFACE.draw();
INTERFACE.step();
},
auto_play() {
if(INTERFACE_DATA.auto_mode !== (GAME_DATA.turn & 1) || GAME_DATA.state.checkmate) { return; }
if(INTERFACE_DATA.Game.auto !== (GAME_DATA.turn & 1) || GAME_DATA.state.checkmate) { return; }
function state_score(state, player) {
let score = 0;
@ -1516,11 +1684,6 @@ const INTERFACE = {
let result = determine_play(GAME_DATA, GAME_DATA.turn & 1, 1);
if(result !== null) {
// Add 7 to tile to indicate Dusk player pool.
//if(result.play.source == 1 && (GAME_DATA.turn & 1) === 1) {
// result.play.from += 7;
//}
INTERFACE.process(result.play);
} else {
console.log("warn: autoplay move was null.");
@ -1541,6 +1704,43 @@ const INTERFACE = {
}
return null;
},
react()
{
if(INTERFACE_DATA.mode == INTERFACE.Mode.Review) {
let high = 0;
let low = GameMessage.Reaction;
MESSAGE_COMPOSE([
PACK.u16(OpCode.GameMessage),
PACK.u32(high),
PACK.u32(low),
]);
}
},
reaction_generate()
{
if(INTERFACE_DATA !== null) {
if(INTERFACE_DATA.Animation.queue.length > 0) {
let index = INTERFACE_DATA.Animation.queue.pop();
INTERFACE_DATA.Animation.particles.push({
index:index,
color:0,
scale:1.75 + (Math.random() * 0.25),
position:[0, 0],
rotation:0,
velocity:[1 - (Math.random() * 2), 2 + (Math.random() * 4)],
angular:0.01 - (Math.random() * 0.02),
time:1.5 + (Math.random() * 1.5),
});
if(INTERFACE_DATA.Timeout.draw === null) {
INTERFACE_DATA.Timeout.draw = setTimeout(INTERFACE.draw, 1);
}
setTimeout(INTERFACE.reaction_generate, 50);
}
}
}
};
INTERFACE.TileScale = 0.9;

View File

@ -620,10 +620,13 @@ const SCENES = {
load(data) {
// Bottom Buttons
let buttons_bottom = [ ];
if(data.mode != INTERFACE.Mode.Review) {
if(data.mode == INTERFACE.Mode.Player) {
let button_resign = UI.button(LANG("resign"), () => { INTERFACE.resign(); });
button_resign.setAttribute("id", "button-resign");
buttons_bottom.push(button_resign);
} else {
let button_react = UI.button("Clap", () => { INTERFACE.react(); });
buttons_bottom.push(button_react);
}
buttons_bottom.push(UI.button(LANG("back"), () => {
LOAD(SCENES.Browse);
@ -728,12 +731,7 @@ const SCENES = {
INTERFACE.uninit();
}
message(code, data) {
switch(code) {
case OpCode.GameState:
case OpCode.GamePlay: {
INTERFACE.message(code, data);
} break;
}
}
disconnect() {
LOAD(SCENES.Offline);

View File

@ -309,30 +309,61 @@ function MESSAGE(event) {
}
} break;
case OpCode.GamePlay: {
console.log("RECV GamePlay");
case OpCode.GameMessage: {
console.log("RECV GameMessage");
result = UNPACK.u32(bytes, index);
index = result.index;
let high = result.data;
result = UNPACK.u32(bytes, index);
index = result.index;
let low = result.data;
let opcode = low & 0xFF;
let dat = ((low >> 8) & 0xFFFFFF) | ((high & 0xFF) << 24);
let ext = (high >> 8) & 0xFFFFFF;
data = {
status:0,
play:new GAME.Play(0, 0, 0),
code:opcode,
};
// Status
result = UNPACK.u16(bytes, index);
index = result.index;
data.status = result.data;
switch(opcode) {
case GameMessage.Error: { } break;
// Turn
result = UNPACK.u16(bytes, index);
index = result.index;
data.turn = result.data;
case GameMessage.Move: {
data.turn = dat & 0xFFFF;
data.from = (dat >> 16) & 0x3F;
data.to = (dat >> 22) & 0x3F;
} break;
// Play description
result = UNPACK.u16(bytes, index);
index = result.index;
data.play.source = result.data & 0xF;
data.play.from = (result.data >> 4) & 0x3F;
data.play.to = (result.data >> 10) & 0x3F;
case GameMessage.Drop: {
data.turn = dat & 0xFFFF;
data.piece = (dat >> 16) & 0x3F;
data.to = (dat >> 22) & 0x3F;
} break;
case GameMessage.Alt: {
data.turn = dat & 0xFFFF;
data.from = (dat >> 16) & 0x3F;
data.to = (dat >> 22) & 0x3F;
} break;
case GameMessage.Online: {
data.client = dat & 0x3;
data.state = (dat & 0x4) != 0;
} break;
case GameMessage.Undo: {
data.state = dat & 0x3;
} break;
case GameMessage.Retire: { } break;
case GameMessage.Reaction: {
data.index = dat & 0xFFFF;
} break;
}
} break;
case OpCode.ChallengeAnswer: {