├── .gitignore ├── cover.png ├── assets ├── heart.png ├── icon.png ├── log.png ├── tile.png ├── goblin.png ├── card_base.png ├── tile_base.glb ├── tile_slot.png ├── tile_woods.png └── villager.png ├── tile_base.blend ├── tile_base.blend1 ├── README.md ├── wasm └── index.html ├── LICENSE ├── Cargo.toml ├── src ├── main.rs └── game │ ├── mod.rs │ ├── animate.rs │ ├── camera.rs │ ├── progress_bar.rs │ ├── tile.rs │ └── card.rs ├── .cargo └── config.toml ├── .github └── workflows │ ├── ci.yaml │ └── release.yaml ├── source.svg └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cart/card_combinator/HEAD/cover.png -------------------------------------------------------------------------------- /assets/heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cart/card_combinator/HEAD/assets/heart.png -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cart/card_combinator/HEAD/assets/icon.png -------------------------------------------------------------------------------- /assets/log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cart/card_combinator/HEAD/assets/log.png -------------------------------------------------------------------------------- /assets/tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cart/card_combinator/HEAD/assets/tile.png -------------------------------------------------------------------------------- /tile_base.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cart/card_combinator/HEAD/tile_base.blend -------------------------------------------------------------------------------- /tile_base.blend1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cart/card_combinator/HEAD/tile_base.blend1 -------------------------------------------------------------------------------- /assets/goblin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cart/card_combinator/HEAD/assets/goblin.png -------------------------------------------------------------------------------- /assets/card_base.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cart/card_combinator/HEAD/assets/card_base.png -------------------------------------------------------------------------------- /assets/tile_base.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cart/card_combinator/HEAD/assets/tile_base.glb -------------------------------------------------------------------------------- /assets/tile_slot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cart/card_combinator/HEAD/assets/tile_slot.png -------------------------------------------------------------------------------- /assets/tile_woods.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cart/card_combinator/HEAD/assets/tile_woods.png -------------------------------------------------------------------------------- /assets/villager.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cart/card_combinator/HEAD/assets/villager.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Card Combinator 2 | 3 | ![cover](cover.png) 4 | 5 | A game about stacking cards on top of each other to make new cards. Built for [Bevy Jam #2](https://itch.io/jam/bevy-jam-2). -------------------------------------------------------------------------------- /wasm/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is dual-licensed under either 2 | 3 | * MIT License (docs/LICENSE-MIT or http://opensource.org/licenses/MIT) 4 | * Apache License, Version 2.0 (docs/LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) 5 | 6 | at your option. -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "card_combinator" 3 | version = "0.1.0" 4 | edition = "2021" 5 | license = "MIT OR Apache-2.0" 6 | 7 | [profile.dev.package."*"] 8 | opt-level = 3 9 | 10 | [profile.dev] 11 | opt-level = 1 12 | 13 | [dependencies] 14 | bevy = "0.8" 15 | bevy-inspector-egui = "0.12" 16 | bevy_rapier3d = {version = "0.16", features = ["debug-render"]} -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #[allow(dead_code, unused_variables, unused_mut, unused_imports)] 2 | mod game; 3 | 4 | use bevy::{asset::AssetServerSettings, prelude::*}; 5 | use bevy_rapier3d::prelude::*; 6 | 7 | use crate::game::GamePlugin; 8 | 9 | fn main() { 10 | App::new() 11 | .insert_resource(AmbientLight { 12 | color: Color::WHITE, 13 | brightness: 0.4, 14 | }) 15 | .insert_resource(AssetServerSettings { 16 | watch_for_changes: true, 17 | ..default() 18 | }) 19 | .insert_resource(ClearColor(Color::rgb(0.2, 0.2, 0.2))) 20 | .add_plugins(DefaultPlugins) 21 | .add_plugin(RapierPhysicsPlugin::::default()) 22 | // .add_plugin(bevy_inspector_egui::WorldInspectorPlugin::new()) 23 | // .add_plugin(RapierDebugRenderPlugin::default()) 24 | .add_plugin(GamePlugin) 25 | .run(); 26 | } 27 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | # Add the contents of this file to `config.toml` to enable "fast build" configuration. Please read the notes below. 2 | 3 | # NOTE: For maximum performance, build using a nightly compiler 4 | # If you are using rust stable, remove the "-Zshare-generics=y" below (as well as "-Csplit-debuginfo=unpacked" when building on macOS). 5 | 6 | [target.x86_64-unknown-linux-gnu] 7 | linker = "/usr/bin/clang" 8 | rustflags = ["-Clink-arg=-fuse-ld=lld"] 9 | 10 | # NOTE: you must manually install https://github.com/michaeleisel/zld on mac. you can easily do this with the "brew" package manager: 11 | # `brew install michaeleisel/zld/zld` 12 | # [target.x86_64-apple-darwin] 13 | # rustflags = ["-C", "link-arg=-fuse-ld=/usr/local/bin/zld", "-Zshare-generics=y", "-Csplit-debuginfo=unpacked"] 14 | 15 | # [target.x86_64-pc-windows-msvc] 16 | # linker = "rust-lld.exe" 17 | # rustflags = ["-Zshare-generics=y"] 18 | 19 | # Optional: Uncommenting the following improves compile times, but reduces the amount of debug info to 'line number tables only' 20 | # In most cases the gains are negligible, but if you are on macos and have slow compile times you should see significant gains. 21 | #[profile.dev] 22 | #debug = 1 23 | -------------------------------------------------------------------------------- /src/game/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod animate; 2 | pub mod camera; 3 | pub mod card; 4 | pub mod progress_bar; 5 | pub mod tile; 6 | 7 | use std::f32::consts::PI; 8 | 9 | use self::camera::PlayerCameraPlugin; 10 | use crate::game::{ 11 | card::{Card, CardBundle, CardPlugin, CardType}, 12 | progress_bar::{ProgressBar, ProgressBarBundle, ProgressBarPlugin}, 13 | tile::TilePlugin, 14 | }; 15 | use bevy::prelude::*; 16 | 17 | pub struct GamePlugin; 18 | 19 | impl Plugin for GamePlugin { 20 | fn build(&self, app: &mut App) { 21 | app.add_plugin(CardPlugin) 22 | .add_plugin(PlayerCameraPlugin) 23 | .add_plugin(ProgressBarPlugin) 24 | .add_plugin(TilePlugin) 25 | .add_startup_system(setup); 26 | } 27 | } 28 | 29 | fn setup( 30 | mut commands: Commands, 31 | asset_server: Res, 32 | mut materials: ResMut>, 33 | mut meshes: ResMut>, 34 | ) { 35 | commands.spawn_bundle(CardBundle { 36 | transform: Transform::from_xyz(-0.5, 0.0, 0.0), 37 | card: Card::from(CardType::Villager), 38 | ..default() 39 | }); 40 | commands.spawn_bundle(CardBundle { 41 | transform: Transform::from_xyz(0.5, 0.0, 0.0), 42 | card: Card::from(CardType::Villager), 43 | ..default() 44 | }); 45 | 46 | // commands.spawn_bundle(CardBundle { 47 | // transform: Transform::from_xyz(0.0, 3.0, 0.0), 48 | // card: Card::from(CardType::Goblin), 49 | // ..default() 50 | // }); 51 | 52 | // commands.spawn_bundle(CardBundle { 53 | // transform: Transform::from_xyz(1.0, 0.0, 0.0), 54 | // card: Card { 55 | // card_type: CardType::Log, 56 | // ..default() 57 | // }, 58 | // ..default() 59 | // }); 60 | } 61 | -------------------------------------------------------------------------------- /src/game/animate.rs: -------------------------------------------------------------------------------- 1 | use std::{ops::Range, time::Duration}; 2 | 3 | use bevy::prelude::Timer; 4 | 5 | pub struct AnimateRange { 6 | timer: Timer, 7 | ease: Ease, 8 | range: Range, 9 | } 10 | 11 | impl AnimateRange { 12 | pub fn new(duration: Duration, ease: Ease, range: Range, repeat: bool) -> Self { 13 | Self { 14 | timer: Timer::new(duration, repeat), 15 | ease, 16 | range, 17 | } 18 | } 19 | 20 | pub fn set_percent(&mut self, percent: f32) { 21 | self.timer.set_elapsed(Duration::from_secs_f32( 22 | self.timer.duration().as_secs_f32() * percent, 23 | )); 24 | } 25 | 26 | pub fn percent(&mut self) -> f32 { 27 | self.timer.percent() 28 | } 29 | 30 | pub fn reset(&mut self) { 31 | self.timer.reset(); 32 | } 33 | 34 | pub fn just_finished(&mut self) -> bool { 35 | self.timer.just_finished() 36 | } 37 | 38 | pub fn finished(&mut self) -> bool { 39 | self.timer.finished() 40 | } 41 | 42 | pub fn tick(&mut self, delta: Duration) -> f32 { 43 | self.timer.tick(delta); 44 | let amount = self.ease.ease(self.timer.percent()); 45 | self.range.start + ((self.range.end - self.range.start) * amount) 46 | } 47 | } 48 | 49 | #[derive(Copy, Clone)] 50 | #[allow(dead_code)] 51 | pub enum Ease { 52 | Linear, 53 | // Sin, 54 | InOutCirc, 55 | OutBack, 56 | // Custom(fn(f32) -> f32), 57 | } 58 | 59 | impl Ease { 60 | pub fn ease(&self, x: f32) -> f32 { 61 | match self { 62 | Ease::Linear => x, 63 | // Ease::Sin => x.sin(), 64 | Ease::InOutCirc => { 65 | if x < 0.5 { 66 | (1. - (1. - (2. * x).powf(2.)).sqrt()) / 2. 67 | } else { 68 | ((1. - (-2. * x + 2.).powf(2.)).sqrt() + 1.) / 2. 69 | } 70 | } 71 | Ease::OutBack => { 72 | const C1: f32 = 1.70158; 73 | const C3: f32 = C1 + 1.0; 74 | 75 | 1. + C3 * (x - 1.).powf(3.) + C1 * (x - 1.).powf(2.) 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | 14 | # Run cargo test 15 | test: 16 | name: Test Suite 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Checkout sources 20 | uses: actions/checkout@v2 21 | - name: Cache 22 | uses: actions/cache@v2 23 | with: 24 | path: | 25 | ~/.cargo/bin/ 26 | ~/.cargo/registry/index/ 27 | ~/.cargo/registry/cache/ 28 | ~/.cargo/git/db/ 29 | target/ 30 | key: ${{ runner.os }}-cargo-test-${{ hashFiles('**/Cargo.toml') }} 31 | - name: Install stable toolchain 32 | uses: actions-rs/toolchain@v1 33 | with: 34 | profile: minimal 35 | toolchain: stable 36 | override: true 37 | - name: Install Dependencies 38 | run: sudo apt-get update; sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev 39 | - name: Run cargo test 40 | uses: actions-rs/cargo@v1 41 | with: 42 | command: test 43 | 44 | # Run cargo clippy -- -D warnings 45 | clippy_check: 46 | name: Clippy 47 | runs-on: ubuntu-latest 48 | steps: 49 | - name: Checkout sources 50 | uses: actions/checkout@v2 51 | - name: Cache 52 | uses: actions/cache@v2 53 | with: 54 | path: | 55 | ~/.cargo/bin/ 56 | ~/.cargo/registry/index/ 57 | ~/.cargo/registry/cache/ 58 | ~/.cargo/git/db/ 59 | target/ 60 | key: ${{ runner.os }}-cargo-clippy-${{ hashFiles('**/Cargo.toml') }} 61 | - name: Install stable toolchain 62 | uses: actions-rs/toolchain@v1 63 | with: 64 | toolchain: stable 65 | profile: minimal 66 | components: clippy 67 | override: true 68 | - name: Install Dependencies 69 | run: sudo apt-get update; sudo apt-get install pkg-config libx11-dev libasound2-dev libudev-dev 70 | - name: Run clippy 71 | uses: actions-rs/clippy-check@v1 72 | with: 73 | token: ${{ secrets.GITHUB_TOKEN }} 74 | args: -- -D warnings 75 | 76 | # Run cargo fmt --all -- --check 77 | format: 78 | name: Format 79 | runs-on: ubuntu-latest 80 | steps: 81 | - name: Checkout sources 82 | uses: actions/checkout@v2 83 | - name: Install stable toolchain 84 | uses: actions-rs/toolchain@v1 85 | with: 86 | toolchain: stable 87 | profile: minimal 88 | components: rustfmt 89 | override: true 90 | - name: Run cargo fmt 91 | uses: actions-rs/cargo@v1 92 | with: 93 | command: fmt 94 | args: --all -- --check 95 | -------------------------------------------------------------------------------- /src/game/camera.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | use bevy::{input::mouse::MouseWheel, prelude::*}; 4 | 5 | use crate::game::animate::{AnimateRange, Ease}; 6 | 7 | #[derive(Component)] 8 | pub struct PlayerCamera { 9 | base_speed: f32, 10 | } 11 | 12 | impl Default for PlayerCamera { 13 | fn default() -> Self { 14 | Self { base_speed: 4.0 } 15 | } 16 | } 17 | pub struct PlayerCameraPlugin; 18 | 19 | impl Plugin for PlayerCameraPlugin { 20 | fn build(&self, app: &mut App) { 21 | app.add_startup_system(setup_camera).add_system(move_camera); 22 | } 23 | } 24 | 25 | fn setup_camera(mut commands: Commands) { 26 | // camera 27 | commands 28 | .spawn_bundle(Camera3dBundle { 29 | transform: Transform { 30 | translation: Vec3::new(0.0, -1.5, 8.0), 31 | rotation: Quat::from_rotation_x(0.2), 32 | ..default() 33 | }, 34 | ..default() 35 | }) 36 | .insert(PlayerCamera::default()); 37 | } 38 | 39 | pub fn move_camera( 40 | mut view_height: Local, 41 | mut scroll_accumulation: Local, 42 | time: Res