Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .mailmap
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Andrei Kurochkin <end41r@gmx.de> <andi.kurochkin@gmail.com>
Andrei Kurochkin <end41r@gmx.de> <82461251+end41r@users.noreply.github.com>
Konrad Kortes <konrad.kortes@gmail.com> <konrad@MacBook-Pro-von-Konrad.local>
Konrad Kortes <konrad.kortes@gmail.com> <konrad@MacBookPro.fritz.box>
Konrad Kortes <konrad.kortes@gmail.com> <konrad@MacBook-Pro-von-Konrad.fritz.box>
Tom Heinrich <tom2005.heinrich@gmail.com>
Paul Gustav Hoffmann <241015669+VoidNoVerges@users.noreply.github.com>
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ derive_more = { version = "2.1.1", features = ["deref", "deref_mut"] }
rand = "0.9.2"
strum = "0.27"
strum_macros = "0.27"

[features]
wiz_debug = []
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,7 @@ We also have a pr check for clippy and fmt, so if it fails, please use
- `cargo fmt` to fix formatting (automatically)
- `cargo clippy` to check for the warnings (most of them will be gone if you use `cargo clippy --fix --bin "wizard"`)
- If you really trust clippy, you can also do `cargo clippy --fix --bin "wizard" --allow-dirty`, but its better not to use it :)

### To run in debug mode with the wiz_debug feature enabled, run
`cargo run --features wiz_debug`
This will allow you to start the game with 1 player.
Binary file added assets/MagicSchoolOne.ttf
Binary file not shown.
Binary file added assets/Menu_Button.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/background_forall.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/button1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/ingame_background.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/menu_container.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/wizard_lobby_menu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/wizard_main_menu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/wizard_main_menu2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
106 changes: 102 additions & 4 deletions src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ pub enum PlayerCount {
P6,
}

const TITLE_FONT: &[u8] = include_bytes!("../../assets/MagicSchoolOne.ttf");

impl PlayerCount {
pub fn to_usize(self) -> usize {
match self {
Expand Down Expand Up @@ -54,7 +56,6 @@ pub enum MenuState {
#[allow(dead_code)]
PlayingTest,
}

pub struct App {
window_size: Size,

Expand Down Expand Up @@ -99,6 +100,22 @@ pub struct App {

// Gameplay view state
pub viewable_hand: ViewableHand,

// UI Buttons (main menu)
pub btn_host: crate::client::views::Button,
pub btn_join: crate::client::views::Button,
pub btn_rules: crate::client::views::Button,
pub btn_exit: crate::client::views::Button,

// Buttons for other menus
pub btn_create_lobby: crate::client::views::Button,
pub btn_back: crate::client::views::Button,
pub btn_connect: crate::client::views::Button,
pub btn_send_chat: crate::client::views::Button,
pub btn_start_game: crate::client::views::Button,
pub btn_back_to_menu: crate::client::views::Button,

pub btn_ready_owned: crate::client::views::Button,
}

impl Default for App {
Expand All @@ -115,7 +132,6 @@ impl Default for App {
msg: String::new(),

menu: MenuState::Main,

host_name: "".to_string(),
host_player_count: PlayerCount::P4,
join_name: "".to_string(),
Expand Down Expand Up @@ -152,6 +168,81 @@ impl Default for App {
winner: None,

viewable_hand: ViewableHand::new(window_size),

//Buttons
btn_host: crate::client::views::Button::new(0, "Host", "assets/button1.png", 180, 44),
btn_join: crate::client::views::Button::new(
1,
"Beitreten",
"assets/button1.png",
180,
44,
),
btn_rules: crate::client::views::Button::new(
2,
"Spielregeln",
"assets/button1.png",
180,
44,
),
btn_exit: crate::client::views::Button::new(
3,
"Spiel verlassen",
"assets/button1.png",
180,
44,
),

btn_create_lobby: crate::client::views::Button::new(
10,
"Lobby erstellen",
"assets/button1.png",
160,
40,
),
btn_back: crate::client::views::Button::new(
11,
"zurück",
"assets/button1.png",
100,
36,
),
btn_connect: crate::client::views::Button::new(
12,
"Verbinden",
"assets/button1.png",
140,
40,
),
btn_send_chat: crate::client::views::Button::new(
13,
"Senden",
"assets/button1.png",
100,
36,
),
btn_start_game: crate::client::views::Button::new(
14,
"Starten",
"assets/button1.png",
140,
40,
),
btn_back_to_menu: crate::client::views::Button::new(
15,
"Zurück zum Menü",
"assets/button1.png",
160,
40,
),

btn_ready_owned: crate::client::views::Button::new(
20,
"Bereit",
"assets/button1.png",
100,
36,
),
}
}
}
Expand All @@ -167,7 +258,6 @@ pub enum AppMessage {
HostPlayerCountChanged(PlayerCount),
JoinNameChanged(String),
ServerAddressChanged(String),
CopyToClipboard(String),

SendChat,
ChatInputChanged(String),
Expand All @@ -192,6 +282,9 @@ pub enum AppMessage {

// Gameplay view messages
HandMessage(HandMessage),

// Button messages from view widgets
ButtonMessage(crate::client::views::ButtonMessage),
}

fn subscription(state: &App) -> Subscription<AppMessage> {
Expand All @@ -205,10 +298,15 @@ fn subscription(state: &App) -> Subscription<AppMessage> {
}

pub fn main() -> iced::Result {
use std::borrow::Cow;
iced::application(App::default, update, view)
.title("Wizard")
.subscription(subscription)
// Keep this value in sync with the App::default function.
.window_size(Size::new(640.0, 480.0))
.window_size(Size::new(300.0, 800.0))
.settings(iced::Settings {
fonts: vec![Cow::Borrowed(TITLE_FONT)],
..Default::default()
})
.run()
}
120 changes: 112 additions & 8 deletions src/client/update.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use iced::{clipboard, Task};
use iced::Task;
use std::sync::Arc;

use super::{connect_ws, App, AppMessage, MenuState, PlayerCount};
Expand Down Expand Up @@ -70,11 +70,6 @@ pub fn update(state: &mut App, msg: AppMessage) -> Task<AppMessage> {
state.ip = addr;
Task::none()
}
AppMessage::CopyToClipboard(addr) => {
state.last_msg = "Server address copied to clipboard.".to_string();
clipboard::write(addr)
}

AppMessage::SendChat => {
if let Ok(guard) = state.ws_tx.lock() {
if let Some(ref tx) = *guard {
Expand Down Expand Up @@ -295,9 +290,106 @@ pub fn update(state: &mut App, msg: AppMessage) -> Task<AppMessage> {
state.viewable_hand.update_with_msg(hand_msg);
Task::none()
}
AppMessage::ButtonMessage(btn_msg) => {
// route to buttons (each button filters by id internally)
state.btn_host.update_with_msg(btn_msg.clone());
state.btn_join.update_with_msg(btn_msg.clone());
state.btn_rules.update_with_msg(btn_msg.clone());
state.btn_exit.update_with_msg(btn_msg.clone());

state.btn_create_lobby.update_with_msg(btn_msg.clone());
state.btn_back.update_with_msg(btn_msg.clone());
state.btn_connect.update_with_msg(btn_msg.clone());
state.btn_send_chat.update_with_msg(btn_msg.clone());
state.btn_start_game.update_with_msg(btn_msg.clone());
state.btn_back_to_menu.update_with_msg(btn_msg.clone());

state.btn_ready_owned.update_with_msg(btn_msg);
Task::none()
}
AppMessage::AnimationTick => {
state.viewable_hand.update_animations();
Task::none()

// Update button animations
state.btn_host.update_animations();
state.btn_join.update_animations();
state.btn_rules.update_animations();
state.btn_exit.update_animations();

state.btn_create_lobby.update_animations();
state.btn_back.update_animations();
state.btn_connect.update_animations();
state.btn_send_chat.update_animations();
state.btn_start_game.update_animations();
state.btn_back_to_menu.update_animations();
state.btn_ready_owned.update_animations();

let mut pending_msgs: Vec<Task<AppMessage>> = Vec::new();

state.btn_host.check_click_end(|&_id| {
pending_msgs.push(Task::done(AppMessage::Host));
});

state.btn_join.check_click_end(|_| {
pending_msgs.push(Task::done(AppMessage::Navigate(MenuState::Join)));
});

state.btn_rules.check_click_end(|_| {
pending_msgs.push(Task::done(AppMessage::GameRules));
});

state.btn_exit.check_click_end(|_| {
pending_msgs.push(Task::done(AppMessage::CloseGame));
});

state.btn_create_lobby.check_click_end(|&_id| {
pending_msgs.push(Task::done(AppMessage::CreateLobby));
});

state.btn_ready_owned.check_click_end(|&_id| {
if let Some(my_id) = state.my_id {
pending_msgs.push(Task::done(AppMessage::ToggleReady(my_id)));
}
});

state.btn_back.check_click_end(|_| {
pending_msgs.push(Task::done(AppMessage::Navigate(MenuState::Main)));
});

state.btn_connect.check_click_end(|_| {
pending_msgs.push(Task::done(AppMessage::Connect));
});

state.btn_send_chat.check_click_end(|_| {
pending_msgs.push(Task::done(AppMessage::SendChat));
});

state.btn_start_game.check_click_end(|_| {
let can_start = if cfg!(feature = "wiz_debug") {
true
} else {
state.lobby.as_ref().is_some_and(|lobby| {
lobby.players.len() == state.host_player_count.to_usize()
&& lobby.players.iter().all(|p| p.ready)
})
};
let is_host = state.my_id.is_some()
&& state.my_id.unwrap()
== state
.lobby
.as_ref()
.and_then(|l| l.players.iter().find(|p| p.is_host).map(|p| p.id))
.unwrap_or_default();
if can_start && is_host {
pending_msgs.push(Task::done(AppMessage::StartGame));
}
});

state.btn_back_to_menu.check_click_end(|_| {
pending_msgs.push(Task::done(AppMessage::BackToMenu));
});

Task::batch(pending_msgs)
}
AppMessage::WindowResized(size) => {
state.window_size = size;
Expand Down Expand Up @@ -456,7 +548,19 @@ fn handle_server_message(state: &mut App, msg: ServerMessage) {
println!("{}", log);
state.game_log.push(log.clone());
state.last_msg = log;
state.menu = MenuState::Playing;

// Check if the host's name is "wizard_master" to enter debug
// Easter egg :)
if let Some(ref lobby) = state.lobby {
if let Some(host) = lobby.players.iter().find(|p| p.is_host) {
if host.name == "wizard_master" {
state.menu = MenuState::PlayingTest;
} else {
state.menu = MenuState::Playing;
}
}
}

state.scores.clear();
state.bids.clear();
state.tricks_won.clear();
Expand Down
Loading