/*
* This file is part of laurelin_client
* Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
*
* Licensed under GPL-3.0-only.
* See LICENSE for licensing information.
*/
use bevy::prelude::*;
use bevy_mod_picking::prelude::*;
use bevy_picking_rapier::RapierPickTarget;
use bevy_rapier3d::prelude::*;
use bevy_text_mesh::prelude::*;
use crate::game_status::Card;
pub struct CardPlugin;
impl Plugin for CardPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<VisualCardData>()
.add_system(on_spawn_card);
}
}
// NOTE: kind of like using enum variants
pub mod visual_card_kind {
use bevy::prelude::*;
#[derive(Component)]
pub struct Normal;
#[derive(Component)]
pub struct Supply(pub usize);
#[derive(Component)]
pub struct Hand(pub usize);
}
#[derive(Component, Clone)]
pub struct ClickedCard;
#[derive(Component)]
pub struct VisualCard {
pub card: Card,
}
impl VisualCard {
pub const ASPECT_RATIO: f32 = 5. / 8.;
}
impl Default for VisualCard {
fn default() -> Self {
Self {
card: Card {
name: "".to_string(),
short_details: vec![],
long_details: vec![],
cost: 0,
actions: vec![],
to_be_trashed: false,
},
}
}
}
#[derive(Bundle)]
pub struct VisualCardBundle {
pub visual_card: VisualCard,
pub transform: Transform,
pub global_transform: GlobalTransform,
pub visibility: Visibility,
pub computed_visibility: ComputedVisibility,
pub collider: Collider,
pub rigid_body: RigidBody,
}
impl Default for VisualCardBundle {
fn default() -> Self {
Self {
visual_card: VisualCard::default(),
transform: Transform {
translation: Vec3 {
x: 0.,
y: 0.,
z: 0.,
},
/// defaults to being rotated 90 degress on X, so flat on the
/// table.
rotation: Quat::from_euler(EulerRot::XYZ, -1.571, 0., 0.),
..Default::default()
},
global_transform: GlobalTransform::default(),
visibility: Visibility::default(),
computed_visibility: ComputedVisibility::default(),
collider: Collider::cuboid(
VisualCard::ASPECT_RATIO / 2.,
VisualCard::ASPECT_RATIO,
0.01,
),
rigid_body: RigidBody::Dynamic,
}
}
}
#[derive(Resource)]
pub struct VisualCardData {
font_normal: Handle<TextMeshFont>,
font_medium: Handle<TextMeshFont>,
pub font_bold: Handle<TextMeshFont>,
mesh: Handle<Mesh>,
card_background: Handle<StandardMaterial>,
}
impl FromWorld for VisualCardData {
fn from_world(world: &mut World) -> Self {
let world = world.cell();
let mut meshes = world.resource_mut::<Assets<Mesh>>();
let mut materials = world.resource_mut::<Assets<StandardMaterial>>();
let asset_server = world.resource::<AssetServer>();
let card_background_material = StandardMaterial {
unlit: true,
alpha_mode: AlphaMode::Blend,
base_color_texture: Some(asset_server.load("card_background.png")),
..Default::default()
};
Self {
font_normal: asset_server.load("fonts/FiraMono-Regular.ttf#mesh"),
font_medium: asset_server.load("fonts/FiraMono-Medium.ttf#mesh"),
font_bold: asset_server.load("fonts/FiraMono-Bold.ttf#mesh"),
mesh: meshes.add(
shape::Quad {
size: Vec2::new(VisualCard::ASPECT_RATIO / 2., VisualCard::ASPECT_RATIO),
..Default::default()
}
.into(),
),
card_background: materials.add(card_background_material),
}
}
}
fn on_spawn_card(
mut commands: Commands,
card_data: Res<VisualCardData>,
new_cards: Query<(Entity, &VisualCard), Added<VisualCard>>,
) {
for (entity, card) in &new_cards {
commands.entity(entity).insert((
PickableBundle::default(),
RapierPickTarget::default(),
OnPointer::<Click>::target_insert(ClickedCard),
));
commands.entity(entity).with_children(|parent| {
parent.spawn((PbrBundle {
material: card_data.card_background.clone(),
mesh: card_data.mesh.clone(),
transform: Transform {
scale: Vec3::new(2., 2., 1.),
..Default::default()
},
..Default::default()
},));
// card name
parent.spawn(TextMeshBundle {
text_mesh: TextMesh {
text: card.card.name.clone(),
style: TextMeshStyle {
font: card_data.font_medium.clone(),
font_size: SizeUnit::NonStandard(5.),
color: Color::rgb(0.2, 0.2, 0.2),
..Default::default()
},
..Default::default()
},
transform: Transform::from_xyz(-0.3, 0.2, 0.),
..Default::default()
});
// card cost
parent.spawn(TextMeshBundle {
text_mesh: TextMesh {
text: card.card.cost.to_string(),
style: TextMeshStyle {
font: card_data.font_bold.clone(),
font_size: SizeUnit::NonStandard(7.),
color: Color::rgb(1., 1., 1.),
..Default::default()
},
..Default::default()
},
transform: Transform::from_xyz(0.2, -0.575, 0.),
..Default::default()
});
parent.spawn(TextMeshBundle {
text_mesh: TextMesh {
text: card.card.short_details.join("\n"),
style: TextMeshStyle {
font: card_data.font_normal.clone(),
font_size: SizeUnit::NonStandard(4.),
color: Color::rgb(1., 1., 1.),
..Default::default()
},
size: TextMeshSize {
width: SizeUnit::NonStandard(42.),
..Default::default()
},
..Default::default()
},
transform: Transform::from_xyz(-0.25, 0.075, 0.),
..Default::default()
});
});
}
}