diff --git a/assets/i18n/de.json b/assets/i18n/de.json index 2946e9a5..f1de7fc0 100644 --- a/assets/i18n/de.json +++ b/assets/i18n/de.json @@ -1812,6 +1812,15 @@ "event_command_teleport_event": "Teleport event", "event_command_teleport_player": "Teleport player", "event_command_wait_move_completion": "Wait for move completion", + "event_command_wait_move_completion_select": "Waiting", + "event_command_wait_move_completion_select_all": "All event", + "event_command_wait_move_completion_select_some": "Wait specific events", + "event_command_wait_move_completion_multiselect_label": "Events to wait", + "event_command_wait_move_completion_multiselect_static_player": "Player", + "event_command_wait_move_completion_multiselect_static_this_event": "This event", + "event_editor_wait_move_completion_wait_all": "Wait all events", + "event_editor_wait_move_completion_timeout": "Maximum waiting time", + "event_editor_wait_move_completion_timeout_info": "The event continues to run if travel takes longer.", "event_command_manage_event_reappearance": "Manage event reappearance", "event_command_manage_path_finding": "Manage path finding", "event_command_manage_follow_me": "Manage follow me", diff --git a/assets/i18n/en.json b/assets/i18n/en.json index 9a6fabd5..27c5d695 100644 --- a/assets/i18n/en.json +++ b/assets/i18n/en.json @@ -1812,6 +1812,15 @@ "event_command_teleport_event": "Teleport event", "event_command_teleport_player": "Teleport player", "event_command_wait_move_completion": "Wait for move completion", + "event_command_wait_move_completion_select": "Waiting", + "event_command_wait_move_completion_select_all": "All event", + "event_command_wait_move_completion_select_some": "Wait specific events", + "event_command_wait_move_completion_multiselect_label": "Events to wait", + "event_command_wait_move_completion_multiselect_static_player": "The player", + "event_command_wait_move_completion_multiselect_static_this_event": "This event", + "event_editor_wait_move_completion_wait_all": "Wait all events", + "event_editor_wait_move_completion_timeout": "Maximum waiting time", + "event_editor_wait_move_completion_timeout_info": "The event continues to run if travel takes longer.", "event_command_manage_event_reappearance": "Manage event reappearance", "event_command_manage_path_finding": "Manage path finding", "event_command_manage_follow_me": "Manage follow me", diff --git a/assets/i18n/es.json b/assets/i18n/es.json index 46e80a02..db9ad3dc 100644 --- a/assets/i18n/es.json +++ b/assets/i18n/es.json @@ -1812,6 +1812,15 @@ "event_command_teleport_event": "Teleport event", "event_command_teleport_player": "Teleport player", "event_command_wait_move_completion": "Wait for move completion", + "event_command_wait_move_completion_select": "Waiting", + "event_command_wait_move_completion_select_all": "All event", + "event_command_wait_move_completion_select_some": "Wait specific events", + "event_command_wait_move_completion_multiselect_label": "Events to wait", + "event_command_wait_move_completion_multiselect_static_player": "Player", + "event_command_wait_move_completion_multiselect_static_this_event": "This event", + "event_editor_wait_move_completion_wait_all": "Wait all events", + "event_editor_wait_move_completion_timeout": "Maximum waiting time", + "event_editor_wait_move_completion_timeout_info": "The event continues to run if travel takes longer.", "event_command_manage_event_reappearance": "Manage event reappearance", "event_command_manage_path_finding": "Manage path finding", "event_command_manage_follow_me": "Manage follow me", diff --git a/assets/i18n/fr.json b/assets/i18n/fr.json index 83e9fe1a..541991c1 100644 --- a/assets/i18n/fr.json +++ b/assets/i18n/fr.json @@ -1812,6 +1812,15 @@ "event_command_teleport_event": "Téléporter un événement", "event_command_teleport_player": "Téléporter le joueur", "event_command_wait_move_completion": "Attendre la fin des déplacements", + "event_command_wait_move_completion_select": "Attendre", + "event_command_wait_move_completion_select_all": "Tous les événements", + "event_command_wait_move_completion_select_some": "Des événements spécifiques", + "event_command_wait_move_completion_multiselect_label": "Événements à attendre", + "event_command_wait_move_completion_multiselect_static_player": "Le joueur", + "event_command_wait_move_completion_multiselect_static_this_event": "Cet événement", + "event_editor_wait_move_completion_wait_all": "Attendre tous les événements", + "event_editor_wait_move_completion_timeout": "Délai d'attente max", + "event_editor_wait_move_completion_timeout_info": "L’événement continue son exécution si les déplacements prennent plus de temps.", "event_command_manage_event_reappearance": "Gérer la réapparition de l'événement", "event_command_manage_path_finding": "Gérer le path finding", "event_command_manage_follow_me": "Gérer le Follow Me", diff --git a/assets/i18n/it.json b/assets/i18n/it.json index f99f14d6..7fca9d30 100644 --- a/assets/i18n/it.json +++ b/assets/i18n/it.json @@ -1812,6 +1812,15 @@ "event_command_teleport_event": "Teleport event", "event_command_teleport_player": "Teleport player", "event_command_wait_move_completion": "Wait for move completion", + "event_command_wait_move_completion_select": "Waiting", + "event_command_wait_move_completion_select_all": "All event", + "event_command_wait_move_completion_select_some": "Wait specific events", + "event_command_wait_move_completion_multiselect_label": "Events to wait", + "event_command_wait_move_completion_multiselect_static_player": "Player", + "event_command_wait_move_completion_multiselect_static_this_event": "This event", + "event_editor_wait_move_completion_wait_all": "Wait all events", + "event_editor_wait_move_completion_timeout": "Maximum waiting time", + "event_editor_wait_move_completion_timeout_info": "The event continues to run if travel takes longer.", "event_command_manage_event_reappearance": "Manage event reappearance", "event_command_manage_path_finding": "Manage path finding", "event_command_manage_follow_me": "Manage follow me", diff --git a/assets/i18n/ja.json b/assets/i18n/ja.json index 76c221ce..79f3ad67 100644 --- a/assets/i18n/ja.json +++ b/assets/i18n/ja.json @@ -1812,6 +1812,15 @@ "event_command_teleport_event": "Teleport event", "event_command_teleport_player": "Teleport player", "event_command_wait_move_completion": "Wait for move completion", + "event_command_wait_move_completion_select": "Waiting", + "event_command_wait_move_completion_select_all": "All event", + "event_command_wait_move_completion_select_some": "Wait specific events", + "event_command_wait_move_completion_multiselect_label": "Events to wait", + "event_command_wait_move_completion_multiselect_static_player": "Player", + "event_command_wait_move_completion_multiselect_static_this_event": "This event", + "event_editor_wait_move_completion_wait_all": "Wait all events", + "event_editor_wait_move_completion_timeout": "Maximum waiting time", + "event_editor_wait_move_completion_timeout_info": "The event continues to run if travel takes longer.", "event_command_manage_event_reappearance": "Manage event reappearance", "event_command_manage_path_finding": "Manage path finding", "event_command_manage_follow_me": "Manage follow me", diff --git a/assets/i18n/nl.json b/assets/i18n/nl.json index 6d56e200..00e09317 100644 --- a/assets/i18n/nl.json +++ b/assets/i18n/nl.json @@ -1812,6 +1812,15 @@ "event_command_teleport_event": "Teleport event", "event_command_teleport_player": "Teleport player", "event_command_wait_move_completion": "Wait for move completion", + "event_command_wait_move_completion_select": "Waiting", + "event_command_wait_move_completion_select_all": "All event", + "event_command_wait_move_completion_select_some": "Wait specific events", + "event_command_wait_move_completion_multiselect_label": "Events to wait", + "event_command_wait_move_completion_multiselect_static_player": "Player", + "event_command_wait_move_completion_multiselect_static_this_event": "This event", + "event_editor_wait_move_completion_wait_all": "Wait all events", + "event_editor_wait_move_completion_timeout": "Maximum waiting time", + "event_editor_wait_move_completion_timeout_info": "The event continues to run if travel takes longer.", "event_command_manage_event_reappearance": "Manage event reappearance", "event_command_manage_path_finding": "Manage path finding", "event_command_manage_follow_me": "Manage follow me", diff --git a/assets/i18n/pt.json b/assets/i18n/pt.json index 9eb2d65c..3904fa3b 100644 --- a/assets/i18n/pt.json +++ b/assets/i18n/pt.json @@ -1812,6 +1812,15 @@ "event_command_teleport_event": "Teleportar evento", "event_command_teleport_player": "Teleportar jogador", "event_command_wait_move_completion": "Aguardar a conclusão do movimento", + "event_command_wait_move_completion_select": "Waiting", + "event_command_wait_move_completion_select_all": "All event", + "event_command_wait_move_completion_select_some": "Wait specific events", + "event_command_wait_move_completion_multiselect_label": "Events to wait", + "event_command_wait_move_completion_multiselect_static_player": "Player", + "event_command_wait_move_completion_multiselect_static_this_event": "This event", + "event_editor_wait_move_completion_wait_all": "Wait all events", + "event_editor_wait_move_completion_timeout": "Maximum waiting time", + "event_editor_wait_move_completion_timeout_info": "The event continues to run if travel takes longer.", "event_command_manage_event_reappearance": "Gerenciar reaparecimento do evento", "event_command_manage_path_finding": "Gerenciar busca de caminhos", "event_command_manage_follow_me": "Gerenciar siga-me", diff --git a/assets/i18n/zh_Hans.json b/assets/i18n/zh_Hans.json index 659098d7..63ce6692 100644 --- a/assets/i18n/zh_Hans.json +++ b/assets/i18n/zh_Hans.json @@ -1812,6 +1812,15 @@ "event_command_teleport_event": "Teleport event", "event_command_teleport_player": "Teleport player", "event_command_wait_move_completion": "Wait for move completion", + "event_command_wait_move_completion_select": "Waiting", + "event_command_wait_move_completion_select_all": "All event", + "event_command_wait_move_completion_select_some": "Wait specific events", + "event_command_wait_move_completion_multiselect_label": "Events to wait", + "event_command_wait_move_completion_multiselect_static_player": "Player", + "event_command_wait_move_completion_multiselect_static_this_event": "This event", + "event_editor_wait_move_completion_wait_all": "Wait all events", + "event_editor_wait_move_completion_timeout": "Maximum waiting time", + "event_editor_wait_move_completion_timeout_info": "The event continues to run if travel takes longer.", "event_command_manage_event_reappearance": "Manage event reappearance", "event_command_manage_path_finding": "Manage path finding", "event_command_manage_follow_me": "Manage follow me", diff --git a/assets/i18n/zh_Hant.json b/assets/i18n/zh_Hant.json index 8f37a9b6..03536689 100644 --- a/assets/i18n/zh_Hant.json +++ b/assets/i18n/zh_Hant.json @@ -1812,6 +1812,15 @@ "event_command_teleport_event": "Teleport event", "event_command_teleport_player": "Teleport player", "event_command_wait_move_completion": "Wait for move completion", + "event_command_wait_move_completion_select": "Waiting", + "event_command_wait_move_completion_select_all": "All event", + "event_command_wait_move_completion_select_some": "Wait specific events", + "event_command_wait_move_completion_multiselect_label": "Events to wait", + "event_command_wait_move_completion_multiselect_static_player": "Player", + "event_command_wait_move_completion_multiselect_static_this_event": "This event", + "event_editor_wait_move_completion_wait_all": "Wait all events", + "event_editor_wait_move_completion_timeout": "Maximum waiting time", + "event_editor_wait_move_completion_timeout_info": "The event continues to run if travel takes longer.", "event_command_manage_event_reappearance": "Manage event reappearance", "event_command_manage_path_finding": "Manage path finding", "event_command_manage_follow_me": "Manage follow me", diff --git a/psdk-binaries/pokemonsdk b/psdk-binaries/pokemonsdk index 1a85ea61..406792df 160000 --- a/psdk-binaries/pokemonsdk +++ b/psdk-binaries/pokemonsdk @@ -1 +1 @@ -Subproject commit 1a85ea6150f17d5f6733949c4b87615ca97c4b97 +Subproject commit 406792df1e02d0ab75a06b76a7086c9577278e1b diff --git a/src/backendTasks/convertRMXPEventsToStudioEvents.ts b/src/backendTasks/convertRMXPEventsToStudioEvents.ts index 52a89fc0..413d606c 100644 --- a/src/backendTasks/convertRMXPEventsToStudioEvents.ts +++ b/src/backendTasks/convertRMXPEventsToStudioEvents.ts @@ -1,5 +1,5 @@ import { DbSymbol } from '@modelEntities/dbSymbol'; -import type { CommandId, StudioEventCommand } from '@modelEntities/event/command'; +import type { StudioEventCommand } from '@modelEntities/event/command'; import { EVENT_START_CSV_FILE_ID, type Appearance, @@ -12,6 +12,7 @@ import { import { StudioMap } from '@modelEntities/map'; import { findFirstAvailableCsvFileId, findFirstAvailableId } from '@utils/ModelUtils'; import log from 'electron-log'; +import { CommandId } from '../models/entities/event/globalCommand'; import { defineBackendServiceFunction } from './defineBackendServiceFunction'; import { readRMXPEvents, RMXPEvent } from './readRMXPEvents'; diff --git a/src/designSystem/MultiSelect/MultiSelect.tsx b/src/designSystem/MultiSelect/MultiSelect.tsx index 484e32c5..12e147e4 100644 --- a/src/designSystem/MultiSelect/MultiSelect.tsx +++ b/src/designSystem/MultiSelect/MultiSelect.tsx @@ -1,8 +1,8 @@ +import DownIcon from '@assets/icons/global/down-icon.svg'; import React from 'react'; +import { MultiSelectContainer, MultiSelectTextArea } from './MultiSelectContainer'; import { RenderOptions } from './RenderOptions'; import { MultiSelectProps, useMultiSelect } from './useMultiSelect'; -import { MultiSelectContainer, MultiSelectTextArea } from './MultiSelectContainer'; -import DownIcon from '@assets/icons/global/down-icon.svg'; import { ValueType } from './useRenderOptions'; export const MultiSelect = (props: MultiSelectProps) => { @@ -11,9 +11,12 @@ export const MultiSelect = ( chooseValue: (props.chooseValue as Value[]) ?? [], }); + const { name, ...textAreaProps } = inputProps; + return ( - + + {name && currentValues.map((val, i) => )} diff --git a/src/designSystem/Select/RenderOptions.tsx b/src/designSystem/Select/RenderOptions.tsx index 63d62a1f..c82be13d 100644 --- a/src/designSystem/Select/RenderOptions.tsx +++ b/src/designSystem/Select/RenderOptions.tsx @@ -1,7 +1,7 @@ +import React, { MouseEventHandler } from 'react'; import List, { ListRowProps } from 'react-virtualized/dist/es/List'; import { SelectOption } from './types'; import { RenderOptionsProps, useRenderOptions } from './useRenderOptions'; -import React, { MouseEventHandler } from 'react'; const CLASSES = ['option', 'option highlighted', 'option current', 'option highlighted current']; diff --git a/src/designSystem/Select/useSelect.ts b/src/designSystem/Select/useSelect.ts index 1cef6525..8b5a4de2 100644 --- a/src/designSystem/Select/useSelect.ts +++ b/src/designSystem/Select/useSelect.ts @@ -1,3 +1,4 @@ +import { normalize } from '@utils/normalize'; import { ChangeEventHandler, FocusEventHandler, @@ -9,10 +10,9 @@ import { useRef, useState, } from 'react'; +import type { List } from 'react-virtualized/dist/es/List'; import { RenderOptionRef, SelectOption } from './types'; import { getNotFoundExclusionPattern, getSelectDefaultLabel, positionAndShowPopover } from './utils'; -import type { List } from 'react-virtualized/dist/es/List'; -import { normalize } from '@utils/normalize'; export type SelectProps = { options: Readonly[]>; diff --git a/src/hooks/useInputAttrs.tsx b/src/hooks/useInputAttrs.tsx index 16403ac0..08413899 100644 --- a/src/hooks/useInputAttrs.tsx +++ b/src/hooks/useInputAttrs.tsx @@ -17,6 +17,7 @@ import { inputAttrs } from '@utils/inputAttrs'; import { basename } from '@utils/path'; import React, { useMemo, useRef, useState } from 'react'; import { z } from 'zod'; +import { MultiSelect } from '../designSystem/MultiSelect'; type WithSchemaKeyAndName = { name: string; @@ -80,9 +81,9 @@ export const useInputAttrs = (schema: z.ZodObject, d MultiLineInput: ({ name, schemaKey, ...props }: ReactProps) => ( ), - Select: ({ name, schemaKey, ...props }: ReactProps) => { - const { type, ...attrs } = inputAttrs(schema, name, defaults, schemaKey); - return ; }, Toggle: ({ name, schemaKey, ...props }: ReactProps) => { const { type, required, ...attrs } = inputAttrs(schema, name, defaults, schemaKey); @@ -164,22 +165,56 @@ export const useInputAttrsWithLabel = (schema: z.ZodObj ); }, - Select: ({ name, schemaKey, label, labelLeft, ...props }: ReactPropsWithLabel) => { - const { type, ...attrs } = inputAttrs(schema, name, defaults, schemaKey); - if (!label) return ; if (labelLeft) return ( - ); return ( - + + ); + }, + MultiSelect: ({ + name, + schemaKey, + label, + defaultValue, + selectAllOption, + whenAllOptionSelected, + ...props + }: ReactPropsWithLabel) => { + const { type, ...attrs } = inputAttrs(schema, name, defaults, schemaKey); + if (!label) + return ( + + ); + + return ( + + + ); }, @@ -232,6 +267,52 @@ export const useNodeInputAttrsWithLabel = (schema: z.Zo ); }, + Select: ({ name, schemaKey, label, defaultValue, ...props }: ReactPropsWithLabel) => { + const { type, defaultValue: _defaultValue, ...attrs } = inputAttrs(schema, name, defaults, schemaKey); + + if (!label) return + + ); + }, + MultiSelect: ({ + name, + schemaKey, + label, + defaultValue, + selectAllOption, + whenAllOptionSelected, + ...props + }: ReactPropsWithLabel) => { + const { type, ...attrs } = inputAttrs(schema, name, defaults, schemaKey); + if (!label) + return ( + + ); + + return ( + + + + + ); + }, MultiLineInput: ({ name, schemaKey, label, labelLeft, ...props }: ReactPropsWithLabel) => { if (!label) return ; diff --git a/src/hooks/useZodForm.ts b/src/hooks/useZodForm.ts index 45367217..1ae00cfc 100644 --- a/src/hooks/useZodForm.ts +++ b/src/hooks/useZodForm.ts @@ -148,7 +148,7 @@ export const useZodForm = ( ) => { const formRef = useRef(null); const touchedInputValidity = useRef({}); - const d = useMemo(() => formData(formRef, schema, defaults, fixturesBeforeValidation), [schema]); + const d = useMemo(() => formData(formRef, schema, defaults, fixturesBeforeValidation), [schema, defaults]); const [isValid, setIsValid] = useState(d.defaultValid); const onTouched = (inputName: string, isValid: boolean, value: string) => { diff --git a/src/models/entities/event/command.ts b/src/models/entities/event/command.ts index e76ad6a6..258292f2 100644 --- a/src/models/entities/event/command.ts +++ b/src/models/entities/event/command.ts @@ -1,24 +1,8 @@ import { POSITIVE_OR_ZERO_INT } from '@modelEntities/common'; import { z } from 'zod'; import type { StudioEventCommandCategory } from './category'; - -export const COMMAND_ID_VALIDATOR = z.string().brand('CommandId'); -export type CommandId = z.infer; - -export const COMMAND_CONNECTION_ID_VALIDATOR = z.string().brand('ConnectionId'); -export type ConnectionId = z.infer; - -const EVENT_COMMAND_STUDIO_DATA_VALIDATOR = z.object({ - x: z.number().int(), - y: z.number().int(), - comments: z.array(z.string()), -}); - -export const EVENT_COMMAND_CONNECTION_VALIDATOR = z.object({ - sourceHandle: z.string(), - target: COMMAND_ID_VALIDATOR, - targetHandle: z.string(), -}); +import { COMMAND_CONNECTION_ID_VALIDATOR, EVENT_COMMAND_CONNECTION_VALIDATOR, EVENT_COMMAND_STUDIO_DATA_VALIDATOR } from './globalCommand'; +import { EVENT_COMMAND_WAIT_MOVEMENT_COMPLETION_VALIDATOR } from './movement/waitMovementCompletion'; export type StudioEventCommandConnection = z.infer; @@ -98,7 +82,7 @@ export const EVENT_COMMAND_VALIDATOR = z.discriminatedUnion('type', [ GENERIC_COMMAND('move_event'), GENERIC_COMMAND('teleport_event'), GENERIC_COMMAND('teleport_player'), - GENERIC_COMMAND('wait_move_completion'), + EVENT_COMMAND_WAIT_MOVEMENT_COMPLETION_VALIDATOR, GENERIC_COMMAND('manage_event_reappearance'), GENERIC_COMMAND('manage_path_finding'), GENERIC_COMMAND('manage_follow_me'), diff --git a/src/models/entities/event/event.ts b/src/models/entities/event/event.ts index 0c4f780c..e2485ded 100644 --- a/src/models/entities/event/event.ts +++ b/src/models/entities/event/event.ts @@ -1,7 +1,8 @@ import { z } from 'zod'; import { POSITIVE_OR_ZERO_INT } from '../common'; import { DB_SYMBOL_VALIDATOR } from '../dbSymbol'; -import { COMMAND_ID_VALIDATOR, EVENT_COMMAND_VALIDATOR } from './command'; +import { EVENT_COMMAND_VALIDATOR } from './command'; +import { COMMAND_ID_VALIDATOR } from './globalCommand'; const TEMPLATE_PARAMETER_NAME_VALIDATOR = z.string().brand('TemplateParameterName'); diff --git a/src/models/entities/event/globalCommand.ts b/src/models/entities/event/globalCommand.ts new file mode 100644 index 00000000..eda369a6 --- /dev/null +++ b/src/models/entities/event/globalCommand.ts @@ -0,0 +1,19 @@ +import { z } from 'zod'; + +export const COMMAND_ID_VALIDATOR = z.string().brand('CommandId'); +export type CommandId = z.infer; + +export const COMMAND_CONNECTION_ID_VALIDATOR = z.string().brand('ConnectionId'); +export type ConnectionId = z.infer; + +export const EVENT_COMMAND_STUDIO_DATA_VALIDATOR = z.object({ + x: z.number().int(), + y: z.number().int(), + comments: z.array(z.string()), +}); + +export const EVENT_COMMAND_CONNECTION_VALIDATOR = z.object({ + sourceHandle: z.string(), + target: COMMAND_ID_VALIDATOR, + targetHandle: z.string(), +}); diff --git a/src/models/entities/event/movement/waitMovementCompletion.ts b/src/models/entities/event/movement/waitMovementCompletion.ts new file mode 100644 index 00000000..54e02691 --- /dev/null +++ b/src/models/entities/event/movement/waitMovementCompletion.ts @@ -0,0 +1,23 @@ +import { z } from 'zod'; +import { POSITIVE_OR_ZERO_INT } from '../../common'; +import { COMMAND_CONNECTION_ID_VALIDATOR, EVENT_COMMAND_CONNECTION_VALIDATOR, EVENT_COMMAND_STUDIO_DATA_VALIDATOR } from '../globalCommand'; + +export const EVENT_COMMAND_WAIT_MOVEMENT_COMPLETION_VALIDATOR = z.object({ + waitAllEvents: z.boolean().default(false), + waitById: z.array(z.string()).default([]), + timeout: POSITIVE_OR_ZERO_INT, + + type: z.literal('wait_move_completion'), + connections: z.record(COMMAND_CONNECTION_ID_VALIDATOR, EVENT_COMMAND_CONNECTION_VALIDATOR), + studioData: EVENT_COMMAND_STUDIO_DATA_VALIDATOR, +}); + +export type StudioEventWaitMovementCompletion = z.infer; + +export const createWaitMovementCompletionCommand = () => { + return { + waitAllEvents: false, + waitById: [], + timeout: 0, + }; +}; diff --git a/src/utils/eventCommandCreation.ts b/src/utils/eventCommandCreation.ts index 622ab5ff..8335d1da 100644 --- a/src/utils/eventCommandCreation.ts +++ b/src/utils/eventCommandCreation.ts @@ -1,5 +1,6 @@ import type { StudioEventCommand, StudioEventCommandData, StudioEventCommandType } from '@modelEntities/event/command'; import { StudioEvent } from '@modelEntities/event/event'; +import { createWaitMovementCompletionCommand } from '../models/entities/event/movement/waitMovementCompletion'; import { findFirstAvailableTextIdEvent } from './ModelUtils'; const createShowMessageCommand = (event: StudioEvent) => { @@ -46,7 +47,7 @@ export const EventCommandCreation: Record props.theme.colors.dark15}; border: 1px solid ${(props) => props.theme.colors.text400}; } + + .params { + color: ${(props) => props.theme.colors.text400}; + ${(props) => props.theme.fonts.normalSmall}; + } `; export const EditorWithCollapseContainer = styled(EditorContainer)` diff --git a/src/views/components/world/event/EventEditor.tsx b/src/views/components/world/event/EventEditor.tsx index 051a53e2..d5a09b3e 100644 --- a/src/views/components/world/event/EventEditor.tsx +++ b/src/views/components/world/event/EventEditor.tsx @@ -1,7 +1,6 @@ import { Background, Controls, ReactFlow, ReactFlowProvider } from '@xyflow/react'; import { CommandLibrary } from '@components/world/event/commandLibrary/CommandLibrary'; import { EventProvider } from '@components/world/event/common/EventContext'; -import type { CommandId } from '@modelEntities/event/command'; import { CommandEditorAndDeletionKeys, CommandEditorOverlay } from './commands/editors/CommandEditorOverlay'; import { useDialogsRef } from '@src/hooks/useDialogsRef'; import { CommandToNodes } from './common/CommandToNodes'; @@ -10,6 +9,7 @@ import { CustomConnectionLineStyle, edgeTypes } from './common/CustomEdge'; import { useEventFlow } from './hooks/useEventFlow'; import styled from 'styled-components'; import React, { useMemo, useRef } from 'react'; +import { CommandId } from '../../../../models/entities/event/globalCommand'; const GRID_SIZE = 32; diff --git a/src/views/components/world/event/commands/DefaultCommand.tsx b/src/views/components/world/event/commands/DefaultCommand.tsx index 4a4269e6..3f0b9e58 100644 --- a/src/views/components/world/event/commands/DefaultCommand.tsx +++ b/src/views/components/world/event/commands/DefaultCommand.tsx @@ -1,10 +1,10 @@ -import { CommandNodeProps } from './CommandNodeProps'; -import { useHandleConnectionState } from '../hooks/useHandleConnectionState'; -import { IconsFromCommand } from '../common/EventIcon'; -import { CommandNode } from '../common/CommandNode'; -import { CustomHandle } from '../common/CustomHandle'; import { Position } from '@xyflow/react'; import React from 'react'; +import { CommandNode } from '../common/CommandNode'; +import { CustomHandle } from '../common/CustomHandle'; +import { IconsFromCommand } from '../common/EventIcon'; +import { useHandleConnectionState } from '../hooks/useHandleConnectionState'; +import { CommandNodeProps } from './CommandNodeProps'; export const DefaultCommand = ({ id, data: { dialogsRef, command }, selected }: CommandNodeProps) => { //const date = useMemo(() => new Date().toLocaleString(), []); diff --git a/src/views/components/world/event/commands/ShowMessageCommand.tsx b/src/views/components/world/event/commands/ShowMessageCommand.tsx index c198482a..0914c3c3 100644 --- a/src/views/components/world/event/commands/ShowMessageCommand.tsx +++ b/src/views/components/world/event/commands/ShowMessageCommand.tsx @@ -2,11 +2,12 @@ import { Input, InputWithTopLabelContainer, Label, MultiLineInput } from '@compo import { InputFormContainer } from '@components/inputs/InputContainer'; import { TranslateInputContainer } from '@components/inputs/TranslateInputContainer'; import { useDialogsRef } from '@hooks/useDialogsRef'; -import { CommandId, StudioEventCommandData, StudioEventCommandShowMessage } from '@modelEntities/event/command'; +import { StudioEventCommandData, StudioEventCommandShowMessage } from '@modelEntities/event/command'; import { useGetProjectText, useSetProjectText } from '@utils/ReadingProjectText'; import React, { useEffect, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import styled, { css } from 'styled-components'; +import { CommandId } from '../../../../../models/entities/event/globalCommand'; import { useCommandNode } from '../hooks/useCommandNode'; import { CommandNodeProps } from './CommandNodeProps'; import { ShowMessageEditorTitle, ShowMessageOverlay } from './editors/ShowMessageOverlay'; diff --git a/src/views/components/world/event/commands/editors/CommandEditorOverlay.tsx b/src/views/components/world/event/commands/editors/CommandEditorOverlay.tsx index b669cec3..dec9b591 100644 --- a/src/views/components/world/event/commands/editors/CommandEditorOverlay.tsx +++ b/src/views/components/world/event/commands/editors/CommandEditorOverlay.tsx @@ -1,12 +1,14 @@ import { defineEditorOverlay } from '@components/editor/EditorOverlayV2'; import { DialogRefData } from '@hooks/useDialogsRef'; -import type { CommandId, StudioEventCommandType } from '@modelEntities/event/command'; +import type { StudioEventCommandType } from '@modelEntities/event/command'; import type { StudioEvent } from '@modelEntities/event/event'; import { assertUnreachable } from '@utils/assertUnreachable'; import React from 'react'; +import { CommandId } from '../../../../../../models/entities/event/globalCommand'; import { DefaultEditor } from './DefaultEditor'; import { InsertScriptEditor } from './InsertScriptEditor'; import { ShowMessageEditor } from './ShowMessageEditor'; +import { WaitMovementCompletionEditor } from './movementCommands/WaitMovementCompletion'; export type CommandEditorAndDeletionKeys = StudioEventCommandType; export type CommandDialogsRef = React.RefObject | null>; @@ -42,6 +44,7 @@ export const CommandEditorOverlay = defineEditorOverlay; case 'manage_event_reappearance': case 'manage_path_finding': case 'manage_follow_me': diff --git a/src/views/components/world/event/commands/editors/EventEditorProps.ts b/src/views/components/world/event/commands/editors/EventEditorProps.ts index b32bc2a3..23846497 100644 --- a/src/views/components/world/event/commands/editors/EventEditorProps.ts +++ b/src/views/components/world/event/commands/editors/EventEditorProps.ts @@ -1,5 +1,5 @@ -import type { CommandId } from '@modelEntities/event/command'; import type { StudioEvent } from '@modelEntities/event/event'; +import { CommandId } from '../../../../../../models/entities/event/globalCommand'; export type EventEditorProps = { commandId?: CommandId; diff --git a/src/views/components/world/event/commands/editors/movementCommands/WaitMovementCompletion.tsx b/src/views/components/world/event/commands/editors/movementCommands/WaitMovementCompletion.tsx new file mode 100644 index 00000000..1899dc7f --- /dev/null +++ b/src/views/components/world/event/commands/editors/movementCommands/WaitMovementCompletion.tsx @@ -0,0 +1,126 @@ +import { Editor } from '@components/editor'; +import { EditorHandlingClose, useEditorHandlingClose } from '@components/editor/useHandleCloseEditor'; +import { InputFormContainer } from '@components/inputs/InputContainer'; +import { useInputAttrsWithLabel } from '@src/hooks/useInputAttrs'; +import { useZodForm } from '@src/hooks/useZodForm'; +import React, { forwardRef, useMemo, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import styled from 'styled-components'; +import z from 'zod'; +import { + EVENT_COMMAND_WAIT_MOVEMENT_COMPLETION_VALIDATOR, + StudioEventWaitMovementCompletion, +} from '../../../../../../../models/entities/event/movement/waitMovementCompletion'; +import { InputWithLeftLabelContainer, InputWithTopLabelContainer, Label } from '../../../../../inputs'; +import { useCommandEditor } from '../../../hooks/useCommandEditor'; +import { useSharedOptions } from '../../sharedSelectOptions'; +import { EventEditorProps } from '../EventEditorProps'; + +const TimeoutInfo = styled.div` + ${({ theme }) => theme.fonts.normalSmall}; + color: ${({ theme }) => theme.colors.text400}; + user-select: none; +`; + +const WAIT_MOVEMENT_COMPLETION_EDITOR_SCHEMA = EVENT_COMMAND_WAIT_MOVEMENT_COMPLETION_VALIDATOR.pick({ + timeout: true, +}).extend({ + waitAllEvents: z.enum(['all', 'some']), + waitById: z.array(z.string()).optional(), +}); + +export const WaitMovementCompletionEditor = forwardRef(({ commandId: defaultCommandId, event }, ref) => { + const { command, updateCommand } = useCommandEditor(event, defaultCommandId); + const commandDataForForm = useMemo( + () => ({ + ...command, + waitAllEvents: (command.waitAllEvents ? 'all' : 'some') as 'all' | 'some', + }), + [command.waitAllEvents, command.waitById, command.timeout], + ); + const { globalStaticEvent } = useSharedOptions(); + const { canClose, getFormData, getRawFormData, defaults, formRef } = useZodForm(WAIT_MOVEMENT_COMPLETION_EDITOR_SCHEMA, commandDataForForm); + const { EmbeddedUnitInput, Select, MultiSelect } = useInputAttrsWithLabel(WAIT_MOVEMENT_COMPLETION_EDITOR_SCHEMA, defaults); + const timeoutRef = useRef(null); + const { t } = useTranslation(); + + const [waitEvent, setWaitEvent] = useState(command.waitAllEvents ? 'all' : 'some'); + + const waitEventOptions = useMemo( + () => [ + { label: t('event_command_wait_move_completion_select_all'), value: 'all' }, + { label: t('event_command_wait_move_completion_select_some'), value: 'some' }, + ], + [t], + ); + + const eventToWaitOptions = useMemo( + () => [ + ...globalStaticEvent(), + { + label: t('event_command_wait_move_completion_multiselect_static_this_event'), + value: event?.dbSymbol as string, + }, + ], + [event?.dbSymbol], + ); + + const onClose = () => { + const rawData = getRawFormData(); + const waitById = (rawData.waitById as string[] | undefined) ?? []; + const waitByIdIsInvalid = waitEvent === 'some' && waitById.length === 0; + if (waitByIdIsInvalid) return; + + const result = canClose() && getFormData(); + if (!result || !result.success) return; + + updateCommand({ + ...result.data, + waitAllEvents: waitEvent === 'all', + timeout: timeoutRef.current ? Number(timeoutRef.current.value) : result.data.timeout, + }); + }; + + const canCloseWithWaitById = () => { + const rawData = getRawFormData(); + const waitById = (rawData.waitById as string[] | undefined) ?? []; + if (waitEvent === 'some' && waitById.length === 0) return false; + + return canClose(); + }; + useEditorHandlingClose(ref, onClose, canCloseWithWaitById); + + return ( + + + + + {waitEvent === 'some' && ( + + )} + + + ); +}; diff --git a/src/views/components/world/event/commands/sharedSelectOptions.ts b/src/views/components/world/event/commands/sharedSelectOptions.ts new file mode 100644 index 00000000..96898006 --- /dev/null +++ b/src/views/components/world/event/commands/sharedSelectOptions.ts @@ -0,0 +1,10 @@ +import { useTranslation } from 'react-i18next'; + +export const useSharedOptions = () => { + const { t } = useTranslation(); + + const globalStaticEvent = () => [{ label: t('event_command_wait_move_completion_multiselect_static_player'), value: 'player' }]; + return { + globalStaticEvent, + }; +}; diff --git a/src/views/components/world/event/common/CommandToNodes.ts b/src/views/components/world/event/common/CommandToNodes.ts index 9acd8792..69b954f0 100644 --- a/src/views/components/world/event/common/CommandToNodes.ts +++ b/src/views/components/world/event/common/CommandToNodes.ts @@ -3,6 +3,7 @@ import { JSX } from 'react'; import type { CommandNodeProps } from '../commands/CommandNodeProps'; import { DefaultCommand } from '../commands/DefaultCommand'; import { InsertScriptCommand } from '../commands/InsertScriptCommand'; +import { WaitMovementCompletionCommand } from '../commands/nodes/movementCommands/WaitMovementCompletion'; import { ShowMessageCommand } from '../commands/ShowMessageCommand'; import { ShadowNode } from './ShadowNode'; @@ -28,7 +29,7 @@ export const CommandToNodes: Record(event: StudioEvent, defaultCommandId?: CommandId) => { diff --git a/src/views/components/world/event/hooks/useCommandNode.tsx b/src/views/components/world/event/hooks/useCommandNode.tsx index 8c40ad76..fb13413f 100644 --- a/src/views/components/world/event/hooks/useCommandNode.tsx +++ b/src/views/components/world/event/hooks/useCommandNode.tsx @@ -1,8 +1,9 @@ import { useEventActions } from '@components/world/event/common/EventContext'; -import { CommandNode } from '../common/CommandNode'; -import { CommandId, StudioEventCommand } from '@modelEntities/event/command'; +import { StudioEventCommand } from '@modelEntities/event/command'; import { cloneEntity } from '@utils/cloneEntity'; import { useCallback } from 'react'; +import { CommandId } from '../../../../../models/entities/event/globalCommand'; +import { CommandNode } from '../common/CommandNode'; export const useCommandNode = (id: string) => { const { setCurrentEditedNode, updateEvent } = useEventActions();