object scripting wip
This commit is contained in:
+113
-11
@@ -3,7 +3,7 @@ use egui::{Sense, vec2};
|
||||
|
||||
use crate::font::BitmapFont;
|
||||
use crate::font_dialog::{FontDialogState, show as show_font_dialog};
|
||||
use crate::game::{ALL_ARCHETYPES, Archetype, FontSpec, Glyph};
|
||||
use crate::game::{ALL_ARCHETYPES, Archetype, Board, Glyph};
|
||||
use crate::render::{PALETTE_PANEL_W, paint_glyph};
|
||||
|
||||
/// Which tab is active in the editor side panel.
|
||||
@@ -11,8 +11,12 @@ use crate::render::{PALETTE_PANEL_W, paint_glyph};
|
||||
pub(crate) enum EditorTab {
|
||||
/// Archetype + glyph palette for painting cells.
|
||||
Palette,
|
||||
/// Board-level properties.
|
||||
/// Object placement and script assignment.
|
||||
Objects,
|
||||
/// Board-level properties (font, board script).
|
||||
Board,
|
||||
/// Named script library for this board.
|
||||
Scripts,
|
||||
/// World-level properties (placeholder).
|
||||
World,
|
||||
}
|
||||
@@ -23,7 +27,7 @@ pub(crate) struct EditorState {
|
||||
pub(crate) selected: Archetype,
|
||||
/// The glyph (tile index + colors) to stamp. Independent of the archetype's default.
|
||||
pub(crate) glyph: Glyph,
|
||||
/// Whether the glyph picker dialog is currently open.
|
||||
/// Whether the glyph picker dialog is currently open (Palette tab).
|
||||
pub(crate) glyph_picker_open: bool,
|
||||
/// Whether the font picker dialog is currently open.
|
||||
pub(crate) font_dialog_open: bool,
|
||||
@@ -31,6 +35,17 @@ pub(crate) struct EditorState {
|
||||
pub(crate) font_dialog_state: FontDialogState,
|
||||
/// Which side-panel tab is currently shown.
|
||||
pub(crate) tab: EditorTab,
|
||||
/// Index into `board.objects` of the currently selected object, or `None`.
|
||||
pub(crate) selected_object: Option<usize>,
|
||||
/// Whether the glyph picker is open for editing the selected object's glyph.
|
||||
pub(crate) object_glyph_picker_open: bool,
|
||||
/// Local copy of the selected object's cell glyph for the picker to edit.
|
||||
///
|
||||
/// Kept separate from `board.cells` to avoid a borrow conflict: `glyph_picker::show`
|
||||
/// takes both `&mut Glyph` (edit target) and `&[(Glyph, Archetype)]` (board palette),
|
||||
/// and those can't both alias `board.cells`. This copy is written back to the board
|
||||
/// each frame while an object is selected.
|
||||
pub(crate) object_editing_glyph: Glyph,
|
||||
}
|
||||
|
||||
impl EditorState {
|
||||
@@ -43,19 +58,23 @@ impl EditorState {
|
||||
font_dialog_open: false,
|
||||
font_dialog_state: FontDialogState::from_spec(None),
|
||||
tab: EditorTab::Palette,
|
||||
selected_object: None,
|
||||
object_glyph_picker_open: false,
|
||||
object_editing_glyph: Glyph::player(), // overwritten on first selection
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders the editor side panel (must be called before `CentralPanel`).
|
||||
///
|
||||
/// Shows tab bar (Palette / Board / World) and the content for the active tab.
|
||||
/// `font_spec` is the board's current font override (may be `None` for default).
|
||||
/// `active_font` is the resolved font currently in use for rendering.
|
||||
/// Shows tab bar (Palette / Objects / Board / Scripts / World) and the content
|
||||
/// for the active tab. `board` is the current board, used by the Objects and
|
||||
/// Scripts tabs and to read/write the board font. `active_font` is the resolved
|
||||
/// font currently in use for rendering.
|
||||
pub(crate) fn show_editor_panel(
|
||||
ctx: &egui::Context,
|
||||
editor: &mut EditorState,
|
||||
font_spec: &mut Option<FontSpec>,
|
||||
board: &mut Board,
|
||||
active_font: &BitmapFont,
|
||||
) {
|
||||
let tw = active_font.tile_w as f32;
|
||||
@@ -65,9 +84,12 @@ pub(crate) fn show_editor_panel(
|
||||
.resizable(true)
|
||||
.default_width(PALETTE_PANEL_W)
|
||||
.show(ctx, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
// Tab bar — wraps automatically if the panel is narrow.
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
ui.selectable_value(&mut editor.tab, EditorTab::Palette, "Palette");
|
||||
ui.selectable_value(&mut editor.tab, EditorTab::Objects, "Objects");
|
||||
ui.selectable_value(&mut editor.tab, EditorTab::Board, "Board");
|
||||
ui.selectable_value(&mut editor.tab, EditorTab::Scripts, "Scripts");
|
||||
ui.selectable_value(&mut editor.tab, EditorTab::World, "World");
|
||||
});
|
||||
ui.separator();
|
||||
@@ -111,18 +133,98 @@ pub(crate) fn show_editor_panel(
|
||||
}
|
||||
}
|
||||
|
||||
EditorTab::Objects => {
|
||||
match editor.selected_object {
|
||||
None => {
|
||||
ui.label("Click an object on the board to select it.");
|
||||
}
|
||||
Some(i) => {
|
||||
// Collect sorted script names before taking a mutable borrow on objects.
|
||||
let mut script_names: Vec<String> =
|
||||
board.scripts.keys().cloned().collect();
|
||||
script_names.sort();
|
||||
|
||||
ui.label("Glyph");
|
||||
// Preview button — same pattern as Palette tab.
|
||||
let (cell_rect, glyph_btn) =
|
||||
ui.allocate_exact_size(vec2(tw, th), Sense::click());
|
||||
paint_glyph(
|
||||
ui.painter(),
|
||||
cell_rect,
|
||||
&editor.object_editing_glyph,
|
||||
active_font,
|
||||
);
|
||||
if glyph_btn.clicked() {
|
||||
editor.object_glyph_picker_open = true;
|
||||
}
|
||||
|
||||
let obj = &mut board.objects[i];
|
||||
let current =
|
||||
obj.script_name.as_deref().unwrap_or("(none)").to_string();
|
||||
|
||||
ui.label("Script");
|
||||
egui::ComboBox::from_id_salt("obj_script")
|
||||
.selected_text(¤t)
|
||||
.show_ui(ui, |ui| {
|
||||
// "(none)" clears the script assignment.
|
||||
if ui
|
||||
.selectable_label(obj.script_name.is_none(), "(none)")
|
||||
.clicked()
|
||||
{
|
||||
obj.script_name = None;
|
||||
}
|
||||
for name in &script_names {
|
||||
let is_sel =
|
||||
obj.script_name.as_deref() == Some(name.as_str());
|
||||
if ui.selectable_label(is_sel, name.as_str()).clicked() {
|
||||
obj.script_name = Some(name.clone());
|
||||
}
|
||||
}
|
||||
// Placeholder for future "create and edit a new script" flow.
|
||||
let _ = ui.selectable_label(false, "(create new...)");
|
||||
});
|
||||
|
||||
if ui.button("Edit Script").clicked() {
|
||||
// TODO: open script editor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EditorTab::Board => {
|
||||
let font_label = match font_spec {
|
||||
let font_label = match &board.font {
|
||||
Some(s) => s.path.clone(),
|
||||
None => "(default)".to_owned(),
|
||||
};
|
||||
ui.label(format!("Font: {font_label}"));
|
||||
if ui.button("Font…").clicked() {
|
||||
editor.font_dialog_state = FontDialogState::from_spec(font_spec.as_ref());
|
||||
editor.font_dialog_state =
|
||||
FontDialogState::from_spec(board.font.as_ref());
|
||||
editor.font_dialog_open = true;
|
||||
}
|
||||
}
|
||||
|
||||
EditorTab::Scripts => {
|
||||
if board.scripts.is_empty() {
|
||||
ui.label("No scripts defined.");
|
||||
} else {
|
||||
let mut names: Vec<&String> = board.scripts.keys().collect();
|
||||
names.sort();
|
||||
egui::ScrollArea::vertical()
|
||||
.id_salt("scripts_list")
|
||||
.show(ui, |ui| {
|
||||
for name in names {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(name);
|
||||
if ui.button("Edit").clicked() {
|
||||
// TODO: open script editor
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
EditorTab::World => {}
|
||||
}
|
||||
});
|
||||
@@ -133,7 +235,7 @@ pub(crate) fn show_editor_panel(
|
||||
ctx,
|
||||
&mut editor.font_dialog_open,
|
||||
&mut editor.font_dialog_state,
|
||||
font_spec,
|
||||
&mut board.font,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user