initial commit
This commit is contained in:
commit
72c0a3d7bf
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
3984
Cargo.lock
generated
Normal file
3984
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
31
Cargo.toml
Normal file
31
Cargo.toml
Normal file
|
@ -0,0 +1,31 @@
|
|||
[package]
|
||||
edition = "2018"
|
||||
name = "lyrix"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.4"
|
||||
bevy_console = "0.2.3"
|
||||
bevy_egui = "0.8.0"
|
||||
bevy_physimple = "0.2.0"
|
||||
egui = "0.13.1"
|
||||
lazy_static = "1.4.0"
|
||||
ron = "0.6.2"
|
||||
spin = "0.9.2"
|
||||
|
||||
[dependencies.bevy_rapier2d]
|
||||
features = ["simd-stable", "render"]
|
||||
version = "0.11.0"
|
||||
|
||||
[dependencies.serde]
|
||||
features = ["derive"]
|
||||
version = "1"
|
||||
|
||||
[dependencies.bevy]
|
||||
features = ["bevy_gltf", "bevy_winit", "render", "bevy_wgpu"]
|
||||
version = "0.5.0"
|
||||
|
||||
|
||||
[dependencies.rhai]
|
||||
features = ["sync", "only_i32", "no_optimize"]
|
||||
version = "1.2.0"
|
BIN
assets/branding/icon.png
Normal file
BIN
assets/branding/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
202
assets/fonts/Roboto/LICENSE.txt
Normal file
202
assets/fonts/Roboto/LICENSE.txt
Normal file
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
BIN
assets/fonts/Roboto/Roboto-Black.ttf
Normal file
BIN
assets/fonts/Roboto/Roboto-Black.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Roboto/Roboto-BlackItalic.ttf
Normal file
BIN
assets/fonts/Roboto/Roboto-BlackItalic.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Roboto/Roboto-Bold.ttf
Normal file
BIN
assets/fonts/Roboto/Roboto-Bold.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Roboto/Roboto-BoldItalic.ttf
Normal file
BIN
assets/fonts/Roboto/Roboto-BoldItalic.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Roboto/Roboto-Italic.ttf
Normal file
BIN
assets/fonts/Roboto/Roboto-Italic.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Roboto/Roboto-Light.ttf
Normal file
BIN
assets/fonts/Roboto/Roboto-Light.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Roboto/Roboto-LightItalic.ttf
Normal file
BIN
assets/fonts/Roboto/Roboto-LightItalic.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Roboto/Roboto-Medium.ttf
Normal file
BIN
assets/fonts/Roboto/Roboto-Medium.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Roboto/Roboto-MediumItalic.ttf
Normal file
BIN
assets/fonts/Roboto/Roboto-MediumItalic.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Roboto/Roboto-Regular.ttf
Normal file
BIN
assets/fonts/Roboto/Roboto-Regular.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Roboto/Roboto-Thin.ttf
Normal file
BIN
assets/fonts/Roboto/Roboto-Thin.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Roboto/Roboto-ThinItalic.ttf
Normal file
BIN
assets/fonts/Roboto/Roboto-ThinItalic.ttf
Normal file
Binary file not shown.
5
assets/mods/NewSwordMod.lad
Normal file
5
assets/mods/NewSwordMod.lad
Normal file
|
@ -0,0 +1,5 @@
|
|||
register_mod("NewSwordMod", "A simple mod to add a new sword");
|
||||
register_weapon("sword", 0, 0);
|
||||
register_object("hi");
|
||||
print("hello there");
|
||||
instance("sword", 1, 2);
|
4
assets/mods/asset.list
Normal file
4
assets/mods/asset.list
Normal file
|
@ -0,0 +1,4 @@
|
|||
ModList (
|
||||
value: 42,
|
||||
vec: [Mod(name: "NewSwordMod", enabled: true)],
|
||||
)
|
1
assets/mods/modlist.txt
Normal file
1
assets/mods/modlist.txt
Normal file
|
@ -0,0 +1 @@
|
|||
NewSwordMod
|
279
src/character_controller.rs
Normal file
279
src/character_controller.rs
Normal file
|
@ -0,0 +1,279 @@
|
|||
use bevy::{prelude::*, render::camera::Camera};
|
||||
use bevy_physimple::prelude::*;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Player {
|
||||
double_jump: bool,
|
||||
on_wall: Option<Vec2>,
|
||||
on_floor: bool,
|
||||
}
|
||||
|
||||
pub struct Gravity(Vec2);
|
||||
|
||||
pub struct ModManager;
|
||||
impl Plugin for ModManager {
|
||||
fn build(&self, app: &mut AppBuilder) {
|
||||
app // Basic setup of the app
|
||||
.add_plugin(Physics2dPlugin)
|
||||
// .add_system(bevy::input::system::exit_on_esc_system.system())
|
||||
.add_startup_system(setup.system())
|
||||
.add_system(controller_on_stuff.system())
|
||||
.add_system(character_system.system())
|
||||
.add_system(change_sensor_color.system())
|
||||
.add_system(gravity.system())
|
||||
.add_system(ray_head.system());
|
||||
}
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub struct PlayerCam;
|
||||
|
||||
fn setup(mut coms: Commands, mut mats: ResMut<Assets<ColorMaterial>>, a_server: Res<AssetServer>) {
|
||||
let wall = mats.add(Color::BLACK.into());
|
||||
|
||||
// insert a gravity struct
|
||||
coms.insert_resource(Gravity(Vec2::new(0.0, -540.0)));
|
||||
|
||||
// Spawn character
|
||||
coms.spawn_bundle(SpriteBundle {
|
||||
sprite: Sprite::new(Vec2::splat(28.0)),
|
||||
material: mats.add(Color::ALICE_BLUE.into()),
|
||||
..Default::default()
|
||||
})
|
||||
.insert_bundle(KinematicBundle {
|
||||
shape: CollisionShape::Square(Square::size(Vec2::splat(28.0))),
|
||||
..Default::default()
|
||||
})
|
||||
.insert(Player::default());
|
||||
|
||||
// Spawn the damn camera
|
||||
// coms.spawn_bundle(OrthographicCameraBundle::new_2d());
|
||||
/*coms.spawn_bundle(OrthographicCameraBundle::new_2d())
|
||||
.insert(PlayerCam);*/
|
||||
|
||||
// Controls
|
||||
let style = TextStyle {
|
||||
font: a_server.load("fonts/Roboto/Roboto-Black.ttf"),
|
||||
font_size: 32.0,
|
||||
color: Color::ANTIQUE_WHITE,
|
||||
};
|
||||
let alignment = TextAlignment {
|
||||
vertical: VerticalAlign::Bottom,
|
||||
horizontal: HorizontalAlign::Left,
|
||||
};
|
||||
let text = "A/D - Movement\nSpace/W - Jump/Double jump\nS - Stomp(when mid air)";
|
||||
coms.spawn_bundle(Text2dBundle {
|
||||
text: Text::with_section(text, style, alignment),
|
||||
transform: Transform::from_xyz(-270.0, 360.0, 0.0),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
coms.spawn_bundle(OrthographicCameraBundle::new_2d());
|
||||
// center floor
|
||||
coms.spawn_bundle(SpriteBundle {
|
||||
sprite: Sprite::new(Vec2::new(2000.0, 230.0)),
|
||||
material: wall.clone(),
|
||||
transform: Transform::from_xyz(0.0, -400.0, 0.0),
|
||||
..Default::default()
|
||||
})
|
||||
.insert_bundle(StaticBundle {
|
||||
shape: CollisionShape::Square(Square::size(Vec2::new(2000.0, 230.0))),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
// side wall
|
||||
coms.spawn_bundle(SpriteBundle {
|
||||
sprite: Sprite::new(Vec2::new(40.0, 300.0)),
|
||||
material: wall.clone(),
|
||||
transform: {
|
||||
let mut t = Transform::from_xyz(450.0, 0.0, 0.0);
|
||||
t.rotation = Quat::from_rotation_z(-0.1 * 3.14);
|
||||
t
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.insert_bundle(StaticBundle {
|
||||
shape: CollisionShape::Square(Square::size(Vec2::new(40.0, 300.0))),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
// smaller other side wall
|
||||
coms.spawn_bundle(SpriteBundle {
|
||||
sprite: Sprite::new(Vec2::new(30.0, 90.0)),
|
||||
material: wall.clone(),
|
||||
transform: Transform::from_xyz(-150.0, -160.0, 0.0),
|
||||
..Default::default()
|
||||
})
|
||||
.insert_bundle(StaticBundle {
|
||||
shape: CollisionShape::Square(Square::size(Vec2::new(30.0, 90.0))),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
// Floating platform
|
||||
coms.spawn_bundle(SpriteBundle {
|
||||
sprite: Sprite::new(Vec2::new(200.0, 30.0)),
|
||||
material: wall.clone(),
|
||||
transform: Transform::from_xyz(-150.0, 0.0, 0.0),
|
||||
..Default::default()
|
||||
})
|
||||
.insert_bundle(StaticBundle {
|
||||
shape: CollisionShape::Square(Square::size(Vec2::new(200.0, 30.0))),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
// Spawn the sensor
|
||||
const SENSOR_SIZE: f32 = 50.0;
|
||||
coms.spawn_bundle(SpriteBundle {
|
||||
sprite: Sprite::new(Vec2::splat(SENSOR_SIZE)),
|
||||
material: mats.add(Color::GOLD.into()),
|
||||
transform: Transform::from_xyz(30.0, -150.0, 0.0),
|
||||
..Default::default()
|
||||
})
|
||||
.insert_bundle(SensorBundle {
|
||||
shape: CollisionShape::Square(Square::size(Vec2::splat(SENSOR_SIZE))),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
// Spawn another cube which we will try to push or something
|
||||
const CUBE_SIZE: f32 = 35.0;
|
||||
coms.spawn_bundle(SpriteBundle {
|
||||
sprite: Sprite::new(Vec2::splat(CUBE_SIZE)),
|
||||
material: mats.add(Color::CRIMSON.into()),
|
||||
transform: Transform::from_xyz(100.0, 0.0, 0.0),
|
||||
..Default::default()
|
||||
})
|
||||
.insert_bundle(KinematicBundle {
|
||||
shape: CollisionShape::Square(Square::size(Vec2::splat(CUBE_SIZE))),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
|
||||
fn gravity(time: Res<Time>, grav: Res<Gravity>, mut q: Query<&mut Vel>) {
|
||||
// Since the lib itself doesnt take care of gravity(for obv reasons) we need to do it here
|
||||
let g = grav.0;
|
||||
let t = time.delta_seconds();
|
||||
|
||||
for mut v in q.iter_mut() {
|
||||
v.0 += t * g;
|
||||
}
|
||||
}
|
||||
|
||||
fn controller_on_stuff(
|
||||
mut query: Query<(Entity, &mut Player)>,
|
||||
mut colls: EventReader<CollisionEvent>,
|
||||
) {
|
||||
// Iterate over the collisions and check if the player is on a wall/floor
|
||||
let (e, mut c) = query
|
||||
.single_mut()
|
||||
.expect("should be only one player :shrug:");
|
||||
|
||||
// clear the current data on c
|
||||
c.on_floor = false;
|
||||
c.on_wall = None;
|
||||
|
||||
for coll in colls.iter().filter(|&c| c.is_b_static) {
|
||||
if coll.entity_a == e {
|
||||
let n = coll.normal.dot(Vec2::Y);
|
||||
|
||||
if n > 0.7 {
|
||||
c.on_floor = true;
|
||||
} else if n.abs() <= 0.7 {
|
||||
c.on_wall = Some(coll.normal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn character_system(
|
||||
input: Res<Input<KeyCode>>,
|
||||
time: Res<Time>,
|
||||
gravity: Res<Gravity>,
|
||||
mut query: Query<(&mut Player, &mut Vel)>,
|
||||
) {
|
||||
let gravity = gravity.0;
|
||||
|
||||
for (mut controller, mut vel) in query.iter_mut() {
|
||||
if let Some(normal) = controller.on_wall {
|
||||
// If we are colliding with a wall, make sure to stick
|
||||
vel.0 -= normal * 0.1;
|
||||
// and limit our speed downwards
|
||||
if vel.0.y < -1.0 {
|
||||
vel.0.y = -1.0;
|
||||
}
|
||||
}
|
||||
// There are 2 places in which we apply a jump, so i made a little colsure for code reusability
|
||||
let jump = |body: &Player, vel: &mut Vel| {
|
||||
vel.0 = vel.0.slide(gravity.normalize()) - gravity * 0.6;
|
||||
let wall = body.on_wall.unwrap_or(Vec2::ZERO) * 250.0;
|
||||
vel.0 += wall;
|
||||
};
|
||||
|
||||
let should_jump = input.just_pressed(KeyCode::Space) || input.just_pressed(KeyCode::W);
|
||||
if controller.on_floor || controller.on_wall.is_some() {
|
||||
controller.double_jump = true;
|
||||
if should_jump {
|
||||
jump(&controller, &mut vel);
|
||||
}
|
||||
} else if controller.double_jump && should_jump {
|
||||
controller.double_jump = false;
|
||||
jump(&controller, &mut vel);
|
||||
}
|
||||
|
||||
// This is for the testing purpose of the continuous collision - aka "The Stomp"
|
||||
if input.just_pressed(KeyCode::S) && !controller.on_floor {
|
||||
vel.0 = Vec2::new(0.0, -500.0);
|
||||
}
|
||||
// REMINDER: Dont forget to multiply by `time.delta_seconds()` when messing with movement
|
||||
let acc = Vec2::new(1000.0, 0.0) * time.delta_seconds();
|
||||
if input.pressed(KeyCode::A) {
|
||||
vel.0 -= acc;
|
||||
} else if input.pressed(KeyCode::D) {
|
||||
vel.0 += acc;
|
||||
} else {
|
||||
// This is not a good way to do friction
|
||||
vel.0.x *= 1.0 - (10.0 * time.delta_seconds());
|
||||
}
|
||||
// terminal velocity
|
||||
const TERMINAL_X: f32 = 500.0;
|
||||
if vel.0.x.abs() > TERMINAL_X {
|
||||
vel.0.x = TERMINAL_X.copysign(vel.0.x); // you can also do `TERMINAL_X * vel.0.x.signum()`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn change_sensor_color(
|
||||
mut materials: ResMut<Assets<ColorMaterial>>,
|
||||
q: Query<(&Sensor, &Handle<ColorMaterial>)>,
|
||||
) {
|
||||
// Simply change the color of the sensor if something is inside it
|
||||
for (s, h) in q.iter() {
|
||||
if let Some(mut m) = materials.get_mut(h) {
|
||||
m.color = if s.bodies.len() == 0 {
|
||||
Color::GOLD
|
||||
} else {
|
||||
Color::ALICE_BLUE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ray_head(
|
||||
mut ts: Query<&mut Transform, Without<RayCast>>,
|
||||
q: Query<(&RayCast, &Children, &Transform)>,
|
||||
) {
|
||||
for (r, c, rt) in q.iter() {
|
||||
if let Some(c) = c.first() {
|
||||
if let Ok(mut t) = ts.get_mut(*c) {
|
||||
// We use the offset in the `unwrap_or` because we want to offset the position to be where the ray "ends"
|
||||
// while in the `map`(and `pos` by extension) we want the position relative to the transform component
|
||||
// since `a.collision_point` is in global space
|
||||
|
||||
let pos = Vec2::new(rt.translation.x, rt.translation.y);
|
||||
t.translation = r
|
||||
.collision
|
||||
.map(|a| a.collision_point - pos)
|
||||
.unwrap_or(r.cast + r.offset)
|
||||
.extend(0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
72
src/console.rs
Normal file
72
src/console.rs
Normal file
|
@ -0,0 +1,72 @@
|
|||
// use crate::dice;
|
||||
// use crate::VERSION;
|
||||
use bevy::{
|
||||
app::{AppBuilder, EventReader, EventWriter, Plugin},
|
||||
prelude::IntoSystem,
|
||||
};
|
||||
use bevy_console::{
|
||||
ConsoleCommandEntered, ConsoleConfiguration, ConsolePlugin, HelpCommand, PrintConsoleLine,
|
||||
};
|
||||
|
||||
pub struct ConsoleManager;
|
||||
impl Plugin for ConsoleManager {
|
||||
fn build(&self, app: &mut AppBuilder) {
|
||||
// add things to your app here
|
||||
|
||||
app.add_plugin(ConsolePlugin)
|
||||
.insert_resource(ConsoleConfiguration {
|
||||
help: vec![
|
||||
HelpCommand::new(
|
||||
"dice".to_string(),
|
||||
"Usage: dice <number of dice>d<dice sides>".to_string(),
|
||||
),
|
||||
HelpCommand::new(
|
||||
"version".to_string(),
|
||||
"prints the client version".to_string(),
|
||||
),
|
||||
],
|
||||
..Default::default()
|
||||
})
|
||||
.add_system(listen_to_console_events.system());
|
||||
}
|
||||
}
|
||||
fn listen_to_console_events(
|
||||
mut console_events: EventReader<ConsoleCommandEntered>,
|
||||
mut console_line: EventWriter<PrintConsoleLine>,
|
||||
) {
|
||||
for event in console_events.iter() {
|
||||
let command: &str = &event.command;
|
||||
|
||||
// only useful for debuging
|
||||
// println!("{:?}", event);
|
||||
match command {
|
||||
"hi" => {
|
||||
console_line.send(PrintConsoleLine::new("Hello".to_string()));
|
||||
}
|
||||
"dice" => {
|
||||
// console_line.send(PrintConsoleLine::new(dice(&event.args).to_string()));
|
||||
}
|
||||
"ban" => {
|
||||
if &event.args == "" {
|
||||
console_line.send(PrintConsoleLine::new("No user provided".to_string()));
|
||||
} else {
|
||||
let user_list = event.args.split(" ");
|
||||
|
||||
for user in user_list {
|
||||
console_line.send(PrintConsoleLine::new(
|
||||
format!("Banned {}", user).to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
"version" => {
|
||||
let version = format!("Current client version is ");
|
||||
console_line.send(PrintConsoleLine::new(version.to_string()));
|
||||
}
|
||||
error => console_line.send(PrintConsoleLine::new(format!(
|
||||
"Command {} not found",
|
||||
error
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
41
src/main.rs
Normal file
41
src/main.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
mod character_controller;
|
||||
mod mods;
|
||||
mod timesys;
|
||||
|
||||
use bevy::prelude::*;
|
||||
use bevy_console::ConsoleConfiguration;
|
||||
|
||||
use mods::mod_manager::ModManager;
|
||||
mod console;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
enum AppState {
|
||||
Menu,
|
||||
InGame,
|
||||
ModMenu,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Load mods after play is pressed
|
||||
|
||||
App::build()
|
||||
.insert_resource(Msaa { samples: 4 })
|
||||
.insert_resource(ClearColor(Color::rgb(0.4, 0.4, 0.4)))
|
||||
.insert_resource(WindowDescriptor {
|
||||
width: 1440.,
|
||||
height: 900.,
|
||||
title: "Lyrix".to_string(),
|
||||
vsync: true,
|
||||
..Default::default()
|
||||
})
|
||||
.add_plugins(DefaultPlugins)
|
||||
.insert_resource(ConsoleConfiguration {
|
||||
// override config here
|
||||
..Default::default()
|
||||
})
|
||||
// .add_state(AppState::Menu)
|
||||
.add_plugin(console::ConsoleManager)
|
||||
.add_plugin(ModManager)
|
||||
.add_plugin(character_controller::ModManager)
|
||||
.run();
|
||||
}
|
2
src/mods/mod.rs
Normal file
2
src/mods/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
pub mod mod_manager;
|
||||
pub mod modding_api;
|
155
src/mods/mod_manager.rs
Normal file
155
src/mods/mod_manager.rs
Normal file
|
@ -0,0 +1,155 @@
|
|||
use bevy::{
|
||||
asset::{AssetLoader, LoadContext, LoadedAsset},
|
||||
prelude::*,
|
||||
reflect::TypeUuid,
|
||||
utils::BoxedFuture,
|
||||
};
|
||||
use bevy_console::PrintConsoleLine;
|
||||
use rhai::Engine;
|
||||
use serde::Deserialize;
|
||||
use std::{sync::Arc, sync::RwLock};
|
||||
|
||||
use super::modding_api::{register_mod, register_object, register_weapon};
|
||||
|
||||
#[derive(Debug, Deserialize, TypeUuid)]
|
||||
#[uuid = "39cadc56-aa9c-4543-8640-a018b74b5051"]
|
||||
pub struct Mod {
|
||||
name: String,
|
||||
enabled: bool,
|
||||
}
|
||||
#[derive(Debug, Deserialize, TypeUuid)]
|
||||
#[uuid = "39cadc56-aa9c-4543-8640-a018b74b5050"]
|
||||
pub struct LoadedMod {
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, TypeUuid)]
|
||||
#[uuid = "39cadc56-aa9c-4543-8640-a018b74b5052"]
|
||||
pub struct ModList {
|
||||
pub value: i32,
|
||||
pub vec: Vec<Mod>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ModListLoader;
|
||||
impl AssetLoader for ModListLoader {
|
||||
fn load<'a>(
|
||||
&'a self,
|
||||
bytes: &'a [u8],
|
||||
load_context: &'a mut LoadContext,
|
||||
) -> BoxedFuture<'a, Result<(), anyhow::Error>> {
|
||||
Box::pin(async move {
|
||||
let custom_asset = ron::de::from_bytes::<ModList>(bytes)?;
|
||||
load_context.set_default_asset(LoadedAsset::new(custom_asset));
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn extensions(&self) -> &[&str] {
|
||||
&["list"]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ModLoader;
|
||||
impl AssetLoader for ModLoader {
|
||||
fn load<'a>(
|
||||
&'a self,
|
||||
bytes: &'a [u8],
|
||||
load_context: &'a mut LoadContext,
|
||||
) -> BoxedFuture<'a, Result<(), anyhow::Error>> {
|
||||
Box::pin(async move {
|
||||
let custom_asset = LoadedMod {
|
||||
name: std::str::from_utf8(bytes).unwrap().to_string(),
|
||||
};
|
||||
load_context.set_default_asset(LoadedAsset::new(custom_asset));
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
fn extensions(&self) -> &[&str] {
|
||||
&["rhai"]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct State {
|
||||
pub handle: Handle<ModList>,
|
||||
pub mod_handle: Handle<LoadedMod>,
|
||||
pub loaded: bool,
|
||||
pub printed: bool,
|
||||
}
|
||||
|
||||
pub struct ModManager;
|
||||
impl Plugin for ModManager {
|
||||
fn build(&self, app: &mut AppBuilder) {
|
||||
app.init_resource::<State>()
|
||||
.add_asset::<ModList>()
|
||||
.init_asset_loader::<ModListLoader>()
|
||||
.add_asset::<LoadedMod>()
|
||||
.init_asset_loader::<ModLoader>()
|
||||
.add_startup_system(engine_settings.system())
|
||||
.add_system(build_mods.system());
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AddonState {
|
||||
rebuild_addons: bool,
|
||||
}
|
||||
|
||||
fn build_mods(
|
||||
mut engine: ResMut<Engine>,
|
||||
mut addon_state: ResMut<AddonState>,
|
||||
|
||||
mut console_line: EventWriter<PrintConsoleLine>,
|
||||
) {
|
||||
if !addon_state.rebuild_addons {
|
||||
return;
|
||||
}
|
||||
println!("Rebuilt addons");
|
||||
|
||||
let logbook = Arc::new(RwLock::new(Vec::<String>::new()));
|
||||
|
||||
// Redirect print/debug output to 'log'
|
||||
let log = logbook.clone();
|
||||
engine.on_print(move |s| log.write().unwrap().push(format!("{}", s)));
|
||||
|
||||
let log = logbook.clone();
|
||||
engine.on_debug(move |s, src, pos| {
|
||||
log.write().unwrap().push(format!(
|
||||
"DEBUG of {} at {:?}: {}",
|
||||
src.unwrap_or("unknown"),
|
||||
pos,
|
||||
s
|
||||
))
|
||||
});
|
||||
|
||||
let result = engine.run(include_str!("../../assets/mods/NewSwordMod.lad"));
|
||||
match result {
|
||||
Ok(_) => console_line.send(PrintConsoleLine::new("Mod Loaded".to_string())),
|
||||
Err(error) => console_line.send(PrintConsoleLine::new(error.to_string())),
|
||||
}
|
||||
|
||||
for entry in logbook.read().unwrap().iter() {
|
||||
console_line.send(PrintConsoleLine::new(entry.to_string()));
|
||||
}
|
||||
|
||||
addon_state.rebuild_addons = false;
|
||||
}
|
||||
|
||||
fn engine_settings(mut commands: Commands) {
|
||||
let mut engine = Engine::new();
|
||||
|
||||
// Configure the engine
|
||||
engine.set_max_array_size(50).set_max_operations(500);
|
||||
|
||||
engine.register_fn("register_mod", register_mod);
|
||||
engine.register_fn("register_weapon", register_weapon);
|
||||
engine.register_fn("register_object", register_object);
|
||||
|
||||
commands.insert_resource(engine);
|
||||
commands.insert_resource(AddonState {
|
||||
rebuild_addons: true,
|
||||
});
|
||||
}
|
67
src/mods/modding_api.rs
Normal file
67
src/mods/modding_api.rs
Normal file
|
@ -0,0 +1,67 @@
|
|||
use lazy_static::lazy_static;
|
||||
use rhai::{ImmutableString, INT};
|
||||
use spin::Mutex;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Object {
|
||||
name: String,
|
||||
scripted: ScriptedBool,
|
||||
}
|
||||
// pub struct Weapon{}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Weapon {
|
||||
name: String,
|
||||
delay: u64,
|
||||
damage: u8,
|
||||
scripted: ScriptedBool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ScriptedBool {
|
||||
Scripted(String),
|
||||
NotScripted,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref OBJECT_LIST: Mutex<Vec<Object>> = Mutex::new(vec![]);
|
||||
static ref WEAPON_LIST: Mutex<Vec<Weapon>> = Mutex::new(vec![]);
|
||||
static ref OBJECT_LIST3: Mutex<Vec<Object>> = Mutex::new(vec![]);
|
||||
static ref OBJECT_LIST4: Mutex<Vec<Object>> = Mutex::new(vec![]);
|
||||
}
|
||||
|
||||
pub fn register_mod(name: ImmutableString, description: ImmutableString) {
|
||||
println!("{}", name);
|
||||
println!("{}", description);
|
||||
}
|
||||
/// register an unscripted weapon
|
||||
pub fn register_weapon(name: ImmutableString, delay: INT, damage: INT) {
|
||||
let mut list = WEAPON_LIST.lock();
|
||||
let weapon = Weapon {
|
||||
name: name.to_string(),
|
||||
scripted: ScriptedBool::NotScripted,
|
||||
delay: 0,
|
||||
damage: 0,
|
||||
};
|
||||
list.push(weapon);
|
||||
println!("{:?}", list);
|
||||
}
|
||||
/// Register a generic object without a script element
|
||||
pub fn register_object(name: ImmutableString) {
|
||||
let mut list = OBJECT_LIST.lock();
|
||||
let object = Object {
|
||||
name: name.to_string(),
|
||||
scripted: ScriptedBool::NotScripted,
|
||||
};
|
||||
list.push(object);
|
||||
println!("{:?}", list);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn register_scripted_object() {
|
||||
todo!()
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub fn register_scripted_weapon() {
|
||||
todo!()
|
||||
}
|
93
src/timesys.rs
Normal file
93
src/timesys.rs
Normal file
|
@ -0,0 +1,93 @@
|
|||
use bevy::{
|
||||
app::{AppBuilder, Plugin},
|
||||
core::{Time, Timer},
|
||||
ecs::system::{Res, ResMut},
|
||||
prelude::IntoSystem,
|
||||
};
|
||||
struct TickTimer(Timer);
|
||||
|
||||
pub struct TimeDate {
|
||||
pub tick: u64, // when hits 40 inc second
|
||||
pub second: u64, // when hits 60 inc minute
|
||||
pub minute: u64, // when hits 120 inc day
|
||||
pub day: u8, // when hits 255 inc season
|
||||
pub season: u8, // when hits 4 inc year and restart all previous counters
|
||||
pub year: u64, // ahhhhhhhhh
|
||||
}
|
||||
|
||||
pub struct TickManager;
|
||||
impl Plugin for TickManager {
|
||||
fn build(&self, app: &mut AppBuilder) {
|
||||
app.insert_resource(TimeDate {
|
||||
tick: 0, // when hits 40 inc second
|
||||
second: 0, // when hits 60 inc minute
|
||||
minute: 0, // when hits 120 inc day
|
||||
day: 0, // when hits 255 inc season
|
||||
season: 0, // when hits 4 inc year and restart all previous counters
|
||||
year: 0, // ahhhhhhhhh
|
||||
})
|
||||
// the reason we call from_seconds with the true flag is to make the timer repeat itself
|
||||
.insert_resource(TickTimer(Timer::from_seconds(0.0025, true)))
|
||||
.add_system(game_tick.system())
|
||||
.add_system(inc_sec.system())
|
||||
.add_system(inc_min.system())
|
||||
.add_system(inc_day.system())
|
||||
.add_system(inc_season.system())
|
||||
.add_system(inc_year.system());
|
||||
}
|
||||
}
|
||||
|
||||
fn game_tick(time: Res<Time>, mut timer: ResMut<TickTimer>, mut timesys: ResMut<TimeDate>) {
|
||||
if timer.0.tick(time.delta()).just_finished() {
|
||||
timesys.tick += 1;
|
||||
}
|
||||
// Physics runs every tick
|
||||
// Mob AI acts on its goal every tick
|
||||
}
|
||||
|
||||
fn inc_sec(mut timesys: ResMut<TimeDate>) {
|
||||
if timesys.tick == 39 {
|
||||
timesys.second += 1;
|
||||
timesys.tick = 0;
|
||||
//println!("Second {}", timesys.second)
|
||||
}
|
||||
// Mob AI updates goal every second
|
||||
}
|
||||
fn inc_min(mut timesys: ResMut<TimeDate>) {
|
||||
if timesys.second == 59 {
|
||||
timesys.second = 0;
|
||||
timesys.minute += 1;
|
||||
//println!("Minute {}", timesys.minute)
|
||||
}
|
||||
}
|
||||
|
||||
fn inc_day(mut timesys: ResMut<TimeDate>) {
|
||||
if timesys.minute == 119 {
|
||||
timesys.minute = 0;
|
||||
timesys.day += 1;
|
||||
//println!("Day {}", timesys.day)
|
||||
}
|
||||
// Run the calender check every day
|
||||
}
|
||||
|
||||
fn inc_season(mut timesys: ResMut<TimeDate>) {
|
||||
/*
|
||||
Jan 1 to Mar 14 Season one ()
|
||||
Mar 14 to mar 26
|
||||
Mar 26 to Aug 7
|
||||
Aug 7 to Oct 19
|
||||
oct 19 to Dec 31
|
||||
*/
|
||||
|
||||
if timesys.day == 72 {
|
||||
timesys.day = 0;
|
||||
timesys.season += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn inc_year(mut timesys: ResMut<TimeDate>) {
|
||||
if timesys.season == 4 {
|
||||
timesys.season = 0;
|
||||
timesys.year += 1;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue