DEVELOPMENT ENVIRONMENT

~liljamo/deck-builder

60c55b1b3cdc7a6116de996cb83096dd31681688 — Jonni Liljamo 1 year, 4 months ago ea466a6
feat(client): load cards from a yaml file
A client/assets/card_manifest.yaml => client/assets/card_manifest.yaml +70 -0
@@ 0,0 1,70 @@
# - name:
#   short_details:
#     -
#   long_details:
#   cost: 0
#   actions:
#     - command:
#         R:
#           R:
#           R:
#       target_self: true
#   to_be_trashed: false

---
version: 0.0.1
cards:
  - name: The Hermits Sack
    short_details:
      - +2d4 currency
    long_details:
      - Roll 2d4 for currency
    cost: 0
    actions:
      - command:
          !RollForCurrency
            amount: 2
            sides: 4
        target_self: true
    to_be_trashed: false

  - name: Punakärpässieni
    short_details:
      - +3VP, trash this
    long_details:
      - Get 3VP, trashing the card in the process
    cost: 6
    actions:
      - command:
          !GiveVP
            amount: 3
        target_self: true
    to_be_trashed: true

  - name: Test Card 3
    short_details:
      - Mark random card in opponents deck to be trashed
    long_details:
      - Placeholder.
    cost: 6
    actions:
      - command:
          !MarkCardInDeckToBeTrashed
            index: ~
        target_self: false
    to_be_trashed: false

  - name: Test Card 4
    short_details:
      - +1d4 plays
    long_details:
      - Roll 1d4 for plays
    cost: 4
    actions:
      - command:
          !RollForPlays
            amount: 1
            sides: 4
        target_self: true
    to_be_trashed: false


M client/src/main.rs => client/src/main.rs +38 -1
@@ 29,6 29,7 @@ mod util;
mod plugins;

mod game_status;
use game_status::Card;

#[macro_export]
macro_rules! seed_gen {


@@ 92,6 93,10 @@ fn main() {
    // clear color
    app.insert_resource(ClearColor(Color::rgb(0.53, 0.53, 0.7)));

    // yaml assets
    app.add_asset::<util::YamlAsset>()
        .init_asset_loader::<util::YamlLoader>();

    // add the top level app state
    app.add_state::<AppState>();



@@ 109,6 114,8 @@ fn main() {
    app.add_plugin(plugins::MenuPlugin);
    app.add_plugin(plugins::GamePlugin);

    app.add_event::<LoadCardManifestEvent>()
        .add_system(load_card_manifest.run_if(on_event::<LoadCardManifestEvent>()));
    app.add_startup_system(setup).add_system(move_camera);
    app.run();



@@ 118,7 125,14 @@ fn main() {
#[derive(Component)]
struct PlayerCamera;

fn setup(mut commands: Commands) {
fn setup(
    mut commands: Commands,
    asset_server: Res<AssetServer>,
    mut lcm_ev_w: EventWriter<LoadCardManifestEvent>,
) {
    commands.insert_resource(CardManifestHandle { handle: asset_server.load("card_manifest.yaml") });
    lcm_ev_w.send(LoadCardManifestEvent);

    commands
        .spawn((
            Camera3dBundle {


@@ 134,6 148,18 @@ fn setup(mut commands: Commands) {
        .insert(PlayerCamera);
}

struct LoadCardManifestEvent;

fn load_card_manifest(
    mut commands: Commands,
    yaml_assets: Res<Assets<util::YamlAsset>>,
    card_manifest_handle: Res<CardManifestHandle>,
) {
    let card_manifest_yaml: String = yaml_assets.get(&card_manifest_handle.handle).unwrap().0.clone();
    let card_manifest: CardManifest = serde_yaml::from_str(&card_manifest_yaml).unwrap();
    commands.insert_resource(card_manifest);
}

fn move_camera(
    time: Res<Time>,
    input: Res<Input<KeyCode>>,


@@ 211,6 237,17 @@ impl Default for NetworkingOptions {
    }
}

#[derive(Resource, serde::Deserialize)]
pub struct CardManifest {
    pub version: String,
    pub cards: Vec<Card>,
}

#[derive(Resource)]
pub struct CardManifestHandle {
    pub handle: Handle<util::YamlAsset>,
}

#[derive(Resource, Reflect)]
#[reflect(Resource)]
pub struct Global {

M client/src/plugins/game/ui/mod.rs => client/src/plugins/game/ui/mod.rs +22 -80
@@ 11,9 11,9 @@ use bevy_egui::{egui, EguiContexts};

use crate::{
    api::game::{Action, Command, Game},
    game_status::{Card, CardAction, PlayerState},
    game_status::PlayerState,
    plugins::GameActionCreateCallEvent,
    AppState, Global,
    AppState, Global, CardManifest,
};

use super::{GameData, RefreshGameEvent};


@@ 337,6 337,7 @@ pub fn dev_details_ui(
    mut contexts: EguiContexts,
    global: Res<Global>,
    game_data: Res<GameData>,
    card_manifest: Res<CardManifest>,
    mut create_action_ev_w: EventWriter<GameActionCreateCallEvent>,
    mut rg_ev_w: EventWriter<RefreshGameEvent>,
) {


@@ 357,7 358,7 @@ pub fn dev_details_ui(
                if status.actions.is_empty() && game.host_id == global.user.as_ref().unwrap().id {
                    if ui.button("Init Game").clicked() {
                        // NOTE/FIXME: hardcoded game init
                        hardcoded_init(game, &mut create_action_ev_w);
                        hardcoded_init(game, &mut create_action_ev_w, &card_manifest);
                        rg_ev_w.send(RefreshGameEvent);
                    }
                }


@@ 380,85 381,26 @@ pub fn dev_details_ui(
        });
}

fn hardcoded_init(game: &Game, create_action_ev_w: &mut EventWriter<GameActionCreateCallEvent>) {
fn hardcoded_init(
    game: &Game,
    create_action_ev_w: &mut EventWriter<GameActionCreateCallEvent>,
    card_manifest: &CardManifest,
) {
    // first, piles
    create_action_ev_w.send(GameActionCreateCallEvent {
        action: Action::new(
            &game.id,
            &game.host_id,
            &game.host_id,
            &Command::InitSupplyPile {
                card: Card {
                    name: "Narcissistic Cannibal".to_string(),
                    short_details: vec![],
                    long_details: vec![],
                    cost: 4,
                    actions: vec![],
                },
                amount: 10,
            },
            None,
        ),
    });
    create_action_ev_w.send(GameActionCreateCallEvent {
        action: Action::new(
            &game.id,
            &game.host_id,
            &game.host_id,
            &Command::InitSupplyPile {
                card: Card {
                    name: "Gib Möney".to_string(),
                    short_details: vec!["Roll 1d6 for currency".to_string()],
                    long_details: vec![],
                    cost: 0,
                    actions: vec![CardAction {
                        command: Command::RollForCurrency {
                            amount: 1,
                            sides: 6,
                        },
                    }],
                },
                amount: 10,
            },
            None,
        ),
    });
    create_action_ev_w.send(GameActionCreateCallEvent {
        action: Action::new(
            &game.id,
            &game.host_id,
            &game.host_id,
            &Command::InitSupplyPile {
                card: Card {
                    name: "Test Card 3".to_string(),
                    short_details: vec![],
                    long_details: vec![],
                    cost: 4,
                    actions: vec![],
    for card in &card_manifest.cards {
        create_action_ev_w.send(GameActionCreateCallEvent {
            action: Action::new(
                &game.id,
                &game.host_id,
                &game.host_id,
                &Command::InitSupplyPile {
                    card: card.clone(),
                    amount: 6,
                },
                amount: 10,
            },
            None,
        ),
    });
    create_action_ev_w.send(GameActionCreateCallEvent {
        action: Action::new(
            &game.id,
            &game.host_id,
            &game.host_id,
            &Command::InitSupplyPile {
                card: Card {
                    name: "Test Card 4".to_string(),
                    short_details: vec![],
                    long_details: vec![],
                    cost: 2,
                    actions: vec![],
                },
                amount: 10,
            },
            None,
        ),
    });
                None,
            ),
        });
    }

    // second, set a player to the action phase, to start the game
    create_action_ev_w.send(GameActionCreateCallEvent {

M client/src/util/mod.rs => client/src/util/mod.rs +28 -0
@@ 6,6 6,8 @@
 * See LICENSE for licensing information.
 */

use bevy::{asset::{AssetLoader, LoadedAsset}, reflect::TypeUuid};

pub mod egui;

mod action_to_log;


@@ 27,3 29,29 @@ pub fn get_next_player<'a>(player: &'a PlayerStatus, game_status: &'a GameStatus
        .find(|np| np.1.turn_n == next_turn_n)
        .unwrap()
}

#[derive(Debug, TypeUuid)]
#[uuid = "da42e27e-e968-4c6a-9892-d96e38b0e643"]
pub struct YamlAsset(pub String);

#[derive(Default)]
pub struct YamlLoader;

impl AssetLoader for YamlLoader {
    fn load<'a>(
            &'a self,
            bytes: &'a [u8],
            load_context: &'a mut bevy::asset::LoadContext,
        ) -> bevy::utils::BoxedFuture<'a, Result<(), bevy::asset::Error>> {
        Box::pin(async move {
            let yaml_str = std::str::from_utf8(bytes)?;
            let asset = YamlAsset(yaml_str.into());
            load_context.set_default_asset(LoadedAsset::new(asset));
            Ok(())
        })
    }

    fn extensions(&self) -> &[&str] {
        &["yaml"]
    }
}