Move passable and opaque into objectdef
This commit is contained in:
+27
-19
@@ -72,7 +72,7 @@ pub struct FontSpec {
|
|||||||
///
|
///
|
||||||
/// For scripted objects (`Archetype::Object`), the behavior will eventually be
|
/// For scripted objects (`Archetype::Object`), the behavior will eventually be
|
||||||
/// determined by the Rhai script rather than this struct. See the Object variant
|
/// determined by the Rhai script rather than this struct. See the Object variant
|
||||||
/// for details.
|
/// for details. (TODO fix this comment)
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct Behavior {
|
pub struct Behavior {
|
||||||
/// Whether an entity can walk through this cell.
|
/// Whether an entity can walk through this cell.
|
||||||
@@ -104,8 +104,6 @@ pub enum Archetype {
|
|||||||
Empty,
|
Empty,
|
||||||
/// A solid wall; impassable and opaque.
|
/// A solid wall; impassable and opaque.
|
||||||
Wall,
|
Wall,
|
||||||
/// A scripted object. Behavior defaults to impassable until script-driven.
|
|
||||||
Object,
|
|
||||||
/// Sentinel for map files that reference an unknown archetype name.
|
/// Sentinel for map files that reference an unknown archetype name.
|
||||||
/// Renders as a yellow `?` on red to make the error visible in-game.
|
/// Renders as a yellow `?` on red to make the error visible in-game.
|
||||||
ErrorBlock,
|
ErrorBlock,
|
||||||
@@ -125,10 +123,6 @@ impl Archetype {
|
|||||||
passable: false,
|
passable: false,
|
||||||
opaque: true,
|
opaque: true,
|
||||||
},
|
},
|
||||||
Archetype::Object => Behavior {
|
|
||||||
passable: false,
|
|
||||||
opaque: false,
|
|
||||||
},
|
|
||||||
Archetype::ErrorBlock => Behavior {
|
Archetype::ErrorBlock => Behavior {
|
||||||
passable: false,
|
passable: false,
|
||||||
opaque: true,
|
opaque: true,
|
||||||
@@ -141,7 +135,6 @@ impl Archetype {
|
|||||||
match self {
|
match self {
|
||||||
Archetype::Empty => "empty",
|
Archetype::Empty => "empty",
|
||||||
Archetype::Wall => "wall",
|
Archetype::Wall => "wall",
|
||||||
Archetype::Object => "object",
|
|
||||||
Archetype::ErrorBlock => "error_block",
|
Archetype::ErrorBlock => "error_block",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,11 +155,6 @@ impl Archetype {
|
|||||||
fg: Rgba8 { r: 0x80, g: 0x80, b: 0x80, a: 255 },
|
fg: Rgba8 { r: 0x80, g: 0x80, b: 0x80, a: 255 },
|
||||||
bg: Rgba8 { r: 0x60, g: 0x60, b: 0x60, a: 255 },
|
bg: Rgba8 { r: 0x60, g: 0x60, b: 0x60, a: 255 },
|
||||||
},
|
},
|
||||||
Archetype::Object => Glyph {
|
|
||||||
tile: 63,
|
|
||||||
fg: Rgba8 { r: 255, g: 255, b: 0, a: 255 }, // yellow
|
|
||||||
bg: Rgba8 { r: 0, g: 0, b: 0, a: 255 },
|
|
||||||
},
|
|
||||||
// Visually distinct so malformed map files are immediately obvious.
|
// Visually distinct so malformed map files are immediately obvious.
|
||||||
Archetype::ErrorBlock => Glyph {
|
Archetype::ErrorBlock => Glyph {
|
||||||
tile: 63,
|
tile: 63,
|
||||||
@@ -199,7 +187,7 @@ impl TryFrom<&str> for Archetype {
|
|||||||
///
|
///
|
||||||
/// `ErrorBlock` is excluded — it is a sentinel for load errors, not a valid
|
/// `ErrorBlock` is excluded — it is a sentinel for load errors, not a valid
|
||||||
/// editing choice.
|
/// editing choice.
|
||||||
pub const ALL_ARCHETYPES: &[Archetype] = &[Archetype::Empty, Archetype::Wall, Archetype::Object];
|
pub const ALL_ARCHETYPES: &[Archetype] = &[Archetype::Empty, Archetype::Wall];
|
||||||
|
|
||||||
/// A scripted object placed on the board, loaded from a map file.
|
/// A scripted object placed on the board, loaded from a map file.
|
||||||
///
|
///
|
||||||
@@ -228,11 +216,25 @@ pub struct ObjectDef {
|
|||||||
/// Visual representation of this object. Owned by the object (not derived
|
/// Visual representation of this object. Owned by the object (not derived
|
||||||
/// from the grid cell), so scripts can change tile, fg, and bg at runtime.
|
/// from the grid cell), so scripts can change tile, fg, and bg at runtime.
|
||||||
pub glyph: Glyph,
|
pub glyph: Glyph,
|
||||||
|
/// Whether the object blocks movement, of players or other objects
|
||||||
|
pub passable: bool,
|
||||||
|
/// Whether the object blocks line of sight / FOV for the player
|
||||||
|
pub opaque: bool,
|
||||||
/// Name of the Rhai script in [`Board::scripts`] that drives this object.
|
/// Name of the Rhai script in [`Board::scripts`] that drives this object.
|
||||||
/// `None` means this object has no script yet.
|
/// `None` means this object has no script yet.
|
||||||
pub script_name: Option<String>,
|
pub script_name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ObjectDef {
|
||||||
|
pub fn default_glyph() -> Glyph {
|
||||||
|
Glyph {
|
||||||
|
tile: 63,
|
||||||
|
fg: Rgba8 { r: 255, g: 255, b: 0, a: 255 }, // yellow
|
||||||
|
bg: Rgba8 { r: 0, g: 0, b: 0, a: 255 },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A portal that teleports the player to a named entry point on another board.
|
/// A portal that teleports the player to a named entry point on another board.
|
||||||
///
|
///
|
||||||
/// Portals are loaded from `[[portals]]` entries in `.toml` map files and
|
/// Portals are loaded from `[[portals]]` entries in `.toml` map files and
|
||||||
@@ -345,17 +347,23 @@ impl Board {
|
|||||||
/// Objects block movement before the grid cell archetype is consulted.
|
/// Objects block movement before the grid cell archetype is consulted.
|
||||||
/// Panics if `x` or `y` are out of bounds.
|
/// Panics if `x` or `y` are out of bounds.
|
||||||
pub fn is_passable(&self, x: usize, y: usize) -> bool {
|
pub fn is_passable(&self, x: usize, y: usize) -> bool {
|
||||||
// An object on this cell always blocks (until scripting overrides this).
|
// An object on this cell blocks if it's unpassable
|
||||||
if self.object_at(x, y).is_some() {
|
if let Some(obj) = self.object_at(x, y) && !obj.passable {
|
||||||
return false;
|
false
|
||||||
|
} else {
|
||||||
|
self.get(x, y).1.behavior().passable
|
||||||
}
|
}
|
||||||
self.get(x, y).1.behavior().passable
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the index into [`Board::objects`] of the object at `(x, y)`, if any.
|
/// Returns the index into [`Board::objects`] of the object at `(x, y)`, if any.
|
||||||
pub fn object_at(&self, x: usize, y: usize) -> Option<usize> {
|
pub fn object_index_at(&self, x: usize, y: usize) -> Option<usize> {
|
||||||
self.objects.iter().position(|o| o.x == x && o.y == y)
|
self.objects.iter().position(|o| o.x == x && o.y == y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a borrow of the actual object at `(x, y)` if any
|
||||||
|
pub fn object_at(&self, x: usize, y: usize) -> Option<&ObjectDef> {
|
||||||
|
self.object_index_at(x, y).map(|idx| &self.objects[idx])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Holds the active game board and provides game-logic operations.
|
/// Holds the active game board and provides game-logic operations.
|
||||||
|
|||||||
@@ -138,11 +138,19 @@ pub(crate) struct MapFileObjectEntry {
|
|||||||
fg: String,
|
fg: String,
|
||||||
/// Background color as an `"#RRGGBB"` hex string.
|
/// Background color as an `"#RRGGBB"` hex string.
|
||||||
bg: String,
|
bg: String,
|
||||||
|
/// Whether the object blocks player / object movement
|
||||||
|
#[serde(default)]
|
||||||
|
passable: bool,
|
||||||
|
/// Whether the object blocks player line of sight / FOV
|
||||||
|
#[serde(default = "default_true")]
|
||||||
|
opaque: bool,
|
||||||
/// Name of the Rhai script in the `[scripts]` table, if any.
|
/// Name of the Rhai script in the `[scripts]` table, if any.
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
script_name: Option<String>,
|
script_name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_true() -> bool { true }
|
||||||
|
|
||||||
/// Parses an `"#RRGGBB"` hex color string into an [`Rgba8`].
|
/// Parses an `"#RRGGBB"` hex color string into an [`Rgba8`].
|
||||||
/// Returns opaque black on any parse failure.
|
/// Returns opaque black on any parse failure.
|
||||||
fn parse_color(hex: &str) -> Rgba8 {
|
fn parse_color(hex: &str) -> Rgba8 {
|
||||||
@@ -256,6 +264,8 @@ impl TryFrom<MapFile> for Board {
|
|||||||
fg: parse_color(&e.fg),
|
fg: parse_color(&e.fg),
|
||||||
bg: parse_color(&e.bg),
|
bg: parse_color(&e.bg),
|
||||||
},
|
},
|
||||||
|
passable: e.passable,
|
||||||
|
opaque: e.opaque,
|
||||||
script_name: e.script_name,
|
script_name: e.script_name,
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@@ -365,6 +375,8 @@ impl From<&Board> for MapFile {
|
|||||||
tile: TileIndex::Num(o.glyph.tile),
|
tile: TileIndex::Num(o.glyph.tile),
|
||||||
fg: color_to_hex(o.glyph.fg),
|
fg: color_to_hex(o.glyph.fg),
|
||||||
bg: color_to_hex(o.glyph.bg),
|
bg: color_to_hex(o.glyph.bg),
|
||||||
|
passable: o.passable,
|
||||||
|
opaque: o.opaque,
|
||||||
script_name: o.script_name.clone(),
|
script_name: o.script_name.clone(),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|||||||
@@ -180,6 +180,10 @@ pub(crate) fn show_editor_panel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let obj = &mut board.objects[i];
|
let obj = &mut board.objects[i];
|
||||||
|
|
||||||
|
ui.checkbox(&mut obj.passable, "Passable");
|
||||||
|
ui.checkbox(&mut obj.opaque, "Opaque");
|
||||||
|
|
||||||
let current =
|
let current =
|
||||||
obj.script_name.as_deref().unwrap_or("(none)").to_string();
|
obj.script_name.as_deref().unwrap_or("(none)").to_string();
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ mod render;
|
|||||||
|
|
||||||
use editor::{EditorState, EditorTab, show_editor_panel};
|
use editor::{EditorState, EditorTab, show_editor_panel};
|
||||||
use font::BitmapFont;
|
use font::BitmapFont;
|
||||||
use kiln_core::game::{Archetype, FontSpec, GameState, Glyph, ObjectDef};
|
use kiln_core::game::{FontSpec, GameState, Glyph, ObjectDef};
|
||||||
use kiln_core::map_file;
|
use kiln_core::map_file;
|
||||||
use render::{
|
use render::{
|
||||||
DEFAULT_WINDOW_H, DEFAULT_WINDOW_W, MIN_WINDOW_H, MIN_WINDOW_W, board_origin, draw_board,
|
DEFAULT_WINDOW_H, DEFAULT_WINDOW_W, MIN_WINDOW_H, MIN_WINDOW_W, board_origin, draw_board,
|
||||||
@@ -236,22 +236,24 @@ impl eframe::App for App {
|
|||||||
if self.editor.tab == EditorTab::Objects {
|
if self.editor.tab == EditorTab::Objects {
|
||||||
if self.editor.placing_object {
|
if self.editor.placing_object {
|
||||||
// Placement mode: create a new object here if none exists.
|
// Placement mode: create a new object here if none exists.
|
||||||
if board.object_at(cx, cy).is_none() {
|
if board.object_index_at(cx, cy).is_none() {
|
||||||
board.objects.push(ObjectDef {
|
board.objects.push(ObjectDef {
|
||||||
x: cx,
|
x: cx,
|
||||||
y: cy,
|
y: cy,
|
||||||
glyph: Glyph {
|
glyph: Glyph {
|
||||||
tile: Archetype::Object.default_glyph().tile,
|
tile: ObjectDef::default_glyph().tile,
|
||||||
fg: Archetype::Object.default_glyph().fg,
|
fg: ObjectDef::default_glyph().fg,
|
||||||
bg: Archetype::Object.default_glyph().bg,
|
bg: ObjectDef::default_glyph().bg,
|
||||||
},
|
},
|
||||||
|
passable: false,
|
||||||
|
opaque: true,
|
||||||
script_name: None,
|
script_name: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
self.editor.placing_object = false;
|
self.editor.placing_object = false;
|
||||||
} else {
|
} else {
|
||||||
// Selection mode: click to select an existing object.
|
// Selection mode: click to select an existing object.
|
||||||
let found = board.object_at(cx, cy);
|
let found = board.object_index_at(cx, cy);
|
||||||
self.editor.selected_object = found;
|
self.editor.selected_object = found;
|
||||||
self.editor.object_glyph_picker_open = false;
|
self.editor.object_glyph_picker_open = false;
|
||||||
if let Some(i) = found {
|
if let Some(i) = found {
|
||||||
|
|||||||
@@ -37,3 +37,11 @@ content = """
|
|||||||
# #
|
# #
|
||||||
############################################################
|
############################################################
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
[[objects]]
|
||||||
|
fg = "#aa3333"
|
||||||
|
bg = "#000000"
|
||||||
|
x = 10
|
||||||
|
y = 10
|
||||||
|
tile = "#"
|
||||||
|
passable = true
|
||||||
Reference in New Issue
Block a user