world-digger-amethyst

[Archive] Game about Digging a lot using upgradable tools. Made using the Amethyst Engine. Might resume work on it eventually.
git clone https://git.jojolepro.com/world-digger-amethyst.git
Log | Files | Refs | README | LICENSE

main.rs (16709B)


      1 extern crate amethyst;
      2 extern crate amethyst_rhusics;
      3 extern crate rhusics_core;
      4 extern crate rhusics_ecs;
      5 extern crate collision;
      6 extern crate shred;
      7 
      8 use amethyst::{Application, Error, State, Trans};
      9 use amethyst::assets::{Loader,AssetStorage,Handle};
     10 use amethyst::config::Config;
     11 use amethyst::controls::{FlyControlTag,FlyControlBundle};
     12 use amethyst::core::frame_limiter::FrameRateLimitStrategy;
     13 use amethyst::core::transform::{GlobalTransform, Transform, TransformBundle};
     14 use amethyst::core::{Time,Parent,ECSBundle};
     15 use amethyst::ecs::{World,VecStorage,Component,Fetch,Entity,System,Join,ReadStorage,FetchMut,Entities,WriteStorage};
     16 use amethyst::input::{InputBundle,InputHandler};
     17 use amethyst::renderer::{AmbientColor, Camera, DisplayConfig, DrawShaded, ElementState, Event,
     18                          KeyboardInput, Material, MaterialDefaults, MeshHandle, ObjFormat,
     19                          Pipeline, PosNormTex, Projection, RenderBundle, Rgba, Stage,
     20                          VirtualKeyCode, WindowEvent,Texture,MouseButton,ScreenDimensions,WindowMessages};
     21 use amethyst::renderer::mouse::{release_cursor,grab_cursor,set_mouse_cursor_none,set_mouse_cursor};
     22 use amethyst::shrev::EventChannel;
     23 use amethyst::ui::{Anchor, Anchored, DrawUi, FontAsset, MouseReactive, Stretch, Stretched,
     24                    TtfFormat, UiBundle, UiEvent, UiFocused, UiImage, UiText,
     25                    UiTransform,TextEditing};
     26 use amethyst::winit::MouseCursor;
     27 
     28 //use amethyst_rhusics::{time_sync, DefaultBasicPhysicsBundle3,SpatialPhysicsBundle3};
     29 use collision::{Aabb3,Ray3};
     30 use collision::dbvt::query_ray_closest;
     31 use collision::primitive::{Primitive3,Cuboid};
     32 /*use rhusics_core::{CollisionShape, RigidBody,Collider,ContactEvent,Velocity,ForceAccumulator};
     33 use rhusics_ecs::WithRigidBody;
     34 use rhusics_ecs::physics3d::{register_physics,BodyPose3, CollisionMode,
     35                              CollisionStrategy, Mass3,DynamicBoundingVolumeTree3,SpatialSortingSystem3,ContactEvent3,
     36                              SpatialCollisionSystem3,GJK3,CurrentFrameUpdateSystem3,NextFrameSetupSystem3,ContactResolutionSystem3,Velocity3};*/
     37 use amethyst::core::cgmath::{Deg, Array, Basis3,Basis2, One, Point3, Quaternion, Vector3,Matrix3,Zero,EuclideanSpace,Rotation};
     38 
     39 use shred::{Dispatcher,DispatcherBuilder};
     40 
     41 mod player;
     42 use player::{Tool,Backpack,BlockDefinition,BlockDefinitions,BlockInstance,Inventory,UiUpdaterSystem,MineProgress};
     43 mod ui;
     44 use ui::{create_game_ui,load_tool_icon,UiShit,load_ui_shit,create_buy_ui};
     45 
     46 /*
     47 TODO
     48 
     49 Raycast
     50 Click mine
     51 UI
     52 -Layouting
     53 -Macro for btn, auto layout by pos
     54 
     55 
     56 
     57 
     58 */
     59 
     60 
     61 
     62 
     63 struct MiningSystem{
     64     was_down: bool,
     65 }
     66 
     67 impl MiningSystem{
     68     pub fn new() -> Self{
     69         MiningSystem{
     70             was_down: false,
     71         }
     72     }
     73 }
     74 
     75 impl<'a> System<'a> for MiningSystem {
     76     type SystemData = (
     77         Entities<'a>,
     78         Fetch<'a, DynamicBoundingVolumeTree3<f32>>,
     79         ReadStorage<'a, Camera>,
     80         ReadStorage<'a, Transform>,
     81         ReadStorage<'a, BlockInstance>,
     82         Fetch<'a, InputHandler<String,String>>,
     83         FetchMut<'a, Inventory>,
     84         FetchMut<'a, MineProgress>,
     85         Fetch<'a, Time>,
     86         WriteStorage<'a, ForceAccumulator<Vector3<f32>, Vector3<f32>>>,
     87     );
     88 
     89     fn run(&mut self, (entities,tree,camera,transform,block_definitions,input, mut inventory, mut progress, time,mut force): Self::SystemData) {
     90         let btn_down = input.mouse_button_is_down(MouseButton::Left);
     91 
     92         if btn_down {
     93             for (tr, _) in (&transform, &camera).join() {
     94                 let forward = Quaternion::from(tr.rotation).conjugate().invert() * -Vector3::unit_z();
     95                 let ray = Ray3::new(Point3::from_vec(tr.translation), forward);
     96                 if let Some((v, p)) = query_ray_closest(&*tree, ray) {
     97                     println!("hit bounding volume of {:?} at point {:?}", v.value, p);
     98 
     99 
    100                     /*if let Some(mut f) = force.get_mut(v.value){
    101                         f.add_force(Vector3::new(1.0,-10.0,1.0));
    102                         println!("ADDING FORCE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
    103                     }*/
    104 
    105 
    106                     // TODO raycast lookat + dist check, are we mining same block that we were
    107                     if Some(v.value) != progress.block {
    108                         progress.block = Some(v.value);
    109                         progress.start = time.absolute_time_seconds();
    110                         progress.progress = 0.0;
    111                     }
    112 
    113                     progress.progress = (time.absolute_time_seconds() - progress.start) as f32 / inventory.tool.use_time;
    114 
    115                     if progress.progress > 1.0 {
    116                         progress.progress = 1.0;
    117                     }
    118 
    119                     if progress.progress == 1.0 {
    120                         progress.reset();
    121 
    122                         entities.delete(v.value);
    123 
    124                         // switch to block def
    125                         inventory.carrying += 1;
    126 
    127                         if inventory.carrying > inventory.backpack.capacity {
    128                             inventory.carrying = inventory.backpack.capacity;
    129                         }
    130                     }
    131 
    132                 } else{
    133                     progress.reset();
    134                 }
    135             }
    136         }else if self.was_down{
    137             progress.reset();
    138         }
    139 
    140         self.was_down = btn_down;
    141     }
    142 }
    143 
    144 
    145 /*
    146 fn query_ray(tree: & DynamicBoundingVolumeTree3<f32>, ray: Ray3<f32>) -> Vec<(TreeValueWrapped<Entity, Aabb3<f32>>, Point3<f32>)> {
    147     let mut visitor = ContinuousVisitor::new(&ray);
    148     tree.query(&mut visitor)
    149 }
    150 */
    151 
    152 
    153 
    154 pub type Shape = CollisionShape<Primitive3<f32>, BodyPose3<f32>, Aabb3<f32>, ObjectType>;
    155 
    156 #[repr(u8)]
    157 #[derive(Debug, Clone, PartialOrd, PartialEq)]
    158 pub enum ObjectType {
    159     Box,
    160 }
    161 
    162 impl Default for ObjectType {
    163     fn default() -> Self {
    164         ObjectType::Box
    165     }
    166 }
    167 
    168 impl Collider for ObjectType {
    169     fn should_generate_contacts(&self, other: &ObjectType) -> bool {
    170         true
    171     }
    172 }
    173 
    174 impl Component for ObjectType {
    175     type Storage = VecStorage<Self>;
    176 }
    177 
    178 
    179 fn event_was_key_pressed(event: Event,key: VirtualKeyCode)->bool{
    180     match event {
    181         Event::WindowEvent { event, .. } => match event {
    182             WindowEvent::KeyboardInput {
    183                 input:
    184                 KeyboardInput {
    185                     virtual_keycode,
    186                     state: ElementState::Pressed,
    187                     ..
    188                 },
    189                 ..
    190             } => match virtual_keycode {
    191                 Some(key2) if key == key2 => return true,
    192                 _ => (),
    193             },
    194             _ => (),
    195         },
    196         _ => (),
    197     }
    198     false
    199 }
    200 
    201 
    202 struct BuyMenuState{
    203     local_entities: Vec<Entity>,
    204 }
    205 
    206 impl State for BuyMenuState{
    207     fn on_start(&mut self, mut world: &mut World){
    208         self.local_entities = create_buy_ui(&mut world);
    209         release_cursor(&mut world.write_resource());
    210         set_mouse_cursor(&mut world.write_resource(),MouseCursor::Default);
    211     }
    212 
    213     fn on_stop(&mut self, mut world: &mut World){
    214         world.delete_entities(self.local_entities.as_slice());
    215         let dim = world.read_resource::<ScreenDimensions>();
    216         let mut msg = world.write_resource::<WindowMessages>();
    217 
    218         // Make mouse hidden again
    219         grab_cursor(&mut msg);
    220         set_mouse_cursor_none(&mut msg);
    221     }
    222     fn handle_event(&mut self, _: &mut World, event: Event) -> Trans {
    223         if event_was_key_pressed(event,VirtualKeyCode::P){
    224             println!("Pop buy menu");
    225             return Trans::Pop;
    226         }
    227         Trans::None
    228     }
    229 }
    230 
    231 struct GameState{
    232     dispatcher: Option<Dispatcher<'static,'static>>,
    233 }
    234 
    235 impl GameState{
    236     pub fn new() -> Self{
    237         GameState{
    238             dispatcher: None,
    239         }
    240     }
    241 }
    242 
    243 pub fn with_bundle<B>(mut disp_builder: DispatcherBuilder<'static,'static>,mut world: &mut World, bundle: B) -> DispatcherBuilder<'static,'static>
    244     where
    245         B: ECSBundle<'static, 'static>,
    246 {
    247     bundle
    248         .build(&mut world, disp_builder)
    249         .expect("Failed to add bundle to dispatcher builder")
    250 }
    251 
    252 
    253 impl State for GameState {
    254     fn on_start(&mut self, mut world: &mut World) {
    255         let mut dispatcher = DispatcherBuilder::new()
    256             .add(MiningSystem::new(),"mining",&[]);
    257         dispatcher = with_bundle(dispatcher, &mut world,FlyControlBundle::<String, String>::new(
    258             Some(String::from("move_x")),
    259             Some(String::from("move_y")),
    260             Some(String::from("move_z")),
    261         ).with_speed(20.0).with_sensitivity(0.3,0.3));
    262         self.dispatcher = Some(dispatcher.build());
    263 
    264 
    265         initialise_camera(world);
    266 
    267         let (mut comps,cube) = {
    268             let mat_defaults = world.read_resource::<MaterialDefaults>().clone();
    269 
    270             let loader = world.read_resource::<Loader>();
    271             let cube = {
    272                 let mesh_storage = world.read_resource();
    273                 loader.load("cube.obj", ObjFormat, (), (), &mesh_storage)
    274             };
    275 
    276             let tex_storage = world.read_resource();
    277 
    278 
    279             let radius = 4;
    280             let cube_size = 1.0;
    281 
    282             let mut comps: Vec<(Material, Transform)> = vec![];
    283 
    284             for x in -radius..radius {
    285                 for y in -radius..radius {
    286                     for z in -radius..radius {
    287 
    288                         // CUBE COLOR
    289                         let r_color = (x + radius) as f32 / (radius as f32 * 2.0);
    290                         let g_color = (y + radius) as f32 / (radius as f32 * 2.0);
    291                         let b_color = (z + radius) as f32 / (radius as f32 * 2.0);
    292 
    293                         let color = mat_from_color([r_color, g_color, b_color, 1.0], &mat_defaults, &loader, &tex_storage);
    294                         // CUBE COLOR END
    295 
    296                         let x = x as f32 * cube_size;
    297                         let y = y as f32 * cube_size;
    298                         let z = z as f32 * cube_size;
    299                         let mut trans = Transform::default();
    300                         trans.translation = Vector3::new(x, y, z);
    301 
    302                         comps.push((color, trans));
    303                     }
    304                 }
    305             }
    306             (comps,cube)
    307         };
    308 
    309         while let Some(c) = comps.pop(){
    310             world
    311                 .create_entity()
    312                 .with(cube.clone())
    313                 .with(c.0)
    314                 .with(GlobalTransform::default())
    315                 .with_dynamic_rigid_body(
    316                     Shape::new_simple_with_type(
    317                         CollisionStrategy::FullResolution,
    318                         CollisionMode::Discrete,
    319                         Cuboid::new(1.0, 1.0,1.0).into(),
    320                         ObjectType::Box,
    321                     ),
    322                     BodyPose3::new(Point3::new(c.1.translation.x, c.1.translation.y,c.1.translation.z), Quaternion::one()),
    323                     Velocity3::from_linear(Vector3::new(0.0,0.0,0.0)),
    324                     RigidBody::default(),
    325                     Mass3::new(1.0),
    326                 )
    327                 .with(
    328                     ForceAccumulator::<Vector3<f32>,Vector3<f32>>::new()
    329                 )
    330                 .with(c.1)
    331                 .build();
    332         }
    333 
    334         //Plane under the cubes
    335 
    336         let mut trans = Transform::default();
    337         trans.translation = Vector3::new(0.0, -20.0, 0.0);
    338         trans.scale = Vector3::new(50.0,5.0,50.0);
    339         world
    340             .create_entity()
    341             .with(GlobalTransform::default())
    342             .with_static_rigid_body(
    343                 Shape::new_simple_with_type(
    344                     CollisionStrategy::FullResolution,
    345                     CollisionMode::Discrete,
    346                     Cuboid::new(50.0, 5.0,50.0).into(),
    347                     ObjectType::Box,
    348                 ),
    349                 BodyPose3::new(Point3::new(trans.translation.x, trans.translation.y,trans.translation.z), Quaternion::one()),
    350                 RigidBody::default(),
    351                 Mass3::infinite(),
    352             )
    353             .with(trans)
    354             .build();
    355 
    356 
    357         // INVENTORY
    358         let tool1 = Tool{
    359             name: String::from("Spoon"),
    360             icon: load_tool_icon(&world,String::from("Spoon")),
    361             use_time: 1.0,
    362             mine_quantity: 1,
    363             cost: 0,
    364         };
    365 
    366         let backpack1 = Backpack{
    367             name: String::from("Hands"),
    368             icon: load_tool_icon(&world,String::from("Spoon")),
    369             capacity: 5,
    370             cost: 0,
    371         };
    372 
    373         let inventory = Inventory{
    374             tool: tool1,
    375             backpack: backpack1,
    376             carrying: 0,
    377             money: 0,
    378         };
    379 
    380 
    381         let progress = MineProgress{
    382             block: None,
    383             start: 0.0,
    384             progress: 0.0,
    385         };
    386 
    387 
    388 
    389         // World registering stuff
    390 
    391         world.add_resource(inventory);
    392         world.add_resource(progress);
    393         world.add_resource(Time::default());
    394         world.add_resource(AmbientColor(Rgba::from([1.0; 3])));
    395         world.register::<BlockInstance>();
    396 
    397         // UI
    398 
    399         ui::create_game_ui(&mut world);
    400 
    401     }
    402 
    403     fn handle_event(&mut self, _: &mut World, event: Event) -> Trans {
    404         /*match event {
    405             Event::WindowEvent { event, .. } => match event {
    406                 WindowEvent::KeyboardInput {
    407                     input:
    408                     KeyboardInput {
    409                         virtual_keycode,
    410                         state: ElementState::Pressed,
    411                         ..
    412                     },
    413                     ..
    414                 } => match virtual_keycode {
    415                     Some(VirtualKeyCode::Escape) => return Trans::Quit,
    416                     _ => (),
    417                 },
    418                 _ => (),
    419             },
    420             _ => (),
    421         }*/
    422 
    423         if event_was_key_pressed(event,VirtualKeyCode::X){
    424             println!("Push buy menu");
    425             return Trans::Push(Box::new(BuyMenuState{
    426                 local_entities: Vec::<Entity>::new(),
    427             }));
    428         }
    429 
    430         Trans::None
    431     }
    432 
    433     fn update(&mut self, world: &mut World) -> Trans {
    434         time_sync(world);
    435         self.dispatcher.as_mut().unwrap().dispatch(&mut world.res);
    436         Trans::None
    437     }
    438 }
    439 
    440 fn mat_from_color(color: [f32;4], defaults: &MaterialDefaults, loader: &Loader, tex_storage: &AssetStorage<Texture>)->Material{
    441     let color = loader.load_from_data(color.into(), (), &tex_storage);
    442     Material {
    443         albedo: color,
    444         ..defaults.0.clone()
    445     }
    446 }
    447 
    448 fn main() {
    449     if let Err(error) = run() {
    450         eprintln!("Could not run the example!");
    451         eprintln!("{}", error);
    452         ::std::process::exit(1);
    453     }
    454 }
    455 
    456 /// Wrapper around the main, so we can return errors easily.
    457 fn run() -> Result<(), Error> {
    458     let resources_directory = format!("{}/assets", env!("CARGO_MANIFEST_DIR"));
    459 
    460     let display_config_path = format!(
    461         "{}/resources/display_config.ron",
    462         env!("CARGO_MANIFEST_DIR")
    463     );
    464 
    465     let display_config = DisplayConfig::load(display_config_path);
    466 
    467     let key_bindings_path = format!(
    468         "{}/resources/input.ron",
    469         env!("CARGO_MANIFEST_DIR")
    470     );
    471 
    472     let pipeline_builder = Pipeline::build().with_stage(
    473         Stage::with_backbuffer()
    474             .clear_target([1.0, 0.6, 0.8, 1.0], 1.0)
    475             .with_pass(DrawShaded::<PosNormTex>::new())
    476             .with_pass(DrawUi::new()),
    477     );
    478 
    479     // PHYSIC-------------------------
    480     /*let mut channel = EventChannel::<ContactEvent<Entity,Point3<f32>>>::new();
    481     let reader_2 = channel
    482         .register_reader();*/
    483 
    484     let mut game = Application::build(resources_directory, GameState::new())?
    485         .with_frame_limit(FrameRateLimitStrategy::Unlimited, 0)
    486         /*.with_bundle(FlyControlBundle::<String, String>::new(
    487             Some(String::from("move_x")),
    488             Some(String::from("move_y")),
    489             Some(String::from("move_z")),
    490         ).with_speed(20.0).with_sensitivity(0.3,0.3))?*/
    491         .with_bundle(UiBundle::<String,String>::new())?
    492         .with_bundle(
    493             InputBundle::<String, String>::new().with_bindings_from_file(&key_bindings_path),
    494         )?
    495         .with_bundle(RenderBundle::new(pipeline_builder, Some(display_config)))?
    496         .with_bundle(SpatialPhysicsBundle3::<Primitive3<f32>,Aabb3<f32>,ObjectType>::new())?
    497         .with(UiUpdaterSystem,"ui_updater",&[])
    498         .with_bundle(TransformBundle::new().with_dep(&["sync_system"]))?
    499         .build()?;
    500     game.run();
    501     Ok(())
    502 }
    503 
    504 fn initialise_camera(world: &mut World) {
    505     let local = Transform::default();
    506 
    507     world
    508         .create_entity()
    509         .with(Camera::from(Projection::perspective(1.3, Deg(60.0))))
    510         .with(local)
    511         .with(GlobalTransform::default())
    512         .with(FlyControlTag)
    513         .build();
    514 }