Skip to main content

traaains/
main.rs

1//! Trains simulator, implemented using bevy.
2//!
3//! Currently relying on nightly Rust due to using some unfinished features (mainly `generic_const_exprs`).
4
5#![allow(incomplete_features)]
6#![feature(generic_const_exprs)]
7#![feature(clamp_magnitude)]
8
9use std::collections::HashMap;
10
11use crate::{
12    editor::EditorPlugin,
13    game::{camera::CameraPlugin, cursor::CursorPlugin, pausing::PausingPlugin},
14    sim::SimPlugin,
15    ui::UiPlugin,
16};
17
18use bevy::{
19    DefaultPlugins,
20    app::{App, AppExit, PluginGroup, Startup},
21    asset::{Assets, Handle},
22    audio::AudioPlugin,
23    camera::ClearColor,
24    color::Color,
25    ecs::{
26        resource::Resource,
27        system::{Commands, ResMut},
28    },
29    feathers::{FeathersPlugins, dark_theme::create_dark_theme, palette, theme::UiTheme},
30    mesh::Mesh,
31    picking::mesh_picking::MeshPickingPlugin,
32    shape::Circle,
33    sprite_render::ColorMaterial,
34    state::{app::AppExtStates, state::States},
35};
36
37#[cfg(feature = "test_world")]
38pub mod test_world;
39// pub mod dev;
40pub mod editor;
41pub mod game;
42pub mod shape;
43pub mod sim;
44pub mod ui;
45pub mod utils;
46
47#[derive(PartialEq, Eq, Hash)]
48pub enum AssetType {
49    BogieNormal,
50    CarriageNormal,
51    TrackNodeNormal,
52    TrackNodeActive,
53    TrackSegmentNormal,
54    TrackSegmentGhost,
55    TrackSegmentHighlight,
56    SignalNormal,
57    Indicator,
58}
59
60#[derive(Resource)]
61pub struct AssetsManager {
62    meshes: HashMap<AssetType, Handle<Mesh>>,
63    materials: HashMap<AssetType, Handle<ColorMaterial>>,
64}
65
66pub fn setup_assets_manager(
67    mut cmd: Commands,
68    mut meshes: ResMut<Assets<Mesh>>,
69    mut materials: ResMut<Assets<ColorMaterial>>,
70) {
71    cmd.insert_resource(AssetsManager {
72        meshes: HashMap::from([
73            (AssetType::TrackNodeNormal, meshes.add(Circle::new(2.0))),
74            (AssetType::Indicator, meshes.add(Circle::new(1.5))),
75            (AssetType::SignalNormal, meshes.add(Circle::new(1.0))),
76        ]),
77        materials: HashMap::from([
78            (
79                AssetType::CarriageNormal,
80                materials.add(Color::hsl(0.0, 0.95, 0.7)),
81            ),
82            (
83                AssetType::TrackNodeNormal,
84                materials.add(Color::hsl(20.0, 0.95, 0.7)),
85            ),
86            (
87                AssetType::TrackNodeActive,
88                materials.add(Color::hsl(0.0, 0.95, 0.7)),
89            ),
90            (
91                AssetType::BogieNormal,
92                materials.add(Color::hsl(0.0, 0.0, 0.7)),
93            ),
94            (
95                AssetType::TrackSegmentNormal,
96                materials.add(Color::hsl(180.0, 0.95, 0.7)),
97            ),
98            (
99                AssetType::TrackSegmentGhost,
100                materials.add(Color::hsl(0.0, 0.0, 0.5)),
101            ),
102            (
103                AssetType::TrackSegmentHighlight,
104                materials.add(Color::hsl(50.0, 1.0, 0.5)),
105            ),
106            (
107                AssetType::Indicator,
108                materials.add(Color::srgb(1.0, 0.0, 0.5)),
109            ),
110            (
111                AssetType::SignalNormal,
112                materials.add(Color::hsl(60.0, 0.95, 0.7)),
113            ),
114        ]),
115    });
116}
117
118/// Main state. All other states depend on this. This will handle switching between any "main screen" and the actual simulation in the future.
119#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, States)]
120pub enum AppState {
121    #[default]
122    Main,
123}
124
125fn main() -> AppExit {
126    let mut app = App::new();
127    app.add_plugins((
128        // this default audio config breaks on linux and we don't need audio anyway
129        DefaultPlugins.build().disable::<AudioPlugin>(),
130        MeshPickingPlugin,
131        FeathersPlugins,
132    ))
133    .insert_resource(UiTheme(create_dark_theme()))
134    .insert_resource(ClearColor(palette::GRAY_0));
135
136    // [cfg(debug_assertions)]
137    // app.add_plugins(DevDefaultPlugins);
138
139    app.init_state::<AppState>()
140        // basic game plugins managing the camera, updating resources about cursor position etc.
141        .add_plugins((CameraPlugin, CursorPlugin, PausingPlugin))
142        // plugin taking care of all of the game's UI
143        .add_plugins(UiPlugin)
144        .add_plugins(EditorPlugin)
145        .add_plugins(SimPlugin)
146        .add_systems(Startup, setup_assets_manager);
147
148    #[cfg(feature = "test_world")]
149    app.add_systems(Startup, test_world::basic_world.after(setup_assets_manager));
150
151    app.run()
152}