V3 Doc Event Editor - PokemonWorkshop/PokemonStudio GitHub Wiki

Event Editor

The Event Editor is a central component of Pokémon Studio to manage the event commands. It allows game creators to manage the event commands.

1. Overview & behavior

The Event Editor uses the library xyflow (= reactflow). This allows commands to be presented in a graph form. There are three fundamental entities:

  • Command: it's a node
  • Link between commands: in xyflow, they are named edges
  • Handle: the anchor points that can be placed on a node and used to link them

File: src\views\components\world\event\EventEditor.tsx

Users can:

  • Drag & Drop a command from the Command Library to add a command in the event
  • Delete a command using DEL key
  • Move a command
  • Edit a command
  • Connect a command

Multiple selection is available for deleting and moving commands.

2. Code structure and organization

Adding a command node

Folder: src\views\components\world\event\commands

  • Create a command node component (ex: InsertScriptCommand)
  • Follow this template for the implementation:
const MY_COMMAND_EDITOR_SCHEMA = EVENT_COMMAND_MY_COMMAND_VALIDATOR.pick({ /* use the props needed */ });

export const MyCommandCommand = ({ id, data: { dialogsRef, command, comments }, selected }: CommandNodeProps) => {
  const { CommandNode, updateCommand } = useCommandNode<StudioEventCommandMyCommand>(id);
  const { type: commandType, ...commandData } = command as StudioEventCommandData<StudioEventCommandMyCommand>;
  const { canClose, getFormData, reload, defaults, formRef } = useZodForm(MY_COMMAND_EDITOR_SCHEMA, commandData);
  const { /* use the components needed */ } = useNodeInputAttrsWithLabel(MY_COMMAND_EDITOR_SCHEMA, defaults);
  const { t } = useTranslation();

  const onBlur = () => {
    const result = canClose() && getFormData();
    if (!result || !result.success) return;

    updateCommand(result.data);
  };

  useEffect(() => {
    reload(commandData);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [command]);

  return (
    <CommandNode commandType={commandType} commentCount={comments.length} dialogsRef={dialogsRef} nodeId={id} selected={selected}>
      {/*Here you can define more handles (two handles are created by default)*/}
      <InputFormContainer ref={formRef} onBlur={onBlur}>        
        {/*Here is the content of the node*/}
      </InputFormContainer>
    </CommandNode>
  );
};

Adding default values for a command

File: src\utils\eventCommandCreation.ts

  • Create a function to define default values

Example for the insert script command:

const insertScriptCommand = { script: '' };

export const EventCommandCreation: Record<StudioEventCommandType, Omit<StudioEventCommandData<StudioEventCommand>, 'type'>> = {
  show_message: {},
  ...,
  insert_script: insertScriptCommand, /*Add Command here*/
};

Adding a command editor

Folder: src\views\components\world\event\commands\editors

  • Create a command editor component (ex: InsertScriptEditor)
  • Follow this template for the implementation:
const NEW_COMMAND_EDITOR_SCHEMA = EVENT_COMMAND_NEW_COMMAND_VALIDATOR.pick({ /*Select the useful properties*/ });

export const NewCommandEditor = forwardRef<EditorHandlingClose, EventEditorProps>(({ commandId: defaultCommandId, event }, ref) => {
  const { command, updateCommand } = useCommandEditor<StudioEventCommandInsertScript>(event, defaultCommandId);
  const { canClose, getFormData, defaults, formRef } = useZodForm(NEW_COMMAND_EDITOR_SCHEMA, command);
  const { Input } = useInputAttrsWithLabel(NEW_COMMAND_EDITOR_SCHEMA, defaults);
  const { t } = useTranslation();

  const onClose = () => {
    const result = canClose() && getFormData();
    if (!result || !result.success) return;

    updateCommand(result.data);
  };
  useEditorHandlingClose(ref, onClose, canClose);

  return (
    <Editor type="edit" title={t(`event_command_new_command`)}>
      <InputFormContainer ref={formRef}>
        <Input name="" label={t(`event_command_label`)} />
      </InputFormContainer>
    </Editor>
  );
});

NewCommandEditor.displayName = 'NewCommandEditor';

Link the command with the command editor

File: src\views\components\world\event\commands\editors\CommandEditorOverlay.tsx

  • Add the command editor in the switch case

Example for the insert script command:

export const CommandEditorOverlay = defineEditorOverlay<CommandEditorAndDeletionKeys, { commandId?: CommandId; event: StudioEvent }>(
  'CommandEditorOverlay',
  (dialogToShow, handleCloseRef, closeDialog, { commandId, event }) => {
    switch (dialogToShow) {
      case 'show_message':
        ...
      case 'insert_script':
        /* Add Editor here */
        return <InsertScriptEditor commandId={commandId} event={event} ref={handleCloseRef} />; 
      ...
    }
  }
);

Add or update the color and the icon of the command

File: src\views\components\world\event\common\EventIcon.tsx

  • Add a new color in EventColor (if necessary)
  • Add the icon and the color in IconsFromCommand
⚠️ **GitHub.com Fallback** ⚠️