From b5e7d9f575748b06f0c06199b954e58f67fde9e3 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 13 Feb 2026 17:36:05 +0000 Subject: [PATCH] Add create-character tool for placing new characters on the stage Adds a new toolbar button that lets users click on the stage to create a brand new character with an actor at the clicked position, then opens the paint editor for the character and resets to the pointer tool. https://claude.ai/code/session_01KcEJ6DLY6RJU1aCTzGShAe --- .../src/editor/components/cursor-support.tsx | 3 +++ frontend/src/editor/components/stage/stage.tsx | 17 +++++++++++++++-- frontend/src/editor/components/toolbar.tsx | 2 +- frontend/src/editor/constants/constants.ts | 1 + .../src/editor/img/cursor_create_character.png | Bin 0 -> 179 bytes .../editor/img/sidebar_create-character.png | Bin 0 -> 273 bytes 6 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 frontend/src/editor/img/cursor_create_character.png create mode 100644 frontend/src/editor/img/sidebar_create-character.png diff --git a/frontend/src/editor/components/cursor-support.tsx b/frontend/src/editor/components/cursor-support.tsx index 34866fba..3430a623 100644 --- a/frontend/src/editor/components/cursor-support.tsx +++ b/frontend/src/editor/components/cursor-support.tsx @@ -13,6 +13,7 @@ const IMAGES = { IGNORE_SQUARE: new URL("../img/cursor_ignored_square.png", import.meta.url).href, CURSOR_STAMP_RULE: new URL("../img/cursor_stamp_rule.png", import.meta.url).href, ADD_CLICK_CONDITION: new URL("../img/cursor_add_click_condition.png", import.meta.url).href, + CREATE_CHARACTER: new URL("../img/cursor_create_character.png", import.meta.url).href, }; /** All our normal cursors are done via css ala `tool-stamp`, `tool-record`. @@ -113,6 +114,8 @@ export const StampCursorSupport = () => { cursorEl.setAttribute("src", IMAGES.IGNORE_SQUARE); } else if (selectedToolId == TOOLS.ADD_CLICK_CONDITION) { cursorEl.setAttribute("src", IMAGES.ADD_CLICK_CONDITION); + } else if (selectedToolId == TOOLS.CREATE_CHARACTER) { + cursorEl.setAttribute("src", IMAGES.CREATE_CHARACTER); } else { cursorEl.style.display = "none"; cursorEl.removeAttribute("src"); diff --git a/frontend/src/editor/components/stage/stage.tsx b/frontend/src/editor/components/stage/stage.tsx index e0af56ee..3f3b7aaf 100644 --- a/frontend/src/editor/components/stage/stage.tsx +++ b/frontend/src/editor/components/stage/stage.tsx @@ -16,6 +16,7 @@ import { toggleSquareIgnored, upsertRecordingCondition, } from "../../actions/recording-actions"; +import { createCharacter } from "../../actions/characters-actions"; import { changeActors, changeActorsIndividually, @@ -85,7 +86,7 @@ type SpriteDragState = { mode: "move" | "copy"; // Whether we're moving or copying (alt key) }; -const DRAGGABLE_TOOLS = [TOOLS.IGNORE_SQUARE, TOOLS.TRASH, TOOLS.STAMP]; +const DRAGGABLE_TOOLS = [TOOLS.IGNORE_SQUARE, TOOLS.TRASH, TOOLS.STAMP, TOOLS.CREATE_CHARACTER]; // Single empty image used for hiding native drag preview // eslint-disable-next-line react-refresh/only-export-components @@ -861,6 +862,17 @@ export const Stage = ({ } } } + if (selectedToolId === TOOLS.CREATE_CHARACTER) { + const newCharacterId = makeId("character"); + const action = createCharacter(newCharacterId); + dispatch(action); + dispatch( + createActors(world.id, stage.id, [ + { character: action.values as Character, initialValues: { position: { x, y } } }, + ]), + ); + dispatch(paintCharacterAppearance(newCharacterId, "idle")); + } }; // Note: In this handler, the mouse cursor may be outside the stage @@ -909,7 +921,8 @@ export const Stage = ({ TOOLS.TRASH === selectedToolId || TOOLS.STAMP === selectedToolId || TOOLS.RECORD === selectedToolId || - TOOLS.PAINT === selectedToolId + TOOLS.PAINT === selectedToolId || + TOOLS.CREATE_CHARACTER === selectedToolId ) { dispatch(selectToolId(TOOLS.POINTER)); } diff --git a/frontend/src/editor/components/toolbar.tsx b/frontend/src/editor/components/toolbar.tsx index dbd6fcff..48e03e65 100644 --- a/frontend/src/editor/components/toolbar.tsx +++ b/frontend/src/editor/components/toolbar.tsx @@ -154,7 +154,7 @@ const Toolbar = () => {
- {[TOOLS.POINTER, TOOLS.STAMP, TOOLS.TRASH, TOOLS.RECORD, TOOLS.PAINT].map(renderTool)} + {[TOOLS.POINTER, TOOLS.CREATE_CHARACTER, TOOLS.STAMP, TOOLS.TRASH, TOOLS.RECORD, TOOLS.PAINT].map(renderTool)}
diff --git a/frontend/src/editor/constants/constants.ts b/frontend/src/editor/constants/constants.ts index 8581ddda..bafd1de5 100644 --- a/frontend/src/editor/constants/constants.ts +++ b/frontend/src/editor/constants/constants.ts @@ -28,6 +28,7 @@ export enum TOOLS { TRASH = "trash", RECORD = "record", PAINT = "paint", + CREATE_CHARACTER = "create-character", // Used in the recording flow IGNORE_SQUARE = "ignore-square", diff --git a/frontend/src/editor/img/cursor_create_character.png b/frontend/src/editor/img/cursor_create_character.png new file mode 100644 index 0000000000000000000000000000000000000000..d09771a11853a8b645b4c9471c664a55c36aa111 GIT binary patch literal 179 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dERh}-6Ar*7pUUTGYP~dU7m}H~j z{(0H1i>#i#3MM{>>N~C1PId}3W?;B7=ey|jT`72f{rbVxU!Tup zst&jqWzYTl%656b<;9j3pJb=--zeg{RyFPH`nS5TXXkkCYyQNjysYBIwu$c(j!d}n eScXNcfRQPkSM%cx!789D7(8A5T-G@yGywnxG)5T! literal 0 HcmV?d00001 diff --git a/frontend/src/editor/img/sidebar_create-character.png b/frontend/src/editor/img/sidebar_create-character.png new file mode 100644 index 0000000000000000000000000000000000000000..d267f74fd308560809b1fbdb2f89183b9ec27399 GIT binary patch literal 273 zcmV+s0q*{ZP)<$Kabw~qfP*u|CNdin65B^Vf(>DaQxw`t7WOCBz^)uzZM~~fr~`fq zi2!^C+2Z&$q^btZGyqw-2V~{CixB`CPIc7PASfTI8Z1F5&w81pHRryI-2=X<5k>l7 zzwRSwq0uC+Gyqxs4p4rrvW%bvc2*eK)ef{~-dV1vj70>`4(KRajXq@eK!u?*Tq%0T zdM>RcEg>pXeBZ1j)hytXV>jbB1f=_H26ae9kxfAYZQu