Compare commits

...

18 Commits

Author SHA1 Message Date
ed0fb59cae
Gotta go fast 2022-12-25 17:05:28 +01:00
321557f47f
Trivial movement thingie 2022-12-25 01:50:58 +01:00
f4388e1626
Reorganize source code 2022-12-24 21:32:47 +01:00
d54148e5db
Slightly better handling 2022-12-24 02:55:37 +01:00
accda086f4
Basic right-click movement 2022-12-24 02:43:18 +01:00
b562abe976
Add a "pawn" that gets "rendered" above the ground layer. 2022-12-23 17:00:57 +01:00
22ed2261f3
Add a terrain def and use it 2022-12-23 12:48:48 +01:00
d4175e0710
Cleanup 2022-12-21 13:18:05 +01:00
e04e620930
Fix this 2022-12-04 17:12:37 +01:00
f12412ff14
Update Bevy 2022-11-17 18:41:50 +01:00
c87b0bf5d5
Make it work for 0.8 2022-11-01 21:45:02 +01:00
1702da5d16 Updates 2022-11-01 21:06:09 +01:00
d38e41de29
Just a really bad terrain generator 2022-08-11 05:11:22 +02:00
d31c73b005
Paperwork stuff 2022-08-10 20:28:33 +02:00
a83a8ce528
Simplify and cleanup 2022-08-10 17:54:15 +02:00
e683e5c490 Typo in README.md 2022-05-13 01:35:57 +02:00
31e82e73ba Set license better 2022-05-13 01:32:21 +02:00
7e49baf599 Add camera movement 2022-05-13 01:13:09 +02:00
24 changed files with 1092 additions and 721 deletions

3
.gitignore vendored
View File

@ -1 +1,4 @@
/target
.vscode/*
!.vscode/launch.json
!.vscode/extensions.json

8
.idea/.gitignore generated vendored
View File

@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

6
.idea/GitLink.xml generated
View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="uk.co.ben_gibson.git.link.SettingsState">
<option name="host" value="e0f86390-1091-4871-8aeb-f534fbc99cf0" />
</component>
</project>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerExplorerSettingsProvider">
<option name="highlightColorRGB" value="-16711681" />
</component>
</project>

7
.idea/discord.xml generated
View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DiscordProjectSettings">
<option name="show" value="PROJECT_FILES" />
<option name="description" value="" />
</component>
</project>

View File

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GitToolBoxProjectSettings">
<option name="commitMessageIssueKeyValidationOverride">
<BoolValueOverride>
<option name="enabled" value="true" />
</BoolValueOverride>
</option>
<option name="commitMessageValidationEnabledOverride">
<BoolValueOverride>
<option name="enabled" value="true" />
</BoolValueOverride>
</option>
</component>
</project>

8
.idea/modules.xml generated
View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/monstrous.iml" filepath="$PROJECT_DIR$/.idea/monstrous.iml" />
</modules>
</component>
</project>

11
.idea/monstrous.iml generated
View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="CPP_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/vcs.xml generated
View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

6
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"recommendations": [
"vadimcn.vscode-lldb",
"rust-lang.rust-analyzer"
]
}

45
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,45 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug executable 'monstrous'",
"cargo": {
"args": [
"build",
"--bin=monstrous",
"--package=monstrous"
],
"filter": {
"name": "monstrous",
"kind": "bin"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug unit tests in executable 'monstrous'",
"cargo": {
"args": [
"test",
"--no-run",
"--bin=monstrous",
"--package=monstrous"
],
"filter": {
"name": "monstrous",
"kind": "bin"
}
},
"args": [],
"cwd": "${workspaceFolder}"
}
]
}

1249
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -2,10 +2,18 @@
name = "monstrous"
version = "0.1.0"
edition = "2021"
license = "AGPL-3.0+"
license = "AGPL-3.0-or-later"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bevy = "0.7.0"
bevy_ecs_tilemap = "0.6.0"
bevy = "0.9"
bevy_ecs_tilemap = "0.9"
rand = "0.8.5"
ron = "0.8.0"
serde = { version = "1.0.143", features = ["derive"] }
# Enable max optimizations for dependencies, but not for our code:
[profile.dev.package."*"]
opt-level = 3

32
README.md Normal file
View File

@ -0,0 +1,32 @@
# Monstrous
A game of survival, community, and unspeakable horror. Maybe. One day. Hopefully.
## How to build?
Having Rust installed, clone the repository and do:
```bash
$ cargo run
```
...that's it. Right now it won't do much.
## License
Monstrous, a game of survival, community and unspeakable horror.
Copyright (C) 2022 Sandra Modzelewska
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.

BIN
assets/pawn.aseprite Normal file

Binary file not shown.

BIN
assets/pawn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

23
assets/surfaces.ron Normal file
View File

@ -0,0 +1,23 @@
[
SurfaceDef {
label: "Mud",
name: "mud",
description: "Soil saturated with water.",
texture_index: 13,
support: 20.0,
},
SurfaceDef {
label: "Grass",
name: "grass",
description: "Green. Try to touch it.",
texture_index: 14,
support: 40.0,
},
SurfaceDef {
label: "Sand",
name: "sand",
description: "Gets everywhere, ruins vanilla sex fantasies.",
texture_index: 15,
support: 20.0,
},
]

BIN
assets/tileset.aseprite Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -1 +0,0 @@
pub mod texture;

View File

@ -1,20 +0,0 @@
use bevy::{prelude::*, render::render_resource::TextureUsages};
pub fn set_texture_filters_to_nearest(
mut texture_events: EventReader<AssetEvent<Image>>,
mut textures: ResMut<Assets<Image>>,
) {
// quick and dirty, run this for all textures anytime a texture is created.
for event in texture_events.iter() {
match event {
AssetEvent::Created { handle } => {
if let Some(mut texture) = textures.get_mut(handle) {
texture.texture_descriptor.usage = TextureUsages::TEXTURE_BINDING
| TextureUsages::COPY_SRC
| TextureUsages::COPY_DST;
}
}
_ => (),
}
}
}

View File

@ -1,50 +1,80 @@
use bevy::prelude::*;
// Copyright 2022 ModZero.
// SPDX-License-Identifier: AGPL-3.0-or-later
use bevy::{
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
input::mouse::MouseMotion,
prelude::*,
};
use bevy_ecs_tilemap::prelude::*;
mod helpers;
use pawns::{make_pawn, pawn_control, pawn_motion, pawn_transform};
use terrain::make_ground_layer;
fn startup(mut commands: Commands, asset_server: Res<AssetServer>, mut map_query: MapQuery) {
commands.spawn_bundle(OrthographicCameraBundle::new_2d());
mod pawns;
mod terrain;
let texture_handle = asset_server.load("tileset.png");
fn startup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera2dBundle::default());
let map_entity = commands.spawn().id();
let mut map = Map::new(0u16, map_entity);
let tileset_texture_handle = asset_server.load("tileset.png");
let pawn_texture_handle: Handle<Image> = asset_server.load("pawn.png");
let (mut layer_builder, layer_entity) = LayerBuilder::<TileBundle>::new(
let tilemap_size = TilemapSize { x: 320, y: 320 };
let tile_size = TilemapTileSize { x: 32.0, y: 32.0 };
let tilemap_entity = make_ground_layer(
&mut commands,
LayerSettings::new(
MapSize(3, 3),
ChunkSize(8, 8),
TileSize(8.0, 8.0),
TextureSize(24.0, 24.0)
),
0u16,
0u16
tilemap_size,
tileset_texture_handle,
tile_size,
);
layer_builder.set_all(TileBundle::default());
make_pawn(
&mut commands,
tilemap_entity,
pawn_texture_handle,
&tile_size,
&tilemap_size,
);
}
map_query.build_layer(&mut commands, layer_builder, texture_handle);
map.add_layer(&mut commands, 0u16, layer_entity);
commands
.entity(map_entity)
.insert(map)
.insert(Transform::from_xyz(-128.0, -128.0, 0.0))
.insert(GlobalTransform::default());
fn mouse_motion(
mut motion_evr: EventReader<MouseMotion>,
buttons: Res<Input<MouseButton>>,
mut query: Query<(&mut Transform, &mut OrthographicProjection), With<Camera>>,
) {
for ev in motion_evr.iter() {
if buttons.pressed(MouseButton::Middle) {
for (mut transform, mut _ortho) in query.iter_mut() {
let direction = Vec3::new(ev.delta.x, ev.delta.y * -1.0, 0.0);
transform.translation += direction;
}
}
}
}
fn main() {
App::new()
.insert_resource(WindowDescriptor {
width: 1270.0,
height: 720.0,
title: String::from("Monstrous"),
..Default::default()
})
.add_plugins(DefaultPlugins)
.add_plugins(
DefaultPlugins
.set(WindowPlugin {
window: WindowDescriptor {
width: 1270.0,
height: 720.0,
title: String::from("Monstrous"),
..Default::default()
},
..default()
})
.set(ImagePlugin::default_nearest()),
)
.add_plugin(LogDiagnosticsPlugin::default())
.add_plugin(FrameTimeDiagnosticsPlugin::default())
.add_plugin(TilemapPlugin)
.add_startup_system(startup)
.add_system(helpers::texture::set_texture_filters_to_nearest)
.add_system(mouse_motion)
.add_system(pawn_control)
.add_system(pawn_motion)
.add_system(pawn_transform)
.run();
}

161
src/pawns.rs Normal file
View File

@ -0,0 +1,161 @@
use bevy::{
input::{mouse::MouseButtonInput, ButtonState},
math::Vec4Swizzles,
prelude::*,
render::camera::RenderTarget,
};
use bevy_ecs_tilemap::prelude::*;
#[derive(Component)]
pub struct PlayerCharacter {}
#[derive(Component, Default, Clone, Copy, Debug)]
pub struct PawnPos {
pub x: u32,
pub y: u32,
}
#[derive(Component, Default, Clone, Copy, Debug)]
pub struct PawnDest {
pub x: u32,
pub y: u32,
}
#[derive(Component, Default, Clone, Copy, Debug)]
pub struct PawnNextTile {
pub x: u32,
pub y: u32,
pub progress: f32,
}
#[derive(Bundle, Default)]
struct PawnBundle {
position: PawnPos,
sprite: Sprite,
transform: Transform,
global_transform: GlobalTransform,
texture: Handle<Image>,
visibility: Visibility,
computed_visibility: ComputedVisibility,
}
pub fn make_pawn(
commands: &mut Commands,
tilemap_entity: Entity,
texture_handle: Handle<Image>,
tile_size: &TilemapTileSize,
map_size: &TilemapSize,
) {
let pos = PawnPos {
x: map_size.x / 2,
y: map_size.y / 2,
};
commands
.spawn(PawnBundle {
position: pos,
transform: Transform::from_xyz(
(pos.x as f32) * tile_size.x,
(pos.y as f32) * tile_size.y,
1.0,
),
texture: texture_handle,
visibility: Visibility { is_visible: true },
..Default::default()
})
.insert(PlayerCharacter {})
.set_parent(tilemap_entity);
info!("Pawn at {:?}", pos);
}
pub fn pawn_control(
mut commands: Commands,
wnds: Res<Windows>,
query: Query<Entity, With<PlayerCharacter>>,
mut buttons_evr: EventReader<MouseButtonInput>,
camera_q: Query<(&GlobalTransform, &Camera)>,
tilemap_q: Query<(&TilemapSize, &TilemapGridSize, &TilemapType, &Transform)>,
) {
for ev in buttons_evr.iter() {
if (ev.button == MouseButton::Right) && (ev.state == ButtonState::Released) {
let (cam_gt, cam) = camera_q.single();
let wnd = if let RenderTarget::Window(id) = cam.target {
wnds.get(id).unwrap()
} else {
wnds.get_primary().unwrap()
};
if let Some(screen_pos) = wnd.cursor_position() {
query.for_each(|entity| {
if let Some(world_ray) = cam.viewport_to_world(cam_gt, screen_pos) {
let (map_size, map_grid_size, map_type, map_t) = tilemap_q.single();
let tilemap_pos = (map_t.compute_matrix().inverse()
* Vec4::from((world_ray.origin, 1.0)))
.xy();
if let Some(tile_pos) =
TilePos::from_world_pos(&tilemap_pos, map_size, map_grid_size, map_type)
{
commands
.entity(entity)
.insert(PawnDest {
x: tile_pos.x,
y: tile_pos.y,
})
.remove::<PawnNextTile>();
}
}
});
}
}
}
}
type PawnsToPath = (Without<PawnNextTile>, With<PawnDest>);
pub fn pawn_motion(
mut commands: Commands,
mut pawn_q: Query<(Entity, &PawnPos, &PawnDest), PawnsToPath>,
) {
pawn_q.for_each_mut(|(entity, pos, dest)| {
if pos.x == dest.x && pos.y == dest.y {
commands.entity(entity).remove::<PawnDest>();
} else {
let next_tile = { TilePos::new(dest.x, dest.y) };
commands.entity(entity).insert(PawnNextTile {
x: next_tile.x,
y: next_tile.y,
progress: 0.0,
});
}
})
}
pub fn pawn_transform(
mut commands: Commands,
mut pawn_q: Query<(Entity, &mut PawnPos, &mut Transform, &mut PawnNextTile)>,
tilemap_q: Query<&TilemapTileSize>,
time: Res<Time>,
) {
let map_tile_size = tilemap_q.single();
pawn_q.for_each_mut(|(entity, mut pos, mut transform, mut next_tile)| {
let distance = Vec2::from((pos.x as f32, pos.y as f32))
.distance(Vec2::from((next_tile.x as f32, next_tile.y as f32)));
let velocity = 1.0;
let new_progress = next_tile.progress + velocity * time.delta_seconds();
if new_progress >= distance {
pos.x = next_tile.x;
pos.y = next_tile.y;
transform.translation.x = (pos.x as f32) * map_tile_size.x;
transform.translation.y = (pos.y as f32) * map_tile_size.y;
commands.entity(entity).remove::<PawnNextTile>();
} else {
next_tile.progress = new_progress;
transform.translation.x = (pos.x as f32
+ (next_tile.x as f32 - pos.x as f32) * (next_tile.progress / distance))
* map_tile_size.x;
transform.translation.y = (pos.y as f32
+ (next_tile.y as f32 - pos.y as f32) * (next_tile.progress / distance))
* map_tile_size.y;
}
});
}

96
src/terrain.rs Normal file
View File

@ -0,0 +1,96 @@
// Copyright 2022 ModZero.
// SPDX-License-Identifier: AGPL-3.0-or-later
use bevy::prelude::*;
use bevy_ecs_tilemap::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct SurfaceDef<'a> {
pub label: &'a str,
pub name: &'a str,
pub description: &'a str,
pub texture_index: u32,
pub support: f32,
}
#[derive(Component, Clone)]
struct TileTerrain {
terrain_id: usize,
}
impl TileTerrain {
fn def(&self) -> &SurfaceDef {
&TERRAINS[self.terrain_id]
}
}
const TERRAINS: [SurfaceDef; 3] = [
SurfaceDef {
label: "Mud",
name: "mud",
description: "Soil saturated with water.",
texture_index: 13,
support: 20.0,
},
SurfaceDef {
label: "Grass",
name: "grass",
description: "Green. Try to touch it.",
texture_index: 14,
support: 40.0,
},
SurfaceDef {
label: "Sand",
name: "sand",
description: "Gets everywhere, ruins vanilla sex fantasies.",
texture_index: 15,
support: 20.0,
},
];
pub fn make_ground_layer(
commands: &mut Commands,
tilemap_size: TilemapSize,
texture_handle: Handle<Image>,
tile_size: TilemapTileSize,
) -> Entity {
let mut tile_storage = TileStorage::empty(tilemap_size);
let tilemap_entity = commands.spawn_empty().id();
for x in 0..tilemap_size.x {
for y in 0..tilemap_size.y {
let tile_pos = TilePos { x, y };
let tile_terrain = TileTerrain { terrain_id: 1 };
let tile_entity = commands
.spawn((
tile_terrain.clone(),
TileBundle {
position: tile_pos,
tilemap_id: TilemapId(tilemap_entity),
texture_index: TileTextureIndex(tile_terrain.def().texture_index),
..Default::default()
},
))
.id();
tile_storage.set(&tile_pos, tile_entity);
}
}
let grid_size = tile_size.into();
let map_type = TilemapType::default();
commands.entity(tilemap_entity).insert(TilemapBundle {
grid_size,
map_type,
size: tilemap_size,
storage: tile_storage.clone(),
texture: TilemapTexture::Single(texture_handle),
tile_size,
transform: get_tilemap_center_transform(&tilemap_size, &grid_size, &map_type, 0.0),
..Default::default()
});
tilemap_entity
}