Placeholder castle

This commit is contained in:
Tobias Berger 2024-04-11 14:15:59 +02:00
parent 78e4dedf21
commit e64742a159
Signed by: toby
GPG key ID: 2D05EFAB764D6A88
4 changed files with 165 additions and 110 deletions

4
Cargo.lock generated
View file

@ -1599,9 +1599,9 @@ checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58"
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.35" version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]

BIN
assets/images/castle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View file

@ -2,7 +2,8 @@
#![allow( #![allow(
clippy::cast_possible_truncation, clippy::cast_possible_truncation,
clippy::cast_sign_loss, clippy::cast_sign_loss,
clippy::too_many_lines clippy::too_many_lines,
clippy::module_name_repetitions
)] )]
mod card; mod card;
@ -24,15 +25,48 @@ use player::{Player, Stats};
fn main() { fn main() {
nannou::app(Model::init) nannou::app(Model::init)
.update(update) .update(update)
.loop_mode(LoopMode::RefreshSync) .loop_mode(LoopMode::Wait)
.run(); .run();
} }
struct Textures {
ant: wgpu::Texture,
castle: wgpu::Texture,
}
impl Textures {
fn load(app: &App) -> Self {
let ant = wgpu::Texture::from_image(
app,
&image::load_from_memory_with_format(
include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/assets/images/ant.png"
)),
ImageFormat::Png,
)
.expect("Failed to load image"),
);
let castle = wgpu::Texture::from_image(
app,
&image::load_from_memory_with_format(
include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/assets/images/castle.png"
)),
ImageFormat::Png,
)
.expect("Failed to load image"),
);
Self { ant, castle }
}
}
struct Model { struct Model {
// Store the window ID so we can refer to this specific window later if needed. // Store the window ID so we can refer to this specific window later if needed.
window: WindowId, window: WindowId,
font: Font, font: Font,
texture: wgpu::Texture, textures: Textures,
ups: f64, ups: f64,
just_clicked: RwLock<bool>, just_clicked: RwLock<bool>,
red_hand: RwLock<[Card; HAND_CARD_COUNT]>, red_hand: RwLock<[Card; HAND_CARD_COUNT]>,
@ -53,20 +87,9 @@ impl Model {
.build() .build()
.unwrap(); .unwrap();
let texture = wgpu::Texture::from_image(
app,
&image::load_from_memory_with_format(
include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/assets/images/ant.png"
)),
ImageFormat::Png,
)
.expect("Failed to load image"),
);
Self { Self {
window, window,
texture, textures: Textures::load(app),
font: Font::from_bytes(include_bytes!(concat!( font: Font::from_bytes(include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"), env!("CARGO_MANIFEST_DIR"),
"/assets/fonts/Monocraft/Monocraft-NF.ttf" "/assets/fonts/Monocraft/Monocraft-NF.ttf"
@ -136,56 +159,88 @@ const HAND_CARD_COUNT: usize = 8;
const HAND_ASPECT_WIDTH: f32 = 1f32; const HAND_ASPECT_WIDTH: f32 = 1f32;
const HAND_ASPECT_HEIGHT: f32 = 2f32; const HAND_ASPECT_HEIGHT: f32 = 2f32;
// Draw the state of your `Model` into the given `Frame` here. // Draw the state of your `Model` into the given `Frame` here.
fn player_panel(model: &Model, draw: &Draw, bounds: Rect, player: Player) {
fn player_panel( let part_bounds = bounds.part_horizontal(match player {
draw: &Draw, Player::Red => 0.8,
bounds: Rect, Player::Black => 0.2,
player_stats: &RwLock<Stats>, });
player: Player, let [castle_bounds, status_bounds] = match player {
font: Font, Player::Red => part_bounds,
) { Player::Black => [part_bounds[1], part_bounds[0]],
let rect = Rect::from_x_y_w_h(bounds.x(), bounds.y(), 1f32, 5f32).fit_into(&bounds);
let rect = match player {
Player::Red => rect.align_right_of(bounds),
Player::Black => rect.align_left_of(bounds),
}; };
let player_stats = player_stats.read().expect("player stats poisoned"); let castle_bounds = castle_bounds.top_part(0.7);
let castle_rect = Rect::from_x_y_w_h(
castle_bounds.x(),
castle_bounds.y(),
model.textures.castle.width() as f32,
model.textures.castle.height() as f32,
)
.fit_into(castle_bounds);
draw.texture(&model.textures.castle)
.xy(castle_rect.xy())
.wh(castle_rect.wh())
.finish();
let status_rect = status_bounds
.align_left_of(status_bounds)
.align_middle_y_of(status_bounds);
let player_stats = *model
.stats_of(player)
.read()
.expect("player stats poisoned");
let status = match player { let status = match player {
Player::Red => format!( Player::Red => format!(
"{:<3} Builders\n{:<5} Bricks\n{:<3} Soldiers\n{:<4} Weapons\n{:<7} Magi\n{:<3} Crystals\n{:<5} Castle\n{:<6} Fence", "{:<3} Builders\n{:<5} Bricks\n{:<3} Soldiers\n{:<4} Weapons\n{:<7} Magi\n{:<3} Crystals\n{:<5} Castle\n{:<6} Fence",
player_stats.builders, player_stats.builders,
player_stats.bricks, player_stats.bricks,
player_stats.soldiers, player_stats.soldiers,
player_stats.weapons, player_stats.weapons,
player_stats.magi, player_stats.magi,
player_stats.crystals, player_stats.crystals,
player_stats.castle, player_stats.castle,
player_stats.fence, player_stats.fence,
), ),
Player::Black => format!( Player::Black => format!(
"Builders {:>3}\nBricks {:>5}\nSoldiers {:>3}\nWeapons {:>4}\nMagi {:>7}\nCrystals {:>3}\nCastle {:>5}\nFence {:>6}", "Builders {:>3}\nBricks {:>5}\nSoldiers {:>3}\nWeapons {:>4}\nMagi {:>7}\nCrystals {:>3}\nCastle {:>5}\nFence {:>6}",
player_stats.builders, player_stats.builders,
player_stats.bricks, player_stats.bricks,
player_stats.soldiers, player_stats.soldiers,
player_stats.weapons, player_stats.weapons,
player_stats.magi, player_stats.magi,
player_stats.crystals, player_stats.crystals,
player_stats.castle, player_stats.castle,
player_stats.fence, player_stats.fence,
) )
}; };
drop(player_stats); let character_size = model
.font
.glyph('A')
.standalone()
.get_data()
.expect("No glyph found")
.extents
.unwrap();
let character_width = character_size.width();
let character_height = character_size.height();
let character_aspect_ratio = character_width as f32 / character_height as f32;
let font_size = f32::min(
status_bounds.w() / 12f32 / character_aspect_ratio,
status_bounds.h() / 8f32,
) as u32;
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
let drawing = draw let drawing = draw
.text(&status) .text(&status)
.xy(rect.xy()) .xy(status_rect.xy())
.wh(rect.wh()) .wh(status_rect.wh())
.color(WHITE) .color(WHITE)
.font_size((rect.h() * 0.02f32).trunc() as u32) .font_size(font_size)
.align_text_middle_y() .align_text_middle_y()
.font(font) .font(model.font.clone())
.color(match player { .color(match player {
Player::Red => RED, Player::Red => RED,
Player::Black => WHITE, Player::Black => WHITE,
@ -213,14 +268,14 @@ fn hand(
HAND_CARD_COUNT as f32 * HAND_ASPECT_WIDTH, HAND_CARD_COUNT as f32 * HAND_ASPECT_WIDTH,
HAND_ASPECT_HEIGHT, HAND_ASPECT_HEIGHT,
) )
.fit_into(&bounds) .fit_into(bounds)
.align_bottom_of(bounds); .align_bottom_of(bounds);
let current_player = *model let current_player = *model
.current_player .current_player
.read() .read()
.expect("current player poisoned"); .expect("current player poisoned");
for (idx, rect) in hand_rect for (idx, &rect) in hand_rect
.split_horizontal::<HAND_CARD_COUNT>() .split_horizontal::<HAND_CARD_COUNT>()
.iter() .iter()
.enumerate() .enumerate()
@ -239,11 +294,11 @@ fn hand(
let image_rect = Rect::from_x_y_w_h( let image_rect = Rect::from_x_y_w_h(
rect.x(), rect.x(),
rect.y(), rect.y(),
model.texture.width() as f32, model.textures.ant.width() as f32,
model.texture.height() as f32, model.textures.ant.height() as f32,
) )
.fit_into(rect) .fit_into(rect)
.align_bottom_of(*rect); .align_bottom_of(rect);
draw.text(&format!( draw.text(&format!(
"{:?}\n{}{}", "{:?}\n{}{}",
@ -275,7 +330,7 @@ fn hand(
.font(model.font.clone()) .font(model.font.clone())
.finish(); .finish();
draw.texture(&model.texture) draw.texture(&model.textures.ant)
.xy(image_rect.xy()) .xy(image_rect.xy())
.wh(image_rect.wh()) .wh(image_rect.wh())
.finish(); .finish();
@ -339,22 +394,11 @@ fn view(app: &App, model: &Model, frame: Frame) {
let window_rect = window.rect(); let window_rect = window.rect();
let player_one_bounds = window_rect.left_part(0.2); let [player_one_rect, player_two_rect] = window_rect.split_horizontal::<2>();
player_panel( let player_one_bounds = player_one_rect;
&draw, player_panel(model, &draw, player_one_bounds, Player::Black);
player_one_bounds, let player_two_bounds = player_two_rect;
model.stats_of(Player::Black), player_panel(model, &draw, player_two_bounds, Player::Red);
Player::Black,
model.font.clone(),
);
let player_two_bounds = window_rect.right_part(0.2);
player_panel(
&draw,
player_two_bounds,
model.stats_of(Player::Red),
Player::Red,
model.font.clone(),
);
let hand_bounds = window_rect.bottom_part(0.3); let hand_bounds = window_rect.bottom_part(0.3);
hand( hand(

View file

@ -1,28 +1,31 @@
use nannou::geom::Rect; use nannou::{geom::Rect, math::num_traits::Float};
pub trait RectHelper { pub trait RectHelper<S: Float> {
fn fit_into(&self, bounds: &Rect<f32>) -> Rect<f32>; fn fit_into(self, bounds: Rect<S>) -> Rect<S>;
fn bottom_half(&self) -> Rect<f32>; fn bottom_half(self) -> Rect<S>;
fn bottom_part(&self, part: f32) -> Rect<f32>; fn bottom_part(self, part: S) -> Rect<S>;
fn top_half(&self) -> Rect<f32>; fn top_half(self) -> Rect<S>;
fn top_part(&self, part: f32) -> Rect<f32>; fn top_part(self, part: S) -> Rect<S>;
fn left_half(&self) -> Rect<f32>; fn left_half(self) -> Rect<S>;
fn left_part(&self, part: f32) -> Rect<f32>; fn left_part(self, part: S) -> Rect<S>;
fn right_half(&self) -> Rect<f32>; fn right_half(self) -> Rect<S>;
fn right_part(&self, part: f32) -> Rect<f32>; fn right_part(self, part: S) -> Rect<S>;
fn split_horizontal<const N: usize>(&self) -> [Rect<f32>; N]; fn part_horizontal(self, part: S) -> [Rect<S>; 2];
fn split_vertical<const N: usize>(&self) -> [Rect<f32>; N]; fn part_vertical(self, part: S) -> [Rect<S>; 2];
fn split_horizontal_vec(&self, num: usize) -> Vec<Rect<f32>>;
fn split_vertical_vec(&self, num: usize) -> Vec<Rect<f32>>; fn split_horizontal<const N: usize>(self) -> [Rect<S>; N];
fn split_vertical<const N: usize>(self) -> [Rect<S>; N];
fn split_horizontal_vec(self, num: usize) -> Vec<Rect<S>>;
fn split_vertical_vec(self, num: usize) -> Vec<Rect<S>>;
} }
impl RectHelper for Rect<f32> { impl RectHelper<f32> for Rect<f32> {
fn fit_into(&self, bounds: &Rect<f32>) -> Self { fn fit_into(self, bounds: Self) -> Self {
let content_aspect_ratio = self.w() / self.h(); let content_aspect_ratio = self.w() / self.h();
let bounds_aspect_ratio = bounds.w() / bounds.h(); let bounds_aspect_ratio = bounds.w() / bounds.h();
@ -43,11 +46,11 @@ impl RectHelper for Rect<f32> {
} }
} }
fn bottom_half(&self) -> Self { fn bottom_half(self) -> Self {
Self::from_corners(self.mid_left(), self.bottom_right()) Self::from_corners(self.mid_left(), self.bottom_right())
} }
fn bottom_part(&self, part: f32) -> Self { fn bottom_part(self, part: f32) -> Self {
debug_assert!((0f32..=1f32).contains(&part)); debug_assert!((0f32..=1f32).contains(&part));
Self::from_corner_points( Self::from_corner_points(
[self.left(), self.bottom()], [self.left(), self.bottom()],
@ -55,11 +58,11 @@ impl RectHelper for Rect<f32> {
) )
} }
fn top_half(&self) -> Self { fn top_half(self) -> Self {
Self::from_corners(self.top_left(), self.mid_right()) Self::from_corners(self.top_left(), self.mid_right())
} }
fn top_part(&self, part: f32) -> Self { fn top_part(self, part: f32) -> Self {
debug_assert!((0f32..=1f32).contains(&part)); debug_assert!((0f32..=1f32).contains(&part));
Self::from_corner_points( Self::from_corner_points(
[self.left(), self.top()], [self.left(), self.top()],
@ -67,11 +70,11 @@ impl RectHelper for Rect<f32> {
) )
} }
fn left_half(&self) -> Self { fn left_half(self) -> Self {
Self::from_corners(self.top_left(), self.mid_bottom()) Self::from_corners(self.top_left(), self.mid_bottom())
} }
fn left_part(&self, part: f32) -> Self { fn left_part(self, part: f32) -> Self {
debug_assert!((0f32..=1f32).contains(&part)); debug_assert!((0f32..=1f32).contains(&part));
Self::from_corner_points( Self::from_corner_points(
[self.left(), self.bottom()], [self.left(), self.bottom()],
@ -79,11 +82,11 @@ impl RectHelper for Rect<f32> {
) )
} }
fn right_half(&self) -> Self { fn right_half(self) -> Self {
Self::from_corners(self.top_left(), self.mid_bottom()) Self::from_corners(self.mid_top(), self.bottom_right())
} }
fn right_part(&self, part: f32) -> Self { fn right_part(self, part: f32) -> Self {
debug_assert!((0f32..=1f32).contains(&part)); debug_assert!((0f32..=1f32).contains(&part));
Self::from_corner_points( Self::from_corner_points(
[self.right(), self.bottom()], [self.right(), self.bottom()],
@ -91,8 +94,16 @@ impl RectHelper for Rect<f32> {
) )
} }
fn part_horizontal(self, part: f32) -> [Self; 2] {
[self.left_part(part), self.right_part(1f32 - part)]
}
fn part_vertical(self, part: f32) -> [Self; 2] {
[self.bottom_part(part), self.top_part(1f32 - part)]
}
#[allow(clippy::cast_precision_loss)] #[allow(clippy::cast_precision_loss)]
fn split_horizontal<const N: usize>(&self) -> [Self; N] { fn split_horizontal<const N: usize>(self) -> [Self; N] {
let mut subdivisions = [unsafe { std::mem::zeroed() }; N]; let mut subdivisions = [unsafe { std::mem::zeroed() }; N];
let subdivided_width = self.w() / N as f32; let subdivided_width = self.w() / N as f32;
@ -108,7 +119,7 @@ impl RectHelper for Rect<f32> {
} }
#[allow(clippy::cast_precision_loss)] #[allow(clippy::cast_precision_loss)]
fn split_horizontal_vec(&self, num: usize) -> Vec<Self> { fn split_horizontal_vec(self, num: usize) -> Vec<Self> {
let mut subdivisions = Vec::with_capacity(num); let mut subdivisions = Vec::with_capacity(num);
let subdivided_width = self.w() / num as f32; let subdivided_width = self.w() / num as f32;
@ -124,7 +135,7 @@ impl RectHelper for Rect<f32> {
} }
#[allow(clippy::cast_precision_loss)] #[allow(clippy::cast_precision_loss)]
fn split_vertical<const N: usize>(&self) -> [Self; N] { fn split_vertical<const N: usize>(self) -> [Self; N] {
let mut subdivisions = [unsafe { std::mem::zeroed() }; N]; let mut subdivisions = [unsafe { std::mem::zeroed() }; N];
let subdivided_height = self.h() / N as f32; let subdivided_height = self.h() / N as f32;
@ -140,7 +151,7 @@ impl RectHelper for Rect<f32> {
} }
#[allow(clippy::cast_precision_loss)] #[allow(clippy::cast_precision_loss)]
fn split_vertical_vec(&self, num: usize) -> Vec<Self> { fn split_vertical_vec(self, num: usize) -> Vec<Self> {
let mut subdivisions = Vec::with_capacity(num); let mut subdivisions = Vec::with_capacity(num);
let subdivided_height = self.h() / num as f32; let subdivided_height = self.h() / num as f32;