// -*- coding: utf-8 -*- // // Copyright 2021 Michael Büsch // // Derived from https://github.com/mlochen/dungeon.git // Copyright (C) 2020 Marco Lochen // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 2 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // use crate::player::Player; use crate::vec2d::Vec2D; use crate::wall::Wall; use anyhow as ah; use std::fs::OpenOptions; use std::io::prelude::*; use std::path::Path; pub struct World { walls: Vec, player: Player, } impl World { pub fn new(walls: Vec, player: Player) -> World { World { walls, player } } pub fn load(file: &Path) -> ah::Result { let mut f = match OpenOptions::new().read(true).open(file) { Ok(f) => f, Err(e) => return Err(ah::format_err!("World::load({}): {}", file.display(), e)), }; let mut buf = String::new(); match f.read_to_string(&mut buf) { Ok(_) => (), Err(e) => return Err(ah::format_err!("World::load({}): {}", file.display(), e)), } let lines: Vec> = buf.lines().map(|line| line.chars().collect()).collect(); let height = lines.len(); if height < 2 { return Err(ah::format_err!( "World::load({}): Height too small.", file.display() )); } let width = lines[0].len(); if width < 2 { return Err(ah::format_err!( "World::load({}): Width too small.", file.display() )); } // Create walls. let mut walls = vec![]; for y in 0..(height - 1) { if lines[y].len() != width || lines[y + 1].len() != width { return Err(ah::format_err!( "World::load({}): Inconsistent width.", file.display() )); } for x in 0..(width - 1) { let this = lines[y][x]; let next_in_line = lines[y][x + 1]; let next_in_col = lines[y + 1][x]; if this == '#' { if next_in_line != '#' { // Bottom to top in map. walls.push(Wall::new( Vec2D::new(0.0, -1.0), Vec2D::new(x as f32 + 1.0, y as f32 + 0.5), )); } if next_in_col != '#' { // Left to right in map. walls.push(Wall::new( Vec2D::new(1.0, 0.0), Vec2D::new(x as f32 + 0.5, y as f32 + 1.0), )); } } else { if next_in_line == '#' { // Top to bottom in map. walls.push(Wall::new( Vec2D::new(0.0, 1.0), Vec2D::new(x as f32 + 1.0, y as f32 + 0.5), )); } if next_in_col == '#' { // Right to left in map. walls.push(Wall::new( Vec2D::new(-1.0, 0.0), Vec2D::new(x as f32 + 0.5, y as f32 + 1.0), )); } } } } // Create player, enemies, switches. let mut player = None; #[allow(clippy::needless_range_loop)] for y in 0..height { for x in 0..width { let this = lines[y][x]; if this == 'p' { // Player. if player.is_some() { return Err(ah::format_err!( "World::load({}): Found multiple players.", file.display() )); } player = Some(Player::new(Vec2D::new(x as f32 + 0.5, y as f32 + 0.5))); } if this == 'e' { // Enemy. //TODO } if this == 's' || this == 'g' { // Switch or goal. //TODO } } } let player = match player { Some(p) => p, None => { return Err(ah::format_err!( "World::load({}): No player found.", file.display() )) } }; Ok(Self::new(walls, player)) } pub fn get_walls(&self) -> &[Wall] { &self.walls } pub fn get_player(&self) -> &Player { &self.player } pub fn get_player_mut(&mut self) -> &mut Player { &mut self.player } } // vim: ts=4 sw=4 expandtab