Merge branch 'master' into map_assets

This commit is contained in:
rsninja722 2022-04-02 14:46:47 -04:00
commit c6ef86cb8f
5 changed files with 450 additions and 20 deletions

View File

@ -1,6 +1,7 @@
{
"git.detectSubmodules": false,
"cSpell.words": [
"leaderboard",
"msaa",
"raylib",
"repr",

View File

@ -1,9 +1,14 @@
use std::{collections::HashMap, sync::Arc};
use std::{collections::HashMap, path::PathBuf, sync::Arc};
use crate::asset_manager::{load_texture_from_internal_data, InternalData};
use nalgebra as na;
use raylib::{
camera::Camera2D, prelude::RaylibDrawHandle, texture::Texture2D, RaylibHandle, RaylibThread,
camera::Camera2D,
color::Color,
math::Vector2,
prelude::{RaylibDraw, RaylibDrawHandle, RaylibMode2D},
texture::Texture2D,
RaylibHandle, RaylibThread,
};
use tiled::{Loader, Map, ResourceCache, ResourcePath, ResourcePathBuf, Tileset};
@ -69,7 +74,7 @@ impl ResourceCache for ProgramDataTileCache {
#[derive(Debug)]
pub struct MapRenderer {
map: Map,
tile_textures: HashMap<String, HashMap<u32, Texture2D>>,
tile_textures: HashMap<PathBuf, Texture2D>,
}
impl MapRenderer {
@ -93,9 +98,9 @@ impl MapRenderer {
let mut tile_textures = HashMap::new();
for tileset in map.tilesets() {
for (idx, tile) in tileset.tiles() {
if let Some(image) = tile.data.image {
if let Some(image) = &tile.data.image {
// We now have a path to an image
let image_path = image.source;
let image_path = image.source.clone();
// Load the texture
let texture = load_texture_from_internal_data(
@ -106,10 +111,7 @@ impl MapRenderer {
.unwrap();
// Store the texture in the cache
tile_textures
.entry(tileset.name)
.or_insert_with(HashMap::new)
.insert(idx, texture);
tile_textures.insert(image_path, texture);
}
}
}
@ -125,5 +127,87 @@ impl MapRenderer {
todo!()
}
pub fn render_map(&self, draw_handle: &RaylibDrawHandle, camera: &Camera2D) {}
pub fn render_map(&self, draw_handle: &mut RaylibMode2D<RaylibDrawHandle>, camera: &Camera2D, show_debug_grid:bool) {
// Get the window corners in world space
let screen_width = draw_handle.get_screen_width();
let screen_height = draw_handle.get_screen_height();
let world_win_top_left = draw_handle.get_screen_to_world2D(Vector2::new(0.0, 0.0), camera);
let world_win_bottom_right = draw_handle.get_screen_to_world2D(
Vector2::new(screen_width as f32, screen_height as f32),
camera,
);
// Handle each layer from the bottom up
for layer in self.map.layers() {
// Handle different layer types
match layer.layer_type() {
tiled::LayerType::TileLayer(layer) => {
// Keep track of our sampler X and Y values
let mut sampler_x = 0;
let mut sampler_y = 0;
// Get the tile width and height
let tile_width = 128;
let tile_height = 128;
// Loop until we have covered all tiles on the screen
for y in (world_win_top_left.y as i64)..(world_win_bottom_right.y as i64) {
// Convert the pixel coordinates to tile coordinates
let tile_y = (y as f32 / tile_height as f32).floor() as i32;
// If we are looking at a new tile, update the sampler
if sampler_y != tile_y {
sampler_y = tile_y;
for x in
(world_win_top_left.x as i64)..(world_win_bottom_right.x as i64)
{
// Convert the pixel coordinates to tile coordinates
let tile_x = (x as f32 / tile_width as f32).floor() as i32;
// debug!("Tile: ({}, {})", tile_x, tile_y);
// If we are looking at a new tile, update the sampler
if sampler_x != tile_x {
sampler_x = tile_x;
// Get the tile at this coordinate
if let Some(tile) = layer.get_tile(sampler_x, sampler_y) {
// debug!("Tile: ({}, {})", tile_x, tile_y);
// Fetch the texture for this tile
let real_tile = tile.get_tile().unwrap();
let texture = self
.tile_textures
.get(&real_tile.image.as_ref().unwrap().source)
.unwrap();
// Draw the tile
draw_handle.draw_texture(
texture,
tile_x * tile_width as i32,
tile_y * tile_height as i32,
Color::WHITE,
);
}
if show_debug_grid {
draw_handle.draw_rectangle_lines(
tile_x * tile_width as i32,
tile_y * tile_height as i32,
self.map.tile_width as i32,
self.map.tile_height as i32,
Color::RED,
);
draw_handle.draw_pixel(x as i32, y as i32, Color::BLUE);
}
}
}
}
}
}
tiled::LayerType::ObjectLayer(_) => todo!(),
tiled::LayerType::ImageLayer(_) => todo!(),
tiled::LayerType::GroupLayer(_) => todo!(),
}
}
}
}

View File

@ -0,0 +1,256 @@
//! This scene encompasses the main menu system
use nalgebra as na;
use raylib::{
ffi::{GetMouseX, GetMouseY, IsMouseButtonDown, Texture},
prelude::*,
};
use crate::{
discord::{DiscordChannel, DiscordRpcSignal},
global_resource_package::GlobalResources,
project_constants::ProjectConstants,
};
#[derive(Debug, Clone)]
pub enum MenuStateSignal {
StartGame,
QuitGame,
DoMainMenu,
DoOptions,
DoCredits,
DoLeaderboard,
}
#[derive(Debug)]
pub struct MainMenu {
has_updated_discord_rpc: bool,
}
impl MainMenu {
/// Construct a new `MainMenu`
pub fn new(
raylib_handle: &mut RaylibHandle,
thread: &RaylibThread,
constants: &ProjectConstants,
) -> Self {
Self {
has_updated_discord_rpc: false,
}
}
pub async fn render_main_menu_frame(
&mut self,
raylib: &mut RaylibHandle,
rl_thread: &RaylibThread,
discord: &DiscordChannel,
global_resources: &GlobalResources,
constants: &ProjectConstants,
) -> MenuStateSignal {
// Handle updating discord RPC
if !self.has_updated_discord_rpc {
discord.send(DiscordRpcSignal::EndGameTimer).await.unwrap();
discord
.send(DiscordRpcSignal::ChangeDetails {
details: "Looking at a menu".to_string(),
party_status: None,
})
.await
.unwrap();
self.has_updated_discord_rpc = true;
}
// Get a drawing handle
let mut draw = raylib.begin_drawing(rl_thread);
// Clear the screen
draw.clear_background(Color::WHITE);
//Obtain mouse position
let mouse_x = draw.get_mouse_x();
let mouse_y = draw.get_mouse_y();
//I wanna see where mouseeee
draw.draw_text(&mouse_x.to_string(), 20, 5, 20, Color::BLACK);
draw.draw_text(&mouse_y.to_string(), 70, 5, 20, Color::BLACK);
// TODO: Render stuff
//Initial Option placeholder words in the main menu
draw.draw_text("Game Title", 100, 90, 60, Color::BLACK);
draw.draw_text("Start Game", 100, 190, 34, Color::BLACK);
draw.draw_text("Options", 100, 250, 34, Color::BLACK);
draw.draw_text("Volume", 100, 300, 34, Color::BLACK);
draw.draw_text("Credits", 100, 410, 34, Color::BLACK);
draw.draw_text("Leaderboard", 100, 470, 34, Color::BLACK);
draw.draw_text("Exit", 100, 550, 34, Color::BLACK);
//First two are starting X and Y position, last two finishing X and Y. Made to resemble a box
if mouse_x >= 100 && mouse_y >= 193 && mouse_x <= 290 && mouse_y <= 216 {
//Insides while make a lil shade for it to look cool
draw.draw_text("Start Game", 103, 191, 34, Color::GRAY);
draw.draw_text("Start Game", 100, 190, 34, Color::BLACK);
if draw.is_mouse_button_down(MouseButton::MOUSE_LEFT_BUTTON) {
return MenuStateSignal::StartGame;
}
}
if mouse_x >= 100 && mouse_y >= 250 && mouse_x <= 222 && mouse_y <= 275 {
draw.draw_text("Options", 103, 251, 34, Color::GRAY);
draw.draw_text("Options", 100, 250, 34, Color::BLACK);
if draw.is_mouse_button_down(MouseButton::MOUSE_LEFT_BUTTON) {
return MenuStateSignal::DoOptions;
}
}
if mouse_x >= 100 && mouse_y >= 410 && mouse_x <= 222 && mouse_y <= 437 {
draw.draw_text("Credits", 103, 411, 34, Color::GRAY);
draw.draw_text("Credits", 100, 410, 34, Color::BLACK);
if draw.is_mouse_button_down(MouseButton::MOUSE_LEFT_BUTTON) {
return MenuStateSignal::DoCredits;
}
}
if mouse_x >= 100 && mouse_y >= 470 && mouse_x <= 316 && mouse_y <= 496 {
draw.draw_text("Leaderboard", 103, 471, 34, Color::GRAY);
draw.draw_text("Leaderboard", 100, 470, 34, Color::BLACK);
if draw.is_mouse_button_down(MouseButton::MOUSE_LEFT_BUTTON) {
return MenuStateSignal::DoLeaderboard;
}
}
if mouse_x >= 100 && mouse_y >= 300 && mouse_x <= 215 && mouse_y <= 330 {
draw.draw_text("Volume", 103, 301, 34, Color::GRAY);
draw.draw_text("Volume", 100, 300, 34, Color::BLACK);
if draw.is_mouse_button_down(MouseButton::MOUSE_LEFT_BUTTON) {
//Function for Volume here
}
}
//Exit button has no function yet
if mouse_x >= 100 && mouse_y >= 550 && mouse_x <= 162 && mouse_y <= 575 {
draw.draw_text("Exit", 103, 551, 34, Color::GRAY);
draw.draw_text("Exit", 100, 550, 34, Color::BLACK);
}
// Return MenuStateSignal::StartGame if you want the game to start.
// Otherwise, keep returning MenuStateSignal::DoMainMenu until the player clicks the start button
return MenuStateSignal::DoMainMenu;
}
pub async fn render_options_frame(
&mut self,
raylib: &mut RaylibHandle,
rl_thread: &RaylibThread,
discord: &DiscordChannel,
global_resources: &GlobalResources,
constants: &ProjectConstants,
) -> MenuStateSignal {
//Draw declared
let mut draw = raylib.begin_drawing(rl_thread);
draw.clear_background(Color::WHITE);
//Mouse Position
let mouse_x = draw.get_mouse_x();
let mouse_y = draw.get_mouse_y();
//Show mouse position
draw.draw_text(&mouse_x.to_string(), 20, 5, 20, Color::BLACK);
draw.draw_text(&mouse_y.to_string(), 70, 5, 20, Color::BLACK);
//Top Label
draw.draw_text("Options", 25, 30, 55, Color::BLACK);
//Return Button
draw.draw_text("Return", 100, 550, 34, Color::BLACK);
if mouse_x >= 100 && mouse_y >= 550 && mouse_x <= 216 && mouse_y <= 576 {
draw.draw_text("Return", 103, 551, 34, Color::GRAY);
draw.draw_text("Return", 100, 550, 34, Color::BLACK);
if draw.is_mouse_button_down(MouseButton::MOUSE_LEFT_BUTTON) {
return MenuStateSignal::DoMainMenu; //Goes back to main menu
}
}
return MenuStateSignal::DoOptions;
}
pub async fn render_credits_frame(
&mut self,
raylib: &mut RaylibHandle,
rl_thread: &RaylibThread,
discord: &DiscordChannel,
global_resources: &GlobalResources,
constants: &ProjectConstants,
) -> MenuStateSignal {
let mut draw = raylib.begin_drawing(rl_thread);
draw.clear_background(Color::WHITE);
//Mouse Position
let mouse_x = draw.get_mouse_x();
let mouse_y = draw.get_mouse_y();
//Show mouse position
draw.draw_text(&mouse_x.to_string(), 20, 5, 20, Color::BLACK);
draw.draw_text(&mouse_y.to_string(), 70, 5, 20, Color::BLACK);
//Screen Size
// let window_height = draw.get_screen_height();
let window_width = draw.get_screen_width();
draw.draw_text("Credits", (window_width/2) - 100, 30, 55, Color::BLACK);
draw.draw_text("Carter Tomlenovich", (window_width/2) - 170, 280, 40, Color::DARKBLUE);
draw.draw_text("Emilia Firas", (window_width/2) - 170, 120, 40, Color::DARKBLUE);
draw.draw_text("Emmet Logue", (window_width/2) - 170, 320, 40, Color::DARKBLUE);
draw.draw_text("Evan Pratten", (window_width/2) - 170, 160, 40, Color::DARKBLUE);
draw.draw_text("James Nickoli", (window_width/2) - 170, 240, 40, Color::DARKBLUE);
draw.draw_text("Marcelo Geldres", (window_width/2) - 170, 440, 40, Color::DARKBLUE);
draw.draw_text("Percy", (window_width/2) - 170, 400, 40, Color::DARKBLUE);
draw.draw_text("Silas Bartha", (window_width/2) - 170, 200, 40, Color::DARKBLUE);
draw.draw_text("Taya Armstrong", (window_width/2) - 170, 360, 40, Color::DARKBLUE);
//Return Button
draw.draw_text("Return", 100, 550, 34, Color::BLACK);
if mouse_x >= 100 && mouse_y >= 550 && mouse_x <= 216 && mouse_y <= 576 {
draw.draw_text("Return", 103, 551, 34, Color::GRAY);
draw.draw_text("Return", 100, 550, 34, Color::BLACK);
if draw.is_mouse_button_down(MouseButton::MOUSE_LEFT_BUTTON) {
return MenuStateSignal::DoMainMenu;
}
}
return MenuStateSignal::DoCredits;
}
pub async fn render_leaderboard_frame(
&mut self,
raylib: &mut RaylibHandle,
rl_thread: &RaylibThread,
discord: &DiscordChannel,
global_resources: &GlobalResources,
constants: &ProjectConstants,
) -> MenuStateSignal {
let mut draw = raylib.begin_drawing(rl_thread);
draw.clear_background(Color::WHITE);
//Mouse Position
let mouse_x = draw.get_mouse_x();
let mouse_y = draw.get_mouse_y();
//Show mouse position
draw.draw_text(&mouse_x.to_string(), 20, 5, 20, Color::BLACK);
draw.draw_text(&mouse_y.to_string(), 70, 5, 20, Color::BLACK);
let window_width = draw.get_screen_width();
draw.draw_text("Leaderboard", (window_width/2) - 176, 30, 55, Color::BLACK);
//Return Button
draw.draw_text("Return", 100, 550, 34, Color::BLACK);
if mouse_x >= 100 && mouse_y >= 550 && mouse_x <= 216 && mouse_y <= 576 {
draw.draw_text("Return", 103, 551, 34, Color::GRAY);
draw.draw_text("Return", 100, 550, 34, Color::BLACK);
if draw.is_mouse_button_down(MouseButton::MOUSE_LEFT_BUTTON) {
return MenuStateSignal::DoMainMenu;
}
}
return MenuStateSignal::DoLeaderboard;
}
}

View File

@ -10,16 +10,23 @@ use crate::{
project_constants::ProjectConstants,
};
use self::{player_interaction::PlayableScene, test_fox::TestFoxScene};
use self::{
main_menu::{MainMenu, MenuStateSignal},
player_interaction::PlayableScene,
test_fox::TestFoxScene,
};
mod main_menu;
mod player_interaction;
mod test_fox;
/// Delegate for handling rendering.
/// This is a struct to allow for stateful data (like sub-screens) to be set up
pub struct SceneRenderDelegate {
menu_control_signal: MenuStateSignal,
/* Scenes */
scene_test_fox: TestFoxScene,
scene_playable: PlayableScene,
scene_main_menu: MainMenu,
}
impl SceneRenderDelegate {
@ -34,10 +41,13 @@ impl SceneRenderDelegate {
// Init some scenes
let scene_test_fox = TestFoxScene::new(raylib, rl_thread);
let scene_playable = PlayableScene::new(raylib, rl_thread, constants);
let scene_main_menu = MainMenu::new(raylib, rl_thread, constants);
Self {
menu_control_signal: MenuStateSignal::DoMainMenu,
scene_test_fox,
scene_playable,
scene_main_menu,
}
}
@ -52,10 +62,50 @@ impl SceneRenderDelegate {
global_resources: &GlobalResources,
constants: &ProjectConstants,
) {
// For now, we will just render the game scene
self.scene_test_fox
.render_frame(raylib, rl_thread, &discord, global_resources)
.await;
// Render the main menu if in it, otherwise, render the game
match self.menu_control_signal {
MenuStateSignal::StartGame => {
// self.scene_playable
// .render_frame(raylib, rl_thread, &discord, global_resources, constants)
// .await;
// TODO: remove this test scene
self.scene_test_fox
.render_frame(raylib, rl_thread, &discord, global_resources)
.await;
}
MenuStateSignal::QuitGame => unimplemented!(),
MenuStateSignal::DoMainMenu => {
self.menu_control_signal = self
.scene_main_menu
.render_main_menu_frame(raylib, rl_thread, discord, global_resources, constants)
.await
}
MenuStateSignal::DoOptions => {
self.menu_control_signal = self
.scene_main_menu
.render_options_frame(raylib, rl_thread, discord, global_resources, constants)
.await
}
MenuStateSignal::DoCredits => {
self.menu_control_signal = self
.scene_main_menu
.render_credits_frame(raylib, rl_thread, discord, global_resources, constants)
.await
}
MenuStateSignal::DoLeaderboard => {
self.menu_control_signal = self
.scene_main_menu
.render_leaderboard_frame(
raylib,
rl_thread,
discord,
global_resources,
constants,
)
.await
}
}
}
}

View File

@ -1,18 +1,20 @@
//! This "scene" is used only for testing animation and resource loading
//! It should be removed once the game is being worked on
use raylib::prelude::*;
use nalgebra as na;
use raylib::prelude::*;
use crate::{
discord::DiscordChannel, global_resource_package::GlobalResources,
discord::DiscordChannel,
global_resource_package::GlobalResources,
rendering::utilities::{anim_texture::AnimatedTexture, map_render::MapRenderer},
};
#[derive(Debug)]
pub struct TestFoxScene {
fox_animation: AnimatedTexture,
world_map: MapRenderer
world_map: MapRenderer,
camera: Camera2D,
}
impl TestFoxScene {
@ -22,9 +24,24 @@ impl TestFoxScene {
let fox = AnimatedTexture::new(raylib_handle, thread, "chr", "testFox").unwrap();
// Load the map
let map_renderer = MapRenderer::new("map_gameMap.tmx").unwrap();
let map_renderer = MapRenderer::new("map_gameMap.tmx", raylib_handle, thread).unwrap();
Self { fox_animation: fox, world_map: map_renderer }
// Create a camera
let camera = Camera2D {
target: Vector2 { x: 0.0, y: 0.0 },
offset: Vector2 {
x: raylib_handle.get_screen_width() as f32,
y: (raylib_handle.get_screen_height() as f32) * -0.5,
},
rotation: 0.0,
zoom: 1.0,
};
Self {
fox_animation: fox,
world_map: map_renderer,
camera,
}
}
/// Handler for each frame
@ -50,5 +67,27 @@ impl TestFoxScene {
None,
None,
);
// Allow the camera to be moved with wasd
if draw.is_key_down(KeyboardKey::KEY_W) {
self.camera.target.y -= 5.0;
}
if draw.is_key_down(KeyboardKey::KEY_S) {
self.camera.target.y += 5.0;
}
if draw.is_key_down(KeyboardKey::KEY_A) {
self.camera.target.x -= 5.0;
}
if draw.is_key_down(KeyboardKey::KEY_D) {
self.camera.target.x += 5.0;
}
{
// Begin camera mode
let mut ctx2d = draw.begin_mode2D(self.camera);
// Render the map
self.world_map.render_map(&mut ctx2d, &self.camera, true);
}
}
}